From 45f9785fd9040722d0eeebab9e4cde09def0268e Mon Sep 17 00:00:00 2001 From: Preston Holmes Date: Tue, 2 Jun 2026 05:46:06 -0700 Subject: [PATCH 001/155] Add channel and thread filtering to message broker (#293) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(scion-chat-app): set channel="gchat" on ask_user dialog responses handleDialogSubmit was using the simple SendMessage API which doesn't support structured message fields, so inbound ask_user responses arrived at the hub with no channel set (defaulting to "web"). Switch to SendStructuredMessage with Channel="gchat" to match the pattern already used by cmdMessage. * fix: channel filtering and thread-id routing for chat channel replies Two bugs in the chat channel routing feature: 1. Channel filtering: broker plugins now check msg.Channel and skip messages targeted at a different channel. The hub injects plugin_name into broker credentials so each plugin knows its own channel identity. This prevents cross-channel delivery (e.g., Telegram replies leaking to Google Chat). 2. Thread-id routing: the Telegram plugin now passes msg.ThreadID as message_thread_id to the Telegram Bot API when sending outbound messages. Previously, thread-id was captured on inbound messages but never forwarded on outbound, causing replies to land in the wrong forum topic. Added SendOption variadic parameter to SendMessage, SendMessageWithKeyboard, and SendQueue.Send for backward-compatible thread-id support. * feat(scion-chat-app): add Google Chat thread context support Propagate thread IDs end-to-end so agents can participate in Google Chat threads: - Inbound: auto-set ThreadID on StructuredMessage from the Google Chat event's thread context when no explicit --thread flag is used - Inbound: propagate ThreadID on dialog submit (ask_user responses) - Outbound: pass ThreadID from StructuredMessage to SendMessageRequest so agent replies land in the correct Google Chat thread * fix: route outbound messages to chat-app via ChannelID The FanOutEventBus matched msg.Channel against the bus Name, but the chat-app plugin is registered as "chat-app" while its messages use channel="gchat". Add a ChannelID field to NamedEventBus and PluginInfo so plugins can declare the channel they handle independently of their registered name. The chat-app now reports ChannelID="gchat" via GetInfo(), and the hub reads it at startup to wire routing correctly. * design: per-topic /default agent scoping for Telegram forums Explores how to let /default set a different default agent per forum topic (message_thread_id) rather than per-chat. Conclusion: ~85 lines of changes across store, commands, callbacks, and routing. * feat(scion-telegram): per-topic /default agent scoping for forum groups Add support for setting a different default agent per Telegram forum topic/thread, with the chat-wide default as fallback. - New topic_defaults table keyed on (chat_id, thread_id) - /default in a topic sets/shows the topic-level override - Callback data extended: dflt:: for topic scope - Routing resolves topic default before chat default for both @bot-mention and unaddressed message fallback paths * fix: address PR #293 review feedback - Add !no_sqlite build tag to resource_import_handler_test.go to fix CI vet failure (mockRoundTripper undefined when template_bootstrap_test.go is excluded) - Guard debug log in broker.go Publish against nil msg to prevent panic - Add fitCallback to preserve threadID suffix in Telegram callback_data when the 64-byte limit is exceeded, truncating agentSlug instead - Add slog warning to truncateCallback when truncation occurs * fix: address second round of PR #293 review feedback - Remove redundant channel filters from chat-app and Telegram Publish() methods — the FanOutEventBus already routes by ChannelID, and comparing against the plugin's registered name would silently drop messages - Log errors from GetTopicDefault instead of silently ignoring them - Return distinct error messages in chat-app when ResolveOrAutoRegister fails with a real error vs a nil mapping * fix: address third round of PR #293 review feedback - Add early return for nil msg at top of Publish() to prevent panics in downstream handlers that dereference msg fields - Add thread-safe ChannelName() getter on BrokerServer - Use dynamic ChannelName() in GetInfo() instead of hardcoded "gchat" - Use dynamic ChannelName() in both commands.go call sites * fix: use callback_lookups for long callback data instead of truncation Replace fitCallback() which corrupted agent slugs by truncating them to fit Telegram's 64-byte limit. Long callback payloads are now stored in the callback_lookups table with a short cblu: reference. HandleCallback resolves lookup IDs before routing. Also add defensive check for empty HubUserEmail in chat-app to prevent constructing invalid "user:" sender strings. * fix: address fifth round of PR #293 review feedback - Use local interface instead of concrete *BrokerRPCClient type assertion in pluginChannelID() and isObserverBroker() so in-process brokers and mocks are handled correctly. - Add nil guard for msg in fanout channel routing check. --------- Co-authored-by: Scion --- .design/telegram-per-topic-default.md | 152 ++++++++++++++++++ cmd/server_foreground.go | 39 ++++- extras/scion-chat-app/go.mod | 2 +- .../scion-chat-app/internal/chatapp/broker.go | 18 +++ .../internal/chatapp/commands.go | 25 ++- .../internal/chatapp/notifications.go | 7 +- extras/scion-telegram/go.mod | 2 +- .../scion-telegram/internal/telegram/api.go | 32 ++-- .../internal/telegram/broker_v2.go | 35 ++-- .../internal/telegram/callbacks.go | 49 ++++++ .../scion-telegram/internal/telegram/cards.go | 81 +++++++++- .../internal/telegram/cards_test.go | 35 +++- .../internal/telegram/commands.go | 33 +++- .../internal/telegram/sendqueue.go | 27 ++-- .../scion-telegram/internal/telegram/store.go | 38 +++++ .../internal/telegram/store_test.go | 71 ++++++++ pkg/eventbus/fanout.go | 27 +++- pkg/eventbus/fanout_test.go | 29 ++++ pkg/hub/resource_import_handler_test.go | 2 + pkg/plugin/config.go | 8 + 20 files changed, 654 insertions(+), 58 deletions(-) create mode 100644 .design/telegram-per-topic-default.md diff --git a/.design/telegram-per-topic-default.md b/.design/telegram-per-topic-default.md new file mode 100644 index 000000000..f46af7cdb --- /dev/null +++ b/.design/telegram-per-topic-default.md @@ -0,0 +1,152 @@ +# Design: Per-Topic Default Agent in Telegram + +**Date:** 2026-06-01 +**Branch:** chat-channels +**Status:** Exploration + +## Current Behavior + +The `/default` command sets a single default agent per Telegram group chat. The flow: + +1. User types `/default` in a group → `handleDefault()` (`commands.go:194`) fetches the `GroupLink` by `chat_id` and presents an inline keyboard of agents. +2. User taps an agent button → `handleDefaultCallback()` (`callbacks.go:314`) writes `link.DefaultAgent = agentSlug` and calls `SaveGroupLink()`. +3. On inbound messages, `handleGroupMessage()` (`broker_v2.go:1607-1615`) falls back to `link.DefaultAgent` when no @-mention, reply-to, or conversation context resolves a target. + +**Storage:** The `group_links` table has `chat_id INTEGER PRIMARY KEY` and a single `default_agent TEXT` column. One default per chat. + +## Thread/Topic Context in Telegram + +Telegram forum-mode groups have named topics. Each topic has a `message_thread_id` (int64). The General topic is thread ID 1 (or 0 in some API versions). The plugin already captures this: + +- **Inbound:** `TGMessage.MessageThreadID` is populated by the Telegram Bot API. At `broker_v2.go:1757-1759`, it's stored as `msg.ThreadID`. +- **Outbound:** `Publish()` reads `msg.ThreadID` and passes it back as `SendOption{MessageThreadID: tid}` so replies land in the correct topic. +- **Commands:** When `/default` is typed inside a topic, `msg.MessageThreadID` carries the thread ID. The `CallbackQuery.Message` also has `MessageThreadID`, so the callback knows which topic the button was pressed in. + +**Key insight:** All the thread context is already flowing through the system — it's just not used for default-agent scoping. + +## Proposed Changes + +### 1. Storage: New `topic_defaults` Table + +Add a new table rather than modifying `group_links`: + +```sql +CREATE TABLE IF NOT EXISTS topic_defaults ( + chat_id INTEGER NOT NULL, + thread_id INTEGER NOT NULL, + agent_slug TEXT NOT NULL, + PRIMARY KEY (chat_id, thread_id) +); +``` + +The existing `group_links.default_agent` stays as the chat-level fallback. This avoids a schema migration and keeps the two concepts cleanly separated. + +**Store methods to add:** +- `GetTopicDefault(ctx, chatID, threadID) (string, error)` +- `SetTopicDefault(ctx, chatID, threadID, agentSlug) error` +- `DeleteTopicDefault(ctx, chatID, threadID) error` + +### 2. Command Handling Changes + +**`handleDefault()` in `commands.go`:** +- Read `msg.MessageThreadID`. +- If nonzero (i.e., inside a topic), pass it through to the keyboard builder and callback data. +- The keyboard prompt changes to: "Select the default agent for this topic:" (vs. the current "for @-mentions:"). +- If zero (General topic or non-forum group), behave as today (chat-level default). + +**Callback data format:** +- Current: `dflt:` (e.g., `dflt:coder`) +- New: `dflt::` (e.g., `dflt:coder:42`) +- When `threadID` is empty or "0", it's a chat-level default (backward-compatible). + +**`handleDefaultCallback()` in `callbacks.go`:** +- Parse the optional `threadID` from callback data parts. +- If present and nonzero: call `SetTopicDefault(ctx, chatID, threadID, agentSlug)`. +- If `__none__`: call `DeleteTopicDefault(ctx, chatID, threadID)`. +- Otherwise: set chat-level default as today. + +### 3. Routing Logic Changes + +**`handleGroupMessage()` in `broker_v2.go` (around line 1607):** + +Replace the current fallback: +```go +if len(targets) == 0 && link.DefaultAgent != "" { +``` + +With a two-tier lookup: +```go +if len(targets) == 0 { + defaultAgent := "" + if tgMsg.MessageThreadID != 0 { + topicDefault, _ := b.store.GetTopicDefault(ctx, chatID, tgMsg.MessageThreadID) + if topicDefault != "" { + defaultAgent = topicDefault + } + } + if defaultAgent == "" { + defaultAgent = link.DefaultAgent + } + if defaultAgent != "" { + // existing routing logic + } +} +``` + +Fallback chain: **topic default → chat default → no default**. + +### 4. UX for Querying/Clearing + +**Showing current default:** When `/default` is invoked in a topic, the keyboard should show the topic-level default (if set) with a checkmark, falling back to showing the chat-level default with a "(chat default)" label. + +**Clearing a topic default:** The "No default agent" button in topic context removes the topic override, reverting to the chat-level fallback. It does NOT clear the chat-level default. + +**Showing all topic defaults:** Consider adding a `/defaults` command (or a flag like `/default list`) that shows all topic-specific overrides for the group. This is a nice-to-have, not required for v1. + +## UX Flows + +### Setting a per-topic default +1. User navigates to the "Backend" topic in a forum group. +2. Types `/default`. +3. Sees keyboard: "Select the default agent for this topic:" with agent buttons. +4. Taps "coder" → "Default agent for this topic set to @coder." + +### Clearing a per-topic default +1. In the same topic, types `/default`. +2. Keyboard shows "✓ coder (current)" and "No default agent (use chat default)". +3. Taps "No default agent" → "Topic default removed. Messages will use the chat default (@designer)." + +### Non-forum group (no change) +1. `/default` works exactly as today. +2. `MessageThreadID` is 0, so all code paths hit the chat-level branch. + +## Complexity Assessment + +**This is a small, contained change.** Estimated at 1-2 days of implementation + testing. + +| Area | Scope | +|------|-------| +| New table + 3 store methods | ~40 lines | +| `handleDefault()` thread-awareness | ~10 lines changed | +| Callback data + `handleDefaultCallback()` | ~15 lines changed | +| Routing fallback in `handleGroupMessage()` | ~10 lines changed | +| Keyboard label tweaks in `cards.go` | ~10 lines changed | +| **Total new/changed code** | **~85 lines** | + +No changes needed to: +- The outbound message flow (thread routing already works) +- The `GroupLink` struct or `group_links` table +- The Telegram Bot API client +- Any other command handlers + +## Risks and Edge Cases + +1. **General topic ambiguity:** Telegram uses thread ID 1 for the General topic in forum groups, but non-forum groups have thread ID 0. The code should treat `MessageThreadID == 0` as "no topic" (chat-level default). Thread ID 1 (General) should be a valid topic for per-topic defaults. + +2. **Topic deletion:** If a topic is deleted, its `topic_defaults` row becomes orphaned but harmless — no messages will arrive with that thread ID. Could add periodic cleanup, but not necessary. + +3. **Callback data length:** Telegram limits callback data to 64 bytes. Current format `dflt:agentSlug` uses ~15-25 bytes. Adding `:threadID` (max 20 digits) stays well within limits. If using the `callback_lookups` short-ID system already in the codebase, this is a non-issue. + +4. **Race between topic and chat defaults:** A user might set a chat default expecting it to apply everywhere, not realizing a topic has an override. The `/default` command should clearly indicate when a topic override exists. + +5. **Forum mode toggled off:** If a group admin disables forum mode, all topics collapse. Topic defaults become inert (messages arrive with thread ID 0). The chat-level default takes over naturally. No data loss; if forum mode is re-enabled, the topic defaults resume working. diff --git a/cmd/server_foreground.go b/cmd/server_foreground.go index e8c18b467..307a2cb4e 100644 --- a/cmd/server_foreground.go +++ b/cmd/server_foreground.go @@ -348,9 +348,10 @@ func runServerStart(cmd *cobra.Command, args []string) error { log.Printf("Warning: failed to generate secret for broker plugin %q: %v", bt, secretErr) } else { hubCreds := map[string]string{ - "hub_url": hubEndpoint, - "hmac_key": secretKey, - "broker_id": brokerID, + "hub_url": hubEndpoint, + "hmac_key": secretKey, + "broker_id": brokerID, + "plugin_name": bt, } // Inject project slug map so hub-managed plugins can resolve // human-readable project names without user-level API access. @@ -377,10 +378,11 @@ func runServerStart(cmd *cobra.Command, args []string) error { } observer := isObserverBroker(pluginMgr, bt) + channelID := pluginChannelID(pluginMgr, bt) namedBuses = append(namedBuses, eventbus.NamedEventBus{ - Name: bt, Bus: b, Observer: observer, + Name: bt, Bus: b, Observer: observer, ChannelID: channelID, }) - log.Printf("Message broker spoke added: name=%s observer=%v", bt, observer) + log.Printf("Message broker spoke added: name=%s channel_id=%s observer=%v", bt, channelID, observer) } fanout := eventbus.NewFanOutEventBus(namedBuses, logging.Subsystem("hub.eventbus.fanout")) @@ -1250,13 +1252,38 @@ func startRuntimeBroker(ctx context.Context, cmd *cobra.Command, cfg *config.Glo return nil } +// pluginChannelID returns the channel identifier reported by a broker plugin +// via GetInfo().ChannelID. Returns "" if the plugin does not report one, in +// which case the bus Name is used for channel routing. +func pluginChannelID(pluginMgr *scionplugin.Manager, name string) string { + raw, err := pluginMgr.Get(scionplugin.PluginTypeBroker, name) + if err != nil { + return "" + } + type infoer interface { + GetInfo() (*scionplugin.PluginInfo, error) + } + rpc, ok := raw.(infoer) + if !ok { + return "" + } + info, err := rpc.GetInfo() + if err != nil || info == nil { + return "" + } + return info.ChannelID +} + // isObserverBroker determines whether a broker plugin should be treated as an // observer (fire-and-forget on publish errors). It checks the plugin's // capabilities first, then falls back to a name-based heuristic. func isObserverBroker(pluginMgr *scionplugin.Manager, name string) bool { raw, err := pluginMgr.Get(scionplugin.PluginTypeBroker, name) if err == nil { - if rpc, ok := raw.(*scionplugin.BrokerRPCClient); ok { + type infoer interface { + GetInfo() (*scionplugin.PluginInfo, error) + } + if rpc, ok := raw.(infoer); ok { if info, infoErr := rpc.GetInfo(); infoErr == nil && info != nil { for _, cap := range info.Capabilities { if strings.EqualFold(cap, "observer") { diff --git a/extras/scion-chat-app/go.mod b/extras/scion-chat-app/go.mod index 9620d8200..169b770f9 100644 --- a/extras/scion-chat-app/go.mod +++ b/extras/scion-chat-app/go.mod @@ -1,6 +1,6 @@ module github.com/GoogleCloudPlatform/scion/extras/scion-chat-app -go 1.25.4 +go 1.26.1 require ( cloud.google.com/go/secretmanager v1.16.0 diff --git a/extras/scion-chat-app/internal/chatapp/broker.go b/extras/scion-chat-app/internal/chatapp/broker.go index 2650935d3..005821c08 100644 --- a/extras/scion-chat-app/internal/chatapp/broker.go +++ b/extras/scion-chat-app/internal/chatapp/broker.go @@ -40,6 +40,7 @@ type BrokerServer struct { mu sync.RWMutex subscriptions map[string]bool configured bool + channelName string } // Compile-time interface checks. @@ -66,12 +67,21 @@ func (b *BrokerServer) Configure(config map[string]string) error { b.mu.Lock() defer b.mu.Unlock() b.configured = true + if b.channelName == "" { + b.channelName = "gchat" + } + if v, ok := config["plugin_name"]; ok && v != "" { + b.channelName = v + } b.log.Info("broker plugin configured", "config_keys", len(config)) return nil } // Publish receives a message from the Hub and routes it to the handler. func (b *BrokerServer) Publish(ctx context.Context, topic string, msg *messages.StructuredMessage) error { + if msg == nil { + return nil + } b.log.Debug("received message via broker", "topic", topic, "sender", msg.Sender, @@ -83,6 +93,13 @@ func (b *BrokerServer) Publish(ctx context.Context, topic string, msg *messages. return nil } +// ChannelName returns the configured channel name in a thread-safe manner. +func (b *BrokerServer) ChannelName() string { + b.mu.RLock() + defer b.mu.RUnlock() + return b.channelName +} + // Subscribe registers a topic pattern for receiving messages. func (b *BrokerServer) Subscribe(pattern string) error { b.mu.Lock() @@ -112,6 +129,7 @@ func (b *BrokerServer) GetInfo() (*plugin.PluginInfo, error) { return &plugin.PluginInfo{ Name: "scion-chat-app", Version: "1.0.0", + ChannelID: b.ChannelName(), Capabilities: []string{"chat-bridge", "notification-relay"}, }, nil } diff --git a/extras/scion-chat-app/internal/chatapp/commands.go b/extras/scion-chat-app/internal/chatapp/commands.go index 6230a7ea9..8abcbe470 100644 --- a/extras/scion-chat-app/internal/chatapp/commands.go +++ b/extras/scion-chat-app/internal/chatapp/commands.go @@ -307,12 +307,29 @@ func (r *CommandRouter) handleDialogSubmit(ctx context.Context, event *ChatEvent return r.reply(ctx, event, "This space is not linked to a project.") } - client, err := r.clientForUser(ctx, event) + mapping, err := r.idMapper.ResolveOrAutoRegister(ctx, &eventUserLookup{event}, event.UserID, event.Platform) if err != nil { + r.log.Error("Failed to resolve user mapping", "error", err, "userID", event.UserID) + return r.reply(ctx, event, "Something went wrong, please try again later.") + } + if mapping == nil { return r.reply(ctx, event, "Authentication required. Use `/scionAdmin register` first.") } + client, err := r.idMapper.ClientFor(ctx, mapping) + if err != nil { + return r.reply(ctx, event, fmt.Sprintf("Failed to create client: %v", err)) + } - if err := client.ProjectAgents(link.ProjectID).SendMessage(ctx, agentID, responseText, false); err != nil { + senderEmail := mapping.HubUserEmail + if senderEmail == "" { + return r.reply(ctx, event, "Your user mapping is missing a valid email address.") + } + msg := messages.NewInstruction("user:"+senderEmail, agentID, responseText) + msg.Channel = r.broker.ChannelName() + if event.ThreadID != "" { + msg.ThreadID = event.ThreadID + } + if err := client.ProjectAgents(link.ProjectID).SendStructuredMessage(ctx, agentID, msg, false, false, false); err != nil { return r.reply(ctx, event, fmt.Sprintf("Failed to send response to agent: %v", err)) } return r.reply(ctx, event, fmt.Sprintf("Response sent to agent `%s`.", agentID)) @@ -1063,9 +1080,11 @@ func (r *CommandRouter) cmdMessage(ctx context.Context, event *ChatEvent, args [ // Use the hub user email with "user:" prefix so agents can address replies msg := messages.NewInstruction("user:"+mapping.HubUserEmail, agentSlug, messageText) - msg.Channel = "gchat" + msg.Channel = r.broker.ChannelName() if threadID != "" { msg.ThreadID = threadID + } else if event.ThreadID != "" { + msg.ThreadID = event.ThreadID } if err := client.ProjectAgents(link.ProjectID).SendStructuredMessage(ctx, agentSlug, msg, false, false, false); err != nil { diff --git a/extras/scion-chat-app/internal/chatapp/notifications.go b/extras/scion-chat-app/internal/chatapp/notifications.go index dfcd7ae79..9b4adbfc9 100644 --- a/extras/scion-chat-app/internal/chatapp/notifications.go +++ b/extras/scion-chat-app/internal/chatapp/notifications.go @@ -181,9 +181,10 @@ func (n *NotificationRelay) handleUserMessage(ctx context.Context, projectID str mentions := n.buildMentions(mapping.PlatformUserID, agentSlug, link) if _, err := n.messenger.SendMessage(ctx, SendMessageRequest{ - SpaceID: link.SpaceID, - Text: mentions, - Card: &card, + SpaceID: link.SpaceID, + ThreadID: msg.ThreadID, + Text: mentions, + Card: &card, }); err != nil { n.log.Error("failed to relay user message", "space_id", link.SpaceID, diff --git a/extras/scion-telegram/go.mod b/extras/scion-telegram/go.mod index 436da3fc1..e0dcc5067 100644 --- a/extras/scion-telegram/go.mod +++ b/extras/scion-telegram/go.mod @@ -1,6 +1,6 @@ module github.com/GoogleCloudPlatform/scion/extras/scion-telegram -go 1.25.4 +go 1.26.1 require ( github.com/GoogleCloudPlatform/scion v0.0.0-00010101000000-000000000000 diff --git a/extras/scion-telegram/internal/telegram/api.go b/extras/scion-telegram/internal/telegram/api.go index ba3e03ef0..5e72403be 100644 --- a/extras/scion-telegram/internal/telegram/api.go +++ b/extras/scion-telegram/internal/telegram/api.go @@ -187,9 +187,10 @@ type CallbackQuery struct { // sendMessageRequest is the JSON body for the sendMessage API call. type sendMessageRequest struct { - ChatID int64 `json:"chat_id"` - Text string `json:"text"` - ParseMode string `json:"parse_mode,omitempty"` + ChatID int64 `json:"chat_id"` + Text string `json:"text"` + ParseMode string `json:"parse_mode,omitempty"` + MessageThreadID int64 `json:"message_thread_id,omitempty"` } // sendMessageWithKeyboardRequest is the JSON body for sendMessage with an inline keyboard. @@ -199,6 +200,7 @@ type sendMessageWithKeyboardRequest struct { ParseMode string `json:"parse_mode,omitempty"` ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` ReplyToMessageID int64 `json:"reply_to_message_id,omitempty"` + MessageThreadID int64 `json:"message_thread_id,omitempty"` } // ForceReply instructs Telegram clients to display a reply interface to the @@ -211,10 +213,11 @@ type ForceReply struct { // sendMessageForceReplyRequest is the JSON body for sendMessage with // ForceReply markup and an optional inline keyboard. type sendMessageForceReplyRequest struct { - ChatID int64 `json:"chat_id"` - Text string `json:"text"` - ParseMode string `json:"parse_mode,omitempty"` - ReplyMarkup json.RawMessage `json:"reply_markup,omitempty"` + ChatID int64 `json:"chat_id"` + Text string `json:"text"` + ParseMode string `json:"parse_mode,omitempty"` + ReplyMarkup json.RawMessage `json:"reply_markup,omitempty"` + MessageThreadID int64 `json:"message_thread_id,omitempty"` } // editMessageTextRequest is the JSON body for the editMessageText API call. @@ -458,13 +461,21 @@ func (c *TelegramAPIClient) GetUpdates(ctx context.Context, offset int64, timeou return updates, nil } +// SendOption provides optional parameters for send methods. +type SendOption struct { + MessageThreadID int64 +} + // SendMessage sends a text message to the specified chat. -func (c *TelegramAPIClient) SendMessage(ctx context.Context, chatID int64, text, parseMode string) (*TGMessage, error) { +func (c *TelegramAPIClient) SendMessage(ctx context.Context, chatID int64, text, parseMode string, opts ...SendOption) (*TGMessage, error) { body := sendMessageRequest{ ChatID: chatID, Text: text, ParseMode: parseMode, } + for _, o := range opts { + body.MessageThreadID = o.MessageThreadID + } jsonBody, err := json.Marshal(body) if err != nil { @@ -506,7 +517,7 @@ func (c *TelegramAPIClient) SendMessage(ctx context.Context, chatID int64, text, } // SendMessageWithKeyboard sends a text message with an inline keyboard and optional reply. -func (c *TelegramAPIClient) SendMessageWithKeyboard(ctx context.Context, chatID int64, text, parseMode string, keyboard *InlineKeyboardMarkup, replyToMessageID int64) (*TGMessage, error) { +func (c *TelegramAPIClient) SendMessageWithKeyboard(ctx context.Context, chatID int64, text, parseMode string, keyboard *InlineKeyboardMarkup, replyToMessageID int64, opts ...SendOption) (*TGMessage, error) { body := sendMessageWithKeyboardRequest{ ChatID: chatID, Text: text, @@ -514,6 +525,9 @@ func (c *TelegramAPIClient) SendMessageWithKeyboard(ctx context.Context, chatID ReplyMarkup: keyboard, ReplyToMessageID: replyToMessageID, } + for _, o := range opts { + body.MessageThreadID = o.MessageThreadID + } jsonBody, err := json.Marshal(body) if err != nil { diff --git a/extras/scion-telegram/internal/telegram/broker_v2.go b/extras/scion-telegram/internal/telegram/broker_v2.go index c5372f88c..7603ac680 100644 --- a/extras/scion-telegram/internal/telegram/broker_v2.go +++ b/extras/scion-telegram/internal/telegram/broker_v2.go @@ -689,21 +689,28 @@ func (b *TelegramBrokerV2) Publish(ctx context.Context, topic string, msg *messa } } + // Determine thread ID for Telegram forum topics. + var threadOpts []SendOption + if msg != nil && msg.ThreadID != "" { + if tid, err := strconv.ParseInt(msg.ThreadID, 10, 64); err == nil && tid != 0 { + threadOpts = append(threadOpts, SendOption{MessageThreadID: tid}) + } + } + var errs []error for _, chatID := range chatIDs { var err error if sq != nil { var keyboard *InlineKeyboardMarkup if replyToMsgID > 0 { - // Pass nil keyboard but use replyTo. - _, err = sq.Send(ctx, chatID, text, "", keyboard, replyToMsgID) + _, err = sq.Send(ctx, chatID, text, "", keyboard, replyToMsgID, threadOpts...) } else { - _, err = sq.Send(ctx, chatID, text, "", nil, 0) + _, err = sq.Send(ctx, chatID, text, "", nil, 0, threadOpts...) } } else if replyToMsgID > 0 { - _, err = api.SendMessageWithKeyboard(ctx, chatID, text, "", nil, replyToMsgID) + _, err = api.SendMessageWithKeyboard(ctx, chatID, text, "", nil, replyToMsgID, threadOpts...) } else { - _, err = api.SendMessage(ctx, chatID, text, "") + _, err = api.SendMessage(ctx, chatID, text, "", threadOpts...) } if err != nil { var apiErr *APIError @@ -1535,8 +1542,18 @@ func (b *TelegramBrokerV2) handleGroupMessage(tgMsg *TGMessage) { } b.mu.RUnlock() + // Resolve effective default agent: topic-level override first, then chat-level. + effectiveDefault := link.DefaultAgent + if tgMsg.MessageThreadID != 0 { + if topicDefault, err := b.store.GetTopicDefault(ctx, chatID, tgMsg.MessageThreadID); err != nil { + b.log.Error("Failed to get topic default", "error", err) + } else if topicDefault != "" { + effectiveDefault = topicDefault + } + } + // Resolve target agents from @-mentions. - targets, isAll := resolveTargetAgents(tgMsg, botUsername, link.DefaultAgent, agents) + targets, isAll := resolveTargetAgents(tgMsg, botUsername, effectiveDefault, agents) // Fallback 1: reply-to-bot-message — extract the agent from the replied-to message. if len(targets) == 0 && tgMsg.ReplyToMessage != nil { @@ -1590,13 +1607,13 @@ func (b *TelegramBrokerV2) handleGroupMessage(tgMsg *TGMessage) { // Telegram user — that's a user-to-user message. Mentions embedded // later (offset>0) do not block default routing; resolveUserMentions // injects the resolved scion identity for those. - if len(targets) == 0 && link.DefaultAgent != "" { + if len(targets) == 0 && effectiveDefault != "" { hasAttachment := tgMsg.Photo != nil || tgMsg.Document != nil text := strings.TrimSpace(tgMsg.Text) textRoutes := text != "" && !strings.HasPrefix(text, "/") && !strings.HasPrefix(text, "@") && !hasNonBotUserMention(tgMsg, botUsername, agents) if textRoutes || hasAttachment { - b.log.Debug("Using default agent", "agent", link.DefaultAgent) - targets = []string{link.DefaultAgent} + b.log.Debug("Using default agent", "agent", effectiveDefault) + targets = []string{effectiveDefault} } } diff --git a/extras/scion-telegram/internal/telegram/callbacks.go b/extras/scion-telegram/internal/telegram/callbacks.go index 6dfad5ddb..6bbad9386 100644 --- a/extras/scion-telegram/internal/telegram/callbacks.go +++ b/extras/scion-telegram/internal/telegram/callbacks.go @@ -83,6 +83,19 @@ func (h *CallbackHandler) HandleCallback(ctx context.Context, cb *CallbackQuery) return nil, nil } + if strings.HasPrefix(cb.Data, callbackLookupPrefix) { + shortID := strings.TrimPrefix(cb.Data, callbackLookupPrefix) + lookup, err := h.store.GetCallbackLookup(ctx, shortID) + if err != nil { + return nil, fmt.Errorf("failed to resolve callback lookup %s: %w", shortID, err) + } + if lookup == nil { + h.answerCallback(ctx, cb.ID, "This button has expired. Please try the command again.", false) + return nil, nil + } + cb.Data = lookup.FullData + } + parts := strings.SplitN(cb.Data, ":", 4) if len(parts) < 2 { return nil, fmt.Errorf("invalid callback data: %s", cb.Data) @@ -324,12 +337,22 @@ func (h *CallbackHandler) handleDefaultCallback(ctx context.Context, cb *Callbac messageID = cb.Message.MessageID } + // Parse optional thread ID for topic-scoped defaults. + var threadID int64 + if len(parts) >= 2 { + threadID, _ = strconv.ParseInt(parts[1], 10, 64) + } + link, err := h.store.GetGroupLink(ctx, chatID) if err != nil || link == nil { h.answerCallback(ctx, cb.ID, "Group is not linked to a project.", false) return err } + if threadID != 0 { + return h.handleTopicDefaultCallback(ctx, cb, chatID, messageID, threadID, agentSlug, link) + } + if agentSlug == "__none__" { link.DefaultAgent = "" } else { @@ -352,6 +375,32 @@ func (h *CallbackHandler) handleDefaultCallback(ctx context.Context, cb *Callbac return nil } +func (h *CallbackHandler) handleTopicDefaultCallback(ctx context.Context, cb *CallbackQuery, chatID, messageID, threadID int64, agentSlug string, link *GroupLink) error { + if agentSlug == "__none__" { + if err := h.store.DeleteTopicDefault(ctx, chatID, threadID); err != nil { + h.log.Error("Failed to delete topic default", "chat_id", chatID, "thread_id", threadID, "error", err) + h.answerCallback(ctx, cb.ID, "Failed to update topic default.", false) + return err + } + fallbackMsg := "Topic default removed." + if link.DefaultAgent != "" { + fallbackMsg += fmt.Sprintf(" Messages will use the chat default (@%s).", link.DefaultAgent) + } + h.editMessage(ctx, chatID, messageID, fallbackMsg, nil) + h.answerCallback(ctx, cb.ID, "Topic default: none", false) + } else { + if err := h.store.SetTopicDefault(ctx, chatID, threadID, agentSlug); err != nil { + h.log.Error("Failed to set topic default", "chat_id", chatID, "thread_id", threadID, "error", err) + h.answerCallback(ctx, cb.ID, "Failed to update topic default.", false) + return err + } + h.editMessage(ctx, chatID, messageID, + fmt.Sprintf("Default agent for this topic set to @%s.", agentSlug), nil) + h.answerCallback(ctx, cb.ID, fmt.Sprintf("Topic default: @%s", agentSlug), false) + } + return nil +} + func (h *CallbackHandler) handleAskCallback(ctx context.Context, cb *CallbackQuery, parts []string) (*AskUserResponse, error) { if len(parts) < 2 { return nil, fmt.Errorf("invalid ask callback data") diff --git a/extras/scion-telegram/internal/telegram/cards.go b/extras/scion-telegram/internal/telegram/cards.go index ee3b530a1..cfe24a73e 100644 --- a/extras/scion-telegram/internal/telegram/cards.go +++ b/extras/scion-telegram/internal/telegram/cards.go @@ -14,7 +14,15 @@ package telegram -import "fmt" +import ( + "context" + "crypto/rand" + "encoding/hex" + "fmt" + "log/slog" + "strconv" + "time" +) // ProjectOption represents a project choice for keyboard selection. type ProjectOption struct { @@ -69,6 +77,11 @@ func buildProjectSelectionKeyboard(projects []ProjectOption) *InlineKeyboardMark // Callback data format: setup:dflt: func buildAgentSelectionKeyboard(agents []string, currentDefault string) *InlineKeyboardMarkup { kb := buildAgentKeyboard(agents, currentDefault, "setup:dflt") + for i, row := range kb.InlineKeyboard { + for j, btn := range row { + kb.InlineKeyboard[i][j].CallbackData = truncateCallback(btn.CallbackData) + } + } kb.InlineKeyboard = append(kb.InlineKeyboard, []InlineKeyboardButton{ {Text: "No default agent", CallbackData: "setup:dflt:"}, }) @@ -76,15 +89,34 @@ func buildAgentSelectionKeyboard(agents []string, currentDefault string) *Inline } // buildDefaultAgentKeyboard creates an inline keyboard for /default command. -// Callback data format: dflt: -func buildDefaultAgentKeyboard(agents []string, currentDefault string) *InlineKeyboardMarkup { - kb := buildAgentKeyboard(agents, currentDefault, "dflt") +// Callback data format: dflt: or dflt:: for topic-scoped defaults. +// When the callback data exceeds Telegram's 64-byte limit, it is stored in +// callback_lookups and replaced with a short cblu: reference. +func buildDefaultAgentKeyboard(ctx context.Context, store Store, agents []string, currentDefault string, threadID int64) *InlineKeyboardMarkup { + suffix := "" + if threadID != 0 { + suffix = ":" + strconv.FormatInt(threadID, 10) + } + prefix := "dflt" + kb := buildAgentKeyboard(agents, currentDefault, prefix) + for i, row := range kb.InlineKeyboard { + for j, btn := range row { + kb.InlineKeyboard[i][j].CallbackData = callbackOrLookup(ctx, store, btn.CallbackData+suffix) + } + } noneLabel := "No default agent" + if threadID != 0 { + noneLabel = "No default agent (use chat default)" + } if currentDefault == "" { - noneLabel = "✓ No default agent (current)" + if threadID != 0 { + noneLabel = "✓ No default agent (current, using chat default)" + } else { + noneLabel = "✓ No default agent (current)" + } } kb.InlineKeyboard = append(kb.InlineKeyboard, []InlineKeyboardButton{ - {Text: noneLabel, CallbackData: "dflt:__none__"}, + {Text: noneLabel, CallbackData: callbackOrLookup(ctx, store, "dflt:__none__"+suffix)}, }) return kb } @@ -100,7 +132,7 @@ func buildAgentKeyboard(agents []string, currentDefault string, prefix string) * } btn := InlineKeyboardButton{ Text: label, - CallbackData: truncateCallback(fmt.Sprintf("%s:%s", prefix, agent)), + CallbackData: fmt.Sprintf("%s:%s", prefix, agent), } row = append(row, btn) if len(row) == 2 { @@ -240,5 +272,40 @@ func truncateCallback(data string) string { if len(data) <= maxCallbackData { return data } + slog.Warn("callback_data exceeds 64-byte Telegram limit, truncating", + "len", len(data), "data", data) return data[:maxCallbackData] } + +// callbackLookupPrefix identifies callback data that is stored in the +// callback_lookups table rather than inline. +const callbackLookupPrefix = "cblu:" + +// callbackLookupTTL is how long a stored callback lookup remains valid. +const callbackLookupTTL = 24 * time.Hour + +// callbackOrLookup returns data as-is when it fits within Telegram's 64-byte +// callback_data limit. When data exceeds the limit, the full payload is +// persisted via the store's callback_lookups table and a short cblu: +// reference is returned instead. +func callbackOrLookup(ctx context.Context, store Store, data string) string { + if len(data) <= maxCallbackData { + return data + } + idBytes := make([]byte, 8) + if _, err := rand.Read(idBytes); err != nil { + slog.Error("failed to generate callback lookup ID", "error", err) + return truncateCallback(data) + } + shortID := hex.EncodeToString(idBytes) + lookup := &CallbackLookup{ + ShortID: shortID, + FullData: data, + ExpiresAt: time.Now().Add(callbackLookupTTL), + } + if err := store.SaveCallbackLookup(ctx, lookup); err != nil { + slog.Error("failed to save callback lookup", "error", err, "data", data) + return truncateCallback(data) + } + return callbackLookupPrefix + shortID +} diff --git a/extras/scion-telegram/internal/telegram/cards_test.go b/extras/scion-telegram/internal/telegram/cards_test.go index c813c9dc5..aa0980a7d 100644 --- a/extras/scion-telegram/internal/telegram/cards_test.go +++ b/extras/scion-telegram/internal/telegram/cards_test.go @@ -15,7 +15,9 @@ package telegram import ( + "context" "fmt" + "strings" "testing" "github.com/stretchr/testify/assert" @@ -96,13 +98,44 @@ func TestBuildAgentSelectionKeyboard_NoDefault(t *testing.T) { } func TestBuildDefaultAgentKeyboard_CallbackFormat(t *testing.T) { - kb := buildDefaultAgentKeyboard([]string{"coder", "reviewer"}, "coder") + store := newTestStore(t) + kb := buildDefaultAgentKeyboard(context.Background(), store, []string{"coder", "reviewer"}, "coder", 0) assert.Equal(t, "dflt:coder", kb.InlineKeyboard[0][0].CallbackData) assert.Equal(t, "✓ coder (current)", kb.InlineKeyboard[0][0].Text) assert.Equal(t, "dflt:reviewer", kb.InlineKeyboard[0][1].CallbackData) assert.Equal(t, "reviewer", kb.InlineKeyboard[0][1].Text) } +func TestBuildDefaultAgentKeyboard_TopicScoped(t *testing.T) { + store := newTestStore(t) + kb := buildDefaultAgentKeyboard(context.Background(), store, []string{"coder", "reviewer"}, "coder", 42) + assert.Equal(t, "dflt:coder:42", kb.InlineKeyboard[0][0].CallbackData) + assert.Equal(t, "✓ coder (current)", kb.InlineKeyboard[0][0].Text) + assert.Equal(t, "dflt:reviewer:42", kb.InlineKeyboard[0][1].CallbackData) + + lastRow := kb.InlineKeyboard[len(kb.InlineKeyboard)-1] + assert.Equal(t, "dflt:__none__:42", lastRow[0].CallbackData) + assert.Contains(t, lastRow[0].Text, "use chat default") +} + +func TestBuildDefaultAgentKeyboard_LongSlugUsesLookup(t *testing.T) { + store := newTestStore(t) + ctx := context.Background() + longSlug := "this-is-a-very-long-agent-slug-that-will-exceed-sixty-four-bytes" + kb := buildDefaultAgentKeyboard(ctx, store, []string{longSlug}, "", 99999999) + + cbData := kb.InlineKeyboard[0][0].CallbackData + assert.True(t, strings.HasPrefix(cbData, callbackLookupPrefix), + "expected callback lookup prefix, got %q", cbData) + assert.LessOrEqual(t, len(cbData), maxCallbackData) + + shortID := strings.TrimPrefix(cbData, callbackLookupPrefix) + lookup, err := store.GetCallbackLookup(ctx, shortID) + require.NoError(t, err) + require.NotNil(t, lookup) + assert.Equal(t, fmt.Sprintf("dflt:%s:99999999", longSlug), lookup.FullData) +} + func TestBuildAskUserKeyboard_WithChoices(t *testing.T) { kb := buildAskUserKeyboard("req-42", []string{"Option A", "Option B", "Option C"}) require.Len(t, kb.InlineKeyboard, 2) diff --git a/extras/scion-telegram/internal/telegram/commands.go b/extras/scion-telegram/internal/telegram/commands.go index f5fc77123..bc92df71c 100644 --- a/extras/scion-telegram/internal/telegram/commands.go +++ b/extras/scion-telegram/internal/telegram/commands.go @@ -193,6 +193,7 @@ func (h *CommandHandler) handleSetup(msg *TGMessage) { func (h *CommandHandler) handleDefault(msg *TGMessage) { chatID := msg.Chat.ID + threadID := msg.MessageThreadID ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() @@ -222,8 +223,24 @@ func (h *CommandHandler) handleDefault(msg *TGMessage) { return } - kb := buildDefaultAgentKeyboard(agentSlugs(agents), link.DefaultAgent) - h.replyWithKeyboard(chatID, "Select the default agent for @-mentions:", kb) + promptText := "Select the default agent for @-mentions:" + currentDefault := link.DefaultAgent + + if threadID != 0 { + topicDefault, err := h.store.GetTopicDefault(ctx, chatID, threadID) + if err != nil { + h.log.Error("Failed to get topic default", "error", err) + } else if topicDefault != "" { + currentDefault = topicDefault + } + promptText = "Select the default agent for this topic:" + if link.DefaultAgent != "" { + promptText += fmt.Sprintf("\nChat-wide default: @%s", link.DefaultAgent) + } + } + + kb := buildDefaultAgentKeyboard(ctx, h.store, agentSlugs(agents), currentDefault, threadID) + h.replyWithKeyboardInThread(chatID, threadID, promptText, kb) } func (h *CommandHandler) handleAgents(msg *TGMessage) { @@ -575,6 +592,18 @@ func (h *CommandHandler) replyWithKeyboard(chatID int64, text string, kb *Inline } } +func (h *CommandHandler) replyWithKeyboardInThread(chatID int64, threadID int64, text string, kb *InlineKeyboardMarkup) { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + var opts []SendOption + if threadID != 0 { + opts = append(opts, SendOption{MessageThreadID: threadID}) + } + if _, err := h.api.SendMessageWithKeyboard(ctx, chatID, text, "", kb, 0, opts...); err != nil { + h.log.Error("Failed to send reply with keyboard", "chat_id", chatID, "error", err) + } +} + func isGroupChat(chatID int64) bool { return chatID < 0 } // --- httpHubClient --- diff --git a/extras/scion-telegram/internal/telegram/sendqueue.go b/extras/scion-telegram/internal/telegram/sendqueue.go index 005bf0240..ad339d4ab 100644 --- a/extras/scion-telegram/internal/telegram/sendqueue.go +++ b/extras/scion-telegram/internal/telegram/sendqueue.go @@ -51,12 +51,13 @@ type chatQueue struct { // outboundMessage represents a message waiting to be sent through the queue. type outboundMessage struct { - chatID int64 - text string - parseMode string - keyboard *InlineKeyboardMarkup - replyTo int64 - result chan<- *sendResult // caller blocks on this to receive the outcome + chatID int64 + text string + parseMode string + keyboard *InlineKeyboardMarkup + replyTo int64 + messageThreadID int64 + result chan<- *sendResult // caller blocks on this to receive the outcome } // sendResult carries the outcome of a queued send back to the caller. @@ -88,7 +89,7 @@ func NewSendQueue(api *TelegramAPIClient, log *slog.Logger, maxSize int, minDela // Send enqueues a message and blocks until it is sent (or fails). // It returns the Telegram API response or an error. -func (sq *SendQueue) Send(ctx context.Context, chatID int64, text, parseMode string, keyboard *InlineKeyboardMarkup, replyTo int64) (*TGMessage, error) { +func (sq *SendQueue) Send(ctx context.Context, chatID int64, text, parseMode string, keyboard *InlineKeyboardMarkup, replyTo int64, opts ...SendOption) (*TGMessage, error) { resultCh := make(chan *sendResult, 1) om := &outboundMessage{ @@ -99,6 +100,9 @@ func (sq *SendQueue) Send(ctx context.Context, chatID int64, text, parseMode str replyTo: replyTo, result: resultCh, } + for _, o := range opts { + om.messageThreadID = o.MessageThreadID + } ch, err := sq.enqueue(chatID, om) if err != nil { @@ -235,10 +239,15 @@ func (sq *SendQueue) sendOne(om *outboundMessage) (*TGMessage, error) { ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() + var opts []SendOption + if om.messageThreadID != 0 { + opts = append(opts, SendOption{MessageThreadID: om.messageThreadID}) + } + if om.keyboard != nil || om.replyTo > 0 { - return sq.api.SendMessageWithKeyboard(ctx, om.chatID, om.text, om.parseMode, om.keyboard, om.replyTo) + return sq.api.SendMessageWithKeyboard(ctx, om.chatID, om.text, om.parseMode, om.keyboard, om.replyTo, opts...) } - return sq.api.SendMessage(ctx, om.chatID, om.text, om.parseMode) + return sq.api.SendMessage(ctx, om.chatID, om.text, om.parseMode, opts...) } // removeQueue removes the per-chat queue from the map when the worker exits. diff --git a/extras/scion-telegram/internal/telegram/store.go b/extras/scion-telegram/internal/telegram/store.go index ecca641fc..d99972ea8 100644 --- a/extras/scion-telegram/internal/telegram/store.go +++ b/extras/scion-telegram/internal/telegram/store.go @@ -67,6 +67,11 @@ type Store interface { GetNotificationPrefs(ctx context.Context, telegramUserID string) ([]*NotificationPref, error) GetNotificationPref(ctx context.Context, telegramUserID, projectID, agentSlug string) (*NotificationPref, error) + // TopicDefault — per-topic default agent overrides for forum groups + GetTopicDefault(ctx context.Context, chatID int64, threadID int64) (string, error) + SetTopicDefault(ctx context.Context, chatID int64, threadID int64, agentSlug string) error + DeleteTopicDefault(ctx context.Context, chatID int64, threadID int64) error + // Lifecycle Close() error } @@ -234,6 +239,13 @@ CREATE TABLE IF NOT EXISTS notification_prefs ( enabled INTEGER NOT NULL DEFAULT 1, PRIMARY KEY (telegram_user_id, project_id, agent_slug) ); + +CREATE TABLE IF NOT EXISTS topic_defaults ( + chat_id INTEGER NOT NULL, + thread_id INTEGER NOT NULL, + agent_slug TEXT NOT NULL, + PRIMARY KEY (chat_id, thread_id) +); ` if _, err := s.db.Exec(ddl); err != nil { return err @@ -611,6 +623,32 @@ func (s *sqliteStore) GetNotificationPref(ctx context.Context, telegramUserID, p return &p, nil } +// --- TopicDefault --- + +func (s *sqliteStore) GetTopicDefault(ctx context.Context, chatID int64, threadID int64) (string, error) { + const q = `SELECT agent_slug FROM topic_defaults WHERE chat_id = ? AND thread_id = ?` + var agentSlug string + err := s.db.QueryRowContext(ctx, q, chatID, threadID).Scan(&agentSlug) + if err == sql.ErrNoRows { + return "", nil + } + return agentSlug, err +} + +func (s *sqliteStore) SetTopicDefault(ctx context.Context, chatID int64, threadID int64, agentSlug string) error { + const q = ` +INSERT INTO topic_defaults (chat_id, thread_id, agent_slug) +VALUES (?, ?, ?) +ON CONFLICT(chat_id, thread_id) DO UPDATE SET agent_slug=excluded.agent_slug` + _, err := s.db.ExecContext(ctx, q, chatID, threadID, agentSlug) + return err +} + +func (s *sqliteStore) DeleteTopicDefault(ctx context.Context, chatID int64, threadID int64) error { + _, err := s.db.ExecContext(ctx, `DELETE FROM topic_defaults WHERE chat_id = ? AND thread_id = ?`, chatID, threadID) + return err +} + // --- scan helpers --- func scanGroupLink(row *sql.Row) (*GroupLink, error) { diff --git a/extras/scion-telegram/internal/telegram/store_test.go b/extras/scion-telegram/internal/telegram/store_test.go index 864960e94..da4786a5a 100644 --- a/extras/scion-telegram/internal/telegram/store_test.go +++ b/extras/scion-telegram/internal/telegram/store_test.go @@ -779,6 +779,77 @@ func TestStore_GroupLink_NotifyInGroup(t *testing.T) { assert.False(t, got.NotifyInGroup) } +// --- TopicDefault --- + +func TestStore_TopicDefault_SetAndGet(t *testing.T) { + store := newTestStore(t) + ctx := context.Background() + + // Not found returns empty string. + slug, err := store.GetTopicDefault(ctx, -100, 42) + require.NoError(t, err) + assert.Equal(t, "", slug) + + // Set and retrieve. + require.NoError(t, store.SetTopicDefault(ctx, -100, 42, "coder")) + slug, err = store.GetTopicDefault(ctx, -100, 42) + require.NoError(t, err) + assert.Equal(t, "coder", slug) + + // Different thread returns empty. + slug, err = store.GetTopicDefault(ctx, -100, 99) + require.NoError(t, err) + assert.Equal(t, "", slug) +} + +func TestStore_TopicDefault_Upsert(t *testing.T) { + store := newTestStore(t) + ctx := context.Background() + + require.NoError(t, store.SetTopicDefault(ctx, -100, 42, "coder")) + require.NoError(t, store.SetTopicDefault(ctx, -100, 42, "reviewer")) + + slug, err := store.GetTopicDefault(ctx, -100, 42) + require.NoError(t, err) + assert.Equal(t, "reviewer", slug) +} + +func TestStore_TopicDefault_Delete(t *testing.T) { + store := newTestStore(t) + ctx := context.Background() + + require.NoError(t, store.SetTopicDefault(ctx, -100, 42, "coder")) + require.NoError(t, store.DeleteTopicDefault(ctx, -100, 42)) + + slug, err := store.GetTopicDefault(ctx, -100, 42) + require.NoError(t, err) + assert.Equal(t, "", slug) + + // Delete non-existent is not an error. + require.NoError(t, store.DeleteTopicDefault(ctx, -100, 99)) +} + +func TestStore_TopicDefault_MultipleTopics(t *testing.T) { + store := newTestStore(t) + ctx := context.Background() + + require.NoError(t, store.SetTopicDefault(ctx, -100, 1, "coder")) + require.NoError(t, store.SetTopicDefault(ctx, -100, 2, "reviewer")) + require.NoError(t, store.SetTopicDefault(ctx, -200, 1, "designer")) + + slug, err := store.GetTopicDefault(ctx, -100, 1) + require.NoError(t, err) + assert.Equal(t, "coder", slug) + + slug, err = store.GetTopicDefault(ctx, -100, 2) + require.NoError(t, err) + assert.Equal(t, "reviewer", slug) + + slug, err = store.GetTopicDefault(ctx, -200, 1) + require.NoError(t, err) + assert.Equal(t, "designer", slug) +} + // --- Store lifecycle --- func TestStore_OpenInvalidPath(t *testing.T) { diff --git a/pkg/eventbus/fanout.go b/pkg/eventbus/fanout.go index b4020228f..c0654bf40 100644 --- a/pkg/eventbus/fanout.go +++ b/pkg/eventbus/fanout.go @@ -33,6 +33,12 @@ type NamedEventBus struct { Name string Bus EventBus Observer bool + // ChannelID overrides Name for channel-based message routing. When a + // message has msg.Channel set, the FanOutEventBus matches it against + // ChannelID (if non-empty) or Name. This allows a plugin registered + // under one name (e.g. "chat-app") to handle a different channel + // identifier (e.g. "gchat"). + ChannelID string } // FanOutEventBus implements EventBus by delegating to N child event buses. @@ -58,10 +64,15 @@ func (f *FanOutEventBus) Publish(ctx context.Context, topic string, msg *message var inproc, target *NamedEventBus for i := range f.buses { - switch f.buses[i].Name { - case InProcessBusName: + if f.buses[i].Name == InProcessBusName { inproc = &f.buses[i] - case msg.Channel: + continue + } + channelKey := f.buses[i].ChannelID + if channelKey == "" { + channelKey = f.buses[i].Name + } + if msg != nil && channelKey == msg.Channel { target = &f.buses[i] } } @@ -147,8 +158,9 @@ func (f *FanOutEventBus) Close() error { // BusChannel describes a registered event bus channel. type BusChannel struct { - Name string - Observer bool + Name string + Observer bool + ChannelID string } // BusChannels returns the list of registered bus names (excluding InProcessBus). @@ -159,8 +171,9 @@ func (f *FanOutEventBus) BusChannels() []BusChannel { continue } channels = append(channels, BusChannel{ - Name: nb.Name, - Observer: nb.Observer, + Name: nb.Name, + Observer: nb.Observer, + ChannelID: nb.ChannelID, }) } return channels diff --git a/pkg/eventbus/fanout_test.go b/pkg/eventbus/fanout_test.go index 851551ecb..249a1773e 100644 --- a/pkg/eventbus/fanout_test.go +++ b/pkg/eventbus/fanout_test.go @@ -389,6 +389,35 @@ func TestFanOutEventBus_ChannelRoutingObserverError(t *testing.T) { } } +func TestFanOutEventBus_ChannelRoutingWithChannelID(t *testing.T) { + inproc := newStubEventBus() + chatApp := newStubEventBus() + + fan := NewFanOutEventBus([]NamedEventBus{ + {Name: InProcessBusName, Bus: inproc}, + {Name: "chat-app", Bus: chatApp, ChannelID: "gchat"}, + }, slog.Default()) + + msg := messages.NewInstruction("agent:bot", "user:alice", "hello") + msg.Channel = "gchat" + + if err := fan.Publish(context.Background(), "test.topic", msg); err != nil { + t.Fatalf("unexpected error: %v", err) + } + + inproc.mu.Lock() + if len(inproc.published) != 1 { + t.Errorf("inprocess bus: expected 1 message, got %d", len(inproc.published)) + } + inproc.mu.Unlock() + + chatApp.mu.Lock() + if len(chatApp.published) != 1 { + t.Errorf("chat-app bus: expected 1 message (routed via ChannelID), got %d", len(chatApp.published)) + } + chatApp.mu.Unlock() +} + func TestFanOutEventBus_Subscribe(t *testing.T) { b1 := newStubEventBus() b2 := newStubEventBus() diff --git a/pkg/hub/resource_import_handler_test.go b/pkg/hub/resource_import_handler_test.go index dbd0a55b1..98c521d78 100644 --- a/pkg/hub/resource_import_handler_test.go +++ b/pkg/hub/resource_import_handler_test.go @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +//go:build !no_sqlite + package hub import ( diff --git a/pkg/plugin/config.go b/pkg/plugin/config.go index 5bbba2490..35c455856 100644 --- a/pkg/plugin/config.go +++ b/pkg/plugin/config.go @@ -74,6 +74,14 @@ type PluginInfo struct { // Scion logs a warning if the plugin targets a newer version. MinScionVersion string + // ChannelID is the message channel identifier this broker plugin handles. + // When set, the FanOutEventBus uses this value (instead of the plugin's + // registered name) to route outbound messages with a matching + // msg.Channel field. For example, a plugin registered as "chat-app" can + // set ChannelID to "gchat" so that messages with Channel="gchat" are + // routed to it. + ChannelID string + // Capabilities lists optional capabilities the plugin supports. Capabilities []string } From d54278df53f2b4f0233396498e18d001c3979b9f Mon Sep 17 00:00:00 2001 From: Preston Holmes Date: Tue, 2 Jun 2026 08:47:04 -0700 Subject: [PATCH 002/155] Fix test suite leaking Hub credentials, corrupting agent state (#296) * Fix test suite leaking Hub credentials, corrupting agent state (#123) Tests that spawn sciontool (e.g., TestInitCommand_Integration) inherited live Hub env vars from the agent container, causing the subprocess to talk to the real Hub and reset the agent phase to "starting." - Add scrubHubEnv(t) helpers that use t.Setenv to clear Hub env vars (SCION_HUB_ENDPOINT, SCION_HUB_URL, SCION_AUTH_TOKEN, SCION_AGENT_ID, SCION_AGENT_MODE) with automatic restore on test cleanup - Filter Hub env vars from subprocess Cmd.Env in TestInitCommand_Integration as belt-and-suspenders protection - Convert os.Setenv/os.Unsetenv to t.Setenv throughout hub_test.go and client_test.go for crash-safe env var isolation * Add project log entry for issue #123 fix * Address PR #296 review feedback in init_test.go Replace hardcoded /tmp/sciontool-test path with t.TempDir() to avoid permission conflicts and test races. Replace map allocation in filterHubEnv with slices.Contains on the static hubEnvVars slice. --- ...2026-06-02-fix-test-hub-credential-leak.md | 25 +++ cmd/sciontool/commands/init_test.go | 48 ++++- pkg/sciontool/hooks/handlers/hub_test.go | 198 +++++++----------- pkg/sciontool/hub/client_test.go | 161 ++++++-------- 4 files changed, 205 insertions(+), 227 deletions(-) create mode 100644 .design/project-log/2026-06-02-fix-test-hub-credential-leak.md diff --git a/.design/project-log/2026-06-02-fix-test-hub-credential-leak.md b/.design/project-log/2026-06-02-fix-test-hub-credential-leak.md new file mode 100644 index 000000000..0678b1276 --- /dev/null +++ b/.design/project-log/2026-06-02-fix-test-hub-credential-leak.md @@ -0,0 +1,25 @@ +# Fix: Test suite leaking Hub credentials (issue #123) + +**Date**: 2026-06-02 +**PR**: #125 +**Issue**: #123 + +## Problem + +When `go test` runs inside an agent container, tests inherit live Hub env vars. `TestInitCommand_Integration` builds and spawns a real sciontool binary that inherits these vars, causing the child process to report status to the real Hub and corrupt agent state (resetting phase to "starting"). This is how dev-issue-71b got stuck. + +## Fix + +1. Added `scrubHubEnv(t)` helpers using `t.Setenv` for automatic cleanup in: + - `cmd/sciontool/commands/init_test.go` (primary subprocess fix) + - `pkg/sciontool/hooks/handlers/hub_test.go` (env var hygiene) + - `pkg/sciontool/hub/client_test.go` (env var hygiene) + +2. Added `filterHubEnv(env)` to explicitly strip Hub vars from subprocess environments. + +3. Converted all `os.Setenv`/`os.Unsetenv` patterns to `t.Setenv` in hub-related test files for crash-safe env isolation. + +## Observations + +- The Hub env var list (`SCION_HUB_ENDPOINT`, `SCION_HUB_URL`, `SCION_AUTH_TOKEN`, `SCION_AGENT_ID`, `SCION_AGENT_MODE`) is defined in `pkg/sciontool/hub/client.go:45-56`. The `scrubHubEnv` helpers are inlined in each test file rather than shared, to avoid importing `testing` into production code. +- Pre-existing CI issue: `pkg/hub/resource_import_handler_test.go` has an undefined `mockRoundTripper` symbol that causes `go vet ./...` to fail — not related to this change. diff --git a/cmd/sciontool/commands/init_test.go b/cmd/sciontool/commands/init_test.go index e24da9ac3..c03bfcf92 100644 --- a/cmd/sciontool/commands/init_test.go +++ b/cmd/sciontool/commands/init_test.go @@ -15,6 +15,41 @@ import ( "testing" ) +// hubEnvVars lists the environment variables used by the Hub client. +// Leaking these to a subprocess (e.g., sciontool init) causes the child +// to talk to the real Hub and corrupt agent state. See issue #123. +var hubEnvVars = []string{ + "SCION_HUB_ENDPOINT", + "SCION_HUB_URL", + "SCION_AUTH_TOKEN", + "SCION_AGENT_ID", + "SCION_AGENT_MODE", +} + +// scrubHubEnv clears all Hub-related environment variables for the +// duration of the test, preventing accidental communication with a +// real Hub when tests run inside an agent container. +func scrubHubEnv(t *testing.T) { + t.Helper() + for _, key := range hubEnvVars { + t.Setenv(key, "") + } +} + +// filterHubEnv returns a copy of the environment with all Hub-related +// variables removed. Use when constructing exec.Cmd.Env to prevent +// credential leakage to child processes. +func filterHubEnv(env []string) []string { + var filtered []string + for _, e := range env { + key, _, _ := strings.Cut(e, "=") + if !slices.Contains(hubEnvVars, key) { + filtered = append(filtered, e) + } + } + return filtered +} + // TestInitProjectDataIsolation is a canary test that verifies sciontool source code // does NOT import the pkg/config package, which contains project path resolution logic. // This is a compile-time guarantee that in-container code cannot access project data paths. @@ -135,14 +170,21 @@ func TestInitCommand_Integration(t *testing.T) { t.Skip("skipping integration test in short mode") } + // Clear Hub env vars so the subprocess cannot talk to the real Hub + // and corrupt agent state. See issue #123. + scrubHubEnv(t) + // Build sciontool if needed for integration testing - cmd := exec.Command("go", "build", "-buildvcs=false", "-o", "/tmp/sciontool-test", "../") + binPath := filepath.Join(t.TempDir(), "sciontool-test") + cmd := exec.Command("go", "build", "-buildvcs=false", "-o", binPath, "../") if err := cmd.Run(); err != nil { t.Skipf("failed to build sciontool for integration test: %v", err) } - // Test running a simple command - testCmd := exec.Command("/tmp/sciontool-test", "init", "--", "echo", "hello") + // Test running a simple command — filter Hub env vars from the + // subprocess environment as belt-and-suspenders protection. + testCmd := exec.Command(binPath, "init", "--", "echo", "hello") + testCmd.Env = filterHubEnv(os.Environ()) output, err := testCmd.CombinedOutput() if err != nil { t.Errorf("init command failed: %v\nOutput: %s", err, output) diff --git a/pkg/sciontool/hooks/handlers/hub_test.go b/pkg/sciontool/hooks/handlers/hub_test.go index 1bd5da5e7..817cd31a7 100644 --- a/pkg/sciontool/hooks/handlers/hub_test.go +++ b/pkg/sciontool/hooks/handlers/hub_test.go @@ -16,6 +16,22 @@ import ( "github.com/GoogleCloudPlatform/scion/pkg/sciontool/hooks" ) +// scrubHubEnv clears all Hub-related environment variables for the +// duration of the test, preventing accidental communication with a +// real Hub when tests run inside an agent container. See issue #123. +func scrubHubEnv(t *testing.T) { + t.Helper() + for _, key := range []string{ + "SCION_HUB_ENDPOINT", + "SCION_HUB_URL", + "SCION_AUTH_TOKEN", + "SCION_AGENT_ID", + "SCION_AGENT_MODE", + } { + t.Setenv(key, "") + } +} + // TestHubHandler_EventMapping tests that events are correctly mapped to Hub status updates. func TestHubHandler_EventMapping(t *testing.T) { tests := []struct { @@ -90,9 +106,7 @@ func TestHubHandler_EventMapping(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { tmpHome := t.TempDir() - oldHome := os.Getenv("HOME") - os.Setenv("HOME", tmpHome) - defer os.Setenv("HOME", oldHome) + t.Setenv("HOME", tmpHome) var receivedStatus string var mu sync.Mutex @@ -121,16 +135,11 @@ func TestHubHandler_EventMapping(t *testing.T) { })) defer server.Close() - // Set environment variables for the Hub client - os.Setenv("SCION_HUB_ENDPOINT", server.URL) - os.Setenv("SCION_AUTH_TOKEN", "test-token") - os.Setenv("SCION_AGENT_ID", "test-agent-id") - defer func() { - os.Unsetenv("SCION_HUB_ENDPOINT") - os.Unsetenv("SCION_HUB_URL") - os.Unsetenv("SCION_AUTH_TOKEN") - os.Unsetenv("SCION_AGENT_ID") - }() + // Clear real Hub env, then point at the test server (issue #123). + scrubHubEnv(t) + t.Setenv("SCION_HUB_ENDPOINT", server.URL) + t.Setenv("SCION_AUTH_TOKEN", "test-token") + t.Setenv("SCION_AGENT_ID", "test-agent-id") // Create handler handler := NewHubHandler() @@ -172,11 +181,8 @@ func TestHubHandler_EventMapping(t *testing.T) { // TestHubHandler_NotConfigured tests that nil handler doesn't panic. func TestHubHandler_NotConfigured(t *testing.T) { - // Clear environment to ensure client is not configured - os.Unsetenv("SCION_HUB_ENDPOINT") - os.Unsetenv("SCION_HUB_URL") - os.Unsetenv("SCION_AUTH_TOKEN") - os.Unsetenv("SCION_AGENT_ID") + // Clear environment to ensure client is not configured (issue #123). + scrubHubEnv(t) handler := NewHubHandler() if handler != nil { @@ -206,15 +212,11 @@ func TestHubHandler_ReportMethods(t *testing.T) { })) defer server.Close() - os.Setenv("SCION_HUB_ENDPOINT", server.URL) - os.Setenv("SCION_AUTH_TOKEN", "test-token") - os.Setenv("SCION_AGENT_ID", "test-agent-id") - defer func() { - os.Unsetenv("SCION_HUB_ENDPOINT") - os.Unsetenv("SCION_HUB_URL") - os.Unsetenv("SCION_AUTH_TOKEN") - os.Unsetenv("SCION_AGENT_ID") - }() + // Clear real Hub env, then point at the test server (issue #123). + scrubHubEnv(t) + t.Setenv("SCION_HUB_ENDPOINT", server.URL) + t.Setenv("SCION_AUTH_TOKEN", "test-token") + t.Setenv("SCION_AGENT_ID", "test-agent-id") handler := NewHubHandler() if handler == nil { @@ -377,9 +379,7 @@ func TestHubHandler_StickyStatus(t *testing.T) { os.WriteFile(tmpDir+"/agent-info.json", data, 0644) // Point HOME to the temp dir so readLocalActivity finds our file - origHome := os.Getenv("HOME") - os.Setenv("HOME", tmpDir) - defer os.Setenv("HOME", origHome) + t.Setenv("HOME", tmpDir) var mu sync.Mutex callCount := 0 @@ -400,15 +400,11 @@ func TestHubHandler_StickyStatus(t *testing.T) { })) defer server.Close() - os.Setenv("SCION_HUB_ENDPOINT", server.URL) - os.Setenv("SCION_AUTH_TOKEN", "test-token") - os.Setenv("SCION_AGENT_ID", "test-agent-id") - defer func() { - os.Unsetenv("SCION_HUB_ENDPOINT") - os.Unsetenv("SCION_HUB_URL") - os.Unsetenv("SCION_AUTH_TOKEN") - os.Unsetenv("SCION_AGENT_ID") - }() + // Clear real Hub env, then point at the test server (issue #123). + scrubHubEnv(t) + t.Setenv("SCION_HUB_ENDPOINT", server.URL) + t.Setenv("SCION_AUTH_TOKEN", "test-token") + t.Setenv("SCION_AGENT_ID", "test-agent-id") handler := NewHubHandler() if handler == nil { @@ -447,11 +443,8 @@ func TestHubHandler_StickyStatus(t *testing.T) { // TestHubHandler_ModeBehavior verifies behavior differences between local and hub modes. func TestHubHandler_ModeBehavior(t *testing.T) { t.Run("local mode: HubHandler is nil", func(t *testing.T) { - // Clear hub env vars to simulate local mode - os.Unsetenv("SCION_HUB_ENDPOINT") - os.Unsetenv("SCION_HUB_URL") - os.Unsetenv("SCION_AUTH_TOKEN") - os.Unsetenv("SCION_AGENT_ID") + // Clear hub env vars to simulate local mode (issue #123). + scrubHubEnv(t) handler := NewHubHandler() if handler != nil { @@ -463,15 +456,10 @@ func TestHubHandler_ModeBehavior(t *testing.T) { // Even without a hub, the StatusHandler must write to agent-info.json // for local observability (defense-in-depth). tmpHome := t.TempDir() - origHome := os.Getenv("HOME") - os.Setenv("HOME", tmpHome) - defer os.Setenv("HOME", origHome) + t.Setenv("HOME", tmpHome) - // Clear hub env to ensure local mode - os.Unsetenv("SCION_HUB_ENDPOINT") - os.Unsetenv("SCION_HUB_URL") - os.Unsetenv("SCION_AUTH_TOKEN") - os.Unsetenv("SCION_AGENT_ID") + // Clear hub env to ensure local mode (issue #123). + scrubHubEnv(t) statusHandler := NewStatusHandler() event := &hooks.Event{ @@ -497,9 +485,7 @@ func TestHubHandler_ModeBehavior(t *testing.T) { t.Run("hub mode: HubHandler is active and sends updates", func(t *testing.T) { tmpHome := t.TempDir() - origHome := os.Getenv("HOME") - os.Setenv("HOME", tmpHome) - defer os.Setenv("HOME", origHome) + t.Setenv("HOME", tmpHome) callCount := 0 var mu sync.Mutex @@ -513,14 +499,11 @@ func TestHubHandler_ModeBehavior(t *testing.T) { })) defer server.Close() - os.Setenv("SCION_HUB_ENDPOINT", server.URL) - os.Setenv("SCION_AUTH_TOKEN", "test-token") - os.Setenv("SCION_AGENT_ID", "test-agent") - defer func() { - os.Unsetenv("SCION_HUB_ENDPOINT") - os.Unsetenv("SCION_AUTH_TOKEN") - os.Unsetenv("SCION_AGENT_ID") - }() + // Clear real Hub env, then point at the test server (issue #123). + scrubHubEnv(t) + t.Setenv("SCION_HUB_ENDPOINT", server.URL) + t.Setenv("SCION_AUTH_TOKEN", "test-token") + t.Setenv("SCION_AGENT_ID", "test-agent") handler := NewHubHandler() if handler == nil { @@ -546,9 +529,7 @@ func TestHubHandler_ModeBehavior(t *testing.T) { t.Run("hub mode: StatusHandler still writes agent-info.json", func(t *testing.T) { // In hub mode, StatusHandler should still write locally for defense-in-depth. tmpHome := t.TempDir() - origHome := os.Getenv("HOME") - os.Setenv("HOME", tmpHome) - defer os.Setenv("HOME", origHome) + t.Setenv("HOME", tmpHome) server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) @@ -556,14 +537,11 @@ func TestHubHandler_ModeBehavior(t *testing.T) { })) defer server.Close() - os.Setenv("SCION_HUB_ENDPOINT", server.URL) - os.Setenv("SCION_AUTH_TOKEN", "test-token") - os.Setenv("SCION_AGENT_ID", "test-agent") - defer func() { - os.Unsetenv("SCION_HUB_ENDPOINT") - os.Unsetenv("SCION_AUTH_TOKEN") - os.Unsetenv("SCION_AGENT_ID") - }() + // Clear real Hub env, then point at the test server (issue #123). + scrubHubEnv(t) + t.Setenv("SCION_HUB_ENDPOINT", server.URL) + t.Setenv("SCION_AUTH_TOKEN", "test-token") + t.Setenv("SCION_AGENT_ID", "test-agent") statusHandler := NewStatusHandler() event := &hooks.Event{ @@ -594,9 +572,7 @@ func TestHubHandler_ModeBehavior(t *testing.T) { func TestHubHandler_AssistantTextForwarding(t *testing.T) { t.Run("forwards assistant text to outbound-message endpoint", func(t *testing.T) { tmpHome := t.TempDir() - origHome := os.Getenv("HOME") - os.Setenv("HOME", tmpHome) - defer os.Setenv("HOME", origHome) + t.Setenv("HOME", tmpHome) var mu sync.Mutex var outboundMsg string @@ -624,15 +600,11 @@ func TestHubHandler_AssistantTextForwarding(t *testing.T) { })) defer server.Close() - os.Setenv("SCION_HUB_ENDPOINT", server.URL) - os.Setenv("SCION_AUTH_TOKEN", "test-token") - os.Setenv("SCION_AGENT_ID", "test-agent-id") - defer func() { - os.Unsetenv("SCION_HUB_ENDPOINT") - os.Unsetenv("SCION_HUB_URL") - os.Unsetenv("SCION_AUTH_TOKEN") - os.Unsetenv("SCION_AGENT_ID") - }() + // Clear real Hub env, then point at the test server (issue #123). + scrubHubEnv(t) + t.Setenv("SCION_HUB_ENDPOINT", server.URL) + t.Setenv("SCION_AUTH_TOKEN", "test-token") + t.Setenv("SCION_AGENT_ID", "test-agent-id") handler := NewHubHandler() if handler == nil { @@ -662,9 +634,7 @@ func TestHubHandler_AssistantTextForwarding(t *testing.T) { t.Run("truncates assistant text exceeding 64KB", func(t *testing.T) { tmpHome := t.TempDir() - origHome := os.Getenv("HOME") - os.Setenv("HOME", tmpHome) - defer os.Setenv("HOME", origHome) + t.Setenv("HOME", tmpHome) var mu sync.Mutex var outboundMsg string @@ -685,15 +655,11 @@ func TestHubHandler_AssistantTextForwarding(t *testing.T) { })) defer server.Close() - os.Setenv("SCION_HUB_ENDPOINT", server.URL) - os.Setenv("SCION_AUTH_TOKEN", "test-token") - os.Setenv("SCION_AGENT_ID", "test-agent-id") - defer func() { - os.Unsetenv("SCION_HUB_ENDPOINT") - os.Unsetenv("SCION_HUB_URL") - os.Unsetenv("SCION_AUTH_TOKEN") - os.Unsetenv("SCION_AGENT_ID") - }() + // Clear real Hub env, then point at the test server (issue #123). + scrubHubEnv(t) + t.Setenv("SCION_HUB_ENDPOINT", server.URL) + t.Setenv("SCION_AUTH_TOKEN", "test-token") + t.Setenv("SCION_AGENT_ID", "test-agent-id") handler := NewHubHandler() if handler == nil { @@ -734,9 +700,7 @@ func TestHubHandler_AssistantTextForwarding(t *testing.T) { func TestHubHandler_AssistantTextVisibilityTagging(t *testing.T) { t.Run("tags outbound message with verbose visibility", func(t *testing.T) { tmpHome := t.TempDir() - origHome := os.Getenv("HOME") - os.Setenv("HOME", tmpHome) - defer os.Setenv("HOME", origHome) + t.Setenv("HOME", tmpHome) var mu sync.Mutex var outboundPayload map[string]interface{} @@ -757,15 +721,11 @@ func TestHubHandler_AssistantTextVisibilityTagging(t *testing.T) { })) defer server.Close() - os.Setenv("SCION_HUB_ENDPOINT", server.URL) - os.Setenv("SCION_AUTH_TOKEN", "test-token") - os.Setenv("SCION_AGENT_ID", "test-agent-id") - defer func() { - os.Unsetenv("SCION_HUB_ENDPOINT") - os.Unsetenv("SCION_HUB_URL") - os.Unsetenv("SCION_AUTH_TOKEN") - os.Unsetenv("SCION_AGENT_ID") - }() + // Clear real Hub env, then point at the test server (issue #123). + scrubHubEnv(t) + t.Setenv("SCION_HUB_ENDPOINT", server.URL) + t.Setenv("SCION_AUTH_TOKEN", "test-token") + t.Setenv("SCION_AGENT_ID", "test-agent-id") handler := NewHubHandler() if handler == nil { @@ -800,9 +760,7 @@ func TestHubHandler_AssistantTextVisibilityTagging(t *testing.T) { t.Run("sets has_thinking metadata when thinking content was filtered", func(t *testing.T) { tmpHome := t.TempDir() - origHome := os.Getenv("HOME") - os.Setenv("HOME", tmpHome) - defer os.Setenv("HOME", origHome) + t.Setenv("HOME", tmpHome) var mu sync.Mutex var outboundPayload map[string]interface{} @@ -823,15 +781,11 @@ func TestHubHandler_AssistantTextVisibilityTagging(t *testing.T) { })) defer server.Close() - os.Setenv("SCION_HUB_ENDPOINT", server.URL) - os.Setenv("SCION_AUTH_TOKEN", "test-token") - os.Setenv("SCION_AGENT_ID", "test-agent-id") - defer func() { - os.Unsetenv("SCION_HUB_ENDPOINT") - os.Unsetenv("SCION_HUB_URL") - os.Unsetenv("SCION_AUTH_TOKEN") - os.Unsetenv("SCION_AGENT_ID") - }() + // Clear real Hub env, then point at the test server (issue #123). + scrubHubEnv(t) + t.Setenv("SCION_HUB_ENDPOINT", server.URL) + t.Setenv("SCION_AUTH_TOKEN", "test-token") + t.Setenv("SCION_AGENT_ID", "test-agent-id") handler := NewHubHandler() if handler == nil { diff --git a/pkg/sciontool/hub/client_test.go b/pkg/sciontool/hub/client_test.go index bd129dd3b..63839263b 100644 --- a/pkg/sciontool/hub/client_test.go +++ b/pkg/sciontool/hub/client_test.go @@ -19,7 +19,6 @@ import ( "encoding/json" "net/http" "net/http/httptest" - "os" "testing" "time" @@ -28,53 +27,52 @@ import ( "github.com/stretchr/testify/require" ) +// scrubHubEnv clears all Hub-related environment variables for the +// duration of the test, preventing accidental communication with a +// real Hub when tests run inside an agent container. See issue #123. +func scrubHubEnv(t *testing.T) { + t.Helper() + for _, key := range []string{ + EnvHubEndpoint, + EnvHubURL, + EnvHubToken, + EnvAgentID, + EnvAgentMode, + } { + t.Setenv(key, "") + } +} + func TestNewClient_FromEnvironment(t *testing.T) { - // Save and restore env vars - origEndpoint := os.Getenv(EnvHubEndpoint) - origURL := os.Getenv(EnvHubURL) - origToken := os.Getenv(EnvHubToken) - origAgentID := os.Getenv(EnvAgentID) - defer func() { - os.Setenv(EnvHubEndpoint, origEndpoint) - os.Setenv(EnvHubURL, origURL) - os.Setenv(EnvHubToken, origToken) - os.Setenv(EnvAgentID, origAgentID) - }() + // Clear Hub env vars to prevent leakage from the container (issue #123). + scrubHubEnv(t) t.Run("missing env vars returns nil", func(t *testing.T) { - os.Unsetenv(EnvHubEndpoint) - os.Unsetenv(EnvHubURL) - os.Unsetenv(EnvHubToken) - os.Unsetenv(EnvAgentID) - + scrubHubEnv(t) client := NewClient() assert.Nil(t, client) }) t.Run("missing token returns nil", func(t *testing.T) { - os.Unsetenv(EnvHubEndpoint) - os.Setenv(EnvHubURL, "http://hub.example.com") - os.Unsetenv(EnvHubToken) - os.Unsetenv(EnvAgentID) - + scrubHubEnv(t) + t.Setenv(EnvHubURL, "http://hub.example.com") client := NewClient() assert.Nil(t, client) }) t.Run("missing agentID returns nil", func(t *testing.T) { - os.Setenv(EnvHubEndpoint, "http://hub.example.com") - os.Setenv(EnvHubToken, "test-token") - os.Unsetenv(EnvAgentID) - + scrubHubEnv(t) + t.Setenv(EnvHubEndpoint, "http://hub.example.com") + t.Setenv(EnvHubToken, "test-token") client := NewClient() assert.Nil(t, client, "should not create client without agent ID (local agent scenario)") }) t.Run("with all env vars returns client", func(t *testing.T) { - os.Unsetenv(EnvHubEndpoint) - os.Setenv(EnvHubURL, "http://hub.example.com") - os.Setenv(EnvHubToken, "test-token") - os.Setenv(EnvAgentID, "agent-123") + scrubHubEnv(t) + t.Setenv(EnvHubURL, "http://hub.example.com") + t.Setenv(EnvHubToken, "test-token") + t.Setenv(EnvAgentID, "agent-123") client := NewClient() require.NotNil(t, client) @@ -82,10 +80,11 @@ func TestNewClient_FromEnvironment(t *testing.T) { }) t.Run("prefers SCION_HUB_ENDPOINT over SCION_HUB_URL", func(t *testing.T) { - os.Setenv(EnvHubEndpoint, "http://endpoint.example.com") - os.Setenv(EnvHubURL, "http://url.example.com") - os.Setenv(EnvHubToken, "test-token") - os.Setenv(EnvAgentID, "agent-123") + scrubHubEnv(t) + t.Setenv(EnvHubEndpoint, "http://endpoint.example.com") + t.Setenv(EnvHubURL, "http://url.example.com") + t.Setenv(EnvHubToken, "test-token") + t.Setenv(EnvAgentID, "agent-123") client := NewClient() require.NotNil(t, client) @@ -93,10 +92,10 @@ func TestNewClient_FromEnvironment(t *testing.T) { }) t.Run("falls back to SCION_HUB_URL when SCION_HUB_ENDPOINT not set", func(t *testing.T) { - os.Unsetenv(EnvHubEndpoint) - os.Setenv(EnvHubURL, "http://url.example.com") - os.Setenv(EnvHubToken, "test-token") - os.Setenv(EnvAgentID, "agent-123") + scrubHubEnv(t) + t.Setenv(EnvHubURL, "http://url.example.com") + t.Setenv(EnvHubToken, "test-token") + t.Setenv(EnvAgentID, "agent-123") client := NewClient() require.NotNil(t, client) @@ -261,31 +260,25 @@ func TestClient_Heartbeat(t *testing.T) { } func TestIsHostedMode(t *testing.T) { - origMode := os.Getenv(EnvAgentMode) - defer os.Setenv(EnvAgentMode, origMode) - t.Run("not hosted mode", func(t *testing.T) { - os.Unsetenv(EnvAgentMode) + t.Setenv(EnvAgentMode, "") assert.False(t, IsHostedMode()) - os.Setenv(EnvAgentMode, "solo") + t.Setenv(EnvAgentMode, "solo") assert.False(t, IsHostedMode()) }) t.Run("hosted mode", func(t *testing.T) { - os.Setenv(EnvAgentMode, "hosted") + t.Setenv(EnvAgentMode, "hosted") assert.True(t, IsHostedMode()) }) } func TestGetAgentID(t *testing.T) { - origID := os.Getenv(EnvAgentID) - defer os.Setenv(EnvAgentID, origID) - - os.Setenv(EnvAgentID, "test-agent-id") + t.Setenv(EnvAgentID, "test-agent-id") assert.Equal(t, "test-agent-id", GetAgentID()) - os.Unsetenv(EnvAgentID) + t.Setenv(EnvAgentID, "") assert.Equal(t, "", GetAgentID()) } @@ -677,16 +670,6 @@ func TestClient_StartTokenRefresh(t *testing.T) { } func TestOperatingMode(t *testing.T) { - // Save and restore env vars - origEndpoint := os.Getenv(EnvHubEndpoint) - origURL := os.Getenv(EnvHubURL) - origMode := os.Getenv(EnvAgentMode) - defer func() { - os.Setenv(EnvHubEndpoint, origEndpoint) - os.Setenv(EnvHubURL, origURL) - os.Setenv(EnvAgentMode, origMode) - }() - tests := []struct { name string endpoint string @@ -747,17 +730,16 @@ func TestOperatingMode(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - os.Unsetenv(EnvHubEndpoint) - os.Unsetenv(EnvHubURL) - os.Unsetenv(EnvAgentMode) + // Clear Hub env, then set test values (issue #123). + scrubHubEnv(t) if tt.endpoint != "" { - os.Setenv(EnvHubEndpoint, tt.endpoint) + t.Setenv(EnvHubEndpoint, tt.endpoint) } if tt.hubURL != "" { - os.Setenv(EnvHubURL, tt.hubURL) + t.Setenv(EnvHubURL, tt.hubURL) } if tt.agentMode != "" { - os.Setenv(EnvAgentMode, tt.agentMode) + t.Setenv(EnvAgentMode, tt.agentMode) } mode := OperatingMode() @@ -768,20 +750,8 @@ func TestOperatingMode(t *testing.T) { } func TestOperatingMode_Defaults(t *testing.T) { - // Save and restore env vars - origEndpoint := os.Getenv(EnvHubEndpoint) - origURL := os.Getenv(EnvHubURL) - origMode := os.Getenv(EnvAgentMode) - defer func() { - os.Setenv(EnvHubEndpoint, origEndpoint) - os.Setenv(EnvHubURL, origURL) - os.Setenv(EnvAgentMode, origMode) - }() - - // Clear all relevant env vars - os.Unsetenv(EnvHubEndpoint) - os.Unsetenv(EnvHubURL) - os.Unsetenv(EnvAgentMode) + // Clear all relevant env vars (issue #123). + scrubHubEnv(t) mode := OperatingMode() assert.Equal(t, ModeLocal, mode, "should default to ModeLocal when no env vars are set") @@ -818,18 +788,11 @@ func TestNewClient_UsesTokenFile(t *testing.T) { cleanup := SetTokenHome(t.TempDir()) defer cleanup() - origEndpoint := os.Getenv(EnvHubEndpoint) - origToken := os.Getenv(EnvHubToken) - origAgentID := os.Getenv(EnvAgentID) - defer func() { - os.Setenv(EnvHubEndpoint, origEndpoint) - os.Setenv(EnvHubToken, origToken) - os.Setenv(EnvAgentID, origAgentID) - }() - - os.Setenv(EnvHubEndpoint, "http://hub.example.com") - os.Setenv(EnvHubToken, "original-env-token") - os.Setenv(EnvAgentID, "agent-123") + // Clear Hub env, then set test values (issue #123). + scrubHubEnv(t) + t.Setenv(EnvHubEndpoint, "http://hub.example.com") + t.Setenv(EnvHubToken, "original-env-token") + t.Setenv(EnvAgentID, "agent-123") t.Run("uses env token when no file exists", func(t *testing.T) { client := NewClient() @@ -974,27 +937,21 @@ func TestGitHubTokenFile_WriteAndRead(t *testing.T) { } func TestIsGitHubAppEnabled(t *testing.T) { - orig := os.Getenv(EnvGitHubAppEnabled) - defer os.Setenv(EnvGitHubAppEnabled, orig) - - os.Unsetenv(EnvGitHubAppEnabled) + t.Setenv(EnvGitHubAppEnabled, "") assert.False(t, IsGitHubAppEnabled()) - os.Setenv(EnvGitHubAppEnabled, "false") + t.Setenv(EnvGitHubAppEnabled, "false") assert.False(t, IsGitHubAppEnabled()) - os.Setenv(EnvGitHubAppEnabled, "true") + t.Setenv(EnvGitHubAppEnabled, "true") assert.True(t, IsGitHubAppEnabled()) } func TestGitHubTokenPath(t *testing.T) { - orig := os.Getenv(EnvGitHubTokenPath) - defer os.Setenv(EnvGitHubTokenPath, orig) - - os.Unsetenv(EnvGitHubTokenPath) + t.Setenv(EnvGitHubTokenPath, "") assert.Equal(t, DefaultGitHubTokenPath, GitHubTokenPath()) - os.Setenv(EnvGitHubTokenPath, "/custom/path/token") + t.Setenv(EnvGitHubTokenPath, "/custom/path/token") assert.Equal(t, "/custom/path/token", GitHubTokenPath()) } From e64a1a752bfda67a8b9794d4d0c08581eeafb32a Mon Sep 17 00:00:00 2001 From: Preston Holmes Date: Tue, 2 Jun 2026 20:27:57 -0700 Subject: [PATCH 003/155] docs: document REGION and ZONE overrides in starter-hub README --- scripts/starter-hub/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/starter-hub/README.md b/scripts/starter-hub/README.md index c8bf8b8bf..68f0a240a 100644 --- a/scripts/starter-hub/README.md +++ b/scripts/starter-hub/README.md @@ -17,6 +17,8 @@ file paths from two primary variables: | `HUB_NAME` | `demo` | Deployment name — drives GCE instance, SA, firewall rule, cluster, and DNS names | | `BASE_DOMAIN` | `scion-ai.dev` | Root domain — combined with `HUB_NAME` to form `hub..` | | `ENABLE_GKE` | `false` | Set to `true` to provision a GKE cluster, grant `container.admin`, configure credentials, and use Kubernetes as the default runtime. | +| `REGION` | `us-central1` | GCP region for GKE and resource locations | +| `ZONE` | `us-central1-a` | GCP zone for the GCE VM instance | To stand up a second hub (e.g., "staging"): From b2eaa59888677f46908650725cf8cb921c91ae5e Mon Sep 17 00:00:00 2001 From: Preston Holmes Date: Tue, 2 Jun 2026 20:34:17 -0700 Subject: [PATCH 004/155] fix: use sudo to check repository path existence in gce-demo-setup-repo.sh --- scripts/starter-hub/gce-demo-setup-repo.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/starter-hub/gce-demo-setup-repo.sh b/scripts/starter-hub/gce-demo-setup-repo.sh index df0553277..01d4fd167 100755 --- a/scripts/starter-hub/gce-demo-setup-repo.sh +++ b/scripts/starter-hub/gce-demo-setup-repo.sh @@ -61,7 +61,7 @@ gcloud compute ssh "${INSTANCE_NAME}" \ echo \"Repository /home/scion/scion already exists, fetching latest...\" sudo -u scion sh -c 'cd /home/scion/scion && git fetch origin && git reset --hard origin/HEAD' else - if [ -e \"/home/scion/scion\" ]; then + if sudo test -e \"/home/scion/scion\"; then echo \"Removing existing non-git path /home/scion/scion...\" sudo rm -rf /home/scion/scion fi From 5d21b250931962fc9e338036f155084785fc911e Mon Sep 17 00:00:00 2001 From: Preston Holmes Date: Wed, 3 Jun 2026 07:31:52 -0700 Subject: [PATCH 005/155] cmd: fix nil-pointer panic in harness-config when Hub is disabled --- cmd/harness_config.go | 57 +++++++++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 24 deletions(-) diff --git a/cmd/harness_config.go b/cmd/harness_config.go index c422ff120..7b108d7cd 100644 --- a/cmd/harness_config.go +++ b/cmd/harness_config.go @@ -82,7 +82,7 @@ var harnessConfigListCmd = &cobra.Command{ // Include Hub results if requested if showHub { hubCtx, err := CheckHubAvailabilityWithOptions(gp, true) - if err == nil { + if err == nil && hubCtx != nil { hubResp, err := hubCtx.Client.HarnessConfigs().List(context.Background(), &hubclient.ListHarnessConfigsOptions{ Status: "active", }) @@ -297,6 +297,9 @@ var harnessConfigSyncCmd = &cobra.Command{ if err != nil { return err } + if hubCtx == nil { + return fmt.Errorf("Hub integration is not enabled. Configure via 'scion config set hub.enabled true' and 'scion config set hub.endpoint '") + } PrintUsingHub(hubCtx.Endpoint) @@ -339,6 +342,9 @@ var harnessConfigPullCmd = &cobra.Command{ if err != nil { return err } + if hubCtx == nil { + return fmt.Errorf("Hub integration is not enabled. Configure via 'scion config set hub.enabled true' and 'scion config set hub.endpoint '") + } PrintUsingHub(hubCtx.Endpoint) @@ -414,31 +420,31 @@ var harnessConfigShowCmd = &cobra.Command{ return fmt.Errorf("harness-config %q not found locally and Hub unavailable: %w", name, err) } - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - defer cancel() + if hubCtx != nil { + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() - resp, err := hubCtx.Client.HarnessConfigs().List(ctx, &hubclient.ListHarnessConfigsOptions{ - Name: name, - Status: "active", - }) - if err != nil { - return fmt.Errorf("failed to search Hub: %w", err) - } - - for _, hc := range resp.HarnessConfigs { - if hc.Name == name || hc.Slug == name { - if isJSONOutput() { - return outputJSON(hc) + resp, err := hubCtx.Client.HarnessConfigs().List(ctx, &hubclient.ListHarnessConfigsOptions{ + Name: name, + Status: "active", + }) + if err == nil { + for _, hc := range resp.HarnessConfigs { + if hc.Name == name || hc.Slug == name { + if isJSONOutput() { + return outputJSON(hc) + } + fmt.Printf("Name: %s\n", hc.Name) + fmt.Printf("Source: hub\n") + fmt.Printf("ID: %s\n", hc.ID) + fmt.Printf("Harness: %s\n", hc.Harness) + fmt.Printf("Status: %s\n", hc.Status) + fmt.Printf("Content Hash: %s\n", truncateHash(hc.ContentHash)) + fmt.Printf("Scope: %s\n", hc.Scope) + fmt.Printf("Files: %d\n", len(hc.Files)) + return nil + } } - fmt.Printf("Name: %s\n", hc.Name) - fmt.Printf("Source: hub\n") - fmt.Printf("ID: %s\n", hc.ID) - fmt.Printf("Harness: %s\n", hc.Harness) - fmt.Printf("Status: %s\n", hc.Status) - fmt.Printf("Content Hash: %s\n", truncateHash(hc.ContentHash)) - fmt.Printf("Scope: %s\n", hc.Scope) - fmt.Printf("Files: %d\n", len(hc.Files)) - return nil } } @@ -467,6 +473,9 @@ var harnessConfigDeleteCmd = &cobra.Command{ if err != nil { return err } + if hubCtx == nil { + return fmt.Errorf("Hub integration is not enabled. Configure via 'scion config set hub.enabled true' and 'scion config set hub.endpoint '") + } PrintUsingHub(hubCtx.Endpoint) From 26caeb927a835e287322e0712d7e2ea01c1fe62a Mon Sep 17 00:00:00 2001 From: Preston Holmes Date: Wed, 3 Jun 2026 07:51:31 -0700 Subject: [PATCH 006/155] scripts/starter-hub: add MACHINE_TYPE override support to provision script --- scripts/starter-hub/README.md | 1 + scripts/starter-hub/gce-demo-provision.sh | 32 ++++++++++++----------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/scripts/starter-hub/README.md b/scripts/starter-hub/README.md index 68f0a240a..e1bf26c41 100644 --- a/scripts/starter-hub/README.md +++ b/scripts/starter-hub/README.md @@ -19,6 +19,7 @@ file paths from two primary variables: | `ENABLE_GKE` | `false` | Set to `true` to provision a GKE cluster, grant `container.admin`, configure credentials, and use Kubernetes as the default runtime. | | `REGION` | `us-central1` | GCP region for GKE and resource locations | | `ZONE` | `us-central1-a` | GCP zone for the GCE VM instance | +| `MACHINE_TYPE` | *(derived)* | Compute machine type to use (overrides `SIZE_CHOICE`) | To stand up a second hub (e.g., "staging"): diff --git a/scripts/starter-hub/gce-demo-provision.sh b/scripts/starter-hub/gce-demo-provision.sh index 2ff455b5f..b22f5a4db 100755 --- a/scripts/starter-hub/gce-demo-provision.sh +++ b/scripts/starter-hub/gce-demo-provision.sh @@ -99,22 +99,24 @@ fi # Prompt for size (only needed if creating the instance) if [[ "${INSTANCE_EXISTS}" == "false" ]]; then - if [[ -z "${SIZE_CHOICE:-}" ]]; then - echo "Choose instance size:" - echo "1) Small (10s of agents) - e2-standard-4 (4 vCPU, 16GB)" - echo "2) Medium (~50 agents) - n2-standard-16 (16 vCPU, 64GB)" - echo "3) Large (100s of agents) - n2-standard-32 (32 vCPU, 128GB)" - echo "4) XLarge (~1000 agents) - n2-standard-128 (128 vCPU, 512GB)" - read -p "Select [1-4]: " SIZE_CHOICE - fi + if [[ -z "${MACHINE_TYPE:-}" ]]; then + if [[ -z "${SIZE_CHOICE:-}" ]]; then + echo "Choose instance size:" + echo "1) Small (10s of agents) - e2-standard-4 (4 vCPU, 16GB)" + echo "2) Medium (~50 agents) - n2-standard-16 (16 vCPU, 64GB)" + echo "3) Large (100s of agents) - n2-standard-32 (32 vCPU, 128GB)" + echo "4) XLarge (~1000 agents) - n2-standard-128 (128 vCPU, 512GB)" + read -p "Select [1-4]: " SIZE_CHOICE + fi - case $SIZE_CHOICE in - 1) MACHINE_TYPE="e2-standard-4" ;; - 2) MACHINE_TYPE="n2-standard-16" ;; - 3) MACHINE_TYPE="n2-standard-32" ;; - 4) MACHINE_TYPE="n2-standard-128" ;; - *) echo "Invalid choice: $SIZE_CHOICE"; exit 1 ;; - esac + case $SIZE_CHOICE in + 1) MACHINE_TYPE="e2-standard-4" ;; + 2) MACHINE_TYPE="n2-standard-16" ;; + 3) MACHINE_TYPE="n2-standard-32" ;; + 4) MACHINE_TYPE="n2-standard-128" ;; + *) echo "Invalid choice: $SIZE_CHOICE"; exit 1 ;; + esac + fi echo "Selected Machine Type: ${MACHINE_TYPE}" fi From e4585dc93ed7bd5af04377804b20345d6d17a26f Mon Sep 17 00:00:00 2001 From: alan blount Date: Wed, 3 Jun 2026 18:09:01 -0400 Subject: [PATCH 007/155] docs: add external channels, GCE hub setup, and multi-broker guides (#299) Three new documentation pages: - External Channels: covers Telegram (bidirectional group chat), Discord (outbound webhooks), and A2A protocol bridge in one page. Summarizes concepts and links to detailed READMEs in extras/. - Hub Setup on GCE: step-by-step walkthrough of deploying a hub using the starter-hub scripts. Covers provisioning, repo setup, TLS, and post-setup next steps. - Multi-Broker Setup: how to connect multiple machines to a single hub for distributed agent execution. Covers architecture, broker registration, selection, and cross-broker considerations. Sidebar updated to include all three pages. --- docs-site/astro.config.mjs | 3 + .../content/docs/hub-admin/hub-setup-gce.md | 76 +++++++++++++++++++ .../docs/hub-user/external-channels.md | 63 +++++++++++++++ .../src/content/docs/hub-user/multi-broker.md | 59 ++++++++++++++ 4 files changed, 201 insertions(+) create mode 100644 docs-site/src/content/docs/hub-admin/hub-setup-gce.md create mode 100644 docs-site/src/content/docs/hub-user/external-channels.md create mode 100644 docs-site/src/content/docs/hub-user/multi-broker.md diff --git a/docs-site/astro.config.mjs b/docs-site/astro.config.mjs index 7a3155213..1dee4e28e 100644 --- a/docs-site/astro.config.mjs +++ b/docs-site/astro.config.mjs @@ -74,11 +74,14 @@ export default defineConfig({ { label: 'Secret Management', slug: 'hub-user/secrets' }, { label: 'Runtime Broker', slug: 'hub-user/runtime-broker' }, { label: 'Messaging & Notifications', slug: 'hub-user/messaging' }, + { label: 'External Channels', slug: 'hub-user/external-channels' }, + { label: 'Multi-Broker Setup', slug: 'hub-user/multi-broker' }, ], }, { label: 'Hub Administration', items: [ { label: 'Hub Setup', slug: 'hub-admin/hub-server' }, + { label: 'Hub Setup (GCE)', slug: 'hub-admin/hub-setup-gce' }, { label: 'Kubernetes', slug: 'hub-admin/kubernetes' }, { label: 'Security', slug: 'hub-admin/auth' }, { label: 'Permissions', slug: 'hub-admin/permissions' }, diff --git a/docs-site/src/content/docs/hub-admin/hub-setup-gce.md b/docs-site/src/content/docs/hub-admin/hub-setup-gce.md new file mode 100644 index 000000000..eec3f4ea0 --- /dev/null +++ b/docs-site/src/content/docs/hub-admin/hub-setup-gce.md @@ -0,0 +1,76 @@ +--- +title: Hub Setup on GCE +description: Deploy a Scion Hub on a Google Compute Engine VM using the starter scripts. +--- + +## Overview + +The quickest path to a deployed Scion Hub is a single Google Compute Engine VM using the starter scripts in `scripts/starter-hub/`. These scripts automate VM provisioning, repository setup, TLS configuration, and Hub startup. + +## Prerequisites + +- A **GCP project** with billing enabled. +- The **gcloud CLI** installed and configured (`gcloud auth login`, project set). +- A **domain name** (optional but recommended for HTTPS/TLS). + +## Steps + +The starter scripts are designed to be run in sequence from your local machine. + +### 1. Provision the VM + +```bash +./scripts/starter-hub/gce-demo-provision.sh +``` + +Creates a GCE VM with the necessary machine type, disk, firewall rules, and service account. + +### 2. Set Up the Repository + +```bash +./scripts/starter-hub/gce-demo-setup-repo.sh +``` + +SSHs into the VM and clones the Scion repository, installing required dependencies. + +### 3. Build and Deploy + +```bash +./scripts/starter-hub/gce-demo-deploy.sh +``` + +Builds the Hub server and its dependencies on the VM. + +### 4. Configure TLS (Optional) + +```bash +./scripts/starter-hub/gce-certs.sh +``` + +Sets up Caddy as a reverse proxy with automatic TLS certificate provisioning. Requires a domain name pointed at the VM's external IP. + +### 5. Generate Hub Configuration + +```bash +./scripts/starter-hub/hub-config.sh +``` + +Generates the `settings.yaml` file with your chosen options (domain, auth settings, etc.). + +### 6. Start the Hub + +```bash +./scripts/starter-hub/gce-start-hub.sh +``` + +Starts the Hub service on the VM. The Hub is now ready to accept connections. + +## Post-Setup + +Once the Hub is running: + +1. **Access the Web Dashboard** — Navigate to your domain (or the VM's external IP) in a browser. +2. **Create your first project** — Use the dashboard or `scion project create` from the CLI. +3. **Register a Runtime Broker** — Connect a machine to execute agents. See [Runtime Broker](/scion/hub-user/runtime-broker/) for details on registering your local machine or a remote VM. + +For ongoing Hub administration (auth, permissions, observability), see the other guides in the Hub Administration section. diff --git a/docs-site/src/content/docs/hub-user/external-channels.md b/docs-site/src/content/docs/hub-user/external-channels.md new file mode 100644 index 000000000..319cdf6ca --- /dev/null +++ b/docs-site/src/content/docs/hub-user/external-channels.md @@ -0,0 +1,63 @@ +--- +title: External Channels +description: Connect Scion to Telegram, Discord, and A2A for external messaging and notifications. +--- + +## Overview + +Scion can relay agent messages and notifications to external platforms, extending communication beyond the CLI and Web Dashboard. Three channels are available: **Telegram** (bidirectional group chat), **Discord** (outbound webhook notifications), and **A2A protocol** (expose agents as A2A endpoints for programmatic interaction). + +## Telegram + +The Telegram integration provides **bidirectional messaging** — users can message agents from Telegram groups and receive replies directly in the chat. + +### How It Works + +- A Telegram bot (created via [@BotFather](https://core.telegram.org/bots#botfather)) acts as the bridge between Telegram groups and the Scion Hub. +- The bot runs as a Hub plugin (`scion-plugin-telegram`), which must be built and configured in the Hub's `settings.yaml`. +- **Group linking:** Use the `/setup` bot command in a Telegram group to link it to a Scion project. +- **Identity linking:** Use `/register` to associate your Telegram account with your Scion Hub identity. + +### Routing & Commands + +- **@-mention routing:** Mention a specific agent (e.g., `@mybot agent-name message`) to route a message to that agent. +- **Default agent:** Set a default agent with `/default` so untagged messages route automatically. +- Available bot commands: `/agents` (list agents), `/default` (set default), `/settings` (configure group), `/notifications` (toggle notification types). + +### Group Settings + +Each linked group can be configured via `/settings`: + +- **Observer mode (`a2a`):** Show agent-to-agent messages in the group, so you can watch how agents coordinate. +- **Commentary:** Show agent reply messages (responses to other agents) in the group. +- **Group notifications (`grp`):** Post agent state change notifications (completed, error, waiting for input) in the group chat. + +For full setup instructions, bot configuration, and troubleshooting, see [extras/scion-telegram/README.md](https://github.com/GoogleCloudPlatform/scion/tree/main/extras/scion-telegram). + +## Discord + +Discord integration provides **outbound-only** webhook notifications — agents can push messages to a Discord channel, but cannot receive inbound messages from Discord. + +- **Severity-based color coding:** Messages are color-coded by severity (info, warning, error, urgent). +- **@mentions:** Urgent messages and explicit `ask_user` requests can trigger `@user` or `@role` mentions. + +### Configuration + +Set the webhook URL in one of two ways: + +- **settings.yaml:** Set `server.discord_webhook_url` in the Hub configuration. +- **Environment variable:** Set `SCION_DISCORD_WEBHOOK_URL`. + +For more details, see [Hub Setup — Discord Integration](/scion/hub-admin/hub-server/#discord-integration). + +## A2A Protocol Bridge + +The A2A (Agent-to-Agent protocol) bridge exposes Scion agents as **standard A2A endpoints**, allowing external A2A clients to discover and interact with them programmatically. + +- **Discovery:** External clients can query available agents and their capabilities via the A2A protocol. +- **Interaction modes:** Supports blocking (synchronous), SSE streaming, and push notification delivery. +- **Standalone service:** Runs as a separate bridge process alongside the Hub (see `extras/scion-a2a-bridge`). + +This is useful for integrating Scion agents into larger multi-agent systems or exposing them to third-party A2A-compatible clients. + +For setup and configuration, see [extras/scion-a2a-bridge/README.md](https://github.com/GoogleCloudPlatform/scion/tree/main/extras/scion-a2a-bridge). diff --git a/docs-site/src/content/docs/hub-user/multi-broker.md b/docs-site/src/content/docs/hub-user/multi-broker.md new file mode 100644 index 000000000..ed6d8e47e --- /dev/null +++ b/docs-site/src/content/docs/hub-user/multi-broker.md @@ -0,0 +1,59 @@ +--- +title: Multi-Broker Setup +description: Connect multiple machines to a single Scion Hub for distributed agent execution. +--- + +## Overview + +A single Scion Hub can dispatch agents to **multiple Runtime Brokers**. Each broker is a machine — a laptop, cloud VM, or Kubernetes cluster — that runs agent containers. This lets teams pool compute resources and target specific machines for specific workloads. + +## Architecture + +``` + ┌──────────┐ + ┌────────────┤ Scion Hub├────────────┐ + │ └────┬─────┘ │ + │ │ │ + ┌────▼─────┐ ┌──────▼───┐ ┌────────▼──────┐ + │ Broker A │ │ Broker B │ │ Broker C │ + │ (laptop) │ │(cloud VM)│ │ (K8s cluster) │ + └───────────┘ └──────────┘ └───────────────┘ +``` + +Each broker maintains a persistent WebSocket connection to the Hub. The Hub acts as the control plane; brokers handle container execution locally. + +## Adding a Broker + +On each machine you want to register: + +1. **Install Scion** and configure the Hub endpoint (`scion login`). +2. **Register the broker** with the Hub: + ```bash + scion broker register + ``` +3. **Authorize projects** the broker should serve: + ```bash + scion broker provide + ``` + +Repeat for each machine. See [Runtime Broker](/scion/hub-user/runtime-broker/) for detailed setup. + +## Broker Selection + +When starting an agent, the Hub selects an available broker automatically. You can override this: + +- **Target a specific broker** with the `--broker` flag: + ```bash + scion start --broker my-cloud-vm + ``` +- **Check broker availability** across all registered brokers: + ```bash + scion broker status + ``` + +## Considerations + +- Each broker manages its own **port pools, container images, and local storage**. Images must be available on each broker independently. +- **Shared directories** (mounted volumes) only work within a single broker — agents on different brokers cannot share a local directory. +- **Workspace strategy** may differ per broker: local brokers typically use git worktrees (`.scion_worktrees/`), while hub-hosted git projects use a single workspace checkout. +- Broker capacity is determined by the machine's resources. The Hub does not enforce cross-broker resource limits. From c5143bc71404c4914702ea5f4c1c6a6fe60a4b6d Mon Sep 17 00:00:00 2001 From: Preston Holmes Date: Wed, 3 Jun 2026 16:45:12 -0700 Subject: [PATCH 008/155] Add sort and filter to agent list view (#297) * Add sort and filter capabilities to agent list view (#71) CLI: add --phase, --activity, --template filter flags and --sort, --reverse sort flags to 'scion list'. Validates flag values against known phases/activities. Passes phase filter server-side in hub mode for efficiency. Web UI: add phase filter chips (All/Running/Stopped/Suspended/Error), sortable table headers (Name, Status, Updated), and sort dropdown for grid view. Filter and sort state persists to localStorage. Closes #71 * Address review feedback: input canonicalization and validation - CLI: canonicalize --phase/--activity/--sort to lowercase in validateListFlags, remove redundant empty check on filterActivity - Web UI: validate localStorage phase filter against known values instead of raw cast - Web UI: validate localStorage sort config field/dir values before applying - Web UI: handle invalid date strings in formatRelativeTime with isNaN guard --- cmd/list.go | 122 +++++++++- cmd/list_test.go | 349 +++++++++++++++++++++++++++++ web/src/components/pages/agents.ts | 217 +++++++++++++++++- 3 files changed, 677 insertions(+), 11 deletions(-) diff --git a/cmd/list.go b/cmd/list.go index 9bb59bb56..27ff5f402 100644 --- a/cmd/list.go +++ b/cmd/list.go @@ -20,6 +20,7 @@ import ( "fmt" "os" "sort" + "strings" "text/tabwriter" "time" @@ -35,18 +36,31 @@ import ( ) var ( - listAll bool - listDeleted bool - listRunning bool - sortByTime bool + listAll bool + listDeleted bool + listRunning bool + sortByTime bool + filterPhase string + filterActivity string + filterTemplate string + sortField string + sortReverse bool ) +var validSortFields = map[string]bool{ + "name": true, "phase": true, "created": true, "updated": true, "last-seen": true, +} + // listCmd represents the list command var listCmd = &cobra.Command{ Use: "list", Aliases: []string{"ls"}, Short: "List running scion agents", RunE: func(cmd *cobra.Command, args []string) error { + if err := validateListFlags(); err != nil { + return err + } + // Check if Hub should be used hubCtx, err := CheckHubAvailability(projectPath) if err != nil { @@ -108,6 +122,7 @@ func listAgentsViaHub(hubCtx *HubContext) error { opts := &hubclient.ListAgentsOptions{ IncludeDeleted: listDeleted, + Phase: filterPhase, } agentSvc := hubCtx.Client.Agents() @@ -269,6 +284,93 @@ func filterRunningAgents(agents []api.AgentInfo) []api.AgentInfo { return filtered } +// validateListFlags checks that filter and sort flag values are valid. +func validateListFlags() error { + if filterPhase != "" { + filterPhase = strings.ToLower(filterPhase) + if !state.Phase(filterPhase).IsValid() { + valid := make([]string, 0, len(state.Phases())) + for _, p := range state.Phases() { + valid = append(valid, string(p)) + } + return fmt.Errorf("invalid phase %q; valid values: %s", filterPhase, strings.Join(valid, ", ")) + } + } + if filterActivity != "" { + filterActivity = strings.ToLower(filterActivity) + if !state.Activity(filterActivity).IsValid() { + valid := make([]string, 0, len(state.Activities())) + for _, a := range state.Activities() { + valid = append(valid, string(a)) + } + return fmt.Errorf("invalid activity %q; valid values: %s", filterActivity, strings.Join(valid, ", ")) + } + } + if sortField != "" { + sortField = strings.ToLower(sortField) + if !validSortFields[sortField] { + valid := make([]string, 0, len(validSortFields)) + for k := range validSortFields { + valid = append(valid, k) + } + sort.Strings(valid) + return fmt.Errorf("invalid sort field %q; valid values: %s", sortField, strings.Join(valid, ", ")) + } + } + return nil +} + +// filterAgentsByFlags applies --phase, --activity, and --template filters. +func filterAgentsByFlags(agents []api.AgentInfo) []api.AgentInfo { + if filterPhase == "" && filterActivity == "" && filterTemplate == "" { + return agents + } + filtered := make([]api.AgentInfo, 0, len(agents)) + for _, a := range agents { + if filterPhase != "" && !strings.EqualFold(a.Phase, filterPhase) { + continue + } + if filterActivity != "" && !strings.EqualFold(a.Activity, filterActivity) { + continue + } + if filterTemplate != "" && !strings.EqualFold(a.Template, filterTemplate) { + continue + } + filtered = append(filtered, a) + } + return filtered +} + +// sortAgentsByField sorts agents by the --sort field. +func sortAgentsByField(agents []api.AgentInfo) { + if sortField == "" { + return + } + sort.SliceStable(agents, func(i, j int) bool { + var less bool + switch sortField { + case "name": + less = strings.ToLower(agents[i].Name) < strings.ToLower(agents[j].Name) + case "phase": + less = agents[i].Phase < agents[j].Phase + case "created": + less = agents[i].Created.Before(agents[j].Created) + case "updated": + less = agents[i].Updated.Before(agents[j].Updated) + case "last-seen": + less = agents[i].LastSeen.Before(agents[j].LastSeen) + default: + return false + } + // Timestamps default to descending (newest first); name/phase default to ascending + descByDefault := sortField == "created" || sortField == "updated" || sortField == "last-seen" + if descByDefault != sortReverse { + return !less + } + return less + }) +} + func displayAgents(agents []api.AgentInfo, all bool, hubMode bool) error { if listRunning { agents = filterRunningAgents(agents) @@ -281,7 +383,12 @@ func displayAgents(agents []api.AgentInfo, all bool, hubMode bool) error { agents[i].Template = config.FriendlyTemplateName(agents[i].Template) } - if sortByTime { + // Apply --phase, --activity, --template filters + agents = filterAgentsByFlags(agents) + + if sortField != "" { + sortAgentsByField(agents) + } else if sortByTime { sort.Slice(agents, func(i, j int) bool { return agents[i].LastSeen.After(agents[j].LastSeen) }) @@ -574,4 +681,9 @@ func init() { listCmd.Flags().BoolVar(&listDeleted, "deleted", false, "Include soft-deleted agents in listing") listCmd.Flags().BoolVarP(&listRunning, "running", "r", false, "Only show agents that are not stopped or errored") listCmd.Flags().BoolVarP(&sortByTime, "time", "t", false, "Sort by last activity, most recent first") + listCmd.Flags().StringVar(&filterPhase, "phase", "", "Filter by lifecycle phase (running, stopped, error, ...)") + listCmd.Flags().StringVar(&filterActivity, "activity", "", "Filter by runtime activity (thinking, waiting_for_input, ...)") + listCmd.Flags().StringVar(&filterTemplate, "template", "", "Filter by template name") + listCmd.Flags().StringVar(&sortField, "sort", "", "Sort by field (name, phase, created, updated, last-seen)") + listCmd.Flags().BoolVar(&sortReverse, "reverse", false, "Reverse sort order") } diff --git a/cmd/list_test.go b/cmd/list_test.go index e28bad216..c91693039 100644 --- a/cmd/list_test.go +++ b/cmd/list_test.go @@ -580,3 +580,352 @@ func TestHubAgentToAgentInfo_HarnessConfigTopLevelTakesPrecedence(t *testing.T) t.Errorf("HarnessConfig = %q, want %q (top-level should take precedence)", info.HarnessConfig, "gemini") } } + +func TestFilterAgentsByPhase(t *testing.T) { + agents := []api.AgentInfo{ + {Name: "running-1", Phase: "running", Template: "default", Runtime: "docker", Project: "p"}, + {Name: "stopped-1", Phase: "stopped", Template: "default", Runtime: "docker", Project: "p"}, + {Name: "running-2", Phase: "running", Template: "claude", Runtime: "docker", Project: "p"}, + {Name: "error-1", Phase: "error", Template: "default", Runtime: "docker", Project: "p"}, + } + + filterPhase = "running" + defer func() { filterPhase = "" }() + + old := os.Stdout + r, w, _ := os.Pipe() + os.Stdout = w + + err := displayAgents(agents, false, false) + w.Close() + os.Stdout = old + + if err != nil { + t.Fatalf("displayAgents returned error: %v", err) + } + + var buf bytes.Buffer + buf.ReadFrom(r) + output := buf.String() + + if !strings.Contains(output, "running-1") { + t.Errorf("output should contain 'running-1': %s", output) + } + if !strings.Contains(output, "running-2") { + t.Errorf("output should contain 'running-2': %s", output) + } + if strings.Contains(output, "stopped-1") { + t.Errorf("output should NOT contain 'stopped-1': %s", output) + } + if strings.Contains(output, "error-1") { + t.Errorf("output should NOT contain 'error-1': %s", output) + } +} + +func TestFilterAgentsByActivity(t *testing.T) { + agents := []api.AgentInfo{ + {Name: "thinking-agent", Phase: "running", Activity: "thinking", Template: "default", Runtime: "docker", Project: "p"}, + {Name: "waiting-agent", Phase: "running", Activity: "waiting_for_input", Template: "default", Runtime: "docker", Project: "p"}, + {Name: "no-activity", Phase: "stopped", Template: "default", Runtime: "docker", Project: "p"}, + } + + filterActivity = "thinking" + defer func() { filterActivity = "" }() + + old := os.Stdout + r, w, _ := os.Pipe() + os.Stdout = w + + err := displayAgents(agents, false, false) + w.Close() + os.Stdout = old + + if err != nil { + t.Fatalf("displayAgents returned error: %v", err) + } + + var buf bytes.Buffer + buf.ReadFrom(r) + output := buf.String() + + if !strings.Contains(output, "thinking-agent") { + t.Errorf("output should contain 'thinking-agent': %s", output) + } + if strings.Contains(output, "waiting-agent") { + t.Errorf("output should NOT contain 'waiting-agent': %s", output) + } + if strings.Contains(output, "no-activity") { + t.Errorf("output should NOT contain 'no-activity': %s", output) + } +} + +func TestFilterAgentsByTemplate(t *testing.T) { + agents := []api.AgentInfo{ + {Name: "claude-agent", Phase: "running", Template: "claude", Runtime: "docker", Project: "p"}, + {Name: "gemini-agent", Phase: "running", Template: "gemini", Runtime: "docker", Project: "p"}, + } + + filterTemplate = "claude" + defer func() { filterTemplate = "" }() + + old := os.Stdout + r, w, _ := os.Pipe() + os.Stdout = w + + err := displayAgents(agents, false, false) + w.Close() + os.Stdout = old + + if err != nil { + t.Fatalf("displayAgents returned error: %v", err) + } + + var buf bytes.Buffer + buf.ReadFrom(r) + output := buf.String() + + if !strings.Contains(output, "claude-agent") { + t.Errorf("output should contain 'claude-agent': %s", output) + } + if strings.Contains(output, "gemini-agent") { + t.Errorf("output should NOT contain 'gemini-agent': %s", output) + } +} + +func TestFilterAgentsCombined(t *testing.T) { + agents := []api.AgentInfo{ + {Name: "match", Phase: "running", Activity: "thinking", Template: "claude", Runtime: "docker", Project: "p"}, + {Name: "wrong-phase", Phase: "stopped", Activity: "thinking", Template: "claude", Runtime: "docker", Project: "p"}, + {Name: "wrong-activity", Phase: "running", Activity: "executing", Template: "claude", Runtime: "docker", Project: "p"}, + {Name: "wrong-template", Phase: "running", Activity: "thinking", Template: "gemini", Runtime: "docker", Project: "p"}, + } + + filterPhase = "running" + filterActivity = "thinking" + filterTemplate = "claude" + defer func() { filterPhase = ""; filterActivity = ""; filterTemplate = "" }() + + old := os.Stdout + r, w, _ := os.Pipe() + os.Stdout = w + + err := displayAgents(agents, false, false) + w.Close() + os.Stdout = old + + if err != nil { + t.Fatalf("displayAgents returned error: %v", err) + } + + var buf bytes.Buffer + buf.ReadFrom(r) + output := buf.String() + + lines := strings.Split(strings.TrimSpace(output), "\n") + if len(lines) != 2 { + t.Fatalf("expected 2 lines (header + 1 agent), got %d: %s", len(lines), output) + } + if !strings.Contains(lines[1], "match") { + t.Errorf("only 'match' agent should appear: %s", lines[1]) + } +} + +func TestSortAgentsByName(t *testing.T) { + agents := []api.AgentInfo{ + {Name: "charlie", Template: "default", Runtime: "docker", Project: "p", Phase: "running"}, + {Name: "alice", Template: "default", Runtime: "docker", Project: "p", Phase: "running"}, + {Name: "bob", Template: "default", Runtime: "docker", Project: "p", Phase: "running"}, + } + + sortField = "name" + defer func() { sortField = "" }() + + old := os.Stdout + r, w, _ := os.Pipe() + os.Stdout = w + + err := displayAgents(agents, false, false) + w.Close() + os.Stdout = old + + if err != nil { + t.Fatalf("displayAgents returned error: %v", err) + } + + var buf bytes.Buffer + buf.ReadFrom(r) + output := buf.String() + + lines := strings.Split(strings.TrimSpace(output), "\n") + if len(lines) < 4 { + t.Fatalf("expected 4 lines, got %d: %s", len(lines), output) + } + if !strings.Contains(lines[1], "alice") { + t.Errorf("first agent should be 'alice': %s", lines[1]) + } + if !strings.Contains(lines[2], "bob") { + t.Errorf("second agent should be 'bob': %s", lines[2]) + } + if !strings.Contains(lines[3], "charlie") { + t.Errorf("third agent should be 'charlie': %s", lines[3]) + } +} + +func TestSortAgentsByCreated(t *testing.T) { + now := time.Now() + agents := []api.AgentInfo{ + {Name: "oldest", Template: "default", Runtime: "docker", Project: "p", Phase: "running", Created: now.Add(-3 * time.Hour)}, + {Name: "newest", Template: "default", Runtime: "docker", Project: "p", Phase: "running", Created: now.Add(-1 * time.Hour)}, + {Name: "middle", Template: "default", Runtime: "docker", Project: "p", Phase: "running", Created: now.Add(-2 * time.Hour)}, + } + + sortField = "created" + defer func() { sortField = "" }() + + old := os.Stdout + r, w, _ := os.Pipe() + os.Stdout = w + + err := displayAgents(agents, false, false) + w.Close() + os.Stdout = old + + if err != nil { + t.Fatalf("displayAgents returned error: %v", err) + } + + var buf bytes.Buffer + buf.ReadFrom(r) + output := buf.String() + + lines := strings.Split(strings.TrimSpace(output), "\n") + if len(lines) < 4 { + t.Fatalf("expected 4 lines, got %d: %s", len(lines), output) + } + // Timestamps default to descending (newest first) + if !strings.Contains(lines[1], "newest") { + t.Errorf("first agent should be 'newest': %s", lines[1]) + } + if !strings.Contains(lines[2], "middle") { + t.Errorf("second agent should be 'middle': %s", lines[2]) + } + if !strings.Contains(lines[3], "oldest") { + t.Errorf("third agent should be 'oldest': %s", lines[3]) + } +} + +func TestSortAgentsReverse(t *testing.T) { + now := time.Now() + agents := []api.AgentInfo{ + {Name: "oldest", Template: "default", Runtime: "docker", Project: "p", Phase: "running", Created: now.Add(-3 * time.Hour)}, + {Name: "newest", Template: "default", Runtime: "docker", Project: "p", Phase: "running", Created: now.Add(-1 * time.Hour)}, + {Name: "middle", Template: "default", Runtime: "docker", Project: "p", Phase: "running", Created: now.Add(-2 * time.Hour)}, + } + + sortField = "created" + sortReverse = true + defer func() { sortField = ""; sortReverse = false }() + + old := os.Stdout + r, w, _ := os.Pipe() + os.Stdout = w + + err := displayAgents(agents, false, false) + w.Close() + os.Stdout = old + + if err != nil { + t.Fatalf("displayAgents returned error: %v", err) + } + + var buf bytes.Buffer + buf.ReadFrom(r) + output := buf.String() + + lines := strings.Split(strings.TrimSpace(output), "\n") + if len(lines) < 4 { + t.Fatalf("expected 4 lines, got %d: %s", len(lines), output) + } + // --reverse on timestamp: ascending (oldest first) + if !strings.Contains(lines[1], "oldest") { + t.Errorf("first agent should be 'oldest': %s", lines[1]) + } + if !strings.Contains(lines[2], "middle") { + t.Errorf("second agent should be 'middle': %s", lines[2]) + } + if !strings.Contains(lines[3], "newest") { + t.Errorf("third agent should be 'newest': %s", lines[3]) + } +} + +func TestDisplayAgentsFilteredEmpty(t *testing.T) { + agents := []api.AgentInfo{ + {Name: "running-agent", Phase: "running", Template: "default", Runtime: "docker", Project: "p"}, + } + + filterPhase = "error" + defer func() { filterPhase = "" }() + + old := os.Stdout + r, w, _ := os.Pipe() + os.Stdout = w + + err := displayAgents(agents, false, false) + w.Close() + os.Stdout = old + + if err != nil { + t.Fatalf("displayAgents returned error: %v", err) + } + + var buf bytes.Buffer + buf.ReadFrom(r) + output := buf.String() + + if !strings.Contains(output, "No active agents") { + t.Errorf("expected empty message when filter matches nothing, got: %s", output) + } + if strings.Contains(output, "running-agent") { + t.Errorf("output should NOT contain filtered-out agent: %s", output) + } +} + +func TestValidateListFlags(t *testing.T) { + tests := []struct { + name string + phase string + activity string + sort string + wantErr bool + errContain string + }{ + {"valid phase", "running", "", "", false, ""}, + {"valid activity", "", "thinking", "", false, ""}, + {"valid sort", "", "", "name", false, ""}, + {"invalid phase", "bogus", "", "", true, "invalid phase"}, + {"invalid activity", "", "bogus", "", true, "invalid activity"}, + {"invalid sort", "", "", "bogus", true, "invalid sort field"}, + {"all empty", "", "", "", false, ""}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + filterPhase = tt.phase + filterActivity = tt.activity + sortField = tt.sort + defer func() { filterPhase = ""; filterActivity = ""; sortField = "" }() + + err := validateListFlags() + if tt.wantErr { + if err == nil { + t.Fatalf("expected error, got nil") + } + if !strings.Contains(err.Error(), tt.errContain) { + t.Errorf("error %q should contain %q", err.Error(), tt.errContain) + } + } else if err != nil { + t.Fatalf("unexpected error: %v", err) + } + }) + } +} diff --git a/web/src/components/pages/agents.ts b/web/src/components/pages/agents.ts index c7f099b5d..dba0b1a71 100644 --- a/web/src/components/pages/agents.ts +++ b/web/src/components/pages/agents.ts @@ -23,8 +23,11 @@ import { LitElement, html, css, nothing } from 'lit'; import { customElement, property, state } from 'lit/decorators.js'; -import type { PageData, Agent, Capabilities } from '../../shared/types.js'; +import type { PageData, Agent, AgentPhase, Capabilities } from '../../shared/types.js'; import { can, isTerminalAvailable, getAgentDisplayStatus, isAgentRunning } from '../../shared/types.js'; + +type AgentSortField = 'name' | 'status' | 'created' | 'updated'; +type SortDir = 'asc' | 'desc'; import type { StatusType } from '../shared/status-badge.js'; import { apiFetch, extractApiError } from '../../client/api.js'; import { stateManager } from '../../client/state.js'; @@ -89,6 +92,15 @@ export class ScionPageAgents extends LitElement { @state() private agentScope: 'all' | 'mine' | 'shared' = 'all'; + @state() + private phaseFilter: AgentPhase | '' = ''; + + @state() + private sortField: AgentSortField = 'updated'; + + @state() + private sortDir: SortDir = 'desc'; + static override styles = [ listPageStyles, css` @@ -224,6 +236,41 @@ export class ScionPageAgents extends LitElement { .project-link:hover { text-decoration: underline; } + + .filter-bar { + display: flex; + align-items: center; + gap: 0.75rem; + margin-bottom: 1rem; + flex-wrap: wrap; + } + + .filter-bar .label { + font-size: 0.8125rem; + color: var(--scion-text-muted, #64748b); + font-weight: 500; + } + + th.sortable { + cursor: pointer; + user-select: none; + } + + th.sortable:hover { + color: var(--scion-text, #1e293b); + } + + .sort-indicator { + display: inline-block; + margin-left: 0.25rem; + font-size: 0.625rem; + vertical-align: middle; + opacity: 0.4; + } + + th.sorted .sort-indicator { + opacity: 1; + } `, ]; @@ -246,6 +293,28 @@ export class ScionPageAgents extends LitElement { } } + // Read persisted phase filter + const storedPhase = localStorage.getItem('scion-filter-agents-phase'); + if (storedPhase === 'running' || storedPhase === 'stopped' || storedPhase === 'suspended' || storedPhase === 'error') { + this.phaseFilter = storedPhase; + } + + // Read persisted sort + const storedSort = localStorage.getItem('scion-sort-agents'); + if (storedSort) { + try { + const parsed = JSON.parse(storedSort); + if ( + parsed && + (parsed.field === 'name' || parsed.field === 'status' || parsed.field === 'created' || parsed.field === 'updated') && + (parsed.dir === 'asc' || parsed.dir === 'desc') + ) { + this.sortField = parsed.field; + this.sortDir = parsed.dir; + } + } catch { /* ignore invalid stored sort */ } + } + // Set SSE scope to dashboard (all project summaries). // This must happen before checking hydrated data because setScope clears // state maps when the scope changes (e.g. from agent-detail to dashboard). @@ -468,6 +537,73 @@ export class ScionPageAgents extends LitElement { this.viewMode = e.detail.view; } + private get displayAgents(): Agent[] { + let list = this.agents; + if (this.phaseFilter) { + list = list.filter(a => a.phase === this.phaseFilter); + } + const sorted = [...list]; + sorted.sort((a, b) => { + let cmp = 0; + switch (this.sortField) { + case 'name': + cmp = (a.name || '').localeCompare(b.name || ''); + break; + case 'status': + cmp = getAgentDisplayStatus(a).localeCompare(getAgentDisplayStatus(b)); + break; + case 'created': + cmp = (a.created || '').localeCompare(b.created || ''); + break; + case 'updated': + cmp = (a.updated || '').localeCompare(b.updated || ''); + break; + } + return this.sortDir === 'asc' ? cmp : -cmp; + }); + return sorted; + } + + private formatRelativeTime(isoString: string): string { + const date = new Date(isoString); + if (isNaN(date.getTime())) return '—'; + const now = Date.now(); + const diffMs = now - date.getTime(); + if (diffMs < 0) return 'just now'; + const seconds = Math.floor(diffMs / 1000); + if (seconds < 60) return 'just now'; + const minutes = Math.floor(seconds / 60); + if (minutes < 60) return `${minutes}m ago`; + const hours = Math.floor(minutes / 60); + if (hours < 24) return `${hours}h ago`; + const days = Math.floor(hours / 24); + return `${days}d ago`; + } + + private setPhaseFilter(phase: AgentPhase | ''): void { + if (this.phaseFilter === phase) return; + this.phaseFilter = phase; + if (phase) { + localStorage.setItem('scion-filter-agents-phase', phase); + } else { + localStorage.removeItem('scion-filter-agents-phase'); + } + } + + private toggleSort(field: AgentSortField): void { + if (this.sortField === field) { + this.sortDir = this.sortDir === 'asc' ? 'desc' : 'asc'; + } else { + this.sortField = field; + this.sortDir = field === 'name' ? 'asc' : 'desc'; + } + localStorage.setItem('scion-sort-agents', JSON.stringify({ field: this.sortField, dir: this.sortDir })); + } + + private sortIndicator(field: AgentSortField): string { + return this.sortField === field ? (this.sortDir === 'asc' ? '▲' : '▼') : '▲'; + } + private setScope(scope: 'all' | 'mine' | 'shared'): void { if (this.agentScope === scope) return; this.agentScope = scope; @@ -538,7 +674,10 @@ export class ScionPageAgents extends LitElement { - ${this.loading ? this.renderLoading() : this.error ? this.renderError() : this.renderAgents()} + ${this.loading ? this.renderLoading() : this.error ? this.renderError() : html` + ${this.renderFilterBar()} + ${this.renderAgents()} + `} `; } @@ -566,6 +705,50 @@ export class ScionPageAgents extends LitElement { `; } + private renderFilterBar() { + return html` +
+ Status: +
+ + + + + +
+ ${this.viewMode === 'grid' ? html` + + + + Sort: ${this.sortField} + + ) => this.toggleSort(e.detail.item.value as AgentSortField)}> + Name + Status + Created + Updated + + + ` : nothing} +
+ `; + } + private renderAgents() { if (this.agents.length === 0) { if (this.agentScope === 'mine') { @@ -589,6 +772,17 @@ export class ScionPageAgents extends LitElement { return this.renderEmptyState(); } + const filtered = this.displayAgents; + if (filtered.length === 0 && this.phaseFilter) { + return html` +
+ +

No Matching Agents

+

No agents match the current filter. Try changing the status filter.

+
+ `; + } + return this.viewMode === 'grid' ? this.renderGrid() : this.renderTable(); } @@ -614,7 +808,7 @@ export class ScionPageAgents extends LitElement { private renderGrid() { return html` -
${this.agents.map((agent) => this.renderAgentCard(agent))}
+
${this.displayAgents.map((agent) => this.renderAgentCard(agent))}
`; } @@ -743,16 +937,26 @@ export class ScionPageAgents extends LitElement { - + - + + - ${this.agents.map((agent) => this.renderAgentRow(agent))} + ${this.displayAgents.map((agent) => this.renderAgentRow(agent))}
Name this.toggleSort('name')} + >Name ${this.sortIndicator('name')} Project TemplateStatus this.toggleSort('status')} + >Status ${this.sortIndicator('status')} this.toggleSort('updated')} + >Updated ${this.sortIndicator('updated')} Task Actions
@@ -779,6 +983,7 @@ export class ScionPageAgents extends LitElement { size="small" > + ${agent.updated ? this.formatRelativeTime(agent.updated) : '\u2014'} ${agent.taskSummary || '\u2014'} From 0ede01015487e3bfffbb0bc72465065c3eb943e6 Mon Sep 17 00:00:00 2001 From: Preston Holmes Date: Wed, 3 Jun 2026 16:45:54 -0700 Subject: [PATCH 009/155] Add prominent disconnected overlay to web terminal (#295) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add prominent disconnected overlay to web terminal When the WebSocket connection drops, a full-terminal overlay now appears with 50% black opacity and large red "DISCONNECTED" text centered on it. The overlay appears immediately on disconnect and disappears when the connection is re-established. The small status indicator in the toolbar remains as a secondary signal. Fixes #77 * Move disconnected overlay to be a sibling of xterm container The overlay was a child of .terminal-container, whose DOM is managed by xterm.js. Lit re-rendering the overlay on connect/disconnect state changes conflicts with xterm's DOM management. Fix: introduce .terminal-wrapper as the relative-positioning context, make .terminal-container absolutely positioned inside it, and render the overlay as a sibling — outside xterm's managed subtree. * Use wasConnected flag instead of terminal ref for overlay reactivity Replace the non-reactive `this.terminal` reference in the overlay condition with a new `@state() wasConnected` flag. This fixes two issues: 1. Lit reactivity: `this.terminal` lacked `@state()` so changes to it didn't trigger re-renders. The new `wasConnected` is properly decorated as reactive state. 2. Initial connection: using `this.terminal` would flash the overlay during the brief window between terminal init and WebSocket open. `wasConnected` is only set true after the first successful connect, so the overlay only appears after a genuine disconnection. --- web/src/components/pages/terminal.ts | 47 ++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/web/src/components/pages/terminal.ts b/web/src/components/pages/terminal.ts index f3413d83a..f619d369b 100644 --- a/web/src/components/pages/terminal.ts +++ b/web/src/components/pages/terminal.ts @@ -67,6 +67,9 @@ export class ScionPageTerminal extends LitElement { @state() private connected = false; + @state() + private wasConnected = false; + @state() private error: string | null = null; @@ -228,12 +231,43 @@ export class ScionPageTerminal extends LitElement { opacity: 0.4; } - .terminal-container { + .terminal-wrapper { flex: 1; position: relative; overflow: hidden; } + .terminal-container { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + } + + .disconnected-overlay { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.5); + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + z-index: 10; + pointer-events: none; + } + + .disconnected-overlay .overlay-text { + color: #ef4444; + font-size: 2rem; + font-weight: 700; + letter-spacing: 0.15em; + text-shadow: 0 2px 8px rgba(0, 0, 0, 0.6); + } + .loading-state, .error-state { display: flex; @@ -588,6 +622,7 @@ export class ScionPageTerminal extends LitElement { this.socket.onopen = () => { console.debug('[Terminal] WebSocket connected'); this.connected = true; + this.wasConnected = true; this.error = null; // Re-fit now that the connection is live so tmux gets accurate dimensions if (this.fitAddon) { @@ -683,6 +718,7 @@ export class ScionPageTerminal extends LitElement { } this.fitAddon = null; this.clipboardAddon = null; + this.wasConnected = false; } /** @@ -809,7 +845,14 @@ export class ScionPageTerminal extends LitElement { ` : ''} -
+
+
+ ${!this.connected && this.wasConnected + ? html`
+ DISCONNECTED +
` + : ''} +
`; } } From f7e0109d6037471553cd24f852675d0a68c79b92 Mon Sep 17 00:00:00 2001 From: Preston Holmes Date: Thu, 4 Jun 2026 17:55:42 -0700 Subject: [PATCH 010/155] =?UTF-8?q?feat(store):=20Postgres=20backend=20?= =?UTF-8?q?=E2=80=94=20pgx=20driver,=20Ent=20schema=20parity,=20full=20sto?= =?UTF-8?q?re=20port,=20LISTEN/NOTIFY=20(#304)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * P0-1: switch Postgres driver from lib/pq to pgx/v5 stdlib - Add github.com/jackc/pgx/v5/stdlib (registers as "pgx") - driver_postgres.go: blank import pgx stdlib instead of lib/pq - OpenPostgres: open via sql.Open("pgx", dsn) + entsql.OpenDB - Introduce PoolConfig (applied to *sql.DB); thread through OpenSQLite/OpenPostgres and update all callers - go mod tidy drops lib/pq * P0-2: add connection pool config to DatabaseConfig - DatabaseConfig gains MaxOpenConns / MaxIdleConns / ConnMaxLifetime plus ConnMaxLifetimeDuration() helper - DefaultGlobalConfig sets sqlite pool defaults (MaxOpenConns=1, load-bearing for write serialization) - applyDatabasePoolDefaults fills postgres defaults (20/5/30m) and forces sqlite MaxOpenConns=1; called in both load paths - Mirror fields in V1DatabaseConfig + both conversion directions - Wire pool settings into entc.OpenSQLite in initStore * P0-3/P0-4: CRUD-parity test harness + spec-driven fixture generator P0-3: pkg/store/storetest/ — backend-agnostic, table-driven CRUD oracle. A Factory(t) -> store.Store is injected; generic Domain[T] descriptors drive Create/Read/Update/Delete (+optional soft-delete)/List-paginate/List-filter. Ships group + policy domains and runs green against today's CompositeStore (SQLite base + Ent DB). Ready to accept a postgresFactory for P3-2. P0-4: internal/fixturegen/ — Go-defined spec seeding >=1 row per table across all 30 domain tables, with edge cases (NULL optionals, max-length strings, nested/unicode JSON, soft-deleted agent, BLOB). Deterministic. 'go run ./internal/fixturegen' emits testdata/hub-v46-fixture.db, prints a 30-table coverage report, and caches the blob to the scratchpad mount. CI gate fails if any table has zero rows. * feat(ent): add 23 new Ent schemas for full table parity (P1-2 + P1-3) * feat(observability): add Cloud Monitoring scaffolding for LISTEN/NOTIFY metrics (P0-5) * P2: port notification + gcp/github/token domains to Ent entadapter Add Ent-backed implementations of the notification, GCP service account, GitHub App installation, and user access token store sub-interfaces: - notification_store.go: NotificationStore (subscriptions, notifications, templates). Dispatch uses an atomic conditional update as the multi-replica claim primitive, and an optional NotificationPublisher designs in the LISTEN/NOTIFY fan-out for created/dispatched events. - external_store.go: GCPServiceAccountStore + GitHubInstallationStore + UserAccessTokenStore. GitHub create is idempotent (INSERT OR IGNORE semantics), repositories/scopes are JSON, default_scopes is CSV, and tokens support key-hash lookup. Legacy api_keys is intentionally not surfaced. - storetest: add GCPServiceAccount, SubscriptionTemplate, and NotificationSubscription CRUD-parity domains. Does not modify composite.go. * P2: port schedule, maintenance, message domains to Ent entadapter - schedule_store.go: ScheduleStore + ScheduledEventStore sub-interfaces with dialect-aware SELECT FOR UPDATE SKIP LOCKED claim helper for the ListDueSchedules / ListPendingScheduledEvents job-claim paths (plain SELECT on SQLite, SKIP LOCKED on Postgres). - maintenance_store.go: run-state RMW, AbortRunningMaintenanceOps, Go-side seed (uuid.New) replacing SQLite randomblob() UUID seeds. - message_store.go: CRUD, read flags, PurgeOldMessages, design-in PublishUserMessage hook for Postgres LISTEN/NOTIFY. - pkg/ent/client_driver.go: hand-written Client.Driver() accessor for dialect detection + raw locking queries. * feat(entadapter): port user + allowlist/invite domains to Ent (P2) Implements the Ent-backed store adapters for the user and allowlist/invite domains, plus their CRUD-parity oracle descriptors. pkg/store/entadapter/user_store.go (store.UserStore): - CreateUser/GetUser/GetUserByEmail/UpdateUser/UpdateUserLastSeen/ DeleteUser/ListUsers. - Case-insensitive email: emails are normalized to lower case on write (so the plain unique index enforces case-insensitive uniqueness, equivalent to the legacy UNIQUE COLLATE NOCASE) and matched with EmailEqualFold (lower(email)=lower($1)) on read. ent codegen + AutoMigrate cannot emit a real lower(email) functional index across both SQLite (tests) and Postgres, so the invariant is enforced at the port layer. - Offset-based pagination matching the legacy SQLite store. pkg/store/entadapter/allowlist_store.go (store.AllowListStore + store.InviteCodeStore): - Full allow-list + invite-code CRUD. - BulkAddAllowListEntries uses CreateBulk + OnConflictColumns(email). Ignore() for race-safe INSERT-OR-IGNORE; added/skipped counts mirror the legacy per-row semantics (existing + within-batch dups skipped). - IncrementInviteUseCount is a single atomic conditional UPDATE (revoked=false AND not expired AND (max_uses=0 OR use_countkey projection, version bump on update, get-then-update upsert, and transitive ListProgenySecrets via a created_by IN-list over the ancestor set (user scope + allow_progeny only; encrypted value withheld). - entadapter/template_store.go: TemplateStore implementing store.TemplateStore + store.HarnessConfigStore. base_template hierarchy, scope/project_id backwards-compat lookups, content_hash, JSON config/files columns, DeleteByScope. Subscription templates are owned by NotificationStore. - Direct Ent unit tests incl. a progeny-inheritance parity test. - storetest: Template/HarnessConfig/Secret/EnvVar domain descriptors wired into RunStoreSuite for cross-backend CRUD parity. * P2: port project/broker + brokersecret domains to Ent Port the project/broker domain (projects, runtime_brokers, project_contributors, project_sync_state) and the broker-auth domain (broker_secrets, broker_join_tokens) from raw SQL to Ent adapters. - pkg/store/entadapter/project_store.go: implements ProjectStore, RuntimeBrokerStore, ProjectProviderStore and ProjectSyncStateStore. * provider + sync-state upserts use Ent OnConflict().UpdateNewValues() (sql/upsert) keyed on the (project_id, broker_id) unique index. * runtime broker heartbeat/update use an optimistic version-CAS loop on a new internal lock_version token, serializing concurrent writers portably across SQLite (tests) and Postgres without SELECT ... FOR UPDATE. * slug lookups support case-insensitive matching (EqualFold). * project computed fields (AgentCount, ActiveBrokerCount, ProjectType) are derived via Ent queries, matching the legacy SQLite store. - pkg/store/entadapter/brokersecret_store.go: implements BrokerSecretStore (per-broker HMAC secrets + short-lived join tokens, expiry cleanup). - Project Ent schema: add operational fields for full parity (default_runtime_broker_id, shared_dirs, github_*, git_identity). - RuntimeBroker Ent schema: relax vestigial type column to Optional, add internal lock_version concurrency token. - Regenerate Ent with sql/upsert,sql/lock features. - storetest: add Project, RuntimeBroker, BrokerSecret and BrokerJoinToken CRUD-parity domains. - Unit tests for both adapters. Per the integration plan, composite.go wiring and ensureEntProject shadow removal are deferred to P2-collapse. * P2: port agent domain to Ent entadapter (XL) * chore(ent): regenerate Ent code for all 30 entity schemas Regenerated with --feature sql/upsert,sql/lock to support OnConflict upserts and ForUpdate/SKIP LOCKED job claims. * P2-collapse: collapse dual-DB into single Ent store Wire all Ent-backed sub-stores into CompositeStore via embedding, removing the raw-SQL base store and the User/Agent/Project shadow-sync machinery (ensureEntUser/ensureEntAgent/ensureEntProject). CompositeStore now serves every domain from a single Ent client and implements Close/Ping/Migrate directly. Collapse initStore() to open one Ent SQLite DB (no _ent shadow DSN, no MigrateGroveToProjectData, no raw sqlite.New). Register the User, AllowList, and InviteCode domains in the storetest CRUD-parity suite. Update entadapter tests for the single-DB NewCompositeStore(client) signature. go build ./... green; go test ./pkg/store/entadapter/... ./pkg/store/storetest/... green. * P2-delete: remove raw-SQL store implementation Delete the ~6k-LOC raw-SQL store (sqlite.go) and its per-domain sibling files (brokersecret, gcp_service_account, github_installation, maintenance, messages, notification, project_sync_state, schedule, scheduled_event) plus their tests, including the inline schema-migration scaffold. Keep driver.go, which registers the pure-Go SQLite driver used by Ent's SQLite backend. Repoint the two non-test consumers to the Ent-backed store: - cmd/hub_secret_migrate.go now opens an Ent client + CompositeStore. - internal/fixturegen opens via entc and seeds the Ent schema's *sql.DB. go build ./... green; no remaining production references to the raw store. * test: compile-migrate downstream suites to Ent store + fix signing-key PK Replace the removed raw-SQL store in downstream tests with an Ent-backed newTestStore helper (pkg/hub, pkg/secret) and update cmd/server_test.go and internal/fixturegen tests. Port the 8 raw-SQL DB() access sites in hub tests via a new CompositeStore.DB() escape-hatch accessor. Fix a production bug surfaced by the collapse: hub/server.go signingKeySecretID generated a non-UUID secret primary key, which the Ent secret store rejects; it now derives a deterministic UUIDv5. go build ./... green; entadapter and storetest suites green. NOTE: hub/secret/fixturegen suites now COMPILE but many tests still fail because their fixtures seed non-UUID string IDs that the UUID-PK Ent schema rejects; addressed in follow-up commits (tid() helper). * test(hub): map non-UUID fixture IDs to UUIDs via tid() helper Wrap human-readable test identifiers in tid() (deterministic UUIDv5) so the UUID-PK Ent store accepts them while preserving cross-reference consistency and ID-equality assertions. Reduces pkg/hub failures from 611 to 79; remaining failures are behavioral, not ID-format, and are addressed separately. # Conflicts: # pkg/hub/handlers_project_test.go # pkg/hub/httpdispatcher_test.go * fix(store): seed maintenance ops in Migrate; initStore uses Migrate Restore raw-SQL parity: CompositeStore.Migrate now runs AutoMigrate and seeds built-in maintenance operations (the raw store seeded these in its migrations). initStore and hub test helpers call s.Migrate() so production and tests seed consistently. Fixes the maintenance-operation hub tests (404 'Operation not found'). pkg/hub failures 79 -> 71. * test(hub): satisfy Ent NotEmpty validators in fixtures Add slugs/broker names to test fixtures that previously relied on the raw store's lenient (no-validator) inserts: project/agent slugs in the logs test helper, broker slugs in embedded/profile/authz fixtures, and BrokerName on envgather ProjectProvider literals. pkg/hub failures 71 -> 57. * test(secret): map non-UUID fixture IDs to UUIDs via tid() Apply the tid() helper to pkg/secret fixtures (including a dynamically built secret ID) so the UUID-PK Ent store accepts them. pkg/secret now fully green. * test(cmd): map non-UUID fixture IDs to UUIDs via tid(); add broker slug/name Wrap broker/grove/agent IDs passed to registerGlobalProjectAndBroker and the dispatcher tests in tid(), and supply RuntimeBroker.slug / ProjectContributor broker_name to satisfy Ent validators. cmd now green except TestDeleteStopped_RequiresGroveContext, which requires the 'docker' binary (absent in this sandbox) and is unrelated to the store migration. # Conflicts: # cmd/server_dispatcher_test.go * test(hub): wrap remaining latent non-UUID fixture IDs Catch IDs that surfaced behind earlier failures (stale-agent-*, agent-visible-authz, agent-profile-hb, env-owner-1). No more UUID-parse errors in pkg/hub; the remaining ~56 failures are behavioral (URL paths built from old raw IDs, assertion mismatches), addressed next. * fix(entadapter): Get-by-id returns ErrNotFound for non-UUID identifiers Restore raw-SQL store parity: a malformed identifier cannot match any UUID primary key, so get-by-id lookups now report store.ErrNotFound instead of store.ErrInvalidInput. This matches the raw store (a lookup with a bad id simply returned no row) and is what callers depend on — e.g. resolveTemplate passes a template *name* to GetTemplate and relies on ErrNotFound to fall back to slug-based resolution. New parseGetID helper applied across all 17 get-by-id methods. pkg/hub failures 56 -> 40; entadapter/storetest stay green. * test(hub): fix store-less id wraps and project-route URL paths - controlchannel_client_test: revert tid() wraps (store-less path-builder test; IDs must match the expected literal paths). - github/envgather: project-scoped route handlers resolve the project by UUID id, so build paths with tid(rawID) via fmt.Sprintf instead of the old raw-id literal. pkg/hub failures 40 -> 32. * test(hub): unwrap projectIDFromServiceAccountEmail expectation The tid() sweep over-wrapped a non-ID expected value in a pure-function test; restore the literal GCP project id. * fix(ent): GCPServiceAccount.project_id is a string, not a UUID The GCP service account project_id holds the GCP *cloud project* identifier (e.g. 'my-project-123'), a free-form string — not a UUID. The schema declared it field.UUID, so entadapter CreateGCPServiceAccount/Update did parseUUID(sa.ProjectID) and rejected real GCP project ids, breaking SA mint/create with a 400 in production (storetest masked it by passing a UUID). Change the schema field to field.String, regenerate Ent, and store/read project_id as a string in external_store.go. Fixes ~7 hub GCP tests; pkg/hub 31 -> 23. * test(hub): fix GCP SA project-id assertion and project-settings id Unwrap the over-wrapped 'my-project' expectation now that project_id is a string, and wrap the dynamic project-settings project ID with tid(). * test(hub): fix bootstrap sync-to-finalize agent paths and storage keys Build the finalize request path from the agent's tid() UUID and seed mock storage under WorkspaceStoragePath(projectID, agent.ID) — the handler derives the workspace key from the agent's real id, not the old raw name. pkg/hub 23 -> 19. * test(hub): revert tid() over-wraps in store-less events_test events_test exercises the in-memory ChannelEventPublisher directly; its ProjectID/IDs are subject-string components, not stored UUIDs. The tid() sweep wrongly rewrote them so published subjects no longer matched the subscriptions (timeouts). Restore the literal values. pkg/hub 19 -> 12. * test(hub): fix maintenance-run path and notifications agentId queries Use tid() UUIDs in the maintenance run-detail path and the notifications agentId query params; guard list indexing with require.Len so a mismatch fails cleanly instead of panicking (panics truncate the package run). * test(hub): wrap remaining fixture IDs revealed after panic-cascade cleared Panics ([0] on empty lists) had been truncating the package run, hiding many failures and starving the tid() sweep. With those guarded, sweep the newly reached tests: wrap dynamic rune-suffix IDs and the setupProjectWithBroker / seedCreatedAgentForHarnessTest helper IDs, and convert raw query-param project IDs to tid(). No UUID-parse errors remain in pkg/hub. * test(hub): unwrap tid() in scheduler_test (mock store, raw ids) scheduler_test uses an in-memory mockScheduledEventStore, not the Ent store, so its ids need no UUIDs; the erroneous tid() wraps broke raw getEvent lookups and caused a nil-pointer panic that truncated the package run. * fix(ent): Template.harness may be empty (raw-store parity) A template imported from a directory that declares no harness type has an empty harness; the raw-SQL store stored it, but the Ent NotEmpty validator made BootstrapTemplatesFromDir silently skip such templates. Drop NotEmpty and regenerate. Removing the [0]-on-empty panics this caused un-truncates the hub package run (true failure count now visible). * test(hub): wrap dynamic fixture IDs in wake/workspace/signing-key tests Wrap tid() around the wake_test, setupWorkspaceProject, and empty-value signing-key secret IDs now reachable after panic removal. No panics in the hub package run. * test(hub): convert raw-id URL path segments to tid() Build GET/PUT/DELETE paths for agents/projects/brokers/templates/harness-configs and workspace sync routes from tid(rawID) so the by-id handlers resolve the entity (raw ids no longer match the UUID PKs). pkg/hub 93 -> 80. * fix(entadapter)+test(hub): FK error mapping + permissions FK fixtures mapError now distinguishes foreign-key violations (-> ErrInvalidInput, a bad reference) from unique-constraint violations (-> ErrAlreadyExists); previously both surfaced as a misleading 'already exists'/409. Seed the users/agents that group memberships and policy bindings reference (the Ent store enforces user/agent FK edges the raw store lacked), wrap remaining raw fixture/URL ids in tid(), and give the AddAgent fixtures slugs. All pkg/hub permissions tests pass. * fix(hub): seed creator users for agent-created agents; cascade-delete subscriptions on hard agent delete * test(hub): seed broker slug/name in dispatcher and project_cache fixtures (Ent validators) * test(hub): use tid() in principal/agent URL paths; broker slug in template_bootstrap * fix(entadapter): cascade-delete agents on project delete (raw-store parity); test(hub): seed FK users, broker_name, deterministic UUIDs * test(hub): MaxOpenConns=1 for SQLite test store (serialize writes); tid() URLs + FK user seeds in events/stopall * test(hub): unwrap over-wrapped tid() in unit tests (workspace/logfilter/gcp/web); valid-UUID NotFound cases; tid() scheduled-event URLs * fix(ent): allow empty display_name (raw-store NOT NULL parity, email fallback); test(hub): seed FK owner users, UUID policy/broker/agent IDs in authz remediation * feat(migrate): add Migration β tool (Ent-SQLite → Ent-Postgres) Implements 'scion server migrate --from sqlite://... --to postgres://...' per postgres-strategy.md §7.3. - entc.OpenSQLiteReadOnly: opens source with PRAGMA query_only=ON (no WAL write), MaxOpenConns=1 so the source is never mutated. - entc.MigrateData: generic reflection-based, dependency-ordered copy of all 30 Ent entities (FK-ordered core first), idempotent (skips rows whose PK already exists), atomic per entity (txn), chunked CreateBulk, source/dest row-count verification after each entity, plus the Group.child_groups M2M edge. FK columns are plain fields so edges are preserved via setters. - cmd/server migrate: DSN parsing (sqlite://, file:, bare path; postgres URL or keyword form), --keep-source default / --drop-source cutover, progress logging. Verified end-to-end against live CloudSQL Postgres 16 (integration test + real CLI run): full copy, idempotent re-run, FK + M2M + value round-trips, --drop-source removal. * feat(concurrency): dialect-aware multi-replica primitives for Postgres (P3-3..6) Add cluster-coordination primitives so N stateless hub processes can share one Postgres, each degrading to a no-op on single-writer SQLite: - store.AdvisoryLocker + entadapter TryAdvisoryLock (pg_try_advisory_lock on a dedicated conn); Scheduler.RegisterRecurringSingleton gates the heartbeat, stalled, purge, schedule-evaluator and github-health sweeps to one replica/tick. - store.ScheduledEventClaimer + ClaimScheduledEvent atomic claim; fireEvent claims one-shot events before side effects (dedup across replica startup recovery). - CompositeStore.RunSerializable: SERIALIZABLE + retry on 40001/40P01 (single run on SQLite) for future multi-row invariants. - dbmetrics.StartPoolSampler feeds DB connection-pool gauges to the P0-5 scaffold; wired into StartBackgroundServices via SetDBMetrics. Verified existing primitives correct (agent StateVersion CAS, FOR UPDATE sweeps, notification atomic dispatch). Found and documented the schedule SKIP LOCKED early-commit gap (lock released before the status transition), closed by the singleton evaluator. Audit + budget docs in scratchpad. Tests: locking_test.go (advisory no-op, serializable, claim exactly-once incl. 8-way concurrent), pool_sampler_test.go. * feat(hub): widen events to EventPublisher interface + Postgres LISTEN/NOTIFY publisher P3-7: Decouple call sites from the concrete *ChannelEventPublisher. - Add Subscribe(patterns...) (<-chan Event, func()) to the EventPublisher interface; implement it on noopEventPublisher (nil channel) — *ChannelEventPublisher already had it. - Factor the Publish* methods into a shared eventBuilder (sink func) so every backend emits identical subjects/payloads; ChannelEventPublisher embeds it. - web.go (field + SetEventPublisher), messagebroker.go and notifications.go (field + constructor) now take EventPublisher; handlers_messages.go gates SSE on "not the no-op publisher" instead of a concrete type assertion. P3-8: PostgresEventPublisher over pgx LISTEN/NOTIFY (cross-replica delivery). - Per-grove channels plus a global channel (flat exact-match); event type in the JSON envelope. Grove-scoped subjects publish to both the grove channel and the global channel; subscriptions group their patterns by resolved channel so an event is matched only against patterns that opted into the arriving channel (no double delivery). - 8 KB NOTIFY limit handled by reference-and-refetch via scion_event_payloads (TTL-swept so every replica can refetch). - PublishTx enrolls the NOTIFY in a caller transaction (atomic write+publish; rollback => no deliver). Delivery flows exclusively through the listener. - Listener goroutine reconnects with backoff and re-LISTENs (resubscribe); dynamic LISTEN/UNLISTEN applied on a poll (WaitForNotification timeout does not invalidate the pgconn connection). - Emits pkg/observability/dbmetrics signals (published/delivered/dropped, payload size, publish->deliver latency, reconnects, pool stats). - cmd: newEventPublisher selects the backend by database driver (postgres => PostgresEventPublisher, else ChannelEventPublisher) with safe fallback. Tests: routing/registry/payload-offload/metrics/transactional-executor unit tests run without a DB; cross-replica delivery, oversized round-trip, transactional rollback, and reconnect+resubscribe are gated behind SCION_TEST_POSTGRES_DSN. go build ./... green; full pkg/hub suite green. Note: server.go's equivalent type-assertion cleanup is left in the working tree (co-edited with concurrent P0-5/scheduler work) and is functionally optional — HEAD server.go already compiles against the widened interface. * test(store): parameterize store suites over {sqlite, postgres} (P3-2) Add pkg/store/enttest: a backend-selecting Ent client factory for the store test suites. Default is in-memory SQLite; built with -tags integration and SCION_TEST_POSTGRES_URL set, it provisions a per-package ephemeral Postgres database (created/dropped via TestMain) and isolates each test in its own schema (search_path) so tests never observe each other's rows. Falls back to SQLite when the env var is unset. Route all entadapter and storetest helpers through enttest.NewClient so the same CRUD-parity oracle runs unchanged against either backend. Fix two real Postgres bugs surfaced by the new path: - entadapter/dialect.go ancestryContains: emit the bind parameter via Builder.Arg ($n on Postgres) instead of a literal '?' through ExprP, which was not rebound and produced a syntax error; and use jsonb_array_elements_text (the column is jsonb on Postgres, not json). - schedule_store_test ClaimPath: make the concurrent-claim assertion backend-aware. SQLite serializes (MaxOpenConns=1, no SKIP LOCKED) so every caller sees both due rows; Postgres uses FOR UPDATE SKIP LOCKED so concurrent callers may observe a disjoint subset (0..2) and must only never error or exceed 2. Verified: full SQLite suite green; storetest CRUD parity green on CloudSQL Postgres; entadapter green on Postgres (schedule ClaimPath fix confirmed). * fix(hub): start dispatcher/broker for any subscription-capable EventPublisher Wave C integration: newEventPublisher can now return a PostgresEventPublisher (LISTEN/NOTIFY) in addition to ChannelEventPublisher. The dispatcher/broker startup previously hard-asserted *ChannelEventPublisher, which silently skipped starting them under Postgres. Gate on (not noop and not nil) instead, matching the existing pattern in handlers_messages.go. * fix(hub): harden Postgres event publish + verify wiring; lower PG pool default Task 1 — LISTEN/NOTIFY publish path: - Add TestPostgresIntegration_HandlerCreateProjectEmitsNotify: drives the real POST /api/v1/projects handler with a PostgresEventPublisher and asserts a pg_notify lands on scion_ev_global via an independent raw LISTEN — the exact capability the multi-replica live test probed. Verified PASSING against live CloudSQL, proving the handler -> s.events -> pg_notify wiring is correct end to end (the four pre-existing SCION_TEST_POSTGRES_DSN integration tests also pass). The multi-hub 'no NOTIFY' symptom was not reproducible against the current tree. - Bound the autocommit publish (Publish* methods) with publishTimeout (5s). These run synchronously on the caller's (request handler) goroutine and acquire from the event pool; on a connection-starved instance that acquire could block indefinitely, stalling CRUD and silently never emitting NOTIFY. The timeout converts that into a logged error + dropped event (publishing is fire-and-forget). PublishTx (transactional path) is unaffected. Task 2 — connection budget: - Lower the default Postgres MaxOpenConns 20 -> 10 so multiple replicas fit a modest connection budget (see CONNECTION-BUDGET.md). CloudSQL instance scion-postgres-test resized db-f1-micro -> db-g1-small and max_connections set to 100 (out of band). * test(store): add Postgres stress/integration suite (contention, isolation, pool, NOTIFY, migration, schema, multi-process) Add pkg/store/integrationtest/: a Postgres-only suite that exercises behavior the SQLite parity suites cannot reach. Gated by //go:build integration and SCION_TEST_POSTGRES_URL; skips cleanly otherwise. Coverage: - Contention: state_version CAS race (no lost updates, >=N-1 retries, final version==1+N), SKIP LOCKED / conditional-UPDATE event claim (single winner + disjoint drain), unique-key races (project slug, user email, agent slug). - Isolation: SERIALIZABLE conflict + RunSerializable retry recovery, REPEATABLE READ no-phantom snapshot, READ COMMITTED dirty-read prevention. - Pool: exhaustion + queued recovery, saturated pool honoring context deadline, long txn not starving short queries, healing after pg_terminate_backend. - LISTEN/NOTIFY: ordered burst no-drop, 8000B payload limit, listener reconnect/resume, cross-channel isolation. - Migration: 1000+ row counts + bounded-memory listing, idempotent re-migration. - Schema: NULL semantics, unicode/emoji, nested JSON + special chars, large-text non-truncation, TIMESTAMPTZ microsecond precision. - Multi-process: forks the test binary for cross-process advisory-lock exclusivity and cross-process NOTIFY delivery. Configurable concurrency via SCION_TEST_CONCURRENCY (default 10). Extend pkg/store/enttest with Active() and NewSchemaURL() so tests can open custom-pool clients and share a DSN with forked child processes; non-integration stubs keep the package API stable. * fix(db): recycle stale conns + keepalives; skip singleton tick on lock error Stale-connection pool stalls (CloudSQL drops idle conns after ~10m): - Add ConnMaxIdleTime to DatabaseConfig/PoolConfig (default 5m pg, 0 sqlite) and apply SetConnMaxIdleTime on the database/sql pool. - OpenPostgres now parses the DSN with pgx and opens via stdlib.OpenDB with TCP keepalive GUCs (idle 60s / interval 15s / count 4) and a 10s connect timeout, so a silently-dropped peer is detected instead of the first query after idle hanging on a dead socket. - pgx event pool (events_postgres.go): set keepalives + connect timeout on both the pool's ConnConfig and the dedicated listener connection, plus MaxConnIdleTime 5m / MaxConnLifetime 30m. Advisory-lock leader election (scheduler.go): - A lock-acquisition error no longer falls open to running the handler unguarded (which would duplicate singleton work across replicas); the tick is skipped and retried next interval. Added regression tests. Test harness (enttest/integrationtest): - Accept libpq keyword/value DSNs (not just URL form) when deriving the ephemeral db/schema/params; add WithConnParam helper. - Fix migration idempotency test's per-pass row-count expectation. * fix(store): bound advisory-lock conn checkout + unlock with short timeout TryAdvisoryLock checked a connection out of the pool and ran the unlock on the full 55s scheduler-handler context (acquire) and an unbounded context.Background() (release). On a pool that could not promptly serve a healthy connection, db.Conn() blocked for the entire 55s before failing with 'context deadline exceeded' on every tick; with several singleton handlers firing each 60s tick, those long-blocked goroutines and their pending pool connection requests piled up across ticks and kept the pool jammed (checked out client-side, idle server-side). The unbounded unlock was a second leak vector: if the held connection died mid critical-section, ExecContext could hang forever, so conn.Close() never ran and the connection leaked out of the pool permanently. Bind both the acquire (db.Conn + pg_try_advisory_lock) and the release (pg_advisory_unlock) to a 5s timeout so a bad tick fails fast and retries next tick instead of parking a goroutine for ~55s, and so a dead connection can never block release from freeing the conn. Lock semantics are unchanged: cancelling the acquire context tears down only that context, not the checked-out session that holds the lock. * feat(migrate): in-process migration α (legacy raw-SQL hub.db → Ent) Upgrade a legacy raw-SQL Hub database (the ~53-migration, 30-table schema from the removed pkg/store/sqlite store) to the consolidated Ent-backed SQLite schema, in-process on first boot, behind an automatic backup. pkg/ent/entc/migrate_alpha.go: - IsLegacyRawSQLSchema: detect via the schema_migrations sentinel + the legacy-only agents.agent_id column (no-op for an Ent/empty/absent file). - MigrateAlphaSQLite: backup (checkpoint WAL + copy to hub.db.bak.), AutoMigrate a fresh Ent schema, ATTACH the legacy file, copy every table with INSERT…SELECT (foreign_keys OFF), verify per-table row counts, then atomically swap the migrated file into place. - Data-driven column mapping (created_at→created, updated_at→updated, agents.agent_id→slug, policies→access_policies); bespoke SQL for the group_members/policy_bindings polymorphic splits and surrogate ids; groups.parent_id→group_child_groups edge. - Deterministic UUIDv5 remap for legacy non-UUID primary keys (internal signing-key secrets; plugin runtime-broker ids) with consistent rewrite of every foreign-key reference via a TEMP _id_remap table. - Tolerates missing legacy tables (older schema versions). cmd/server_foreground.go: detect + migrate in initStore's sqlite path, with a --no-auto-migrate operator opt-out (cmd/server.go). Validated end-to-end against four production hub.db files (scion-integration, -integration2, -demo, -gteam): exact row-count parity (up to ~19k rows), every entity reads back through the live Ent store, idempotent re-runs, and broker FK references resolve post-remap. Pre-existing dangling agent created_by/owner_id refs are faithfully preserved (loader runs FK-off). * fix(config): apply real Postgres pool size (leaked SQLite default of 1 starved the pool) The struct-level default for Database.MaxOpenConns/MaxIdleConns is 1 — the value SQLite REQUIRES to serialize writes. applyDatabasePoolDefaults only bumped postgres to a real pool when the value was <= 0, but a postgres deployment configured via env/driver override inherits the embedded default of 1, so the guard never fired and the Ent pool ran with a SINGLE connection. Effect in production (both integration hubs): every singleton scheduler tick checks out the lone pool connection to hold its advisory lock, then blocks waiting for a second connection to do its work — a self-deadlock that resolves only at the 55s handler context deadline. All API requests serialize behind the one connection, so GET /api/v1/* served in ~55s across the board. Note env overrides could not paper over this: envKeyToConfigKey splits on every underscore, so SCION_SERVER_DATABASE_MAX_OPEN_CONNS maps to database.max.open.conns, not database.max_open_conns — silently ignored. Treat the leaked SQLite default (<= 1) as 'unset' for postgres so the pool default (10) applies; explicit sizing of 2+ is still respected. SQLite remains pinned to 1. Adds regression tests for all three cases. * docs: add multi-node broker dispatch and NFS workspace designs - broker-dispatch.md: DB-as-state-machine + LISTEN/NOTIFY pattern for cross-replica broker command routing and agent lifecycle dispatch - nfs-workspace.md: NFS workspace coordination for VM (host bind-mount) and K8s/Cloud Run (per-pod mount) runtime models * fix(store): address PR #304 review — context leaks and DSN parsing Thread the server's cancellable context into initStore and initWebServer instead of using context.Background(), so that: - DB migrations and the health-check ping cancel on Ctrl+C during startup (medium-priority review comment). - The Postgres LISTEN/NOTIFY event publisher goroutine shuts down cleanly when the server exits, preventing connection leaks (high-priority review comment). Also fix parseSQLiteSourceDSN to handle the file:// prefix before the file: prefix, so that file:///var/lib/hub.db correctly resolves to /var/lib/hub.db instead of ///var/lib/hub.db. Add test cases for file:// and file:/// DSN forms. * docs: add project log for PR #304 review fixes * fix(store): context leak in legacy migration & double file: prefix 1. Thread the server's cancellable context through maybeMigrateLegacySQLite → MigrateAlphaSQLite so that Ctrl+C during first-boot legacy migration aborts it instead of running with an uncancellable context.Background(). 2. Guard against a double "file:" prefix when constructing the SQLite DSN. If the operator's database.url already starts with "file:", we no longer blindly prepend another "file:" prefix. Also correctly appends cache=shared with "&" when the DSN already contains query parameters. * fix(store): rename ProjectTypeHubNative → ProjectTypeHubManaged (rebase fixup) Upstream renamed hub-native to hub-managed while the PR was in flight. Update the two remaining references that the rebase conflict resolution missed. --------- Co-authored-by: Scion --- .claude/scheduled_tasks.lock | 1 + .design/broker-dispatch.md | 772 + .design/nfs-workspace.md | 740 + .../2026-06-04-pr304-review-fixes.md | 30 + .gitignore | 6 + .scion/project-id | 1 + cmd/common_envgather_test.go | 50 +- cmd/hub_secret_migrate.go | 9 +- cmd/list_test.go | 4 +- cmd/message_test.go | 4 +- cmd/server.go | 2 + cmd/server_dispatcher_test.go | 42 +- cmd/server_foreground.go | 169 +- cmd/server_migrate.go | 212 + cmd/server_migrate_test.go | 141 + cmd/server_test.go | 54 +- cmd/tid_test.go | 23 + .../tg_1780415922_photo_AQADQgxrG_Tb-UR-.jpg | Bin 0 -> 160057 bytes go.mod | 5 +- go.sum | 13 +- internal/fixturegen/fixturegen_test.go | 89 + internal/fixturegen/generate.go | 180 + internal/fixturegen/main.go | 123 + internal/fixturegen/spec.go | 318 + pkg/config/hub_config.go | 106 + pkg/config/hub_config_test.go | 45 + pkg/config/settings_v1.go | 28 +- pkg/ent/accesspolicy_create.go | 1001 + pkg/ent/accesspolicy_query.go | 37 + pkg/ent/agent.go | 304 +- pkg/ent/agent/agent.go | 195 + pkg/ent/agent/where.go | 1551 +- pkg/ent/agent_create.go | 2712 +- pkg/ent/agent_query.go | 37 + pkg/ent/agent_update.go | 1451 +- pkg/ent/allowlistentry.go | 151 + pkg/ent/allowlistentry/allowlistentry.go | 95 + pkg/ent/allowlistentry/where.go | 406 + pkg/ent/allowlistentry_create.go | 746 + pkg/ent/allowlistentry_delete.go | 88 + pkg/ent/allowlistentry_query.go | 565 + pkg/ent/allowlistentry_update.go | 365 + pkg/ent/apikey.go | 202 + pkg/ent/apikey/apikey.go | 125 + pkg/ent/apikey/where.go | 596 + pkg/ent/apikey_create.go | 1053 + pkg/ent/apikey_delete.go | 88 + pkg/ent/apikey_query.go | 565 + pkg/ent/apikey_update.go | 565 + pkg/ent/brokerjointoken.go | 140 + pkg/ent/brokerjointoken/brokerjointoken.go | 82 + pkg/ent/brokerjointoken/where.go | 301 + pkg/ent/brokerjointoken_create.go | 644 + pkg/ent/brokerjointoken_delete.go | 88 + pkg/ent/brokerjointoken_query.go | 565 + pkg/ent/brokerjointoken_update.go | 314 + pkg/ent/brokersecret.go | 169 + pkg/ent/brokersecret/brokersecret.go | 95 + pkg/ent/brokersecret/where.go | 411 + pkg/ent/brokersecret_create.go | 819 + pkg/ent/brokersecret_delete.go | 88 + pkg/ent/brokersecret_query.go | 565 + pkg/ent/brokersecret_update.go | 392 + pkg/ent/client.go | 4187 +- pkg/ent/client_driver.go | 29 + pkg/ent/ent.go | 60 +- pkg/ent/entc/client.go | 116 +- pkg/ent/entc/client_test.go | 4 +- pkg/ent/entc/driver_postgres.go | 2 +- pkg/ent/entc/migrate_alpha.go | 728 + pkg/ent/entc/migrate_alpha_nosqlite.go | 60 + pkg/ent/entc/migrate_alpha_test.go | 296 + pkg/ent/entc/migrate_beta.go | 402 + pkg/ent/entc/migrate_beta_integration_test.go | 296 + pkg/ent/entc/migrate_grove_to_project_test.go | 2 +- pkg/ent/envvar.go | 219 + pkg/ent/envvar/envvar.go | 176 + pkg/ent/envvar/where.go | 651 + pkg/ent/envvar_create.go | 1130 + pkg/ent/envvar_delete.go | 88 + pkg/ent/envvar_query.go | 565 + pkg/ent/envvar_update.go | 600 + pkg/ent/gcpserviceaccount.go | 233 + .../gcpserviceaccount/gcpserviceaccount.go | 165 + pkg/ent/gcpserviceaccount/where.go | 761 + pkg/ent/gcpserviceaccount_create.go | 1187 + pkg/ent/gcpserviceaccount_delete.go | 88 + pkg/ent/gcpserviceaccount_query.go | 565 + pkg/ent/gcpserviceaccount_update.go | 624 + pkg/ent/generate.go | 2 +- pkg/ent/githubinstallation.go | 172 + .../githubinstallation/githubinstallation.go | 114 + pkg/ent/githubinstallation/where.go | 485 + pkg/ent/githubinstallation_create.go | 860 + pkg/ent/githubinstallation_delete.go | 88 + pkg/ent/githubinstallation_query.go | 564 + pkg/ent/githubinstallation_update.go | 428 + pkg/ent/group_create.go | 781 + pkg/ent/group_query.go | 37 + pkg/ent/groupmembership_create.go | 521 + pkg/ent/groupmembership_query.go | 37 + pkg/ent/harnessconfig.go | 329 + pkg/ent/harnessconfig/harnessconfig.go | 261 + pkg/ent/harnessconfig/where.go | 1506 + pkg/ent/harnessconfig_create.go | 1927 + pkg/ent/harnessconfig_delete.go | 88 + pkg/ent/harnessconfig_query.go | 565 + pkg/ent/harnessconfig_update.go | 1130 + pkg/ent/hook/hook.go | 276 + pkg/ent/invitecode.go | 198 + pkg/ent/invitecode/invitecode.go | 135 + pkg/ent/invitecode/where.go | 546 + pkg/ent/invitecode_create.go | 1014 + pkg/ent/invitecode_delete.go | 88 + pkg/ent/invitecode_query.go | 565 + pkg/ent/invitecode_update.go | 534 + pkg/ent/maintenanceoperation.go | 223 + .../maintenanceoperation.go | 149 + pkg/ent/maintenanceoperation/where.go | 806 + pkg/ent/maintenanceoperation_create.go | 1168 + pkg/ent/maintenanceoperation_delete.go | 88 + pkg/ent/maintenanceoperation_query.go | 565 + pkg/ent/maintenanceoperation_update.go | 634 + pkg/ent/maintenanceoperationrun.go | 176 + .../maintenanceoperationrun.go | 111 + pkg/ent/maintenanceoperationrun/where.go | 541 + pkg/ent/maintenanceoperationrun_create.go | 909 + pkg/ent/maintenanceoperationrun_delete.go | 88 + pkg/ent/maintenanceoperationrun_query.go | 565 + pkg/ent/maintenanceoperationrun_update.go | 460 + pkg/ent/message.go | 241 + pkg/ent/message/message.go | 167 + pkg/ent/message/where.go | 806 + pkg/ent/message_create.go | 1286 + pkg/ent/message_delete.go | 88 + pkg/ent/message_query.go | 565 + pkg/ent/message_update.go | 702 + pkg/ent/migrate/schema.go | 853 +- pkg/ent/mutation.go | 31675 ++++++++++++++-- pkg/ent/notification.go | 208 + pkg/ent/notification/notification.go | 141 + pkg/ent/notification/where.go | 561 + pkg/ent/notification_create.go | 1008 + pkg/ent/notification_delete.go | 88 + pkg/ent/notification_query.go | 565 + pkg/ent/notification_update.go | 538 + pkg/ent/notificationsubscription.go | 189 + .../notificationsubscription.go | 123 + pkg/ent/notificationsubscription/where.go | 566 + pkg/ent/notificationsubscription_create.go | 922 + pkg/ent/notificationsubscription_delete.go | 88 + pkg/ent/notificationsubscription_query.go | 565 + pkg/ent/notificationsubscription_update.go | 478 + pkg/ent/policybinding_create.go | 601 + pkg/ent/policybinding_query.go | 37 + pkg/ent/predicate/predicate.go | 69 + pkg/ent/project.go | 76 +- pkg/ent/project/project.go | 48 + pkg/ent/project/where.go | 455 + pkg/ent/project_create.go | 1209 + pkg/ent/project_query.go | 37 + pkg/ent/project_update.go | 332 + pkg/ent/projectcontributor.go | 212 + .../projectcontributor/projectcontributor.go | 131 + pkg/ent/projectcontributor/where.go | 721 + pkg/ent/projectcontributor_create.go | 1140 + pkg/ent/projectcontributor_delete.go | 88 + pkg/ent/projectcontributor_query.go | 565 + pkg/ent/projectcontributor_update.go | 633 + pkg/ent/projectsyncstate.go | 167 + pkg/ent/projectsyncstate/projectsyncstate.go | 99 + pkg/ent/projectsyncstate/where.go | 411 + pkg/ent/projectsyncstate_create.go | 900 + pkg/ent/projectsyncstate_delete.go | 88 + pkg/ent/projectsyncstate_query.go | 565 + pkg/ent/projectsyncstate_update.go | 457 + pkg/ent/runtime.go | 741 +- pkg/ent/runtimebroker.go | 323 + pkg/ent/runtimebroker/runtimebroker.go | 227 + pkg/ent/runtimebroker/where.go | 1426 + pkg/ent/runtimebroker_create.go | 1871 + pkg/ent/runtimebroker_delete.go | 88 + pkg/ent/runtimebroker_query.go | 565 + pkg/ent/runtimebroker_update.go | 1078 + pkg/ent/schedule.go | 269 + pkg/ent/schedule/schedule.go | 187 + pkg/ent/schedule/where.go | 996 + pkg/ent/schedule_create.go | 1469 + pkg/ent/schedule_delete.go | 88 + pkg/ent/schedule_query.go | 565 + pkg/ent/schedule_update.go | 831 + pkg/ent/scheduledevent.go | 209 + pkg/ent/scheduledevent/scheduledevent.go | 135 + pkg/ent/scheduledevent/where.go | 711 + pkg/ent/scheduledevent_create.go | 1086 + pkg/ent/scheduledevent_delete.go | 88 + pkg/ent/scheduledevent_query.go | 565 + pkg/ent/scheduledevent_update.go | 591 + pkg/ent/schema/agent.go | 87 +- pkg/ent/schema/allowlist.go | 71 + pkg/ent/schema/apikey.go | 81 + pkg/ent/schema/brokerjointoken.go | 67 + pkg/ent/schema/brokersecret.go | 67 + pkg/ent/schema/envvar.go | 81 + pkg/ent/schema/gcpserviceaccount.go | 88 + pkg/ent/schema/githubinstallation.go | 65 + pkg/ent/schema/harnessconfig.go | 105 + pkg/ent/schema/invitecode.go | 76 + pkg/ent/schema/maintenanceoperation.go | 76 + pkg/ent/schema/maintenanceoperationrun.go | 76 + pkg/ent/schema/message.go | 87 + pkg/ent/schema/notification.go | 78 + pkg/ent/schema/notificationsubscription.go | 81 + pkg/ent/schema/project.go | 26 +- pkg/ent/schema/projectcontributor.go | 81 + pkg/ent/schema/projectsyncstate.go | 72 + pkg/ent/schema/runtimebroker.go | 111 + pkg/ent/schema/schedule.go | 94 + pkg/ent/schema/scheduledevent.go | 82 + pkg/ent/schema/secret.go | 94 + pkg/ent/schema/subscriptiontemplate.go | 68 + pkg/ent/schema/template.go | 119 + pkg/ent/schema/user.go | 29 +- pkg/ent/schema/useraccesstoken.go | 83 + pkg/ent/secret.go | 264 + pkg/ent/secret/secret.go | 236 + pkg/ent/secret/where.go | 941 + pkg/ent/secret_create.go | 1454 + pkg/ent/secret_delete.go | 88 + pkg/ent/secret_query.go | 565 + pkg/ent/secret_update.go | 820 + pkg/ent/subscriptiontemplate.go | 153 + .../subscriptiontemplate.go | 93 + pkg/ent/subscriptiontemplate/where.go | 404 + pkg/ent/subscriptiontemplate_create.go | 772 + pkg/ent/subscriptiontemplate_delete.go | 88 + pkg/ent/subscriptiontemplate_query.go | 565 + pkg/ent/subscriptiontemplate_update.go | 410 + pkg/ent/template.go | 373 + pkg/ent/template/template.go | 291 + pkg/ent/template/where.go | 1826 + pkg/ent/template_create.go | 2234 ++ pkg/ent/template_delete.go | 88 + pkg/ent/template_query.go | 565 + pkg/ent/template_update.go | 1328 + pkg/ent/tx.go | 69 + pkg/ent/user.go | 16 +- pkg/ent/user/user.go | 10 +- pkg/ent/user/where.go | 55 + pkg/ent/user_create.go | 684 +- pkg/ent/user_query.go | 37 + pkg/ent/user_update.go | 62 +- pkg/ent/useraccesstoken.go | 213 + pkg/ent/useraccesstoken/useraccesstoken.go | 139 + pkg/ent/useraccesstoken/where.go | 611 + pkg/ent/useraccesstoken_create.go | 1046 + pkg/ent/useraccesstoken_delete.go | 88 + pkg/ent/useraccesstoken_query.go | 565 + pkg/ent/useraccesstoken_update.go | 575 + pkg/hub/admin_maintenance_test.go | 11 +- pkg/hub/admin_mode_test.go | 6 +- pkg/hub/agenttoken_test.go | 6 +- pkg/hub/audit_gcp_test.go | 8 +- pkg/hub/authz_integration_test.go | 44 +- pkg/hub/authz_project_owner_test.go | 46 +- pkg/hub/authz_test.go | 224 +- pkg/hub/bootstrap_test.go | 49 +- pkg/hub/brokerauth_test.go | 7 +- pkg/hub/brokerclient_test.go | 57 +- pkg/hub/capabilities_test.go | 66 +- pkg/hub/capability_marshal_test.go | 16 +- pkg/hub/controlchannel_test.go | 14 +- pkg/hub/demo_policy_test.go | 56 +- pkg/hub/embedded_broker_test.go | 13 +- pkg/hub/envgather_resolution_test.go | 42 +- pkg/hub/envgather_test.go | 174 +- pkg/hub/events.go | 112 +- pkg/hub/events_integration_test.go | 12 +- pkg/hub/events_postgres.go | 744 + pkg/hub/events_postgres_test.go | 737 + pkg/hub/gcp_ratelimit_test.go | 16 +- pkg/hub/handlers_agent_test.go | 452 +- pkg/hub/handlers_auth_test.go | 2 +- pkg/hub/handlers_authz_remediation_test.go | 70 +- pkg/hub/handlers_broker_test.go | 22 +- pkg/hub/handlers_envsecret_authz_test.go | 106 +- pkg/hub/handlers_gcp_identity_test.go | 30 +- pkg/hub/handlers_github_app_test.go | 56 +- pkg/hub/handlers_github_app_webhook_test.go | 44 +- pkg/hub/handlers_logs_test.go | 2 + pkg/hub/handlers_messages.go | 11 +- pkg/hub/handlers_notifications_test.go | 63 +- pkg/hub/handlers_permissions_test.go | 302 +- pkg/hub/handlers_principals_test.go | 42 +- pkg/hub/handlers_project_test.go | 106 +- pkg/hub/handlers_scheduled_events_test.go | 20 +- pkg/hub/handlers_schedules_test.go | 6 +- pkg/hub/handlers_stopall_test.go | 53 +- pkg/hub/handlers_test.go | 181 +- pkg/hub/harness_capabilities_test.go | 6 +- pkg/hub/harness_config_handlers_test.go | 41 +- pkg/hub/heartbeat_timeout_test.go | 3 +- pkg/hub/httpdispatcher_test.go | 510 +- pkg/hub/logquery_test.go | 2 +- pkg/hub/maintenance_executors_test.go | 3 +- pkg/hub/messagebroker.go | 4 +- pkg/hub/messagebroker_test.go | 11 +- pkg/hub/notifications.go | 4 +- pkg/hub/notifications_integration_test.go | 35 +- pkg/hub/notifications_test.go | 11 +- pkg/hub/project_cache_test.go | 29 +- pkg/hub/project_settings_handlers_test.go | 2 +- pkg/hub/project_workspace_handlers_test.go | 8 +- pkg/hub/resolve_secrets_test.go | 70 +- pkg/hub/scheduler.go | 76 + pkg/hub/scheduler_test.go | 80 + pkg/hub/server.go | 75 +- pkg/hub/server_test.go | 53 +- pkg/hub/stalled_detection_test.go | 14 +- pkg/hub/template_bootstrap_test.go | 12 +- pkg/hub/template_file_handlers_test.go | 8 +- pkg/hub/teststore_test.go | 71 + pkg/hub/user_activity_test.go | 12 +- pkg/hub/useraccesstoken_test.go | 50 +- pkg/hub/wake_test.go | 6 +- pkg/hub/web.go | 6 +- pkg/hub/web_test.go | 8 +- pkg/hub/workspace_handlers_test.go | 168 +- pkg/observability/dbmetrics/dbmetrics.go | 272 + pkg/observability/dbmetrics/dbmetrics_test.go | 128 + pkg/observability/dbmetrics/pool_sampler.go | 79 + .../dbmetrics/pool_sampler_test.go | 123 + pkg/secret/gcpbackend_test.go | 7 +- pkg/secret/localbackend_test.go | 59 +- pkg/secret/teststore_test.go | 64 + pkg/store/concurrency.go | 97 + pkg/store/entadapter/agent_store.go | 745 + pkg/store/entadapter/agent_store_test.go | 378 + pkg/store/entadapter/allowlist_store.go | 588 + pkg/store/entadapter/brokersecret_store.go | 266 + .../entadapter/brokersecret_store_test.go | 223 + pkg/store/entadapter/composite.go | 391 +- pkg/store/entadapter/composite_test.go | 139 +- pkg/store/entadapter/dialect.go | 80 + pkg/store/entadapter/external_store.go | 548 + pkg/store/entadapter/external_store_test.go | 225 + pkg/store/entadapter/group_store.go | 26 +- pkg/store/entadapter/group_store_test.go | 20 +- pkg/store/entadapter/locking.go | 254 + pkg/store/entadapter/locking_test.go | 158 + pkg/store/entadapter/main_test.go | 34 + pkg/store/entadapter/maintenance_store.go | 348 + .../entadapter/maintenance_store_test.go | 183 + pkg/store/entadapter/message_store.go | 251 + pkg/store/entadapter/message_store_test.go | 202 + pkg/store/entadapter/notification_store.go | 671 + .../entadapter/notification_store_test.go | 304 + pkg/store/entadapter/policy_store.go | 2 +- pkg/store/entadapter/policy_store_test.go | 9 +- pkg/store/entadapter/project_store.go | 1060 + pkg/store/entadapter/project_store_test.go | 622 + pkg/store/entadapter/schedule_store.go | 691 + pkg/store/entadapter/schedule_store_test.go | 259 + .../entadapter/scheduled_event_store_test.go | 171 + pkg/store/entadapter/secret_store.go | 528 + pkg/store/entadapter/secret_store_test.go | 443 + pkg/store/entadapter/template_store.go | 585 + pkg/store/entadapter/template_store_test.go | 368 + .../user_allowlist_behavior_test.go | 218 + .../entadapter/user_allowlist_oracle_test.go | 144 + pkg/store/entadapter/user_store.go | 326 + pkg/store/enttest/enttest.go | 73 + pkg/store/enttest/enttest_postgres.go | 316 + pkg/store/enttest/enttest_sqlite.go | 45 + pkg/store/integrationtest/README.md | 70 + pkg/store/integrationtest/contention_test.go | 301 + pkg/store/integrationtest/doc.go | 34 + pkg/store/integrationtest/harness_test.go | 163 + pkg/store/integrationtest/isolation_test.go | 171 + pkg/store/integrationtest/main_test.go | 35 + pkg/store/integrationtest/migration_test.go | 104 + .../integrationtest/multiprocess_test.go | 222 + pkg/store/integrationtest/notify_test.go | 219 + pkg/store/integrationtest/pool_test.go | 212 + pkg/store/integrationtest/schema_test.go | 176 + pkg/store/sqlite/allow_list_invite_test.go | 236 - pkg/store/sqlite/brokersecret.go | 290 - pkg/store/sqlite/brokersecret_test.go | 334 - pkg/store/sqlite/gcp_service_account.go | 189 - pkg/store/sqlite/gcp_service_account_test.go | 230 - pkg/store/sqlite/github_installation.go | 180 - pkg/store/sqlite/github_installation_test.go | 184 - pkg/store/sqlite/maintenance.go | 269 - pkg/store/sqlite/maintenance_test.go | 209 - pkg/store/sqlite/messages.go | 226 - pkg/store/sqlite/messages_test.go | 251 - pkg/store/sqlite/notification.go | 553 - pkg/store/sqlite/notification_test.go | 878 - pkg/store/sqlite/project_sync_state.go | 142 - pkg/store/sqlite/project_sync_state_test.go | 138 - pkg/store/sqlite/schedule.go | 365 - pkg/store/sqlite/schedule_test.go | 290 - pkg/store/sqlite/scheduled_event.go | 316 - pkg/store/sqlite/scheduled_event_test.go | 372 - pkg/store/sqlite/secret_type_test.go | 219 - pkg/store/sqlite/sqlite.go | 6021 --- pkg/store/sqlite/sqlite_test.go | 2927 -- pkg/store/storetest/domains.go | 593 + pkg/store/storetest/domains_project_broker.go | 258 + .../storetest/domains_secret_template.go | 317 + pkg/store/storetest/domains_user.go | 201 + pkg/store/storetest/main_test.go | 34 + pkg/store/storetest/storetest.go | 296 + pkg/store/storetest/storetest_test.go | 48 + scratch/apitest/main.go | 237 + scratch/dbdiag/main.go | 42 + scratch/dbdiag2/main.go | 17 + scratch/minttoken/main.go | 61 + scratch/nm2-test-pod-a.yaml | 76 + scratch/nm2-test-pod-b1.yaml | 71 + scratch/nm2-test-pod-b2.yaml | 71 + scratch/nm2-test-pod-e.yaml | 97 + scratch/scion-nfs-pv.yaml | 31 + shared-dirs/scratchpad/coordinator.md | 75 + .../scratchpad/instance-interaction.md | 88 + 425 files changed, 154830 insertions(+), 21490 deletions(-) create mode 100644 .claude/scheduled_tasks.lock create mode 100644 .design/broker-dispatch.md create mode 100644 .design/nfs-workspace.md create mode 100644 .design/project-log/2026-06-04-pr304-review-fixes.md create mode 100644 .scion/project-id create mode 100644 cmd/server_migrate.go create mode 100644 cmd/server_migrate_test.go create mode 100644 cmd/tid_test.go create mode 100644 downloads/tg_1780415922_photo_AQADQgxrG_Tb-UR-.jpg create mode 100644 internal/fixturegen/fixturegen_test.go create mode 100644 internal/fixturegen/generate.go create mode 100644 internal/fixturegen/main.go create mode 100644 internal/fixturegen/spec.go create mode 100644 pkg/ent/allowlistentry.go create mode 100644 pkg/ent/allowlistentry/allowlistentry.go create mode 100644 pkg/ent/allowlistentry/where.go create mode 100644 pkg/ent/allowlistentry_create.go create mode 100644 pkg/ent/allowlistentry_delete.go create mode 100644 pkg/ent/allowlistentry_query.go create mode 100644 pkg/ent/allowlistentry_update.go create mode 100644 pkg/ent/apikey.go create mode 100644 pkg/ent/apikey/apikey.go create mode 100644 pkg/ent/apikey/where.go create mode 100644 pkg/ent/apikey_create.go create mode 100644 pkg/ent/apikey_delete.go create mode 100644 pkg/ent/apikey_query.go create mode 100644 pkg/ent/apikey_update.go create mode 100644 pkg/ent/brokerjointoken.go create mode 100644 pkg/ent/brokerjointoken/brokerjointoken.go create mode 100644 pkg/ent/brokerjointoken/where.go create mode 100644 pkg/ent/brokerjointoken_create.go create mode 100644 pkg/ent/brokerjointoken_delete.go create mode 100644 pkg/ent/brokerjointoken_query.go create mode 100644 pkg/ent/brokerjointoken_update.go create mode 100644 pkg/ent/brokersecret.go create mode 100644 pkg/ent/brokersecret/brokersecret.go create mode 100644 pkg/ent/brokersecret/where.go create mode 100644 pkg/ent/brokersecret_create.go create mode 100644 pkg/ent/brokersecret_delete.go create mode 100644 pkg/ent/brokersecret_query.go create mode 100644 pkg/ent/brokersecret_update.go create mode 100644 pkg/ent/client_driver.go create mode 100644 pkg/ent/entc/migrate_alpha.go create mode 100644 pkg/ent/entc/migrate_alpha_nosqlite.go create mode 100644 pkg/ent/entc/migrate_alpha_test.go create mode 100644 pkg/ent/entc/migrate_beta.go create mode 100644 pkg/ent/entc/migrate_beta_integration_test.go create mode 100644 pkg/ent/envvar.go create mode 100644 pkg/ent/envvar/envvar.go create mode 100644 pkg/ent/envvar/where.go create mode 100644 pkg/ent/envvar_create.go create mode 100644 pkg/ent/envvar_delete.go create mode 100644 pkg/ent/envvar_query.go create mode 100644 pkg/ent/envvar_update.go create mode 100644 pkg/ent/gcpserviceaccount.go create mode 100644 pkg/ent/gcpserviceaccount/gcpserviceaccount.go create mode 100644 pkg/ent/gcpserviceaccount/where.go create mode 100644 pkg/ent/gcpserviceaccount_create.go create mode 100644 pkg/ent/gcpserviceaccount_delete.go create mode 100644 pkg/ent/gcpserviceaccount_query.go create mode 100644 pkg/ent/gcpserviceaccount_update.go create mode 100644 pkg/ent/githubinstallation.go create mode 100644 pkg/ent/githubinstallation/githubinstallation.go create mode 100644 pkg/ent/githubinstallation/where.go create mode 100644 pkg/ent/githubinstallation_create.go create mode 100644 pkg/ent/githubinstallation_delete.go create mode 100644 pkg/ent/githubinstallation_query.go create mode 100644 pkg/ent/githubinstallation_update.go create mode 100644 pkg/ent/harnessconfig.go create mode 100644 pkg/ent/harnessconfig/harnessconfig.go create mode 100644 pkg/ent/harnessconfig/where.go create mode 100644 pkg/ent/harnessconfig_create.go create mode 100644 pkg/ent/harnessconfig_delete.go create mode 100644 pkg/ent/harnessconfig_query.go create mode 100644 pkg/ent/harnessconfig_update.go create mode 100644 pkg/ent/invitecode.go create mode 100644 pkg/ent/invitecode/invitecode.go create mode 100644 pkg/ent/invitecode/where.go create mode 100644 pkg/ent/invitecode_create.go create mode 100644 pkg/ent/invitecode_delete.go create mode 100644 pkg/ent/invitecode_query.go create mode 100644 pkg/ent/invitecode_update.go create mode 100644 pkg/ent/maintenanceoperation.go create mode 100644 pkg/ent/maintenanceoperation/maintenanceoperation.go create mode 100644 pkg/ent/maintenanceoperation/where.go create mode 100644 pkg/ent/maintenanceoperation_create.go create mode 100644 pkg/ent/maintenanceoperation_delete.go create mode 100644 pkg/ent/maintenanceoperation_query.go create mode 100644 pkg/ent/maintenanceoperation_update.go create mode 100644 pkg/ent/maintenanceoperationrun.go create mode 100644 pkg/ent/maintenanceoperationrun/maintenanceoperationrun.go create mode 100644 pkg/ent/maintenanceoperationrun/where.go create mode 100644 pkg/ent/maintenanceoperationrun_create.go create mode 100644 pkg/ent/maintenanceoperationrun_delete.go create mode 100644 pkg/ent/maintenanceoperationrun_query.go create mode 100644 pkg/ent/maintenanceoperationrun_update.go create mode 100644 pkg/ent/message.go create mode 100644 pkg/ent/message/message.go create mode 100644 pkg/ent/message/where.go create mode 100644 pkg/ent/message_create.go create mode 100644 pkg/ent/message_delete.go create mode 100644 pkg/ent/message_query.go create mode 100644 pkg/ent/message_update.go create mode 100644 pkg/ent/notification.go create mode 100644 pkg/ent/notification/notification.go create mode 100644 pkg/ent/notification/where.go create mode 100644 pkg/ent/notification_create.go create mode 100644 pkg/ent/notification_delete.go create mode 100644 pkg/ent/notification_query.go create mode 100644 pkg/ent/notification_update.go create mode 100644 pkg/ent/notificationsubscription.go create mode 100644 pkg/ent/notificationsubscription/notificationsubscription.go create mode 100644 pkg/ent/notificationsubscription/where.go create mode 100644 pkg/ent/notificationsubscription_create.go create mode 100644 pkg/ent/notificationsubscription_delete.go create mode 100644 pkg/ent/notificationsubscription_query.go create mode 100644 pkg/ent/notificationsubscription_update.go create mode 100644 pkg/ent/projectcontributor.go create mode 100644 pkg/ent/projectcontributor/projectcontributor.go create mode 100644 pkg/ent/projectcontributor/where.go create mode 100644 pkg/ent/projectcontributor_create.go create mode 100644 pkg/ent/projectcontributor_delete.go create mode 100644 pkg/ent/projectcontributor_query.go create mode 100644 pkg/ent/projectcontributor_update.go create mode 100644 pkg/ent/projectsyncstate.go create mode 100644 pkg/ent/projectsyncstate/projectsyncstate.go create mode 100644 pkg/ent/projectsyncstate/where.go create mode 100644 pkg/ent/projectsyncstate_create.go create mode 100644 pkg/ent/projectsyncstate_delete.go create mode 100644 pkg/ent/projectsyncstate_query.go create mode 100644 pkg/ent/projectsyncstate_update.go create mode 100644 pkg/ent/runtimebroker.go create mode 100644 pkg/ent/runtimebroker/runtimebroker.go create mode 100644 pkg/ent/runtimebroker/where.go create mode 100644 pkg/ent/runtimebroker_create.go create mode 100644 pkg/ent/runtimebroker_delete.go create mode 100644 pkg/ent/runtimebroker_query.go create mode 100644 pkg/ent/runtimebroker_update.go create mode 100644 pkg/ent/schedule.go create mode 100644 pkg/ent/schedule/schedule.go create mode 100644 pkg/ent/schedule/where.go create mode 100644 pkg/ent/schedule_create.go create mode 100644 pkg/ent/schedule_delete.go create mode 100644 pkg/ent/schedule_query.go create mode 100644 pkg/ent/schedule_update.go create mode 100644 pkg/ent/scheduledevent.go create mode 100644 pkg/ent/scheduledevent/scheduledevent.go create mode 100644 pkg/ent/scheduledevent/where.go create mode 100644 pkg/ent/scheduledevent_create.go create mode 100644 pkg/ent/scheduledevent_delete.go create mode 100644 pkg/ent/scheduledevent_query.go create mode 100644 pkg/ent/scheduledevent_update.go create mode 100644 pkg/ent/schema/allowlist.go create mode 100644 pkg/ent/schema/apikey.go create mode 100644 pkg/ent/schema/brokerjointoken.go create mode 100644 pkg/ent/schema/brokersecret.go create mode 100644 pkg/ent/schema/envvar.go create mode 100644 pkg/ent/schema/gcpserviceaccount.go create mode 100644 pkg/ent/schema/githubinstallation.go create mode 100644 pkg/ent/schema/harnessconfig.go create mode 100644 pkg/ent/schema/invitecode.go create mode 100644 pkg/ent/schema/maintenanceoperation.go create mode 100644 pkg/ent/schema/maintenanceoperationrun.go create mode 100644 pkg/ent/schema/message.go create mode 100644 pkg/ent/schema/notification.go create mode 100644 pkg/ent/schema/notificationsubscription.go create mode 100644 pkg/ent/schema/projectcontributor.go create mode 100644 pkg/ent/schema/projectsyncstate.go create mode 100644 pkg/ent/schema/runtimebroker.go create mode 100644 pkg/ent/schema/schedule.go create mode 100644 pkg/ent/schema/scheduledevent.go create mode 100644 pkg/ent/schema/secret.go create mode 100644 pkg/ent/schema/subscriptiontemplate.go create mode 100644 pkg/ent/schema/template.go create mode 100644 pkg/ent/schema/useraccesstoken.go create mode 100644 pkg/ent/secret.go create mode 100644 pkg/ent/secret/secret.go create mode 100644 pkg/ent/secret/where.go create mode 100644 pkg/ent/secret_create.go create mode 100644 pkg/ent/secret_delete.go create mode 100644 pkg/ent/secret_query.go create mode 100644 pkg/ent/secret_update.go create mode 100644 pkg/ent/subscriptiontemplate.go create mode 100644 pkg/ent/subscriptiontemplate/subscriptiontemplate.go create mode 100644 pkg/ent/subscriptiontemplate/where.go create mode 100644 pkg/ent/subscriptiontemplate_create.go create mode 100644 pkg/ent/subscriptiontemplate_delete.go create mode 100644 pkg/ent/subscriptiontemplate_query.go create mode 100644 pkg/ent/subscriptiontemplate_update.go create mode 100644 pkg/ent/template.go create mode 100644 pkg/ent/template/template.go create mode 100644 pkg/ent/template/where.go create mode 100644 pkg/ent/template_create.go create mode 100644 pkg/ent/template_delete.go create mode 100644 pkg/ent/template_query.go create mode 100644 pkg/ent/template_update.go create mode 100644 pkg/ent/useraccesstoken.go create mode 100644 pkg/ent/useraccesstoken/useraccesstoken.go create mode 100644 pkg/ent/useraccesstoken/where.go create mode 100644 pkg/ent/useraccesstoken_create.go create mode 100644 pkg/ent/useraccesstoken_delete.go create mode 100644 pkg/ent/useraccesstoken_query.go create mode 100644 pkg/ent/useraccesstoken_update.go create mode 100644 pkg/hub/events_postgres.go create mode 100644 pkg/hub/events_postgres_test.go create mode 100644 pkg/hub/teststore_test.go create mode 100644 pkg/observability/dbmetrics/dbmetrics.go create mode 100644 pkg/observability/dbmetrics/dbmetrics_test.go create mode 100644 pkg/observability/dbmetrics/pool_sampler.go create mode 100644 pkg/observability/dbmetrics/pool_sampler_test.go create mode 100644 pkg/secret/teststore_test.go create mode 100644 pkg/store/concurrency.go create mode 100644 pkg/store/entadapter/agent_store.go create mode 100644 pkg/store/entadapter/agent_store_test.go create mode 100644 pkg/store/entadapter/allowlist_store.go create mode 100644 pkg/store/entadapter/brokersecret_store.go create mode 100644 pkg/store/entadapter/brokersecret_store_test.go create mode 100644 pkg/store/entadapter/dialect.go create mode 100644 pkg/store/entadapter/external_store.go create mode 100644 pkg/store/entadapter/external_store_test.go create mode 100644 pkg/store/entadapter/locking.go create mode 100644 pkg/store/entadapter/locking_test.go create mode 100644 pkg/store/entadapter/main_test.go create mode 100644 pkg/store/entadapter/maintenance_store.go create mode 100644 pkg/store/entadapter/maintenance_store_test.go create mode 100644 pkg/store/entadapter/message_store.go create mode 100644 pkg/store/entadapter/message_store_test.go create mode 100644 pkg/store/entadapter/notification_store.go create mode 100644 pkg/store/entadapter/notification_store_test.go create mode 100644 pkg/store/entadapter/project_store.go create mode 100644 pkg/store/entadapter/project_store_test.go create mode 100644 pkg/store/entadapter/schedule_store.go create mode 100644 pkg/store/entadapter/schedule_store_test.go create mode 100644 pkg/store/entadapter/scheduled_event_store_test.go create mode 100644 pkg/store/entadapter/secret_store.go create mode 100644 pkg/store/entadapter/secret_store_test.go create mode 100644 pkg/store/entadapter/template_store.go create mode 100644 pkg/store/entadapter/template_store_test.go create mode 100644 pkg/store/entadapter/user_allowlist_behavior_test.go create mode 100644 pkg/store/entadapter/user_allowlist_oracle_test.go create mode 100644 pkg/store/entadapter/user_store.go create mode 100644 pkg/store/enttest/enttest.go create mode 100644 pkg/store/enttest/enttest_postgres.go create mode 100644 pkg/store/enttest/enttest_sqlite.go create mode 100644 pkg/store/integrationtest/README.md create mode 100644 pkg/store/integrationtest/contention_test.go create mode 100644 pkg/store/integrationtest/doc.go create mode 100644 pkg/store/integrationtest/harness_test.go create mode 100644 pkg/store/integrationtest/isolation_test.go create mode 100644 pkg/store/integrationtest/main_test.go create mode 100644 pkg/store/integrationtest/migration_test.go create mode 100644 pkg/store/integrationtest/multiprocess_test.go create mode 100644 pkg/store/integrationtest/notify_test.go create mode 100644 pkg/store/integrationtest/pool_test.go create mode 100644 pkg/store/integrationtest/schema_test.go delete mode 100644 pkg/store/sqlite/allow_list_invite_test.go delete mode 100644 pkg/store/sqlite/brokersecret.go delete mode 100644 pkg/store/sqlite/brokersecret_test.go delete mode 100644 pkg/store/sqlite/gcp_service_account.go delete mode 100644 pkg/store/sqlite/gcp_service_account_test.go delete mode 100644 pkg/store/sqlite/github_installation.go delete mode 100644 pkg/store/sqlite/github_installation_test.go delete mode 100644 pkg/store/sqlite/maintenance.go delete mode 100644 pkg/store/sqlite/maintenance_test.go delete mode 100644 pkg/store/sqlite/messages.go delete mode 100644 pkg/store/sqlite/messages_test.go delete mode 100644 pkg/store/sqlite/notification.go delete mode 100644 pkg/store/sqlite/notification_test.go delete mode 100644 pkg/store/sqlite/project_sync_state.go delete mode 100644 pkg/store/sqlite/project_sync_state_test.go delete mode 100644 pkg/store/sqlite/schedule.go delete mode 100644 pkg/store/sqlite/schedule_test.go delete mode 100644 pkg/store/sqlite/scheduled_event.go delete mode 100644 pkg/store/sqlite/scheduled_event_test.go delete mode 100644 pkg/store/sqlite/secret_type_test.go delete mode 100644 pkg/store/sqlite/sqlite.go delete mode 100644 pkg/store/sqlite/sqlite_test.go create mode 100644 pkg/store/storetest/domains.go create mode 100644 pkg/store/storetest/domains_project_broker.go create mode 100644 pkg/store/storetest/domains_secret_template.go create mode 100644 pkg/store/storetest/domains_user.go create mode 100644 pkg/store/storetest/main_test.go create mode 100644 pkg/store/storetest/storetest.go create mode 100644 pkg/store/storetest/storetest_test.go create mode 100644 scratch/apitest/main.go create mode 100644 scratch/dbdiag/main.go create mode 100644 scratch/dbdiag2/main.go create mode 100644 scratch/minttoken/main.go create mode 100644 scratch/nm2-test-pod-a.yaml create mode 100644 scratch/nm2-test-pod-b1.yaml create mode 100644 scratch/nm2-test-pod-b2.yaml create mode 100644 scratch/nm2-test-pod-e.yaml create mode 100644 scratch/scion-nfs-pv.yaml create mode 100644 shared-dirs/scratchpad/coordinator.md create mode 100644 shared-dirs/scratchpad/instance-interaction.md diff --git a/.claude/scheduled_tasks.lock b/.claude/scheduled_tasks.lock new file mode 100644 index 000000000..297107d87 --- /dev/null +++ b/.claude/scheduled_tasks.lock @@ -0,0 +1 @@ +{"sessionId":"f5564605-9019-4019-968f-3ae798b30ae8","pid":44,"procStart":"238465155","acquiredAt":1780324261213} \ No newline at end of file diff --git a/.design/broker-dispatch.md b/.design/broker-dispatch.md new file mode 100644 index 000000000..fdb008d66 --- /dev/null +++ b/.design/broker-dispatch.md @@ -0,0 +1,772 @@ +# Design: Multi-Node Broker Dispatch over LISTEN/NOTIFY + +**Branch:** `postgres/wave-b-integration` +**Date:** 2026-06-02 +**Author:** broker-architect agent +**Status:** Approach approved by @ptone (2026-06-02). Scope: **message + agent +lifecycle dispatch only**; model is **"DB as state machine, NOTIFY as the +communications channel."** PTY, logs, and exec are out of scope (§10). +**Reviewers:** @ptone +**Implements:** the agreed "DB-state-machine + NOTIFY-signaled dispatch" approach. + +Inputs: `RESEARCH-MESSAGE-DISPATCH.md`, `RESEARCH-BROKER-ROUTING.md`, +`pkg/hub/controlchannel.go`, `pkg/hub/controlchannel_client.go`, +`pkg/hub/events_postgres.go`, `pkg/hub/server.go`, `.design/postgres-strategy.md`. + +--- + +## 1. Problem statement + +A runtime broker opens **one** outbound WebSocket "control channel" to **one** hub +replica. That replica holds the live socket in an in-memory map +(`ControlChannelManager.connections`). Dispatch (`start`/`stop`/`message`/`exec`/…) +decides reachability purely from that local map +(`HybridBrokerClient.useControlChannel` → `manager.IsConnected`). + +Behind a load balancer with N replicas, an API call lands on an arbitrary replica. +If the broker's socket is on Hub A but the call lands on Hub B: + +- `IsConnected(brokerID)` is **false** on Hub B → falls back to HTTP at + `broker.Endpoint`. +- For NAT'd / control-channel-only brokers (`Endpoint == ""` — the entire reason the + control channel exists) the HTTP fallback **fails**, and worse, for messages the + store row + SSE event were already written, so the UI shows "sent" while the agent + never receives it (silent split-brain). Probability of failure ≈ (N−1)/N. + +Two further defects compound this: + +- **No broker→hub affinity** exists in the DB. A replica cannot even discover which + peer owns a socket. (`runtime_brokers` has `status`/`connection_state`/ + `last_heartbeat` but no owning-replica column.) +- **`onDisconnect` status race** (`server.go:691`): the callback unconditionally + stamps the broker `offline`. When a broker flaps A→B, Hub A's delayed disconnect + can clobber Hub B's freshly-written `online` (last-writer-wins on + `runtime_brokers.status`). + +## 2. Design goals & non-goals + +### 2.0 Hard constraint (maintainer-confirmed, 2026-06-02) + +> **There is no hub-to-hub HTTP addressability.** A node generally cannot reach +> another node directly. A broker's reverse tunnel lands on an **arbitrary** node and +> stays sticky there. Therefore **Postgres LISTEN/NOTIFY is the only inter-node +> transport**, and dispatch must reach the socket-holding node *without any node +> addressing another*. + +### 2.0.1 Model: DB as state machine, NOTIFY as the signal (maintainer-directed) + +> **The DB holds the durable state/intent; NOTIFY is only the wakeup signal.** A +> dispatch is *not* "send a command over NOTIFY and hope a node is listening." It is +> "write the intent to the DB (durable), then NOTIFY so the socket-holding node wakes +> and **reconciles** DB intent → broker." If the NOTIFY is missed, or the owning node +> is down, the intent persists and is reconciled when a node next owns the socket +> (on (re)connect). This gives durability and at-least-once delivery **for free**, and +> makes the NOTIFY payload a tiny signal rather than the source of truth. + +This reframes the response pattern too: the originator observes **DB state changes** (the +agent's 3-layer `phase`/`activity`/`detail`, or the message's `dispatch_state`) via the +events that already publish those transitions cross-node — not a bespoke RPC reply. A +**rolling timeout resets on each such change** (§6.4), so liveness, not a fixed clock, +bounds the wait. + +Consequences baked into this design: +- Intent is **persisted to the DB** first; a NOTIFY on the global channel signals + "reconcile broker X". The node holding the socket **self-selects** and reconciles. + No node ever addresses a peer. +- Responses are **DB-state transitions observed via existing events** (`agent..status` + phase changes; `agent.deleted`; message `dispatch_state`). No hub-to-hub reply path. +- The `connected_hub_id` affinity column is **not load-bearing for routing** — + ownership is decided by who physically holds the socket. Affinity exists only to + (a) fast-fail when *no* node owns the broker and (b) fix the `onDisconnect` race. +- **PTY, logs, and exec are out of scope.** PTY/log streams cannot ride NOTIFY and + cannot be hub-to-hub reverse-proxied (no addressability); exec is an interactive + request/response that does not fit the state-machine model. The only path for these + is LB sticky-routing the client to the owning node — a separate problem (§10). + +**Goals** +- A dispatch arriving at *any* node reaches the broker's socket, wherever it lives — + with no node addressing another. +- Reuse the existing `PostgresEventPublisher` (LISTEN/NOTIFY, payload-offload, + reconnect) — no new transport. +- **Durable + at-least-once** for in-scope dispatch: intent persists in the DB and is + reconciled on (re)connect, so a missed NOTIFY or a down owner does not lose the + command (§2.0.1). +- Fix the `onDisconnect` clobber race as a side effect of affinity tracking. +- Preserve today's fast path (local socket → tunnel) unchanged and at zero added + latency. +- Preserve today's API semantics (start/stop "done" == broker accepted the command; + see §6). +- **Support long, multi-step provisioning** (GKE pod cold-start, future runtime + providers): reuse the existing 3-layer agent state (phase/activity/detail) for interim + feedback and a **rolling timeout** that resets on each update, so duration is bounded by + broker liveness, not a fixed clock (§6.4). + +**In scope (commands):** `message` (incl. broadcast / `set[]`), and **agent +lifecycle**: `start`, `stop`, `restart`, `delete`, and create-time ops +(`create-with-gather`, `finalize-env`, `check-prompt`). + +**Non-goals (this design)** +- **PTY / interactive streams** (`OpenStream`/`SendStreamData`), **logs** + (`GetAgentLogs`), and **exec** (`ExecAgent`) — explicitly out of scope per maintainer + (§10). They do not fit "DB as state machine" and/or cannot ride NOTIFY. +- Hub-to-hub HTTP of any kind (does not exist; §2.0). +- Replacing the HTTP-endpoint fast path for direct-mode brokers (kept as a fallback + tier; rare under NAT'd deployments). + +--- + +## 3. Architecture overview + +``` + shared Postgres (one DB, N hubs) + ┌──────────────────────────────────────────────────────────────────────┐ + │ runtime_brokers (+ connected_hub_id, connected_session_id, …) │ + │ scion_event_payloads (existing oversized-payload offload) │ + │ LISTEN/NOTIFY channels: │ + │ scion_ev_global / scion_ev_g_ (existing events) │ + │ scion_broker_cmd (NEW: dispatch commands) │ + └──────────────────────────────────────────────────────────────────────┘ + ▲ ▲ ▲ │ ▲ │ + │ │ NOTIFY cmd │ │ NOTIFY agent.status │ │ + │ │ LISTEN │ ▼ LISTEN │ ▼ + ┌────┴──┴─────┐ ┌──────┴────────┐ ┌─────┴───────┐ + │ Hub B │ │ Hub A │ │ Hub C │ + │ (API entry) │ │ owns brokerX │ │ │ + │ │ │ socket in-mem │ │ │ + │ instanceID= │ │ instanceID= │ │ instanceID= │ + │ b2f1… │ │ a9c3… │ │ c7e0… │ + └─────────────┘ └──────┬────────┘ └─────────────┘ + ║ WS control channel + ┌────╨─────┐ + │ broker X │ (NAT'd; Endpoint == "") + │ agents │ + └──────────┘ + +Outbound dispatch (API on Hub B, socket on Hub A): + 1. Hub B handler → HybridBrokerClient. + 2. local IsConnected(X)? NO + 3. write DURABLE INTENT (broker_dispatch row / message.dispatch_state) + NOTIFY + scion_broker_cmd{broker_id:X} — in ONE transaction (PublishTx) + 4. Hub A's signal-listener wakes, sees ownsLocally(X)==true, CAS-claims the intent, + runs LOCAL tunnel , marks the intent done + 5. (for start/stop) Hub A sets phase + PublishAgentStatus ── NOTIFY agent.status ──┐ + 6. Hub B, which Subscribed to agent..status before step 3, wakes and returns ◄──┘ + to the API caller. (message = fire-and-forget: Hub B already returned 202 at step 3, + durably. If NO node owns X, the intent persists and reconciles on X's reconnect.) +``` + +Two NOTIFY directions, both on infrastructure that already exists: + +- **Command signal (NEW channel `scion_broker_cmd`)** is a *tiny wakeup* — `{broker_id}`, + no payload. The durable command lives in the DB. Every node receives the signal; only + the socket-holder reconciles (ownership *self-selected*). Affinity (`connected_hub_id`) + is a fast-fail hint, not the correctness gate; the reconnect-drain is the durability + backstop. +- **Response (EXISTING channels `scion_ev_*`)** is the already-published + `AgentStatusEvent` (carries `Phase`) for lifecycle, or a slim `broker.dispatch.` + completion event for data-returning ops. The originating node subscribes and waits; the + authoritative result is always the DB row. + +--- + +## 4. Component 1 — Hub instance identity & broker affinity + +### 4.1 Per-process instance ID (NEW — do **not** reuse `hubID`) + +`hubID` (`config.ResolveHubID`) is **logical**: it is `HubID` from config if set, +else `sha256(hostname)[:12]`. It is used for **secret namespacing** and is explicitly +intended to be *stable* — operators may configure the *same* `HubID` across replicas +so they share a secret scope. Therefore `hubID` is **not safe** as an affinity key: +two replicas can legitimately share it. + +Introduce a distinct **per-process instance ID**: + +```go +// Server field, set once at construction. +instanceID string // e.g. uuid.NewString(); unique per hub process/boot +``` + +- Generated at boot (random UUID). Optionally seed from `POD_NAME`+boot-nonce in k8s + for log readability, but uniqueness must not depend on hostname. +- Lives only in memory + the affinity column; never persisted to config. +- Exposed as `Server.InstanceID()`. + +### 4.2 Schema change — `runtime_brokers` + +Add three nullable columns (Ent schema `pkg/ent/schema/runtimebroker.go` + store model +`pkg/store/models.go` + migration): + +| Column | Type | Meaning | +|---|---|---| +| `connected_hub_id` | `TEXT` null | instance ID of the replica currently holding the socket; `NULL` when no replica owns it | +| `connected_session_id` | `TEXT` null | the `BrokerConnection.sessionID` (uuid) of the owning socket — disambiguates reconnects | +| `connected_at` | `TIMESTAMPTZ` null | when the current owner registered the socket | + +Reuse the existing `lock_version` optimistic-concurrency token (already on the row, +already CAS-looped by `UpdateRuntimeBrokerHeartbeat`). + +> Dialect-neutral per `postgres-strategy.md` §6.4: `TEXT`/`TIMESTAMPTZ` work on both +> SQLite and Postgres. No Postgres-only types. + +### 4.3 Affinity write paths (store methods) + +Two new store methods, both modeled on the `UpdateRuntimeBrokerHeartbeat` CAS loop +(`project_store.go:755`): + +```go +// ClaimRuntimeBrokerConnection sets affinity to this replica unconditionally +// (the newest connection wins — mirrors HandleUpgrade replacing an existing local +// socket). Bumps status->online + heartbeat in the same CAS write. +ClaimRuntimeBrokerConnection(ctx, brokerID, hubInstanceID, sessionID string) error + +// ReleaseRuntimeBrokerConnection clears affinity ONLY IF it still names +// (hubInstanceID, sessionID) — compare-and-clear. Returns (cleared bool). +// If affinity already moved to another replica/session, it is a no-op and the +// caller MUST NOT stamp the broker offline (fixes the §1 race). +ReleaseRuntimeBrokerConnection(ctx, brokerID, hubInstanceID, sessionID string) (bool, error) +``` + +`ClaimRuntimeBrokerConnection` is called from `markBrokerOnline` +(`server.go:2456`) — pass the new `sessionID` out of `HandleUpgrade` (it already +generates one at `controlchannel.go:202`; thread it through the `onConnect` path). + +### 4.4 The `onDisconnect` race fix (Component 5 in the brief) + +Today (`server.go:691`): +```go +srv.controlChannel.SetOnDisconnect(func(brokerID string) { + s.UpdateRuntimeBrokerHeartbeat(ctx, brokerID, store.BrokerStatusOffline) // UNCONDITIONAL + ... +}) +``` + +New: `SetOnDisconnect` must receive the **sessionID** of the connection that dropped +(extend the callback signature to `func(brokerID, sessionID string)` — `removeConnection` +already has the `*BrokerConnection`, so it can pass `hc.sessionID`). Then: + +```go +srv.controlChannel.SetOnDisconnect(func(brokerID, sessionID string) { + cleared, err := s.store.ReleaseRuntimeBrokerConnection(ctx, brokerID, s.instanceID, sessionID) + if err != nil { /* log */ return } + if !cleared { + // Another replica (or a newer session on this replica) already owns the + // socket. Do NOT mark offline — that would clobber the live owner. + slog.Info("broker reconnected elsewhere; skipping offline stamp", + "brokerID", brokerID, "staleSession", sessionID) + return + } + // We were the owner and nobody replaced us: mark offline + publish. + s.store.UpdateRuntimeBrokerHeartbeat(ctx, brokerID, store.BrokerStatusOffline) + ... // provider status updates + PublishBrokerDisconnected (unchanged) +}) +``` + +This is correct under A→B flap because the offline stamp is now gated on +"affinity still names *me* with *this* session". `HandleUpgrade` already closes+replaces +an existing **local** connection (`controlchannel.go:218`); the sessionID guard extends +that safety **across** replicas. + +> Note `Shutdown()` (`controlchannel.go:544`) deliberately nils `onDisconnect` to avoid +> touching the DB during teardown. Keep that — on graceful shutdown we intentionally do +> **not** clear affinity (the broker will reconnect and re-claim; a brief stale-but-dead +> affinity row is handled by the liveness check in §5.3). + +--- + +## 5. Component 2 & 3 — Command dispatch channel & command types + +### 5.1 Channel choice + +Single global channel **`scion_broker_cmd`** (not per-broker). Rationale: + +- Postgres channels have no wildcards; a per-broker channel + (`scion_broker_cmd_`) would require every replica to `LISTEN` on the channel of + every broker it *might* own — but a replica doesn't know which brokers will dial it + next, so it would have to LISTEN on all of them anyway. A single channel is simpler + and matches the `scion_ev_global` precedent. +- Volume is low (dispatch is human-paced lifecycle/message ops, not data-plane traffic). + One global channel is fine. Each node filters the signal by `ownsLocally(brokerID)`. + +A dedicated signal-listener goroutine (mirroring `runListener` in `events_postgres.go`) +LISTENs on `scion_broker_cmd`. On a signal for a broker it owns, it runs the reconcile +drain (§5.3). Implement as a sibling type **`PostgresCommandBus`** reusing the same +connect/reconnect/keepalive helpers (`connectListener`, `applyConnKeepalives`, +`nextBackoff`) — kept separate from `PostgresEventPublisher` so the event-fanout path and +the dispatch path are independently testable and pooled. + +### 5.2 Intent lives in the DB; the NOTIFY is a tiny signal + +Per the state-machine model (§2.0.1), the command **payload is not carried in the +NOTIFY**. The durable intent is written to the DB; the NOTIFY only says "broker X has +pending work, whoever owns it should reconcile." + +**NOTIFY `scion_broker_cmd` payload — a signal, not a command:** +```jsonc +{ "broker_id": "uuid", "kind": "dispatch" } // optional "cmd_id" for log correlation +``` +Tiny, never near the 8000-byte cap, never carries secrets. If the payload is ever lost +(LISTEN reconnect gap), correctness is unaffected — the intent is still in the DB and is +picked up by the next reconcile (NOTIFY-loss is just latency, not loss). + +**Durable intent — two tables:** + +1. **Messages reuse their existing row.** `store.Message` is already persisted *before* + dispatch today. Add a `dispatch_state` (`pending|dispatched|failed`) + + `dispatched_at`. No duplication; the message *is* the durable intent. + +2. **Lifecycle uses a new `broker_dispatch` intent table:** + +```sql +CREATE TABLE broker_dispatch ( + id UUID PRIMARY KEY, + broker_id UUID NOT NULL, + agent_id UUID, -- null for project-scoped ops + agent_slug TEXT, + project_id UUID, + op TEXT NOT NULL, -- start|stop|restart|delete|finalize_env|check_prompt|create + args TEXT, -- JSON; env/secrets/inlineConfig live here (see note) + state TEXT NOT NULL, -- pending|in_progress|done|failed + result TEXT, -- JSON; for ops that return data (check_prompt, env-gather) + claimed_by TEXT, -- hub instanceID that reconciled it + attempts INT NOT NULL DEFAULT 0, + error TEXT, + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), + deadline_at TIMESTAMPTZ +); +CREATE INDEX broker_dispatch_pending_idx ON broker_dispatch (broker_id, state); +``` + +Notes: +- `args` holds the bulky/secret-bearing fields (`resolvedEnv`, `resolvedSecrets`, + `inlineConfig`, structured message bodies). They sit in a DB column, **not** in a + NOTIFY payload — so secrets never appear in PG NOTIFY logs, and there is no 8000-byte + limit to work around (the §6 oversized-offload concern for commands disappears + entirely; offload remains only for the *event* path). On Postgres these can later + become `JSONB` per strategy §6.4; `TEXT` keeps SQLite parity for now. +- `deadline_at` lets a late reconciler drop a command the caller already abandoned. +- Atomic publish: the intent row INSERT and the NOTIFY are issued in **one transaction** + via `PublishTx` (events_postgres.go:236) — the signal is delivered only if the intent + commits. + +### 5.3 Routing decision — `HybridBrokerClient` + +Because there is no hub-to-hub addressing (§2.0), routing is **not** "find the owner and +send to it". It is "run locally if I hold the socket, otherwise **broadcast** and let the +holder self-select". Affinity is consulted only to *fast-fail* — to avoid waiting out a +timeout when we can already tell nobody owns the broker. + +```go +func (c *HybridBrokerClient) route(ctx, brokerID) routeDecision { + if c.controlChannel.manager.IsConnected(brokerID) { + return routeLocal // I hold the socket → tunnel directly (unchanged) + } + // I don't hold it. Some OTHER node might. We cannot address that node, so: + owner, alive := c.affinity.Lookup(ctx, brokerID) // reads runtime_brokers (hint only) + switch { + case owner != "" && alive: + return routeForward // NOTIFY-broadcast; the holder self-selects + case brokerEndpointSet: // direct-mode broker (hub→broker HTTP, not hub→hub) + return routeHTTP // existing fallback; rare under NAT'd deployments + default: + return routeUndeliverable // no owner & no endpoint → typed retryable error + } +} +``` + +Important: `routeForward` writes the **durable intent** (a `broker_dispatch` row, or a +`message.dispatch_state=pending`) and NOTIFYs the global `scion_broker_cmd` channel in +one transaction; *every* node receives the signal but only the socket-holder reconciles. +The affinity lookup is a **hint** that *a* node owns the broker, so we should write intent ++ signal (and wait for the resulting state transition) rather than fast-fail. Even if the +hint is stale, correctness holds: a wrong "alive" costs one timeout (the intent stays +durable and reconciles later); a wrong "dead" is reaped by §7.1. + +**Durability backstop — reconcile-on-connect.** Independent of any NOTIFY, when a broker +(re)connects to a node (`markBrokerOnline` / claim), that node runs a drain: +`SELECT … FROM broker_dispatch WHERE broker_id=$X AND state='pending'` plus pending +messages, and reconciles them. So even if *no* node owned the broker when the intent was +written (broker was down, or every NOTIFY was missed), the work executes the moment a +node next owns the socket. This is what makes the design durable + at-least-once without +a separate work queue (§2.0.1). + +"alive" = the owning node is believed up. Since we can't ping it (no hub-to-hub HTTP), +liveness is inferred from `last_heartbeat` freshness on the broker row (the broker's own +HTTP heartbeat lands on any node and keeps the row fresh while the tunnel is up), backed +by the command timeout. This is OQ3: confirm `last_heartbeat`-freshness + timeout is +sufficient for v1 (recommended), given a dedicated `hub_instances` table buys us nothing +without hub-to-hub addressing. + +### 5.4 Command types (in scope) + +Each command is: **write durable intent → NOTIFY signal → owner reconciles → originator +observes the resulting DB-state transition.** The "observe" column is always an existing +or DB-backed state change, never a bespoke RPC reply. + +| `op` | Durable intent | Owner reconcile (local tunnel) | Originator observes | +|---|---|---|---| +| `message` / broadcast / `set[]` | `message.dispatch_state=pending` (row already exists) | `MessageAgent`; set `dispatched_at` | fire-and-forget: return 202 once intent is durable (§6.1) | +| `start` | `broker_dispatch{op:start, args}` | `StartAgent`; set phase `Starting→Running` + `PublishAgentStatus` (today's behavior) | `agent..status` phase ∈ {running, error} | +| `stop` | `broker_dispatch{op:stop}` | `StopAgent`; set phase `Stopped` + publish | `agent..status` phase ∈ {stopped, error} | +| `restart` | `broker_dispatch{op:restart, args}` | `RestartAgent` | phase ∈ {running, error} | +| `delete` | `broker_dispatch{op:delete, args}` | `DeleteAgent` (idempotent; 404 ok); set state=done | `agent.deleted` event (already published cross-node) or dispatch state=done | +| `finalize_env` | `broker_dispatch{op:finalize_env, args}` | `FinalizeEnv`; write `result`; set phase | dispatch state→done (+ `result`) and/or phase | +| `check_prompt` / `create-with-gather` | `broker_dispatch{op:…, args}` | run tunnel; write `result` (bool / env-requirements) | dispatch state→done; originator reads `result` | + +All in-scope ops map to a **DB-state transition** the originator can observe. The two +data-returning create-time ops (`check_prompt`, `create-with-gather`) write their result +into `broker_dispatch.result` — the result is *state*, consistent with the model, and is +durable/re-readable rather than a fire-once reply. **No separate `cmd-ack` RPC event and +no command-body offload are needed** (both removed from the earlier draft). + +> **exec, logs, PTY are out of scope** (§10) — they are interactive request/response or +> streaming, not state transitions, and per maintainer are deferred. + +--- + +## 6. Component 4 — Response = observing the DB-state transition + +The originator never waits on a point-to-point reply. It **subscribes to the existing +event stream** (which already fans DB-state changes across nodes via +`PostgresEventPublisher`) *before* writing intent, then waits for the transition. + +### 6.1 Fire-and-forget (message, broadcast, set[]) + +The message row is durable *before* dispatch (today, and still). So: +- Originator writes `dispatch_state=pending` + NOTIFY (one tx), returns **202** at once. +- Owner reconciles: tunnels the message, sets `dispatch_state=dispatched, dispatched_at`. +- **Loss visibility** (replaces today's silent split-brain): a sweep flags any row stuck + `pending` beyond T. Because the row is durable, a broker that was offline gets its + messages on reconnect-drain (§5.3) — they are delayed, never dropped. This is strictly + better than today, where an undeliverable message surfaced as a *synchronous* error + *after* the row + SSE were already written. + +### 6.2 Lifecycle via the 3-layer agent state (start, stop, restart) — reuses existing events + +The owner already calls `PublishAgentStatus` after dispatch +(handlers.go:1192/2345/2828), which NOTIFYs `agent..status` carrying the existing +**3-layer state** (events.go:97): `Phase` (top-level lifecycle), `Activity` (what the +agent is doing — "building", "pulling image", "waiting"), and `Detail` (untyped free-text +for broker/runtime-specific interim states). All three already cross nodes via the event +layer. The originating node watches **any change to the agent record**, not just the +terminal phase: + +1. Originator `Subscribe("agent."+agentID+".status")` **before** writing intent. +2. Write `broker_dispatch` intent + NOTIFY (one tx). +3. Loop over `AgentStatusEvent`s: + - **Any** change (phase/activity/detail) → forward progress: surface to the caller and + **reset the rolling timeout** (§6.4). + - **Terminal** phase (start/restart: {running, error}; stop: {stopped, error}) → done. +4. `error` phase → return the agent's `Message`. Rolling-timeout expiry (no update within + the window) → dispatch **failed** (§6.4); the originator marks the outcome and returns. + +> **Semantics preserved (OQ2 confirmed):** "done" == the owner accepted the command and +> published the resulting phase — *not* waiting for the harness to report truly-ready. +> The owner runs the local accept-and-publish sequence; the originator observes it. + +### 6.3 Lifecycle/create ops without a phase (delete, finalize_env, check_prompt, gather) + +Observe the **`broker_dispatch` row reaching a terminal state**. A slim completion event +`broker.dispatch.` (subject → `scion_ev_global`, reuses the existing publisher) is +emitted by the owner when it sets state `done|failed`; the originator subscribed to it +before writing intent and reads `result`/`error` from the row on wake. Because the +**authoritative result is the DB row**, a missed event is recoverable (bounded re-read at +timeout) — no point-to-point reply to lose. `delete` may instead observe the existing +`agent.deleted` event. + +### 6.4 Rolling timeout on the 3-layer state (OQ2/OQ6 — resolved per maintainer + coordinator) + +> Maintainer + coordinator: long providers (GKE pod cold-start; future runtimes) sit in +> schedule→image-pull→init for minutes, so a fixed wall-clock timeout is wrong. Instead: +> **a rolling timeout that resets on each interim state update.** Brokers are expected to +> update the sub-state (`activity`/`detail`) within the window; if a step needs longer the +> broker runs its own timer loop to keep emitting heartbeat-style `detail` updates. If no +> update arrives within the window, the dispatch is considered **failed**. Interim states +> are **untyped** (free-text `detail`) — no canonical sub-state set to define. + +This is the whole model — it replaces the earlier inactivity-bound + absolute-cap + +provider-config machinery: + +``` +window := dispatchRollingTimeout // single tunable; reset on ANY agent-record change +loop: select { + case ev := <-sub: // ANY change to phase/activity/detail + if terminal(ev.Phase) { return ev } // running | stopped | error → done + reset(window) // forward progress (incl. a detail heartbeat) + case <-window: return ErrDispatchFailed // broker went silent → FAILED + case <-ctx.Done(): return ctx.Err() +} +``` + +Properties: +- **Liveness-based, not duration-based.** A 10-minute GKE start succeeds as long as the + broker keeps updating `detail`; a broker that dies mid-step fails fast (within one + window), regardless of how long the step "should" take. +- **The broker owns the heartbeat.** A slow step (e.g. image pull) means the broker's own + timer loop emits periodic `detail` updates ("pulling image… 40%"). This pushes the + liveness contract to where the knowledge is, and needs no provider-specific config in + the hub. +- **One knob.** `dispatchRollingTimeout` (a single default, e.g. 60–90s) rather than + per-provider bounds. Providers express "I need longer" by *keeping the heartbeat going*, + not by configuring a number. +- **Cross-node for free.** The waiting node (Hub B) watches the same `agent..status` + events every node already receives; the owning node (Hub A) just keeps publishing the + 3-layer state as it does today. +- **Failure is authoritative.** On window expiry the originator marks the outcome + (`broker_dispatch.state=failed` / agent `phase=error`) and returns failure. Because a + well-behaved broker heartbeats while working, silence genuinely means stuck/dead. + +> **Long-poll caveat (note, not a blocker):** a multi-minute synchronous request can +> exceed an L7 LB idle timeout *on the dispatch connection itself* (interim updates flow +> on the SSE/event stream, not on the blocked dispatch socket). If that bites, the op can +> return 202 once intent is durable + first update seen, and the client watches +> `agent..status` for the terminal phase — same state, just observed by the client +> instead of the hub. Flagged for implementation; not required by this design. + +| Op | Wait model | +|---|---| +| message / broadcast | none — return 202 on durable intent | +| start / restart | rolling timeout on the agent record; terminal phase → done; window expiry → failed | +| stop / delete | rolling timeout (typically one window) — terminal phase / `agent.deleted` | +| finalize_env / check_prompt / gather | rolling timeout — `broker_dispatch` terminal state + `result` | + +--- + +## 7. Error handling & edge cases + +Because intent is **durable**, *messages* degrade to added latency (never lost), while +*lifecycle* ops follow the rolling-timeout contract (silence ⇒ failed + retryable). + +| Case | Behavior | +|---|---| +| **Rolling-timeout expiry** (no agent-record update within the window — broker stuck/dead mid-step) | The in-flight dispatch is **failed**: originator marks `broker_dispatch.state=failed` / agent `phase=error` and returns 503. This is the §6.4 contract — a well-behaved broker heartbeats `detail` while working, so silence is genuine failure. | +| **No node owns the broker** (broker offline) | Intent is written `pending` and persists. **Message:** return 202 — delivered on reconnect-drain (§5.3), never dropped. **Lifecycle:** originator can see the broker is offline up front (affinity/heartbeat) and return retryable immediately rather than wait a full window; the `pending` intent may be reaped (§7.1) or left for reconnect-drive per op. | +| **Owner believed alive but is actually dead** (crashed without clearing affinity) | No status updates reach the originator → rolling window expires. **Message:** stays `pending`, reconciled when a node next owns the socket. **Lifecycle:** failed + retryable (above). Stale affinity reaped by §7.1. | +| **Owner alive but socket just dropped there** | Reconciler sees `ownsLocally==false` → ignores. Intent stays `pending`; broker re-dials and the new owner drains it (message) or the user retries (lifecycle). | +| **Two nodes both think they own it** (flap mid-signal) | The `broker_dispatch` claim is a CAS (`state pending→in_progress WHERE state='pending'`), so exactly one node executes a given intent. Messages: dedupe on `dispatch_state` CAS likewise. No double-execution. | +| **NOTIFY/intent write fails** (pool saturated) | It's one transaction (`PublishTx`): either both the intent row and the signal commit, or neither. On failure the handler returns 503 retryable with **no partial state**. Bounded by `publishTimeout` (5s). | +| **Large args** (env/secrets/inlineConfig) | Live in the `broker_dispatch.args` DB column — no NOTIFY size limit, no payload-offload table, no secrets in PG logs. | +| **Completion/phase event missed** (subscriber buffer overflow or originator crash) | The authoritative result is the **DB row** (phase / `dispatch_state` / `broker_dispatch.state`+`result`). On timeout the originator may re-read it; the command itself already ran. At-least-once; all in-scope ops are idempotent (start/stop/restart/delete are broker-idempotent — `DeleteAgent` allows 404; message dedupes on `dispatch_state`). | +| **Reconcile runs after the caller gave up** (`deadline_at` passed) | Reconciler may skip (lifecycle) or still deliver (message — better late than never). Correctness relies on the originator's own timeout, not the reconciler's clock. | +| **Completion event lands on a non-originating node** | Harmless — the event is broadcast to all nodes via `scion_ev_global`; only the node with a live `Subscribe` for that agent/dispatch matches; others ignore it. | + +### 7.1 Stale-affinity reaping + +A recurring **singleton** job (reuse `RegisterRecurringSingleton` / +`pg_try_advisory_lock`, precedent `server.go:1858`) clears `connected_hub_id` for +brokers whose `last_heartbeat` is older than `2 × heartbeatInterval` AND whose +`connected_hub_id` is non-NULL. This bounds how long a crashed owner's affinity misleads +`route` into `routeForward` (after which `route` falls to `routeUndeliverable`, i.e. a +durable `pending` intent + retryable status). The same job can mark `broker_dispatch` +rows stuck `in_progress` past `deadline_at` back to `pending` (re-drive) or `failed`. + +### 7.2 Routing order (summary) + +``` +local socket (tunnel) ── fastest, unchanged + └─ else a node owns it → write durable intent + NOTIFY signal → owner reconciles + └─ else broker.Endpoint set → HTTP (direct-mode brokers; existing; rare under NAT) + └─ else → write durable intent (pending) + retryable status + → reconciled on broker reconnect-drain (never silent) +``` + +The HTTP tier is retained for direct-mode brokers (`Endpoint` set, reachable hub→broker — +distinct from the nonexistent hub→hub path). Whether any production broker uses it is OQ1; +under pure NAT it is never taken. + +--- + +## 8. Data model & migration summary + +```sql +-- 1. Broker affinity (fixes the disconnect race; hint for routing). +ALTER TABLE runtime_brokers + ADD COLUMN connected_hub_id TEXT, + ADD COLUMN connected_session_id TEXT, + ADD COLUMN connected_at TIMESTAMPTZ; +-- lock_version already present and CAS-looped. + +-- 2. Durable lifecycle/create intent (the state machine). +CREATE TABLE broker_dispatch ( … ); -- see §5.2 + +-- 3. Message delivery state (messages are already durable rows). +ALTER TABLE messages + ADD COLUMN dispatch_state TEXT NOT NULL DEFAULT 'pending', -- pending|dispatched|failed + ADD COLUMN dispatched_at TIMESTAMPTZ; +``` + +- Ent: add the affinity fields to `pkg/ent/schema/runtimebroker.go`, a new + `BrokerDispatch` schema, and the message fields — all dialect-neutral (`TEXT`/ + `TIMESTAMPTZ`, no Postgres-only annotations) per strategy §6.4. +- Store model: add fields to `store.RuntimeBroker` (`models.go:281`) and `store.Message`; + add `BrokerDispatch` model + store methods (insert/claim-CAS/complete/drain). +- New NOTIFY channel `scion_broker_cmd` (no DDL; channels are ephemeral). The existing + `scion_event_payloads` table is **not** needed by dispatch (args live in + `broker_dispatch.args`); it stays in use only by the event path. +- New in-memory `Server.instanceID`. + +No SQLite-path behavior changes: single-process SQLite always takes the local fast path +(`IsConnected==true`), so the intent tables are written-through but routing never forwards +and the reconcile-drain simply runs locally. The affinity columns still fix the +disconnect race harmlessly. + +--- + +## 9. Sequence diagrams + +### 9.1 `message` (durable, fire-and-forget) — socket on Hub A, API on Hub B + +``` +User→LB→Hub B: POST /agents/{id}/message +Hub B: BEGIN tx: persist Message (dispatch_state=pending) + + PublishUserMessage (SSE) [unchanged, cross-node] + + NOTIFY scion_broker_cmd{broker_id:X} [signal only] COMMIT +Hub B: 202 Accepted ───────────────────────────────────────► User (immediate, durable) +Hub A: signal-listener wakes; ownsLocally(X)=yes +Hub A: drain: SELECT messages WHERE broker_id=X AND dispatch_state='pending' +Hub A: CAS dispatch_state pending→dispatched; MessageAgent (local tunnel) → broker → agent + (broker offline at notify time? → no owner acts; row stays pending; + delivered when X reconnects and its new owner runs the same drain — never lost) +``` + +### 9.2 `start` — observe phase, with intermediate sub-states (long provider, e.g. GKE) + +``` +User→LB→Hub B: POST /agents/{id}/start +Hub B: Subscribe("agent.{id}.status") [BEFORE writing intent] +Hub B: BEGIN tx: INSERT broker_dispatch{op:start, args, state=pending} + + NOTIFY scion_broker_cmd{broker_id:X} COMMIT +Hub A: signal-listener; ownsLocally=yes; CAS-claim dispatch row pending→in_progress +Hub A: StartAgent local tunnel → broker accepts; mark dispatch done +Hub A: broker/provider advances; each step → PublishAgentStatus ── NOTIFY agent.{id}.status ─┐ + phase=starting; activity="pulling image"; detail="… 40%" (broker heartbeats detail) │ +Hub B: <-sub: ANY change (phase/activity/detail) → surface to caller + RESET rolling window ◄─┤ +Hub B: <-sub: phase==running (terminal) → 200 OK ◄──────────────────────────────────────────┘ + phase==error → 502 + agent.Message + no update within rolling window → dispatch FAILED: mark phase=error / dispatch.state=failed, 503 + (broker is expected to keep emitting detail while working; silence ⇒ stuck/dead) +``` + +### 9.3 `check_prompt` / env-gather (data result, no phase) + +``` +Hub B: Subscribe("broker.dispatch.{id}") [BEFORE writing intent] +Hub B: BEGIN tx: INSERT broker_dispatch{op:check_prompt, state=pending} + + NOTIFY scion_broker_cmd{broker_id:X} COMMIT +Hub A: ownsLocally=yes; CAS-claim; run local tunnel → result (bool / env-requirements) +Hub A: UPDATE broker_dispatch SET state=done, result=… ; Publish broker.dispatch.{id} +Hub B: <-sub ; read result from the dispatch row → return to caller + (event missed? re-read row at timeout — result is authoritative DB state) +``` + +### 9.4 Broker flap A→B (disconnect race fix) + +``` +t0 broker X socket on Hub A: connected_hub_id=a9c3, session=s1 +t1 LB reshuffle; X re-dials, lands on Hub B +t2 Hub B HandleUpgrade(session=s2); ClaimRuntimeBrokerConnection(X, b2f1, s2) + → row now (b2f1, s2), status=online +t3 Hub A's old socket finally errors; onDisconnect(X, s1) + → ReleaseRuntimeBrokerConnection(X, a9c3, s1): affinity is (b2f1,s2) ≠ (a9c3,s1) + → cleared=false → SKIP offline stamp ✅ (today this clobbered B's online) +``` + +--- + +## 10. Out of scope (maintainer-confirmed): PTY, logs, exec + +These are **not** part of this work item. They do not fit "DB as state machine": + +- **PTY / interactive streams** (`OpenStream`/`SendStreamData`/`ResizeStream`/ + `CloseStream`) — high-frequency, ordered, back-pressured bytes. NOTIFY is wrong for + this (8000B cap, no flow control, fan-out to all nodes). +- **Logs** (`GetAgentLogs`) and **exec** (`ExecAgent`) — request/response bodies / + streaming, not state transitions. + +Why they can't simply reuse this design: the response/stream must originate from the +*owning* node, and **there is no hub-to-hub transport** (§2.0) to relay it. So the only +viable future approach is **sticky client routing** — terminate the user's stream on the +owning node directly: + +- **LB session affinity** keyed so the PTY/logs/exec client lands on the node that owns + the broker (e.g. cookie/route keyed to broker or agent), **or** +- introduce **hub addressability** (a `hub_instances(instance_id, endpoint, last_seen)` + table + reachable internal endpoints) so an entry node can reverse-proxy the + upgrade/stream to the owner. This is a larger change explicitly deferred. + +Until one of those lands, PTY/logs/exec work only when the client happens to hit the +owning node. **Document as a known multi-node limitation; gate "full multi-node GA" on a +separate streaming design.** + +--- + +## 11. Open questions for the maintainer (@ptone) + +Asked one at a time per protocol; answers folded back into this doc as received. + +- **OQ1 — RESOLVED (2026-06-02).** Maintainer reframed: there is **no hub-to-hub HTTP**; + broker tunnels are sticky to an arbitrary node; **NOTIFY is the only inter-node + transport**. Folded into §2.0. (Whether direct-mode `broker.Endpoint` brokers exist at + all is a minor optimization; the design does not depend on it.) + +- **Scope — RESOLVED (2026-06-02).** Maintainer: **message + agent lifecycle only**; + **PTY, logs, exec out of scope**; model is **"DB as state machine, NOTIFY as the + communications channel."** Folded into §2.0.1, §5, §6, §10. + +- **OQ4 (durability) — RESOLVED by the state-machine model.** Intent is durable in the + DB and reconciled on broker reconnect-drain (§5.3), so an owner being down delays but + never loses a command. No separate ephemeral queue. *Confirm this is the intended + durability bar* (vs. also persisting a per-attempt audit log). + +- **OQ5 (loss visibility) — RESOLVED by the state-machine model.** Messages carry + `dispatch_state` + `dispatched_at` on the existing row; lifecycle carries + `broker_dispatch.state`. Sweep in §7.1. *Confirm column placement is acceptable.* + +- **OQ2 — RESOLVED (2026-06-02).** Contract confirmed (owner accepts + publishes phase + = done; not harness-ready). Timeouts: long providers (GKE, future runtimes) need more + time + interim feedback, handled by a **rolling timeout on the existing 3-layer agent + state** that resets on each interim update (see OQ6 below for the full resolution). + Folded into §6.2, §6.4, §9.2. + +- **OQ6 — RESOLVED (2026-06-02, maintainer + coordinator).** Reuse the existing 3-layer + state (phase/activity/**detail**); interim states are **untyped** free-text in `detail` + (no canonical set to define). Timeout is a **rolling window that resets on each interim + update**; brokers heartbeat `detail` (own timer loop) while a step runs; no update within + the window ⇒ dispatch **failed**. Folded into §6.2, §6.4, §9.2. (Async-202 noted only as + an LB-idle escape hatch, §6.4.) + +Still genuinely open (lower stakes; sensible default proposed — non-blocking): + +- **OQ3 (liveness signal):** Is `last_heartbeat`-freshness + the rolling dispatch timeout + sufficient to decide "a node owns this broker" for v1 (recommended), or introduce a + `hub_instances` heartbeat table now? (Note: `hub_instances` buys nothing for *this* + scope without hub-to-hub HTTP; it only pays off for the deferred PTY/logs/exec work in + §10, so deferring it with that work is the natural call.) + +--- + +## 12. Implementation sequencing (suggested) + +1. **Phase 1 — affinity + race fix (independently shippable).** Per-process + `instanceID`; affinity columns on `runtime_brokers`; `ClaimRuntimeBrokerConnection` / + `ReleaseRuntimeBrokerConnection` (CAS compare-and-clear); thread `sessionID` through + `markBrokerOnline` and the `onDisconnect(brokerID, sessionID)` callback. **Fixes the + disconnect-race correctness bug today**, with no dependency on dispatch. +2. **Phase 2 — state-machine substrate.** `broker_dispatch` table + store methods + (insert / CAS-claim / complete / drain) and `messages.dispatch_state`/`dispatched_at`. + `PostgresCommandBus`: a listener on `scion_broker_cmd` reusing the events_postgres + connect/keepalive/reconnect helpers; the **reconcile-on-connect drain** wired into + `markBrokerOnline`. +3. **Phase 3 — message dispatch.** `route` in `HybridBrokerClient`; transactional + intent+signal for `message`/broadcast/`set[]`; owner drain → tunnel → mark dispatched. + (Fixes the originally-reported message split-brain.) +4. **Phase 4 — lifecycle dispatch.** `start`/`stop`/`restart`/`delete` via + `broker_dispatch` + phase/`agent.deleted` observation; then the create-time data ops + (`finalize_env`, `check_prompt`, `create-with-gather`) via `broker_dispatch.result` + + the `broker.dispatch.` completion event. +5. **Phase 5 — hardening.** Stale-affinity / stuck-`in_progress` reaper singleton; + `pending`-message sweep + metrics. +6. **Deferred — PTY / logs / exec.** Separate streaming design (§10). Out of scope. + +Phase 1 is independently shippable and fixes a real correctness bug today. Phases 2–3 +deliver the originally-reported message-dispatch fix; Phase 4 completes lifecycle. diff --git a/.design/nfs-workspace.md b/.design/nfs-workspace.md new file mode 100644 index 000000000..bd1c4d0f2 --- /dev/null +++ b/.design/nfs-workspace.md @@ -0,0 +1,740 @@ +# Design: NFS-Coordinated Workspace Sharing Across Nodes + +**Branch:** `postgres/wave-b-integration` +**Date:** 2026-06-02 +**Author:** nfs-architect agent +**Status:** Design proposal — **all open questions (Q1–Q6) resolved with maintainer** (see §11) +**Vocabulary:** follows `GLOSSARY.md` (Runtime Broker, Project, workspace sharing modes) +**Reviewers:** @ptone +**Context:** Multi-node Scion (Postgres-backed Hub, brokers/agents spread across VMs and GKE/Cloud Run) needs a shared filesystem so an agent can reach its project workspace regardless of which node it lands on. + +Inputs (verified against source): +`pkg/runtime/common.go`, `pkg/runtime/k8s_runtime.go`, `pkg/config/shared_dirs.go`, +`pkg/api/types.go`, `pkg/store/models.go`, `pkg/ent/schema/{project,agent}.go`, +`pkg/runtimebroker/{types,handlers,start_context}.go`, `pkg/agent/run.go`, +`scripts/starter-Hub/`, `pkg/gcp/storage.go`. + +--- + +## 1. Problem statement + +Workspace storage in Scion is **node-local** today. Two facts make that fatal once +agents can be scheduled across nodes: + +1. **Docker/VM path.** The Runtime Broker computes a host path + (`~/.scion/project-configs/__/...` or a git checkout) and bind-mounts + it into the container: `-v HOST:/workspace` (`pkg/runtime/common.go:181-241`). That + host path only exists on the node where the Runtime Broker created it. A second agent for + the same project, dispatched to a different VM, sees an empty disk. + +2. **Kubernetes path.** The workspace volume is an **EmptyDir** + (`pkg/runtime/k8s_runtime.go:1080-1087`); the Runtime Broker then copies files into the pod + after start via `kubectl cp`, gated on a `/tmp/.scion-home-ready` marker + (`k8s_runtime.go:317-350`). Contents live only inside that pod and die with it. + There is no shared durable workspace at all. + +Shared directories are the one place a cross-node primitive already exists: on K8s +they are **project-scoped `ReadWriteMany` PVCs** named `scion-shared--` +(`k8s_runtime.go:657-751`), storage class from `KubernetesConfig.SharedDirStorageClass`. +On Docker they are plain host bind mounts (`pkg/config/shared_dirs.go`). So the RWX +PVC concept is proven; we extend the same idea to the **workspace** and give Docker an +equivalent via NFS. + +**Goal:** a project's workspace (and shared dirs) live on a network filesystem +addressable from every node, so any agent — on any VM, GKE pod, or Cloud Run +instance — mounts the same bytes. The Hub coordinates *which* NFS path maps to *which* +project/agent and tells the runtime how to mount it. + +### 1.1 Non-goals + +- **Creating & permissioning the NFS store.** Operator/Terraform owns *creation and + permissioning* of the NFS instance and its shares — this is the **only** thing that + happens outside the Hub / Runtime Broker lifecycle (maintainer, Q1). Everything else, including + **mounting** the share, is the Hub / Runtime Broker's job (§4.2). A single NFS instance may + expose **multiple shares** and serve **multiple Hub instances within one project**. +- **A distributed POSIX lock manager.** We rely on NFS-native advisory locking plus + Scion's existing per-agent state isolation; we do *not* build a lock service. +- **Replacing GCS-FUSE volumes.** `type: gcs` volumes (`common.go:142-163`, + `k8s_runtime.go:1238-1275`) stay as-is; NFS is a parallel backend. +- **Auto-migration of existing node-local workspaces.** New backend applies to new + projects/agents; migration is a separate effort (§10). + +--- + +## 2. Current architecture (verified) + +| Concern | Docker / VM | Kubernetes | +|---|---|---| +| Workspace storage | host bind mount `-v HOST:/workspace` (`common.go:185`) | EmptyDir + post-start `kubectl cp` (`k8s_runtime.go:1084`, `:317-350`) | +| Workspace host path | Runtime Broker-computed, node-local (`agent/run.go:755-780`, `start_context.go:92-110`) | n/a (synced in) | +| Container workspace path | `ResolveContainerWorkspace` → `/workspace` or `/repo-root/` (`common.go:52-69`) | same logic, `config.ContainerWorkspace` | +| Shared dirs | host bind mount under `project-configs/.../shared-dirs/` (`shared_dirs.go:33-118`) | project-scoped RWX PVC `scion-shared--` (`k8s_runtime.go:657-751`) | +| Volume types | `local`, `gcs` (`api/types.go:248-279`) | `gcs` (CSI), no local bind | +| Container UID | host user `scion` | UID/GID 1000, `FSGroup=hostGID` (`k8s_runtime.go:1021-1033`) | +| Placement metadata | none on Agent; Runtime Broker decides at dispatch | none | +| Workspace entity | **none** — derived from `Project.GitRemote` + workspace sharing mode (today a 2-value label `scion.dev/workspace-mode` ∈ {`shared`,`per-agent`}, `store/models.go:177-184`; glossary's canonical 3 modes are the target — §3.1) | same | + +Key data structures: + +- `api.VolumeMount{Source,Target,ReadOnly,Type,Bucket,Prefix,Mode}` (`api/types.go:248-256`). + `Validate()` only accepts `""|local|gcs` (`:264-276`). +- `api.SharedDir{Name,ReadOnly,InWorkspace}` (`api/types.go:205-210`); persisted as a + JSON column on the **Project** Ent entity (`ent/schema/project.go:62-63`). +- `api.KubernetesConfig{... SharedDirStorageClass, SharedDirSize}` (`api/types.go:291-302`). +- `config.V1StorageConfig{Provider,Bucket,LocalPath}` — Hub *artifact* storage, not + workspaces (`config/settings_v1.go:416-420`). +- `runtime.RunConfig{Workspace,RepoRoot,ContainerWorkspace,HomeDir,Volumes,SharedDirs,GitClone,...}` + (`runtime/interface.go`) — the per-agent contract the Runtime Broker fills and the runtime consumes. +- `runtimebroker.CreateAgentConfig{Workspace,RepoRoot,HomeDir,Volumes,SharedDirs,GitClone}` + — the Hub→Runtime Broker wire contract (`runtimebroker/types.go:369-411`). + +The single most important leverage point: **the runtime already mounts whatever host +path / volume the Runtime Broker hands it.** If we make that host path land on an NFS mount +(Docker) or swap the EmptyDir for an NFS-backed volume (K8s), most of the machinery is +untouched. The design is therefore mostly about **path mapping, provisioning, and +config**, not about rewriting the mount code. + +--- + +## 3. Core concept: a workspace storage backend + +Introduce an explicit **workspace storage backend** selected by config, with three +values: `local` (today's behavior, default), `nfs` (this design), and — reserved — +`gcs` (FUSE, already exists for *volumes* but not for the primary workspace). + +A backend answers three questions for any (project, agent): + +1. **Resolve** — given Project ID / agent ID / workspace sharing mode, what is the + storage location? For NFS this is a *server-relative export path*, computed + **deterministically from IDs** (no new DB column required for resolution — any + replica computes the same path): + + ``` + /projects//workspace # Shared-plain & Worktree-per-agent + /projects//shared-dirs/ # shared directories + ``` + +2. **Provision** — ensure the directory exists and, for git projects, is cloned/worktree'd + (§7). Idempotent; guarded against concurrent first-access (§8.2). + +3. **Realize** — emit the runtime-specific mount: + - Docker: a bind mount whose `Source` is the host NFS mountpoint + relative path. + - K8s: an NFS-backed volume (static PV+subPath or Filestore-CSI PVC) at the workspace path. + - Cloud Run: an NFS volume in the service/job spec with `path = /`. + +The Hub owns resolution + the mount spec it sends; the **Runtime Broker/runtime owns +provisioning and the actual mount syscall** (it is the component with filesystem / +cluster access). This mirrors today's split (Hub computes `CreateAgentConfig`, the +Runtime Broker realizes it). + +### 3.1 NFS applicability is driven by **workspace sharing mode** (maintainer, Q3) + +Per the glossary (`GLOSSARY.md` → *Workspace sharing mode*) there is **one** universal +set of three modes; the backend is **not** a separate per-project toggle — the sharing +mode *is* the selector: + +| Workspace sharing mode | What it means | Storage backend | +|---|---|---| +| **Shared-plain** | one workspace directory mounted into every agent, no per-agent isolation (plain/non-git projects) | **shared NFS workspace** | +| **Worktree-per-agent** | each agent gets its own git worktree over one shared checkout (one clone's history) | **shared NFS workspace** (the shared checkout + all worktrees live on it) | +| **Clone-per-agent** | each agent gets its own full git clone | **NOT NFS** — node-local disk (nothing is shared, so there is nothing to put on NFS) | + +So: **NFS backs the workspace for both sharing modes that share anything** +(Shared-plain and Worktree-per-agent) and backs **shared directories always**. +**Clone-per-agent** is the sole case that stays on node-local storage — a deliberate +"throwaway isolated clone" path. The `backend` config value (§6.1) therefore really +answers "*is a shared NFS workspace available on this Hub?*"; whether a given agent uses +it follows mechanically from the project's sharing mode. + +**Terminology note:** today the code carries only a two-value label +`scion.dev/workspace-mode ∈ {shared, per-agent}` (`store/models.go:177-184`). The +glossary's three canonical modes (Shared-plain / Worktree-per-agent / Clone-per-agent) +are the target vocabulary; aligning the label/enum to all three is a prerequisite +clean-up for this work (Worktree-per-agent is noted as "not yet on Hub-managed +projects"). + +--- + +## 4. Model A — VMs / Docker (host-level NFS, bind into container) + +### 4.1 Topology + +``` + ┌────────────── NFS server (Filestore / self-hosted) ─────────────┐ + │ export: /scion-workspaces │ + │ projects//workspace │ + │ projects//shared-dirs/ │ + └───────▲───────────────────────────────▲──────────────────────────┘ + │ mount (Runtime Broker, on startup) │ + ┌───────────┴──────────┐ ┌────────────┴─────────┐ + │ VM node-1 │ │ VM node-2 │ + │ /mnt/nfs/workspaces │ │ /mnt/nfs/workspaces │ + │ Runtime Broker + dockerd │ │ Runtime Broker + dockerd │ + │ agent ctr │ │ agent ctr │ + │ -v /mnt/nfs/.../ws │ │ -v /mnt/nfs/.../ws │ + │ :/workspace │ │ :/workspace │ + └──────────────────────┘ └──────────────────────┘ +``` + +### 4.2 Host mount — owned by the Hub / Runtime Broker, idempotent on (re)start + +**Decision (maintainer, Q1):** mounting is part of the **Hub / Runtime Broker service +lifecycle**, not an operator step. When a Runtime Broker comes online (cold start or restart) +it **ensures the configured share(s) are mounted** before accepting NFS-backed +dispatch; the operator only created+permissioned the store. This must be **idempotent +and restart-safe** — a Runtime Broker bouncing must reconcile, not double-mount or fail on an +already-present mount. + +Mount reconciliation at Runtime Broker startup (and re-checked before each NFS dispatch): + +``` +for each configured share S needed on this node: + target = / # stable, per-share path + if not is_mountpoint(target): + mkdir -p target + mount -t nfs -o vers=4.1,hard,nconnect=4,_netdev S.server:S.export target + else: + verify it points at the expected server:export (else log + remount) +``` + +Implementation notes: +- A Runtime Broker may need **multiple shares mounted at once** (a single NFS instance can + expose many shares, and one project may be served by multiple Hub instances). The + mount layer is therefore a *set* of shares keyed by share-id, each at its own + `/`, not a single global `/mnt/nfs/workspaces`. +- Prefer a managed systemd `.mount`/`automount` unit *written and started by the + Runtime Broker* over a raw `mount(8)` call, so the OS handles remount-on-reboot and the + Runtime Broker's job is reconciliation, not lifecycle. +- Run inside the existing Runtime Broker bring-up path (alongside doctor/health checks). On + mount failure, the Runtime Broker reports unhealthy for NFS-backed projects rather than + silently falling back to local disk. +- Requires the Runtime Broker to have mount privilege (root or `CAP_SYS_ADMIN`/sudo for + `mount`); call out in deployment docs. + +### 4.3 Path computation change (the only real code change for Model A) + +Today the Runtime Broker resolves the workspace/shared-dir host path under +`~/.scion/project-configs/...` (`pkg/config/shared_dirs.go:33-54`, +`runtimebroker/start_context.go:92-110`). With backend=`nfs`, that resolution is +redirected to the NFS mountpoint: + +``` +hostBase = / # Runtime Broker ensures this is mounted (§4.2) +workspace host path = hostBase/projects//workspace +shared dir host path = hostBase/projects//shared-dirs/ +``` + +`SharedDirsToVolumeMounts` (`shared_dirs.go:90-118`) is unchanged in shape — it still +emits `VolumeMount{Source: hostPath, Target: /scion-volumes/}`; only the base +path moves onto NFS. The container sees **no difference** — it is still a bind mount at +`/workspace` (or `/repo-root/`), and the existing repo-root tmpfs-shadow isolation +(`common.go:357-362`) continues to protect per-agent state. + +### 4.4 Lifecycle + +- **Create on first agent:** Runtime Broker `mkdir -p` + provision (clone) under the + project dir if absent (§7). The shared NFS workspace is reused across agents in both + Shared-plain and Worktree-per-agent modes (§3.1); a Worktree-per-agent agent adds its + own worktree under the shared checkout. +- **Persist across agents:** the workspace survives agent deletion (it is project-scoped). + A Worktree-per-agent agent's worktree is removed on that agent's deletion; the shared + checkout persists. +- **Cleanup:** on project deletion the Hub instructs a Runtime Broker to `rm -rf` the + project subtree (mirrors `cleanupSharedDirPVCs` on K8s, `k8s_runtime.go:753-770`). + Optional idle-GC by mtime (§10). (Clone-per-agent workspaces are node-local, not on + NFS, and are cleaned by the existing local path on agent delete.) + +--- + +## 5. Model B — GKE / Cloud Run (direct NFS per pod/instance) + +No shared host mount. Each pod/instance mounts the NFS share directly. **Decision +(maintainer, Q4): the target is Filestore *basic* tier**, which has one share per +instance and a 1 TiB minimum and **no multishare** — so strategy **(a) static RWX PV + +per-workspace `subPath` is THE default**, and the dynamic per-project-share strategy (b) +is an Enterprise-tier-only future option, not used now. + +### 5.1 GKE — strategy (a): one RWX PV + per-workspace `subPath` (recommended default) + +A single `PersistentVolume` (RWX) points at the Filestore share (or self-hosted NFS). +Each pod mounts it with a `subPath` equal to the project/agent relative path. This +avoids per-workspace PVC/Filestore-share churn (Filestore *basic* tier has a 1 TiB +minimum per instance — one share per workspace is economically impossible). + +```yaml +# Provisioned once by operator/Hub-bootstrap: +apiVersion: v1 +kind: PersistentVolume +metadata: { name: scion-workspaces } +spec: + capacity: { storage: 1Ti } + accessModes: [ReadWriteMany] + nfs: { server: 10.0.0.2, path: /scion-workspaces } # or csi: filestore.csi... + mountOptions: [vers=4.1, hard, nconnect=4] + persistentVolumeReclaimPolicy: Retain +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: { name: scion-workspaces, namespace: scion-agents } +spec: + accessModes: [ReadWriteMany] + storageClassName: "" # bind to the static PV + volumeName: scion-workspaces + resources: { requests: { storage: 1Ti } } +``` + +Pod spec the runtime builds (replaces the EmptyDir at `k8s_runtime.go:1080-1087`): + +```yaml +volumes: +- name: workspace + persistentVolumeClaim: { claimName: scion-workspaces } +containers: +- name: agent + volumeMounts: + - name: workspace + mountPath: /workspace + subPath: projects//workspace # <-- per-workspace isolation within share +``` + +`subPath` is the linchpin: the pod can only see its own project subtree, never the +whole export — this is the K8s mount-isolation story (§9.4). + +### 5.2 GKE — strategy (b): Filestore CSI dynamic PVC per project *(Enterprise-only, deferred)* + +**Not used with the chosen basic tier (Q4).** Recorded for the future: for projects that +need a *dedicated* share (isolation/quota), the Filestore CSI driver +`filestore.csi.storage.gke.io` with a StorageClass lets the Hub create **one PVC per +project** (project-scoped like shared dirs today, `k8s_runtime.go:707-712`). This only +pencils out on Enterprise / "multishare" tiers (many small shares per instance); on basic +tier each PVC would demand a full 1 TiB instance, so it is economically impossible. +When/if adopted, reuse the existing shared-dir PVC code paths (`createSharedDirPVCs`, +`cleanupSharedDirPVCs`) generalized from "shared dir" to "any RWX workspace claim". + +### 5.3 GKE — shared dirs + +Already RWX PVCs. With NFS, simply point `KubernetesConfig.SharedDirStorageClass` at +a Filestore/NFS class, or fold shared dirs into the single-share+subPath model +(`subPath: projects//shared-dirs/`). No new code beyond config. + +### 5.4 Cloud Run + +Cloud Run (gen2 execution environment) supports **NFS volume mounts** and Filestore +directly. The Hub emits a volume in the Service/Job spec; each instance mounts at the +workspace path: + +```yaml +# Cloud Run service (knative-ish) volume +volumes: +- name: workspace + nfs: + server: 10.0.0.2 + path: /scion-workspaces/projects//workspace # server-side path = isolation + readOnly: false +containers: +- volumeMounts: [{ name: workspace, mountPath: /workspace }] +``` + +Cloud Run has no `subPath`, so isolation comes from the **server-side `path`** being +the project subdir (the instance can only reach what the export path exposes). The Hub +must therefore put the project id in the NFS `path`, not rely on in-container subPath. + +### 5.5 The big consequence for K8s/CR: workspace is pre-populated + +Today K8s starts with an empty workspace and the Runtime Broker `kubectl cp`s files in +(`k8s_runtime.go:317-350`). With an NFS-backed workspace the bytes are **already +present** on shared storage. The post-start sync of *workspace contents* becomes +unnecessary in the NFS case — provisioning (clone) happens once, out-of-band (§7), not +per-pod. Home-dir/secret sync and the `/tmp/.scion-home-ready` gate may still be needed +for non-workspace material; that path stays, but the workspace copy step is skipped +when backend=`nfs`. This is a meaningful simplification *and* a behavior change to call +out in review (§11 Q5). + +--- + +## 6. Data model & config changes + +### 6.1 New config block (Hub settings) + +```go +// pkg/config/settings_v1.go +type V1WorkspaceStorageConfig struct { + Backend string `json:"backend,omitempty" koanf:"backend"` // "local" (default) | "nfs" + + NFS *V1NFSConfig `json:"nfs,omitempty" koanf:"nfs"` +} + +type V1NFSConfig struct { + // One NFS instance may expose multiple shares (maintainer, Q1); a Runtime Broker + // mounts the set it needs. MountRoot is the local base under which each + // share is mounted at /. + MountRoot string `json:"mount_root,omitempty"` // e.g. /mnt/nfs ; per-share dir appended + MountOptions string `json:"mount_options,omitempty"` // default "vers=4.1,hard,nconnect=4,_netdev" + Shares []V1NFSShare `json:"shares,omitempty"` + + // Stable, node-independent ownership for NFS-backed trees (§9.1). Default + // 1000:1000 to converge with the K8s pod UID/GID. The Runtime Broker advertises + // these as SCION_HOST_UID/GID for NFS-backed agents instead of os.Getuid(). + UID int `json:"uid,omitempty"` // default 1000 + GID int `json:"gid,omitempty"` // default 1000 + + // Kubernetes realization (Model B) + StorageClass string `json:"storage_class,omitempty"` // Filestore-CSI dynamic strategy (5.2) + SubPathRoot string `json:"subpath_root,omitempty"` // default "projects" +} + +type V1NFSShare struct { + ID string `json:"id,omitempty"` // stable share id → mount dir + (K8s) PV name + Server string `json:"server,omitempty"` // e.g. 10.0.0.2 or Filestore IP + Export string `json:"export,omitempty"` // server export path, e.g. /scion-workspaces + PVName string `json:"pv_name,omitempty"` // K8s static PV+subPath strategy (5.1) +} +``` + +A project selects which share holds its workspaces (default: the single configured +share; explicit when multiple exist). Resolution becomes +`/projects//...`, and the Runtime Broker ensures that share is mounted (§4.2) +before realizing the bind mount. + +- `Backend` defaults to `local` → **zero behavior change** for existing deployments. +- **No separate per-project backend toggle.** Per the maintainer (Q3), backend selection + follows the **workspace sharing mode** (§3.1), not an independent flag: when + `Backend: nfs` is configured on the Hub, **Shared-plain** and **Worktree-per-agent** + projects use the shared NFS workspace; **Clone-per-agent** always uses node-local disk. + `Backend` thus gates *availability* of NFS on a Hub; the mode decides *use*. (If NFS is + not configured, shared modes degrade to single-node local — same as today.) + +### 6.2 Extend `VolumeMount` for explicit NFS volumes + +For user-declared volumes (and to let shared dirs / workspace flow through one code +path), add `nfs` to the type set: + +```go +type VolumeMount struct { + // ... existing ... + Type string // "local" | "gcs" | "nfs" + Server string `json:"server,omitempty"` // NFS: server host/IP + // Source is reused as the server export path for NFS; Prefix/subpath optional +} +``` + +Update `VolumeMount.Validate()` (`api/types.go:259-279`) to accept `nfs` requiring +`Server` + `Source` (export path) + `Target`. The existing `type: "nfs"` fixtures +(`pkg/api/types_test.go`, `pkg/config/templates_test.go`), presently *rejected*, become +valid (maintainer-confirmed, Q6). Workspace, shared directories, and ad-hoc user volumes +all flow through this one NFS volume path. + +### 6.3 Generalize the K8s shared-dir PVC helpers + +`sharedDirPVCName` / `createSharedDirPVCs` / `cleanupSharedDirPVCs` +(`k8s_runtime.go:657-770`) become a generic "project RWX claim" helper used for both +shared dirs and (strategy 5.2) workspaces. No schema change; reuses +`KubernetesConfig.SharedDir{StorageClass,Size}` plus the new `NFS.StorageClass`. + +### 6.4 No new Workspace entity, no placement column + +Resolution is deterministic from Project ID + agent ID + workspace sharing mode, so a +replica behind the load balancer computes the same NFS path without a DB lookup — +consistent with the broker-dispatch design (`DESIGN-BROKER-DISPATCH.md`) preference for +derivable state. We add +**no** placement/node column to Agent. (Optional future: cache the resolved path in +`AgentAppliedConfig.WorkspaceStoragePath`, which already exists for the GCS-bootstrap +case, `store/models.go:127-165` — reuse it rather than add a field.) + +--- + +## 7. Workspace provisioning (git clone onto NFS) + +A workspace must exist and (for git projects) be cloned/worktree'd before the harness +runs. Where the clone executes differs by model: + +- **Model A (Docker):** the **Runtime Broker** has direct filesystem access to + `/mnt/nfs/workspaces`. It runs the existing clone/worktree logic targeting the NFS + path. This is the smallest change — same code, different base dir. + +- **Model B (K8s/CR):** the Runtime Broker has **no** host access to the pod's NFS mount. + Two options: + 1. **Init container (recommended):** the pod gets an init container that mounts the + same workspace volume and performs the clone/worktree into it before the main + container's gate releases. The clone runs where the mount lives; no extra Hub + filesystem access needed. Fits the existing gate model (`k8s_runtime.go:317-350`). + 2. **Hub-side provisioner:** a Hub / Runtime Broker node mounts the same export and clones into + the project subdir out-of-band. Simpler to reason about for shared mode (clone + once, centrally) but requires the Hub node to have the NFS mount. See §11 Q5. + +**First-access guard (shared mode):** multiple agents for the same project may start +concurrently and race to clone the same dir. Guard with one of: +- a **sentinel file** (`.scion-provisioned`) created atomically after a successful clone; +- a **Postgres advisory lock** keyed by project-id (the Hub already uses advisory locks + — see commit `dcd4e0f6`/`f6d2a727` — so this is a natural, cross-node-correct choice); +- an NFS **advisory `flock`** on a lockfile in the project dir (works on NFSv4). + +Recommended: Postgres advisory lock for the *decision* ("am I the provisioner?"), +because it is already cross-node-correct and avoids NFS lock-manager variability. + +--- + +## 8. Sequence diagrams + +### 8.1 Agent start — Model A (Docker, shared NFS workspace) + +``` +User/API Hub (any replica) Runtime Broker (node-N) dockerd / agent ctr + │ start agent │ │ │ + ├─────────────────►│ │ │ + │ resolve backend=nfs │ │ + │ relPath=projects//workspace│ │ + │ CreateAgentConfig{ │ │ + │ Workspace=, │ │ + │ WorkspaceBackend=nfs} │ │ + │ ├─ dispatch ──────────────►│ │ + │ │ validate /mnt/nfs present │ + │ │ hostPath = /mnt/nfs/workspaces/ │ + │ │ acquire advisory lock(pid) │ + │ │ if !provisioned: git clone → hostPath │ + │ │ release lock │ + │ │ RunConfig.Workspace = hostPath │ + │ │ ├─ docker run -v hostPath:/workspace ─────►│ + │ │ │ harness runs│ + │◄─────────────────┤◄─────────────┤◄─────────────────────────────────────────┤ +``` + +### 8.2 Agent start — Model B (GKE, Filestore, static PV + subPath) + +``` +Hub (any replica) K8s runtime (Runtime Broker) kube-apiserver / kubelet pod + │ resolve backend=nfs │ │ │ + │ subPath=projects//workspace │ │ + │ ── CreateAgentConfig ────►│ │ │ + │ ensure PVC scion-workspaces (reuse if exists, k8s_runtime style) │ + │ buildPod: volume=PVC, mount /workspace subPath= │ + │ initContainer: clone into subPath (advisory-locked) │ + │ ├─ create Pod ──────────────►│ │ + │ │ schedule + mount NFS (subPath) ────────►│ + │ │ │ initC clones (once) │ + │ │ │ gate releases ───────►│ + │ │ │ harness runs │ + │ │ (NO kubectl cp of workspace — already on NFS) │ +``` + +--- + +## 9. Cross-cutting concerns + +### 9.1 Permissions / UID mapping — branch on host-FS vs NFS + +Rewritten per the maintainer's Q2 guidance (*study the existing remapping, then branch +on host-vs-NFS, keep it simple*). How it works today and the minimal branch NFS needs: + +**How Docker reconciles host-FS ownership today (verified):** +- The Runtime Broker injects its **own host UID/GID** as `SCION_HOST_UID`/`SCION_HOST_GID` + (`common.go:281-282` → `os.Getuid()/os.Getgid()`). +- Container starts as **root**; `sciontool init` → `setupHostUser()` + (`cmd/sciontool/commands/init.go:923`) **remaps the image's `scion` user to that host + UID/GID** via `usermod -o -u $SCION_HOST_UID -g $SCION_HOST_GID scion` (`init.go:1045`; + direct `/etc/passwd` fast-path on fuse-overlayfs, `:1033/:1086`), `chown`s the + workspace (`ensureWorkspaceOwnership`, `:1416`), then drops privileges. Files thus land + on the bind mount **owned by the Runtime Broker's host user** — fine, because the disk is + node-local. +- Podman rootless uses `--userns=keep-id:uid=1000,gid=1000` + `SCION_KEEPID_UID` with an + early drop to 1000 (`podman.go:172`, `init.go:975`). +- K8s uses a **fixed** UID/GID 1000 + `FSGroup=hostGID` (`k8s_runtime.go:1021-1033`). + +**Why NFS can't reuse the "host-UID" scheme:** one export is shared by agents on +*different* nodes whose brokers may have *different* host UIDs, and NFS authorizes by +**numeric UID on the wire**. Files written by node-1 (owned by node-1's Runtime Broker UID) may +be unwritable by node-2. NFS-backed trees therefore need a **node-independent, stable +UID/GID**. + +**The branch (small, localized):** choose *which UID the Runtime Broker advertises to the +container*, on host-vs-NFS: + +``` +// in buildCommonRunArgs (pkg/runtime/common.go, near :281) +uid, gid := os.Getuid(), os.Getgid() // host-FS path: UNCHANGED (today's behavior) +if backend == nfs { + uid, gid = cfg.NFS.UID, cfg.NFS.GID // stable; default 1000:1000 to match K8s +} +addEnv("SCION_HOST_UID", uid); addEnv("SCION_HOST_GID", gid) +``` + +The whole downstream remap pipeline (`setupHostUser` → `usermod`/passwd-edit → drop) is +**unchanged** — it already does the right thing for whatever UID it's told; for NFS it +remaps `scion` to the stable UID instead of the host Runtime Broker UID. One branch on the +*value advertised*, no new remap machinery. + +**Chown discipline on NFS:** do **not** recursively `chown` the NFS tree on every start +(`ensureWorkspaceOwnership`) — slow over the network and racy across nodes. Skip/guard +it when backend=nfs. Ownership is set **once**: (1) operator permissions the store at +creation (Q1) for the stable GID; (2) the provisioner (§7) `chown`s a project subtree +once at first creation, under the advisory lock. + +**Convergence:** set the NFS stable UID/GID = **1000:1000** so Docker, Podman, and K8s +all agree. On K8s, `FSGroup` (today `os.Getgid()`, `k8s_runtime.go:1022`) should be the +stable GID for NFS-backed pods — same host-vs-NFS branch. + +**Podman rootless + NFS** is the awkward corner: `keep-id` maps via subuid ranges and +won't cleanly yield a stable shared on-wire UID. Recommend rootful Docker / the fixed +scheme for NFS-backed projects and treat rootless+NFS as initially unsupported (Q2). + +- Provisioner sets project subtrees `chown 1000:1000`, mode `0770` (or `2770` setgid for + GID inheritance). UID alone does **not** isolate projects — all agents share UID 1000 + — so project isolation relies on subPath/server-path scoping, not ownership (§9.4). + +### 9.2 Concurrent access (by workspace sharing mode) +- **Clone-per-agent:** node-local, not on NFS (§3.1) → no shared-storage contention at + all. The isolation escape hatch. +- **Worktree-per-agent (on shared NFS workspace):** each agent gets its **own git + worktree** over the one shared checkout (one `.git`/object DB, many worktrees — already + a first-class layout, `common.go:192-196`). Working trees and per-agent index are + isolated, so the dangerous case (concurrent `git checkout` on one index) does not + arise; only worktree *add/remove* touches shared `.git` metadata and is guarded by the + provisioning advisory lock (§7). This is the maintainer-confirmed model for shared git + workspaces on NFS (Q3). +- **Shared-plain (on shared NFS workspace):** one directory mounted into all agents, + intentionally no isolation (plain/non-git). Concurrent writers coordinate at the + application level; Scion's per-agent state already lives outside this mount. +- NFSv4 supports byte-range + `flock` advisory locks; NFSv3 needs `rpc.lockd`. Prefer + **NFSv4.1** in mount options to get reliable locking and `nconnect` throughput. + +### 9.3 Performance +- Git is metadata-heavy (thousands of small files); NFS round-trips dominate + `git status`/`checkout`. Mitigations: + - Mount tuning: `vers=4.1, hard, nconnect=4-8, rsize/wsize=1M, actimeo` tuned for + workload. + - Storage tier: Filestore **zonal/enterprise** (not basic) for IOPS; or self-hosted + NFS on SSD. + - Keep hot, ephemeral, per-agent scratch (build caches, `node_modules`) on **local + ephemeral** disk or a `shared-dir` with `InWorkspace`, not on the cloned tree. + - Optional: local `.git` cache with NFS-hosted worktrees (advanced; defer). +- Benchmark git clone + `status` + a build on Filestore tiers before committing a + default (action item for the integration suite). + +### 9.4 Security / isolation — projects and Hubs (Filestore basic) — Q4 + +**Agent ↔ project isolation (the default we ship):** a single Filestore-basic export is +shared by all nodes, so at the host/cluster level **any node that mounts the export can +reach every project's bytes**. Agent-level isolation comes from only ever exposing the +project's own subtree to the container — **never the export root**: +- K8s: `subPath: projects//...` (§5.1) — pod sees only its project. +- Cloud Run: the server-side NFS `path` is the project subdir (§5.4) — no subPath needed. +- Docker: bind-mount only `projects//...`, not `/`. +This is **Tier 1** and, per Q4, what we adopt given the basic-tier constraint. + +**Hub ↔ Hub isolation (desired, but constrained by the protocol + tier).** The maintainer +asked whether one Hub (with its own GCP service account) could be prevented from mounting +another Hub's shares. Key fact: **NFS data-plane access is not GCP-service-account-aware.** +A service account governs the *GCP control plane* (creating/IAM on the Filestore +instance), but mounting an NFS export is authorized by **network reachability + host/UID**, +not by IAM/SA. So SA-based mount isolation is **not achievable at the NFS layer on any +tier** — and Filestore *basic* additionally has no per-client export ACLs (those are an +Enterprise feature). The realistic isolation levers are therefore: + 1. **Network/firewall scoping (basic-tier feasible, partial):** Filestore basic attaches + to one VPC; restrict TCP/2049 to specific source ranges/tags. This separates Hubs + **only if they sit on different networks/instances** — within one shared instance, + every authorized client can reach the whole share. + 2. **One Filestore instance per Hub (true Hub isolation, the "more expensive option"):** + because basic = one share per instance (1 TiB min), real per-Hub isolation means a + dedicated instance per Hub, firewalled to that Hub's nodes. This is the cost ptone + flagged. + 3. **Enterprise multishare + per-share network rules:** finer isolation without an + instance per Hub — but that is the pricier tier we are *not* using. + +**Recommendation (matches Q4):** ship **Tier 1** now — single shared basic instance, +subpath/server-path scoping for agent↔project isolation, optional firewall scoping. Treat +strict Hub↔Hub isolation as **deferred**: document that it requires either an +instance-per-Hub (basic) or Enterprise multishare, and is skipped until the cost is +justified. Make the share assignment explicit in config (`V1NFSShare.ID` per Hub) so a +future move to instance-per-Hub is a config change, not a redesign. +- **Never** bind/mount the export root into a container — always the project subtree. + +### 9.5 Provisioning lifecycle & reclaim +- Create-on-first-agent; persist project-scoped; cleanup on project deletion (mirror + `cleanupSharedDirPVCs`). PV `reclaimPolicy: Retain` so deleting a PVC never nukes the + Filestore share. Optional idle GC by mtime with a long TTL; log what is reclaimed + (no silent deletion). + +--- + +## 10. Migration & rollout + +1. **Phase 0:** land config + `VolumeMount` `nfs` type + validation; backend defaults + `local`. No runtime behavior change. Unit tests (the existing `nfs` fixtures flip + from "rejected" to "accepted"). +2. **Phase 1 (Model A):** redirect Runtime Broker path resolution to the NFS host mount when + backend=nfs; provisioning + advisory lock. Integration-test on two GCE VMs sharing + one Filestore instance (fits the postgres integration VM fleet). +3. **Phase 2 (Model B / GKE):** static PV + subPath workspace volume; init-container + provisioning; skip workspace `kubectl cp`. Generalize shared-dir PVC helpers. +4. **Phase 3:** Cloud Run NFS volumes; Filestore-CSI dynamic option. +5. **Migration of existing workspaces:** one-shot copy of node-local + `project-configs/...` trees into the NFS layout, flip the project label. Separate + tool; not auto. + +--- + +## 11. Maintainer decisions (all resolved) + +All six questions were resolved with @ptone over `scion message` (2026-06-02), one at a +time; each decision is folded into the sections noted below. + +- **Q1 — NFS server & host-mount ownership. [RESOLVED]** Operator creates and + permissions the NFS store only; **the Hub / Runtime Broker mounts** the share(s) as part of its + service bring-up and re-mounts idempotently on restart. A single NFS instance may + expose multiple shares and serve multiple Hub instances within a project. Reflected + in §1.1, §4.2, §6.1. +- **Q2 — UID alignment. [RESOLVED]** Studied the existing remapping: Docker advertises + the Runtime Broker's host UID (`SCION_HOST_UID`, `common.go:281`) and `sciontool init` remaps + `scion` to it (`usermod`, `init.go:1045`); K8s is fixed at 1000. NFS needs a *stable* + node-independent UID, so the design **branches on host-vs-NFS**: for NFS-backed agents + the Runtime Broker advertises a stable configured `NFS.UID/GID` (default **1000:1000**, + matching K8s) instead of `os.Getuid()`, reusing the unchanged remap pipeline; per-start + NFS chown is skipped (operator + one-time provisioner chown instead). Podman-rootless + + NFS is **unsupported initially**. Confirmed by maintainer. Detailed in §9.1, §6.2. +- **Q3 — Sharing mode ↔ NFS, and concurrency. [RESOLVED]** Maintainer set the model + using the glossary's canonical **workspace sharing modes**: NFS backs **both** the + workspace **and** shared directories; a **shared NFS workspace** serves **all** sharing + modes that share state — **Shared-plain** and **Worktree-per-agent** (worktrees ride on + the shared NFS checkout). The **only** non-NFS case is **Clone-per-agent** (node-local). + Consequence: **no separate per-project backend toggle** — the sharing mode is the + selector; Hub `Backend: nfs` only governs availability. Reflected in §3.1, §6.1, §9.2. +- **Q4 — Isolation tier & GKE strategy. [RESOLVED]** Target is **Filestore basic** → + **Tier 1** default: single shared export + subpath/server-path scoping for agent↔project + isolation, and **static RWX PV + `subPath`** (§5.1) as the GKE strategy (Filestore-CSI + dynamic §5.2 is Enterprise-only, deferred). Maintainer wants Hub↔Hub isolation ideally; + flagged that **NFS mounts are not service-account-gated** (network/host/UID auth only) + and basic tier has no per-client export ACLs, so true Hub isolation needs either an + instance-per-Hub (basic) or Enterprise multishare — **deferred** as the costlier option, + with per-Hub share IDs in config to keep that path open. Reflected in §5, §9.4. +- **Q5 — K8s provisioning & sync change. [RESOLVED — yes to both]** Provision the shared + NFS workspace via an **init container** (mounts the same NFS volume, clones/worktree-adds + once under a Postgres advisory lock on Project ID), and **skip the post-start `kubectl + cp` of workspace contents** when backend=nfs. The home-dir/secret sync and the + `/tmp/.scion-home-ready` readiness gate are unchanged, and the workspace `kubectl cp` is + retained for the local backend. Reflected in §5.5, §7, §8.2. +- **Q6 — `VolumeMount` nfs type. [RESOLVED — yes, NFS first-class]** Add `nfs` as a + first-class `VolumeMount.Type` with a `Server` field (`Source` = server export path); + extend `Validate()` to require `Server`+`Source`+`Target`, flipping the existing + `type: "nfs"` fixtures (`pkg/api/types_test.go`, `pkg/config/templates_test.go`) from + rejected to valid. Workspace, shared directories, and ad-hoc user volumes all flow + through this one unified NFS volume path. Reflected in §6.2. + +--- + +## 12. Summary + +NFS-backed workspaces reuse Scion's proven RWX-PVC pattern (today's shared dirs) and +its existing "Runtime Broker realizes the mount the Hub describes" split. The change is mostly +**path mapping + provisioning + config**, not a rewrite: + +- **Model A (Docker/VM):** the Runtime Broker mounts the NFS share(s) idempotently at + startup (operator only created+permissioned the store), redirects workspace/shared-dir + base paths there, and bind-mounts as today. Container unchanged. +- **Model B (K8s/Cloud Run):** workspace volume becomes NFS-backed (static PV+subPath + default, Filestore-CSI dynamic option; Cloud Run NFS volume with project-scoped server + path). Workspace is pre-populated on shared storage, so the post-start `kubectl cp` + of workspace contents is dropped. +- **Coordination:** deterministic path resolution from IDs (no new placement column); + provisioning guarded by Postgres advisory locks; project-scoped lifecycle mirroring + `cleanup*PVCs`; isolation via subPath / server-path scoping with a per-project-share + upgrade path. diff --git a/.design/project-log/2026-06-04-pr304-review-fixes.md b/.design/project-log/2026-06-04-pr304-review-fixes.md new file mode 100644 index 000000000..13606eafd --- /dev/null +++ b/.design/project-log/2026-06-04-pr304-review-fixes.md @@ -0,0 +1,30 @@ +# PR #304 Review Feedback Fixes + +**Date**: 2026-06-04 +**Branch**: `pr/postgres-core` +**PR**: #304 + +## Changes Made + +Addressed all three review comments from the automated code review: + +### 1. Context leak in `initStore` (medium priority) +- `initStore` was using `context.Background()` for `s.Migrate()` and `s.Ping()`. +- Refactored `initStore` to accept a `context.Context` parameter. +- The server's cancellable context (from `runServerStart`) is now threaded through, allowing graceful cancellation on Ctrl+C during startup. + +### 2. Goroutine/connection leak in event publisher (high priority) +- `initWebServer` was calling `newEventPublisher(context.Background(), cfg)`. +- The Postgres event publisher starts a LISTEN/NOTIFY goroutine that only stops when its context is cancelled. With `context.Background()`, this goroutine and its connection leak on shutdown. +- Refactored `initWebServer` to accept a `context.Context` parameter and pass the server's cancellable context to `newEventPublisher`. +- Note: the standalone hub path (non-web mode) at line 236 already used `ctx` correctly. + +### 3. DSN parsing for `file://` prefix (medium priority) +- `parseSQLiteSourceDSN` only had a `file:` prefix handler, so `file:///var/lib/hub.db` was trimmed to `///var/lib/hub.db` (triple slash). +- Added a `file://` case before the `file:` case, so `file:///abs` correctly resolves to `/abs`. +- Added three test cases covering `file://`, `file:///`, and `file:///...?query` forms. + +## Verification +- `go build ./cmd/...` passes +- `go vet ./cmd/...` passes +- All DSN parsing tests pass (including new cases) diff --git a/.gitignore b/.gitignore index 0f6ae3117..8602ae133 100644 --- a/.gitignore +++ b/.gitignore @@ -49,3 +49,9 @@ Thumbs.db # local tools .antigravitycli/ + +# Generated test fixtures (reproducible via `go run ./internal/fixturegen`, +# shared via the scratchpad mount — not committed as binary blobs). +testdata/hub-v46-fixture.db +testdata/hub-v46-fixture.db-wal +testdata/hub-v46-fixture.db-shm diff --git a/.scion/project-id b/.scion/project-id new file mode 100644 index 000000000..59df1969e --- /dev/null +++ b/.scion/project-id @@ -0,0 +1 @@ +c7c7775e-e3a0-43de-9d26-274688d467d0 diff --git a/cmd/common_envgather_test.go b/cmd/common_envgather_test.go index aa7c13920..6817a371a 100644 --- a/cmd/common_envgather_test.go +++ b/cmd/common_envgather_test.go @@ -49,7 +49,7 @@ func TestGatherAndSubmitEnv_NonInteractiveGathersFromLocalEnv(t *testing.T) { defer os.Unsetenv("TEST_SECRET_KEY") // Set up mock Hub server - projectID := "grove-1" + projectID := tid("grove-1") server, captured := newEnvGatherMockHubServer(t, projectID) defer server.Close() @@ -63,9 +63,9 @@ func TestGatherAndSubmitEnv_NonInteractiveGathersFromLocalEnv(t *testing.T) { } resp := &hubclient.CreateAgentResponse{ - Agent: &hubclient.Agent{ID: "agent-1"}, + Agent: &hubclient.Agent{ID: tid("agent-1")}, EnvGather: &hubclient.EnvGatherResponse{ - AgentID: "agent-1", + AgentID: tid("agent-1"), Required: []string{"TEST_SECRET_KEY"}, Needs: []string{"TEST_SECRET_KEY"}, }, @@ -103,7 +103,7 @@ func TestGatherAndSubmitEnv_NonInteractiveAllowsWhenAllSatisfied(t *testing.T) { }, } - result, err := gatherAndSubmitEnv(context.Background(), nil, "grove-1", resp) + result, err := gatherAndSubmitEnv(context.Background(), nil, tid("grove-1"), resp) require.NoError(t, err) // Should return the original response since no env was gathered assert.Equal(t, resp, result) @@ -137,7 +137,7 @@ func TestGatherAndSubmitEnv_NonInteractiveMultipleKeysMissing(t *testing.T) { }, } - _, err := gatherAndSubmitEnv(context.Background(), nil, "grove-1", resp) + _, err := gatherAndSubmitEnv(context.Background(), nil, tid("grove-1"), resp) require.Error(t, err) assert.Contains(t, err.Error(), "cannot satisfy required environment variables") assert.Contains(t, err.Error(), "KEY_A") @@ -231,7 +231,7 @@ func TestStartAgentViaHub_GlobalGroveSkipsWorkspaceBootstrap(t *testing.T) { outputFormat = "json" templateName = "" harnessConfigFlag = "codex" - runtimeBrokerID = "broker-1" + runtimeBrokerID = tid("broker-1") globalDir := t.TempDir() settingsPath := filepath.Join(globalDir, "settings.yaml") @@ -256,12 +256,12 @@ func TestStartAgentViaHub_GlobalGroveSkipsWorkspaceBootstrap(t *testing.T) { captured = &req json.NewEncoder(w).Encode(&hubclient.CreateAgentResponse{ Agent: &hubclient.Agent{ - ID: "agent-1", - Slug: "agent-1", - Name: "agent-1", + ID: tid("agent-1"), + Slug: tid("agent-1"), + Name: tid("agent-1"), Status: "running", Phase: "running", - RuntimeBrokerID: "broker-1", + RuntimeBrokerID: tid("broker-1"), RuntimeBrokerName: "scion", Created: time.Now().UTC(), }, @@ -312,7 +312,7 @@ func newEnvGatherMockHubServer(t *testing.T, projectID string) (*httptest.Server captured[k] = v } json.NewEncoder(w).Encode(map[string]interface{}{ - "agent": map[string]interface{}{"id": "agent-1", "status": "running"}, + "agent": map[string]interface{}{"id": tid("agent-1"), "status": "running"}, }) default: @@ -365,9 +365,9 @@ func TestGatherAndSubmitEnv_InteractiveSecretPrompt(t *testing.T) { } resp := &hubclient.CreateAgentResponse{ - Agent: &hubclient.Agent{ID: "agent-1"}, + Agent: &hubclient.Agent{ID: tid("agent-1")}, EnvGather: &hubclient.EnvGatherResponse{ - AgentID: "agent-1", + AgentID: tid("agent-1"), Required: []string{"MY_SECRET"}, Needs: []string{"MY_SECRET"}, SecretInfo: map[string]hubclient.SecretKeyInfo{ @@ -403,9 +403,9 @@ func TestGatherAndSubmitEnv_FileSecretShowsGuidance(t *testing.T) { outputFormat = "json" // suppress stderr resp := &hubclient.CreateAgentResponse{ - Agent: &hubclient.Agent{ID: "agent-1"}, + Agent: &hubclient.Agent{ID: tid("agent-1")}, EnvGather: &hubclient.EnvGatherResponse{ - AgentID: "agent-1", + AgentID: tid("agent-1"), Required: []string{"FILE_CERT"}, Needs: []string{"FILE_CERT"}, SecretInfo: map[string]hubclient.SecretKeyInfo{ @@ -418,7 +418,7 @@ func TestGatherAndSubmitEnv_FileSecretShowsGuidance(t *testing.T) { }, } - _, err := gatherAndSubmitEnv(context.Background(), nil, "grove-1", resp) + _, err := gatherAndSubmitEnv(context.Background(), nil, tid("grove-1"), resp) require.Error(t, err) assert.Contains(t, err.Error(), "FILE_CERT") } @@ -440,9 +440,9 @@ func TestGatherAndSubmitEnv_MixedSecretAndEnvKeys(t *testing.T) { // ENV_ONLY is not in SecretInfo → it's an env-only key that can't be prompted resp := &hubclient.CreateAgentResponse{ - Agent: &hubclient.Agent{ID: "agent-1"}, + Agent: &hubclient.Agent{ID: tid("agent-1")}, EnvGather: &hubclient.EnvGatherResponse{ - AgentID: "agent-1", + AgentID: tid("agent-1"), Required: []string{"ENV_ONLY", "SECRET_KEY"}, Needs: []string{"ENV_ONLY", "SECRET_KEY"}, SecretInfo: map[string]hubclient.SecretKeyInfo{ @@ -454,7 +454,7 @@ func TestGatherAndSubmitEnv_MixedSecretAndEnvKeys(t *testing.T) { }, } - _, err := gatherAndSubmitEnv(context.Background(), nil, "grove-1", resp) + _, err := gatherAndSubmitEnv(context.Background(), nil, tid("grove-1"), resp) require.Error(t, err) // Should fail because ENV_ONLY is not secret-eligible and not in local env assert.Contains(t, err.Error(), "ENV_ONLY") @@ -481,9 +481,9 @@ func TestGatherAndSubmitEnv_NonInteractiveSecretsMissing(t *testing.T) { os.Unsetenv("ENV_B") resp := &hubclient.CreateAgentResponse{ - Agent: &hubclient.Agent{ID: "agent-1"}, + Agent: &hubclient.Agent{ID: tid("agent-1")}, EnvGather: &hubclient.EnvGatherResponse{ - AgentID: "agent-1", + AgentID: tid("agent-1"), Required: []string{"SECRET_A", "ENV_B"}, Needs: []string{"SECRET_A", "ENV_B"}, SecretInfo: map[string]hubclient.SecretKeyInfo{ @@ -495,7 +495,7 @@ func TestGatherAndSubmitEnv_NonInteractiveSecretsMissing(t *testing.T) { }, } - _, err := gatherAndSubmitEnv(context.Background(), nil, "grove-1", resp) + _, err := gatherAndSubmitEnv(context.Background(), nil, tid("grove-1"), resp) require.Error(t, err) assert.Contains(t, err.Error(), "cannot satisfy required environment variables") } @@ -528,9 +528,9 @@ func TestGatherAndSubmitEnv_InteractiveSecretEmptyInput(t *testing.T) { } resp := &hubclient.CreateAgentResponse{ - Agent: &hubclient.Agent{ID: "agent-1"}, + Agent: &hubclient.Agent{ID: tid("agent-1")}, EnvGather: &hubclient.EnvGatherResponse{ - AgentID: "agent-1", + AgentID: tid("agent-1"), Required: []string{"MY_SECRET"}, Needs: []string{"MY_SECRET"}, SecretInfo: map[string]hubclient.SecretKeyInfo{ @@ -542,7 +542,7 @@ func TestGatherAndSubmitEnv_InteractiveSecretEmptyInput(t *testing.T) { }, } - _, err := gatherAndSubmitEnv(context.Background(), nil, "grove-1", resp) + _, err := gatherAndSubmitEnv(context.Background(), nil, tid("grove-1"), resp) require.Error(t, err) assert.Contains(t, err.Error(), "MY_SECRET") } diff --git a/cmd/hub_secret_migrate.go b/cmd/hub_secret_migrate.go index 20721d762..f2913504d 100644 --- a/cmd/hub_secret_migrate.go +++ b/cmd/hub_secret_migrate.go @@ -23,9 +23,10 @@ import ( "time" "github.com/GoogleCloudPlatform/scion/pkg/config" + "github.com/GoogleCloudPlatform/scion/pkg/ent/entc" "github.com/GoogleCloudPlatform/scion/pkg/secret" "github.com/GoogleCloudPlatform/scion/pkg/store" - "github.com/GoogleCloudPlatform/scion/pkg/store/sqlite" + "github.com/GoogleCloudPlatform/scion/pkg/store/entadapter" "github.com/spf13/cobra" ) @@ -89,11 +90,13 @@ func runSecretMigrate(cmd *cobra.Command, args []string) error { return fmt.Errorf("failed to load config: %w", err) } - // Open database - db, err := sqlite.New(cfg.Database.URL) + // Open database (single Ent-backed store) + entClient, err := entc.OpenSQLite("file:"+cfg.Database.URL+"?cache=shared", entc.PoolConfig{}) if err != nil { return fmt.Errorf("failed to open database: %w", err) } + db := entadapter.NewCompositeStore(entClient) + defer db.Close() if err := db.Migrate(ctx); err != nil { return fmt.Errorf("failed to migrate database: %w", err) } diff --git a/cmd/list_test.go b/cmd/list_test.go index c91693039..0c9a89aab 100644 --- a/cmd/list_test.go +++ b/cmd/list_test.go @@ -101,7 +101,7 @@ func TestFormatLastActivity(t *testing.T) { func TestDisplayAgentsLocalMode(t *testing.T) { agents := []api.AgentInfo{ { - Name: "agent-1", + Name: tid("agent-1"), Template: "default", HarnessConfig: "claude", Runtime: "docker", @@ -458,7 +458,7 @@ func TestHubAgentToAgentInfo_PhaseFromStatusFallback(t *testing.T) { func TestHubAgentToAgentInfo_HarnessConfigFromTopLevel(t *testing.T) { // When the Hub returns harnessConfig at the top level, use it directly a := hubclient.Agent{ - ID: "agent-1", + ID: tid("agent-1"), Name: "test-agent", HarnessConfig: "gemini", } diff --git a/cmd/message_test.go b/cmd/message_test.go index 6126a5f4e..a4b7245ce 100644 --- a/cmd/message_test.go +++ b/cmd/message_test.go @@ -197,7 +197,7 @@ func TestSendMessageViaHub_Broadcast(t *testing.T) { projectID := "grove-msg-broadcast" agents := []hubclient.Agent{ - {Name: "agent-1", Status: "running"}, + {Name: tid("agent-1"), Status: "running"}, {Name: "agent-2", Status: "running"}, {Name: "agent-3", Status: "running"}, } @@ -230,7 +230,7 @@ func TestSendMessageViaHub_Broadcast(t *testing.T) { require.NotNil(t, s.StructuredMsg) assert.True(t, s.StructuredMsg.Broadcasted) } - assert.ElementsMatch(t, []string{"agent-1", "agent-2", "agent-3"}, names) + assert.ElementsMatch(t, []string{tid("agent-1"), "agent-2", "agent-3"}, names) } func TestSendMessageViaHub_BroadcastNoAgents(t *testing.T) { diff --git a/cmd/server.go b/cmd/server.go index cb566ddc7..91a441913 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -29,6 +29,7 @@ var ( enableRuntimeBroker bool runtimeBrokerPort int dbURL string + noAutoMigrate bool enableDevAuth bool enableDebug bool storageBucket string @@ -238,6 +239,7 @@ func init() { serverStartCmd.Flags().IntVar(&hubPort, "port", 9810, "Hub API port (standalone mode only; ignored when --enable-web is set, use --web-port instead)") serverStartCmd.Flags().StringVar(&hubHost, "host", "0.0.0.0", "Hub API host to bind") serverStartCmd.Flags().StringVar(&dbURL, "db", "", "Database URL/path") + serverStartCmd.Flags().BoolVar(&noAutoMigrate, "no-auto-migrate", false, "Skip automatic in-process upgrade of a legacy raw-SQL hub.db to the Ent schema (operator opt-out)") // Runtime Broker API flags serverStartCmd.Flags().BoolVar(&enableRuntimeBroker, "enable-runtime-broker", false, "Enable the Runtime Broker API") diff --git a/cmd/server_dispatcher_test.go b/cmd/server_dispatcher_test.go index b563b9c15..c61e5fb75 100644 --- a/cmd/server_dispatcher_test.go +++ b/cmd/server_dispatcher_test.go @@ -79,40 +79,42 @@ func TestDispatchAgentStart(t *testing.T) { ctx := context.Background() s := newTestStore(t) mgr := &mockAgentManager{} - brokerID := "test-broker" + brokerID := tid("test-broker") adapter := newAgentDispatcherAdapter(mgr, s, brokerID) - // Create test project and broker - project := &store.Project{ - ID: "proj-1", - Slug: "test-project", + // Create test grove and broker + grove := &store.Project{ + ID: tid("grove-1"), + Slug: "test-grove", Name: "Test Project", } - err := s.CreateProject(ctx, project) + err := s.CreateProject(ctx, grove) require.NoError(t, err) broker := &store.RuntimeBroker{ ID: brokerID, Name: "test-broker", + Slug: "test-broker", } err = s.CreateRuntimeBroker(ctx, broker) require.NoError(t, err) provider := &store.ProjectProvider{ - ProjectID: project.ID, - BrokerID: brokerID, - LocalPath: "/tmp/fake/project", + ProjectID: grove.ID, + BrokerID: brokerID, + BrokerName: "test-broker", + LocalPath: "/tmp/fake/grove", } err = s.AddProjectProvider(ctx, provider) require.NoError(t, err) // Create agent agent := &store.Agent{ - ID: "agent-1", + ID: tid("agent-1"), Slug: "test-agent", Name: "test-agent", - ProjectID: project.ID, + ProjectID: grove.ID, Template: "gemini", Image: "test-image", Detached: true, @@ -133,7 +135,7 @@ func TestDispatchAgentStart(t *testing.T) { assert.Equal(t, "test-agent", mgr.startOpts.Name) assert.Equal(t, true, mgr.startOpts.Resume) assert.Equal(t, "new task", mgr.startOpts.Task) - assert.Equal(t, "/tmp/fake/project", mgr.startOpts.ProjectPath) + assert.Equal(t, "/tmp/fake/grove", mgr.startOpts.ProjectPath) assert.Equal(t, "gemini", mgr.startOpts.Template) assert.Equal(t, "BAR", mgr.startOpts.Env["FOO"]) @@ -149,24 +151,24 @@ func TestDispatchAgentRestart(t *testing.T) { ctx := context.Background() s := newTestStore(t) mgr := &mockAgentManager{} - brokerID := "test-broker" + brokerID := tid("test-broker") adapter := newAgentDispatcherAdapter(mgr, s, brokerID) - // Create test project and agent - project := &store.Project{ - ID: "proj-1", - Slug: "test-project", + // Create test grove and agent + grove := &store.Project{ + ID: tid("grove-1"), + Slug: "test-grove", Name: "Test Project", } - err := s.CreateProject(ctx, project) + err := s.CreateProject(ctx, grove) require.NoError(t, err) agent := &store.Agent{ - ID: "agent-1", + ID: tid("agent-1"), Slug: "test-agent", Name: "test-agent", - ProjectID: project.ID, + ProjectID: grove.ID, } err = s.CreateAgent(ctx, agent) require.NoError(t, err) diff --git a/cmd/server_foreground.go b/cmd/server_foreground.go index 307a2cb4e..7519261e9 100644 --- a/cmd/server_foreground.go +++ b/cmd/server_foreground.go @@ -36,10 +36,12 @@ import ( "github.com/GoogleCloudPlatform/scion/pkg/apiclient" "github.com/GoogleCloudPlatform/scion/pkg/brokercredentials" "github.com/GoogleCloudPlatform/scion/pkg/config" + "github.com/GoogleCloudPlatform/scion/pkg/ent" "github.com/GoogleCloudPlatform/scion/pkg/ent/entc" "github.com/GoogleCloudPlatform/scion/pkg/eventbus" "github.com/GoogleCloudPlatform/scion/pkg/harness" "github.com/GoogleCloudPlatform/scion/pkg/hub" + "github.com/GoogleCloudPlatform/scion/pkg/observability/dbmetrics" scionplugin "github.com/GoogleCloudPlatform/scion/pkg/plugin" "github.com/GoogleCloudPlatform/scion/pkg/runtime" "github.com/GoogleCloudPlatform/scion/pkg/runtimebroker" @@ -47,7 +49,6 @@ import ( "github.com/GoogleCloudPlatform/scion/pkg/storage" "github.com/GoogleCloudPlatform/scion/pkg/store" "github.com/GoogleCloudPlatform/scion/pkg/store/entadapter" - "github.com/GoogleCloudPlatform/scion/pkg/store/sqlite" "github.com/GoogleCloudPlatform/scion/pkg/util" "github.com/GoogleCloudPlatform/scion/pkg/util/logging" "github.com/spf13/cobra" @@ -167,7 +168,7 @@ func runServerStart(cmd *cobra.Command, args []string) error { // 8. Initialize store var s store.Store if enableHub { - s, err = initStore(cfg) + s, err = initStore(ctx, cfg) if err != nil { return err } @@ -231,7 +232,7 @@ func runServerStart(cmd *cobra.Command, args []string) error { if !enableWeb { // Hub runs its own HTTP server (standalone mode). - eventPub := hub.NewChannelEventPublisher() + eventPub := newEventPublisher(ctx, cfg) hubSrv.SetEventPublisher(eventPub) log.Printf("Starting Hub API server on %s:%d", cfg.Hub.Host, cfg.Hub.Port) @@ -259,7 +260,7 @@ func runServerStart(cmd *cobra.Command, args []string) error { // 12. Start Web var webSrv *hub.WebServer if enableWeb { - webSrv = initWebServer(cfg, hubSrv, devAuthToken, adminEmailList, adminMode, maintenanceMessage, requestLogger) + webSrv = initWebServer(ctx, cfg, hubSrv, devAuthToken, adminEmailList, adminMode, maintenanceMessage, requestLogger) // In combined mode, start Hub background services now that the // ChannelEventPublisher has been wired by initWebServer. @@ -641,49 +642,120 @@ func checkServerPorts(cfg *config.GlobalConfig) error { return nil } -// initStore initializes the database store. -func initStore(cfg *config.GlobalConfig) (store.Store, error) { +// initStore initializes the database store. The provided context is used for +// schema migration and the initial health-check ping so that a Ctrl+C during +// startup cancels those operations gracefully. +func initStore(ctx context.Context, cfg *config.GlobalConfig) (store.Store, error) { + connMaxLifetime, err := cfg.Database.ConnMaxLifetimeDuration() + if err != nil { + return nil, fmt.Errorf("invalid database pool config: %w", err) + } + connMaxIdleTime, err := cfg.Database.ConnMaxIdleTimeDuration() + if err != nil { + return nil, fmt.Errorf("invalid database pool config: %w", err) + } + + // The connection pool config is shared across backends. For SQLite, + // MaxOpenConns is forced to 1 by applyDatabasePoolDefaults to serialize + // writes; for Postgres it carries the larger pool sizing (default 10/5/30m + // lifetime, 5m idle) since Postgres handles concurrent connections natively. + pool := entc.PoolConfig{ + MaxOpenConns: cfg.Database.MaxOpenConns, + MaxIdleConns: cfg.Database.MaxIdleConns, + ConnMaxLifetime: connMaxLifetime, + ConnMaxIdleTime: connMaxIdleTime, + } + + var entClient *ent.Client switch cfg.Database.Driver { case "sqlite": - sqliteStore, err := sqlite.New(cfg.Database.URL) - if err != nil { - return nil, fmt.Errorf("failed to open database: %w", err) + // Migration α: upgrade a legacy raw-SQL hub.db (the former + // pkg/store/sqlite schema) to the consolidated Ent schema before opening + // it. Detection is conservative and the whole step is a no-op for an + // already-Ent file, so it is safe to run on every boot. + if err := maybeMigrateLegacySQLite(ctx, cfg.Database.URL); err != nil { + return nil, err } - if err := sqliteStore.Migrate(context.Background()); err != nil { - sqliteStore.Close() - return nil, fmt.Errorf("failed to run migrations: %w", err) + // All Hub state lives in a single Ent-backed SQLite database. + // Guard against a double "file:" prefix when the operator already + // supplies "file:/path/hub.db" in their config. + sqliteDSN := cfg.Database.URL + if !strings.HasPrefix(sqliteDSN, "file:") { + sqliteDSN = "file:" + sqliteDSN } - - entDSN := cfg.Database.URL + "_ent" - entClient, err := entc.OpenSQLite("file:" + entDSN + "?cache=shared") + if !strings.Contains(sqliteDSN, "?") { + sqliteDSN += "?cache=shared" + } else if !strings.Contains(sqliteDSN, "cache=") { + sqliteDSN += "&cache=shared" + } + entClient, err = entc.OpenSQLite(sqliteDSN, pool) if err != nil { - sqliteStore.Close() - return nil, fmt.Errorf("failed to open ent database: %w", err) + return nil, fmt.Errorf("failed to open database: %w", err) } - if err := entc.AutoMigrate(context.Background(), entClient); err != nil { - entClient.Close() - sqliteStore.Close() - return nil, fmt.Errorf("failed to run ent migrations: %w", err) + case "postgres": + // Postgres uses the pgx stdlib driver. The URL is a standard + // connection string (e.g. "postgres://user:pass@host:5432/db?sslmode=require"). + entClient, err = entc.OpenPostgres(cfg.Database.URL, pool) + if err != nil { + return nil, fmt.Errorf("failed to open database: %w", err) } + default: + return nil, fmt.Errorf("unsupported database driver: %s", cfg.Database.Driver) + } - if err := entc.MigrateGroveToProjectData(context.Background(), entDSN, sqliteStore); err != nil { - entClient.Close() - sqliteStore.Close() - return nil, fmt.Errorf("failed to migrate ent data: %w", err) - } + s := entadapter.NewCompositeStore(entClient) - s := entadapter.NewCompositeStore(sqliteStore, entClient) + // Migrate runs Ent's schema migration and seeds built-in maintenance + // operations (parity with the former raw-SQL store). + if err := s.Migrate(ctx); err != nil { + s.Close() + return nil, fmt.Errorf("failed to run migrations: %w", err) + } - if err := s.Ping(context.Background()); err != nil { - sqliteStore.Close() - return nil, fmt.Errorf("database ping failed: %w", err) - } + if err := s.Ping(ctx); err != nil { + s.Close() + return nil, fmt.Errorf("database ping failed: %w", err) + } - return s, nil - default: - return nil, fmt.Errorf("unsupported database driver: %s", cfg.Database.Driver) + return s, nil +} + +// maybeMigrateLegacySQLite detects a legacy raw-SQL hub.db at path and, unless +// the operator opted out with --no-auto-migrate, upgrades it in-process to the +// consolidated Ent schema (after taking an automatic backup). It is a no-op when +// the file is already the Ent schema, empty, or absent. The provided context +// allows the migration to be cancelled (e.g. Ctrl+C during first boot). +func maybeMigrateLegacySQLite(ctx context.Context, path string) error { + legacy, err := entc.IsLegacyRawSQLSchema(path) + if err != nil { + return fmt.Errorf("detecting database schema: %w", err) + } + if !legacy { + return nil + } + + if noAutoMigrate { + // The operator opted out, but the file is a legacy schema the Ent store + // cannot open. Fail loudly with guidance rather than crash later. + return fmt.Errorf("detected a legacy raw-SQL hub database at %s but --no-auto-migrate is set; "+ + "remove the flag to upgrade it in place (a backup is taken automatically), "+ + "or point --db at an already-migrated database", path) + } + + log.Printf("Detected legacy raw-SQL hub database at %s. Backing up and migrating to the Ent schema...", path) + report, err := entc.MigrateAlphaSQLite(ctx, path, entc.AlphaOptions{ + Logf: func(format string, args ...any) { log.Printf(format, args...) }, + }) + if err != nil { + return fmt.Errorf("migrating legacy database (original left untouched): %w", err) } + if report.Skipped { + return nil + } + log.Printf("Migration α complete: %d tables, %d rows migrated. Backup: %s", + len(report.Tables), report.TotalRows(), report.BackupPath) + return nil } // initDevAuth initializes dev authentication and returns the token. @@ -990,8 +1062,31 @@ func initHubStorage(ctx context.Context, hubSrv *hub.Server, cfg *config.GlobalC } } -// initWebServer creates and configures the Web server. -func initWebServer(cfg *config.GlobalConfig, hubSrv *hub.Server, devAuthToken string, adminEmailList []string, adminMode bool, maintenanceMessage string, requestLogger *slog.Logger) *hub.WebServer { +// newEventPublisher selects the event publisher backend based on the configured +// database driver. With Postgres it returns a PostgresEventPublisher +// (cross-replica LISTEN/NOTIFY); otherwise it returns the in-process +// ChannelEventPublisher. If the Postgres publisher cannot be started it falls +// back to the in-process publisher so a single instance still functions, logging +// a prominent warning since cross-replica SSE delivery will be unavailable. +func newEventPublisher(ctx context.Context, cfg *config.GlobalConfig) hub.EventPublisher { + if strings.EqualFold(cfg.Database.Driver, "postgres") { + // Metrics export is wired separately (see pkg/observability/dbmetrics); + // use a disabled recorder until a MeterProvider is configured. + pub, err := hub.NewPostgresEventPublisher(ctx, cfg.Database.URL, dbmetrics.NewDisabled(), logging.Subsystem("hub.events")) + if err != nil { + log.Printf("WARNING: failed to start Postgres event publisher (%v); falling back to in-process events. Cross-replica SSE will not work.", err) + return hub.NewChannelEventPublisher() + } + log.Printf("Using Postgres LISTEN/NOTIFY event publisher") + return pub + } + return hub.NewChannelEventPublisher() +} + +// initWebServer creates and configures the Web server. The provided context is +// threaded to the event publisher so that the Postgres LISTEN/NOTIFY goroutine +// is cancelled cleanly on shutdown, preventing connection leaks. +func initWebServer(ctx context.Context, cfg *config.GlobalConfig, hubSrv *hub.Server, devAuthToken string, adminEmailList []string, adminMode bool, maintenanceMessage string, requestLogger *slog.Logger) *hub.WebServer { webHost := cfg.Hub.Host if webHost == "" { webHost = "0.0.0.0" @@ -1045,7 +1140,7 @@ func initWebServer(cfg *config.GlobalConfig, hubSrv *hub.Server, devAuthToken st webSrv.SetRequestLogger(requestLogger) // Create shared event publisher for real-time SSE - eventPub := hub.NewChannelEventPublisher() + eventPub := newEventPublisher(ctx, cfg) webSrv.SetEventPublisher(eventPub) // Wire Hub services into WebServer if Hub is enabled diff --git a/cmd/server_migrate.go b/cmd/server_migrate.go new file mode 100644 index 000000000..5811a4bf3 --- /dev/null +++ b/cmd/server_migrate.go @@ -0,0 +1,212 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cmd + +import ( + "fmt" + "os" + "strings" + + "github.com/GoogleCloudPlatform/scion/pkg/ent/entc" + "github.com/spf13/cobra" +) + +var ( + migrateFrom string + migrateTo string + migrateDropSource bool + migrateKeepSource bool + migrateBatchSize int +) + +// serverMigrateCmd implements Migration β from the PostgreSQL strategy: an +// entity-by-entity copy from an Ent-on-SQLite database to an Ent-on-Postgres +// database. Both endpoints share the same Ent schema, so the copy is a plain +// dependency-ordered transfer through the Ent client. +var serverMigrateCmd = &cobra.Command{ + Use: "migrate", + Short: "Migrate Hub data from SQLite to PostgreSQL", + Long: `Copy all Hub state from an Ent-backed SQLite database into an +Ent-backed PostgreSQL database. + +The migration is: + - Dependency-ordered: parents are inserted before children so every foreign + key resolves at insert time. + - Idempotent: rows whose primary key already exists in the destination are + skipped, so a failed run can be safely restarted. + - Read-only on the source: the SQLite file is opened with PRAGMA query_only, + so the running SQLite hub can stay up until you cut over. + - Verified: source and destination row counts are compared after every + entity, aborting on any mismatch. + +By default the source SQLite file is left untouched (--keep-source). Pass +--drop-source for an explicit cutover that deletes the SQLite file after a +successful, verified migration. + +Examples: + # Dry-safe copy; leaves SQLite in place. + scion server migrate \ + --from sqlite:///var/lib/scion/hub.db \ + --to "postgres://scion:secret@db.example.com:5432/scion?sslmode=require" + + # Explicit cutover: delete the SQLite file once migration succeeds. + scion server migrate \ + --from sqlite:///var/lib/scion/hub.db \ + --to "postgres://scion:secret@db:5432/scion?sslmode=require" \ + --drop-source`, + RunE: runServerMigrate, +} + +func init() { + serverCmd.AddCommand(serverMigrateCmd) + + serverMigrateCmd.Flags().StringVar(&migrateFrom, "from", "", "Source SQLite DSN (e.g. sqlite:///path/to/hub.db) [required]") + serverMigrateCmd.Flags().StringVar(&migrateTo, "to", "", "Destination PostgreSQL DSN (e.g. postgres://user:pass@host:5432/db?sslmode=require) [required]") + serverMigrateCmd.Flags().BoolVar(&migrateKeepSource, "keep-source", true, "Leave the source SQLite file untouched (default)") + serverMigrateCmd.Flags().BoolVar(&migrateDropSource, "drop-source", false, "Delete the source SQLite file after a successful migration (explicit cutover)") + serverMigrateCmd.Flags().IntVar(&migrateBatchSize, "batch-size", 0, "Max rows per bulk insert statement (0 = default)") + + _ = serverMigrateCmd.MarkFlagRequired("from") + _ = serverMigrateCmd.MarkFlagRequired("to") +} + +func runServerMigrate(cmd *cobra.Command, _ []string) error { + ctx := cmd.Context() + out := cmd.OutOrStdout() + + srcDSN, srcPath, err := parseSQLiteSourceDSN(migrateFrom) + if err != nil { + return err + } + dstDSN, err := parsePostgresDestDSN(migrateTo) + if err != nil { + return err + } + + fmt.Fprintf(out, "Opening source (read-only): %s\n", srcPath) + src, err := entc.OpenSQLiteReadOnly(srcDSN) + if err != nil { + return fmt.Errorf("opening source sqlite: %w", err) + } + defer src.Close() + + fmt.Fprintln(out, "Opening destination PostgreSQL") + dst, err := entc.OpenPostgres(dstDSN, entc.PoolConfig{MaxOpenConns: 10, MaxIdleConns: 5}) + if err != nil { + return fmt.Errorf("opening destination postgres: %w", err) + } + defer dst.Close() + + fmt.Fprintln(out, "Ensuring destination schema (auto-migrate)") + if err := entc.AutoMigrate(ctx, dst); err != nil { + return fmt.Errorf("destination auto-migrate: %w", err) + } + + fmt.Fprintln(out, "Migrating entities...") + report, err := entc.MigrateData(ctx, src, dst, entc.MigrateOptions{ + BatchSize: migrateBatchSize, + Logf: func(format string, args ...any) { + fmt.Fprintf(out, " "+format+"\n", args...) + }, + }) + if err != nil { + return fmt.Errorf("migration failed: %w", err) + } + + total := 0 + for _, e := range report.Entities { + total += e.Dest + } + fmt.Fprintf(out, "Migration complete: %d entities, %d rows total, %d child-group edges\n", + len(report.Entities), total, report.ChildGroupEdgs) + + if migrateDropSource { + fmt.Fprintf(out, "Dropping source SQLite file: %s\n", srcPath) + if err := dropSQLiteFile(srcPath); err != nil { + return fmt.Errorf("dropping source: %w", err) + } + fmt.Fprintln(out, "Source dropped.") + } else { + fmt.Fprintf(out, "Source left in place: %s\n", srcPath) + } + + return nil +} + +// parseSQLiteSourceDSN normalizes the --from value into a modernc.org/sqlite DSN +// and returns the bare filesystem path (for --drop-source). It accepts: +// +// sqlite:///abs/path/hub.db -> /abs/path/hub.db +// sqlite://rel/path/hub.db -> rel/path/hub.db +// file:/abs/path/hub.db -> passed through, path extracted +// /abs/path/hub.db -> bare path +func parseSQLiteSourceDSN(raw string) (dsn, path string, err error) { + if raw == "" { + return "", "", fmt.Errorf("--from is required") + } + switch { + case strings.HasPrefix(raw, "sqlite://"): + path = strings.TrimPrefix(raw, "sqlite://") + // sqlite:///abs -> "/abs"; an extra leading slash denotes an absolute path. + case strings.HasPrefix(raw, "sqlite:"): + path = strings.TrimPrefix(raw, "sqlite:") + case strings.HasPrefix(raw, "file://"): + path = strings.TrimPrefix(raw, "file://") + // file:///abs -> "/abs"; the third slash begins the absolute path. + if i := strings.IndexByte(path, '?'); i >= 0 { + path = path[:i] + } + case strings.HasPrefix(raw, "file:"): + path = strings.TrimPrefix(raw, "file:") + // Strip any query parameters from the extracted path. + if i := strings.IndexByte(path, '?'); i >= 0 { + path = path[:i] + } + default: + path = raw + } + if path == "" { + return "", "", fmt.Errorf("could not determine sqlite file path from %q", raw) + } + // cache=shared matches how the hub opens its SQLite database elsewhere. + dsn = "file:" + path + "?cache=shared" + return dsn, path, nil +} + +// parsePostgresDestDSN validates the --to value and returns a DSN the pgx +// stdlib driver accepts. Both URL-style ("postgres://...") and keyword/value +// ("host=... port=...") DSNs are passed through unchanged. +func parsePostgresDestDSN(raw string) (string, error) { + if raw == "" { + return "", fmt.Errorf("--to is required") + } + if strings.HasPrefix(raw, "postgres://") || + strings.HasPrefix(raw, "postgresql://") || + strings.Contains(raw, "host=") { + return raw, nil + } + return "", fmt.Errorf("--to must be a PostgreSQL DSN (postgres://... or host=...), got %q", raw) +} + +// dropSQLiteFile removes the SQLite database file and any WAL/SHM/journal +// sidecar files left next to it. +func dropSQLiteFile(path string) error { + for _, suffix := range []string{"", "-wal", "-shm", "-journal"} { + if err := os.Remove(path + suffix); err != nil && !os.IsNotExist(err) { + return err + } + } + return nil +} diff --git a/cmd/server_migrate_test.go b/cmd/server_migrate_test.go new file mode 100644 index 000000000..f94c8eb79 --- /dev/null +++ b/cmd/server_migrate_test.go @@ -0,0 +1,141 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cmd + +import "testing" + +func TestParseSQLiteSourceDSN(t *testing.T) { + tests := []struct { + name string + in string + wantDSN string + wantPath string + wantErr bool + }{ + { + name: "absolute sqlite url", + in: "sqlite:///var/lib/scion/hub.db", + wantDSN: "file:/var/lib/scion/hub.db?cache=shared", + wantPath: "/var/lib/scion/hub.db", + }, + { + name: "relative sqlite url", + in: "sqlite://data/hub.db", + wantDSN: "file:data/hub.db?cache=shared", + wantPath: "data/hub.db", + }, + { + name: "sqlite single-colon form", + in: "sqlite:/tmp/hub.db", + wantDSN: "file:/tmp/hub.db?cache=shared", + wantPath: "/tmp/hub.db", + }, + { + name: "file triple-slash absolute url", + in: "file:///var/lib/scion/hub.db", + wantDSN: "file:/var/lib/scion/hub.db?cache=shared", + wantPath: "/var/lib/scion/hub.db", + }, + { + name: "file double-slash relative url", + in: "file://data/hub.db", + wantDSN: "file:data/hub.db?cache=shared", + wantPath: "data/hub.db", + }, + { + name: "file triple-slash with query", + in: "file:///tmp/hub.db?mode=ro", + wantDSN: "file:/tmp/hub.db?cache=shared", + wantPath: "/tmp/hub.db", + }, + { + name: "file url with query", + in: "file:/tmp/hub.db?cache=shared", + wantDSN: "file:/tmp/hub.db?cache=shared", + wantPath: "/tmp/hub.db", + }, + { + name: "bare path", + in: "/tmp/hub.db", + wantDSN: "file:/tmp/hub.db?cache=shared", + wantPath: "/tmp/hub.db", + }, + { + name: "empty", + in: "", + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + dsn, path, err := parseSQLiteSourceDSN(tt.in) + if tt.wantErr { + if err == nil { + t.Fatalf("expected error, got dsn=%q path=%q", dsn, path) + } + return + } + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if dsn != tt.wantDSN { + t.Errorf("dsn = %q, want %q", dsn, tt.wantDSN) + } + if path != tt.wantPath { + t.Errorf("path = %q, want %q", path, tt.wantPath) + } + }) + } +} + +func TestParsePostgresDestDSN(t *testing.T) { + tests := []struct { + name string + in string + wantErr bool + }{ + {name: "url form", in: "postgres://u:p@host:5432/db?sslmode=require"}, + {name: "postgresql scheme", in: "postgresql://u:p@host:5432/db"}, + {name: "keyword form", in: "host=h port=5432 user=u password=p dbname=db sslmode=require"}, + {name: "empty", in: "", wantErr: true}, + {name: "not postgres", in: "sqlite:///tmp/hub.db", wantErr: true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := parsePostgresDestDSN(tt.in) + if tt.wantErr { + if err == nil { + t.Fatalf("expected error, got %q", got) + } + return + } + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if got != tt.in { + t.Errorf("dsn = %q, want unchanged %q", got, tt.in) + } + }) + } +} + +func TestServerMigrateCmdRegistered(t *testing.T) { + for _, c := range serverCmd.Commands() { + if c.Name() == "migrate" { + return + } + } + t.Fatal("migrate subcommand not registered under server") +} diff --git a/cmd/server_test.go b/cmd/server_test.go index 5df3e2f7d..cd74fe60d 100644 --- a/cmd/server_test.go +++ b/cmd/server_test.go @@ -21,17 +21,19 @@ import ( "testing" "github.com/GoogleCloudPlatform/scion/pkg/config" + "github.com/GoogleCloudPlatform/scion/pkg/ent/entc" "github.com/GoogleCloudPlatform/scion/pkg/store" - "github.com/GoogleCloudPlatform/scion/pkg/store/sqlite" + "github.com/GoogleCloudPlatform/scion/pkg/store/entadapter" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func newTestStore(t *testing.T) store.Store { t.Helper() - s, err := sqlite.New(":memory:") + client, err := entc.OpenSQLite("file:"+t.Name()+"?mode=memory&cache=shared", entc.PoolConfig{}) require.NoError(t, err) - require.NoError(t, s.Migrate(context.Background())) + require.NoError(t, entc.AutoMigrate(context.Background(), client)) + s := entadapter.NewCompositeStore(client) t.Cleanup(func() { s.Close() }) return s } @@ -41,31 +43,31 @@ func TestRegisterGlobalGroveAndBroker_DedupByName(t *testing.T) { s := newTestStore(t) settings := &config.Settings{} - // First registration: creates broker with ID "broker-1" and name "test-broker" - effectiveID, err := registerGlobalProjectAndBroker(ctx, s, "broker-1", "test-broker", "http://localhost:9800", nil, true, settings) + // First registration: creates broker with ID tid("broker-1") and name "test-broker" + effectiveID, err := registerGlobalProjectAndBroker(ctx, s, tid("broker-1"), "test-broker", "http://localhost:9800", nil, true, settings) require.NoError(t, err) - assert.Equal(t, "broker-1", effectiveID) + assert.Equal(t, tid("broker-1"), effectiveID) // Verify broker was created - broker, err := s.GetRuntimeBroker(ctx, "broker-1") + broker, err := s.GetRuntimeBroker(ctx, tid("broker-1")) require.NoError(t, err) assert.Equal(t, "test-broker", broker.Name) assert.Equal(t, store.BrokerStatusOnline, broker.Status) // Second registration with a DIFFERENT ID but SAME name. // This simulates a restart where the broker ID was lost/regenerated. - effectiveID, err = registerGlobalProjectAndBroker(ctx, s, "broker-2", "test-broker", "http://localhost:9800", nil, true, settings) + effectiveID, err = registerGlobalProjectAndBroker(ctx, s, tid("broker-2"), "test-broker", "http://localhost:9800", nil, true, settings) require.NoError(t, err) // Should return the original broker-1 ID (dedup by name) - assert.Equal(t, "broker-1", effectiveID, "should reuse existing broker ID found by name") + assert.Equal(t, tid("broker-1"), effectiveID, "should reuse existing broker ID found by name") // Verify no duplicate was created - _, err = s.GetRuntimeBroker(ctx, "broker-2") + _, err = s.GetRuntimeBroker(ctx, tid("broker-2")) assert.ErrorIs(t, err, store.ErrNotFound, "broker-2 should NOT exist in the database") // Verify original broker was updated - broker, err = s.GetRuntimeBroker(ctx, "broker-1") + broker, err = s.GetRuntimeBroker(ctx, tid("broker-1")) require.NoError(t, err) assert.Equal(t, "test-broker", broker.Name) assert.Equal(t, store.BrokerStatusOnline, broker.Status) @@ -77,17 +79,17 @@ func TestRegisterGlobalGroveAndBroker_SameIDNoDedup(t *testing.T) { settings := &config.Settings{} // First registration - effectiveID, err := registerGlobalProjectAndBroker(ctx, s, "broker-1", "test-broker", "http://localhost:9800", nil, true, settings) + effectiveID, err := registerGlobalProjectAndBroker(ctx, s, tid("broker-1"), "test-broker", "http://localhost:9800", nil, true, settings) require.NoError(t, err) - assert.Equal(t, "broker-1", effectiveID) + assert.Equal(t, tid("broker-1"), effectiveID) // Second registration with the same ID (normal restart case) - effectiveID, err = registerGlobalProjectAndBroker(ctx, s, "broker-1", "test-broker", "http://localhost:9800", nil, false, settings) + effectiveID, err = registerGlobalProjectAndBroker(ctx, s, tid("broker-1"), "test-broker", "http://localhost:9800", nil, false, settings) require.NoError(t, err) - assert.Equal(t, "broker-1", effectiveID) + assert.Equal(t, tid("broker-1"), effectiveID) // Verify broker was updated (not duplicated) - broker, err := s.GetRuntimeBroker(ctx, "broker-1") + broker, err := s.GetRuntimeBroker(ctx, tid("broker-1")) require.NoError(t, err) assert.Equal(t, "test-broker", broker.Name) assert.Equal(t, false, broker.AutoProvide, "auto-provide should be updated to false") @@ -99,19 +101,19 @@ func TestRegisterGlobalGroveAndBroker_NewBrokerNewName(t *testing.T) { settings := &config.Settings{} // Register first broker - effectiveID, err := registerGlobalProjectAndBroker(ctx, s, "broker-1", "broker-alpha", "http://localhost:9800", nil, true, settings) + effectiveID, err := registerGlobalProjectAndBroker(ctx, s, tid("broker-1"), "broker-alpha", "http://localhost:9800", nil, true, settings) require.NoError(t, err) - assert.Equal(t, "broker-1", effectiveID) + assert.Equal(t, tid("broker-1"), effectiveID) // Register a genuinely different broker (different ID AND different name) - effectiveID, err = registerGlobalProjectAndBroker(ctx, s, "broker-2", "broker-beta", "http://localhost:9801", nil, true, settings) + effectiveID, err = registerGlobalProjectAndBroker(ctx, s, tid("broker-2"), "broker-beta", "http://localhost:9801", nil, true, settings) require.NoError(t, err) - assert.Equal(t, "broker-2", effectiveID) + assert.Equal(t, tid("broker-2"), effectiveID) // Both brokers should exist - _, err = s.GetRuntimeBroker(ctx, "broker-1") + _, err = s.GetRuntimeBroker(ctx, tid("broker-1")) assert.NoError(t, err) - _, err = s.GetRuntimeBroker(ctx, "broker-2") + _, err = s.GetRuntimeBroker(ctx, tid("broker-2")) assert.NoError(t, err) } @@ -121,13 +123,13 @@ func TestRegisterGlobalGroveAndBroker_DedupCaseInsensitive(t *testing.T) { settings := &config.Settings{} // Register broker with lowercase name - effectiveID, err := registerGlobalProjectAndBroker(ctx, s, "broker-1", "scion-demo", "http://localhost:9800", nil, true, settings) + effectiveID, err := registerGlobalProjectAndBroker(ctx, s, tid("broker-1"), "scion-demo", "http://localhost:9800", nil, true, settings) require.NoError(t, err) - assert.Equal(t, "broker-1", effectiveID) + assert.Equal(t, tid("broker-1"), effectiveID) // Register with different ID and mixed-case name // GetRuntimeBrokerByName uses LOWER() for case-insensitive match - effectiveID, err = registerGlobalProjectAndBroker(ctx, s, "broker-2", "Scion-Demo", "http://localhost:9800", nil, true, settings) + effectiveID, err = registerGlobalProjectAndBroker(ctx, s, tid("broker-2"), "Scion-Demo", "http://localhost:9800", nil, true, settings) require.NoError(t, err) - assert.Equal(t, "broker-1", effectiveID, "should match case-insensitively") + assert.Equal(t, tid("broker-1"), effectiveID, "should match case-insensitively") } diff --git a/cmd/tid_test.go b/cmd/tid_test.go new file mode 100644 index 000000000..f97f4b6d8 --- /dev/null +++ b/cmd/tid_test.go @@ -0,0 +1,23 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cmd + +import "github.com/google/uuid" + +// tid deterministically maps a human-readable test identifier to a stable UUID +// string, so fixtures stay readable while satisfying the UUID-PK Ent store. +func tid(name string) string { + return uuid.NewSHA1(uuid.NameSpaceOID, []byte(name)).String() +} diff --git a/downloads/tg_1780415922_photo_AQADQgxrG_Tb-UR-.jpg b/downloads/tg_1780415922_photo_AQADQgxrG_Tb-UR-.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a218474afa9c564d6bdfeae83780a8f9d8be6ed0 GIT binary patch literal 160057 zcmb4qXIK+W)b=I>1O%ja6c9r1Jrt4N5s+R)dRGWVKs5A@l+Z&79Rx(GR2AtZlu)E9 zHFT6>!Sa0T`~G-;f14}0_S&7yoO9pj+%r2n^WVq+J_FQxTDn>Q1OfmM@B{p}0;mII zB-coX<;6?1mCS&BFZ8XGI9t6 z;P#!F5O59YvN0>_WgT;^1}ndj%G}_fy#}zSZCdF6C*w$%Z_y9?0yOww$gQgtsF`3EmEs zGV8xSw$KJ$Rf;a>AL8ED*1E~u@ieX34s*~hh_5`)R~9TT?s*zvKX-1utk6XmuHa3b zEc0=s6n6wD0JKXt#)xeaQ`lCyc5&8)u?05De5BuZnh8|s^mi$hKoyk03&AY}R{Pgu}y zs-bYNLA89-&@R3Zz{W_3J43{&rPLaFau!TR8>=OEtdjgP8gdka!Z|Fx*d{YO6h#FC z^aDz*L+^L-&s<+aQcQBUc}iEKxAd%yWNzx-jLLB=pq&i3B447KQa9$e_oEMM^IMm` zd}KB5`>ngc+ajFMmtnoBukb~-z;JX>_^czJclU_S%s+q)UTDf)=VH-0lCtZ#>t3NI zrJmU6qV0rEtdJ3cC1%#N3+ZU$JUO>(;gNvWT^4|}iWi!xL175tw-u~HL9mnnUbO&# z4RG=sP=7Gyv*mkA%yYbjemhua-y4f>>LtY|}6z(sU~;!2EyALw=O%#M|COk>K&XEpm1lI_sBn#uc9iXKUllASy< zhIBe6omr;bde`ZNfM5+$$NO4j;?!LDLI{?R*_S>Mb{|a^1wbfNIV)z_RNdvYl9A=sJ*QWJf_a< z4I1%Yf2EDj-@7sB!ZkRg*fv$(ozYY4EsuzvpsuB%Am=#hnj z#WBx`Yp~Iu7FYj;z?~GWV~>DILO;@C&x)^VO4PgJxWr45fXLxOF6Ij*#&r5c2z?=} z6ee|f3Qb%SuDn*9xvRFTlhahy3n z{0&~OkZ)}R6~A^41&yj5yKas5C>%s|4O=N(#%QZdsn|Q0YwPVQUALskrSf0wH9E`= zxnn8U`Xpdc&}4pU*xsP#AK zb?)thS4unBFSGfxfe7LY*_^%DCmD>PG1Wy#B%e_L+JneiPpgG--N%Jx89dcYj0+r? z=DBan%{j1gAEQ%Y*Ur1oT!;PP{T^iRL!8K zJ*EN$(~V;G+VR>sjU0Z<{-{)8nnf2iKxAN;6h}H^Vd4Vy5G3R7zc~o`3GfU_^XY=JmpI)JCwnSlCfj;JNwQ;_FXX!~b#|kdGtFQCx!U=;} zWvTmx3>D*LoWSFfzJ zOmw~;jyd38Nt%7GZ~IV?$SuT6%Zk>frY{>x=O5$LC6fRs?HcSFF}JQWeC>PZUJgws zA|V2ZS=|oDY$lJ2i<~;1@pOfDyv$zqu4Pt!*ljMC|9w|XZY8BvY`!H~`@xF{BaZ!* zg0YRYv-y4GVWU!6x0N<3r<~cFSV-cLv0R(zN5b;A=XZ=^_=asugwPkMN#FbvmQy{) zdIzaIJlxlC^7cu4`VMif&~Lqy{xr#b8?S*M-i=Ne zq-{i%f?gla&1>f&r@zfqKy>r|qEx`wo2>KYS;5CQF&$N_RVIRVp({DT6~io`Du3wJ zx3udbupK|UfAexBI*nY!J%N@`&22gTp+sNQk7+{EZo5l!Yj#7_o-^T-A|}lJC2gUJ zyOBT(!ULYZQ$N_2o=9#ga_{M;S|$zJa|&JO!kwz+g6^byT5SX&mx=`j03a)u6WyuT zFU*eU=j1jOp-2eS6OfzW(O`}?mNJ&BzGk$>?zKXg4P^C#6EPTdDVEIq}+M$O--lo!x#<@1R*A3M(JKa^34P4Lnop0QUa2V=b zIQc#4gs&pL&59{X)9BK}cXL(Y;`NBzp=!i)SJqG2gfo?6CL+)Js{7#CA}X;Ty2 zC-I&g`B76klYkHjVyIuH|1~C;Rfgc0YWHgj;|{Xr?vkrM^{Zt)Xt!=;W=kQdr9i9y zLEZIgwX)Z<0wUzqjI>omgjsGL9;SQKlX%>IKm99{LDOU&xHt)y3y&^$VRA+Sk7kPM z4$R1INp1wu=vqYeP{BWF*?jrCo#~SKs81J8IJ?+4)?PFJKFgarCb{g}A!^pAVbeLz z-g?&FBFtCF62fZzyz>ox8q_aPQsS*KX%6 z%TINX%Ig-43khKOIquRR63{YRSX8go(})<)LNrR|R)GBj zGKM=K(OnZ_gz3KjaK@VSd(z!+MQzuGYwWfygMYF!V`();aGe~8%77`so$FZ5kN)?Y z(j+>Wt9Hw7)awj4H}dI>Lq_mZ(^&i@F7-fR8b&FpVRac0!IJ9Sn7=ds=gGMR;=r<4 z<`=UY;daimT|4tzNKAwqjo!_29D8oXu-+r5Huqi*4yFCDRFt27TwtdNQB~`k!6J%P zv#J=^#xL&qdygAK$Nq3EY`4AX>hK@ z`WDuswqcBSeWyI;NA`NpPi<>UcWeKo<wKrg=4s8B)va-3$?c1q&!6H}##4I(TcqJi za)Q)udm=wK*7?czhTPtHNW(;`9#hpiJdG&)jqQ`)p*FAG(L4Q@I;VF2Azwo9jeljk z&Hc`fi!r}lTXhDBcNDD7 z7(_ZkQNHi}K-1X4w7E#E+LnM~eMMueOc29$KO4c*c~5rERPV$yGO?>)_SMoH^Tr`j{gq1Qc!^)Iep@I$1&)mO7?!FPgFYN- z8B%De9kn{VKQ5u5HO#+OTUpXGkA0S=TdRj+GeDeO(8NFS+G zU=%VR$tSzU>x_r_!CN(~#<{=dUu-CQw|u^Gc)eZaqjF8ibzX3}&d(-WfOp?SA+W1f z#BD>T`S&?`ZO_bha@cU!fnU)NVQhLerEsuW;fS3ibe#RjJN6? z>rJG5_p)x|^|iKO`fQO}&6@3NF;}T|$sVVm>PQ;?5%F3-TuR+2_}p)Isllubr?u-A z8O`5<4=qZ|vV7RzSMZX1j_|zrks-k$9$_=zDZKi~Yo2(js+!`kspoCQd2Uj|H(zCo z8;YvP*G5tRYnn-f@TyjVk$eV`0-M0=lPHm-OhURJ+3ou@PS>)0CkOQqQ49l>Eba!3 ztyu=O0&YoK6%bU8x6XpCMh!2PWtg76-a}8nNGqif>fL_l)1_z9q~?l~DqwD_B!k1&HL~V`(nk!G(8T%? zBQKt0?kcwd#%4W9t6Bjqs3Fg)P1O?poSt&0sh64&k6V{D)U;8*o-`$gh-{GCKLJpM z(E4*rb728spbH5&!DK6Vh-j7hM4R?8T?;y}GYu$cXK3@-q4N#-Rh}SAZ7G#L$ z6hyI;1`47zy9S&(q=wXR>H-020BUNo|8SNAnK51=6=qywggD%93X~5!a$Oc;s9qdC z@cvRhZ)q!JJk_Wy*TXVO69YZ(AIaedn%yip(A|OV(B?#53R` ze>ePfuUH-dJCAX7+i_{2@-|hp&TlK6mdeGBcGlP)`XM`?iEn*EiCk5g&YU>k(Knqi z-#_;mlMSfqq(l4raTdsxExu&_>MfV+b$XpQq;PeUW9@PCA#v^f>#ysW>bGN?OYMtb zwn4Br`^59+`j4wByUb`;YnAo0utI^(I)vHX>SF6$MK@MLKp^i-`6n(yu-{gr_bgVZ zP&44vve?BfMmni#Aam*xt|NZN*clAmludOnEg>||krP_Bw zUh8b`IOThZajHp2ccHGqmGG4$q3`?Ga+?mNqmC&j0>&@rQt4u;qc;Agz3fjX6-?08 z=F7Jxgu}7PXK&JX@OX>bi=F+xPAli%%}4{Y$(5JhnL& z__^9{;S!YkfvWLj(MM(HkFl~hV|L1~(ey$EW=>nXYA%;f=JPsck1WxUxo zZmAP?vy9H!u`W?#i!xGN(lfEkRmK|Q*91i_r7E9cCo+VYbO!E^=jo37E9eDocWW2_ zXsjem99$NQ26QC7rZ6(DI4o#7wMp-w(5sC4`bvdh?{8sd&QMIwhdNT8x{4=D? z2{AaItZfb|Qy!#Cnyw-{^nJgOW1}T9Dw+6k^W#Lv`WNOb+O0#G=zzoV4%Rl zK*)tTS7S`=e=Dw*i2fMwF#SZi(rc@aC=V&IvnwM1-u6PMv#@sdq4bodcJU`=CryXd zy{r1w;=Xt$#lhpLcK;TWrNi+TisoGhvhW|@%H6S3($Zm;t2n%(DDN*j1%g;vuFoPG z;oZ0!v6b8&F`+Ou^LgJXaQS+O>_IP|QXztjrFsSbS=BS`VZ<1YNx?s(9CoRdyS)*=;*AGx0Un;Z;Zx zT!v_j0HcSXWI?lq)Jy-myhTF3Xg+@7YuBOUi(kijoWa+Ugjy5X5=sp%#<%*u^VB+FlW_cxC2QlQ6nrJ6xE* z!Pfm;@#z-^?qe4zm!);lGj`uGuZ#!^d&oN157speo!&y}xVo19^zi{gT=S4fRY^$&Tv6AE^tYZ}IhH9-^ zMyF&yhbe6M?(BCt6YYdNnf+wI#t>+kbu4N#a=71&_&QLcQY6!6G-YJ3VAlIVfp;N- zJ}l&EM`3u6%&?H2Mfj>vN9g1AJG!Gu8$v5?*&S1~mDt$DlG6UU{)MvSyupKrz{^FF zaMQtaM8uHtb(4Q^-&Oaa{a>`s)^it_+abG4@#G!M6 zJrH5{rE)JoxbC~>JpYy5@<>i<&ni>6*8|~p`9p=~#cua}*_NGJF`5BZ2@9@k4vKW<^Q3FFHX`Rbrw??mojQNhOXYq_8Ni} zDc|JvFgRJj3I{FjWO^4|^o6>ISv=15bBJ2Z%|UpmD^y)RedAnR^`ac%C#6BNvkBh8Q>bzbNJ!=0+=#~>Nd!*1_XOH{-(`=~o zQoh;ra{VhJ;0!<9RPfFJDlpeS{L47MmkGu3bNbWb_VZTP@=w{A`@@-X66ZBK!Q4ii zyOvV6q8mEd-1f+>!-kKONbs#!FTC#MwT=0ldW0<^p5w@zGM7CTl*-N zKR&NUnABf%L+-~?7Pg0WJb(DKN0~Skc}~05^C>fDSoXG!MYTcbq18C_W;r(jVf%aH zb{MPku)|v3eD5{(L&mT_0q0dEz6P!@1;-rXS%i!fWzu}q>_Vyo#9ga>pPn7!cJ=36 zS=>&AB$;Zr9$}Oz*;m{4y+paNzT#IZ_eYwjNg>i?rE1J5I$95^bXp%holTi#R)u9= zAsK^427{G=s-1T8U|YdN*Lk6^obXiT8}eUYp5bJQgz_}4Db-QFlM{C3 z@pGuq?)7?0z=E8<%vZi4WsTq8*&?)Vsj#T4Bg{jzx!s196m1Iads-CEPy0oW-Ar@m zwg_5s61eP^To!A5_+*3V(qNa%nKwN0-gG1-oD)Lo83hBN9su3l$^S)+>bIZMKn9vp zuf=>Ngg--Ax1|7Ih8gX3qe41d3lDHt7=2Q-E@O8rVnqI~Ep2d^Z)Shc>V@LuWpRU* zUA(eou3zDxc`&7!u;NsrOLxVwHp{k6_P&;!v(){bLlp#jezlO*ABT8oMO(?06A5*GkiY9IT5_H%&|QNpAE}nzrheK&L`uEYhC!ecGZd6MmP2-kDCJymkV?J|CoTO^pW8sB={CaRxUdU{mYOh$OTPzp`|$ z7IV)EQ~2|n_T$K<^3PChr*_fZh|BTvxOt^I|F1T-*p7r?nhbWDBuhB zdwuSAQ@B^^=M{N#TquVePN)7g&5}?;LHC30ikKKV+v}046 zFBImAx z;OOGL!KwARKkg54iDEgDe_JTnJ*C{}#kKmY;+w(nv0nlhzwe8CQT+Yh&k zf@36`9wDrqtifkkWMY~XO{@bL(0anAvq&>ZQ}2lQ+~{@<1S$w8~CXZCkH#Rxl^S+FCPY)##ez=Me2{OO|v^{<*qhboICM zryGqP63_6EU1yN5&`pMv8qhidWTn?=5g+_Q1Xde4I)ld4XLsj{=n0R zUMCw~o4VYVjXS2{M$eG#r?Ne}`2mFMYL$Dqa3ZtwU-B(-4;wVQgcjYZgq4}Xm0lT+ zQ-*pkTri(YEVMX0d;Dart}j>23Oj}W(lNF6u|brgmjtTq&V;vMGFs8bbT6#oq-HSl zticzH;RaQ(QUcddLQQAnBCz|&%vHO)Twi@M*Zd~NLhZlz7TIQjDQ zS-=am-+O)CBducrNo6|fYJRuX03CeNn^9;cZMw!on$l~!0Fh29X_KA~oti;~IGvgy z@SZjyy({fEqUecL%8fFDqt!I-jD6CH%9DHhfOt>SnnEuiH|w=ysr7D;(VFnab!G0X zgZdaBIy+v4C3{;!s((b6*ZtUaYyV*0PQ#DZnCyJ7nf--O3|E+lS$0~EDd8FIe0Foq z%fy1Q0~@Ip>1wtao5~sFT3`K4TrRw3>zURthK4wkx;VHe(_jr+e z-T0~#cy@xjf--`2o@}^V@&56cwMj>^d+EFR{7NJ13xfK^=q7AW z{gCXHSW$0z>64#WMc)-i^OD0xhD?r`bc{qTTXfpZt?UCa9SmAFMO>l? zpE=2*g7(M7L2X8n$OhrKE+sFruy1U*SHa`Ko|hx0`%0Zlo|e|0$Z`?;u_ZU#Z%Pvl z^Etx#3tJ_OZGlf0^+or7UgpFb;En$9j~T9qQs>UhjA!o8)f{zKCX)I1g^sfQ`Te1D zx;v`!g86dI8(*Qj*XhJNJ9{3HTF|L1)HnpqwfM%QclvzlVq#`LRm4!`KK&vVS5BWB z)*w3}AUathf3$pv%GFA7X8q%DdWsy04I_>!pZ(gOTCnW!kd3KlHs{z%h=)gxUMER6 zU4NFJRZWt+6_^I6iO5PxVt}|9X|gC&HD`J&d6braO)9x$yiZniu2%L~weE}TM7-Z4 zNXO$um@a8@1Y;x@jd)CbbE}wzgJ1AwX3M(WzESXF`%R4_c0PD|e2@%ITVI|dAx1>& zptb8Vv)=dJ=OaZy{TBItg8a5sc56{C1NM1_g~jrWh^*Jpkyk~7WXi8e!TuvE@@lz)9lYCmJuBrh z%)&jlnq}}?=fSTQ`Dv~29+s0Uex@1C2bv5K^yc60h47f9#pn_LKxh9zCu4OTH2M7L z=Ranf_t$pcHD5xTT5?(lOU3wV)Ez-%HkM@*W*WSz9z8xBw+TKPj(>YwZvN-9KC6?5 zr-55$a^7s#(>n^tg%=!Y@{8=LJ!_jox2n$ZHrA{kkEZ5B4HG@Rs@ciG%hB~JsW=}U)|YZ}VA!dxO{ASMOAXELlF67D zDW6Ge6|v1-H>yqcw770mF4E;<7nqOz#3`-l689~F(c{}lMsjxIQhoDt&fIx@4NTRBOg z+F6Ub8T~3hqtY8v_|rV&l}(1|@1E`Y?pM3{owgE;ZNGmT8a|NVdHWnU8ia6)>j{|I zI}_x2y&h%Xm?ym04ZeX?GVwic#E0jIhlw&fU6KF5bvO{o-6Sovc)Fq}%2RZUCrCjk zY%0p=WjZ)y*B<0}z!P*Y>YLrb;mwEhcf~X(D-vWfCg7?(pA#z*Yos%i;H9+O6l%?! zWw@WzrVeo^hY@`eqlqae%1q~tP{8o8X?()KzBDCVzLi2pnja|e;Hp8DUeI>{~7*dd%CbRm(({&7z4Lkr3W*TQY)y)|mPS3Nooy%PnFb61VgO1~2qmB=OpRhrAJ`O9C8g%c zh5`>&kenDT6bV+EV~44kHC2%5{2i%c0BnY`+CUW$r*@arIpGZEk?3;-lRL3)8>MO*<16m3Py%SZ{1mNIBsO?&`i zo-$98>(ox38EC5lCsqKo0*(Uc098&^kfJTdR`3zO4$f z$4cu|l?Qe;094Vq#89IkS1ja{&D5b&#Z$*p-&U36?x6O>!Xrz;GH~cam7cB>o&~>Hp=?qV==;F7V_A@sZn<%Ls;GpWND6r1aHu1kNF6r)o`QOsLRFd)C;$LD*gLQ_<}A=H zvXLaf5a4l)X9TGLxhaK#cmM`4DiNro9u$?4003&Ou+r&&wi@t~pr8>?zO9;MPIiBI zOTYPE6<{uS45R?8%y|Rv)S5g1fD*zj%x;sS4rD?S6RRkRV7k)cByf0M0SO!cEs2v) z0bt@VX*kq_Bo9URk9ikx0Wn~p3T5R4As_|M04SI(j-0URQh?I~ECB@#R>h3KQ2+^7 zlp%%cuf>*5$gu1-bM`w6fGkCWQks=a50D0~q7;+?ERmBHs!OCQy(zA)Dh>5u<^-#TlLCMtCqPEw0Y0O8fJXd}udvxtRSGaI z2B03GTP~{q(1C46snQryN(-5b`l(4;xATp+WozgSydOk?PSKQ-M4}`lN?9ST6sqq4 z5>j6Y={c}{V47A{0*p{vIs-!@IsgLT)nU>^s`9PkApB4#G_!m(E%PQA7$DTt0Iz2S zohpTH6bQE}*S-wKJ@{lq!U+L3-;2|N*O^h&;-vQF;mCr_$$htmu`(DXSTEG% zF_amAQi_93!YPRWClGMTK~!X`aC#yq^S`t$^%I7@?^oTV9E{fS6Q&-RmXMHsr%FnK z0`m}vNdX%K08To37z*HY0?*;G5GZpV3Npq)WdH+Le}jckAUPmefB|SCgt}MlKR_3@ zAbc{@>k9Bf5+Di80E|)`U?qV90|Vj!1XZs_DIv}rS<1|}yGNsNdfK-9; z05x$!TeA$J^1yh6NdTl?CR&vYz+G{YYBaP{0mW?YmCdf&jLp+%3$d6aa`oSz!fZ0Hk>c1!%!3$)(=`9;{B_ zegs%EwQTw_C?diz%2Ln~e@B3nIS(F3>WRftLczLusUbpiY$*vEYJ015A zAQS`z8Uj!icZ_`$IA0%7puG!b3_uNj4PfsK)Evp;g)YPb6G;Rr^Q-blE1etxK!OM~ z)Wd@W+@Au9IHW2SfbtYrKtVeIUdDP=aB(q=0-z&7fyk=EbV0R4IRR>v0GM^9_dqp+ zQj;Z?g4jSo1VQvsbOx-BUa>94rzr=RQD8^{q->xnfefk^kmN;DsN%sa0OL;fL7F+ba9Zk3Qs3nv_x^rhW$sffL{adI9DnfCtwiInzOiV6rGr6x0*+ z1WZsOYD#$kHvJAJPW{j0L@o+VfJk)!hy>R;0buK315lW_svhUR@B^qN{uPVk5(gQA zg9OomIDyCluyokZ3p924U7NvYfBa`;qVN-VDHL-oD;W<308S%-u0s<4MKp*V2ox#k zvo8R=BgrF*1cMU98>|`hPgPU=-ZVfDWm6>uxrPnAqXYMTfPFG0NF@*l4q)h1 zr~jw#Qdg(uDutBN{i~6+pqk|!v|7d0Z#6u4`2M2g?8Jfrs`F8^znE+A>Qtts6pnu{_UOQ2Mo4ll{0SF8VA%a{Zg8lDKFd_il0Y*zl zLrhK2$t5l!DJ8ASefu7d%w0!@$GmFl8k*n+Fj5G(yNwuHGX7xmKY;OQeV8F(QfafE z=}riXvzW9_OMvm>U$cO!<$Vxk>!bQT> zn&wN_G@7`=5*@AM?utlhmTKbRGo1DPM!-+WjtAlQf@8b0c^TY~uK($r{#$w4C!}Ss zUnu5u$M!$q?w!L#pYBG*yKnvbj!InTRlFz%%h*j6KT@Q&mjT1lA^|YA{{Ut&cm4hI ztoH=TJNKkNDE>w&jU{b6;{y1MAZ`N8%>e%g)}3TrYuM9uo=OQVMhA0bc#b@pJd`EvqCGNM*sSKVCj-`IyCf!1uMj^VcE!8CawTF2N zO~S&4;H9ukXt85fC?Lgr zp{NFJQA}C2c7Vqmo`24+W;KsMYa9(Ue+&5B(u}{8@%)78r}5Fg>EA_pl9xeDLke+o z4U{qF8VVXzB7uL;YyJ!>9F{_~npPpVjBX8>iNtAs(p?KPwfUm}ZT)S#7&{p1gu;l<&+HCJH`({Jh@ES3RwgS?QYF zt#&8x#7!I??cK32;VSW(6Q0<4Q=I*2!olqrpTo!kE_NPn)d1>2MwWPvuvi+5NmPtx zX#>e~_IqYNE?6c^kJdfYl z&L58rY%?AZziW5XeLm8KSr0H5-t&BrMpWEfJe|z^G5LN>F9zRw)Z_k z#KY(+#ghZr#8_dAHHKJT(Rmi(#fSBd*X!?e>I--s3s^_v^st%Wj;^wt@Vj8x)q*L~9tdZ@I^Y||&dPk~(WW49Ji za&*^^wZAne^m2Z}r`uqnEdTKH_`Q_QE}V{Hetck#bv&b}~1HCLwWQRNURyHcn4E zUW|`O8e+?qS<36kjTBm%DyC@0h*BIA8E+GKIbHq=dd<4~j)5;Uf7NjES2^aj0PgwP z-1((H3P^;bo1CZ#EbS7(qhlW_rHozkebg5l2&5FqLdNI&i??7 zK(U$!wYQ$L#$?G?(>{5N&!lCmp)z~UyxOf~uO-G;51TkSk10hd5uCV^$?|vIQglCj z+xSDl-1bOAbEJ3pdfL(2&%l8~DaB^bNuPp`noZyY!e+yc?O0FLE6$fA+-IacnPPCi zicmeJw>NW+r(V9;{F4#C_2Gc+K>O2(Go8c6FpK{CP^Ip`|A2;O2a(;s*v+F_aQCWo zLT!t9-@`fKO?~ZRb25J1r&Z=IZ7&fyv^#O2FIjN+5p{m0eY9}iJ>)~$w%|uMO;}ehC&DOy}9s5Bd*QFSU8YPEptuP=zVIkz9NQoX0k*d|rdpo|vxk2PuGPA1`Se?lL& zvn;cF^d;w!gU=e4%+TsbKP@*8FHBy~`}c>zK-B^F6t4_gdEJk8JzTFL_P z7zzF_s`Ppc!>?X^Agntc-5{Em;V82{{ctOYj16o>8jmM-&~4xwfBXNb%icw zy4Oz$M0rV4(Cvk~h}wT1)A;kpIb`ai{19!74K30Ey?HX%C0QB4l^IZA?e zi{{DBgN6mTv;VONO-gY7P4TR;RR>MxjmV#9K}B+j7bngS@^g5VtcWt)F-JPakXCzR zyN}N7!*Zx-))};qk`t>)^fUMCvBRIwe|-A$;_~+CXNVF7rD;O>7PEnz!Rp(j11P$h zxu?3(me;|=Lz4in*MRELwq2I8OjeWKC6L|q&f)jH!tpInj(_>R`Fl%LMevP{USDSpv#nLzMb(XbNw*Po1!N{fRQ#0xp->(rRa|Go4=HQU;lmb z>u1wp+G2Z+a_cXPD?DLbx^4Lb-NG|hXq+gOQkIcz{H5<7D|J+EeBFOQx5fAt>`tvC zK9Ruao%+4}*9@bXa`3~t-GG5Rri4ZMo7}G{qL=HJy?+jC3 z@9J^Bp;>fbnXEg0gf8dvl>S&5>7eeOop>6m(ZXJb)3|tQN!Ba&DR%gaaOb0tVS@1? z$DgL}o2_@><-m1d`0IH%hyIvqu{EW-Uzr_)^)qJkvCW zXRx{ym+=popf56A#4gz*lcAaoB>sv0RuUu|*EN4=Kg_@T+0j5EM$r3niSn(6uV_vy z9UM+Aj)GZ}AdqBi=Q>Y;-6HbV$V98s9EHkCrjt*3Hv6a4TkH0$y6+mX`Q(3%Kc{$O z7cRe}6#n?l(L_=A<2QDqcSuWYhKFu}J^vb3LF|#r{CHL5W1POr!mLPzZ9)fwUonM~ z=0EkRt!KzZo9{4$x;j`h<;v{nh8p!`Z%dwWcf2=fLR)P{t44bqlYeB$V|26zR{#W1 zvGk8#bfx9kK^8_tiF1Z*KHz)@SjDXoLQ=*p&#vA4`lqPy(;Yu$pTf^a`RH!VMUka9 zc9PFd{;o&d*ne*Ojc=g|dpbz|gPkVkE{(V$WVi{NFI;>#Ke6qX-BTD>=gpu>=7R`z z`h)q5*YBVCUXu zA;ws<%UJg+0hvqRC%mNlBj!lozM3x7t=Ki_u7z+iR|iA%x9P&P440iM&)z4tIcn8k z=mT!|EL%9v2u5$*_UE)nR!UT{l0TAFBw3C{ZfdbuO{Z^9-%8c(R2o#kmF$ZbIC^$3^`?=#B_h{)hwPWAh33ux+$gD*nKAFK^c9&XG$5-dtTdOmK zD`8lIlLHDyd=$V}y^!oJ!m4(!YrXlp?E1m4b%{G)B&X8Jx6=hfWm_<;n+p zx>AUrWk;;T1^T^JDOHb1zhR~-M=L++R^ynzN5v&o^O!sXpFH$3)!RiQH(#Qa{ac@X zwU$vXW|qlqcrKhQ`<6rVvZuiU%lvDozsZ;Dk`u94C#}CXe*zy;pCI+pOYnM6lU9={ zJIa%d!+QWFReiLL5>As@n((RpNqGF^P??KlDZ-PP)lLjKtp^^q7paSkdiq1!!a*XV zgv*cJTs1A(7DeZ$Y_6M@YJf`dvoU{~24UOa-3j0w*A%ytuS@NO_P}w5a3#0Cft~Nu zPJw6EFAUOs?W#t(2s9~ZO*mVyMdZ#;!~z36cTjTlJ9!>gSz}907I*fWHMW*KasmW? zWtRjla{MCv{%zd;o7hBn2lNj{L4QWKo?EiIfEG6|1R2b_779`^HF6iq!$7VRF1x=a z4*H_rp`%OAO6tZq7gljfDr+5C{Dh=ugnxPLcbh5`fnthVQ}*3-RL~BV5HqI@-&fkz zxe7?Qy}hrR0Iji}uqDx*mm*x4YfO*7TK1U;AzzbPf{9yumc>1S{rEyObJ{0fopJn> z3>re&~7>Tj!2qL7P;f7tS_R8*k5?oK|E1v1; zP!dj)3mJW}4$;8tDDmzWUuzk`J@tUx(+(!Jz4v1E&ZTMvqF`2lJh4&$Or)Zh+|2!g z#QBy=(rsb&-$QMJC01<;x8;RGb1@N9`d{BVg8od02fq}f5kzG$o8n%Oh1z*`L@>}`r@1#Jg zp2^I4AeY1l+9S_2hMH{Ie;zlVm?&P*@1zW%Jes`+tITY6(!i3)X)TyH1iECiBL;36 zo_2wIXAaxJ!O&?$=CCukGTLgkp_r#~BzqA)3xU&wZAh-LwWz;NOJv&^_Or3wO!Nyi zx1>l{LM8eMnQH(~x2u6WO2YcqbQ7P@3E6++JgSNAZ^)*2Y4hv8x5+?Npoy4b?4L>6Ehd@`U$dWRP#$M zgW8uShNyaH+xpbm%9wO$dZGz)gCG*3nyL#%*pdzDt3nPlgbs7&gfl!zFWF7`NR*^F zY=)Y4mlitU9k`+6MuP4oHp2s*&v#6qbkBFfPfX6_7Jf%f{)!3lP3x?UZX}$yWL*DF z#zN|0AAS0wwMwR%!gXEZG|+_O7xa2mksO-50kxW(2VGvT#m}AeMH|;|eolzl zGfI(6bdRD}T5aReFKeeyHhNUhbGOCc@{8GJG=tkaJfz)_gT$V*z#z*>?c;uRYepci zCzMr^u7j^fP0p1pP@n#`2-eiI*mh|%OZSr802&XiC z!Zw{|Q;qw48RSt&eB}9dD8XtU<2u=VK1- zEkVeL4QuO3mnO<$fAD#1;IF-<3|8~BryF=JDTQ}_4nNec=n%;70B+l`<(1dMKeZL^ zSLK_duY!!~X}`xs%j!7oIv)|MQ%fWXwL;)28Gh`HsE=MZWY8mJV&#-j(I#knXR6%L zRbpv~K}lRygbi1g&?oiMO$x1>NNzwfis(Ew7$J#R1{BmlpJ~8J?e+2nRkosmm=)&HQ=xk*dqO+vL;_ zYjo4Dl?TEKJ{PxHD?v5iPTPGh@?TYkXtak-vQZjK&8CXj;leQUP)vwc$5~i)9byi z>I*j)pomcd_zuFH>sgG!Iu)=ZVfdDp)i|O)(UJMzXw>f zogat2{k0s>7zflP*jcBMl@Jq^Mj}`4WrMasq~Ni}4HZXnA4j#pszDV4na4J5X_~fv z8o7h~%sgJ?tCd|Q@(tL|ICEfour1gV9Hi0l(PDysYlbXUBvoV@!?>$H#EJNMtONA~ z*~hfF0Y3ZmCYn*0Qa1)(gv( z?lpvxp_DtxQ98+N>r^Y)z<=@Eft?E>8tc{^X+v-e?vaxNQCb355=~uG#IG$qzN%)| zWH*HcE>;-=ST&UDX@b>|=)$~c&=wg7a5I^tVi>Ua-37Q>3hQZ(Oa?#OOhQ}OC$2#X zljjZib|UFB%oeZ?TQ~Z?3xjJ5_b-8U%~ny@j^8g$ZZ}C0n(|tdW?mLNOI6%n;jjwu zSKtXv@t^*K;h(a=iqo_*1%|1ZO);GF#@8{0=kevsw9EFSFUc>i88dfPCN?`Zv#3?; zL$uNk@poUHb{x|dYj*v?zOJ`4O0q0&0uRtQT(QpZ4 zX%6Rx^-V3|NC9fhhV?S8&={6Q$KDictQ1Z2rqB_Zl$MyVwIG(h!^IZl84?ZW0|V+@ z9FWS$mAq~TDR$;z;Z(E8V}oCLWSngl=N#Aan%x&!i$@D=qjKrmN!5sJ9SWxB~V7X_cEJS#P5fQ^?N!FD-TxbKUXwxR6xCVu#U3bINDubQL1UP`+jPMvW*a%`ZG*lEx>(( zp2=j5EvK4RAMJ~q@>{K@;Q?m`Vy=z14>iQAjp_x?i|YBH39oa#;3V>Wec6*a^vTp$ zKCefQ8FwqE32MCG{j2CrdC|S`rlM{;VSTR*uJdJ!0y2_yO`)%V-#RP{+rpV=dvROq- zgSldB-(bc^Tmz4Y!Zldj!T$U6PBG*m_oCm|5IODC{M>o;N$T8_Ow|8_=U^|Uap`PH z137lGNgB+RSg@8QX&j`O)Zm_JXg3+-KyOwwY#hurR9I{N+-GY;iS!-!DyL#8cF{$V z7m>=BN({m62Qm29E_jwChCpASU6b<~QYAfsM= zt=Ug}Lqq$gkBiNxURY7byO0Q;az5_Y#ys*l{gptE@#^QTW_%KPg=~}gtqc@1j&)ig zcGH|kA-1NpIU$E;G6dduNbv3_?oXCOgF#GwVMoz!a<&gibQQC~!TKsyrEi8iHJ*Sf zz=n9t%!n0TwP4dzl= z!Bzp#qs7$&b}ur|3fnU+8vpB1ogY04ZHwWvn66VpeMd*V3F+G*na@u@zU z|CoEWul^xHiJ`SS0du8*%4vy0`n13uEkL%KGAv<{AAVJ}zSWbg#)aef5lpwQoB9Z9 zg7vfi4z)7Kh#yzx5g5!^#KrxXFxJk%oOI`a?Q^K863)F0?yhIj+&^N~vWk55E$cXX z3OP%=w3hSVSidn0B-eo1LO>ZFgQ=!4R2rz+<_p-PStnPp?#;&TX!n3;Ud4MrtxVw> z(Ztrm(M!&5py|m*iG@UBZJgV4qJ3Qkgkk5ulo z?XhFq$P)p3tL6e;D$<|o)IWluMvw%d=BQpBpyWH;DT7|)ye{bQ;6o3F}^uzw~C5 zch%C(LUfdE$6V`ZiDFhL8uC#-6j?vWEcqq2a#4mNNaM4eXvt?6GN3JL-jzU;n4{H`Z6?i$H6~4cof} z#*_An8!`q!ZE~s>ENMWU;~z21hGfd ztmS(8ghYsN4Y-_Yex{4GjC$BBl(3<0&vLI*exgPXhLR{=$KIq;J3oKrEme}PzqX z=}8otj}O}cbq83vpa*E$8E%^enzI{Z2MZ!o(cdLLFi=~~J@mJUcjXU8dwd09_;W%_ zJn>|+BS>Ej;{@+G2LE^bSD^dd15=hUGHBeIFO`20&b7O4yNm#= zoyrXjabRI8OLPIyppC}v8_*^~qxHW^Xw<`|>r1FRp92K|s?ajMV*;GIk!0sz$OF(1 zJw!~v|2sk8h7K-lr7eOl5)PjFKGI3n^fDT3)H-+8x43Jv^6BXKC@3utTw4WL}|P`hVPuHJedkQQT) zTiJd5^7%~C!Vsr^tP39af#%bV zNc(w(V27x(Xkr3+h=H6?beBANP5Gmk1#DHoKH4!1UG*R$Di}F%et7%g=I(ZlCs$wR zPoy9m^YO%QdeCve<8dYeA%?)G)`_6#$I4v9^a*e6T*hcOmo_B&zKkcgO8pAn)K-w! z`htRZ?1Xh+2XLH>?t?B5ckx91Fm)M?^*A{wTx@p1_bj%5a&{!2)7JwRB&Xzd zdt-}{@eJwmdSDJ;!E3mO#z<8pPd3J6wl@}Dc))P^KM?zNy?HV>Yw@i7&8Sw;d`P9I zP8h9ogeTN!z)aMlal${_+_Q1laOL8+E9Z(U{o;k^^TRrE4FXl8`bG~|>z@Ran-NQ9 z4LaFHi4hiiZ_1+`s?#jj=^k}-PjA~Xq9zwo%T-4T#kf~?4YsNww=W#Muh&bjMm1@q z2UKbIzd5J~1QyYFur&?dv-;F4T#;9Pr2^5!&1-#DqA+hIfqm zLpj%Tf-U-irhXRk2P1}?R}b&Y*5-?A2idjJkY~`CI4eZYMcY;+G6TdQLDQY6BR(OODW zjGBB$o~+H!NJ2E3WYVd@%C7R^V`E;A3cjgr9RGg#r-`^O^fnwCcFQuF&rQ}JBpEK( zE`EGB&blsXxT559+O&pstGEW7&)JKR-)VfEFr&enGKS(7?-xI7P$MDt!29SN5RP-p zfgCBC`eAoxTiRi3FU2OqrBx7oqlWt;1ZiX+0)|ZCi}Wp&y5N!;1c>t z&CCiKOKK#Cn`O8U#yu0u=%o2Ni2^Zy+a|We`S@nkr*~uet#0BUj55eI0=C@>H3a0O z6?gmfgJ#pJXMD|j|3_5Jf-c#!0UNvjDvQ8@vv;kt2|42L=A{XFuL;#ln+@CARObfZ zu3*4?4dW5j1V95GHNwLQEyx5w!=5Z#UyX;e$|_f**>;dUkn|(u7-({RKCA94q01wS zt)-dnG^MQwIr~%Sx9;Qp2?$e?wFhPB0kIaL#85nnuHcLBfKQBah>9rD0N*eNaN!}n z5Cqxf5QgJHgriDs+4A`F43=(uDe*%mMeB3V?xEaci`P;|=M1Y1J7A5)Vpj{?u&%!o z|Nr7LK>q_j9|XpJD}dbyz7U)qH2Dh+=q?B)$2FeJ{-1tW-_i+?nvHs-G;2)~n$eh| z8I08qHi$J>&CLtP34S03tGM&=7$_L&PB8Gag{ta(!F- z6Z*Ple|Ye$a{VI1r~;#Vj7g&`(NVm6UcgU!Cl!w|AtEuE?YryitA(Y;vQ{~}U$m#T zqxo0#h+Tl&Lg0K<)@0Fq%(b?m05BpfNe^!T!sYYK)=yS-v z(mV=f(PH-P7{2!hqt|CE%C?2gPz$jw=S5xXu9YP`!fQ8vCFmMGwz^!@I1;}F#ioB! zS$?aGnE>H%H%!YWBt$^_S~|O_g!t_4+QZ~cHTKZE_dx>^_snt`O9f>-NDrP0SxwD} zPeu@|`*qmk-POVb&1)P5(N;CBu5tFlO0kmomPhEOj7==9@K}VTKROHCsSR9@vk_lZ zd?z&Ud5wAc-uW!$hl;yVix+pe$X#Zeo-`H3!8lBPyl^%IMLh10V>Pp?sCd|1$N%6i z4h`t>Ob%$V^OO2Pe<>Fz$B;L%fB$4EFZ&OM76TdifI=71AB^v3s?qp1Pi4#Ygh`8k z_*NBEpjNZ&gWdWkp$+B2w^TGb88^g$YMf#Xis+8A6XFFHNHmX(7z=H@kxGZJT-B6b z`j?DOh%3Njc5-?*Fzm2}fIr=MIs#ldc0rMIIhZsP5Nyn$6-Po=zMfFLXEzkh4E&Y% zvX2!x+gl|y^jP*KV8^;JtE%+gXkq)OiM8g% zg}Kez)}LXeQzOq#cNnyjKe&xFi)k!LyCvGo<{IH)FfAhlF>?qARNL<#6=V0n3#}eJ zC$_dy-O}-k}7MbD;o&HT%nvCZKR^bf8MHW*PL6tCck7qXd$8VaV)*YAi7_Vz6J7 zN#IV5YRZO{G!8iBHz{1G%Y(MpA5${a`f|Fk zi<$k#ZEkJzzH?>vl6nbkI8NTb6XziB!4^|~dS?wMy1&YDuJ$%hQgzL@$lXU%R@^nX zvl3$D`9>TqzA5_thlbgAQsx{mSjUA2?{{o;iG{DtBt#;zbTR4c?hj8BS(J=C&^>Lp zj8KRZ0TGuD7!#656c(9@$KsMZiBCO{X4e(haBPJ>O61Thx%&K&b(e;pI9^t>>Q!QF zxK652tGN%yQ!ScN(Ryw-_pY?LubW%&&31ZgDsMg}4586tuYD_(%w_kDNfs)?V&l+A ztwATl{zFb&X0wu8e%*F{hl3?=_EN8l3h%z!uH0idEw}yRfW<*c|6uFDSyu9lMNM;} zMZ4}wIy9v;aOnXOIZ@F|&#&Wgj#G9*-n{Nziusl2G0*r1x(0%4v)MRzk?(4vRBh%t zdfGVevye{%9nBM$1B2hNIZ%9mh4TE4n&AT+GH8y?e|j+BkO2pO9kLq+bOUSXAw6>z zZej*Nn$zDNjBdo%XgV}IXk+NErSccaZ1jmThONEeQ4R>{uetw-kghf$YN7z+O34G6 zlLpF};I}$`<+9tfI~ccuTGn2Q6phJQ!h^g;O$>mYIY7tJcyO8#J(gy>J&6Gq!30Oe ztkI#GU6u#@QtYgbb#>mix?FKnnW$)!`135Kj=!@d*mL*|j*5yB3bhBY-WKUn{MgFX zRbMt^y*Mk|*>r>l5{Ih&wl-c)pKVMZbdsFrJ-=n3CZM{HrQ?81iOza#KKY2i?4#a7 z42xvPW$Z0)2)L4lBFA!&o&waMuQ@7fLHUE zCfqX;L`&R8f@o+200H0%M!ZgU-%i(fjs4a72++*IG_@H-_fO@s1W*!y$J(S`bt;g} zKXCjjK{f?m2Aq12x;*1aGoQM7n z0^9$Ho|a^xhD4fJC4g#J-xvOlorc%>f0$(IUkU}(1_aCx(>30e07M!uJ>U9(M#}%I z%8q6I%tGC0v)vaKdxb}PrUBTNM{KJ$OXYBhUH#MF;Hi$6lIwVh+)?smy8YxlT`YxI zQdv1C*Hf)to6*Us&QbE9RlbU!6;mFy4sAq;4~h34X|z$5J2hE)EcfUXr*|4(R15$Uk_OWJ4RqHy>i;>G(q+Grjmr2~45vL7sWf z!1IW5Ma3*BDJiU+8k%PYJH1w`XREg$N$hcVNogT4c5J#lKvC6yWrGtc?0oBa;OIi! z-aKz|l*B!)jAlQjuh**!;rm1T$DsvR895X1#z2xPnZOY_-cQqV^i$zgBn~aBVDbX) z4%6O<^p74$@Na5?mkNotrf!Ln)=GU7rSi@JEZLe0dkZy2)!gp|Y?NPQl?k<7yQIrn zPsph?&6l5U^PPFkTv@{?ABX~12;6zBw6O$w-J#jG-&ucAZo!)V%eHF0W_PrtkGQL! ziBp?KG*XaAYv*1E%}>|KL)yV}gx<&_*C+IofhPR5DwX-g=`VjIeO;ZV(D zF{@`b)IUH>HFvI2YV}l;3k7tAf|6OBj$)sPhci6PrBpRO|!FtkEkNQbVVvILF^HVRldA znvK4QCC@C&(rpz|)xlK)vscvfY8;n{@-Ez$B4}?4_NkT8KKpWH1^v7?p#j8|*v?*d z2c-VC867Q5+aEc4wW&QXx)gp(!1<_;i7>BIpSQe%cg|sYI9zojZmnaN~!R>jE9>cS+cl`&?_w}@aXa+LW#PiNB5EVT z9mKYzF$aAp$I*9!U8j{?*`JTlZSnw)Y{=D^>~f*!zGGXY%2@paEp~U4RG3!XN1vW< zng9nTHF{T_s%QKfXR0QoBhz$+ufH@B&(CgpPkh=RQzf1Y9n`2SdL=J!0;y}sFc&q} z>@ICjm!DY>4%YW(e@C(RBFc`&Fay`{qgfOp7G-~31ot(xuk^RIkGbl;W|duOM59g> z(AB__sK2p{95i_XL?OV(`45u<x^|1l1 zV@)nG=c3`G-5oBsFUbLlJuw_yn#!F8Um(bd0Ma;c&lARUNFu#x+?bUXi8YA>2Nx1& zEBscbT>_FxCDd=IgefIJu`1TB*mCLNSCcRnKg8ewb6}Ac-FeUu#h519|6s+9#s+MF zXPyuK0}McCx&aH@x1H#wc+(&MnM1&G(-_gL>iG?Fu>LiRZm5+tU=;}(wRGKJM-s3A zelcLT$W>l_KKk~A>x#B2;3jl(Q|B89Ndv3evY{aapjz3fNOS~6fVBZQxGDTi&2RQZ z(1=ft$h@Ikv*@IZlL!!*85Fru;%+#XlPFm11B3dw4oFeAsbrf>o}6G&&d23xA?E;@ zruZwwS+q3ZWUPwWYtQAuf6r*nU3H(@9LzyV_5kY;hu}zWb!9dq<3@axQUX{O$f_2A zQ2!k< z4^IsFzgM3uw6SEz{x*n{>Dpt+95ytc-ur{`gC~wPErM9YF~%~rNJKx>mS>n9J|Szj zr%aUdZeBc+WPoj3>j3BHhK+v`t#e4{!x@jxR^>!n;;Hoh6P)n}o2sc9_c09s1Sz_G zuT~8~i(oId_mJ1g(v!odk}lC1kFolZ_(VV*9zhn)Ig_C_i4aYZfsm<$UYv?qy}ecT zAoXSRaVLiZo;&m{R{Jaa)mzcXlHCMl6mLk6-8#HVhJUX8cA-SidV>AFycno|6dM-G zswx%m#+=(FZ5l(QAlZ}6ZF~lHOHz&(hg8v`8K6HHE*j;y?gwcOfbD#{VHgE&6qw(}FP~Fo zK3RQ?l+9<47CZ1-yG9UX?w_jvF_M0ZkXC52Z39gWRyaNjiQhVlI$dq^Rn-gYei<-YWxbLcx_r02jXlakpd>OJC;~I z1j{sj!AEVew*{yZ(zM)Ab_Poo82Zqoa&YoPr%xxNJR(O$JI%y26vb2fL5GJdYI znWU}Q5NfE^{aw2n=*x`lIL2V-2RNeGt3U&wFM1N|2Ch?bCgPpm8fsH*fBWSH;Sa~t z=u;rg0NR8>8m+Vx1H0f*4OPW>m+p4KI(c<=(EaBH(F@XKHtTh| zpM9_dsmF0CvlFr71@Q%$iXL}qV~hM!sOL;hUDkm^`6@ZEqk*TWFrwsfMs-rY8r@rr6cUe?EQn zbX+$KTT5F)PgqGR%$huV=k5bbF8|PlOq+;FE%q2^>_Nt&SBFL%yY&ONO{i}NP?vetR)PQ<%gN~)f{racx8+xLD zQvZy^^;*gfw`&Hw4~B})cg7H{RDI2IigfknAirip zB|!BONZj?Jy&VZEyFu|vz~+HRg=7kHosNnZXB)cR_&n}lggs#J+|U~cs37;RA@NKI zE2(9+6=rX{3*d{tMI+b5YdVkpyhiK^pAnal>>{5%3g5PmT%6gJ^cHCnw&d9#!X-K- z|6s_1&w?BQ(T;XgLQR{4Dnir+eEC~izSCNcwM}{4QpiLg>xbj((W4l@zu|(vwBs*u zpiugU*W9oUz^|xBif;hpHjp>~Vg>+qK;qEcqXqyD z?0?SydL1Cs_59bl0hkDoJy@M#2p{;?pbi9)(1IO0d8?LS;$2)|WUr39A(z!& z^l7iw(42HW=fwj2)CJ%kFqE9#?sf70G5{h$>ONjeIb!y|_%h^Ns3h&Q$xQ_jX?V40 zJLfN)*rTm1v~Z^+4S>iEDKSz)tC@ho0}vD=Ae`|I1nl2n$=_T9Y#IP%fS25j>U@sI z72LaTfgR%++_)*(eA&X+Qh5Fq@>1Ka(zbJ3J@wzPxD9^ zdb4Lylyn^A?UVV|4L|>eO}l@}r}|A1BXPq~n<%$6Ecm{duYUGd;|bc1^}(3i5(&ww zSnr$hZ%;8=Q5Q*3lHBf%G^6w&oe#+cN$jSu^gfIpQ6}x?o9}$;dDL(18=~wV zXMQmU`W~|qrx#)ky7XjsF?S2)J}$r#(J<%jUgIXR*ercTI!q4A7cXt(+uhB`RsXs> zausXVO`s1i-er_{z8v*=Y3Uk&;$vs;Dw{uC3QLFy_fbNu~Lc;p`GJAQQ! z;>*xe>Qp|CU+8)ux?JtRyTy+u<%{md%WQJIlOJ~M1 zOg-J-n^51|YTVpidVG|}RL}j;*ElS5okjbi0>av#zniAhhfEKnny)WK<#rigugoVj zWX_4rrYbne`8%=3?yS^%^lWHlWA5?mvPdxey6|3^s|1qm=iw*7ZH*RX?Se13Butki zi6+c91?ejIZkuFHH#!c5;b>FMHS*xGUGwBg9>{@y)MA2TU-)$jgGNFbm{&Di=RL}- z^h?-bRDG{BUs->ayvI54M$j!;@}fSs)R zO>F;1a#no+-@OcT#Q=NnN6_A!Le)?!Y(F0|&mPXCSpzC*+iwh!NE8#8Gn0dbv=K+; z^9C*LkAzl@iG=3|6iz*>P|Y}XSh53hRi&Z$@vX@)8gf=IYl%c>708c_M?WIlW;wNQ z8;hPdzA$-d)0m8M*7sBsN`wkv&P8JA$Je4>+%cI)z|$YppdvvsQTyCq9$4-{z6jmj zOLMQttucOxsqNc}uW^JMJmpl5h|zPr9#MSYbXxSd z$2FMXT6jpx{m1jE9T^2>Ug9`}z0N9%I;4L_uB_72da`DM(Go|uR6?^dNdR}u-tU3y z%VkN><5+hXrNtsFXdUw>=#A~bzaaMiM{~MS{O8yJ1p_T(+%)X0$~d$P%X ze^_v1S?~mUcz;Q@KJg7XK)dz@@F+i+17O$#ma;Su(3k}X0-s4E&up`K&(hLwxvz5V z-;RM7e&yT9^Do@>AK6C`NP7KW*~exK5PpECfOOPf^`{K*%+cL=0^d=K!*~LFy2Log z(fcE?EIB1j?f~9-J<}IRmjbZxM$O{_{jXZzpyOY|1&;o;-45$j zH*k6{UP}?saJaO}%a(sliKI2Q|DNy->uobZBsN}7fuO38)PNN$&6lMG_(fLhD^~^o zIXOG+xz?kVxY4C6yu8*H*TAJLxWBkL0x*znLV@Vu*k8_LD(C}*3V={tXDYDi1VNijS|5ca z7{U4^G+0Beu{Fh2#)2ahcXwBDA3}m_Z3T#V4J%UAgrXw8iJnR2KSm0L^(b#@_u*2h zK8EGgD&7s8s;CXOY6{2H5AA;bkY*Bk%FyR$^amr}1=C<4UfXEGU*ehz@++;c<~qPa zkKwM7iQR`U_H}fHd&(M9m8PgP^`I3JWkcKcSpI$YD#L9Kdy!Tq)~#vhm)_*p)pgQi zgJp2%P}3Z-;vcuXRuk+hIzx}0^*I71&BW|vl7tCRJc}`yFLmrE6ms-Y7r+IZPAvxo zKUy~1%O>e7DxWKex=KA0vm21@>*%IUWDwm;;K-mYYCM%GC1fUdwdxd!dUCzW47pZ5 z_}*x{Y$loBkbC4q2bie&lNMwXpYqP+PNpO+YV|>*^l3k|=89+3xsSH#XQx3(i(;kh z1ZlzI@labgc-U=Xm1!)yD}B1`#qtb$?8Q12O6_%B;kGDp{MFt`mbUH}2}Ox^<+E5j z*-SYvj4BJJ%yIY^IE@2+*Vv^{T#yS#UBt(ivne(sOtYy<*RmG$W9OBxj{;@rr}hq# zYj0z$+s5NJL86XDfmuUD!V@ z*q1496)ZpY8Q3bJCjPA0WEDe>MJ};vqpy;f_)X+nS}erMZ)1(bH>tD8k6pe|peOa4 zMEjPcfN1|TZHUVkVJ0nU`1!!S01EtKg zjYiAl_ju!%G$u34Bh8VR*!Oa*o3LSdB2WhpwF-j#bP}l$<)@iGs!ip@U540C`|puf z%so7jZ~{j{g?Ig4(xiGjKMDg!b%s%2lp?|PNFKA)PSdsi<$6DTPG)Eiv0V)VW9Yj{ zk~<#tqD;W&Dy^?h)=7?j5$$PvL;g*6|I;}BwFy9D_At3o2(nW>{zjyd(AlUY^h8}h z-KX_LE6ipf8~WLo*Sv53>|xNv{?gY!}_w+kCoy;CkNVj3Xt11OeuSq3bgWvOOD=UfZv_t z=+))5=3O>%4@mHq@#<+wpnLh>){eHofoa=#<7UYIH*5cAv8e%6e{4YYM~nY_Hy%JF z=syYM`WqpqFn$mvh3@dcHFzF*t*@OgYYxbfNP{~6U|s|aE9{EkmO~Zqgt+OQ-G`L1 zoLYi83SF{W5Pt@^|25aOv|M0-q|NmzO(sL`l^622px4sbuglZ3p!;2`?h9dk zE+jW4ve_L(hq)oY{$Pm8W=nkmTrGo6YraV)syKuyVLf3><1gzl4RyWS^^6lCIP7<` z1j0k*fDBAvueFAuoqd8CYNF!qkOg@iN^)ol(^y{sT%crLq(O3rvRwyX4O(btP!6Aj z_UV<1(K$jN&z5)7I&_<5j!^kpy?s9L^3Dz;~ zP|Pvr9_=xVlWg6FxqmRuj@>Z_Y?~&6$xb-qum*pv=6{E&D zuV93AkwdRS?l8$+6?w%(hjjM`o6kh_qL@DNDihtx%vj1xZz(fZN{Q;B#`j1e2Waxs zFR{9H&}3~Mz35&Qt7@E*qh;Mgb8S<%hKG}q)h3pBQ?!p9W26iUvCcO2L;HB|^_|~8 zVc1ew3#a`?5enSCr06Kyp4O)273FMG-u%g@?G4{1qfFh#U%(ytefB-A)~ESHs^rBC zE*4M2oD|H|x4v9?J186M&XlW|R1GnvI$wOV2tg{na+3mKKu9#l! z@{9<0lfUcA=)4<9?8FkSi%<2W*Fky7u?LU7Z~GM}!zkNmV~O>8<=Oc!tv?uf*HUTU zg^B~;y#6gEuO&f^j2Y(mst>TuUZDUAzPDf;d*M;Gu1HnV{r!Ri(gO}Ht2S#cC3{eS zE$Atpk*Mc@p~l{$MC!z0aXbi3auIhT2WLM7BGSDM`GLyGC;<(ql1(rfF{X!uzM*o+ zJy|2bBS6YBx0PRbb_NvfJcCV)H9>m0&bvF6;yJ{vbs#5RYDnYG`Xh(R^67N35!Q&f z5a%-F$y*Z%ruA{mB5auz+W_KQGC2v=mR%8nYBSG^&sDLinCW|Py6fre;mo7M?@GZw zFeyGEvUz=5l9`@qpA_h{gYpbHn_U~qyf=e*Y*tsqK7>y=lE>Y5q<>Rou8KU!>7wK3 zHOn6i^Nrs@pMk3dfgI{zC${*e3I8XS3XEs=Y{@{+y`k6tXMqr_+oU3SVKfzT3=UUH6_1?FmWAO0J~1O#AUs)o+l0KFI; z%DhQvqT@F}WBwb{Z2lX0P8mdpHGz};@!R#{PIz=S zb}khSh__!>8niD*<)Y-$+Jg$OulfqO{$Ma#@cXZ5U*Z43$aEc{=x)~zzaV575&MQa zuDyP~x97F8%YV*v_N(NFT;Fv_5Ms3*6MZna2`G)LIZI7>Ki;0VPF#duN;nIg=&mZ`W>m$SIi4qv~bW)S`) z6Y`Vyjq8QOCx_PoxZB1T9S1IpfdQ{=k9PUXT(^VTH!cG`U)wGQ$#i60>0bsFs<#C? zS!_cuV`d}qT+cueKvP4w-UUVSB|_q8lhA4jLGK*Ncjx> zweuVPU)!<6RRyC`bz8}MlP0Gf*aZh$E-M$mYEObz-fFVb4ea_KjZfmtjvjuZZ@SDG zKK+9+((!HQjYVf)83nc+|GB`$8{}!nPArGrwLgP)+x5(KRyYy})grGaI0h{G<2UwK z0J&B(9(6!ie|`Hh2xn!x;OrH}LePZO$?fk!6P7227ZwMgjE)e(zIN@jD>l?c&=-?0 zi`KIiV*Yt2aR0C$uS$-B;;$YK0(Z6ICc?P&-yh&!TEGwHEusg+OahFLA3nOROJd2a zBPjpe{lm?@tZrjs%3+dIe6#jB*b)}jRU*seoGjGhoCJQ{5$j0;R*?$U5Yi_^P@`62 z#C7;ftHZhB)Sh!r+tkBlmqC8K`mke=Y@D*iZC<$R?#t{n9?}f@o~2`nAf-C!yTHDg zyY0SY<7GQ|3rYGL3)w|%k!_B;e${b_>cV;mvM3&Gong06j$EX%nomd+9iN@PaCCGW zNZZvRHRhV4mdt-VMGZI9r%Q7FgTdYt8r5xWYg8b3Wa0OEhtsBLLP92YkR+jK&~BK5 z)$@t45$b|A!{*zy^4>rMVm6I=R}*fp{KW}=w3v{vgda&N1`PHv5Ul5{gMvigz^=F; zBHxUfED(XISn%DUHnz4*%qEDmYPMiplXlKSMK^;|^}*9{rrIY2M(X>0>zbB2kA_s{g5;+unl#CL&=n^BAi5W(& zZIAY26N=I^-(Vlv=IeHe(-%&WzAtbLqpX!@j(S2WDaGpkn+x|Slh~R$BEN-=hZ(9C zsj?fR{N1G|`Y``0*&^%kDYKv|1%f_8IAP8^wa@Tne}(nYsp1>r4*93)7o-k!+}+m1 z#GquTirV|W{sQo9!fZ+oe)JVx+PZD3^Kb@S)NW`l7DW@JyfU^-hSQ-`cuu$>N;-;S z*bo(aa$M-H%FrL%k0ic~(9n;fogh)}uXB#(B?sFfQY|J=%$;~v3*do-U#mq@eu7|w ze;=a9Kl7AUPeIaS;bURr-%qqJWO5O+y%u-ezbD6jv@%M>-H84D2gA>b)7my4n^bVD z<`6D#Zt&A}NA7LZAvn+pFP|cchwQx`Nq#HwVRd+3U1{yHKLRhGi1D0cBTyR`6=e0P z{mtF*qc)J-&xH3wmx)s-er|e0$|G9!-dI&D&M9I}b!>0}6{%yCNRL4*^;(n&g4FN@ zbX@5T1AHj$>bpc&SqGj6+9Q`@Oz^~2(hHN5ax;aLjGT$j_S6PsDrNS&LtFtk=VG$GZC zng@qP$zHb_=dj>Y_17ZkWfY%Cq}d_xfwin6G{8_Q^J$?%TK&YwE6fs@u>un zsFYousG`SLUAE7$VMrFe;U=jKdwHwWZXOET{DNo_?VVk7=Qb{K0XJu(j2FJO{k6gx zN?Ak&Peq)+&j!yK$`TBI-7TE+dU7hH+n2PZlQBEiB=0(BO>1oQ$q!UCQ2mi}CzGBc zlhf9gG)jb;oU%ZT%}xNO!&%VtJ`6J-H$57NUVf3cE>zq^GPB{>lZ}> zGLhr8)NC5+A*W^(*5xcTKC0NdwvpT-v~D?caWwcIp5JTfQjjAcSYoxfW?H<0IS_iD ze(~C_R>2%=vY?hYQMpjQOGF{E6cI*4t3<38T2p8p@j-X2wyaFME6QG$Ebg`6c?YSe zP5z7TkbLUJ49@GY*NHpivN~&qOziCHA&SXL(Iix)D$0vEN{2$ar+kDP4N6HHx;&5~ zb_stL1;gIbD7(ch-K%7sGrllkA43Vz&Ddz8>O$KyXVrPI19g%)=bzL4H0VgjzJCRZcnj1RG7hPvk~ZpFp*Iyyd!CDoTWET|ogm*Zc1 zKjfy7?CqsBPX4{s*DQ0+$OgHra?qrF@s@_Yxbjx zDa@kKn0Ui^J|Pdb>peDJ1)8lh?lx)w!+77&h}ovCeN;+y)8nThXOGmUxJ^RC!Zc1A zo)az3C1%YWDIf`|SJY({W*bg&EwmJZshx3_Q|vZ~`OQSpy1QbJm42=Mcv`EFT-kUKiTNS*WiUHB73j^u|kBN|O3SMB|NBWm5e2 z*pdj?9=YTD()^dTb5!_=w7$fwO0kL72iU~lHIl=)`Q|z)lb&4aRKqM1PrQ^CHeQNK zszh;*SSUagQa`4)O8suIh)T3WM6UHBm}-S3$*e5}2KwtH$>;~aYIu++IF8&&WaWF1 zj{2^Z*v&eQAd1?}Pm{Ne%c2u{`{^{UdL)Xo8b@9s4tSv|?jqgtbfjUVv+MGa3dG%9 zIHEdhx=O%nzIlKPqBsjac`2C`RJ?Vc%B?jVTMM%nReYWJno!syeK)#TC0t?m4K9&= z?I$cer%T01Q7?}E+4+2WByI?Jfi||ZMZOO{J3tM*;Pmg9VkkJVF&L7z;#DZv3`?0}l^Z`#xe3|lDf!#I8R<|+S zuYMt-DnX=}@mQm8p*MzB#<|YgRy8@=m6K^<)Q!SFj%G!M}vU<~35(2l~O2EPTiy5;jQO|L8^d1W5FFgnZJ-bPT*w+7|qlZZcYb)gq)nYEi)aIa5J# zV3G`D6jRzu_y^h7--wi8Ogd8cPnYSmnJzxWGs>D;t%NCJc<6joB1kEronHyC`plGp zUrz+Yj-h9$?53wUmI*19=-CoRR*hPur5j)OMDfmYnHK5Wt6T864GD+A%3(IOpaf;)FmkUl0ssh zg-L^-RM2CVRB8oKp4t=u(UG`D91|!vpN(7Hk@6_6k}IdRN8Ku|jkaYDUI{VZ;ctX? z&c%IvE_k6M+#uNSdJ{J5`YbNxJfKvO`yvDToJw=I?tzjqK4r+tk6`GvOPvV82)_|m zJ#)RS5$xN@Rd^b;Ca+m7n*K{_!{gYqux3Y>u;vuMg3po|iTkfsf`_mL(NaC?)H1j1 zw&%&BWR96%l+2tg*rPovP5c60DyhzTG5B^+;YDUyoPM)QdLm)BsgU%Ob6R&D!S1{D z$qWH7*_o|f%{L88{1mDpqbusfP@K-cB$e ztBtxj?>^Ot=na~9pl&wnl?@ps_vW{8^QXs?+CsfabbOeUrn$XRX2h2aed4b}!)RK> zBFoPjCQHj1F+_@evCgG>W^ZtqSU&2p7yi{@D=;yQhZfybxSPo(Z6I}%KrTrsGMVaV zu||N69#6W4b+yKP?jD$RPx6|7Bgh0OCuhGgt)XQzl^}h#@K85QdB2^a%Aj3h9+2|q z8JT`Cr6}E+N@z6)JS!G?b&EIn>k6F4NcysgN)x6`xOvVL9q0Igh2>@J2XNLKqm&W; z<2Ej{#2N5HJ$Ui;nU{R#gUo=IQ6fK3WpEj`yzUzz;W8qTPi9NeW=m6MO6|QT&vwcz zE5P`sp5;vs3~{=|oXFSsr%F2Da-Tw6}EN0ImDzKp5$70(uU{GN_k71qLn^*>IHW*ht z1v5cBg@7%*L;+}I=01EJ0L7`Tk2=%)kSr-n25 zy*DrMBBNnJ`(#6e`PP~rhL}=(-Bn4{>bXWum;v6r&7$LhX6E(iryo3~hgkeOv0XEy z6|YoDMcR1W&4s+=u*75>><{j^r72 z7X6h1`Qy;BpvcE=NpRM|-pe!^Em`qlvhGKC$nB!hrhdIF|0V} zM6w!2X=JvxT_SvlUjB4W5n9J|JWazS!5YOFO}ItdMAL89ACm;IFX~UHK7^u@IL(Q~ zuft_wnDrKHRJS#%%OR;&35+mZk-ihAZ!`_lbK7KoCf|L-51}N&OvDSl8-1sWbzAcO z7>z3p*8=?lvdqzsHz8#v^gE+?V}k{C60{f-1WK@7WiGBEawgd5fY8fWH?B6(xHcv~ z5};p5D@;@R#*1c1tcI0oUjM{_%*dxwGPR0U7;UKl$$3e7b2X!jwb_9B@A&`eotF0^ zgClILiJC;aKoB~%9`WO8rLlnK(9a4Y+15#k24u$K!b~aKeQ`<-VV6n~G0C{GH0U%G z9NtXHj<#h{HkKk}b?oAioN}F6)`Q94kktv4a+5$2js;(^iGlgfCaIJ_C)=%&rT>zw zbYwfuH9|-23m;e1V4q+gCVy3g>@FtWWGYwh0IbZg&NgK!6#uviPpB^!<8h#_R3Cpq z^UByi+lnsKI+p3h{p30!@S>0M?idz}Q7m7IqaWYfIKO(T2tdetdy#t^)-eMp7pVoT z<%Gg7vK6ryDJ)(%#XPa}4a&^P)+PjQ%)s=IR_wjW23ElNzpFrST)|I<#CmAI^ggRJ zKdvrLtNhbiWSXO+tJR8c9lxqfD%D4t+Bo>xO#n91FP`E*5QiAr)Q zO!|t6@9A{7N|VoZs@AdC`RtOi&@1U5kvX)9>63ilf3zN%PN}No=wp_T&M31ABi~p| z;KnhmTY$^NRSL-xa10Y$KX=HaNcm`#7Tv|q$57I{M0jBY^Fn)%l1NFT)_%5`&LMkd z^db1&(6eTy0&CcDon?w+Jtz>2Z{&( zVDBHPmIh3KXkdl>-@wOvB&UYB#Vkx-#K|8jda%YSXbD&sO;DQfj;qBPhYF%nK3B)s zAL=1Yoxqc7PLTuVI8(ALl{d*t!6K~Y#bQ!#E?sM78njr2`CS1JxAPY}Kpe zJNStPsVnRC5^q0wzR;;v9Qj*!E=*!z3A`R;)2iec99(>Yr%fKZ5Q*W@`k;W!?IDv}1DP()c|`I+tumK8=sY$FedmvS=!Z^-h?D zeWcDxj$OE!oNV(aTx3vPS!j0kCSkU&m_Q5hPL^c4C6XMmCgzaVrr*fY30Dl!*Lok0 z5=MUK6if1w{HdV`AjhrvK)G8U~&ZoIcuiv2uH(ot9})J3Ic0>vhBt7h^Rl3 zKe5+3kqtnuEoG`=BU|8t3s4Q-7<~Z8@{i`mZkuI*7yo<$gdA|?ZCdAT*v2(N$}^Gy z@b_mB|M2%;6Ak?O9(~H|?VewWIvuzRmr}nG24eNHZr(5gIU%{TDilOiSZzGe?rCrF zdy+?KUZl#oM7{I}Nfn+-ScG6IvjCgZq90Y#IY7S3GT|!3Y+6OrY<&lv4!x ziwfERH!^W0Y~wlX$@&0mMt^^z@RIBdVAg=4h1i;(f<7I%*85u}4FbdYG(z?dqkxWa zYwDk0tjcEI_jQ~00{0f}SLmbkZv^^`3-aK9t25QQ#lV(7pZ@nXpiIjEq4SZE5mBEY zqah2i`TAQ0(G*w?^EELXH4%x z&;M%KhzO{XNMbmPSg|sV^il(>??pMAUMe;Jy!p{2qcDz!_p!G?58OY#%;hlX;#A5v ziKE3xZ~E)FB?6OS?l8`Nda_&~qD!Ada7;?dK~XB&xp14FD0}euUA5B7IHIK*3gP0a zWeLk%XaJvs75Lk$P4iaID-Aj+X2A~Tnh#f+AGHXT7c@Ta(Zu#R42>0^i~h)`ewj*X zHYW~g%P>8d<(+n=dos2hTIea0dWed@N>Vp3+`Mqr{EkW#nw@~$1NCbcp4DwU*_8RV z!aAGm2&MUY26=nzQla+n^%N2rWc~=kcX}rOO`J6JC+*cMCgJRzi^!(Vb{Sf1**ZW56AQYQpY z5Qo?`<{|Mr>SUfh6w;SNlD}%}hsN}M6_fNCWs=w94QZ)Ihe&){`44y zS?#etuf`%BA$FrLbnt7i30AINi?XD&Z0A)1d6>J%sj+u}&LJH?5lqrr17W-E$Ff*~ zs}9oXBIz~ea#mBmTATI{V(!I@>vgsIRXPcK>so3($sgKn;}t+5K#i5It3K6ol33x{P9JRY?&V!jMpVoLpi|+&s_?Nro zbKRD?MG(q7!&qR5tLh5;#CBj@E*o%H6sQ%P| zGECyFc#K1q2UIhn*h@Evu(6E;(ph{n`zEU!gpw{aZYYm_Be3U#`EshiCF(%z&|pX< zs`d=4i51r$rpXhaW&0A0ha4GDiI|cIuu!^UO1D=!TCWkU8cVR+rl$?8;U;!sCwz+& zv0{>=Y1>QFM!!%3n#2BBy5x<+m1gZ4!W&DggnGFY81#s@Hs`gboIrjWTJu~)=bIP> z)bs_q#?yl$4TnR)E#9bwncGu{q84=gQ}{cT_9!1v4}mTXoRn^Ik^OU2ZuM$5YeO_% z<~_fqc&Q!wCv^6{8+#$!jo5<85}gA1_R4ug7xr&3hc%5NfrhiRGuob6?ByCM5ClgK zS;C*2oC90ior{|PV+~Gx(FvnW{Gx#^QY7a)I9A;(rhi46>~^D(HN(1KFFm0yTStbR zJ&1R*?1MkO+bq%w(bWLiTyW!#YjgN$;A|7w35-&!yS;sNHK31&KL}A#RmO>$+dj(i zTFv9#hHk6VD%=C+EmGq&=I+5h)qr2@Fd!E+5ci-24`nJmPXe2PM72GPh(;_`Su_p1 zp~KmN54wx$Qz{--Vez1+!s=-wb!_@;LP!CbICz*$H&HG%^KT!1g;8rqd}ms=k+ajc z^@7Q;kGQ(L{o z44b{oP5W`vV|cA5V>`yjJ0-cX4W&C!T8itp zpx}^&KO-P)62++?U>$ljwzht93)PWs@^;Lh-+5)0Pb3b(gOuSgncN_=nRFvs6Fb0!t7G2Td*_uBG? zYDVGl6>pr=BYnpkBb_KKoqK&7cdfG&SD^PBAxzWr)%iAM{5|xaEo!8SABlfF508(*?VDh zJrJ(bi4o{#=~oMD5ZX;VEuWskvystF=SRo{;RM~pg<59&4gE&VF1iC*B{OS?8D z`D(~B^h;#6Y|Z9`n4_Xs^GdpOBU-{pq0tD$XwILAlY7f=mb-Z)v0HiYn%{5Qaq2mD zRPd&{csVGi8NcP?>Lgd^e$Mgg=E8t)3eTe;U7Qej5eMz+-DBi=A8jZ0PqAD)#5#=8 zisej$MS(=Klw(f8ob(f|(P8djMp%5U=q>A1<=}SEls^k$vLsB<*|!n(mo`qt5b6NwQ;`0(|uvszuLLsd!I zmIvk$R7J>3l!qQm7Na9($Ewk!R@x(ERBNM7V?(odGWot$(r_> zD4{VLuGb-9&A34ZFlkhwRa!x*D|5_JYMi_ogwy6uuVhwN1`YVeu4PrM z;0Q))ojUv1hL8H8w=#E#1G5w%S^G7Q1$sfpdl+@<-K6Z3g%qseh&nlY%@|cy)V8OD zwRSU)Agm>)Jka@_O!m8lS{!ClADI!GA-7gSg-hGWCE4UMYBx3#5RH455;?sDr1Lid zs>Ih2oInfKVi%&ibsc{cDe1d5dLU;`5-D#RjH?TC4cY}U!)$Pbp_W4*(n5;2u%b9i zBCTv%g@omiIYcYMg)$c&HW-j%>TBqY*Vdnk%pIKd$U*pEKf=3_W`geD5D|x-7N(w- zn$bWJeOhPQRz2EQ0|pq;m`TlWOpo$BfDn{9H%2rgjEjL1BiL8QjU6ND`6J1(#%kuw zvD<=pYz=;utdUJOk|#H)fti7;DmK_1Dxc?SSLnWbUSCFQ`sgwGer%onByj|0#ub;d`CIa%hL8|x-0E) z!9nj`<+W|=XpP>0-0BM5a6_&YjEg9j@GBR?#xzz^p{j=7c-u02EkdWs;`9C7gUv8g zjcjOjFymc0B8Wgs{KFXtS&d$Oym73X^)1FRVbh0+Y|qyQ$3^lRbuXIGUxHEN%IYf= zvK+4sjCx+k&(yt8^!3eyQ@h3n%(FkF3*lG`h{%Y^3K+gM+zo@L2A1fKoeA-qfy zZ(XxXEr5#K%}ifqSK{!IGthwV+Vu2q*$L=w8HRaEq3x6M?>>?QhbJF*6o4=9xr7(p z>lfYm0-z4t(|<;*E+B%9NJ@Nu@wr}~il)~^aQcPj-=&cNtc*Hs#ij1aSu6v|Yg0iE z6qb@N84t2w(wBco#6DKsv#%eP+!Y;1yF@!zK9&{0=qTHNBedWCMzC}#-$1PRA)W+L zpXY^!G&14WrckzKI6C;Jx0NCwqP=jSw40)VQhY2GaS>%ul;l`wieh*Y9C~m=@`zYd*0~js){YR6{wA(_hC0rzlj9UfLp%gppKJuhm3xQXDWWJE z5G|MCIBAqE|2&1JlTz#cMoU^<22zoteR_Oc;hWztQr5VV3oK z&+7Vk>B0rS3`d|YHFPaJRk1ojQt>>Lnq;6>!Sx!zi*GjR8THeZRIym=LM{%i&2Cfu z@a0JfanX9ZNX?L@pjkOIB2z;!7_4^N?eq1fEf^8oJ1SCC+JLu9TSxr!a>={XkIOzT zdoCPb!aMet<}iLpcVfSs+z2_0RTs;C%P^;f6fnJD_rtG7re-ybS|v6#B;Bv@K1t}s z+aktTM0esr-CBpn3k`wO)D>1(RZr@|(DOOsJZF7PqD}s5d%uua&To@^|z!wWQ@GN{YQc*mm~2KO1Yr#cPL$(v)&p{NgVBn ztp=3)R~zZB%7Ly8D@snRV80Q1wZA}1#WyMh^NSi+B*C@OJ}@`zRJg;#S6b47E4-W= zY__%2BfqxQW@}b?F$?>M)4XyEbiT3t9fT7@y*%Q$?oVph(h1)&sSGftv@UhECu>U& zFd8X5IemkjLT6PAD5PYvj%JXac7vv0b_CkIvVdUda^_s3Gb#$N@^ut|P%QvZe0poB}V z;jrqFnzO?0I^EIfPee5)t&UvmwGA7fr=uTDTTfS_!Gyumj*eydDxROw)u=_G zc@c!A)Y{_1_eL#z6Fd7&O;~TV6)Nv-qkkh1Z4UI$tebbAk9DZZYCWr#3RE(kxH1<_!jczu<`uZ(0bN?WU1uJ+HOy)ZD&v4CvyxK)Ic;!1%3MKQ#Qa5D7LZ=r+1#+T0K17W6(# zF1pphw?nZwIxjTrtg0oHV$|JOn-$VkzgRj$CbAU$eT?c>ge^#<+nLqOM$~0|${C_t}rpO^<^uVIMrhAIrhc>#t~{!mgBX;RY#hY5i#ey}K7r zyLl{PC>_Syp^21wYr&ay zSCO8&schOw#89D2*j2d({iuO-zBX89r@JRW8C{;|C=6_11?N$Pp|j z(JNRHJK@3P$x%PFIgUZ$X2NTWTC*djao3V0<@E}i#}K6|R zmR+^q2*WYX@AMkRlNZf2IYLfJvEv3+`f8TB;+OIQ)rT-8q8!W?wD& zB#x}hJGtO=PRtajdQ>kNU-sE-{C-5yzb^a zkhw$=MZ6FG@UF|_jJ@EB#&AO?jS_rn#`eI@;H8+aXxN@Gnk`sgsE~S6C?X;rK?7WL zERIPm{4{a6>M{NERhqf-JKmibqfIIIO11i3j$U0UUu zCt^l26*<!V%^9CTE%sG3|WCZ+^1$f3atO1Wv`?w`y3yy6$j z*99i91BK0bFwmt^rXStYhYi>bIZ-UF?oAV8D6SC?%hax=M7Fy>|8Rb-w>)5m&mW%7 z@bTfNx$PFHNrt$#VvD#s825EqQM!|G=cUPV^L=gk9KHuExMlyN`=NU<*>42R=A@RG zKSp2;cobIu3jn2Z*j?ZU%HtN3;}-HirNcx#W7r1v2c0Un8v{PnrjQOj8vIj+0c!?r=Hl@cCO@ugpa%_XGoWYjgX4K<;aEG*<)q z{cF7aYu@-*HLV6p9F@0r0@i~8$Bt=iQjJUt<~HY_hWfZg6SgBUl6KEU3x5AeU$Q60 zr+np1pbaj&WO8(F%Pjr4o&wX?Y~7VO%Dk`f@&B*zxOZ5Z^Ns8hkGEt9pVj~3N(4{; zQP}~Z2Vid8l{t6*$PQmXjKj9BIX$4b2Sn7~xt)GJ7-+qf9R5>S|6p#7;(d+5eU*6d z*0XBxQ5L|L@-S@-jw8P5ax%eE=7I<9({k>}8 zUt_-G&Le*@yv_NKcVX~5csTF_F09M+qovh%k8=^RQ}g{$dr_}={e3OGY>H*$w~TT( z&UkrGH})~PB`ATDRNNOJ+~g&Pn}k<`r`d+*iGo~~9P{sSv*Q=_yV_uqZ%%~FtV-G) zTc@cvQ7^@Hb0El7+$6<9I#7%Zn_mvh)-_iD+5CX)!)L|nxuY(Ye5e^_i2kg{F}@3GdczEB7rV<;D`S|d zo7xdO^bP{;zh5bra!L(vLonV`fykjQQ-3%QX&dlz_rCXOcSh&PtIiPDcEx9=^BZ%6xt&)il^3`MpUlU=onIeBqZ zx8|gVlZgJ9xl}LN3T<}X$WeJGB&Y-PH9?b?`t19>7_6Uad!b&bsozW7z9x4WW;*d9 zUi}zn@TCROVRdaE)u+VBbDX9+4Y9Mm2RaU?K&gTH)^d&1o@&W&B9Fb_H9JBd6@r@z z)FaQx3=J0(&+8xBk0nuK=7XM7^8SJ3zf@OGOR6{PWv2f>^0u*&hkM@qcgH7v%S}S==|m^A^i3P(Kw;-2yV)I^0oDuwZLx#N(yLDDaIN|roZKB82t88C`3Ja{^POK{x&10p zo2Bo1L&2~8m|xZIDU;KnkIcYofss}b;x@Bi5!s;R4bryYP8iG(EP^izH3)5e?8H_7 zdQ?%{Qb^oaaY|xb?W^Gx($&*3pa&V8)u|SG2Zzhz3$FG)HGF3g;@r+y|G& zbSx*Vl;o{}Q-U|4lqn4r7$;|+t9xRW9bx6l3D2UN^UEMI1wLMrvJm&-75AUad1C9Srt zWCAS`89%v%y`~BkD zZvebnU}K37{lh_!wGdG0yzyf=B~yF3}% zif6Fhc;XJm9>u%#Hv;e9`c=?S*WVdnH1?}BesAvgMqYqQFvm9|v_BtwZ(5v_i}HnN zhN(ahNbKw#GHI`!pubN@{c116LdDXyK_f&bjss=o99qj-Jk8!fnE|G-!Nsh<8+|gN z6?vdg(qagN+ypD>x>0ZtRH41!5Bs6EOs`va&ZyV!(43Ajq+W`P$nlYV<5DJL8d@RN z!k>2lO7ym}U);V$~xoSIk4Q3(FK)~z?zGnH3WlZ%NjIZ&Dzz_cBMohy!`Xfj} zl#cB6H>gC23sxxQN+AZS77u;uWF;-^gc}XCH|1d5iN6|;rqkS?*WHXxW0!V_TR}H0 zaA$$ya4ANsU+?_BIFyQ7T)&mCy^rscyd(tmlf+h&z#ECu-6@_gUvz7Q>RLFDp`z!% zikHkxT`Xjk5ct zWBXwq2>TdhP_|X??4BM@Ye6hSR~3HT0Oq*?h~K+M6ZduY9*rrw5un6jD@}iri3ax2 zBgTKOnV<;s)glvAdl(Dm5mMMihOVNu+ln%vMOx(dp?!%jrCf%3JfxcPFkoSU0Dp7pz%x{U%3S zT_k?e#a{a?@Vq8^qV)Qe+NSW^BN{^W=Uh?DF7YPOqJ9ilhsB5+Fu+A6^!LjC^>53a}>#AiYE>|n9*e{VjNE)rT7x6k2U3wm&+Wj z<&d0s zx6y@`w~sAxIF%_Oc)p_ip#iPg6^74J*26kGz0|9%CG76SfxdkL@a2QsQ87zX(&H-gY}O0d1JLVhQ98zcAuM98yj3Q-;kkB z#x6Wh`WGw=@aen_c$-#9#2A0!Qo8=t1AyM{UkRKr%ld0y|1rS-Qb8i6Yu)wUY|#3X z+;pwvB7-=plM3P)K*X!O&pONv&nNjYQ>OP6=03An!aS5%K6rUkj#B*{mT^dFuj5b+ zfs5{u^6nX_=@C+Iv6#bkp9>U(Yw?vf=IL@c_$PF(9i7-qz0kl8UQC1#ZXTL$!b!#F z>RLDs^_=H18qIa!vx3K7qwc7UCRm>c?NW1z9V#*QN%ju)zvl#l9kjW8{iyLQ4y##9 zPizppWLLF6=hfsh+gxrgF?`$XFSN}ucNg5R0gItIHHjC_1QCxp=M6_jaV8sYqJRV| z2&`l&X?1EDb%C@neVrK6Kh2R|<2$%vB3Vfb|I{Jdzg&Q8iB9GBBT2EH z0PUwzJsbHUt>~cXK_Y z?E7*%0x>8E5p@L`GIx$mR}|o|Q0Di-=~b-WN9M29FpkGc|(0Tkc+&?hqFG3*mcC7Kk(y@}pV26-0q-n1qn;*QH?Xj+ z6slKPKw* zfR_4=MCsK#$j52(gfgbQ0BcQjeGE%FZ3#JRWoXu(|G|}Xk+*JDj^ksztIJGIOOog? zHqrW%zRBFBDHHhfKyQJt3uU6^(}Yvm0rf^MzxqS=sebMraD<$Ea4-?$S^7Dl65fEso?; zWOcQo{&fL%@Bm(!alls^afu6=$QdT5a_h5`i&J$`0mB|Ad`|DQIri7PDdda>QTHG|Q zwU)EC{S#ik6WJ}D$oOT;G;=d9**55i5mK0DGFd;RVw`M>ohUu2Z5#$&*V>BHD7V;2 zNy>Qn7u+qXx=)J)$6XNibNk`GIOEW(Grn ze4~0S^ArR*RobZ9mzX&a43hGWC<-f+9h5JIIE0u3${MXPC z{A&r$qmy#B$S3WEq$mm(Az6pR@JtwEw^J}sj*H3@2 z>>nc43lERaGtd*nVD`HCi+w-^pxW@D4)rexPr<4+*TY)HG+Nm4CevGP{a9xC(?mTU zrn_pY4FKw*OkG$Vr`=qNGn(6GO}=xx>XbO)Y3KsoGMQ7C$v2cWyV1sW&F1Q#)tR61ojBCXy_J6Xi%T3~ z0mx8yX7z?d;+pgSCq^61*1xy}KvKGU`g;IC^hqSq($fJih|2KLKOS13zE65;7vH=X zp9vV3vhv~Ki9PWlx|cTs`hiATK_cr|#j;kC7*7vsdUSmU5SJYVly!E=bk$+izM&7m z7AF>>1s4~}yMMb&$kJ--=p2B&H~-kN!B~Gh&xzq3ITTrEuiW6s+f60cOE$1NUx@RkL}(>&+(*n_&KypCT6oJM8#VLnf!@VI zVh-whTlORE&@a4$wMOV%4>51bu%{b7uhUp(T(wpcIL0q z9le%l&aV=>KYU7mBCXx#9`-{o;p@S))gcHTZ-C%*+VG_Cx?|Ecw+oKIuWL-r6R$56 zfm4S6#u7nIS-)-`=a2 zs1<*OS3LKbI_;Kd=CO9P-oHnC@qg#$V?FzL@RgSQ+;)F5Eq|$0A^7PZqc)!_iET% z)f=?!_OBr1Hu;WsWCY+HE1~)>VP>ZiMUk;@v>sNp=udrYu{0Bf##tnf{3Egz=yKDm z>G1Yyole(m*_!74h`UdgFVJ-kUA0`HR3=GK(Htf;1%u&787D=NRG2Bq z3#`~H@ma+V7AvVxGNv>$4>fRL=A;+Lp0tbGC%zUogsv~S!>c6cSD0JtuZwe(O`WD& zaqF5=Go?=*1&*%V6 zPbEH5_(G;!MN7c@|EcAZ27NVcl_NTGW>Lck`io@mKRwF0izG z@;W*!>~ygR#WLRDV-s2B#jB8dLT?PGSGD~l*7~L8Xtz|$X>}4phG$@;qbK5gfs*p8L#^#TL@=IV-b;NQO=(63$1}zX=Ee6V z5=;@t(vMr&>TzHAEskhzYMVw$qGW~UWpW^E^Nd?Bix&CIp|A}x5QkY8BgkHD+BI6C z(th>U7_Z{{7_&sBLv#XYzN6k)wnfEDf8Kh4qtgFfa%my6p3P69v2U%GYl7dZD`uSD z7_svVo(O@g=4gLn3JxUP90mWk>RKqnRIWv-)ceXv!cI z%|(0zU>l%pWXn^tTt6pJ#_J7MzNfu|Rdj0`h!uOL($KRLv@|DAVI@8t8)j<#?B^`# z+aUz2x8b5OO1MX)xZFsch7Lh$N`mCr-lcX8u#5oHC>RlL382^i2=lP@_^|btPL1FE z^c!#B9&0YZl1ze;@)}`r(%XDz+}L6|X?QUJok{P$ko5BE_aAOmyH<=}+A}Riq@zhG z%ZbvtCZ!cv^xGUr$!KoAt*r;Zx8qx|7iFKoDmC0;y-2;jZ*jFuK;7xNR}06p=}~%} zQ@&Yie|8bEXV#k8OxqO^6TVU%0>`mNc4$Q!5Qi6G_~>kCClBI;O#+9#}x4UdSi)BmcRblJACEVO3?K=l&N{yNA^fNqzwt#9xpQa^qx?5+#b) zlr5(bgu&%X6B|-`$L68*^YJ!3{M{!2?vXkTu@Ei)kkuQBZ#CmzQUB+<2Cjk;!xO(e zJmT^|qV3LEoUM-$?;~E{Q$SN`QIF)a9 zm6xz)if7%`r8?C3$h4#1>Bkby?V1>(s^L~)yupai=K<0|$REIG5@3%PLKB}04cql* z<7?sQJnDGdMDVK!Hl=OFCg+&%Jw~txy^#KKpBIE~oAQ>;HFV)*lDI-lkk)JOrsvGB zY#Cql_&0)bfB)MWt}7iWnjE&f@-Njm6}-f{MxXbsl=SP_ucLC2Agw(vzM5`!(W4x{ z5y~Hh9^wX%tDuWC(Ou0bx~>#58~a|GZCC2&Xcv`eGY<`Nmm0bKG4iDAhtTFv3&*#t zGiTpL!4*IT?-SkAgK&jaq{2N9l~P1TxqPwsG;c59AzI0bxmz0uwC+vfiLic*9toE8=83DX@u?D8xu({9NhoL$1 zdfF4C-Pq*p`P5%qA)1G8h#vHGrCAU-$`3WqaSy`w#EhPlf$Ew_jfo9N2Az?(YSlkn zHb7g^^qdj(XA3E8GU@KraQ#Npcs{lT>peZQcs^P<`2xK;ym{p_<@-vdP0Jm?8ay44 zEr#w1UGsL@CmxIm7pZ)+eI#Umt~JK2>p+R{8{q;1TO;fCiClG9bsjI?!gt72{*3_h zvOyC`?VgM`cJ$(3X(+nV;}ENf$^R~`TeP30YPRQj_KFf4Yp-e1@LIK}*;3--WSez; z(zd{A{0!GiV+yCdov*a7Kz>$upRq3gp6+q47lRe4_$UoSvqRa&c zBP2LH&22CAsw*~tOqfM*YHjcxNO}5_sm&QfZei#?7Pigx?Qlw zNt2JJP^5X5K3}|rc(N-(SC>9&MExg|*0i3b90p`pLUTtMun8KjvcdD72Fcbn>)hMQ zfe6wf9Tri#{dh)OU`KleO$%Z;2mYr8|v9l-Cf8Wu2XdK zt?q*gfW14qKe<;)m$f1N)n;NEdVHHy$c&slq~xIx^(uzZQE1Mg&pv%X;-=sj3G9UM zaJg&MKtdKwsHSSEl`A9m8EGv7f30uLgp&RA)3V4bswtEu&&yZ%iK6eB1tK!1L0z{B z&_=}Ws{&8ygSrpdA>|*4u^+8XGffOK(hC`k4}4~~BOk$Xdpb$HN|-gj5%l|0ni}I9 zA}=!MqRMbW3?%nme$}FGFvaV?8itH{OaF4{pnBx|M>1myCIWWn9|D9qZr7~W%>^tp zWQH?e?vCqU-(wB@jF z?cQPqSNp&F`N#9B#nCDbN@cZ<$84OXQUvtLLF%;jm4MmevY4yp)m0asZ>KeWsjyDD zr?$Y601y5eZ9gEUHJ&JnKp`z|z|paz#kOV?=&@wu<@!DVB? z*#Gz>|5q1${m(u6$2khX6TlE=*Zqwvrd`&>ecbWC0QFxpv2pg-*ZHZg35)i9aCx`t z04BF2^~vX#!;DQ`$a*+WXa#8I_5&U<8U}ogkpf8y6SW_1@-hwo+M+0byX>DT@yk(@ z82^73``@O2D%z(l^&F$OdW_=1`X! zKrR+0mxx}=F{X+VQ1}iP{U|{W!sII?=Q!Kg1@>g*%3r)dSi3itlYS&L8uO-C;%0@_ zm4M8PkU4hc)Yv`ow`**tTeQwT@d0m#O6%HQraQWM8yFpj8Z)Ik^g21TOYwNLa+u`4 zI2SbB6%9!JSlguZKQx+`+!ez#9-v*6b6BK7v&$1wts%CtIs!2vM(JW|2~rbIGKdk8 zi5B-$7Fj%;F^@TJ0`I)FJ_R%wZV^-=PeVvKX5b1)?pr{@k(@`LY7w!VX)ntCtPGlm( z^Pi*gW>vd>4SlbhYh#4@w+kxzeb$p%fi_uKYSja}?o{#K50w&!s3}hnRHii}%ENq7 z?`u{?-j2t%-i*mhQ+j0W4|sfa9nfUR#imx^Gsy6~mc!+c{FtrnaYfLw)SbfLEk&OW z`m}~gJG*Fd{Yd-Vs7*p-8XNLIMe$5IQJbdM^s1 zQYG}>Yv`f(uJk4-U8({C2BgVFR21%?d!P6DzW4igGER0f&Nw+E8Dp(I=bCG7j2lUQ z+joKZ_9z;^Wyz0hNlzb4bs2YTS#J@X(de0|@T;^8ZrRhiQFB>gO+~w-hL=wEX(y7A z09j(?PYRmHAwEZqVM&?GDhe(tM$VXzpIYk|=3Q}E6K4^x>ojVD^X~@Lz4)+{|HWAL zVOXFZc0Aj_>ZMQ2Q=fyF-fRDciUrP(1Iy;B&G!AcH%E|{)K{`Xr|Vz$*|P#)Ih!z6 zaFnKigsDx)FZ1D!{t3dbUQyE1kycHdzU_HZDf7hNX4k}Rxh5ppI`!F?DfYmxTsYG& zrPiNlch~m4@qTay)^znv?LO-cbd}$&1LBptKXhw#SY*7tLn0w_m0F-bAJ!Us`m$}F zL?ZSOOZr^w>>!C&E6-ot%SB20=jU_0V)fnGKhHY!&DGnPldXKOCx5{%f6}BE?U;6# zZ80@Hk6-tBS-Ld$>wcE_nNU%VJQx%@49Wwqq;cCTo;+AH%e~vImAWrnAE+7J63dG{ zz(H0r#7~Pa=6oh$iFQs`rrbp9U7mK1&$3T0M>Y;(U&`1C6gbQSigZay^ZTFpy$BjO z@^8A$eHa7Jl_pc#u+y4199?{^gf&}?H35H_16?<@I!8#Qx@CV~x8amexn->CZF>|D zO1yJSs=2Ih^mXH2@9Tz@r(*Bdt#Q~7431A93-zoC4_EWWVy&koqTX&j^T2qxs*HAj z6>hhj9y9xkev)GNn?Lt`F{WVDuOOvruJ7YAckaHCITAp6{D^j*YO3C?pR-{AQ(0~C z15UpYDiK{6-hD}5=w|xnmarC5*U&vFy#EpSnVrBW=srW=3!~a*1(gvHc+(!Q^BU7X zRU#GpgUk?KF3t@{TX4lbiRQrsj;5J&yaE z04g{z%r0oUi+x&VSt8j#V&^)gUvQ~d#PlBbv1s{m%X{fre@5JF#i4M!_4JtEzDv!- z6Y};bJp(eWc}{xsSgs?Q-`IB^6dSJu&8kN9lK&=1=G%S0E5R)HF9y(RS^o!d{Ab`l z3bHjf{jf_-YP{DJ94_}S@Nf>m1GyPyvww(FOH05BK&)!K^*GOxg71varTTM1(OE0S z7I&W$y$TDR;8I;ka*C;nX=BZ%93MP+lX*Dna_wsU(&HTCNWqW< zd>hrnd!zm)9M2P%Tocw$Y>OBl9IifpaLf1q@}VNiQp}KPT(5Y6l6VRCfA5t=*y4XB zB|HUc@kj0-)QKPeyUh1b#>Hz>|E;r4eArGdGApdU$0nMjoM~~K63e}yVmjlQg0k|L z*}&^QjAX6aQ;csZV&?5ciT^=M|6xZX{{5x?0ZeCqo&SS<{|lIc_wnEMcqgQ3@H9Dw zTImO@n$Lqm!It~zL!9yhZ46aF1J`o91#L-q+d`nHg(Dj&j*eN%sI z7tHUwz|pe8{JoIhIG_Vm<9@&D{O7b|BawojMjV5()wxz}sh@3}&1_%mR_wQ@n6LSc z$b>w=XKEkc%4Sl3d<$j^nFG+->KnF*G)*LgGk~^AaFSBGihEMPA)FS&bwh*V?-fjN>=<<-@dusEPw3tQJ{d0G3<3U;9(`py-KTK3N;u|+uNAP}Xl zrc-*_-0eP)rL2SHcK-Ye_Q^}t`4Q}mIhp#hbCn#8DQuZ^ZTS4Vu$;KR*p}qF6XvVt z<5HBGSMNm^GSOXP2`}GD5L3x#c_rLFw3CLx%Xq64Cy*wVTLArihZ1alwR&ie+U4>6 z>mz%vIR>^6=oPBYpeonZ0Q=Fw9u*MVtw$Lyr*Nd3CC?(UM518AzLKO>(-YC#)mowM zLwkg-W#JTz&s?|O8jxEGN@6C~uOPYR-PzFZw5ulDCd3(3965YU7?pA1^^{ zvi?tQFc^?D0tYX~HqQvZh;>uNTIxvz>s2~guLP$~+%^7np`IM4S_yo}M9292=)6l)X$tZqG7Jbx!<$BOy`nHHDi7Bg){pup4nb6Jv-{5t0r>d#4 zhN=s7a_9&J_bsqLlq&v$auY0tEq#smF64!x21dUzi8gM8VIimLL8inbUqb#e0>^5~ z2>(s6jB*?eygfgQ!q>I@TiebbpW%ZaGjlz$>)d|=w|~9pf5*SH5n#yHD8Pv}p`=!% z@m(GEYZu-Xsz+;>=aQG;hpQ67|5;4;WSjp>$o(5A{{_I1*67b876^Dt9h9ab?VYg}nB@JM$u8$KD=anZl3%)0{9qBl=wE z{>Jve8o2!7V^bf4f*|&H=FAO$Hn^@)421qs@{QuBUH6q-ATkB0Gp4K}S2l1aO8l`t zlN#!8f(^SuoqLPz-k)3h6Kv?jr5@j!mTz&*S{H71m|(ePQ|oi*>7>6jKTws`7k%<9 z*T}tHt#vD0S7)|65H>Uj8EB-w3qGXs38c0Pjb041Lz^i-oa+eFIPNkQCt-|S2;&(_ zwD>HuNl%3bN*@c9_s zVL{5`H0Zj1+PKPjbyNOrFXjZT@r_D=U%@u)<*KboT93hI^SZ3LN;Hud(O{OJeTT0E zzM^%EWnU_h9I1L+IB}=dX5tR6&^7?p`rrct{JRaM;0$r&AZf1{ivo3XDfy*UBXb6h zK|262{!Zy6Tw8T+S>`Kg-mtUwO6Q(v4W4RWVOMH+8@7)Zu*s4@`~pVq>akf*`_TCO z@O|U;TGO#nCGchY0qcffZ)av`QAz77hdnmLOXL~(n=tZu$28f?2At@Z!OrM7(v7;&If8K7mg?V@ zOICOi8)FZ)3|L}o^&`3znLAgL%UJpZTg!gG*zi8TXfX_+(&+Fo!?$rRn9{&Jb5nU0 zx-^TJ==Ji{1KBejspbT3gIQ6yE-FA*)sXhr=cXom*!Zd)K=q*^@uoPgwl#$;Myrt% z(iB!j`bSXVh7aoVO+UVW(58M?vg*g<>~Uq|Q1;E-co2 zwebqIF$PHzUpaELbo{8!il^&vy+?1djlEk99;_U0o*k~4lsnol?bS69u4m&4Ze*DU zT22c++ur`p)xZ2H)!G`oA@Fg-+w(b&ekJah(TD3a|GM#Jx`xeog`o;-v8%!|`=VD| zj7IP;u^qZj`52pB#3{bIZ9V)~NRZp}xYI5AE_2`DjoxCaH0h(RF28G`R23%MAhX^F z+u{t>&SMetvW+9Yd6Gj(EF%0qg7GfqB7(gi!%c)(#)~>v)dQX_QWCH_5jQ9~+j>~z z^7Ps@Lr=Dl4-5|G32DVKi$+1HLTlPWdG(%dq1Sc92%M4I`H2`8KT33={I+<5YlP=l z^@HzFxoCqR{UA(+mCCgE;k;RhU7?UkbD!|VTDR7C3Y}heS1WUhdR=}}r|CS;o=BH? z@fJgbXUiJ-MiocV(xpK++*i~81NB9W)~oj92*q!Bn*wLWZzfO7cKajdqJ@kLw2}E9 zHXpivp|%DV_Zb&6A+FbRaP5Ce>@48neG4SfJyQ^UxY>W>C__V*HV3W`{w823U5jcX{evwTdU})52kEyv zso~_;SFvu1Z#uF<)|%WHnaWLV4R|MRp6yutYP)4xN;oN`FF4A(mUFKy0qoGeY~<-M z&ro9Ih<017@gZCK@Z^JLBxManXtiv$dN4~4^U}Mz!9$Ub`L63CFKX^dt<0gDOnpWETdaoY)`GpbK+X6|<9J z&fL0NuRHSG@nQxG_|2M&A z1@?LE4=QndhOB(zrSOpBVQmKo^C5UT^wPS9<$aLjc$vu!hm{)_buSZKRldmHtU@tP zqa)fz#vrUmR;piriC zcojlM+i%dHd-ag*`wQ`!HzO+^ul8?uRY|UI-UYvs=Y?s*jfERk0KIvVbZxnH8znLn zpY?lHMt&gm8ptV-I_(4VN8Jdr1iU^}tb1s3jVkP=c}_uJoe#Kj93*1%7e&Ne_omLB z(Nu{7afwZ#0lg#4_h;W+jP3`Hew1F@3+IcE-cyd$qD{FdIF^s zL4M*1#Tf_qhc#8NG0SKc%>;AsJv{oHmlYevXI8Tt$Bg-$UZ>#1GeVL(f!0&pCYllwS*Fz5Or5HYpNEk*a6 zne<*QiCGml34?8dFKt*&lu?lN7johEceR~c(XJ62jv<-+vvk7AT`|>J4{+ zhx1>SmTT$%=)wN~Ls_~_jnwcJ=CBQeCleAdgSP+HLjR{2`d^Iy@9Y0l*jA-{7XJLN zrmg+QXRn6vzUysK+js%h+R*vE*iE(GsM}fPokE!)Bc-6f30z7J&)1Y*M8E%;aig|2 za;~;`Q>b?L->LFH(>u3AcmJIKA-&0czghC_>x;pnueWo`A8txtH2s!SWeLS!+;N-y z({2X)i>Q>Be7CbHFYj;Mlu+O|Sa^_fY%jlfQ$k)`vfEv9?$-`}Uh-x8Y5C5jvsp4> z0yj^}SV~06m*aa!29!c4*xT!0*4tn|CN{=bQS_{C`Y|?-1Nkhon{L0PW zR!r@L*bDX>tj*7s)zM$%Q{q(*i<$y{#yXj`5OEO!=1t9dv)Oy;>go$?fFUJRSk$weX)1na?{a!w zcdJ@R)^kWFExoaFe8@)y@Hb7{35b?qjl<`+Y0pfF(sEjiFFwnTuBRa-KW}>N>xU&- zDztxHO&V?VH$AQViX5c}{>mx44c5W{sAQwaFYn-2`CV!)Qm>8HIT|yLQ)klMf+tNK z$hkpk}31+@EE`5?{F~Knkdi-Z$-8WVa|4^>S^fYalez47fO=;W~pa!nI-#+2gr(yND}(8(08NZxSbifIU|L;V`i zkQ~j%X2#@Xo>9ac%&usy%Hml?qYZ6&1Uc$)@@WIV&ExWZ>N)ykeg=?D!4FEEmzRjJ zx5=SQQcCKoIlH4P;3~tpWlh6)&?b+(8}PD0Lw$_YZ!oKLe1DiidS-GGpu-a*k@&3h zK$d@W(@Lt+X?8U{-cx{krh&ok>l`2pl{n zV(u8txKJ1%iwIMVR@i!{g~9nS2&q&hy%FsQnwUdM+Q`Y9b&)JfnG@fqqBo|3#|Vtm zHoPXfXbbKL^^CTiue0P5T5`<@MmiNs`{pjlTEW|kSnKs%$AEAbv2t8CYKkb%ol8q$ zSqyl;BADKqelI}-c{X{}Kfc@TI-IU+s^DNXA!sbgkp+*)hX^iNhyW;&>cvfd%8)Eq zq14nu;lMq30_7&{<+5C%S!J&VAamJ}n9izDT|mflJfT4EH5q{}H=lKJIfCYLc2Q=W@=sIxNrNj!`dm||3Q7z2A1k8(j!P~*K8N-W*lfxUWIWw!_Jyp7 zcSHY+NTd^MwU#KLDTo->qUOr02yXB-Tn40$-fz8P7IohLMcC+No=k(UEy1$hK8MvP zP1yiEm&$ZEgPA!_PGt@y1#5}hZ&#qx%M>ojxb2)@uvdGC4Zj}3T4>z}jSAH57*Ikq z5dYh!`AEmY`JRX~li*H^5t@M<1+IEFAMAs%lW{Qej2r;&m>Z&`&(2cOx9aEbz^{c)xhFHwBk0zQ3SE1Y=fv?X2)=h zc)*tqNWF51Cg&lrDiT3|ulHtaKuoVD$gbW};Ts`Qhpwkzm;@w+f^JTrVmn@CY@Y%Y ze-Ayvc}!xkTrSrGKaK%N!@OY;ls`~%Lv)}NP~?NXG{sua`=!cBmXa1$%cdE8HO+a= zni{Lxlx^9x396fE;hb|StnazRDdV|`T0#M9Wy85z-?IWaL>b83q=MifX&?EO0n49_ zPPGh0;1?cQw7Kgc<)ltsTzk-Hf3Ykdi1{pr%aj%gJ(Y~V2C1i3!@5`Z(lA;|+-T#k z>`FMDB;(7P7mmilHDUDX>kAi3K~%Zv91Q*|P=mM-F)lX3)p%d_slkqCfXVRm#&{MQ%TZP? z2o*gRn}Tp{^85b2TcU1dsfMQQNs!|*$>?th=0tl?hEMYfa#Lrp5*r_N5Vy8B@ZG7r zY`sxXs5G{Zje>47pCy-EPi$XBY^O#OnlVxsEx4}~trROLR+AHv;qPCdRRCF*M(#_7 zWMhY6PlPr3U@}20vili)$>?B%h$Ughc`8XTvGm|_b9w+M851a55~ZrmFP(0moF-o9 zYNkNXMRs+E5KM$-5_)W}1`AJV`AuoniC1|S%Ge8qQs2=RLD}G2iBd{ik7Tm-V#=`( z%oYlZCR~=*>&f&oGU{4j(hOS4KXc~ihtc(M!R4ls_d?v&`>vZYSPXTFsnf19DsKA9r1VeLpfjy%TcC@vpCZ{Wn2+p?YT;nS}DYZ>FM=TGjvu z@tI6h*(I`gaiKRYw%Y)lSR|*Nj05{npX)462EArDnU4wSr}y62%VnC#w%{U z`IuAC+cNHYQF{*M*|L^dfmHs4jCwMX%J|qLwit8uF4xQikb5M81E-aONXw;nq6G#; z62Vu;wXON80AIstPeH5K%xRdT$;*g9i?H&K+2LXG`4y)Ez*fZ!< zzWv1I0Aeq$&pT5ngC^ny)I2#|7kmq`LP1LA$YC}GBB!fA>a}?Fyqm8vD0DN|$gA$g zNj3BPX@RP}R(vYjSf7@dJIN=wwvdA%fE&V1sjS)-~mwwWk>)3qh0Dh`{$=BsCMx|-7 zYLaYec!)9r*6E3lQqkWT8iv1cV>Z@;Pcx9gHe&RX(o6&iMJtAgJpi8KN~cN9rJ%6- ztS&-!!s`G-!nfr{RaTPVt!DsRIltzZ4BIq&cKCNmMXYN&!VwY68!i-WT=8+jRsxQi ze4_73c`hASHChQgheG0)wE1fnkUV*3>6)Vh$43=?Q#KJ|>H9RH2%{Mv+9y_6UG=ej z{#Grlxj5{vAe2?^S(Pqkh6I@@cn)ftq+=+!WN&18Sg26TJs%Jfd6DwS+X$dNvxAD$LRi$^>Z23 z5Pn|8`b`~@-7@UDVRwnZe(Sg2nf8MvFEh!JmKJKxzvy@oGGjlZOajGQi&3yHswL=x|xo>ViBbX))9}R*4K%ZkGK*e z6vwdV_}*JRnOfiAt>5CC%UjiSoJXrf7tK)P>X*Ebe7f`qKR~apo6}E1F^};d%X`k{Mn$_++WEC*pX)bHiEQ6RL=&n$J$Q8j*bJ4Bw{Q!>(YURQ$04m=1Ug}Jluz*ou zGj0Z}+k;@X_JZl6Lq^&0k4kBSa-JbwhUG@#2|%F1Kkzncf27C;XL#?&?WMQ`$R z^u^1N8!$5o#4vKd(Hh&^m)i@?G;tK)$FQfHu=?nX(AT2cbIVO|Eo-CN~uhRBU0eNE|O#+v>MK5TFy_;js&L;uC$aVT4QBGHMnT4 z%Wg;>omyW5t|`es5LqR;O%QVlS6>D%p1vM6F%e2HNWp=sRw;ra*!IrkxY7{_Lwg0H zTFR#$>)~lK3$=g?7;IRFv6ZSuc>Wl|kPgqcc_(1$wD@g1hSic-C_gpIUk7sPlo6)3 zW?Mf?8$13x5ksW9{n9iFlg^nAagu*ISMc&kQjWuzFOk2yscWjTY z$Uc#%>0Aklm^IJR4NcrvO4vk$mxGweU`+SD zEV87WG8pey(DHVJtTc#!F5dkyPwrvMS4Fl6N|#jk0!RsQvVlT1a$x{Z{&3;>RF#KK z=F7gOrk4{7xy@n}OwuM&se{ork+59mYxQOI9FlK*krkuIg_@HB)yhuyo6kGCHwB{I zGP@YiRhc3abF{)1BMVj?#HsoZun&@ogrhD&YKIa^0JEN0MG;g)X`l5e2dlJjq_m+X zE3x(uK&iZ^BkFunN$>sQTM#+d$e5bROlbLgCTQLv4zcXd{z1+|p319zjNMB!|)g^c_`$oulc_`zz zAI&up-ScNRwNmkV2bK@!bgU$9szQ41;u4qU5a3(aVVhAbf2=4hIPe)AWWywbyhdYJ|L{n@=#5W2MA)fMSZWkd} zw5Hh7mXnNm$#}CMbv#KF5>F}nSlcD(kQ$?LtdAfSt=gg78MVAe#d*s&uup?b%;K4m zCij=AB8*3g8fLA6JLzfB&C8LcpOv89=3+t66hV%`^P0xg{G&x}DoQ5E3PO-5$5X4& zn%J8Gq`l1c%KqGc&oqz8FnX!L$jiNto@f)-D1$_Qr)81qgSd`#Uon94d&c5M{k{1` z6Zbe|!nk>X-52+GP3oZY4uNO~H$lx+UhY_uO8-fE20^f^To*v^tGlbzsgxj6oTFCx z82cn^U*e%$*I>H6^>#3BA@?b`=FH3Yk*=dLrQ@PwXX=EOF;yITF6th;jZXdm{lZ9) zKp;GHFxD6#KPOxtElOU|GLX+`Da{I#(d8eT0?AioJ&beOqU~ioJ1f@=i`;V5gmE)du!i%xC`d^vO)ZsiHO%)jT|J;+uAg>-5;&~$gI~O1nh5Du(t?B zh+*lc@!3rebLO}@_MjQ;do`njj(7-gk7dPC+I_!=HriBJ>ZC5XJMgM`pzRq36qvPNru93x)OdpaRg&sl^#8y(flCnKqGaEsDKm0&}Q}unPId{2!W^YzA24fOTRQwTaI4vm+3>IooL&-5lBM}0*0FV44W)-m0u*$~>?+zdgA$Fa#6^iYTfFV9dk zg3q;!?ui6E7zEKzNX)qJF98xMBr9TWizJ&rI;=b-0_4rDwMZ!^PSx^7uuwKiLpxnz zO01E%1=(P+Jt!l+3LU7WdFYgBbgnGB7&-S5e(6L}c{!O^rq}aA=i%AZkt^@ZEyEMY zpYF(y8o#$=?Vl#g*yXju!9Gu80XaXl$0H3?37xB)+bAaA(eL`^9+4I#v@VeR;_jkvZFl2g+o>dx*L}h@8WUx^S}W5t$xS zLN3R0d+XM(Q<8w~wDCqM$VrZ99YjN59OPl3H>s&Rh*4<_X3VPqo%0M8TdkyDAf6|L znR2jKt_4-@kr|9Q>3Sj4Fi8xcYHX-;R+^j{xohqa%30Q(M6evxUwAzlA7AgCyXTS^Obd)4JK!9QM6rb@gTzrS^9DW8Tos6T9f4#9?NFz`OB)I zAat+K7YDbf_qO2`J;81n&5`67(#TGd$_A%&rHV>mMw|oFvTZ+O5K#<2ePBg`n!M9n zRQ45M$$Cx3dj#G|#d?*LNFL?qce%!u>7Q*tX|wJ7N1#~?<(-f$wyO*Dm+p0@OKDzC zEDs68#>xsGxj24nxx2iHCN#8&X*Z=~*GhEYfl!lIP?`YCr9=`|M_sWr8gXWzbh1c3 z1DOc0n+7=}7s(~m72h16zUeXV1I*@{;*90zD|JP)&$|K9Qb8zXtu)-nRtrO;@7!Mw z{i-B5Prl7s6+c6@3#p~1 zOO4UdNN%kcE7)bEk@T&0%3WNK6;cSK(vJQG8PRrB4Y{kUw;XPuGVa&Pa;1j;p?Y7` zsHEP>jmqKpGg#7E2N$Iyh!A=>6Bm$o13)(kfO?#r&?-l_<0$%!?5@%#9b~>hPT(){ zaG{FVq^(O95%uFH(^aQrd}h7^$zO_R!xJF60-v*;-|HeTp{h~ktVcZQ;(?C-y(t#+ zN3nShnRZsMD`-{dnN!DidQ{Y-W6Q`Y{OKz`EHYydqN|37i;G%PWGgZ{0>e|Jn;Jl+ z`+Zr2wUbqv{$_ia{F;R#uu10nGwvp>dHT;ee)t_H~@+YbOc$ z%?Xftlnb#ZLOIqBuwC4)Sz4U_L8R=tC%RggN&K2|OY~sG!FZa%DL^QJyt{BQ_0!&O zW+S%@vcZN!^217cs^fkc7B#+1!@UeBt({}>$b!IbH+9$&=7VDM`0}8+WZb{k8ks<+%wv?@DkOZV2c5}$`nf%07{`JhBN;1fUPqc&9t)FYuObnOCgI0$EtsaA`}e_3+7ti8-NoDKvhov{ZKTvUaI6;}C3U<)PS=x30f+VdBAK zX+?GeTNdGW0hjXY%1T_3VZ5r7Hn7g3j82FsUppG{n>>VeRXyZs`s!SV&lzQ^xoBwg zj{5giVx_XC*;MO&od2JDgbZTmEIW7qxZXU0nh)cFPNM+FB@w;}?w18!S&Cj!J{=?H zAqOqgHNk_3R@FT`p$tq#bp+ue`2we3IYLu2XKx#`!ymgY@}}b8{_zz0K&T4m%ol+? zfxzzO@%C=OG2as)Pt5_*56%&nbbSQK@qV4yDcIoaPvh_98baM)uPr58nZv84pfwXrM>ItDj_yJe+J*7UsW(pjr6KGe+@mm zC-A=ek$}k^u>Zhk^OAIqWc%TF+h5w~Ca0cHC=YIwI0Uol&n7#UhTV~7uyZ`8EaEY< z_lWa^^#~w{6KEX~bPR-7bV@NAy^^}g$rxWw7%7`*^8pG3DUHE#B-G#94O|J#ZT(4SBQ{||=1+bH>(57^v8G*C zX_~Sm*Pg3EXCz(SqlIhPdfn(M_f5BL(OG3$uz(>Ulp}kY*j85N0U&OxsfXr)MJ{uF z89BWPcoKT7@Qek56^Q($q_L)c363Q;uXj$KH4qV+B4`~>XG4B?8Z|ko>K|K!ashtl zojO<_tIeo+rF4FRVU`$XVAx6p2^#lf+3Oogc&9#vkFj#JSRj?;jzCfN!nDcll`o9j zm3IW0gn2AsKZ&hLbtgwl#A;#saeLYhCbV*^L6>tdM~H43kSXD{;sl6RfWEs%#<*M` z)x8(N%gMfPr238bkkF&H0pQ;8y6=!Nt!O+1m$r=Sz=s&gqc0ZZATM;Qb-GCU6s072 z?6EsXKXnCpwv{v#c9|Y%Nza7NM#qpcpLIUSs1GLqFL$Fk4k) z8aWa8$jGRUe%t4{>j4X8r6F%%H*!G za&4U^Q_>N>Opv@>Z0Ep;Ss98!yfq{%I+!j=2kunNSxleY?#5=WX=KR&^V38IMr8(8 zLF^v@CH5GGG7O0dgGyj=0XlF`hsk>v49&$Mu^Q}Rg4l{V1J0vf?gTRx3YH;Q5g%wW zs+T#ARy~p{ex8xU%Z-bTaWt_((Kl`h5@aez1FeXH%bg_ zO-T86$C`OOWzIf~1>xUCeSod>`LS$NrketEEj;8m=seBn4l5;4$U)1&m`pv#*Nz@D z{^V@1$c{ngtw#z@>9~zjvRfoC(VnDIExIATk}aow%YeK@CIxjevw>q5YN2lpE{N+i zt2ncd1u{5|S*u_pIL1fEI#h@d zQym8i&=~&`dlVYesa1nXgU2hPlOccHgx1-5_@B`m5 z3W8QXgz<4Sgz}ZMYZMnS7}sRTWq;N&Ur}OM(Y>4kgYmSkEs+Lk*|8&e(&R#`6r-=B zvYST5hBYT{%&9M_M?jyPe+O;J(V{dL+l~xkcPorTY)+4j34b!|RB3EkB;*L=yZmxA z`RMXqjT-8C_udmlZn7|`K2`KBB`){v(Mcl=;|o%7Op2$b9+A^?J6GxnQtn~$-dGPT zU27u?aB;7}6ro#$7Qr&`gb!V0t2(@1D*(v7Zi%td!o|5WfT^EY9(fL>5EIfPsTIo7tN}}Y^7>FZpB#`Zj*!IJ`%i{hMghU7#?`8ihxhY!y>gnrm z4RL&B;Y@z+E2$b}@c6jmN9lcUKyN11rlfy-zTy?US*I=kA>(&zans$7(xFCZ{=t;| z;`B|W>I|#yX31^RbLV(7cSH967gcX?+U@XLOq8OteRueNPKqJ)ufLMUhx-^ivUSH_ zOcM7hYd@U->58}ZloX^&QHUv6mA}Qf^9^cudxRJ~`rIoxIrpO&KAbk(6pioKymQOr zMo2ls|Gr)9maw4vy`P}d)KQ$5lRnKBvcZ&#J1Ur8%4wB*T-e(8HX&yCq?;iy#f?v} zZpP}B#u}Y0zlObz&3ZnihDcy9?K{o7+a!9hT{h)U9lARddYjwCrRDUat36m_7<7;I zv@$M$&Nj9gW8$FxZMZk14BO%=r0T&f(eF~qf%BKf&nx)2s8T_4a)M0KNKoYScv_rGP!U%mT{$e0Hc4$hQ{EGrhIxBW z4s|OIu)XQ%0<*> zZ0U?NBn;i90q&Or-xY7nvGP}58)}dWxtPu5yC}*Uek`Iw>glv5yp1N zd46K3Xu|s#8OS$za>~dd#*S52aEJVBVzM~OU6)si{KwL4Iwm$r8E#BD!0h)qzQ;r; zWUG^3MRe9&Uh{Xf#AM|%3gtb*%X^wb4DChkX*lV|f*IHdkHVQFpVBWcnpObvUuQ=$ z+=c(Z!6RiI#LDLn8OkAA?x2n{%!;h8Hl}*9h>Fs89UxHA#|cj#!x=NtOT=*!I_dFE zZo~!4Q~ARSsq%5fc?)({uG}Djjac(lvEvu81-ogx$p?b=kH>QH+8_3 z#%(599do93i-ji`t^^MI$4Ha_bTg3=?($URt^(R7KMmywk!83jG%hPcFx!TKz9rGqyT8Z&AdbnO-Z<*`cwD`aQR(uYt z{7Pql9-K+l@Di>fUr?e`(r^S(JYIQkkGt%-3(=MwZZ4IZyS`tEzD_8kmlW*f3>y+= z$hekaqWYm=!1r>Wb5@W+@fE{4qz94y#HrIsB`!>gd(^KYE~PZP-tovvRmRq*;+i&E zPBmJEw%yfB5WYYnqe8#1M>p4QCD{vaY2(9%%zKgNQ^1($MU}EN9YR4x3?0ktJv%h6 zA%`_}<+#Uf;^ccQCTt>k6ds}BY@aH*uB$PTHUKdqIV!Q7h&CEhhN1CuWZMvOxFK@m zaI1BCXzcV^;_h<;`91vMEbZdyy<-kh|CrZ8HUY2V0-jNY{8TO`RvRtB>4=@{`b4~b zPYP5Z`UUw&mO1kIBKSd}$JB6tA;r}2gZ!Tv9K*Ls!7eEdt-!z& z_@x*Nu8ZSBStL!^gu5M{R(U)eNs{yoA@*kniX|C2uRi&l8x6fozcZ^fM znt02&b$#+RwT#_lYH$-oSqgx&F&>1yFriY8aLbWtDIMSAbAFbW340empVvnjA^XPe8jMm0}HfF z+=iNN5ZYT8BhxUtrAJ?H)x#7-tzDO{hQqSjuxoq3Cpn8}{F629g}vFcEA_}T;JUtC zCd8ZR78(I_uunW+U_N)IvFZ&PEyC;qLJ+N?G$13@NqLa)m^~6Cty#neAhaZBE_4yl z`v5ambI3ahR7?dOhSWf{o0n*LL6e*y53V74gvPl|=`kGLH0SBGmv`?T*5tb>TT{Z0>erO=hMp#8GgZpu+Wm}(9ZN{6GqS#G z{BC&5jZ^0PeVc%9KiCd3OqwTm#fpbFu*r3)cR+xg!LK7k5k;w7|xwy4q z{%QjQGWG=NZ7Jm{kN2l(5sjN7?m<$MZ+cnMKl>p7N%5!So2cR9(MZShL z=c?zW|483^hVEb!UEb1PT!LpE$GT`R;x&fOoLZ3}cE8?AWlH!}N}vAJjhw#L&zWh6 z4gKW11f;g4pZl!o<9y1kp@8PS@)-0pNo!D2M{2{G8tGoC5Yt*f*QQ}i#@g+%%^jfg zsXXoBT0|pzp-Ws6*eYiRW$Qw2uva?iEe&Y`yP67D+J+^{XX-v1dNk~%eYZIXsu{uN zehGs<14^m|$<0d%F?vp*3%0D&!7F8T<`?FJRNJ!B3)>nCI(;F$I`ktSs_)s~=*bAR z@Dzk@MxwzdH}sSlLUZ?>L&oX-iM>WEzqL2vvQRI}Baa_#M|9JNkx3~mGZvs}2OEU> zMA#F5T2t9d$xrY*F=d2K{FHo@W8w!h3n$i==A75U4G@{{HnPcSgWGOsLg}{|>0^v& zHJ@ipZ7SZdaP81$%#epFn&)AOItpICgcY=qX^Oulvc(~YgjS38axID2%uF-^D@%^8 zyf>F<+`0EI^O%67@n=3hUYZ3|gRx|I@LIc}gOE5Te3BJkUu(4=O&$?_VJBqs*V^9M z_Zwm$p>JLHb^@RR2QyU~KcM*P*;3!!HWF|*b%6#Zwwgv`jE#nGX7#$rQm?CUXNIV? zeRN~ankthsa7;Z=Ox6Gl?UmGI1oDO%AW~=2N(Yoax}v9FGolk=mZ<4sBh^5#gj9ajo(IBVbgt;j*!B+ zOH+x7u;!yF*cg-h(P97|p?*!w51~)Z zV$$8jstD$KSBHJe*&)@J!t<(6;tfH^r~K0#e_e&l5SmfBopJ-Sw8uLoMgdl&M?Qi+B&7+ zoBWk$EIO=dEA`;EMe{o8l&Dc%CsQWYsX@}XtcaYv0i{VJQ2UdPdQMaavH4n%gkhLn zZ`s9m&C=Sr4ves?6yL4vA+$NJRpe*oq};IW!<_|IwwRh@s-Fl%*YTsYWZn1*2V*$E zCGu<^Xy8Yx)lWtR1l1r>x1%P+WE#B3$XQ#RH92*r>dvVKld2L3kw z7V=olwbU!#K`OaNMu?&=Rs_!DJr z>U$CQ?Qhe@x#tRo6`~^wC|<1L*ZYshweQdTn#@rBh7G)`BbA<-;h7AJ3T8RF2fTMg z$@Mn@X~P6{HPY9{<;w19wrBCex?V}}bX$tLrO=JJt|5YQI8`)9pH*cQ7R2O{ld zFK-hq9cy~?=vRm6=>HGIKs&#pw`38uClHdd5p?EJOPw2$z@VNBYaLD2-^h;cR7Zjb zVNl3J4JB?B)ubSrl$Me zwx5%|BOJk%Zw`6QB3GQ!t@>j85&A*yJQXa?XM=(GB(TVPJ!g$6 zrHLDth1*A|>M2=crtirwcanEMBHU*y$di|mV}B>@jnLXBy*b*bw@1D={sP`!2gBf8 zXJ^5Jo&>AMgMA8?M79lhL*go9?<3X~-($Q?CAON6x3sa4H& zEF$AElxt9p-Vnf@Q*C058HjLX8&3`h(XNys%wI-!*ONUN(XhscT@oXaI%>o!7#r|R zj{0P{NfrVCu)Z@|#Z=$#uDlE&nbN$5$~MX=&5ik*ejOTia}T%y|zc6c~`3GTiL zvG6ng8WY=`bBKclLB;MpzjJ|{Z^-1No!=*PYs>O;4r7pV4nY@@4d%zRRNqVC1jO>O4c1AuS|@IE8As&JcQgyYk53Mw2Y5x{U91hyM&E7^@mf*9&DfPIl>iscx{#}Q7W zKLMwzOO;d+UhDi2*`cPcnQ_EV0xKRG(O)AGJHgU&7+4im8I74Pb;px1DrVo=qPVo1 zuFZ>FoLN1@@|E|O*$-{)B-6mu4vtxh4LKf!KTipdkU0}25=u&_v`B_+axZ(Xb2>vo zk6}3}CsL`B5i6)nQ7d2z1l6e`fwrZVsmc@Fg=7%UcW%hL9uRA6F%!+)Lane>30<)S zt5(B-3|=m%ijsqye?<`$Rju0xZ+87ybj5#Qs9{@0$s9l={m6uEwR1n0};H4}Gfn_;}Gu~v!VP3>Q$4IFO z$x_TB*BYMTvBI#%g{=a+BSRxeghh6hg`oruQV}i|BBHRx2CDKAu#G*M5wMNE1lUvP zN&O)v>AnV{`nROedOco^!8$hI(|SBIr#eCy&wP7sgi<&fpRpC@JLEVO89!G-tKl2gMNun6j6!ba<`4-{2f}X188)gs1zEuEPSrKVBi(Bk z-3p!;BPL8__Zak%P6L?lK5Q+0(tO-Rk4iyJ(5PL6UqTS}2+uT&XtdyoITqY)>^Z7RB})314yuTB zTvP*gHPh$=Aryl`|P8y!Tb9 zQKH?U&KQwE26St7#kkRGrBh{1;eWAD91^P=ppw$NnPBC2mUGp(v*3uB=lbt22jY$%RHmRHiFOGNWPypiHM!lvY@$MOr}ed!i3~c0{9o z3LR5)O$pS6a{*D5N#>Ol zoCRn?LUKq@p)e=J9XJ$_bVUbN0u}=hAC#z0^&FO)oCN6F$W>SYP`IMFv2`95V4Kzb z3qlD*W=C3Td)hI*loDNRI@IQFCF4=7jh6x5n-n9Bivk>~ik)DvirIUS39ntS*OqU< zZuYNik2n(2MNH%uw1cyxq@fL*sL75RAqlr?b)gP! z$$ODyoWX7mVaoMM6O>rTAy*y_Rp8}b4$pyb_!k)Vo(|c>jwC27iR>m)+vJ|lB(`}z z5=-_L{{VUOk<0TEe!|`qEFR$84nfAnqDl${3Yb0wE`J7G_!j%{J^uhT#~;Z@`-HRv)u2A7Zsb463M)L1xecw+P0)~}A<#lqN1+XcsvYh# zAlzD61v|<=vg;3}k0>g2H{5P2%{~d<$IB&Ffn_n;VBLjrwiBX2_b~1-^a-Yz8QGoN zTv}{dVv75ZBJxGbM^eG&DJC|-3Zj-biW8j~oH|n#5V4vvmc@EXHK=T)p(A;l!#^#H zf)3(8%p^5bw1n2k8|aBD-ex_C=YHuBH4l+-qoS^j(8XGHHU-=gSExrcr@JnIa@NFcVLuoWlRi<1f1ryxuc@kD$PS=xxc^22W_C3e3 z?raA=7(hVXPthN$Io=P@AEY^64rhUWAWgf#o1O@JKLSr?LW+-Hu!?H*B|1+elJ|Z| zZt`sa^6i0#{1*rvuNPB4D zMD`Nd5)HOOX|R{=p#q7W2!zn3xpfc_SRnAu@!vR(@!ZhvoSJ;cD73Cqkme(~4bDT5 zd3Os-&K8k#)rv`0OK%h8hMPn+Mzylj)JbVg$$iZs1{KKCX#><#X(@1B45bmxTCXv% zh%;`um9nbvc6r{d3qrWNO+c=IBi%8+P=4d$twk5F-Vj1ijdvEN$;g>a_bP1;{ z;V?&n>7^4PCb6nrJHPZb6n9uX;adtKe2ZCR>In zOP8?5k&J4rQwvHQqh3&mQ07VAqQiVHRF1I5rJP47Z+18$o~DJnO3`jXxP{cH#AO$3 zjziM0WqM#sQOzbH`{M+8QAB<%ESbSrS&CSNhq&Q*h(-SZsTc4f21KH(a7`)Ll0GYx zdUh@fTnQb&*J8hDnL&A+l0_b+iUlO9N%6Mr2zf7NMxDAu@9eZD+D~^uvl%qj7^ytc zjT(zi_Nd8JRAedi%fhj+(<`y3ww{yNP;$sa$2wd^BZj5vVL}>@zh z6MGI6`gRU%YDgeaJi<`|TEj6H6p|a{bz)Qypr&ffS~i&VGd!|!a@@^nQ$m2pCaNWs zh#RXTls*fHj+_$UjEs=yQ6bP`O~ESMBJnD22Duf<93OEN5o9{CC8BT}!b(MC4K=u` zTH}Ic;6rC&WFym9E=@Ih#+B*Ll+g7%lZp*@t`#PcMy78uF3;guOf=leos#+GE! z!#8)R26&w-4~Y=t6;hqY+SE{8poq~i9r79<>|Fl?AU*5{O&uft-D~J`IO$mNln=mP*^mIS2)vJvt?Ke9wiN`8Bh$*{h|k27P1Hlc9Ef22;dr!2nh z=F&QONjz1yBU1QOv2!Y&wIV8FDv9Z+sd}O^9j4EcJVHXqD;7$W5{E2_FFd{qkqOMgj%;-qf66BVSLD4>}3_kN>Ib?LoG zr82%UGLpqfQC{MkEUPdk^wnw7^cpgD)5pmkVrjab3mxw1iZT$z5pe3X-&Cvq4ywO0 zF)Jh@$ps~C(3gIwQwx$!tvg8T>x8uB39VF1FogtWs#G&<&BXA!wkp{+)UAPuBN#Yd zv|Hi!#~Kk)vTOFSIHg^Hv~kg^MFB%RSk?F5kYGxrh|Ij z9SLYb918eaRyUq|1x_%jBx*F2wIf8DIpB?PHId36t3a;*0Kt{)8_AIdHx}AVXkk4T zpCWaa!X_rIdh;5=YaZ^u$=$sG<-;)EGd&rfNozr4;6H1jXa|D<~l1X7XL_@TN z3Xs}HiimLA9pI^Jbg3nUwpfk^H599ZCC-JG5mRp|K&dE|DH zU(pno5nuFC%FnRd%cR&Y)nvwxQhSWlhjCavU1%SXiMTZ3?u7Wsa!y3H+9$Y{d~!9M zjXs8nJETp=B3^=r;k5^sST;q4gBcNPC~gW!%3vx%bz&Cdl`mv69+UX5ak=7d7Dfhg zl&*rkvDhTtzSrySGu|JJ-{8PA3Nqx4Oxs%J{DQDpGwEq2U+U;ddy?FiNEAlo8z3Q8B)@@-CMop^Wld#hJ?h zVOZ}d$H117cCC2{?-PAW-TS182$h&)v^ObGh2W+80RqxKRxB)$Qlt*6;7SeAkBvt< zvn7r;5NMNRq2*)}=4zE(X$lZaK}<6YP$Wf&?%-rV>V|Fa{{W1&##>0mV0s$iu@JL+ zt7DY+Z0tjzmoS~m)Zxr3B*VMVM`C7BbQ{MOf7nUVwYRYC+-O3YsIyF~ps1Oss_Tqi zs|_V;GBGU6-PZC#wJAOWE2_CxAQrbl=+Si=)Xd-5bqahzv6c#haS01x;2Z}MID!`l z;v5GGI8HJ=bn-ohE^nXWnG?FlgRpi^Bte11j?pcVutb8=ltf+32f|L@asoL_ zhQnQo86ML+#CbSaximY&mpv3oL{YGF2@Yg>B`jM_TKyKg3woltgsUR)tpX&G5htly zB|uOTPO1#H$qG(HH1IkPpFuOE9FGSy!gbJccM+qHM9_OsRK-ORZ)0IE1TCHfyF4GL8rbkl{s#}i zi=rkJt49r{3XNbrsKLkSq~iBDy~nWpjbVu^d!7FPCo|5C${I&IIT^~z7Y}%H27)pm zeh_>kL*WO?G@FSIg5nM{xNiE2fr(M>deVC{ikE zv%~OL!iTU~ZJH#UNF@l)Mbuclja^bcMKn~bUbnEGL1SvsU5QlNPDr?82&wTYbg{5}P;DJzvIYuIDbSppgfS0@C!IENrfcoDr26^W@US&V3JZuLx?rhUNp%6vks--uM6 zLft1w%bKh_&sAM8JL#cg+&R>x$kUBmXlT3Cz*60Qf^QW=i+-qjF8j{L4vUU};iij$1tj3Ep=NksO<#?ZG1!J-u;U!MVTw4z5k7&~jb_Ldm!gkp%G1#3Id=Qoy{E>{<$T9a@Cd)&XRq^%U$}AvfqZ9E1$q z6tafmTeK^(E}<#C7BD4gx;TXbt#*l`52>*&!ZIkOAdsqgw9UklRnE@DD&g)TNjq1C zD~S5}($d-;Tqt5uC~y_cq{_i+7DvWB4!UcZl&e+8@t@f|i{!2y1)woRDq4a*N;)va zgH1Iv4fYvqBJV*HH0wK%Z`zWu#(^=N)`_F7zNYVk`^-dZR-qpl$)*{y%8{Sr zJ0dXyq;4cm0eV|;)I}}}h+@h8raiGji9o-jV&WFOE?{3T-GXQvH?{W?4ngZ!9JZg& z{gdGoHsV8q-YJN5F|t2|=yNB5={i1&{UXAXlI3@KEBO!D65ZQ<^iXR_W;-r3yJJ)n zg4@ivnWv1BQ&S1hQe{qwte$vYFV@W#Y2OSqIREDS}8CFGSw$cU^hQo?UN%f@s2dKbRaw5_29mUnBwH99#DyfJ zbV=Pv(qXuJmL-hr=4BbmIvfgG@ub}3xWM;zppt5al+~10F+aGO0o63rPn5+i5uFkk zVL=Y7W0dR2wbWZ7a1qg{*j_NBP@)OAbuN&#)JE$XAeyh3ADC^jC-*usVmer_$Q!`c zowGAcK~%J~g}qpbeuW>cr67|K=1VL80Hsr-Q-p~c-PZ<~q4wZPj>@ml5koBITl^5P z#XpfQpK&o!TGXGHTarKJR737G8ZPp${MZuYdrj&)>$dQ{t1>Clt9n$s!kjU5cu-sQ@yMP$PXYxh9b6)7 zYyHG4I}xg?dsv-tmw7^=`Tj|Pbd$tyBs*sVhgEda;z+4wEQrpYX|+cCKWdj2?7qkT zjRM7AA@|cm8z97`Eh|&B(;e+nM{jCaXE!$p2~LP)z1PL}CSJs$Au|guk)6 z45y?**kagc3m(|v8x}5M@^~LNgK_L`AA#?@4}IXTAx%)l;wb~?L&xmYV%d*sRkWfmvo(YYiw|b2hYP1sULlh!JUJcV> z)kJ#s%$AUIbc+Q&94R=r)-y&}qFWH7aUn;r-??x-)l_qvRo_|B>yt7& ziuH8S{Tp@Fq+6)rI1U4Z;W&;Ge0!W-Jj;;gXMFCIuE@NznULL(+(1Kd%pxKcK^Grj z;rO2j6AA4ezJd$4G z)MXTOWb#iZuel|XTax!@Bgx3lVayib=Xnz)NnE-?UDQxR|vQvZ`bVp(~y>MC7!MHny)z9FmcSg>k8V z6nsFYUQ2mXCo)Wg(Gg*6Vnk&{7OqCSl5(IsYnossG(a+xxFOa!RYP6Jb2oHYLz?BZ z=fWSzu7a;htze)WjR}|%*sDgZ7F}#9VZCv}L9@PwX#*VA%pHh5fNW5%VsIlNV#12; z+2N4-P;#JefZ(==x}pULJ8XnDl;YR0nEf#Vgq0rX+W7xy(U5k%#*W=i8yd@uJ#g+ zDOUuGtcaz)?U2Ct@y^0J7X7_Li5 zVkQ$2A4c>MjVYi?mcnKbVwYpTlO#g< zee~SH1Sgiv;?W6p*<*o>kgO{QHoBa_OY)<@tAj{0?f4M#sD4En5pXE`2_o|kP~H+{ zU&vfhil~^uDvbGqB z+?F&|6UIamhXL-YDRR(^ibG75xg1!VMv5|FH6g6R)9%3>`|Q62P{q#6{{V!^&}COK zQs?C)QBik3Kd~isT60m1^J5V-5s;u(gtPsKHc_yjZMsB#mddL&GH~Q@LNyODS#`#( zF3=*>mZs#$G8&pp6F`+=NfY^S6FKpeQag**>dPpxIBmCX2(C(rI1SzfIAk}uG=&w2 z$`V#tsFG@uUBXh%B~q%_xm2aZAWuZYus2q8e}3a)qiNQ4NO`8e9mNnyX8fOrFZ5+$ z&n=pt(H2Q%yK*BLWGpLAGDvpjyZPC zg{50WX!bK)n#~T1)FiT_XdLZZE>5!yMrp>WD3W+5n1nvK=rsXmJuf9z65G*ju(o5( zAndaow#DWNYLFlTZ7CFIA_^i$oVk@zv$BPHXwWAmw+gm_HIk^ymM{*tAwh&gOf52)%L3qM)NJ)e_t*2#vT>C#rXMxhf+Yov%tBz!Sz} zFygfoxC!9Ce*(0E>V-#<0V|pjyyb3j|aYq+Dmb3X*jvS{{T`-WUI+Z zHLDh>?xAnU;b9`2OI}i9Mka$b%ze;?=tP#p*Gdst%teGZ{)#ms6L5!7R^0a;5kb_6 z_myL5)g+)hFz%cR82IRObEeAe#c@_ zG-kR!NfBWd=k!IVB2^~{YgaJH+1ZuNN1~a;Tec&4A9500q=Y)}+KD5gbb;%Ss>P-? z@eIi%_azyVkmnqSA__PukcIw2;yOai-p1q%m?L(9Y)0}CI`)SJ8-*QH2R;y}oH zMyPA8A_$I`^e*mG-Yy-EGlb&P;yC&x?w!#qeJ*x)&PH=HnVihzXEQ5Zi!%`1KtuEr zyu2ScK2TBki{^=A+Be(amd^rxg!T?6LBKhYOdfFH3wU!4(Cv%&kCBgq$x6$}qWqkG z=EvMy9^(Ds{_w+FN27Siw<5sfG4DmKcQ3{trXF7azW{!?jn4!&UJhr1Tf7kVeh7Pe z71@kOpwB^!T`lrYB=UEAm1LN+CA-Nj-;`&iA&IqH6G8<#Hpdn5-eSbRB??LV(SUP3eZVxg|P4{+rUrY5dQ!nYpSje(>vA~`HlvmV(tJ@uE&cZbX_UnhL6le%&-$#KZeW^*y`jz)u+X}c;B z5e-942g)=jua^*dn3{o)2dO;lZ?c=zWKe!^(aFQ;& zV3W;o$2fLE`WH|^Dk@auLo9WbA@NEf(nhE7l1tGNc^{LCTLW^Y+XEngZTc~R$wgau z5QVm`JJM2Urj?k&?p=wuTkwZ8r;Wy3*=PNfC|3<&C913o1vZJqHgniu)6o}_q3{^) z6_TMxOO!j5G2OuDVjGlCxs^gXsR&ysB2+mOhAP_QY5oJyNo^r@2b$U>tAz69i?dp`2Lu zuS!XH`WJFA!-lH)EXsikam20!$EhpO(6eC+EDEwJXzovE_#JMfmo2CuNSx6j+M}pm zk?KJ0vpM-fOsFmOL{P)lw5b8wNwww%ip|3>kI^FFF*QnZoJb|R?L1q%q2$bd2wq&XIp zqv{dG%ic(R6sU4yxVwf9_2jmaDD{1Zq|mz+A=}KD@<}`uos5DL8+niPVupm$Mu(EO zcie6?nimlYvS&=|8omc7a}*wzeKV|SePo>{rRh2`zoSjuq}WLjP?Wf0{IFk~h#{r3 zVJ#hOxWI@YSS6OSBrT*Y2o!O!lFI40{{XR@^-@S~h{-46C1j~nhK?}?!nmOLWTr26 zi#sjL7~^beMdy7=G2PoLAh@>*?FZe3N|@0iNtEKELmH@>!PyxCJi1|~sq%zW)FD!$ zQ?)&ZrS(uTzN@e{usBRpLV^*l+TP#c1Ncutxg^Qh%8;v{*lGo0HP$Gc=R|HqfwN*F zk;xp!C-@8TltGF!QS<~O0aH55@H3!{FiYID8HS zcFq)VBY_KGEs`C{rNBt7#Ws?0{>J|RxZExy=O@ZfoQvScPy0R=Q~4avx;j)ur{(M_8+zz-GYz%gjB=GpO2DLA14#Z`(f<-lD2sx7I_<#!^s_< zNx`oqH(o~8=us7cJIJC&OC&AF8|e^4^uvMaogi)mK^jE}AWjBfN|r~{xe!Q)q!Bt~ zch=5=LBw&KCke!HoP9X!obH|9Cx6a|BRj`@=40MscV{~qZaIS99Nz?Y30_4jhQFa4 zH-q5^!Uyq1-{3(n&}`}~2}Op$NHvd*A?*amW{FH3_d?!BD0`uA`Dr^xU|3_w{{VRG zc^1m@G~>wQUP`uJPBrA&`-}Iq&-S#c3-p|kZbt>;-kgmIIKMz__>a*arW}9JVfGKu z6)x~{JR6PRjn4&keh71C;A!m7L3Sh~Fgx3=5=lIh$t1Tsj^bl1mhZ{j@=4|SCAlwl zPT?T;gtr?_HNEFLR^+oAsWSDd42t_N<=T)+obP^S&;Bp!07qFI$u*JrqjKxt1 z5*matXm%aOBxDmNgJ`28gTVwO5+q0fh_;4>0vne# zaQ9bPl=E+dIwPsmOlWG{+o1@yN$I&twi3eR@))jR7qCzqQWpCWvvf4bh-5VuuZECL z?WJh2W-%{#`vwsm4grGr%lBOVb{MXnmQCE8iP1^-+ErQSD7|A0h;7D{t+J;3N zimNK6Ss24&yor*QY*?p%r+&mwArjzF^sa}TS73GK0V5i>D55PbxG!+jwn8kL%~Ezt z5QhZa*ZnBzaKPeO`@uCN=&s5wN?eY)$i+rK(G}7|O$07ffq^v96rngBEfQpsTcSc7 z#^fH83WTW4nIHDV=M#EG6uiZvG`l(&H+KM_^Z$FQOmb8|&PLd~4=WS?TmVGGQh zk5afJ1S|IvjehFH{Uan^L>V;>GR|U4LReLJLX+bxF+~|L5~|+FB&w%H_X@Qpp1f&~kaieoUA@+dKsUhIhFrkd`Ef>>%SAkqXH5DF=VkhXGF zcr%onbJ&(@Z8Da~ZP=>*#l6VDY-HXrHZZcp%<9IILgPD+N2YZ5)^N0JonDLSu7-#c zp?9EL?LPL9Deo~T9|~#Eu!(qN{{SLdOvI9TVqB(9>YoBdLgp;JWb!)qqQpi?E^qc< z1%K>MR4~nl+cK!Ct~nKET|qU|W6jp2%}MyNU8BG&F2qq`NY z*4LSSMG~A{>?-Hs??ZV^Q}-!X&6!RL?G~O-{S!*jW*GEE##(sM3?gZbXu>_m<0EAS zIK*KXB>CKgD^y$Ixi4|?AG9nA)qVtot;_o(SG5ReX$zqy%ngLZmim`;>@{QDipD$}5qG!F!MQy*b52 z+wZYIp?9n{AW({%vgES>vI?jtWrf&kl1c>I2m?A*8NhC%{!pe+`O<}I)eA})4Wx=e zMQtnN`yC#Xo5*tV1?s=im5RrXPf3FVa$Z&Fu|YvCPOaVtV3)B!B5NNJ{)D2XyGXYe za=SY!$vO&Usa5Pk39b2&8M&;{{9t;rERCX`<;d!AQiSEgb6@m95;y50gOUb45cG7_ z(^NX>hpX1VMtUwiBT1)4-i7QsimNiayo-~`Ir)ennv#e`Brya(deES54ba6|NMCJ2 zYR3+eC}e9221cNQp22ft0H}MaA<1VFh*%L33?=OX5Gye6DDVCj0MnFIsDO&dM%5hd zG1L_Mp;F7VlEZLn7qIx#P7zyzqo8zJQ3GfR%xo@-Btr4KoT6u7h`4qfe7N#Q-PYZN zJczV0p;_?VjGow3UD(?Y(LTfhVB8~(`=8QaP8wsdkcUt&Cqg(KQ|Y^c+E-K-K%Elc z$kZ2922cp3T11G0`=R8srQ{rX3ZxTgDRo=aZet07S>8x$A&+DrFIrP80*-LWP$ebH z>@D@(Kf#M%cqU4Tg_O{9dqZJ@RtizLMb|bQWVr#HJHz}a?nb#aMaZMM{QC*=7&l5> zTDk~gCj-&+K*wz#Th;YFQ1xUFsrLo$nTrzKmgd7`Svd}ECm`k=gOQxflPr6qlr7Vo z=N#c_7a_j}^N348b->Iu2<9`o>WKLSQyfwMjW+e!~Dc{mxIF18}947{g zgGI)1i)7&|*y1>jA*UKSVBurfest0I#~w=AIoVF*zAeT*@yx{By%DP+ZZnyifVY^Z zmX!^}cZiSjH7n+a^GD7FMg9+zA2<}h0)MFfXm9FL{{SP}pQIkZ;2c5hh67-jOOXg3 z=a9DtH<`Nf6F(z!+JB0t#Z1anSF&m6~+ za}HU}Ij1yOn0vu9Jqz_mE5Xh=hdaT_@J8XtIUWuML)=5>!4GCjW_mp1J=mLa{{T&G_Y$`S+OZ;AaER5J zo`mR!qUIAJu!Ta^h$Ez&P2L8vBT7oEBd8f-b6)2}#iq@Z$rE);a3sL=Lni`SdXzm5 zL{5Z8G{}(cqE!-Y41J7IU5Iu$Oj7X2KZrq%QX0}3b|hGe=&+VH{`y;jOm3`Y0}3Qj z1kbRuTb7AbM24>7O-Df~C>ySg&|0^QX)~mb;;_UnN<$;!1v5d=NOW31Bt=HZ$wp!{ zlP7It$j7Y;7t)&z5Q-3v#6oO{Y(m(E#5O`pWF*)|$lycR7SRCUM*+ZN+f`wC+_~$I z`N+$DLXX$44^$sV0|(74tr|OqcH$9kXy_6qGS28vUh%NoIT~`V!*u;(lKURmC2xx8 zqIL@=T9R>rzY?UYldRbs;6hw3-pGO^{-l`TCI0|IkL_Vt8L7CFVt5+_S12V-g;qo~ zO{Wl7g6-5jJ51)pN+O~W8bjP8PFs>d8;8NUu{Sd@{Ei5vH6%Q$p&LfyV|Qgo*vW-( zW>Rw;jII`2f*7J(Xml>J&}D{{F*kr9HDou68BJ2d0|Y1?E-aa3A_**;D2+A@tPjp0 zhQ+q>b&BciffZ7u%g|Ejs@Sb6ErSvD z#U_~EkWrHQT-eF$ipvXa! z`;N>e25|cg;iW3ClYTN8D^N+cEZCxc!rlTpj*E4DK{*>tb*@m3HF~jBu8dYPF%9WD zy$vsH8N^%X`Y?kKBE5`^lB#*hJ zxUW@QMi7k?yq^_0{TQ5$C%h$zQIe`_{T9hBlx9jQ4xuEM7a~M(2~xDEpOLc|n&fR$ zJNE>g*pu}C00v_=s;G|ytTv)h`crR5f$?s^>&%Z0AXIl)t0YQnG2>|DM~f;NQW?C_ zQrLKl22bd0<>Or-hU7}xP$V!ruLz#uAGr6g>{lukcn#d^9f}vj>}_c^-1L>$QOQHG zpJD>V9;F?&6D3$ffTJOt_QRc>k!88QF!S^6_+GamoV{jb5i{TWt=AH zj24$Fzr4m#5>;J%L*T)t({g2m4cJW-E2kthOKbj*SMUtGFbr<04EelKagfMi--$u90_Uva8=7bAR&k&8p?wz6p?m*~j)v7>svm(=~e za*F%n1|bS?cp+j4)ijc$Ds;H|V$%>~j#7k#AZTJI@TnsY8%+r`rqe%qO^J+6L!)Mp zp+N+MvD*Wg4Z9mljnf{>#g^pk0t%}Q(CBbC^L~b~8?spNPqT%Wb1tlCn>Ef`!*_L*brpzl-gOS`uv?_3sqVi7Scx&#I zs4wnF%&b?jUKY%T;$3Wo-P0l^(IUD)&5|b_CIvcR%QD22AdHK`OJ6NFx&`RH!4oJ> z7AB$&ZMl1#vRe?TS0+%F%@XEURLV3~m0*!UB_X<8nu&-LalNogXL6-5AkvnFHxFQt znvqFK%&024h)_ID?&TD=>4)^`L9Z)gXz#Jv=h8yuER?A(&w*3 zYK-JmB%LM6I}D;!xqVqqi)%ekuLcO{Lq5-3Pekb~sVtUD+?Tm8c1zuyjF#-?eojU$ z#&a{7yr2(QZy*$rj*)K7k9Itz?pKrhDGU#D?6j2RiUA z)!_IM$h-@K!PxLFF9*nn`T|DY4}lN$jxPga;Bb%B4`eEu2JH%m4GfiI<(Q|dWcK+b zx5(dbkv88aZ^6)H~+`DcPJ1du&{~2g)q6LAl=WcSf7Z-8<)W?w!vALyA2P zSL08WoaZ^ta-63r&!Rx(InGm%CVz*|(~5J~B-D56ucEKg=tTbj1wOUZwNG4htCV`ZTcMJA zR;YTt9<9_*S?E_EO~emL(viwPssT)s8=tFS2C#L@ZeIAu`#L*sj`YZSo^e0EqudI&3daW6O ziBy4$t!pHSjglfFW6){LDwIV59jBuN$fWlY9DPaA{WY#N$I_B|G9nd8{aNZ{>wCVe zul_zyMR91gNNcWiOp`jUhjA0DBSmpIb{lKG5-7TIOgUXo=sh3v;jz%|)LN(Xrbnha zJ1qHhKjWi1&r0dJ9VmLSK9o~BzKaj4bXTbK$4f9z@^jM~Eox@HO60m42#@K$hxDr% zWVd}~^_SL9rF1fy#JN#Fqh6SH`Y=$spYWje9Wv*Cq`!j2 zs^G3gto;S`t2$@+30LU9qXYU^Bk1Gk$&Q4_mC+UIf;!Ojfn};L_UOEiaQ^^^ncRgP zORbCOF16N&_3P5~wvSps`fMap;bf$mjrSy<;7`!EOX)+=B6MT=uc2~%q3HBd5zFc? z;HT2HkEJ>xi>DvJnrS*uOV=GwMj^Ue__!C-dOE+V=s&4wcj&#(>96H2XXrgwQx=P& zNv4aXl<8s{(d$V17XAzCkL0A4x{swUjO%Q>TOV5x_J18f;7pzgl0ShuwqK??SeCCx z)bxKz^?m-ly&v!p`mNIuqzw z80ns=>77mV`@;7^r}*Of^Yzgk3+l}^ZoLS4GG^m44q8nIt0(z${8^^4)|n*qd!puy zXfLzsU+~}5&qEfHK87tMofxh$YWh&jeEr+I~1o#ad#`UxNCvnuEE_UxR(}pcbDSs#ogWM z%kTT%dTYHivyw^H{4u$ib?4lD_CEVV+~Y-lQ|lYQjL1ZSbt|-T zqh&|9HIahPn`)-=4ZLIVnHw^2xOVU)m4injxJe(V3*?s#M@*TSUvn zH+@TatNZllGk7S*OjDY);UVPg9{?(I^pAEqsPrP9aq%j{naPsxn^ymvzfshpN*Q+q z1^8lb!TtJ72TKH#9(qXs076lcnGW@a8h!z{olUYw`Z#ywSGX&6k}?fQ=tsu>E1~J+ zf80(NCY>6urdl;$-6*4^W~olRM9LCwRpe>Fmn~)IAB5?qY-;f`n$Z!Ht%<;YUZ`h! z9=filp2=q=8snr{Lw@m-t3N9g_+`E`EBc-NbS+9H#p$;O7yUK|e@LYGJdol(a+YzNNew=6iJ>0jiNXWw;>yu1TW z3}3T-zszGwn;we%jv>GN+cX0{lF_Wt1NTIAww@SDS@Gu|`r14yP0IT+_^Qaf%G~Zq z-4aiyyitZR{$)4r|+1f2z3KjR+jZmm9^ z7>X;zNJB3i*xET!UcGDjUa%;HZ1Sw|#QUtG9ryEgSN5D6^9hB6)$0vXUgAUW50DBj z6tbzX4*Mm|FRKTh)OSDZj5}&Op5tUOPjs{KZj4j(f%^Oiy=mU%>ONyW)x9FkwdO9` zf1jG@GV^MHJpezC=0_bJy!+g#EEs;pja+l!NNi2|w*o+ex&^`l-~a#suz~U;N9!Re zZ7HshuTKl#G|Z0RVdbHr90X%b+mS)z`4=-pSkNWlETeYYQKPD@jra(U-Hh9%cZ=$s zZ2zS$?q*Vsoa{Taw{1%Bj?Th67UTY)D}>lz+RdqhOX~e#nn>;aA#j?#wV8N|Ge@25 zWuMEf$jl?x!3&r-_Bv`SiM9*}JD3#`7BmpKDpzanKv^{6wpOu3B4#7+NSKVpZ^LfR z{r#>jD_!^>puTC*$Dvhgl=(?DTPeTs9>1R0k}`*y>^GHK8Y9)|-!G=ip+v!vzv+fh zI_?x=e!yi`Y#b;2B`4sX(i{w4Y1uBWmC?^o&wn=$$IY>TIGa19GUoMW2oY%h4DQ(s z=BA>p#}E^qBBZh&sy6Ampww@{_#!EqXlSW_;8lftW8erLPmtEIx>e*0GDCstLtO>J z)9Dib0R|flp9>7aPQ4_`?wio*oaS-A_vUrd|bt86x(XRg;bFJ5&18j3Sn> zx}P05IX`#{U#6d3w9Q>5XVO34i<5JI6kQh~W~ka=uoLT*cwJ_Fcnh@PAPay&Q%5qM zBVC>+Q7gUr?iZh4ZzT9kUOuy)wn@>ks!+mt$)KfuT*$B&b@RXjHt1tFL#Z`?B*Z*BGe0Kc5; z6eZVN2@iHwcuIb1pkH*cmTmBzyFDX3bi>f&0P%5Jva>?F{7d~dhy*$yr&rzuaN;gb zcenzI0UVVQar&$G!39Q4(<1(#58zULYBK)TV1z_EI~szF39TrZILJR+s6b4MYSb3v zG_Gm1f5>@jTmMDm;wopPpJ?@nUfO%>`^BFgn42MO%1;4bx(vJ)h~qS=Hz1nm{isW> zik?PQ0a6tsKf03~m?F~s#hkxRQRegh{v#A!t`bkDRLOVatHyJtN1XU)jBs{L%!`7r zS914Otl&_pfpt3$)&iTXG7TF_U2M?e`nS19QNueRt(++ ziwVR(CZt!FYE{zWP44N?E~_7NMuNBaO{T|~C*0pOk8gDWC1Hp2Ph{awv}p;e0*!EE z; zd{z{Tls?Jedw*(Pr69%WUyp#aO*|u8>P<(s^wi*T9sbmWlH19+Bq#2oY+YwJJlS#F z)I*|Xhv?wTDLE9klP^g}vv6%<+}!S-vTy&GtqncA8V(I+uZFU9mYvNx1#KApC{?Z7 zw6rOPyS6>J=b9T}OmKp@%qcXpQJV&Q;x+vG`K!eW4)-TQ>tV@nem}L|{aoE)XTD=t zh@ELH=eyWttlI8j! zjF49aZhUc+?Pp&55}oFJb)_BJlM}g~$Q;v3n4UMab$UX4pjH^Cqceu=g;j&xMu3yK z=)z?v_jl2B!k3{eB;eO+kX0Z-cHtAV`ef+GYO%u4(?{FyrZ&G>As;=s>*T|>JVj+6 zh_}9EgdCKtq1Ugm$HuPtVPZ~&sy}+L@r``u_hw5o5%#HA11;cIDKPZl<^A03K7uxH zm~Zr4OOWnU^!80R+QN&s;s$Sr!>aB51LP;pUVdp;U78@Ws~fj!X6u6G&U^pD0v}be z<%e-5lfrmtm^@W=Y-BP750Jpnrb_fjb!kQz9x|JE4jBU^pNb%pwwY^VJEP@rRn(I$ z@;LhrY4UZRThFPZ8W3s>T_PL3LOFCO1XIj(P+HODpSZJZ3ad6=I%h!0BHo66b`CC* z2H3#i!FgW%Xb>L{^kKN`~SSj4KyNWzi`ef~orm>K>3mkKNtOz!1~K}ivO zv(WLp)SNCP+*QCX>;H@#-_|Uadke{0_rGtmi_o8_6T)%E>Mamg8k{Oq;$vzz=Gr_3`b~9m6Skw>*z;|l z?4CasWPH_XwrBB_^InvQTGqV9zuXU&!qYbv(ER;JOw?ueD%U>w|&Cvul!T=VGQZ2@x#wXj*+zL6;ec5f&i6yQ6eKCa?REoBBZEiwHnn( zl`|vK@67l0ss35KkX5mY2j5+^Q!Eoz$K!9Br`b4Frg2st3|b$C@Sh@zf-m@_P+=KG z&HDA79aUsOI}2P~SZVh!t9sp241%6l0`g*}lsB zecgG}QvX}Mqo$QRq?c6FGAHDz%Vg93K!z!lwhhQu&d*k*3M>mbEwyX^0QGamN4jzD zO%Rp9OeEfKz8>Urf-8NJkFT4Pl1V*@=tW+yr_OCQQMiu8)H(t9yUg4 z-%LG-SzLvtdypXrmgH*?;Y|j^fC};zl3>2rhnva`ko$&VAq{Z$`#gzK(=v&1H(I~| z;^WS$59r|>Paj5_9VQ%TgeW*O$+r`VC7Fc<6Ua~~${=o!{o`tsoyu;X||Ms6zG6iIpj@I5_U;u&R~)Zc(RC1 zh>-gU`O_p@OCM&(zR$DbR|Oh4C5n#>yOgnAhBtizudA<_pRc+pW<&wv2mrVk8aPq- z-eH28s9(oSop!Z5;QO14)|H~s>u%r{_uVVgXSpq}DtbHV!RsrEqlN6x% zD8uF;19cA#WgNhts&o1C%2+>~a`S0DPtkJYtZf7D9D9W>P#YN@7KzztQmTuU9}rH} za`sX#e1P`+opu%1DF_fh!N3l~KqbzHJuIkDc!PO4`om)s<8eEvM-Z_n;}`}xPYzh4 zjBdFpztsn@rmR%@(ziz0Gq>pJBtyahU@lN#D3n5$=%q3Iv6xObA#!~M7xN)9()|9T zU1UEAjx;a;jy<@-k3AM3zg3&@yNlHtkc2CyDnY4zUfrE+^NkR)&AES|H&N?$#0;38^!-5ztBY-_i_pF(2h>gOAOnw6(cJlafTH_O9D9=W9boaFANEa6aOZ zo}t=(H{Tt8>0W`_=4jiKAG;hsT~<-r zS44B?5M5iM@4l$a|Gem(E1TuiXMI+=Lw~(b*vJYL9+Xtoo^PYK*GWU9tt-E(_1540 zI{go@S6C&P&odJ?C=6sl1&l)3$WhfG(+Rm9<`%wXz`3ZGAl*y+OS2gvWqeQUpfG48 z@C8KpQPNIGX3x)gQ0gYpfIX*cz3JWTt!rU&6<}SPz5I0Ip8t+-ck$Thv$DQKFY{vf zh@F#rh7yDnDlOW3Yk8BBa`4g`>(x1V^ADh&L;km$4V?8{6&zq}|G}Ihd$2~2B}$OT zkLAt(HRHr*m27M02dg}b|5+=1RoRRnt=a`Xr=IeItm}!ryPvOah+kI=kaV4x`tJ=$ zWwH$4RMsFKv)=iEDi|&#D+#IJ7eoAjmWyT#Zmh{S^gXxe!gyG+{KTxf$*4oYGVI=T zWAEGACH;6frnIzkHAhimQ1mWp8~sWn!J_3s#cJET$P>m(ry3{UKY)oa+bQz|{cSd{ z3N-g~PQv{M*qBhg2VWex;YRw5%VY&yjpUw#=k5<4pk0KH!IN(~E)P$u{$Otmq{ra9 z#YcnTv*4I|r{{8A6est*}GG=V6?fPM46!E@?N#T>SPxdbc;3 z=brn204*}eCk%cA5k54$f!@JfZM9=)dpsE7#Qnf;&Ij1)>fzCzp$QvPI%a;B6pEMeq_Iu6~;UQ z!#%4Ufn~`9XOGEs3EnjNVAZd{@bm*g+xXjXFd~wWr$loIrmhIJ7N&c#eS?UL@9e#3 zmrj4;c=%itdQm6Os%_)Us)B;bU~_JXmf0nq2WazjbN33_CU}1&R_?FI!FbIBVutv5 zERQm1crA((p)&_rka=Kilr&J=m=!AQ^o~`)+3~TDdDXSCo-fm=&}p)pFLUd{In}W& z_OS0Sj97ZFJM+6x_EjLw5|Y&wf)G+`A(gGOB0LP%P&rxF5l$@KrBUfO)~$LxWFlH& z_Kqs04(|Or%fL=jdk(ADHi}h1=!CnzUj^f7KRVHkQjhG_U7j_r+K!@dE@2bAsZOQ| z68Lh;r+LTKjvgaVuoDPd&K)huII%+z@0jTnJ9>nbm!EnrHZQhUeds&{b9uHZLfrsp z&w_kTlGnfvodo*7p5*?L<2l5ifS3qN;z1hsx3TiSjL{;l3zq#we?(OpxBvz~Q*W;?mHjddg`*kluyq2t}7Wr3H zZ=CZtWMU%!tWaa8y{a}?m%nZ7w}W0+V{yc>%UdlG5KpH`be-B!Vq)?U6V|?86Z}FR zDn%C*6kn{m&aj2;z93|8c~vy5ZWz7lEDeMCTrQu3t2f|E`9MwOpAMgDiLo6|9k?cR z^+M?;dFA1`b3=Zmbx_QjIaA*;kOaqPDpw1l234PU8Lwv{y>Zj zdby$`GO}3WfgofI;mhvZDy>%P4QX-CGQx^CDlpsHtv0wcw3)h{Rcw&9+3fh7_KU@w zO=DshM3HKT(Jg4dlkYEkir^7sWfT{7?(RAf2ap&fdTy^jc$@z6q>W1C+RJjYrJr#$ zO)1IhJhd2H0-iS_vE|Uiax8;^$VS15K4XGFxSVs@T>@i7`=lAq$uS|1RohJ4kaSnR zRjf>3h%OMH8c}H=Sb*cs(v1rZp;}8Z)@fL|MI<@>9G#j!Enul`4FTwcABuwO95_^V zSl^n<9-T3KOo53R9+ZH!JPX6ci~cLl zJ%MMdouvOWGc~l?)}FQi*-FJhia2I9W%RW>3LTfc+`6FG%y(30Iw!o7?g;Jetc*k?BhdU1+Xk6pTMig3k!#rs=t zp2%ybP5s5^6sY9gay=WrWd(C1us>ZM{Xje)hcfH%Kn9F2-?Po6gp*oDWwT{I9VUwT z^ZRt4STdA)K|7eaCO?MbzSJ0u7Cpu?G#> zy*`cvX!Q{aBfc|tQqfaza(abd_fDwspeJo^8QZCXoY71Q=y7nUPq)eE;nF^A4(iiHj5!uLsA$$GPRx-`eQeWo(vwFa zjY*AkLK9}F#`Ap?gsR1Pt&yOhZ%g&uR2{=pj(6u^+i4Ls<^Mq0OZ3MnKUEnbusTlne>*F3xKPKfuo$rr>E57&6ZGGd}j&RO8H zJa$Wb*Cx=v9vfVMTU#9_T_PTauxt6Qo^{fQ?ihcvc%U`oz%Ew^GSZLKm_Uwm zjt({%wiv3#I6ivz(4o^COX%(14HxxpSg-bLL^`nm>mpV8x!)+Dyde(ckco?O8gaho z<;VuW_F%!jX&`%3%hs8ipCW3p^ddm4bR`L-nW;Bo7HN-|M%I#$d3r}!m6q&n8>5(C zku*9_L7@L;e`r7x4Cv^AvHbPOLzR-43#%m_JtU}I$PXOY!~r@4y{ii_#KT{W+OA;T z{v4d(ltd5bapIfWj)34;vV)@2cq|hz)%yf>+>`w{6aFI`&OwfNp>e?op$VC3 z_BH=nQ!2qf?nZZ=u&i~z3VXRv!%&gx=@pM2cpXC7t502P@t*5<1dWZhEceg4(qurP zmW$DsH-qTmj9(fcT4sn7s;81be_8YzF_}A@D;=lrV3qaX=&LM8t#MommZAB6lA|Aw}A>6!|*}zC`*(SKpB>oS|B->)fRd8 z2-=}gas!yp3%J8_8hOHG=KhsbR0(8fu)B~I68C$1-VKp+g4s0O64^9K{^t*6hh#YV z8Uakmx?4{gA7v%pI}HiF%8G0w$?>WszA;|)=n5))@+4CAs8#?4XbXa;t2o?eg@HI+ zyR{(QsLl!NT1>TlvWzF|B(wYgXWI$WY|u}H5JC;HS{+Nv9#_kw~>F2fzHWx&h zdc1-dWSS=pODLeAR1@|q4vGa(Is`P5!K|XdQ*XlYU@ZNOUq+8-OF^LB~ z?5^!cW+&zWqgn32G1rd%-~Cc59%$q%b`||>(J^sk*B(~X_7#HsBaupOMwuABUzI2# zL(g}GPj?`p=3HjHsb2>a7LGI){myA2>P_U0n|Ep^0P4L(hD)8~cbhkF#z96s;s{_M zyrFD?Nq69~3=^>yfoXi?l3ot7+BR|bHK7bP^U;d-%2tjJuBpuhs$zx@N**u3z+73| zDrRwF1(GM1EZ959Qo;ucz`4yT{Ms;|;opE^`Tql&3N~)^-Mr-AZ2HzJ;k@gLSUz3u zT)d+}sdj=s@5+ALIl^G)IC7Dep|Fi}$CJK`2foW!zXLMzwVl3~EDx}Q(9KO8<~dxy z`Np`QaM#Yp>Lg~>+Sn^Od5h(iuisv7*vokRs}njxrAgVfm{l_>tV>*D(mCXL6`_5!z(?l+g>Ti z;Iz@~UvI0wF+~0z+BO;zI)K~G;t#f_fMSU?8qe7!5)bjOnw+-mwEvX*lcD+5W?L4N z2vWn_RTe1ty4Pzj@&zFZ&kdnd1$(!9>C%w!X)ibPg@3(EEWyo^rnwFqVfdmyH6(3; z%gv%Mn%-sY90rbWznegNY*K?hE}aNb*TH8w{f2gX5y|PQEo5?^P)WEVp<0cU?C~F< z1$cQ5Gao}?Q#6nzT%hOX+O@x3Fj$Da*Jc@Wl_T-StJ`!q#52U(D+KAJO^d%Jqm_-Y zjkE%B1iRoP=}Ge;`)f=XZBX_vQv2$A0`&a|@EVf!6&E4)HUSFWZ z*vgFx=987$^p(qeH<$3>KY#CKungFLGD86c2|^wxZ?i?@V7!J;!*TZv?-PxDn<%?@ zc1|oIrz(lQ7!d&zJHLfNWWshTdk8>(j>I}2E><&`P+rPK>ebylGScO-Y(D*AxF)~0 zP}fcHUmq*{5+8wT4_ragLE4cyVpq1EPWM4=bBMNi?(g467Q8GLqoFH8%igU|{yUBD z%lPSx<&DnvtuG=UFhcV9%z~hQfQ^1WlcP>O%4kn@*cTm@daHM7axs&e_zl>D_>19f zSHx}{ccWC+&9rMWxIU`o&}H|#+cz47W6#c4H)eJ=5$r9vC>Wyw5dOXksXJ3?PcKmf zYuNTet~MCpS?#bTz}?~irAcZ8d6<=io=b}IoR|-7yf4vuJ+?R<1Q_X}Wa)Hbw8NU> zz?ot%yKY|P@T`JiLLf9e3L+SSW_QoBS=xiAOPOw0^P}r8@1}N*&TKA9vp+-6*Bjp_3kZ+*ix)U_!`N&c_XyL_w0KNj{Iy{Eo6j4K7F(Al9f1bn{5wozn#9MLXVI?N4((owSqW zen3=Rg#a7G6_oGc%S{HDFt!6BacEaV(XSXH^d)akc-VN@G}@aRX-|a*_8mUSv8#Tn zulX;5545hAt~dlLcKATgL4&9k>G@70(A8a!8u=I%;>9F8No@^za_Bm}`f4Jha0? zHb;z_XqRY_c7+8@2Ooozuf{8?$D|Uac3Y;advCNj!zDp~+%IM}#?_pARyult5rL z^1wEgS+#AN;+NJ`P!lqbUeh(V-F!@rG)cCO#|A7*vJ!2FK^NQ*8Mq^CJlD;Gi#W}P znx=o?ckeR`=~9?cXW@3EJ0oVlVN}v$cqr=+H{cc4uAmI(UMKG*g_FhxpVm`6)_OuJ zHlDU+jMgJ*P8WKc#2o3X%%?kh>h8h6jZ6D-<}K^t#!Z*^mte)fWPwzz#v^6^^j7u=zY_Mku7mb;%s;)$}F#cp!?Nj7ZOC#b*t=Enhc31;;P%5L0=* zk_`(ea5(X4=XGJmI6NBO>Q4BIO8RvT1fN7?yCSJX413V-KDp%fvgC5$je>%P90`5$ zmbTBwSR(dMt_g->O_$_(FB=)#b#1%iS9Eda0>`89XhZ`{)MomTUnOGO$w?~{-w{VS zw(PbAbvIj>Jg{ z6mxmk*~Yp!eg61vnE5PjkPAI{0E0s)w_-LwGpZY6Ly?29D~CIqv-4W~~}4lNtK5hG{;QQuj^3?x59?rC`>s#mvFvRwCZk9CI)@({s!; z3(J`XjC|uAzvr*)+)%81>q^%aYT9?G;@wJx&A^beUzp_NJjCFz81dKE>p1$axgji9zCXSa4np>1=!9RIgXmpETJ-5I$O+v)03 zA9oMbb%$C|0`>wc35>LegpDg6XoTTyhx;7vOxbJ4(L!O^*{%qWKT|@VKG9OO!@17; zlogq_m({C2NP1ADIo9(Nm+BUmtNro2L15XIq`%SBnG^jHls`?0VE zx{B3nDO4NSo*7N3G|Q#gg0$Bz7_2RRajx|pA-3K(M0G5~Os+`g)AtNAcYlehgR_|2 z(tj{@@1GP#<8n?o{ze&-no{MKxVP-x!pN!_UCT}}QB}%Pg6}k)Pcl}0heRr-*iqXz zUnQ$jte$C?7d{_qu?=8VGUKD}skWdqHj&${t3*LW4aOMtaAep-adZ|iqaAz+G7DNhnQ}Yo+UQN`vL%RbL8>-)?T#3Nj9s`bL ziu{ko?*Mm<$9@8bxsiQyKJMMAI}Dfh)=7O~QQuNo^;-7Rp(AI~?;(-$SGB)AJ>~5b zi7wz;EFZQxZjYGrjEMSpb3J575Mm(E7=M>!hi%bFJjUH`n}CW=#m&#WAeE|y=Dgo) z%KUkl6LC3OjdoH^X^XdwFh0plkHuT@207xIg_V}#wAt;$2n5OLzTv}Q9$y1{^;q>e z{#sI-G%-U13t}zZF29Z|)fd~w(U4`UN2;4_Li%JX#MlGJaNiMhM?~eB+mwCFTI60P zauoX#-ZEC$XCwZE+W8svV~P1}_)eTkLgC)2vD`=!lHevpeU4m!ZyBvkFjp{NzKb3X@J*?Wn!{ z!hD;A^n7+b&u`(gjuo@sH1Y97+KP}72ITB~n&2tCciP9?iV2fA)i}+o&de=6JLPgA zEnjI;asIe%l;h`ej}fvWUzHvs|MQg<*W;~6!atLE<5zp5Npy;7T3R)m=Vva$rpU`k z6s%72^zUcQy(!3wq@e32Qa_7#Z_Q)pA4p?oXAGp-*jZG{#_jzj6rf8*YZt93ha9)+ zOQ&HepC#|=Io1Qa5e^bhIK`v*-`du&a|<&W9y%HAL(-pn-BV3}tcRnfp%Obm>62?o z_x3ppNsqq?6Po+z-EH+`x57+W1>>7}zsc`WR7&58d^{{~=+GmTeGa%IM`NoUxMR6< zaHlU8z2h^HyV{12MdnrD3z--yI%lckd8Hu`@8)#Z;H30a{)F9^&vXLkk>jJwM@ffQ z$4s!&s-5k3zMxjqL<@z}Zy)PYMzFuGRXM41&Md5kV@4in-}Iye8%(#jo#ByHyc+h- zkMh(D6&>lvkjx}q?@kgx^8gm*x zU-~IHDXs%TMI{U_@0A?JK+BK27}+hpXX$NR(2##3{(56x{W~Ocy<`o?isiICo7o5c z7+MCXDxF5ZTrX3dRO_TZf$>VZLfYOtHV!I&UN)$b)*?0ho*RW(LXPLh{A&39cEfOQ zK$zEVJ(I`&7^bHh&3BLvvTQ;^{01$P;EiR#?W5ux@0usH}LGhgk+#bk!TD?H(tO#jz zzrU;f_RO*F->OpQW8|^sD@c>m{+$T+V}!dlSvcF*(UL6lyyMyA6m8CFc;B9JmV8EQ zKSEPe%L9n^3!Z$^vh{ppW24%;57G#PJxyPSbeJdDmQJxZ3KRO;7b7i_cIgp9RtW|df6|Ovm;Lkt$gJw36h7jwjgwMsh zDZ1@M#@|^`q{3dP>?V#_XwYiK5&*0|%hTudUt(Vx{B!$TSx*J!`NEG6FH3rS*O8Gp zm6p)ryB|!0L99z!4;T+VIDs@vv*rv6(fThSIW<-wy) zrxL%lMh41$smydQ>C3t{Z&^#@-sx#?p&$3n;T^HK(=+sO3KMENc~l1;uv#(yEk}gU zZsHl~qeZ3%+GdaDV9qacx>(5mqKOFj0WD;eX_SNhH)=;k&|VGX1K_OZR6=Sij=_ zx+ikiCKaAv%2g7Gk^24=?c27{r2H7^PBbpz@_1tbG?rrxKGq7pWgqbO%e=*xiMKnY~LfnS$?uS>Ol|u5A%S5hlPbQ9R6cL{4eSO1w9-Cfd5e- zrZ2ayDE@~60Rwrq2fYu*NlQA`VM~Q;~>Gdq{XAF8ag_2fGmETkP5gP<(6(LOSN)n!7ttM$8nR8l} z`IQ?IhL3hbUIA161ssPeN$R-LwM2o~8mr6L+b|(g8t%1xkHz^v;ox@<`!O7w731?L zTOwI&&S6iZc`=DdB1W)jV$I~||9rLdF&PT2fwL<9^&;5+O3!Hfz)O4zl1_bd8hqvn z%b_i(F{ElJ9P{N^fZW#bF7RVFl|n{ONR#<~mQe@%wMBEPBJv)$1n6Mi3!P(7M%EYN_Xw5?Q=5V#-W#T$OFY2$7pQTYiv)`C>_u(=P3g50o1B zYV*>6*yr1$C0rFO1}gdc`BqqLdfVlhmt%=2@B0TxG?w-GYt&obe`tZqY|_*}vV)Hk z-(l3OIF;}iP2T$G?sXUjSZ&SMzr2ggUAz1K68v7}$kvd;l=rDm`2ibZ?yT;PqmS?O z%SNP?TYjl?meFKIO9%$RaH=(wX`S+9en86VNUek6Zz5JWcV&=!Qq$a5Kbm5x6M3=W zN&XAY0z8wxG6UXY?mfFdS9q$kV4n!%%Yxr>vyz47fm1rAJ6 z5{%n7@rl%eXz;*1Rp$aBay<(rQhfVkBGLEJiz`kkO}p}4;sUhN;Jhz3xt`{;$VI!u zd_RY5QY82on%z&``9*8Te{h>#?)>b6eD7AZ&C*2{+v+!OH}B{l{*zu>g_OlkTp}kw zQ`X7LSOTY7Ppt7UhAe;Ci22;mkI9+uQv0UvDBKs&lx;a62L8lUM&LnO<5K96E?PwH zSo051So0ih9iO*1L&vnqZR&=k37%KP6V&TEen~M4bi>o6Y)eAuUrHx8I2=H#lhJ+Bl5`R;>0gqzP2VX_6Gp zeCVW8SGZGl3NDxC<~WAa=81y&D*%j^Vpb0jN8n?sXftUYSxdLfM@gdXQnw6*(lS5n z(Wt(D7pqW6xkNtyZb3BJ!<5((hYF`?wCJouYItloX0xFAx42)gA|TUT)%-`y4b_=4 z=;b-PY^m95y%6?wyk|fm6Uc7O@l#x_?ftF0>_cT5hU$+qvf&(94e2r`f16S6Ak~TA zstR5MCs^>Ks6PUQI#C_nBd??Jm`&76=aM6i2P54-o}AMmzthfJ?2Wj}SpAaD*2er0 z{^O{CK^#?4=lBy0j>)$fRLh^c`yS>)vf+dIDA2+ccEtDJa`F{efv5EFvS-p-sLIl_ z#Q{-#k?;~VG8N)WYb+lS`>hBKK~e>ooPP{#Df4Ty&p%i%l;-+s!LELEkstf{0h-L< zYVezKY{MYfws9YGApbz_xQJ7WFCf)2O`~?-HW8xql`}=P`dSbbBEvtnzTSP)#SB0)bY17NIjt_mwx*m0Lx zMN-O2OciUAmzCy_d9*LgiWkU({G-gXqzP8rcJEx|u^St~kT@d<)lOMB!!mRHnfLP3lh zyXLs#-|C5B3lE1|1NEEuTKno{Kn53|%Sc7iesH*oY;YA!t-*lR7RoupyiZJ-48zfh zemnGQ>f`qa@#m}r_r-S=*WciyyN55LNeK*q-S{;`Nw0&&aTsh}qg|&Yf7MF_F<$@D zc-!4bz;?=CBR(_W#b@CO1 z!?abyy#0FqwSQvX{;6wQos!)Rv&30~Gk+Z^s*4-3O_y4U{tgglVJgGEsQE-E_Gq&y zjOkyZZpKv99j!AH_Prk^St_~8mry>0zq>(pqic+yI;gv*B|25ZfIi(9HN;Ws{g|HS zM82o8nuRs`Gh>w|ju=4maOYjT{cCDFdq2~?g6B6TrkOSZYL`sO)&c<_Ksw?dAhD2V zp^3#y)D5W}_%XFR-+Ul6=UiAo5zS|iD{=8^Os7yf2(@Y(9|6sf{*AVTPn4j+(1X?% zF-p$oO|ImxU3S5V$TmUjbP-k7xeNBa&IhI&ogn50gpv93ea%$tJIya^dJRCgsH2Zn zF4~n~!(w>KvX#hW9JU~jU@ePqr1hIIpMa@I2pN@@6uNj+Wm&_*>Z>9f_9C3_4Z@rO z*9prz#VpOhx?vf_h4Z=o7j1bNDQs)Cbxf42TaYrURGgMNu3~+|3_W4JEk$ye*i=V4 z_lM&d=N}aBMx(3=MNXcPv_|$QL;bC%uzsx`tPzj#n+iS{%R_jzJX6 z+pk*PKQ>5JxyNIPBlw~I8(TPUNY)nQgyj=ftt*556;oz07M2*hVm3u>L54hN%@}$I)j_<##CvU@D$g@3AN}6v;P3AnK6j2P{FGyHOhABd!aui zSNp*Oj8Ex@nP#P{NTmh`KACrkC$T$DuzJGoC&7V|QT4aKAE>oNfYF2*rWpJuv!tUi zji3duIfqmbR08x3&KU(_H z)?V4tqMot$n@q<%4RM+Bu(D{_8{~)Um9|h>RTsF`AZ8#}LdFFZ6BF^A*@x@eyJ(8` z5hwykttk#Wt2g#_O~=H)5{pMSAEV7V@GEDe@i75EIe05l={h>1STY{~^BL9#&n;yg zF%tMi&CN_TZ42fjSh5mOP_y>=w)Oiy#bo_KSW{jIjjc~< z!t_#togT~KPv%n#7(v5iJ1*{zd5VKLOSN{OhPBghxgaI3KapL`F~jV;(0^>#D`sM$ z+$a8=+(&=24H~?jW1OeRuF(hBfLoiFuy`yD+;+U~(&uwpI@-#0AMpT|Y3Yq8 zx;p1J@QCvq5hUavfW5a{YVLt|cu;envQfMwK8x}F;QafP+3?Ie@*$k`D^pbs>DO*u zn{&nlotgPQ^ogxcO;)KQPV;vxp9Aa6?OMgtPvscxI=3_Zx4{?iL-&p>^#&*H3M4N# z@3s)N4|IP;T<=&kY~3DWkjI?IGLp`bN2q=v%3R$6^D55RF?Xw>h zH)C%HeiHgol(7#QjoC$|&h;B%!)_Lf(Xu0tpKM|=k?!CX^*3)5iXIX67eZKC$HaKi zzN9MJnQhzR6fy!DjU$HoF0CxhPJ7alhtzJ;`hJ$-2>yuOUqmE`S!+=}0wPKKX?=R- zzRadp4+jdP_W`qf^t4_2C@ZaK1|vk_VHrE9OQRT?YUHsPv&Fz>HOU?8Y);|z$hS~!nl8h^n+Yk_UjIQDip6No2!V`7 zpR=OqAFeb1LCoWWbP$c;#j}v~+FiB?5jyA;SPxP3-t(MYyu;wnjt$|0QFxg^$ zal=!V47C#a>hersxUJkXE1jn&{EJWZ5~!YZMiE&1(38f!@%E<}F*a}e%?+AQ5`8#$ zpM4roSi#pj$jO0wZ5Lg%&j5!fLtVQ|Dl;D5i?K`I1*9uF&8kQB!RMH^@mozdU*aa; z#I(Pi`~#>0U^4(`=~jpg#dpQV{eanKIGeOtcDMZx0-Q&!X{t3|`A*!ZGbY>=fkl|? zM$Fy=lB)9x^-hk=ZP_Ne1Q}KkrGn}3Qn3XjtqbhUeH%n)g!_JU8BMnET;kOI&;JLm zKvBP%AmXMjFG$Pv@TM^}K~mNi#HC9d0xgkwi@S{G=Hm2(wp?9QcM}r1l@?++ma?zn z)>PylGZ!jkID``O{E+>J==gbudHRRJD>HDOCHyk9L%Fx6tkZc6?k05=1R>0?D1T=xfd5A!N!K=FE& zI4MIx1k`k2Xp3b#?Ed9txOCm;_+b0YfxHx>(-ComQEIEv`iW()y>$NokbvS1IH!I| z!1_8IU3BY{q-JIA(M>z?tG$QJFI9~Ur?ITedEam96k ze06H4i}xr}OTTD-;N^8nJDTvvmD8zLT|ex1XpHSDy=LG;wxAV`ru%L%$gf-ew-CS< zOKYV>4pgMY9Nfx)4L?YCR$yDaj6RbVYnhuraz3D9#f8~*o|1!mS(8TkKukr6pTt#Q za#rpjq88YT&Lx{fPKnA88q$Va-XqSw(ZTKtd)cw~6V!OMdX6&Eoknv9MwBs~Ug(Xe z#VW<$vZf}4mM?gW$iO;|AE@5Dr2&QEP6)b}X9;LhR}nTP01J)E;wF(xJ@XJmTtJl8 zZ^YmyUY$J03bKwVR%6o$@dZ`VHkoxaHI8FBVmyss5ortu5ef@{V&Zg1X{aHNddAGf z#JYs3*AUdxGD9rwGNY-+YNKh#2Ah>(3^7Dom@#iI=0!xr51u35CZtW>HfF zSZ>IPX;_ai-OC?_J-!ZZT)f5_mNyX~*xch2r^AR~VXVcukj4X#=7ZcHsJ zjy@9Nyg<8A7_O&fYHR}%VM&UN{i25>9^4QJYJPMC=hw&OI6_?=fP z7Alcq!$D%6zGE7|$8mMe6q!|nDNdLnx~QRDztr|f7ROoll<{f|l3)~=Ze*%;@$&wT3jfkEfQqcsWrFD$3+)JcgV76$gztip#yp(lM z__Fx|y2dZ_4$iHbdH(>Oqg7c@Y3dBsGvx!HaBUYPwVsz1Vcf}W^?+iQ!J z#Npc-KUGs#K^%`imZHCb^u>8~N`M1(Oj8ZKO2N7LTR#vfq4mY@H|&gYWy?C1e^A@o zb#C~^1$6NqAGwkw#EluwCrA-j)WqYe*KA`pDN$ZgLqby1aPyb~iJ`9*yhhsICYtGf zX_w>Zl~YvNcZ_q?iUf+{rYnEAyrdg$>_1qA%L`#%TtpgR3N7~gi>3=Yp*0ptuQ8`i z+wzv)l2)mXTtnt+m|?AJq_o9I3Zq$yl~#9UxLDL}#oHLyNVe-%5DuAwY*Z_Zb6q)x zWhq@LFnf_*5OeML$ka|d=9)FrKASHHJr?2L0o0dSm|NvJJd0Zf@`ue z{ZKWLx0#ccP}FP8RO2uxna5bbbR~<^sSs4VE|EUsAs9ip zeCjC*GZ!47*%GB1x*jeI&%o3hVY-^u<8h>@gc!uD!wSjqM6S}SCgpAXKo*Mi1&K}| zgDMq|^vvWQj~zBe8x-?H00?hW{-=NmOaXGBApOR{2CZxJ4uk0tJ=7$#g47fuS}iYH zsZ4?tRwkrexkauW<~Yf&79nUfV65sVQt&m2gxD=WxZcm z%7*M@Zj$wI0#^!U&$yf`BH5dnFOu6ExUCf_!5thyO*N}*R%Xc3-Jqjnsa+L-52*2d zC=R0lFp;eRS7(kRV0D&hD8o-^6dxk|_1%6WYdDFnhH6}z28QiAF>nKwmGcSPsNH2K z_RWTKi~j)0SaaDabbe*!iZlbnTd25QmSA~6hf~I`6A5{vd$zN=aD87_>SR-F&=h;b zuYs_ks>~k-DOE2`%qoUa914pC8*+@HN7R30-VI$3jX@ApE}nJ#!0v^p1^)mppga}M zl`^lv6eiUDw-rb3C`e4A7-2)L#nvF3`-r%h6T}r_5YvdZHK;dg zn^IyHP4ckq$DkQj=2HibcJ|aVY?~M)`%& zpaH=HF*BGsf(4S!ClQENcL8-R%r%%a##~F7_Ytf`#2VbSaWE2GKIkHJKLj$=<~xVM zn3#hW@$#pbW>G*NWL#Di7!K!*_lcgZN`{>*RzRB@bJ6Z$EO-uO zCzMrY^IYhR6K1+IyY`SU=NoxHi1rn0#M2b?VswWVbi4%3e z>lUE|8%6&B$n*^MfR2H+SUR#Lk|;n7Kf*xH0QN%;0N^3JpiMBw@@oq@tb3Jg2cW;W zjoO_L%nAt>D=)@#DKfQ7KXHM#H*50(D!!J0;@7YI!sL*etIPzmSaNi#nBrIX)IEj` zuk}lXSH??}U%<-h)7KLwe4+io>faCUV$b{enbsz0{{X3MQ_K%;`I6$+b+&&K1lQQF z;#jFWxrAO2WOr&Czey=>^6zm{++(C;L)qpdSS5`T({)p9uZX~2c#CbACC0L`v+^Z_ z1@|=zY7c8s1pZi?LOdg!4kc z1=|{f(i}Ap`E-o3_452fTKXYYjY9an!4cozi^f?RvElM zs3o>NLoo&QO*7WeRP04`$MGvZs|QBc;-68pQPO4K>HCCgtI*{eQ2HaFf~*vG%uX!O zQ`hb@QS16lxMMQ-;K)1oU}wn7aFTj*RzMic>F?k?rmhAn^Pz^WQFJK-Cds!ZJ4Lm=EvR8|Am>wMm9^Y@QrV2K zjm551^kvoa(2I=9dz9G8G0L8kHXFUcAZ=3Wq_t(-WbUo#%@wZ z;XpVon2z=OnB>-G0Nute8K1o@Gsc>d) zP<%Ownf08GmwKA!VvC0y$Kiln4QcZmx(tC24Z>CxS;9?sji@}T(fvhT+5%rZ#?*N0 zD2ri2heljy8(wa?iJOB8!+LML3MG_=vE~OF2#C}72{d_vhmLi8P6@|X^C%%oZc$CY zQJ_eXh2c8P{v{f8S7?!Fd%zfRZ`KRf8=wcSH4`q9(XM}T@d!0kI^lkzRS0WzaaY8t z+$uGf5Kxr&!J1!@nX)7XhPCEU^?+X`Kg23Fj^!;5rs^;REYJ(D-6UGDGus=?b&%?= zsnyIpuS%=p5$ep3O-jpAYogkfITMlay?x64wiH%tKbX#SR>t=ZA=7ObFZh6o0HtHA z*%}^6gD8h()@f};zsvzM%WGKtcY__04=2>2R6Lg1VJ^k3=AnL&xet6wXfa(mVGlo`brDx1zOeK*!Fw45ZVp~b&lwb}aw?78hUlOYcY^si;z+p&YAXiwpmfB>@ zSyp<8MH#HcWvP|7QkT*eqi|cV%+lPl;=3@eNpjJkvD2c};w>um7})-x0$ysE9x>u- zO+O*Pr)iocusf)G5Pj||2!bmlBXZ-IUCTJ@8k_hn)E?c%!X$MbQP6xbE(N=t4~!GP z=>vCP=QK&g=ir>e7>h3cE1!l0xp;=rs1wQJTp+mp04++?a81aOAbei`0Nkaae~z(> z8k@1|?hat<{cai%ta5olT}S@c^%fh2BUwJ9RuXnS7XqXLhJ)1D#q0k7C=yUkl!_|X zY8Z9TU+xBNdVh2L5AFrC{&$va`~LuOD?a84Q&%_Kpwb^N>MOsc{--+O&QACL03{+b zWtWK0l)*>c^@cHC8MIdfbOg)0T?h9Xs+alJ7LWPe+#6%@1+WGWD=Fd#cL%de8pDR5%3+Nl$VDcVu{IlCGb0g zcFF>cZxJX_c(koexWrB>$g0PCC8!Xn6zEL1d6_%SnuRVR8vIuh(;7{Q6kRUhme(9& z8g_RPObCE^g0+#{(iZ2F)G+M`Xoy?-M<_YRGTTTNTnxr6c$RK3b#O3viy%bd5r}NW zR}n)|6IhD@0adR3Vp0n?Ock?Yy(8QmgNe2!3`*R=P-BQSQzf&!qjonEQlD^wu@q`H z<nQi|F=rnJ zJBa6;@qk{&_ge&uN=NoY%G#dnr+8M-t{{suJ}701)#fUG`Z}Xz-e@ zu?vMviJ}FG2=dX-wUx-Ay#X^+98~`2T@Zoa^9?q|m4E7i0|2@PS(i}OUw)8t7z@q1 zm)SbxjV*?|2D2*A9HI9TvAwUtSSVerILo+J9dPm(B`mPyM6K_%*hRL*@cJQk=51%M zNvd#WJfU-)O7dYyMWG&#FwHl1hP}=Ow|n}5kQFu@UcAFGg31@odx?^>5o^)SMb&XF zY7Ft6;$_DsxHrx5Eb68=Wshj5AB4e|9L!n(64R_65XFW8eJ&hwr-}an$g_{!ssO2= zIhn&H;=*9n5M-^&_%+a8L_tL~=pO7Kv}g_6$tprR)53|BIt{Ofm>{5ItXw(!;x`S@ zG8?&9c2j(FFOG)b&$wh=r+7dHta;{Gy6213p>?R7RJAjB?<}QQ=A%euDCk^KEP(2I zikvVD<_pd3*I9`XnVY<6f?~5B3m$TVPGwsglyG0%x-St*l&h(7*ar}YBa$}Hq8s%Z z;u(}BU8l}bwu6clNqNzcKR*OY) z+{0;(vj(E_L2z6HREFMtM5=I$b?#>V4q&8jpM$<3W*`}up@d?pIU^V02A{l}Clc@B z)O*f;311iT8P29Jy|ulqhZl)qEGjjoxM~od)UZD>LZZ_T<_B2qC?7*l?j3bFm{6p? zvg9$4Eb2uSWEnD=<2$>Dv#)H1|QPT;f-$D=x-y3t)9F ztyqk*@E2v)a|wQHvibi2e=+Pz7QEtMw-jl?yyH++)XcP=<2kKqvw>GDFzf+T{$ozr z7;zYXH3rB{S#Q>5l*viYXxgl}44cPsmcz?ykJ;X#6>@>L7Wstr0S&0cCXY7n<~@yh}OP*TG0zM2)vmytixc{;>anx zOa>VxS{_}dJ;lv@bmmx>m{Vm;VkldwG`a(oU@`RoC^+XTydJm;#*^V zg0TMp$$~)T>eYUr=mMLByncTY%^h#Lv98xp+H@6-wsxzAT2m%!>%~+~>`@x!{{Uhs zDvxL~&K?BvGy9i(#t%4wTWZW|g_boLwj#V^bMfuWqJv#dWgHxKj(E7jEvwcDhgX;z z7P|MFWu~fCuf=Z_8`%@gpsUuUsPb+UzuX1hA2Qu#918^LE5*bW&kRk^!4X+?5rGHMjMLu839u7IoWMGmBf_v1TmiCIf$nwgKTuxP z95Eg|$FrHHbp$xLRk%&eDi$J8%MrN-W?aN>+KxsX{SVyT?#F5fnFIvNVhe1RgW!(g z1~1~T;lXaF0Gv(!takc_p}AF=WknKL7EGofjdg}Jr-lKb@T1|XnO3h{_JWL*q}l11 zapeC1wEqAm{mQ{Zr~RN>r~PvY{m9xLx+fS~?54h=>wqf3FOPAW2Bkvl8&th7`XC0+ z_~}2KKdM+#jI>|W59KJspf4nbFuyJWXQMEpmT?IP+DT(lipns$OAcJ>a>~1x?wUiG;j(fKh&B-JEd&OS-&-cXLjr*sRM- zbDPWy3=L9MOJPL?#mSEKm|+rj=pf)YjtjYwcSN*|PGz7M7oysi1Q=!P+!c2mSSljg zmwJm=DPgLJb(;}lFiUR?9c09_F*uEL%xdB`BDheQ6$K~znVP%)2&tW;2=JPZn05H7 zZe79}%)}*PRWg$b(v0FF3tWIY;~ekI6iaapeeui?0G8QR(;I~%iNY7vhEdAp;eEYB zOOd)$7WQlI0uch`X!HHWgx+1ct;bqUmoH0k4osE_U#xg;#F?z>o5># z{zEW@+PVktrXv-4gP~LQOEzI`bsp}h=J#_a{SE;AR5_&vtUY`}o!8DP>tFhq#|X`Z zb?PucW+OtrkGPbB(sW?5H3O8DOACmcdWfvzD_gKQmQUt7UN2B%7BCMm3cOJgP|)#l zP-@z(%|j0lS!|Z+iFfP7G(^MCNoNhbqFp#nSwN#C;U1;gk%%(o7oiPik8=zL=D{0l zU80=IAn&NzxYLxEX=;gk&B1W-DYL0TlxvA-+^RP_5CL%l#-X^fEvy#6CA)~lVTJ-x#9UK|B0NiLh?l_@natJJ zB`wCOk(dUA6!(s0x{S1!x!aCVeI`+6{sr?ZFlONs^D4i8>MT4$1N#D)Oam?a93BV0ZY5e9fmeZ+I#8?4{zK0O%@RrFE!6MYnB z7}@sn0>W_o}00s>A1+F^INp$I9 z&~m=eS7>_gbd_sz9PX`F^`BXRTQ5*- z$f?vQ1EMa2iLNU5eqe(@1HiWp+=qBKjZ4IZgJ5vKX$-TsXXB(HoaZ~iHBFCU=DYC} zl`Ort7Z`x^EF+f=H@R`7?<*$&!H%Qe8!>$#aNZ+$F)|z}))X&4#42p7odL{E-kcpH zrt>f`&saFD`-oZ)zOjH91y#(NG{teKg;hF*frv!QSBMQcFx7J@8d#>faRRb~<|?vR zBr26zjRjwLRt?jyF&T{Y8M-^{Q~-v^(pNIvUj$ZF%DfCInjj~_y9s|?BaRF26-~K_ zFlJS>87YEauM&;(GV0|`1QsjmZBFA>NO?Cg2?I#Y4k6;DcbQ`op@6KB(ExJ_R)t|+ z1&xiWUFrn>g(ENk4>1Q#Pb*6Lq@z*fPo2|?a04ziPo-gh0rK>q+SKfvh} zV~8$r!kVNM(sv1T*)e-Q;ay0&iH@WZ$t+1z!5ouRg<7Z%U7O39R`jhz*!>E{;xO=v z{3VwT1z))AH+H4OTbZ3csutiKzv2&2I$!+2%tunbw^3{D%}slT`*Tx=Lub1*%GAzU?688hae z_B%o=HY?0{HbbN7Er<##7VBS3tIigqFE}B9YB+zXPBF?pYNmaWRZlzO0y>o6jKoOB z!mLz$5mt^*XerAtmBgI7i~;}ys0#zzFk9Exf~bf*9}W!WID+nr!ylM52sv+_eXuA- zmNp9Mn<8`_AyT^67car$N)H`awB&i&|9EM_HRjU1|6szfmi@iI!i)(Z{)(FoNzjI`oAFpq^l8GQdu- zMYwx1mUHZx5pywBZNl9$_(LmH^A>LLQywl`n|wf;hEiJNYc2)j_X`-5x+X-=5NB~K zaaC6pAnI2TOiZpNRZ`etapZujI_WEF`P`wGFw7;(um`_LD^V6*FQiVic7j}r%#keo zqBc{p6wbTN-q9=|s2ngFxlmH!k)r_0Tm^s@DYOj64ODT8zc3Q98mbR)USYXwsNtvy z#LE>?5~3A5C-}ThV~6T0nVQrSwhLR9!uqp4V55dv%oN(nG^bU4%6Wax`S$>E_fP#P z{{W>a*V#Mox!%s=Jw&5Ylc&@Gt1HB>lj;IhmEu#P-Y0wMFjgZ*MJe0>NiOqBlr82Z zCh?em%Kre8?B{_QA99ZQ=^UT38Ctv~r(d7_i3|s8{{V2JuX%rR(EG3b%%}X`VkDi4 zpSf;SrnH^X7X)xBAg}Cy#1X%| zHBwxf)!s+<5tiPtBG7dI0Ng20kN*Hs##w*wGfJ#fXH>*Ov{7BJYucs; z*Z_c_Ey2OBF?mbIrbUeN1z>$e;f1~+XBqVs;_+Q4W*K}kuVe>JaV!`j?$(~O3b(Yt zy!UmON^piVL>*m@#>djA2U2Gk0zA-erq#yf#o|fD~)Q4IM>OnqY&i#5_vW zZr}u_9qRG8fCD{Zrwz*h>z3ijY7QlpTV@M#$#-LwLXJt3O&y?14~)lnEas{$G-6`i(Zk#leV#issUcm?*~n29vn_L+#U#k%KGbW#T_PPZFg~C1O@0 zKtR#lCNCG z%XB~{8;TOM3-9Iy?mDG?5`??ddzqBoD~K(?w=q{SXl*YRgh_+E!sjGfHsidoGm(d^ zyKVOZ*k5dFzQ%Vbt`zgD9|ABxTR_HD8@5H93BGFsR;nLOIv~nQ8n6Am+&P5 zQF_ue)0?v4)`3eDho=hjXQ%s)gni1K-kHM?-W!luLa=#bT?oFZNTJ@?{6=ig)wEY; z`o;@lZ##k!L8!g`2sQpD30H5aLtl(#j<4blhn|QMD`pzhCdg364iz~pV@y0VRC{7q zLDrcvS;E!UYeM*4Mzuq;Oteu|&-|G(Uh`2>0E;CWc$q^^`-`nR+Z1FEnP6=705vtX zCTJ{}y93O(0V}C1*v1%DopAwCapEXy?hVbwOu@2k%moG_I5P%1d5p_3r-jY|nOuw~ z7=EENNSSBmW{dGUQ58^1_SHS*h;$|~gYPQiQu`BJ#^BH2`k6a5E+g@9VB$X&0pU7Q zZpRYv=rfD(cj9AQ*c1Ciz|;yyyf-pooT0|rUMcDDLsE%u`=v%2aLaEsi=>q*Sc-+B zHT5%kYK6DrTBQO{t+)0bnNJJ&7 z$(KDLR$OUovI8Ar4=ayC+()HG*Kq@~JgygTGdY$F18mLGqYJpNiB}yg1UN9cngbh(Jem1+C&3C1y1FBIgmlYGdv=%cwJPOx632?1p9YEsE65R{?zy z>~2+EFO*wSm2p~HGlgZWO-eCQF#|Uq^8o26ahZ5}%&uotayuoMequnzbvkzkbKzNh z6|Nbuvg@oS)21bbOuLqL^vq?bAWKoA;@q#fZobI$hlUY{?uNNvaQxZkJ%{%ZYUloJ zRC#dpaGQIKFA17IILvrT8b@i;KT@0$3QsJq z3T9G_=$Noy_-Tu2;r-(L_=F|Fi7m~m>6StO@GfBe!e6Wa9StrjLKLbxp^6DW3DuQf zn3YEXS896-LPW|nQJTYi!4cy5d|o}x%}Yc&mHLm@0mcn(;ng*$yK`nL@dI79U6pRN ze*;u~*x;8tO`FxGZQM%`&Z%wRd}A_^!PEj2Ko)dc5MsoxbZIqETVMj})$piw!;%W| znMeZw0FcHs;Tc1$zhzmpj$)&0eCS`0?~t7sKmVW8m#++ARq?m z7?ya2xAzbM(knflWw+Rw^C`qiB37@=&RletxU8kXP|MmEc74nbbXeZvi?megZZHnu zYs6a$0=h@1a@Y0g0I+kN!Jg4EVTt;U=3obF(}<@mqs%mrxE952pST-|yvnOsh8p5& z#Ko-5o!~Ara>zviP+FZuz;=n7O@e^rLRLArtC$5qaWsVCOhQpHm}(Tjve<&<&>i8F zk=cV68jbY}8;=r(SdFk+TRH7T^_}WCV{ocQeN0vFn}dhRbxKXiKyJw4l}7^HX%%Dx{DYf2~FLT z6O)}(>(d!!HZ(09#ZW*tyu;7Zflzio62U`pN(ZgMujVE&z6-%7*#(wW_>~!JTHq2S zK@yQV#xGLR?duXite$Gu)MD@irkv@ED3n^H5w93XXUUfXJ^7aR(g?|-I2CC0!p9MG zmOR~g5AB%_hC*9V&P=~iu;I8mzY_ldrIZ*b!5>jE1AC`TD%^l8Zu2oEDv0OyLL9qC zRTz& zlHtlucyCIX!E#oLE}UoaDiuf=M+r^{bRa5Z@Uig?mVjGW!KlS(RHE_4tO7tBD>RK_ z##3FScrBav)?A`0C^)L=FP8gFWKg^uzgG%X4d9=Su-%E-0*1P;sD?>IK@DB2ON@QC z7qC$B!5O8C0vOG0H*oBh^A%#d#3-P}0BLz)NuZ|REODsAEKoVfC0T{OmhNsR%IGmg zJKr@94!a8BonBrzgPgQk0v6+WYrF%9sL`$ApUgoGBV*oo5oVf!MH>v zSuGgh&Z7dW)0SC+TP<`_7SnF9M);1Px8iF&>X~y_nQ1|DBuP~e)S$$_EUAY+VV`6g znwNfh%-m2BL~b}|B*dhZa_}@?QhzY1bw$fIG{+Mm(0?#c?S3k80t6*WtdN2rL{y=T z5S)-kmiv#?RrXaL#%++o14a=X-B7gL=mM5Z#W)&M@8;k5xk{9 zj<4)Mp~D+4@tKzx7Of)jDg-LSSH}4zL-&o)*zUVVZZLNXO>0rY<;6_hEhFks!A=RA zygjD(nahA?`yd&_sa6*HWOdpbBtX&iD?)1^F28)lhY7OfjA|75 zP~J=`_=(mLRU-b*W6TsOltIub57-A>z;MfiZGidkMT7hJ2ZeRm@8i2$W}%H3J=H#bPyf5w!6a zHBiJN!E?EaQ6Cd-Cg5DsG!G*Z>L@GB4MJUugQY^ozYuN2wPt3g9|?GwShYMQS|xm9 zqp9HxVBZqxb5W?I!*pD_qa?9Hk1)i}I>fDvv6L8DSDBBQb5-B8Ox3e0Qsaqy8oD=( zeaHA9LWQFe+TLY`D==xPMN!P6?%BzTrk2z@clM1q{O$$IELcLkgbQdjEO#(1lH)dl{ zLRm7w1b{_kM?@y2kC{R)^Pg;LH-tE*P}VUT^lYaPD-?Qyg{R3KFfl0NkD>(LA0(h| zgV=(>yHv|MDdlcb)wpu4MCb-AS|T@mPKe#vRw(ds>LcZYms{P$hl7nCz1SRB*cCT* z=2{Vrw%qzg2oOatEpDm^L8`uj8qzIF>oZ#J@~_bwHBp4{=3!pOf{VT-@mXOL#h8|& z78@$97}OIlUYoBMQtOY`D{&|eBTmITM*6rVVzVefrxK`^xTtR+1Ve_PJB~6=@wdeM zPGOD2EZa;20IFCmX@*_;&Eee1IgBpn9|3SIlyc%H!yRsLxWy#z9D(LpLh0YXBy2!E zp{ZZEJIRl)F0!{w0}aHhhziTJsgj3iGeJ%~!>x4}bpHUzwo%!YZB!V_JqSjF^A5Co z<_xsHrW7mj12s=1ebwurMBi>xlLj@p!3oQ9hKHysfAT&uIMWkIIARN z0n8)W{m@WaHgOHj$8(Q@99if&AWf4ng*ETQtbZUs#5#w_fgJr{F<-3Yx5$tDe5afG z&R@ZWwx3__Qtk61w!X}58HC5ODW!KQF4>Dr{#kz8!v6qKC*dgLzf+lT&d@#+>U%NT zQ@3wjj#vi@VQ=Y*2(u62+?am^Ig!8)u&I8Nazp2hs?lKEs%e7ch() zB2xhA9ZW1byGacY0L>jrMZB^1uByiF|pQXQX_gA?En{9=MtjWKBd{O!~=us z7PgMl4qWOMTpke?m@jCz_h9e~{<)Ml{*N;@Kf&H0SNXfj8V<|*mR+GCe^5-Xn*N|C@SoIhGxIKV2Yc~~3fJ2XMjWU6hN}-B?kv)SxBH3N{{VZ= zQu&cw!_9wR;wWbqHvXkxV*f|)nl_?BXt>;C`|pSk}4F}{QE z6zIId#VO`5q3-_xxR%88{{V8vBNdec z5XUY$L|N>I8j7pav|`ayC1Wy&SD345l2}(|e=|PDexPC-hN5Bw2uw z)X929$BedqP>;NT!W-S$Lm>F)tY|wxR(IHmeKRg6C@f?k6zOTkFNm5;bcPOYSxcE4 zE?3+OBUg3aHCjDIxj;oweM&9~LB7%1L&O<+66(73mvy<_iD`aBsY-{{WD?+;tMQiBw`_?=KZl z7lI0Gh)}K%60Oxoi-j(ZI!dr_5MUK^A_cBtYq>``fy84<%rkiF+AMTohSnX5jA8vE zLg5aK0-(1v9{Y=mI_R@ltIAU=#z@!!^8VnI1n&Ofsb8Fb>*kQ2^RY@7f?<}&dVt_l zy?;>#7oryzcSKoL`b|Y0%x;qFNr3vOA%t+2Z4=JlnU0s@kYs#@hlr&7cv!JfyeS!^ zYZncG_9&<YtB>abKb_>?TW? zx6)H_SDx!IIG`1huhqsJF$@D4fs7bYx>xft4-Bhla~V01b4H)AnuFyf4wk!(Ew)Es zF^C2%86f%JsDJ81cIJoC5}^f(0_NbB##pac{4p+{&v2{mW6n z^n%cF9Lx&5PAxLZvZWOR1J{m|87vxf$3&}LPLbQ0%tjazhNFTmZItT)#K59b1}B9@ z$rW!C7q}t1fzn*xFfFqCKnw*=MxJ3s-Omo^EEwEYMXazLl8KmsDy58|i#U~gfLBv? zyuTNjw^6frmZsPYb(n@?9^pq3=xfxV@e#INQe&Ez(t`q0S`H`)o9is>FGOE@_j53Q z;uA#6s`rbqIO{MP?JXN`rNek$;)OugAT(+L7BDeg?otAYYZlsdF0L3inQ+Wpq#Dx% za|W0#COS;QqLEzTmSOKqYhvqS#56g(1^0hM3R9=RN`e0X!ne+nU#Q{LsY1Kyo@RRJ z$~O6^n)uQ7F29Mm>>%G;ui`nXu)q~YSft#n+VzQy>1`Nl@0K=l^l6GcwH!?>f;^i! zTe=<}-es7et8X-}8tDs^RWAmvwTx-F@KkB0x-0W=_He?FZm^k&3REp!bm}Z7Z9-Mt zJ)=}?F4Jlr%*Hky4z8$_14(ycuRn86s@0xD7JW(!Fy;RM#AGWXXecj@K*7;FHhUHG zaNJpFt!C=)7t(L5V!CY$$d&aTpD=9#v?4hK+82BRUT(J!q8wE*7ZnQ;H7@T`sG*HL zpjFo~=-UH%Z_K%vtV_mVFyc_1B3p?;;%CO7(QwXV%}b3jDJ6E7sm6XTH+2%=LO=|C z%p|hvJH~1)>riugoeg(@8D*kIE!=ML zRJz)gsgo>lN{H&uP-jxGEAt3jLD`731yn>LoSx8aYdAVj#7p$DwV#5E3(QY(3u z*%CJhS(g}lj1*5X%`((R;($rl@J5`N{UNKm{1UP8MM@zm;f+soaYzU(os7cfPS~3yFeiB@>~V+-*uM=4r>MauWkw%IgL)(J3oD!tlTmc!Qu|DVbe9 z1WBiecZ#=44e=3cbLlo?~jbmyJcYP(^Qu z;eg5qSEPAXV`bdZ0Ixp^js3$W8FrAb;w|&cy!U`}D-rVwz03-zQtmYfXm^H9>d2i` zKQhqI#o|-yC_1?1yQG5Mo~(JatLZF@nQ?R>u4Qay7-d8$*$ne>OIdb7ekTNYxlqPa0N!34t_m7AC1<5Ich=N! z5`wo7aK~HEsMMo#2}8s}3%EVZE?s>`TK5_sh2|bAG-6=&^C;lN0Nv#uApZa{b%9{O z7`Q?Png0Ng3+)73jrW)}eqq)XVjZeSPJ`; z=E&Z8FaDVK|K=E)>X&b5{R>CJF zx0u@E20w|848*0pLdG9ZWRKJr`X~JI3H&K@%i$W4x586z#)8qM)*uUg=MxBNdu9(- zYp!ly052I|?iAA<7yF6~JJQFg_M_=_Wd8u1rL}&giBA1+4yzm@T|^guX#Sw*@;jxIF*p$-2VWPpU{W~ z@4mTGn_(Za(_253YOLLD5iRXd?19xHEmHc4h5DExu=^rB7`~uaNP*t_${LweQ;bm) zI7i$HcQErVwA5bpF-{2D z{sC+%N!e>1)1()kyi0k8v;sk?<#rm@PBr%s$GRW!)g%fXidCsF=F&Mkmp>Mo>$Jzl!6{A)`WN>$A`8$2Y$k;t#)zkpP ziB*~o@XSK#3*6oUT_uREkO<8{g;cB8)J`gxzSSzQlFF9jb+}MgQy0<+TCn`Xi#5&4 zqZ=9R8v%KRUhV>~Qth{hE>fp06@DjlgNN!1hyJ*gDzvrtU239RqmTS0u<5{z8$4=K z?U;%STuU5*;f${HJ($3m8!BMj-ZL)C)2vuC0<2N<2Dkcv2;;E=oN7=bsoyge)-FDZ zsbc|i2R-7=C##BCS_Znz0bw$S^DL`M@eT&Kiq`Y84c_G{)j(3+#itJWlw`}Kw_C?C z)Z((-S5xj+pqGOAmRu`+Wo(qYg|qNR=xPC)^%W@}QEOCdsTD8-svy{_*91)!c(D{I z9{`TAeIdK?rYkJI+7yS7l&EvovN$ZdC&;^DkVW8d06s9e|O8g7rd*goUYeb4k`r}lyE#QDbDyN&tv5u*P9 z6w8mQJfB3uWqu_MH{vF3p3?>#yiCKrKp*JO!VKmHd4ES)OEfq9%N$KC{z<0Htr+D! z@9tN?kMuDMipdu{@WU?5OnZmu#~8cE?ghjFX`#*jAsV%{LzjK-F3x7h0=#o`1m6q= z%Ua}y>@25?y!ZI$J1}#b+&yAjMxX*%l(@WBWzvO(AmC8KTUfZ-RtC)WCvP$D{-9i} z>s8X)Ildrl4ic1D9b)APgDiovj&P7yRkCO=?w?a(m4m|aN@X~~;|qn{rLHa_g)x-q zca~Ce+q>(pB(3fOtE*_svJbdH8!gJ_>!e5~`dS@3Z`7nSE3$OwAh^#kw=;m_c+q`k z%@(o>S^0!O*%?OxX4~sG2r*%J-V-$%D!dg@3`=^NX0YuvMmaLd#y~`(A;YKsyXc`JmIYPxYV!>n+q-Sc8bm001AnoRr-uH;&`tD z??+jYpuj5b+q~vdmR(M%uJOS9ZGwaN{Fw8}8&j0gtes+Mz}(BDdS&sj!%M|i<~tCJ z3j|4D0S1=xclfzup7+V#9=Mli4S3(;7IjfFdA(*-6val=Yf;u% z!IHQ23!E%cJO@1`7T=0cwEjK8&LYJ)Wmi8^(_knDv5|X}U^t<=E3SI(JwRc$pHS&D zkwp8Hk7d*q=B16Q3>E^{^BcfAQLaq3x<#e(Akq|7AIzYCkcI6RzvPYlu2Gj$(4xd4>yJCU~mt0Jc>7H~@`6a=(ZztV6CP6|yWdscpuYhAb07aVcn$ zqVshI%}uj8mrq!^)M$xshC)o+yFZjQ=RtfW-dK{30yOr_IqsGHqX$;Q7xyR|ps|+@ z<5h;@q=_gRZfyJ=;5OlvgTHsYL4Y%5QS~hU04-MPnoc4cK^b_e@>u~%tN@Fy&eIlT z7T0b$>oL%Tiwk~@OY933IJJ3eBG;Au|@=H+M z2E|+22@X=ZjRxa(UAwB=gElyoj$}m__A5T&DQLxvY2RiwhPA=B>I^bQ4C59#lvi6} zFJAMq1&1|Xm?lpHP-_DpSOe`1=DP5Z37JrP&lR1uUExcM(<3E!&CLE?Ak8oSpyjSX zfoyzJ839_o$MG9yNA4oFE|wayJ))jxfbd+G#VlA`!cY{t|gjl9n0vT7N`oE|ZL{!_a`KRSHno2Ux2$ zL``Qn+{i%2+^eJFn<6ty&_B_Tbo<<*V78Kbf=M{*uPNMLa%5kUpD%Tmieo)D}I&3AZFkW z4fX~1HR({Qqx~fbRV&a9#rr5zfT!BS7*YZRE!Ew(>OCfDDK>0zvg7ZW!r0DE@kYd0 zHtmNKUS(BitNDf!$CbOdc4(S?8%m{Dy~|}H(Wg71ac&^8Mz%)M00DVNq4= zXPrf0urQoZ{-v+}w?{*y93TKyDt{0S09cDmm8d9~-Aa@?$qzQ{%>ZM(KG-1lu)OB8 zD+TB%8t$>Xs6Yw;1&jJO5C}F)T;7Y(zVH?sNkwE6+(@k$Qi{Cw;a(*%^dWMoycyNX zB@!ahew=1DN0LybXTIz147gMfPFut7V=O(Z_RO^?bhiRuCiC?Y3jwA`Fu2zQr^lD*n^L4iV`PG8F|`BBdKZ zJ9vap$auIs_k@PwSUo$wBT(CW{s+to<#^|_Z{jyMBUp(05-4Gy)>~H{p%nn6HGU>x z7+*#(whAy=%QJdQHG^i{)O5NmipK%!{-O12<3g^lu>>q+PK#iRie8=_b{<$1i_=qt zaa-wAfw`96a9%N1{{Y!lqyk4%kgrmL)~mQ$*LkehBS%vA2?4?&_7cMVCHynQSD3~D z)uREFc*yZ8=EtPBpU1*_wK&0rJy2rwher!AXJZl&6OpJHCz57O{n1kbi@Hbnfc1{W zO7GkeXWb9R7+c*kx{PxpVZha5`$r3}+^$MK;u^xuB(m<#=Wxu;g8GCE$rPm;QYD4t zg3(?Hh5JihbuLmgoQ%j@^1!y#TPYyX>Oz7keqax+K|tD8o-QzV-T)`HLSyEF%SXWKjeM8lSvx{0wg?*M$vkZQp$HQ zvKIpd(T^+%X-(B#>oGthMr*^f#JGx_DvgSB3txRdxHo0eEx2G(Q143RzOP7|P$ziZ zV;v=C8W@L0+-36}R{5KwiC{HlBVTaSi-oax&zSZf(x|G!b1yMU*I=UUxJurb1Gq(9 z8eqQ*TsJz~5uoa?@7#XyO*O5bR~Y>g=W)TPod(S6f85L%X1rS0OM^fMHE>(6RF1j0 zAZmnV#;e434@!$;7+hf`Qb1DH)pe%2z<^lUsG$0XdSM!RF|HxW=vzEm3iRT zs1D{_i}UNhSQ(so3{O5FCnIInt)|W99z9;tQ+LNm$TrDseAkFrgkZ=VgR-3RRC>6! z1?|4>RTL-{8aLWdZgWDQ+oK&|%oc&#jR(W`xvSR3tpB3 z{u^uux@FVc3$rguAGvwRG_WyWo59SATESEDOl)M{sE6i1xQ6OcTjHgGF0dQkdo^5C zq)P8Xn>Ei2^A>a#_*)k9ZC7YDh7Vd3Ub88wTlBO$V}0dUxJV)^b=%y^kYZSE$iG)I z(OoRZxXy)FF4!$Q!}a&l3wd57tgX?J$P z#$pZZYp9o9b5;;5_i*WR$O6)7#;m!K`^<|s$lMM-Rdv~TnRd9Pwyoodn^zcG0brov z+^>}YRXvNC0!7ivao{G{Xjb4Kg_fpb(Pa;+33IPuX&>Fj1LC6eN9A4j7cDDQJ>ylm zbXnhXSfj)-3r(9;x-8_FW(JoVN^+mjLG|hK(3czLAKOqHt*4qEnCEW~Hn*QO4RWD# zOCRpi&Ef7TL3JE%I#a9;I&Tj!9N37>*gaIrwjiV<9q5h~oOv~>Zna(R860Is8mQ6S zagHF{Fk)t6-Jh~xUB~c2f-t_PeO@8|01AKSL$CURKZG+G%o&VQT)>wVDknT>jW;a>-q0Wa=g%g}2O<~CkG@hDr-9VQU(>Hh!{zE}M15+*c~j)lyh#Qvok z$J~`HTkb=YFO*@N`f3=Yrx<^6M<>z!%9tOg_ZTHFP)sQ}s4v`rpRogW)CT4Kpt?O_ zxtgww0oL7*^$v)2q-J_|8UCk4`pY;y%D=IUb23U8;lq7Nn#|XWItDws{__RDX)Knb z$uws6OHefENskuK35iV+=AxZZcQqlAu^^ciwx)jO;zDVb?T0B?g;;up(v%FF#N5f& z+xv~&)74p0g-HPWhvd6Se_M?)Sfjs^>!%X#X|tY@b^%;*tSlzYx2$ca7b(`^KSxNS z-q}k0F4Gu|Jwd2|*khm6q?`*^qa0e}5U3)r+0Spd3G_T3ZjNJYb4Uf`9WwT~0h+78 zb%o`hY&v+3wMh{i^S5kFFve`Jt}lRBODtu2`6J#G+@uk0l%AO>&(tC3O|zi-HPSx` z7!}<~jdy^J7NDTw{oTT|A0bE;b-Y30Yg!9o zhT2USZD?zw7R?Y4b3R) z%b(93Fh-4K61pWTjw3S;A}Fh;oGzeog{73)q^$dvptL(oo12=O#H`GJ#h7#T{sc8e z)HHN~4#XXsawr{0?skSo*8p)0aRBj{EySruNC!dyaQcCq11VaDI582mKT*WiyiRpq zCk;X_;8zwwnn%=XyDPL5v3}{u`eoUytNx;;eiJQM?iV@tS!m7TFk~K4ahBoYlp6=K zia*{BqeBjzT^{&yt6m+rNw>da|gs=Owa>c<~xDzax?LF3oS|9M^>;Jso;yM zKFf<*FTx9x$cif9Wvcp>X4Sz{NhyPce=4B79}o&(O2WdV)_91z)Kb9ru*JF^E>bLe zWd%OhHh-L_57ma*T*Caoc>Qw71|~7Ah~RpS247PAQ`&!_Jj3D*%0Z1(P9Me}=mRXz zww@Ah!|EIcR@r2yb$l_P2@u64q)*&aUv%H&K*lM`yXp9wJi}7&h=?0sgnV7QD!1u> z!4xVfFpR5I3_*gnqIo<;iOK^F0I^HfByV+ZI?Kw@dn|PDkI0$sCQT|>9^Obb%>KXs$zDgatjX2p~LP8AU!?{E~dJ~zBMm3 zi|Y;obd5WKU^n+HLwbVU`itZc-bhvJ#HRb1XBm3-XVy>6p($$ZNOBBxnvh+JP@ zvf82i#4sj0F?zqbd;-^*W4TLoFRL1vn?QZVnG0OhtQouz6cpA}AUcO*IFEgZw?lzQ zUEpzqzJu#V##nYmRfLR{-JzrSrD_H7H|h5awe7h1zU<|o>1+huz8xhKFG>~*_A?jq zrJ=X0Gqz*Suv2;Wl$UFeb|v(~FB(=yNGM-;>n2-t_4m4%lTbxTz>O7C?cSoo*Y5xE%4AgE~w8G9}rOjc?)BGU| z^-tW*`GF0?;1%T2!7M;7HYuKJALoDEyC!r%2q00`Zd$IY*H7?!Ys6F*pi(^&V_N?J zu!U-W;|z5fNG$7@r~=CBp>4c1c;YhZ)fED*ryTi|f~p##@qI??v;;J_G7hQga|#^{ zy4^?Eg3xEwtrVc=dN8= zB`&+FWo6@xN{JhQ1>hJ6s|_fnnKmH7`NgfTjq{UH@hMXXwBxDLZ#!{4OV^}I@T3~I zK0l^=HvliAo0L|I$^dHC1f#Lj;LD#d@?6Ws3jU$jAT&~^@e`FO2H-*L0Ci!Wb&Q=` zrx$=BeMnH-L45PGm;$sU0c(o4NSMGVn6`7u{{Rt%oP~>1b=nj+C=I#J5A{xlvcU=s zk?oc&A?FVUWgt;tk=pkT{$R-AhnY&c%IQ~|-)X20x=4%jYz?xvjf_)936VA1LrZ>& z`+%C>fxRzlGbvmk+8Ts`mZ%L?{{Rr5pjkFTO5trfCHIovo;c3DN3;&&eZjWGYr;W8 ztgC|9YAsVp7GR>n0g@=TJzQcdkb>wg1v-S=jA-w;iju;H*?*YfLBOUpf(gpN%UG#? zJvsV^BR2)XVwha%(ZE88;YayZO6 zHvrY1W6Mf!9LplL0C2Q-q8m<*e&8tThM%mf5AiY-9#J)4n3iK5=2R_BQUGz{D-y6l zWxNwd7Y*MWpzeks5yaLVAi0=sHA`|+yeNl!!&NG{h}m?-g9Z_Y#v-%xDj%jH=ulVj z5TjFIn~NF;G_m-K()$PX5g!x?J9LVrO0BBgwsHJ~Xe|=_t5>Oo z(;)C^OqBiH4c^A(R?XirA1rZ#$n_KR3Tfy&0AHI}@{cG-EMsxz^SxH9v>~7nN=X#b z^o8hwJX=OC$420?Vg~B6d1|f&A#GqT&*mJKp-2UN^ti~$8eM?X(NU5d_7!gZp&zlU z76^}~195KvufI|2LVk|1Hg=&2_-leA&fN4xM z;s@{DD)O_WO`T2!8pXY41ZNbAmb(qYocM&fz;Hk86=q=Jwp#ppiWrj_8>FRD_Z1$? zV^&-n#1g%WRR|Z<{^kLR2ZQ!md^fjrwk6CE9ajWFYUXV(K5oZ?Uzx09RYThK4Meeu znklEH-=rMPws(%lq(WUVDP}kLGxaRAP)-N@dzSAaXQJq$`dv$c?ku2kuQd9Y*9|gp zwdV6J;gw>PTw7GN=MQLk0=3m4m@I8i+X%5`ad&% zY>k70Fwk!*yJW|yx!^3)(-HW4b2ncUXjba2 zpQvHz-CVraXrQ>->uzh9RxzovaD5q;IszLLBwG{f{v0<@=FBQC_|L5~nIuXuRiDP4uXw zw`dJ{N|q2T#^#ri3QLL|ougCSSc@ql^_OcUozEm|q}vC0l*+xzjq@B8S(S^Gh$$}; zqa8d%=6%ZiPcr??Yw8_$DcV5t{!m=k?k+j}&hamy_XN#tvgH-C?g7+~%|Z^6Az($S ztnfiARg{CWS)f>OO5!~5tQc=PU~`I{1b+Y%bd8Y$iUcfd$t!~s33(q?{TZu{j-OHD zRBtu0b`F(wP^`7NWzT00L{wCp2U_fjtq@S*Td-bKQ!7gL|Vp$4Hg}QWZw%G!QZc zAc0MonRGlcfb5+z$M=4kxP3>S2;yS}kG8h5b%QVz4N$G;%sL+C zlBs0;s z9ban|v1X*J)?)nBGg%ytpo8ia6I)g(|Ct5dQM;Ko}Q5ZmsbhK~2M>SK0(#grfI0 zRIc1kEe_b^^I8GOuNe%&To7S)p@y=n=$SCeh8A_j?g$j+j7AlRBHR!KT)()aCfegA ze|H?1P^`x_Sf_kU$C1G(9zylz9bPeNj#@9+z`PE59FL;kL{i$7WznPd`+&w)t(dm) zc_lb6PHk$xh^mu?na~ew1d$Mjzsa^$!2T*9)zEbzcNZFLk z?R4?h%81H2V){nbom0{p-bZMzS%|>cT(?2^Wg6p00uS6Ag)|KYa_h&qboD9>xogIL zqq3A>M)B86aYJhZRYZQH3Y9Nf*?8eseNPhe16ao6$S8JKN8Bwb23&>SDysQ{>Z~nW zm2zZyhBGpN0@YEjJxn511ETwwaLRDBQFYa}T6bMB92fHX+!j!2HLFfBfZ&8{Kq~5v zM7&v%+_4lvy+Qd%0N9aG6ct=GEBwmr4!`bqe{j)nxc>m&n(h6-O((c>{je|cOm+DuYVjMg zn4a4Lxk?+n3x9$Mc5~WwR`7`TJ0@r^#nL!zKj;w|%ho$zSl54s8{Zjnn(sWp#CBfT zfuQompXEl!_9y<54B3Dd`QxI0$b`C)EiO8%6#?DD0;NZ(qx$81cv_3&52;q`@J+;k zktp4bX__UffwvJxp~+8ZxtEkv)$J;X@6Knh_KRUJv{^C^Z7AIjZ|Vpk6SExl-bP@* zY(_Krgx{%wIP<{VW`c|_tS~?qAS*mlhv~JD{X=Mi$LK*{j;Hb5%e_{GB<9#j(xxZV`9Mg(F(Y8Fc`!p ztyEO+0vAzYj*u`Ri?a@xT})TR0QI?VN(813wYcqh(V}v6T}F~4P@!5!j6tz*JJhM+ zn9?>tAnqdHTKq?$utkkAh^u_m}n9g{%tQ+0K zO4e8fXFKsy%G&@-3ZOMyO#C+D!;@GYW5~0Am3Njf0SEjjJMQ->TtEe_xwH-SmUe49 zii|+&%nBmrQ^at@jweOtQf6K!Ih%erFSKf+TxFtN<|Sc^YU9h)exi_5Ar^I!`Iz20 zgHS|XiDrs`YLBV0PpEE7>J{tt3xA4#=0kdpBGmj*qX8Dwt<($Zmj}XbQM%$;qbp%H|cW1hB&N7sDi}$aJL872mY+1VNRf&6J9XGXXzH(_D&b~Ml+|} zLBH88?yiJ&0oE#WWCGXaP5%I~5;6BBS8wfz^8Kl2`4M|RX-gFOM>_rJ3$GPI#T&ku zBP;cB&c9Nv)Yi0wtHBj+C^*y*C^s-=bEvle08^-ALAUNI>ebTXt;_cVN-qjuSBJV8 zFYQ64eaOClK4da^K|it&n(+;cH@;%K`=-Oe3-9yFd444YZt)due?&OH32VEwDZ$TV zth>F$6_NNsWGlpJqtvJs@m?Uoqo)YVHdCb131A2I?q9_{Dj5`gN_voEG6A%G!__xh zSY)g{;01mWW!vr?%gxLgHPzk4RhfFl3VACR==TN<@x{(@eMZcmNVbS>+C4v{Rp{|B zUVfv^_(4lo)L6TIr*-u)RlcBJUr`LLc9(FE36^k$vZ1}zGe+=*#X4=>cTsG!7VBITmBpEAv*6>l1V zo0jZur8H^|208OK<3^idj{q8NLa$uQt!f2x5T?*t&cTpj^tBhpf<_-~w9p0FPMl7? zqd7R3XOb+o;tQogXb9mV+S0-h!CxbGIKsxYh`8Dp`=YHra_EN;tSng!NjV~cB+4lScP3rBgGFP^iy zzGf^?o6K#kXY~tPY4xZ}F;4PgP{qP=0-FbFi-3Jf*X$4iG%#UNzPp-2%%`GJr9EKL z_z^g3J0gQ;ggCw3CMBMxR~3g3Y1P|^21s%UlE?B&H=DYdV}6aYk#g^7?U&_ILw`yf zN)YgaFbEp#2WSza-Vy=ZF;;0j#frn;8J!&rYB%|fm4DPLAJH9dU0{KKq;3svooW{u z2Z)W8`b#btp_Xu_lr}gJ}k$`jqEDR+j^S2Yn$69q7%m(<5>Fl~M#W*5}2m(nevp96BF*`hyqLC4k~fn3OHyS6hD( z5(t#6BMzf9{lSIGVf!#4G^V3Whfr8x$fZ2oPGzLxmZ~%LFtM9$et(7+E!G0L1roIl z#Xzpyu$)F3K`;7F&R|A{P#7uxYw--yCV^N!y zTcmhk*ieCK4u0kMixq+XxN%TbprE!~CJRc-MYY~w@WB+lYo5e)0F>(S!B?L!P?eSJ zuRXB{0EhxxMg`_t1Xj9@E&7Pn0P4E36Fj<c_%+I3Y;^-hwyUu8*lOU2psElAE)y0ZIqn0-MScD`QG=8w6) zTt<#uEh@HI;C|u_p3prlMdOWPIfx(%E>Z41cBBy=l?&r<3Mk!Uceo|zQL(neXKnUB zDKF)Cjc-ej6e>O!o5+}8P#X;9XXpna7I<-APC)_U@pQXmZCV6xVs{QaT_U)u^0r;xLI&F zzo-f~6BJxjr-CIgSE!Fzr%Qv3ZUyWsw9?H)Jh8x#yLm8FisdS2Q89=W68AIhFJ8Z@ zT+K8c?IA+nVc6zf;cs^yWW5k?q>u?{ndGAuRUF=?F}kLV(Mb~R1Xa{vRI z0aKVR@_sP0QjCJ7(NtQ;r}Z-Zc!+v;D&Uh)O<||%DBokjSzT*!Lkb;SC5~xHUDpGw zI3Ca$4&=0{^+`c^R{~$d@P#i!v;@1a?2byg2CobHn|Sm!48MqRc~x23F=x*NX{UJ3 zF<8{B%#q;fUAMLrw4>N7UeG%fFXFFoJ3uCyO;-I}NatNZ4t9^?l1z>v`?#s zbw&Z74mi;&F|SIgTzPBmE^whw(c0yQUS7~~OM!WUgGf{;xvP1&=xkf>6ctKur65~} zsL7(cBNBsKT(C{_7wo5t=rAzyW*K~E^!ZT-H z2-(N8>NExO646^3&v}_~*40&wm}?N~)+ZopKu8iNg4O;d@`Z00^&3p8{Y{GTUMmq? zEhVO?55#+XTRq*lVK-{Z?8)~~+n}|k?{0e+nufeI&md{OBD4zZBENBMp8-w{91i=1 zMwd1OObxvjR}7Ds9x|W z*G)T&eA4PTEI#pf8oLVBQC7m@3s8L5E@7>-L_-=37mF5Rauk_UW^hy>pz=#Xp71c7 z3W{*&uj?^e01m~9+nI1qcbyg6txIFey0R^Fs&B*{f{+^_RNmfdErbdMD^}-;MKFMg$DZih>Ms7y1+Rq3 zIE)rr@o;f400H+fe6}DDxE}^^nylj-Kt{d+!T?UJ?+{d!pm|o|!h4x#rPa)+R!si@ zj@1H_O4zfYMBbX439Qw%3B7U+3%(^)0UeREB*o%)h1|n+?BwX2l86=jzOHr0W4Ado zjk1EIs9JS`hF@s_pHcJf9g|k`UZ$*WJ`ULNar2`o@*eY32uM3?Upb6%pcc*Vo@JOo zi2neE{$@^NWvjN|xnkdO2eGTTS4f;iMD@8x1sqbhbajjt+{;yBr7{;VG-}Vqj1M3^ z+HrabputMCYl&Y@ zp{5S&00DpmSZ2%_j0$?>)Lk%RZ_06QF$J*dg~$v(q0)eqBtKwV=2o+?hXd+oEhwZT zr;wImZL_a~+Gc!or$&yQ;zH=8y5xA5u7QL7w&4E&YYU)lV-_@{R2%CHb%3Z~&2EJJ z#BsPqh0*%WMwLne&{b=$;ucU|pb8vrxirg#S z<%wbGw3}-;9B83t1jaAKCa@dbST#5-P6TmmRi2nm)4Cqa8V*Ybms0mrH^lM$_WZAlud99SC?liXs~6^w4(^oW_ePSsB@iuV#!6GFod4o^^%3gI*X`d_)cz6DCL zpDbgELt^$Hztm%hY87*Y^UJ##T7}=yZq=N4f)LV6R-o$%*5y1vkO`$PRQz{=VOha~ z#`NWd+mzNEA2c93kCO|QpKsJk+<~5=mwdUc+;zsrjkbp#CZMXdu^2sG3J`f0bYH@O zfKccQXTVLcG|L>;%P?spx-|ld<+18^jg7?2(F*AS#%NE>1Z4*Q0OA7?qxyaZw6na2 z3aE7-aMZ83A1}GnXSj8rbGU(My~bhx074XPFOYNDnX=hhXHv%PG3DQBMw|y+qhE{& z*!q}s`kfT|ok;m1M&FWH*Y?hP`%4-8n42bgulk%k zU6`&u#v;hRSz&Agx_dF(?u#<(ueM`dk-*5}3s|$la!0w1Kd8mkMRyn1L@EAq1{-gZ zIVe8Kc@|%CVksE%SccK!&#doTVO%_CM(!92~H+N)sq>!#4(LR&a0*Gzv2@GNcb)1EPkz(jSXbo5!>t# zVX6ia{{W;Z9F|d^xY6E$q0*JqHK>v7QP7TP4kp#Ky;oSsAL_?aS6OL(;0L-H4n(wy4a`&0slqaX6HguvLX)tSwFGhb9-CJHs-=FsDGi zTT=6W5(_}vKT5dcH3vXk(gpfq0~jqa9A(aP5{lD7)fDd=^A92wVLg%E9?Q*C()MDM z*ECIy1+Im8lqSH_ZrUS_9=jABY~)IcF*$Wc^Nh2KkTCX}=cP+g$Q;(JS%FU?qIZHV zL#RT;+J!+~3gV?`!01e$LHdB8seDY%0G4u2sh!#nmI^eu@dGgaNnZ(Zo2BDN9L}U1 zhujl~?pEvb6xhW2{mOei#fMi$4CnHkcke0X`%A+FNwNJxFoTf+w9B{X2xkhs5yASQ zen+WOq1X9wES>CPZ)ljX*8HIQePU9<`j$a4z{mbXdHwL9&~WE4h1_|6smu>+E_e5q z8T(=tUt|Jr4SJQjsS+A*p)4F|?2p+ivSvEFksaq$oI?92BCjMhu)k10<&ZpHqITGG z4rBxTZ2thRO0$Elq3C|p$&a-u{`9Mt+BQ>nhFIV21lQEW9Y1K%jXo#&iB8g#-F-?8 z5f22Q$Xug}h%wSGjY6Ook(PJ%W<0arQEimXsdJW$|w9Q z5$^MBvp|Mp`x}63?S=FXw-Jlw?7i$|3bJ11-&7^}75JF5!kkW>Jrk>Zk?HHisHFE7 zz@Hc&Q5f(;i34>9Mfa$t1AdUl`A8!%%a9_wC~?F`Z|Y=YBE=#15~v5bw5oZyp&Pzo zfk(Kezd1vh34e?n%KI|R{itcQr@y9JTD9cu6yiecx7@VJhiP~8O_rXe_{rvF%nJkQ zGv#ZyA;yEKqUbu$J<{G<>4Ao*#KeT!XoY~ze zipN+^@{9(1z*XF(2Tvs0;X0Mfwq7M#Fn8QBn}`-DbeY=m99-c5mj*11ycO+zD=U zPBRf`e=+E5o8nrGzTmT#%x+(OcY?V)15--%BB674~M^_VcE?*)T-$^D?UXxF3eCyGAhMxSx%54lsP+`(V8 zdXF;gCGtnNlp97u0wu92!>Y3GTx|eq*atb2E<9>tEN9h@CRGG1?+wRkSn=*2WXOTN zIhffYKHp!cYjD&j0S9B`mRpOLhqv(t0DvhNgFPTSH9rwFGyu)-H8!^%Y~Fb8}!+E;$D~*%N!TWTQ(BE8A2&2;_iG8*kJhFv$kT zSbvE?r5ohFHMp#V9PagAVl?1cNBl$xl|{7K_=XtQKr1#KC6r|YUH<@613{N|U4Ig= zfs>v&i3%2E1zxePyBcF2=cIguhHYTszu!rH3lRYzcyQCwGEWuDO1Dk|7z-uOJg zoZCpLeCi5>1=7{$q!!^tXYXH_?t+tH0s#%0Jj)9Oa8t%V*_N_+v2A@#lkqgL!|r3j zsKDaj6@5nn4IqgZE}eQtr>hE< z-Bi7+N90Pk(^)}X~4*E5S_l!phQ~P79r7YwvVK1u-bmT?M z>!z{`x1nqcakMBI(%*aJ4UN}{ zZ^4ytr|D&yaYV#=RsOCy#T3hHQulcCWCT9IRrJ+U;w1|&0aryC(9t|=x=kI{>jRWF$)`+p4Mu-duaDA z<$?y3+wUHt$YV(X$=_lqiNK(i$8JwC4i%Y76?|Zn$ZbPOzj%YRSgBXT+)4+C+Oc0j zxyyEaw#A4mUa{1cstSvqFQEOx@S`Yb)$7!~`G2-ndFJx-UhC}$60Cfq2-0=iEhdbJ+*(-$_k!c)Ack{n+yK{C)8A14v|(XvwTHaX&wTf zq6IYD&Nvs3U1N)Z0JJS{rC`L)Ma7_4K1Te*AVRwDAyi6MV5ZW#@mg?& zbe736nXv07znJ$I@p7a;uU1+2cW{D017sf!Rm{Z!DzQuNsKNFq2M2d({{W|ECDunx z# zCUJ7DdUx4Owga$7Z0GRp31QHzLtJ+y(&=}Bi^Ap~tW%b>3B&3I zN1<7=qSj1VaOLe#MUOVuJaG#);OC;8@=MIc*r&DVm&Py~q-mI{3bQmzigTDTaV+Ix zK*r)?DIdht<|V8c?ft+C zD)av46=!n(SenV zktR_U(EkAJ12r(xs^jh4;naQ(O;MAU;qNcv@nG<1$HkEmPX+Z{0D+zPQzv$=f{ z5he|~Z!BG~)sV?|Slr2gWDLC6Tlj+e6-5^PR~{prB2Qzs>wco=1UUf_2i&L&ER$yM z+_0n6W2CC!-pNKUBYqg>0HLFUjYjB!AYj&n9HN<3wq@cwyU}GDX89s<%)cS|hVrN` z(!rCJ?^7Zn8NR7pB_!{&8!Wye1n*Y@g0tEcB$c<69<+~0`-{9m&pBF~3Bzq-msQ6} ztCe|eoOU(lTYah~`Uj#eT2r+Rm^Q79y46zx4Ycezb5%<RpBof)?G3*O;z_uz`In zciL1VV9FM5pHPZ=DJTlOxn2@u^c0K+)i}mDxWX=0>^NS5+!oG2=C0S&SX(MqsIEE@ z8XB}w-S-)QEW6%m8IW#%q#hzz$6$XD&$VC(v*>Sqpq><{VL`|hy-MWx2o8=$&EGdO zJV^FXe?bhX%MIlNP-hT{NVR~B}eKT|lek`dJr4;geu{cJ;MpBV?N28Or#tx zj`l%#yf6U2S+7!(-l>JI&lR1@savj%_4FV{MXC)l4VQ$^4@ewS1Gy~Lpq5HQOLaq8 zy}*x_ZEg1J6UD$l<}R;aqAkOp22ch%W?Yn`Pz3!H`$r^>rh?jOiW#UfZn>ht4{%pw zt0y4z;e8T|q59Y@^?=~_1+7!wQx9WRRbyC$Rqolwm7HUErYCA6ot*pJN1`E?ru%}W z@f0QWg%Vm|h=ET@&SBF4eM~bLtN^VO+;F?hTw+qn=_!eOf$*;os*lCMC0&1+d_hMl zhfzQDr5d$EtC@HEnJj;D-hP?aapY3hcs_Hx^$rDsl#B5$%lOT0pHQPus1H9$(R@-T zZ=|b_q_woSh#FJtC}jFVim$A0$UwicW-#l_-_1YEvlldi(x-Vo^yxvJ}-^UU)@2HpY)Ty}ee{r)vLH){K z!wH|){>7UlTgSW&wLNp2312ssarR<|XPm9Qmi9JGl~Yy#UmoLi;62f}6WS|fYr_PH zPq?kK;g6Pq0BGw@(K{EbW>IR7$|Hs>muHM8QS6E<2RK3TgT@17@q!emMiTM%R^eqj zqK`>E-}6W)(>Gsl(&6QDUbp2i3qtRB>w5rY&p$4~vfSJ4Ih{VS#;!1+u7SA}L5d zt^|@&;P(y7;?iAw42@C?j|jq1-4UVR=ZAog#zouTLDoO&PQPV`+^>2 z3ZuXiwra55n2${bt#zw*kc(9d-L6~ez#L?2 z*;kyI)B@N&d=OU+vy9Yt(5_kaQtbk#UnBn9w<4Tqd#(v?bhJCh6g`*(Mm6yg8u*_ z8&YT!HmW^a>N<)&LdvyxnerLk5e~PAmMp!<(S~^-Z11QrrHi$n+_gj#9rxA#r%?Ng1k%05C~M&=R3S}_py@udsGgGb z5O98^2Pz!_0_3aEMCso|L;ess{fNCNgKx4s=%3dSt6A2dx8jW>$y0($9PrVTV-ods z&-$bRFYZhq%$E84s8{xs7~~+em-NeLFQ~mAsFW41E;BYiQn;-6lnZEr96sRQOm&pzEx1PUb;fKUaB=$=uL)x316{N z0?=O)oF773X$3b#1noo3Xs4f~Gl%H_HW${SG?(>NFKrJ~tUpn4{SoE+QLk6r&Pk@T zbvc&d`yl!usSmg-x_!li5W~z`^!tHMzcS>{?iO0m?*hwbyr%QRtBRG6xsQKSiW+t$ zMIw`yk{s50f%W=_Rr-o`i0TjkoIqp0>baUl%-JcnAa8Pv6008+)X2=R-S+*=@K#Kt ziJGZm0%ZIY;Uhp2*nXvT*>u@sSeN*N(4E+WM1U<`#8qFe@V?L}J*uxY%L1=A3k=a0 zzAJH6iX4nO;Z+j0?vYoh%3hXR?38hqqtf;64WGTjz)d~lX%fp!MFSGHwobCUPW{TW zMzLJWWp#3(XFXyKS?+EwH)61`^+ovRAYr+st6@q_Moj7?>FJhg4O6sKojOH>%*Ojg zYaMd|3amXPa;aTqZ4oa&iLK9w?7NsagA|mUOzwl=;z#uI;V%WtM&?y+?c#r|R?WX2_?`pc2x^9K(+rlYi?ob{BkD!C}kb_jZk!nesp zO(=LJw5?jp@D5aoUdjA-yg z^?ge9W3;k3c#cWKsXJZxi@<)OmG{IIpl%)^^#K&GxnCR(gX(vwgZrm`^u*wX>TM5% zH(%OW8SD0sC|7CH36DqIG#Nc%oJF1DY%Bi&Q?Vb0WISd1oFPV8xHQ_<4R?w`z@+av z?;iL%^NxMLalO?+gPEacQs5v%PphN&h5K^2uS~7U?_6dPL2Tht3w-*D$kTr@3j_f+E#(+hvzfPsEH{{UkkO$RI$j)f5}ws($-3__{} zbOg&3!C=6+vsbneD;%^`uMYWRHHeUkeZ^R+qQ+WX^n?t;t5!8-_F`^_n0CGKsBuF8 z8zQtohC4O+cVmJ6MSgb;srCzWt-`vNzus9wZB#O`UE2)k#}%&em1VHdQrV7ols@2l zyr?`LV~CtBwj*Psb1-96As??yn{J}+{ z^<`YB$#^!+g^9x-q?Jz0!ocD0%(N36W-YYeTK^>5R}e8jh`MTx3;?PpMQO-QxUC z?x>6N0_#G^4^2kom`)zTWztIr5`mlY#;%`G7mrg$y}&)7({5@RE~P6wjXq;oMd=q1 zOx!HR#|~pE;?XY#C;g9-};QN)*DB% zzTyxF7^plPM>@%XW0&uLb5K+FBRm;b*lVwdeRrE9&j)BVk}>z6Q>oX^>->9^!1Y|K zD86yhRrcnrpg%H)m7v)T?~d?fp<0Gbp;6^c#iO}Z2u6_#^YXy|E`st%+#<0#;H`wtB0?JWyg?0}tCBjta7?AK`J$HHD5=r<{GpyEafa z(~4%$Ix7w0jPlD6paWv_0O2a97N4Y~3T%3pUUcFSX1x&|H0mu(+Krmi(p1%&G%ECN z5~o+r>lAs2|093)^Cx8Zc@9Tb{yab(?e&2DTF2K-ML(WWNLAB*sUG}8AT*c6) z3vgm0!e@7sDDy3Jy02myq)v_6&VHs{No{4hFR7;DVL-BwON?k-%YkH62q3R%(_5XNXlhj6ET zrTk)vlxf3~8f&i*E6zC#>JK51weEPy%up+7heLt8>fE#>I1E%(2R9Iw2&xQ$bWpyb zXK$3Y-rc2e8wr-|d4gA~WI0jq^AFaEUDDg*nMN>0gF{~6NzFt+nMOZhBde-~Gmvc=b8Ct&KPD?V?YH~N)U8^42;fqw_ z$&CJz(K-^ATW>jlFSTM3k89E#wFKCUsm8wU=*!&!@XiT z42m8`n56m$$HzKeORRa+3@3J0aBMkAeFFv1CW->8@txG5MaKzEq0bf8A|;ZACh=T5 z2FZ$Ji^U^-_>Z#qrS)as>!x0k!%5-`lP)cl{jucnSm`JztZ`ZZ1(_>RZ44WV5IqXJ zXPHh_2<8-4d&OTc31fkL{u2R#ty^$x>oH+?vYBYUt$K?THfR9{MvIM5_kx#LIjW0D zf>E~A8B$+d%dN8mTq#Z7YP!b0+_X69O9pnp#95`!D34jtn2q+1yG1#Jim0`yy<*(F z$`~PFoV6+@GaXBMvcNigIU~|Km5AzR!d`t$eL|va#2Kyt0qWo;Kf&H&`HFWS#DMbp4fOZY@G9yJW(uLwcy^Uer7m8`IN(w z`qXg1qz$yJy&-_>x8IQUPWA3DLZ$41{j$n{lRj3yUSkC;MX*(U2bMh++O=;^(J8eM z@-$J@QZcBmwbjR%N+(K}ZBhKk7(H`ZqK0<|f(VAN$Iv{1Im8e2&DZH9yiBT@cM~~5i z2D0oZ3%wlZg3spi7%S6b(&e&0(2b_stmDM3x`9OM_-{SqU<%1=HY)rO0Etjg*3O4F z2g%)fTsYOX)ab4QPY}1(Ahs$ukcoZ^gJ9(#R&1?Thz1CT0g8c{5$ZM{`h zOVs8ni^qgmQl~p+8?fJH4_~QJY-~!l>ZKcsK7a9pNfIbFe=yNW8>vytaBB=L&u8D^ zh*yr`U4rxC5rw8!ZJ*7`Dmko2YK(@hK^b~j1fX3mqY<>Qp|?G;U14xGBQ^s*g!K?U z9MzhhqMKP*V{3%3;)Wi|ofd|rc5FfX!aCg6 zo+nGjwGa%c;Zaw>FR4-j;8i0Bs(sAQ6_zjXeL|@;K1-wR&$wY=x~QfahTFKvAUzz9 zDH@r#_V9EJ_d`_ukLFk0nlwQv$2?R}w)4a;@a}U6tJkQvSc=_ym~j>;$2k!>n`K8V zx|U7(n&v3#aU4gi3dN6ym&PL%bA!}MM8i&D#^GdJG~T0F2=4^nv4P_7zj?|ylln^M z9zPka`pX9|5WTVW9$#dSFnvVx3itG#S@i~{zM zz)sYFt**MK_XF?nAz_!o6SjPa%A*GvAxdQituZoM-zhKZ4yeRr{!q|kgGuf?R@u0M zl3Npl)}`#DP}Q?SU);V|3oIGU!xeHvRv`mnTWGcBp$5!7XBmfZc+6Mk`y0NuyNB77 zP=4muoW#~yX!NTK?1h79B|g{|vjXe~2O;5<4O<40>vJ3GoKG&k;0Ovx8mxx)zzqP> zldKdv#Jae41l2@pO6o1{rMoEFxwpInBUP&!&7C12rH7|xMli8eTD@TXJp{tJF^J=q z3xw3QR`gK(SK<@P05JI_Opl~jPP10-V)d4Sy2WE|s#gB@L^Z4BoT#~w#YB0VH!HMm z?{O{<%mFPv<4kdgYcZ}Sc$B+<^}zT37Wj3 z_4PAPH&S#b#W4)u>4v=i<(MUTmYW}QA{cs%Z9kFzrN{EW)CSYv{Du4b#q;)mP_Ga* zb;U{0^kxqL%vcyK0oEwAZ^AGz*F@O;U)<^+WE%(eN&=JYjZRHu{@|-T_b2``C8SqG zma4~imU2F$@n@*({874_JWB!jlO8PcpV&#l`;aaVXrK0@l=gC&Kb<|fLe1z_+(JSF@`IDQh%+3`Q|Cd2qeU;vE9wdOS!Gemeq0~EO3sUPl8M_;(0 z8V-;GYp5}qI zt~#yT^)aTUXXj=i!7^6^srrSMJE8{uq)}cW(VJiv<1SEv<55!tt7HmV$1yA#?+%Yk)@YncmUFF38JJd^;TdC= zt_Fc?voH*o^&VSY^DUa|3Hu+osCV}YP=CnfRjb6l9QB;VDXBrT9L&i}=MhT^A~A*5 z;vF7ng?fEJ?Mm=wKl+FxE9n!`=!oY300I&GS;^|sCR`6hvrp0=y&!nO;$^f?5`Z@x z)c&?W*DLN7qv(~Ck%dClf@Xk<6gAITK(fB&f~tV-{@IKK_QXG>u~fgCWi%P`urA)1 z^A!&2eKWS5?jxVHyZ)n$Q{2(GJWtvgkI{n`KFn3m*#TyW%Kftc0Qtjgwm(F%!b%#q zsoXIb#l%6SW15w1y&8qtUsDVCN?6Oe3gow`mUN8GZ^XM)c$wj^f-Dd|$Ucv$RO|6A zdcib(MWwCuo(O2_VV6_+hK+yi57#t6s1~P;V86tyASg67ycEIi6o75I`sduc03s<)I-i&+3&FMO zCN#LB&h5hIhs{@8sa;R~P};~<*}?AAQO6dm85D8#0Mp7U=}#Q^j+I*Fh}KUIAzGyv z3f)rQjmoVUYML7S?l+6&v+?%@TG*6O5wM-5joE&tN$~H`sJ~J&f1gpS7Fvq#-VJ?` zEJnycTfY#2W5+6k<4m_8Le1Z(@!nJsse4Lr23dWY+PXhaW?`!NR9RtFcleA=Nst4i z`h`ll#YfdluR>KU2@eak{s+{x#bIkd#L)sYtFPRwE+(N>fFV5lv1S=8)fcyz2S5R# zWp>VW8NUT3CG{a&g)vyWR+o>c(L9PQQp@cVZqc}T<-{pedx?ss+TM6m8nluA?3Wjbr35S7Msjwkdw4Ow-Uo`+^bDi%hWU%u`Tx>g<&eb2#CXlbLjME zCa%KzAmBS8N#-R)F4BiwOQ}2sWtk~}!%^QmjRmW93PE1hACdyacN#703viIPisR8J zbI&mqrkvDv3(lo$%Mk!pL1yZhE#H`3v0Wud7efpFfJY$pf)NQY+53p*=+5wXjHDbz z4P}*j)>IDGx8hOMZ7^!+Hu2(Ezhr28^zGs%ik(Fm(T@`OodaOijHT1yTsIuWp>rG? z8tBRuT16QhNS&@&=BxJ>m|UG@?L)^{rPw}UpAl7vUF9#N491SUTr%fTX52TGlpJDE z*D)$pk-b;K_?Jy)DE5Up>lX~u;Y13FS8Tv{mzhrx%v|r`%q%k-)Cw*%?Wf^pXA!ar zw-0-ZdPzfLGwuZy)cwn5HT$Bzg-W=9cZIyyA5rEKfLy!woasn^fF+es0v-<|_2OW~ z#6FI(K_3phl>Y#X?kq?s(YSpwxkU>j=>6ZQ(V#Dt1QP=|)K}Hl`}Z1AfZF1oaRW|d zG%H$xjX`P~QHU1f{{T1=pYX!Yp){cpthF@=hEuN}ae63)LDlTcuvti23i0Mrg$qth zKQRk}S3sA0?JbD>xH!GV$b^-!ei4)aU#U(`vDuABl-&CWs{o+r=y#96hYiaXI)0!S z1zng6uEP=$BnU`wirB>>{Jx<&%&2CLJU}6! zF4Fn;A5j7ENRzr1*i=cJHyKa2Me2S~369wMTG!NHaKm{hnW z7#Q?p+-Kn_Wek2%^#SSA96FuX;xMN}Hv?hT6}40@P{w-}`DR;?WE9Re<6P!fbWj6_ zIe+#AO+va9e=T={qks%DSGYpBu3B-PF1r%&H$@rFF6gW6Z093Z3IXk(xH9d6<4wPa ztvo7}7AwrZWS9*3!|J(<4)%uCns}I9?*5^$9g@FbFRI@+18Hj;k<0K@%7seEj+#uD zd(5rG3Fz9qFyb3p_np=24L1^ufrYgf^&TL$?lJd++cBZ_6eEHCM^q9Qn!}CR3Wy4& zTPL}$`0Fb_WLoQ$=g(D&1X@KHUpAd%azd5BWLth%h@EW;Tq(yxDY<;ey$kD`hj2;? zp-wQPj6|C`WvY8HMZrNLvqIj!T_MYBs-W0(y18-K4AmHD-s%WQJn_;8VwsvC3>NDJ zRo8B^ipf>dE$u6q?-JE@9m0jaODBQ%0HHUixUD127DsK8=$FX>5bHeZAy^@J#yOQ# z=^Vi6h~;p@7C&;{ksQNpOQJYKBuXKCooITf)+UOMnx7Hs)QUPUYgn6iMZbX0Ma1S=@Ch$K1O`Em2=LgP>i_1 zaGc99?+jV|ekRQt*Xze=WL3JLdtRU&7ch8JrU_3q6}@@jdO~o!1u3@jSI{rY{{Re1_trBzf~ZE9(FQCbV5>CRzY%y*LqQ08G1@iYWlW%%-VH*4n$!ha;ls=$ zYw1CSZ+z;dAQ<5&8*SBdC~cQSZO*V}uxKDhsDMT6wRpK(SPsa7^>}{Z_RR%|%U7&! z66b*&s15hyH3c9E>#>@<#B_@%7Zv6#SKJO8isf9^Rwz=l$5_!z>J#+=o0?wGwy<%u zKp4ZM4!V?4YR;l@D!WQn3zTkkhyyVPBLtvT%#`HZdQURj#8h38Rtd~yDupIzWJ@;; zMLCR6MVOhJVu9W;l{3#+8P5}z^+#toehyj}VGS z@=V)76=A#%7yke}L0f;{s0B`RCzLAB(9A*mWCqRN6Ls~ZVOjl38~a%hm-i4H9^{rT zuVh`-kBDT|U6|oim^8+8!vJWb2Wfuj^h2)tCK`wN-W8^wZ{{2~X}NbhR)Oa*^LtD5 zec5yT*dSPYM;y+bolKE{MWbh0n8)a%9WRti{m{DnkvuEumgczXFq&RQGb3M^v-wyN za=qeesEV=#^AoVg+U9G?gvl86hO>T%aF^@|jtu>fvle*HXKx5|{V7NbI#xL5_-a$q z=}6quD>!B*QZ?Bg-)Cf~SR86p>{YGH4Wo!f1`Sr;^1M;$nYW6F8!VPhXTuN*YBtv% zt|b_A5oxRLP6gJbjue!oh9D+ZooXtWV6P*7Crc@3EK`(&gr1 zeuz}_US;eDH};sZ`AmPw1ynxbT)ZmG_L?uWs{a5?cKksat@;tzWa&6(r34u@ccKHD zaMv>G%63!xg`7U|N}+uxbr%_YF=YY25G?VBE%?Q|AFV-oT+u9Xc#CLR+5wMhjd*V( zF7$Y&e*jn^{gK$areOB|=JEHl9lvu5{{S;C)B~IQN0Zv&cHg+n2Eh1UAWaX}BOiw} zUq)R200s%9c*?`|WvzV}2T!DG{^VA1^np8lnV;~8t&961Ym@4CC3czQUgTY(o+T$Q zh}HZPD18!=+IfqtkTy@X3i4jk^1kH8~to!7Zex9&A*V0mTwk8oMZ z_bzi@;+bFEv73Fxx?gZ(`6E?g@|Fe*mnIs&+%x{@6M;Ol0*La>56Kt)$nluN{PNIe z^96QOVEgC({I?&HH6iy(Ld4~k{{Vb1x9^R_xNOB6eS87bzGKY(pxD#INU;-5#twgp zlln>v!|FP$`i{aY(sL>GDCqi~h01ebC>mwc6>|o6mye3l>xc_Fr{Mu-ylOaR%AQcV zIb~U3op@e!`j{}=D+j?_mz+nsJ6aX!kDeT8%2VGH4Y`84%pU11gfc&7D;vgoKnjX? zJFYuQ=UpgdvpyMAWV)WC^4Bjz++@%IWqMR@0D{n748+?g<7;)81C%B1kVkhfp=CH4 zi?036MN9O${LiwY0tOx2GMUDr+4!hz=e!tP{Yw^9IXCKD^XVMJ3!0iSS#>F#A!-n@ zO`dKH@cW1wL+$2R;@N>r&Bo9%s|!5bZEf!rDJi_Aer2ZEtS%0)@Z(b8-ghm*-x8sX zlRF!X(|mN5`*RwW*9<$ZWj%!&!rmjo}rf= z_Q5qnh!C%)0OIKfQ-yl|Az$!Ek+Z6axRzN8g+ERP{;1LX5Oa&fT~w*V^i0hLb;;s% z?FNDYiizRNER^%#Qmz~6a`O=3eiElYlq@Ly#Qy-)2bbJX$oEHdxz zT0;KfMpiZE>J82P!wymVifqD*v^e|4mvWCZ6>g{TH=Ws^Q%p(4;J(e*WF0I_G+E=D z@fEDlHC_`|dAOd0qUkQa+WU;k8VOdW!rpfpt5Q+DLcfe#;!qVs?RkUwS53o%E~}&i zm|%*sImqTykge@PsJ(<*Fh7=5om&=Of;jhashSx}g{%5Th0&Kv))*#BbP=mNFnJ)T z&=?6tyX_b@9AeWNSRnayj(TqXiE|SIw}*G_0{PT|C|0w4OkM~ZDqkAJA9;G7s-?P= zf>hgn2(t3J&WYbKEYcyg>YU2LQv!!t>)JV!-K%uYAX2K<%QoLi&BNwaqF(D(=nG=^Mto+`SSf;f|?byAFXMW#FY4c)RIU%o&8e zWAt#I;?PoXB3+H^OvTyI&GBPx<7X3V(pJW- zimVv#wg?){Wry>UUN{HUzf(MkvfqvG#4Hdv2z0vFxKi4Ybn7Yc0UZ}9^NwN(#o z5liZo2>6PvMG=E{PBUAAv@JjbD+@g5R6Fu}BPa^A0+wUlddu6gzP&la>GzWeqb~WfPc-aLKwWym~?vTNk@9nvG2tTa@PM?KlPXWrcCM zZ;Vh8PtK<_+)Hh-?FwPjY$;z6QkUZp2P2rXMq+07EKG*uc@G(jMjh^NEhCA$D!fN2 zsbRD87M($13tKJ}7{pfGH5P648go-@O6D!cQ(q5aUGH!%@(@#SCo^ob2NMZNY~&K? zn$-NipUz-=XYDb|V`+rdkJWHs}Ort~L@oi@o zH>!YH%Kd+cLBCOfiU7V?1ylh=9V?^RP>rC+7lrS3MP|#k-^3vR$aak8pEC?oG;BC| zFt%cBuqxQB=d1gcnC7y&hw?+3W@x>2i!G234N@Z`8+a`c{L9J38FXYtk;d;3?>H*C z-^l(W(qS5Q=T3T%?QC7?6#3Mx=?P2+mo*58xaNVny-Y}q3?0Q^Z`xLHmE@f-D+R3) z=QSFIrV%Scpl6erdtt=QE|g%RT*X$v+~!#nloBSz7~=Z|ex{+xE-bI@ z?g+9RmmFZAga|FU<>3oj>emXm*DG*1g;5qxA{3QYxY{XO`nMk1{LIp7w7kCJn(Fy5 z{DAt8F<2_CuhDD<5B|6)g+Kwfw5F z!4qS^896KT%*X=dazfh918|F_1Cmz|WFVm3YIHb z9U;8mcZq=B(R3IdfyY*AV!*+*d8Ru{A;>n-&Z{nS4AWVeX-oV)MchlIiqYpkTFg)6)Sft(t^axPVJjF(%0aNb`LXAr8!s4 z97k>uY;3AE_0|_NA_YSIfaWJh?#CmJQrUh|rVur6f`@f_OUN{Zq8>u6$7v15n+Hn+ z9ious3f+r9*vi~IJ1HvQ4dCenWaE}(z~4)hYL77kN%bfeY8fbj8e$Vk<_#54DqU}J zS(3jH2RFEf)K!wMJ+%yOJh=B2M=&dxnwVu@GX~|}V|QkzqTC468s*HtskKWk*q1J~ z42gR}BEfMSZ*X?DDbK+_C*-)A%--w!m`g4gBhAA?9vC{mYAwGiE`HDZg$cIhm(-(? z)}Xqw3NaNOIVUXtO)u@5Qr_H9ANIoX|kk8x_=c1Na90( zW*`_#TE<(w)$colp?%_3%=63{RHoV2)T`Vq1af4B!HOV9d*|G>LY#AjVOob!l*)(B zy-I*-xY^3Dud*BPw2&@AykIOos`XLtVD(uNHW?=9)CN737uV=FK)IcLuJ2)if6x`5SCTkZ%e71lI?5wJPa{w4X@3A*rKX+#}|jaG3T zn>B>=7_Iu3d8y49Rk@dE~R zmu6e8ar8%xQ9{!TuPm!0XH_qUsT(NU<33-aCoAPAJznvTQAb*2mfc&AQ(Qh0O#0%&QI-*;wOK&p3DrU_XYvSr|@$q*RH)PMR zKe@6GWlGpO?=rvTMH}Cl-2K6}rY*kUxqiuy89T>YgS-bnnhUSGIL%&SoCmm5miGnD zQO+|0-JM_^s$Y-NB>w*&-2E|p9>e{^ta{V^#4qS!{{X!MxAetp*>n0l!2bZ+C&+t< ze-7XPzuOvf_A@by%MfSmnUvGS;r{@CsaC@O0KFwP&;98QGAvl07^vX8CHDg5{`iQi zOL>4z`+uZB%eE}QUm3Fd4JR>UrYTkC3^88y30QAf4w~-Fqo2tDDF?Mpu@1>B+UZJd(=)03@QV;oc}| zZRR1mN5dMi2e_vs&E{QmDXdGQQx3Ar31&Ba;%5mi;oKT=EP75e5JH{I-&wa(!#IS~ znuu069MzNck$x3jszxYt{NtE~*>8FO0F66OseS|Khkol&=snKPTfp%SotEJc=wq(7 zPFC;@8`;EWa0)?~Q30$7@baG;upG|bQavyTj`6VorbT8X08@_Wo!aFla9+{nY zqYTyA%;hJ!S8tfDg>Knak1gU?t93VCx}8$`V$NgK{@(=u0E`0Y<4_H+^$cg~BeMsB zBID~JQq7x!G$TzQsBU3Y*T*Q~%dE%kq>9QOA9qMzk|s1WdP zFX{&er~V?iMr$kjf$e(#05uyh-~8zkU~x{z`0+GW-9O?OBFRFR(-^7TVZN#hEN>7C zLV8d|xfiDiM!@?Zu!quS-|U_DnD-^GX-nQbNauy@%XVW)BQ@Y z-{Kr!L@aQ5nJ!;#z#qp1&(jVMs$aSYI(G!rbiM9s(KcD9LMh2vh~nQ7R&BOr+Wo~%EMFv{$oDDN?G$y3s<-Z9!u`PM7aUwqgcSsP zjkdS7%jmx1T36h5Db{hb3GNNw#m9@(MT2H%;i4Vn{F8OUT&A-D%E~5mW?v)xc!PR_ zEQmJF{6sS_O0Hdcj*2QJCpmmTw??4=>jmm)K>(w5-B&KtmN4rdmI=Aua)VxvGT^;wpxcJwg^#G9+^Lqg5NEC`pclyg=4Ha-wld3;eWlL)#?yI? zgO$Z)R9Juw40_GMrd;)y9cI~>7o(1`D)Se^;1x#V1Kx875NlenD*Y1IxV0#x^$T_* zk$(^)Pg?=R2OxXr@hZWD?GTMuwgF&`B58*TIITsrw^GOxKTA3d_9oS$o{?x5nS#E& zLfoYbGbnChZCBNZi@Ng^Y4&jx27U84N2mCV39cE}GL;n;o+TU^$Wh6HG#p!UmdbdH z)5{E@s>(fOXdF6CppSAl4RmFSsL)sEq|g?*#7fKR1*@Da3*P!ic_DT+Qjn#toJ}>e ztjO0A>~U}*N7Q>=+ZEG`l(+>PRIYWb)S#eW7cT695k)ATB3uV(fn3Y7v_fDr3vTR! z>h1tAmS8NsohBk1I?CXVoD0PF8a&N*ZYx}at25Awz5B4H*Z#`2Z>WbD`qZTyJ|Y!Y z;}AE3FF^X5T^~`ckHnyvIuD2)35!S6N>v|FZJ%<9YXj1AK8f2-+9usA#8mz%PxvQJ z`iJE1kpiG5tC)?6!V~`h3Ms_!My)=lFH<)t;uB{iyJLa+nw_SJ!7N;TSo{7Ta^q`i z?zq&&EouH~{^N!Wqa>T%XPzNOovQx;?tW#cHskR#6)v$j9xnuK<_fh|ODpJua&7M7 z%l3Db!a^l2tnu#WS_Xjrc0{=*Ebv=LP1E-i4&;9FhGJ!h5H~Rn#VFTaA~>Ol+OuXI zM1h$Z81;;aZk^HF@$OXTS#1%%&31szj9AxR74AQ~tD-!Y-iRg=jM0bla_l$WIx>hL z3cbvNsgY)^D6X>w5JIARQ;#(&BC?=uwz`z#wA#Q4N4Z%0o1tWpwab1ZuuAGg7S(F$ zt;7K8RZq7aJj#g8hHs|M{x(HYg`hIfEa~u2TccRy%p#f!0{c0uvg0UMDgk+QhvK&E zY^G1PCzqG2w_fqhV&z1je=v52;h;RNMFUm)SX9l#SsnXcSO#x(LLDZo%q^el9CwEHE9}nS2(OS_Z@M zaLOrQ#|6!Ebp`2Eg{uy{!xX~6{W0IM-VaF4+V6>tE;dbTy>J)vjvc>XyWDc@_02{=g-cCbzuylx|fh@Vv9!$EkaZK-%X5;L_ZAei(0^;;Q;_ z0xMzFTl!PBGOY}zODmdt<^%#$&(r?vysRrvTJ=@GQrMDSU)KKs5P9Jnx}qW%6}h{L z>dx_Litdnm#0Hk{WV%;u)H>g)iMw?W*y%S$V&F&~xM3U|-w!#$}3?aaC~&0#O!J#*DhfcO2B)P*n6sF?Nz`47;11vzd9sXne{l zxe+#a_;-q>a6nZc0dHgz!l!I&(?Im7hCV)4IgPuf(4s3K*Cx3CVvs!V?Tt-UWsLs-1nNAkbI#iJQ~f@$mw?Nf z{;0$%P@|e&yTX%;w-OCrXNO1DUsCK6@kF-)n;Qdn3lfzT z9rPc{`h+t~*R7^`%ayH_en_Y0&m@!2jH0GeK0rnx`)$!16+J$2{Kh!13jqyTVJ6i29akEDx(huY$;pnQp(B_pk`4IA#n0rrZG0ej{kg+$*kleKOM$SiQ`^ zZUuTvv!1-Mnuj9*!~(2#hO5@BwSQ{N5T#1kvFQ7Q38#pQv%SNpwWnIYRT~hc0|ZJQ z2Oo&mOE-OB-I0b{EJFPZK`Kii7FRB);^F*VxfR3Lki-l9M<(>fLdwJ0S z<$_$)v+0BX0LuzO%4^fsS-?`%3fXT&VAS>TpvUNwnW4t zL1@AA0#k&#EV_oD4C7eRHCKbYSbpGe#1T2kp&#NgWg^#0;`_t}4oFhqzW)I3Iuj}! zyvBBII9r8vYbY&>1SL{bUW>F>^Ejn8hP^?0*ra{KNE}&yddCzZl?r(vLE4vIePGMf zKXAQvwJhp$4&}^d3I)LII`t7rRIkwcVLN^r$vQ7F-#E6)UOyx^4uNahw;VGR-mbdB zVRjrI2qD8N&k0dvy@qP0QM0%s-9>gSoZnd9)7FFCMQV={^1F#41}(Rvu_^708s}Fo z5WUv=9eRp^VxSjV)Ll05{{R^1?gSXUW6Z%;;)z8WtJ4)Z>wO~H0SLCCgG&D50$tsS z!v>xn%|4}VssXO?@)%{M3~rqB7O1N-&HmfjXk^rEV;~3VbG|0OJceIH3H`7ME_S@!JxPWnfmC0#!%b zm33Tpib%#qZJ65iRYv!awp$zB%EwKYaX%AT2vSQ(QOL#XT)|QP>3G$NSk?4|@K#dz z&EoYE(s0;2DQ4V$VJ)h3*aEkpk2T@c*_zqoB)%nwV~g`}dDOXXh|}t`oZKDdC5Dl0 zu4pv`gpfcB&FcL^3v5AK;YW|#0z!7D?hL~kvKb0pT}J!WW$V*9QtKWadzdzfl^krE02Om~jTQ z(vYBn+f?6(n^!%e*Y|O+6L%=Ul%Q8O&Lxv4mGLnO7F@A2xPnzPHsezV6L^hau`Z|T zZHacryDOxoi`*%QyhXf2GQh+g;x&Ed@W)_@T}*JhsX;dk>T-$Hb0z$8Lw9lfVCD~c zk9KACX1QgzxqcNP_KjEe7!uF2SD(!@ujXe9?TKsdjgtUT_DnW?^1c1=@!jVUOy+&;BSQFLP0hC8 zsRXjmQ0wc|I=Z+Sh4_{QufY><=s;cFcC6v+W>z%5a{-?pbOL{0c${^D*G@s0h&Zq7Osq zfVqoY4|p>t)QhF**p%`3!Ga&C=oRcm46<+m=ToVPz(AFz_=@xNgKhREXFo}hEw2P1 zRv1T=uIdeFuaZ<_&_k~FMaPjU<@FLa`iBEOOoSQ6m)dsUQ;e}41mK7vg2!(6Co@rXDw99e2NK}hHcoXl2Q$QR77QH6;8q_U;Q|j3 z7c)j>+!IpqErZM$%cvz{I)QK_n3)myuCa43Q>Yt)-aZM_3l(DLKh(jFCNT&Kl|-z| zJ4&gDqYJr|Y1|Rh7Yr&CIF9BoUgDv-)vxh+><7XJCRZJR-DWds- z3Mwq&j$51G6LEUxTXSu5QzW`I5p^jFbq!TV67F2M%ri~&mbRN+tZ9~od|3LN;$E&X zqs&Cl5iG<(mBwglJ)?I4sNySFh8f%`ltTm2mb}2N?ojQNjm(dC;bOoe(Pn~fAVrUn z+Blan@u_zSbp{VuZfW?oQ5uO;nc{H_fAG9bZgUjBk<3iNFgc3r5NZZHPVvxE;3hEs zBV66VsibiRa5oN65pELk7cdbhj^WNBs23`=aTTdgS&^6+!p!=Vc6FSU6$-<0*=fD7 zb2T|GEX(2*(k~o99T|(34${b02duG+`hasV#0aL8<-5g8w&U6+egt(9H3n{THxrp} z1|wqx=@r)#%N4teE^O}&<~W>7Ih~a*EW;3^5a^XpX{bW)v_%mb8k8A>=@A(W3dBmw z2zPwN#e5~JjM*1TwPChD2{>zHdE?v&k zc*3L7zo|zfPYK!#yg`E4s6X(4^NCzdZdoibH_Y1~aC+gFMOaIhg9q+pK?sUY zJIqzAYg>m?5IyEq0}!DW8H}vzHcEUWMa_notY|H8aVratn7XaTt676jHx}N|qeNv3 z_<}{eu#8xG{3T*s!x(0(mZI%%iwV?u?ri9iHk`XxsIk;O=45Tvy(Fyf)rrfHeM#z@Bz$r)Ez`z z7#g_mu?$$I<_DlZnR2CBn6>6RX?9p|q2KinyL-7+U+%K6n27Ab%y$0(@V2J@E|{>F ziGKqK@8esR%lI&6W@Y9KvMORzQDW*4ip0FYGSfGQ$tlKQ(ztgG;$OmfmQ}ME#2yKZ z+nHs%xn|I-k`&b4V{TYQfb@9Tg(*=G!;#A3r$31(Eu&m zT*@uilq44nEh-bq)N(XM5e2EH3~*_2MRRg(f--YqNj=&-#n@PkDJD)jspQQ2(u`ky z{0Zkd=RD7KU-$j|E_tG0;FoYvI6;R<)0|a=|FV*-97Q$dX8!}RTQL=J8i$E~^1cFe zCgMy2&T4Hi^)+5_`i>rGJo!wn3PNC?jvc9CTcZ)=`?jAvY^pXe|BP6TJ!Zm4Cp$|N z0_J?Cql=)su=P?jQG5iqbc*WKwK)5*K1GE?!g!cX_RUj|QCwp7%UZQAF>NEj{dYuP3U6+jnw&JZ0Fa%oWev zGCq68<4RzU=fT)BfS@9(WoheU{d#_Dpf9TDe_0uZU)(C5kIR}#wBJo&2QNsv5^b+R zQUOsKjne(J`W0qDb?G>Eo5VF2`z2J@uA%&;^#Y#-4)ygHzzXba7?hhY(1CRkloJcG z?(ThK#Wj?mP!{*lr_Jx-6X|$Pa@k~|M@LxlP_BFQDu2W+Zz)nS$c!Bq-3k7o+k60x z-n0Dx&>wVJD^j~U$$BIFkBTY>MIchtJJsOG94K zkJgQ?N$bW_Y;W&7C`s{P-4wyONBQ|xY~HW>jnnl&FXY0MW4#f*EC=T0SA}@!R3FgB z6WpYeQOLJ?Cw=}cdGZ6-`@hxKgw)^KTT$T5{SWd!;S3OD`_q?s7xR7!{Tb6zQ(RGk zEcDr%e870&J&Jp@NBy<#2$9=u{8QMe5be48A)g)nM><3!y9eWfA)Z6X7WLN(96I~K zi{c%WJC-JUKR}{@z{soYx{dqv9SJWsdrzh@Uv`9r3Y*jBKDxf$Z1~&Orpu>BV!$2I70ggwREyQzuHu| z9B+v)S7#KUF6(%6v(+)}%YEk0)27XulFNPhYC`#-MPDJDP?J8-pus{UCTMtitYMB= zLMHVPb$Pemld2vav@?odVH%8Co=dR0X|VekpzM2dJmHpicX{?^KoUb+-SwYUTkX@@ z(9*W|UTu0UdnQ6;8RqX@3kC9*ov_}Y-3&&ih*_PLS{!KX>m>=0=)av^H_lkk{=LBJ zR8On$M=PvG1p}hpxsWJ@-;lG@PlqC#Z%;zQrf4R2n9jc@C-~6P>jR0CX(5yn zm-r}^hcSEp%&(`#g6qIk)LH1xLfGq;*}JcMlcSWa`miNvb)GitW&`L7xW_-eQ7pLY zzc1)28)@eFx1c|e?%7!^mFFUTwXYij`(vAS8BANG>zpz$_3>)dR$;JmUeUy|Tsb)M#hN{fp7yzM4>E1~#^SHIyb zFRLnO1qoAlzVk?e8AK(Kmu;1ftM2hOKU$4dKIpmog{fujJIv@$9fSJ~o`_4Q=X!SV ztUD+*NBYM8x-$(Qu|}N!mP($G5@{4pQgmd!YMh-#_m(*@#M$fh2m24-q8sAb1~$c< zpCxw}#)X-_yeOKjD&wrXY?h3k*{RKvH4neLGcpQNRKBma+U6~}8qu@9tB2K`dpGSc zD=w694eL<8yB#o@P!MSgP*rk*HFCdSSnw>*_I9C5nQthd6k5X1l#dz#@*z`|U$KvQ zX3B=1El)!U6&xCVUE-`~>LMARh{|SQK7ZaEHW*qym}^k3nocO+-Sg+D4_H!NCXOA8 zO)k-x&Q%j?|H}`u8o)Xr`V_mmuZE)JwZ)r94Z4wEZ)1ovp8qjlMbj|6AeTFvty^VC z#4`!C*<_Co&H~E(R8uz*z~}oQ)h#k)F*yI4aK-Id3}?en_#+ca>CoUhnHTOjW{(B? zX;s^-cdaL=_79j47>=)$_A*)09O`-=cv7YIy!l56N98#Y7Mo-^r;KtW*$;cWu|(X4 zuJ4Jz?k!j^*5nx zC%ed~Xfv+GlbSp{U!uz?_ifk$Sre)h9JtJ8*;cv^T-cnsCGKQ|^KpygokO3ja81ne|4Yug$yzVP@W@y74_z-Y6cr3*DU&#jOr2{j5EAv$Cox(ZelW>RRIMW^Ot ze4wU>?69Umucuc$?H6%qAmJahK4m!N+6m~O!4g}&SLlyKr$xwHHE)QYPCEhq~6T_$y5+ORt-w1J=%9Y@QD z=w4`D9j5k^KrN!s0d2$qXYoW3lRThLM`bCM6L(UJL>O6m4CoDQ@@N}yA}J%ZTF^s1 z516=tPDP}XQP*TXOASDwv5+3qmHccLQ5`+!;jr}!(74VI3(i6dPN`GI{kIh9=oijB1E1%&ItaFp?^M*bmM2ZtNobSqsW}^n;dt%UYU>GH<^Lw z94oRd2^5QgbffHwycWv=*ZCP()BMK~8uCO&-t!bPP9g!1&^{W9@A_k{F%Ja=1-lnj zonW|N8YQ!LDY1b=%M{DVch%vf-WJuP>H}?0@f8=wY%c%b$de6p9#8Tx?Hzb^tpJj4 zMp2P)YnchRgJ%JW$h>MKuQUgAPY(xANV(=p4m=1^?x+Z1&aDN3TT+Gzt~cud{`_oA zjB7(!yhvOdu!;of8QdoJk;XY&93?2AkySR)7v441IGJKwTYC^Vr@&-O0Y5ncg`RX} zEmWT$oBXrNGs3-2K3ARYc4mH-rm2_FAyH_4)DN&IJn;Oc!m~fT=V1(*^~(I6P?bbc zqssH+t&$TIX}U)RFJ`S;@!voijH&yL@_2%3+8$Q&*|7kjZ7}`H_0`$}Ihxz^>rm49 zj>*(q%_})pZ&eew#!Yu_o0@-I|IGle_4lffkG`E91zA8!V&}mU8=7N3<|o{qoLx8n zwuKint&6rP5$woOTSuj6Ux-J3-H@=0GK_vre7#w?+Z*g`$)r#nz4!}5Cvt1~^QAVJ;uX83fLINtNM=M(*$;tK~2 zW*;9m`|{R$FL*p_#D1T{Q;-Z8jQFvWz_H~a=}7Yx2mJ@;q-P97E}DLin}H^RMf)k! zrs=0FH8ZH<3czLNq`HxL*7pIeMEMHu60h&>Ze=921M(wMBy^N6jo08jL1H+|Fu_Kz zTbnBjn9ddLRhVx0-yq0Ehpn0ENyM8$@QYlpHMEpb<$<{_Ik*z7-n zrz2LTIJ=K(FIk*-XO&b-EVA~y;U!?REzFk}SxS*^?+odwI2jCKw&>L+8Ua3f?5OTG z30jk5hfJjDi5p2UV;-DzM_!qCS??bn0|3VAfL^!H&X&*P&K`W&{XddZWd zAQU+j2{rPOuMC6PwCLrJL7_R%ri_E_1 zt9aR?1)-8e*ao8i&`-k%_v%fZ_rSx5&lY&CgeK(kk3|oFg5bxMzlQCTn^9&OOBQU0 zeSU#=cAuFi$5T5Y9GM;a({BBwp_Zl8Xmmowexn$&7R(`BTudtlZLMRkD;Wb8cJ3y9_I zuEh2q5(V4pe7-e;_9=tLu#BQ3=Y#_W4u;|&i_`8Ddx|YN z@YE4fz)$$%u*~m8s{O&S(x(} znyIb|s&j#FmAq=BodTq!L#b4O@m)7zI(!-Qv)v&}M4F|ESyw`Aijs5?!F>y8>^D&N zhdzB}L<5L(&xcn#MuE?!J>SU9>R3^ZaeJNmfZZh3PO6=d7nykVpR5CvKc8B`R%ESu zq0eg-WUJ=>FhUA2556)9+bpPLNTSsa%!$xtymlac{Y%KMLVf;?5!E_BcronWjx==U zS*YwdJDA&f-7ij6KTL-LJ zIHNocY%=Y|wXf|pG`o!xP5~cwa-qJ|ZF*el88$zzbva}AMzg? zMH)*Ce;w92P;f=`Lf{ib68Rz|MUAkNgX#Py`($QxWG?||5{b&p$WQvP)=fKWL%0hH z=>*Pj5*T>OeoaX^exw5kGUvCGyP;i6Y%hpb%oo+S;(5B&i1oQw)|LzR)w-M(nK!H4 zfkd>&_T+MUmSx|r9nOE4e11<{6(RS9 zN;<+JlBYtP$cNyn;|lH~d!W}I3}qjPUM#9=<9v4Zv+YkN`sO6IA;aJ!xg=Xmj;n-U_8 z_nJ`Z?}NHAvR7`A)m<@aqT&5Rj)oaZV5wb*Hy{4)wF=oG?m@MzWFCMX74QHOL^17y z&aZxbz_wIPjDDtTzSGcV-#3}XtMC7=UZq)GR+;&mhJ}hSOLPxx^iQ8PIY|2fnZZg@z+QutBpJ#4)-vNV5FH#CC>`4bxO8I)Tsz$|NJ@e=@P0U zql0Hb?p_;QhBhtI7&f#F2QxYxS~obEUPrxfy=E71`GeP}!BJ7=6y{If%{!ky$Pk~h zni7F^Q-zui1#@8I3V(-UM)bF1o%Q1-AzH@*o0_uS(jl(X#i2E8?{Wd{6BZ;nhr)|) zxy{n;0~1sf?*VcL@ zlmE>b7i$!o|Mr-;#lh}i=?=Y*YKkJnaNBzX(l<%vvb7_J6$xGP`;0g`TbZ7}eF@Lr z8{v=J^?Kp4cl`HFmT%@B`NJ0iJ{jfU@TCL2k?<->OE=uOz3ge7lu71KjBEXUm%0g#;)|o z$Ztq*s}-YYO{5L`LFlbl{0{B=IG?PHGq=MFh_6Y+dS`{k+FXN7O1sYW$6`6-Z2NIw zlur>|zAg<9?MH+GP7ZgmAT_B?=pYa@otS-qXJ=g!_1=B0x>Ri+_Ae+qIQs31Gg`;2 z-H4rseD+kVd(Fqt1wtH_9_FIqQIYfH%drP}O z^{L@0Pkg0vriIdF6K8QgMd2M^k-JsWazM{+T+)B2E`H?uCaF3p?VYveG--nv#q>AR zpetTfc8ewYEdn}d73oB`d>rXP`@|oLeikAPgUbJ|DmaJ9Z`v3e@~qap-YI$x3s{`D zu+;aFOx{=1Y_iwAW|343-x54s?f=Xpj_ zOuY2Ymm+a?j7*eEC%dt`-UvEN&dsKfu0~*S4maBSXcQqMiQRxiFhdnxT+2>|8eUv@508Q4mC7k6(PLD-D$QY+VGYNu7hJs6yt$x9H}z(vS}Oli7a%dw z4mjzFi|-a`jao9#w9a3zTm}VaX;tRe=>j5;uOtR0xVJCsBWYf_xR8R@gum1Y|8a^pr@_E@Ri2%X=v5C--fu_IL6 zLK1YfE-k9Z_v;XMB1CknA`45)h%9+*z?w`$dodlC;)2keMb+{=>IrDw)2Q~pKe$iXtZL@ZkIExy4ry`;^1X0D=pp}^DdCu`JZlH#h{ zHYo!`#Z&2C7CwkO)N}K86X7FG>YW^a{Yv(|A5ZvSZR9tMGmr>z7853;eZgAHK7{13 z`H!tdL9R$&y^J4JhRv&%Eb4-2W~o!jBUkxB zL{0y?ri-H>)aamEV{LhuZ%{&K@s{R*5~UIc{Rofs5oFIt3?wr;2j31sGiIrN+Con! zolzS_H&wK#8}&hMDDA5uQACSRJV@m$^c{+dDAY+_Muj+Tiz^JquzXmR`#S_JbXoorr3Tb@LOS#+>d(d{3vXK@y1N7gdEFc09d z3Wt&;iV8f8%kH*9T}Tr~Ss`3oZc6BS1Z`;LrnvK?106kd5eKy5SA*Shz##tzn1ZYj z=UP?dX_Jo8l{**&Nv_?XzZKGTTGHeNf8ze%RWmNa zJ*}+OxwaNVIfU5*ge=aEPuPJ5Y#lW2WEo~d%%%C-We6RYW&W^L~{w^>HM0MJ14eu}(E-*j9mJIkZF zS-8*8;n&Nk4}{|%bONx|8Z>+XO2sR|H7u?_K|aKh3TkSx$Okh~qZX~w#7&DV#V7lb zj_4G_{S@_R-`Mckw!nmucCWW1jlYNUyQkF0Ot%OA%Vpi5noc@pe?oepQw^~9*gOGE zlt0Qs+@m!9x{m?VjGR9A==&xU7Tt7&HWjovMD!&5AWfXw Jb^QDE{{x53TfP7Q literal 0 HcmV?d00001 diff --git a/go.mod b/go.mod index a11ef3b21..1356e2fc8 100644 --- a/go.mod +++ b/go.mod @@ -19,6 +19,7 @@ require ( github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 github.com/hashicorp/go-hclog v1.6.3 github.com/hashicorp/go-plugin v1.7.0 + github.com/jackc/pgx/v5 v5.9.2 github.com/knadh/koanf/parsers/json v1.0.0 github.com/knadh/koanf/parsers/yaml v1.1.0 github.com/knadh/koanf/providers/confmap v1.0.0 @@ -26,7 +27,6 @@ require ( github.com/knadh/koanf/providers/file v1.2.1 github.com/knadh/koanf/providers/rawbytes v1.0.0 github.com/knadh/koanf/v2 v2.3.0 - github.com/lib/pq v1.11.2 github.com/rclone/rclone v1.73.5 github.com/robfig/cron/v3 v3.0.1 github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 @@ -121,6 +121,9 @@ require ( github.com/hashicorp/hcl/v2 v2.18.1 // indirect github.com/hashicorp/yamux v0.1.2 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect + github.com/jackc/puddle/v2 v2.2.2 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/jtolds/gls v4.20.0+incompatible // indirect diff --git a/go.sum b/go.sum index fd70893ca..a51771a86 100644 --- a/go.sum +++ b/go.sum @@ -348,6 +348,14 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/internxt/rclone-adapter v0.0.0-20260220172730-613f4cc8b8fd h1:dSIuz2mpJAPQfhHYtG57D0qwSkgC/vQ69gHfeyQ4kxA= github.com/internxt/rclone-adapter v0.0.0-20260220172730-613f4cc8b8fd/go.mod h1:vdPya4AIcDjvng4ViaAzqjegJf0VHYpYHQguFx5xBp0= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.9.2 h1:3ZhOzMWnR4yJ+RW1XImIPsD1aNSz4T4fyP7zlQb56hw= +github.com/jackc/pgx/v5 v5.9.2/go.mod h1:mal1tBGAFfLHvZzaYh77YS/eC6IX9OWbRV1QIIM0Jn4= +github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= +github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8= github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs= github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo= @@ -413,8 +421,6 @@ github.com/lanrat/extsort v1.4.2 h1:akbLIdo4PhNZtvjpaWnbXtGMmLtnGzXplkzfgl+XTTY= github.com/lanrat/extsort v1.4.2/go.mod h1:hceP6kxKPKebjN1RVrDBXMXXECbaI41Y94tt6MDazc4= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= -github.com/lib/pq v1.11.2 h1:x6gxUeu39V0BHZiugWe8LXZYZ+Utk7hSJGThs8sdzfs= -github.com/lib/pq v1.11.2/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA= github.com/lpar/date v1.0.0 h1:bq/zVqFTUmsxvd/CylidY4Udqpr9BOFrParoP6p0x/I= github.com/lpar/date v1.0.0/go.mod h1:KjYe0dDyMQTgpqcUz4LEIeM5VZwhggjVx/V2dtc8NSo= github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 h1:PwQumkgq4/acIiZhtifTV5OUqqiP82UAl0h87xj/l9k= @@ -463,6 +469,8 @@ github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= +github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo/v2 v2.27.2 h1:LzwLj0b89qtIy6SSASkzlNvX6WktqurSHwkk2ipF/Ns= github.com/onsi/ginkgo/v2 v2.27.2/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo= github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A= @@ -558,6 +566,7 @@ github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpE github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= diff --git a/internal/fixturegen/fixturegen_test.go b/internal/fixturegen/fixturegen_test.go new file mode 100644 index 000000000..f31d05ea1 --- /dev/null +++ b/internal/fixturegen/fixturegen_test.go @@ -0,0 +1,89 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build !no_sqlite + +package main + +import ( + "context" + "path/filepath" + "testing" + + entsql "entgo.io/ent/dialect/sql" + + "github.com/GoogleCloudPlatform/scion/pkg/ent/entc" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// expectedTableCount is the number of domain tables in the hub schema +// (excluding the schema_migrations bookkeeping table). The fixture must cover +// every one of them. +const expectedTableCount = 30 + +// TestFixtureCoverage is the CI coverage gate: it generates the fixture and +// fails if any domain table has zero rows. +func TestFixtureCoverage(t *testing.T) { + path := filepath.Join(t.TempDir(), "fixture.db") + report, err := Generate(context.Background(), path) + require.NoError(t, err) + + t.Logf("fixture covers %d domain tables", report.TotalTables()) + for _, c := range report.Counts { + t.Logf(" %-32s %d row(s)", c.Table, c.Count) + } + + assert.Equal(t, expectedTableCount, report.TotalTables(), + "fixture should cover exactly the %d domain tables", expectedTableCount) + assert.Empty(t, report.Missing, + "every domain table must have at least one fixture row; missing: %v", report.Missing) +} + +// TestFixtureLoadable verifies the generated database is a valid, openable +// SQLite store with the seeded data intact. +func TestFixtureLoadable(t *testing.T) { + ctx := context.Background() + path := filepath.Join(t.TempDir(), "fixture.db") + _, err := Generate(ctx, path) + require.NoError(t, err) + + // Reopen as a fresh Ent client and confirm connectivity + seeded rows. + client, err := entc.OpenSQLite("file:"+path, entc.PoolConfig{}) + require.NoError(t, err) + t.Cleanup(func() { _ = client.Close() }) + db := client.Driver().(*entsql.Driver).DB() + require.NoError(t, db.PingContext(ctx)) + + var users int + require.NoError(t, db.QueryRowContext(ctx, "SELECT COUNT(*) FROM users").Scan(&users)) + assert.Positive(t, users, "users table should have seeded rows") + + // The soft-deleted agent edge case must be present. + var deletedAgents int + require.NoError(t, db.QueryRowContext(ctx, + "SELECT COUNT(*) FROM agents WHERE deleted_at IS NOT NULL").Scan(&deletedAgents)) + assert.Positive(t, deletedAgents, "fixture should include a soft-deleted agent") +} + +// TestFixtureDeterministic verifies the spec produces a stable set of row +// counts across runs (no time.Now()/random values leaking in). +func TestFixtureDeterministic(t *testing.T) { + ctx := context.Background() + r1, err := Generate(ctx, filepath.Join(t.TempDir(), "a.db")) + require.NoError(t, err) + r2, err := Generate(ctx, filepath.Join(t.TempDir(), "b.db")) + require.NoError(t, err) + assert.Equal(t, r1.Counts, r2.Counts, "row counts should be identical across runs") +} diff --git a/internal/fixturegen/generate.go b/internal/fixturegen/generate.go new file mode 100644 index 000000000..7f36457ed --- /dev/null +++ b/internal/fixturegen/generate.go @@ -0,0 +1,180 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "context" + "database/sql" + "fmt" + "os" + "sort" + "strings" + + entsql "entgo.io/ent/dialect/sql" + + "github.com/GoogleCloudPlatform/scion/pkg/ent/entc" +) + +// schemaMigrationsTable is the bookkeeping table excluded from coverage — it is +// not a domain table, so it carries no fixture rows. +const schemaMigrationsTable = "schema_migrations" + +// TableCount records the number of fixture rows seeded into a table. +type TableCount struct { + Table string + Count int +} + +// Report summarizes a fixture generation run. +type Report struct { + Path string // path to the generated .db + Counts []TableCount // per-table row counts (sorted by table name) + Missing []string // domain tables with zero rows (coverage failures) +} + +// TotalTables returns the number of domain tables (excluding schema_migrations) +// the report covers. +func (r *Report) TotalTables() int { return len(r.Counts) } + +// Generate builds a fresh fixture database at path by running the schema +// migrations and seeding the Go-defined Spec, then performs the coverage check. +// Foreign-key enforcement is disabled during seeding so rows can be inserted in +// spec order without a topological sort; the resulting .db is still loadable. +func Generate(ctx context.Context, path string) (*Report, error) { + // Start from a clean file so re-runs are deterministic. + if err := os.Remove(path); err != nil && !os.IsNotExist(err) { + return nil, fmt.Errorf("removing existing fixture %s: %w", path, err) + } + + client, err := entc.OpenSQLite("file:"+path, entc.PoolConfig{}) + if err != nil { + return nil, fmt.Errorf("opening fixture db: %w", err) + } + defer client.Close() + + if err := entc.AutoMigrate(ctx, client); err != nil { + return nil, fmt.Errorf("migrating fixture db: %w", err) + } + + drv, ok := client.Driver().(*entsql.Driver) + if !ok { + return nil, fmt.Errorf("ent client driver does not expose a *sql.DB") + } + db := drv.DB() + if _, err := db.ExecContext(ctx, "PRAGMA foreign_keys = OFF"); err != nil { + return nil, fmt.Errorf("disabling foreign keys: %w", err) + } + + for _, tf := range Spec() { + for i, r := range tf.Rows { + if err := insertRow(ctx, db, tf.Table, r); err != nil { + return nil, fmt.Errorf("seeding %s row %d: %w", tf.Table, i, err) + } + } + } + + report, err := checkCoverage(ctx, db, path) + if err != nil { + return nil, err + } + return report, nil +} + +// checkCoverage lists every domain table in the database and counts its rows. +// A table with zero rows is recorded in Report.Missing. +func checkCoverage(ctx context.Context, db *sql.DB, path string) (*Report, error) { + tables, err := listTables(ctx, db) + if err != nil { + return nil, err + } + + report := &Report{Path: path} + for _, t := range tables { + var n int + if err := db.QueryRowContext(ctx, fmt.Sprintf("SELECT COUNT(*) FROM %q", t)).Scan(&n); err != nil { + return nil, fmt.Errorf("counting rows in %s: %w", t, err) + } + report.Counts = append(report.Counts, TableCount{Table: t, Count: n}) + if n == 0 { + report.Missing = append(report.Missing, t) + } + } + return report, nil +} + +// listTables returns the sorted set of domain table names (excluding SQLite +// internal tables and the schema_migrations bookkeeping table). +func listTables(ctx context.Context, db *sql.DB) ([]string, error) { + rows, err := db.QueryContext(ctx, + `SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' ORDER BY name`) + if err != nil { + return nil, fmt.Errorf("listing tables: %w", err) + } + defer rows.Close() + + var tables []string + for rows.Next() { + var name string + if err := rows.Scan(&name); err != nil { + return nil, err + } + if name == schemaMigrationsTable { + continue + } + tables = append(tables, name) + } + if err := rows.Err(); err != nil { + return nil, err + } + sort.Strings(tables) + return tables, nil +} + +// insertRow inserts a single fixture row using a parameterized statement so +// values are escaped by the driver. Columns are sorted for deterministic SQL. +func insertRow(ctx context.Context, db *sql.DB, table string, r row) error { + cols := make([]string, 0, len(r)) + for c := range r { + cols = append(cols, c) + } + sort.Strings(cols) + + placeholders := make([]string, len(cols)) + vals := make([]any, len(cols)) + quoted := make([]string, len(cols)) + for i, c := range cols { + placeholders[i] = "?" + quoted[i] = fmt.Sprintf("%q", c) + vals[i] = encode(r[c]) + } + + q := fmt.Sprintf("INSERT INTO %q (%s) VALUES (%s)", + table, strings.Join(quoted, ", "), strings.Join(placeholders, ", ")) + _, err := db.ExecContext(ctx, q, vals...) + return err +} + +// encode normalizes Go values into forms the SQLite driver accepts. Booleans +// become 0/1 integers; everything else (string, int, []byte, time.Time, nil) +// passes through unchanged. +func encode(v any) any { + if b, ok := v.(bool); ok { + if b { + return 1 + } + return 0 + } + return v +} diff --git a/internal/fixturegen/main.go b/internal/fixturegen/main.go new file mode 100644 index 000000000..e8a304c3d --- /dev/null +++ b/internal/fixturegen/main.go @@ -0,0 +1,123 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Command fixturegen generates the canonical hub test fixture database +// (testdata/hub-v46-fixture.db) from a Go-defined spec, verifies that every +// domain table is covered, and caches the resulting blob to the shared +// scratchpad mount for reuse by other agents and CI. +// +// Usage: +// +// go run ./internal/fixturegen +// +// The run fails (non-zero exit) if any domain table ends up with zero rows. +package main + +import ( + "context" + "fmt" + "io" + "os" + "path/filepath" +) + +// defaultOutputPath is the repository-relative path of the generated fixture. +const defaultOutputPath = "testdata/hub-v46-fixture.db" + +// defaultCacheDir is the shared-mount location where the fixture blob is cached +// for reuse. Overridable via SCION_FIXTURE_CACHE_DIR. +const defaultCacheDir = "/scion-volumes/scratchpad/postgres-integration/fixtures" + +func main() { + if err := run(); err != nil { + fmt.Fprintf(os.Stderr, "fixturegen: %v\n", err) + os.Exit(1) + } +} + +func run() error { + ctx := context.Background() + + outPath := defaultOutputPath + if err := os.MkdirAll(filepath.Dir(outPath), 0o755); err != nil { + return fmt.Errorf("creating output dir: %w", err) + } + + report, err := Generate(ctx, outPath) + if err != nil { + return err + } + + printReport(report) + + if len(report.Missing) > 0 { + return fmt.Errorf("coverage check failed: %d table(s) with zero rows: %v", + len(report.Missing), report.Missing) + } + + // Cache the blob to the shared mount. A missing/unwritable mount is a + // warning, not a hard failure, so the fixture can still be generated + // locally without the scratchpad. + cacheDir := defaultCacheDir + if v := os.Getenv("SCION_FIXTURE_CACHE_DIR"); v != "" { + cacheDir = v + } + if cached, err := cacheBlob(outPath, cacheDir); err != nil { + fmt.Fprintf(os.Stderr, "warning: could not cache fixture to %s: %v\n", cacheDir, err) + } else { + fmt.Printf("Cached fixture blob -> %s\n", cached) + } + + return nil +} + +// printReport prints the per-table coverage report. +func printReport(r *Report) { + fmt.Printf("Generated fixture: %s\n", r.Path) + fmt.Printf("Coverage: %d domain tables\n", r.TotalTables()) + for _, c := range r.Counts { + fmt.Printf(" %-32s %d row(s)\n", c.Table, c.Count) + } +} + +// cacheBlob copies the generated fixture into cacheDir and returns the +// destination path. +func cacheBlob(srcPath, cacheDir string) (string, error) { + if err := os.MkdirAll(cacheDir, 0o755); err != nil { + return "", err + } + dst := filepath.Join(cacheDir, filepath.Base(srcPath)) + if err := copyFile(srcPath, dst); err != nil { + return "", err + } + return dst, nil +} + +func copyFile(src, dst string) error { + in, err := os.Open(src) + if err != nil { + return err + } + defer in.Close() + + out, err := os.Create(dst) + if err != nil { + return err + } + if _, err := io.Copy(out, in); err != nil { + out.Close() + return err + } + return out.Close() +} diff --git a/internal/fixturegen/spec.go b/internal/fixturegen/spec.go new file mode 100644 index 000000000..06082b82d --- /dev/null +++ b/internal/fixturegen/spec.go @@ -0,0 +1,318 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "strings" + "time" +) + +// The fixture is a Go-defined spec that seeds at least one representative row +// per table of the hub schema, deliberately exercising the edge cases that most +// often break a SQLite->Postgres migration: +// +// - NULL optional fields (nullable columns left unset) +// - max-length strings (multi-kilobyte text values) +// - nested / unicode JSON (emoji + multi-byte scripts in JSON columns) +// - soft-deleted rows (deleted_at populated alongside a live row) +// +// IDs are shared across tables (a single project/user/agent/broker/...) so the +// fixture is internally coherent — foreign keys point at rows that exist — +// even though the loader disables FK enforcement while inserting in spec order. + +// Shared, stable identifiers referenced across multiple tables. +const ( + projectID = "11111111-1111-1111-1111-111111111111" + userID = "22222222-2222-2222-2222-222222222222" + agentID = "33333333-3333-3333-3333-333333333333" + brokerID = "44444444-4444-4444-4444-444444444444" + groupID = "55555555-5555-5555-5555-555555555555" + policyID = "66666666-6666-6666-6666-666666666666" + subID = "77777777-7777-7777-7777-777777777777" +) + +// baseTime is a fixed timestamp so the generated fixture is byte-reproducible +// across runs (no time.Now()). +var baseTime = time.Date(2026, time.January, 1, 12, 0, 0, 0, time.UTC) + +// maxLenString is a deliberately long value used to exercise large TEXT +// handling and column-length assumptions. +var maxLenString = strings.Repeat("x", 8192) + +// unicodeJSON is a nested JSON document mixing emoji and multi-byte scripts. +const unicodeJSON = `{"team":"🚀 platform-éñ","nested":{"langs":["日本語","العربية","emoji 😀"],"depth":{"level":2,"ok":true}}}` + +// nestedConfigJSON is a representative nested config blob. +const nestedConfigJSON = `{"harness":"claude","env":{"LOG_LEVEL":"debug","UNICODE":"naïve café 北京"},"args":["--flag","value"]}` + +// TableFixture is the seed data for a single table. +type TableFixture struct { + Table string + Rows []row +} + +// row is a column->value map. Nil values become SQL NULL; bool becomes 0/1; +// time.Time and []byte are passed through to the driver. +type row map[string]any + +// Spec returns the ordered fixture set for every hub table. Parent rows +// (projects, users, ...) are listed before the rows that reference them. +func Spec() []TableFixture { + return []TableFixture{ + // ---- Core identities referenced elsewhere ---- + {Table: "projects", Rows: []row{ + { // full row with nested/unicode JSON labels + "id": projectID, "name": "Platform", "slug": "platform", + "git_remote": "https://github.com/example/platform.git", + "labels": unicodeJSON, "annotations": `{"note":"primary"}`, + "created_at": baseTime, "updated_at": baseTime, + "owner_id": userID, "visibility": "private", + }, + { // minimal row: nullable optionals (git_remote, labels, owner...) left NULL + "id": "11111111-1111-1111-1111-1111111111aa", "name": "Minimal Project", + "slug": "minimal-project", + }, + }}, + {Table: "users", Rows: []row{ + { + "id": userID, "email": "alice@example.com", "display_name": "Alice", + "role": "admin", "status": "active", + "preferences": `{"theme":"dark"}`, "created_at": baseTime, + }, + { // max-length display_name edge case + NULL avatar_url + "id": "22222222-2222-2222-2222-2222222222aa", + "email": "long@example.com", "display_name": maxLenString, + }, + }}, + {Table: "runtime_brokers", Rows: []row{ + { + "id": brokerID, "name": "broker-1", "slug": "broker-1", "type": "docker", + "status": "online", "created_at": baseTime, "updated_at": baseTime, + "capabilities": `{"webPty":true,"sync":true,"attach":false}`, + }, + }}, + {Table: "agents", Rows: []row{ + { // live agent with nested/unicode JSON + "id": agentID, "agent_id": agentID, "name": "worker", "template": "claude", + "project_id": projectID, "labels": unicodeJSON, + "applied_config": nestedConfigJSON, + "created_at": baseTime, "updated_at": baseTime, + "phase": "running", "visibility": "private", "state_version": 1, + }, + { // soft-deleted agent (deleted_at populated) + "id": "33333333-3333-3333-3333-3333333333aa", "agent_id": "33333333-3333-3333-3333-3333333333aa", + "name": "deleted-worker", "template": "claude", "project_id": projectID, + "created_at": baseTime, "updated_at": baseTime, "deleted_at": baseTime, + "phase": "stopped", "visibility": "private", "state_version": 2, + }, + }}, + + // ---- Permissions ---- + {Table: "groups", Rows: []row{ + { + "id": groupID, "name": "Engineering", "slug": "engineering", + "description": "Eng team", "labels": unicodeJSON, + "created_at": baseTime, "updated_at": baseTime, + "group_type": "explicit", + }, + }}, + {Table: "group_members", Rows: []row{ + {"group_id": groupID, "member_type": "user", "member_id": userID, "role": "owner", "added_at": baseTime}, + {"group_id": groupID, "member_type": "agent", "member_id": agentID, "role": "member", "added_at": baseTime}, + }}, + {Table: "policies", Rows: []row{ + { + "id": policyID, "name": "Allow Read", "description": "read agents", + "scope_type": "hub", "resource_type": "agent", + "actions": `["read","list"]`, "effect": "allow", "priority": 10, + "conditions": unicodeJSON, + "created_at": baseTime, "updated_at": baseTime, + }, + }}, + {Table: "policy_bindings", Rows: []row{ + {"policy_id": policyID, "principal_type": "user", "principal_id": userID}, + {"policy_id": policyID, "principal_type": "group", "principal_id": groupID}, + }}, + + // ---- Config / scoped values ---- + {Table: "env_vars", Rows: []row{ + { + "id": "e0000000-0000-0000-0000-000000000001", "key": "LOG_LEVEL", "value": "debug", + "scope": "project", "scope_id": projectID, "created_at": baseTime, "updated_at": baseTime, + }, + }}, + {Table: "secrets", Rows: []row{ + { // long encrypted_value exercises large TEXT + "id": "5ec00000-0000-0000-0000-000000000001", "key": "API_KEY", + "encrypted_value": maxLenString, "scope": "project", "scope_id": projectID, + "secret_type": "environment", "created_at": baseTime, "updated_at": baseTime, + }, + }}, + {Table: "templates", Rows: []row{ + { + "id": "7e000000-0000-0000-0000-000000000001", "name": "claude", "slug": "claude", + "harness": "claude", "image": "scion/claude:latest", "config": nestedConfigJSON, + "scope": "global", "status": "active", "visibility": "public", + "created_at": baseTime, "updated_at": baseTime, + }, + }}, + {Table: "harness_configs", Rows: []row{ + { + "id": "4a000000-0000-0000-0000-000000000001", "name": "claude-web", "slug": "claude-web", + "harness": "claude", "config": nestedConfigJSON, "scope": "global", + "status": "active", "visibility": "public", "created_at": baseTime, "updated_at": baseTime, + }, + }}, + + // ---- Brokers / project wiring ---- + {Table: "project_contributors", Rows: []row{ + { + "project_id": projectID, "broker_id": brokerID, "broker_name": "broker-1", + "mode": "connected", "status": "online", "last_seen": baseTime, + }, + }}, + {Table: "project_sync_state", Rows: []row{ + { + "project_id": projectID, "broker_id": brokerID, "last_sync_time": baseTime, + "last_commit_sha": "deadbeefcafe", "file_count": 42, "total_bytes": 123456, + }, + }}, + {Table: "broker_secrets", Rows: []row{ + { // BLOB column + "broker_id": brokerID, "secret_key": []byte{0x01, 0x02, 0x03, 0x04, 0xfe, 0xff}, + "algorithm": "hmac-sha256", "created_at": baseTime, "status": "active", + }, + }}, + {Table: "broker_join_tokens", Rows: []row{ + { + "broker_id": brokerID, "token_hash": "abc123hash", "expires_at": baseTime.Add(time.Hour), + "created_at": baseTime, "created_by": userID, + }, + }}, + + // ---- Notifications / messaging ---- + {Table: "notification_subscriptions", Rows: []row{ + { + "id": subID, "scope": "agent", "agent_id": agentID, "subscriber_type": "user", + "subscriber_id": userID, "project_id": projectID, + "trigger_activities": `["COMPLETED","WAITING_FOR_INPUT"]`, + "created_at": baseTime, "created_by": userID, + }, + }}, + {Table: "notifications", Rows: []row{ + { + "id": "0t000000-0000-0000-0000-000000000001", "subscription_id": subID, + "agent_id": agentID, "project_id": projectID, "subscriber_type": "user", + "subscriber_id": userID, "status": "COMPLETED", "message": "agent completed 🎉", + "created_at": baseTime, + }, + }}, + {Table: "subscription_templates", Rows: []row{ + { + "id": "57000000-0000-0000-0000-000000000001", "name": "All Events", + "scope": "project", "trigger_activities": `["COMPLETED","ERROR"]`, + "project_id": projectID, "created_by": userID, + }, + }}, + {Table: "messages", Rows: []row{ + { + "id": "11500000-0000-0000-0000-000000000001", "project_id": projectID, + "sender": "user:alice", "sender_id": userID, "recipient": "agent:worker", + "recipient_id": agentID, "msg": "do the thing — café ☕", "type": "instruction", + "agent_id": agentID, "created_at": baseTime, + }, + }}, + + // ---- Schedules ---- + {Table: "schedules", Rows: []row{ + { + "id": "5c000000-0000-0000-0000-000000000001", "project_id": projectID, + "name": "nightly", "cron_expr": "0 0 * * *", "event_type": "dispatch_agent", + "payload": nestedConfigJSON, "status": "active", "next_run_at": baseTime.Add(24 * time.Hour), + "created_at": baseTime, "updated_at": baseTime, + }, + }}, + {Table: "scheduled_events", Rows: []row{ + { + "id": "5e000000-0000-0000-0000-000000000001", "project_id": projectID, + "event_type": "dispatch_agent", "fire_at": baseTime.Add(time.Hour), + "payload": `{"task":"run"}`, "status": "pending", "created_at": baseTime, + }, + }}, + + // ---- Access control: allow list / invites / tokens ---- + {Table: "allow_list", Rows: []row{ + { + "id": "a1000000-0000-0000-0000-000000000001", "email": "invited@example.com", + "note": "early access", "added_by": userID, "created": baseTime, + }, + }}, + {Table: "invite_codes", Rows: []row{ + { + "id": "1c000000-0000-0000-0000-000000000001", "code_hash": "hash_of_code", + "code_prefix": "scion_in", "max_uses": 5, "use_count": 1, + "expires_at": baseTime.Add(48 * time.Hour), "created_by": userID, "created": baseTime, + }, + }}, + {Table: "user_access_tokens", Rows: []row{ + { + "id": "0a000000-0000-0000-0000-000000000001", "user_id": userID, "name": "ci-token", + "prefix": "scion_pat_ab", "key_hash": "tokenhash", "project_id": projectID, + "scopes": `["agent:read","agent:list"]`, "created_at": baseTime, + }, + }}, + {Table: "api_keys", Rows: []row{ + { // NULL expires_at / last_used optionals + "id": "a9000000-0000-0000-0000-000000000001", "user_id": userID, "name": "legacy-key", + "prefix": "scion_ak", "key_hash": "apikeyhash", "scopes": `["read"]`, "created_at": baseTime, + }, + }}, + + // ---- GCP / GitHub identity ---- + {Table: "gcp_service_accounts", Rows: []row{ + { + "id": "9c000000-0000-0000-0000-000000000001", "scope": "project", "scope_id": projectID, + "email": "agent-worker@example.iam.gserviceaccount.com", "project_id": "gcp-proj-123", + "display_name": "Worker SA", "default_scopes": `["https://www.googleapis.com/auth/cloud-platform"]`, + "created_by": userID, "created_at": baseTime, "managed": true, + }, + }}, + {Table: "github_installations", Rows: []row{ + { + "installation_id": int64(987654), "account_login": "example-org", + "account_type": "Organization", "app_id": int64(112233), + "repositories": `["example/platform","example/infra"]`, "status": "active", + "created_at": baseTime, "updated_at": baseTime, + }, + }}, + + // ---- Maintenance ---- + {Table: "maintenance_operations", Rows: []row{ + { + "id": "0d000000-0000-0000-0000-000000000001", "key": "purge_deleted_agents", + "title": "Purge Deleted Agents", "description": "remove soft-deleted agents", + "category": "cleanup", "status": "pending", "created_at": baseTime, + "metadata": `{"batchSize":100}`, + }, + }}, + {Table: "maintenance_operation_runs", Rows: []row{ + { + "id": "07000000-0000-0000-0000-000000000001", "operation_key": "purge_deleted_agents", + "status": "completed", "started_at": baseTime, "completed_at": baseTime.Add(time.Minute), + "started_by": userID, "result": `{"purged":3}`, "log": "done", + }, + }}, + } +} diff --git a/pkg/config/hub_config.go b/pkg/config/hub_config.go index 2d284bacb..3b880437e 100644 --- a/pkg/config/hub_config.go +++ b/pkg/config/hub_config.go @@ -137,6 +137,50 @@ type RuntimeBrokerConfig struct { type DatabaseConfig struct { Driver string `json:"driver" yaml:"driver" koanf:"driver"` // sqlite, postgres URL string `json:"url" yaml:"url" koanf:"url"` // Connection URL/path + + // Connection pool settings (applied to the underlying *sql.DB). + // MaxOpenConns is the maximum number of open connections to the database. + // For sqlite this MUST be 1 to serialize writes (load-bearing). + MaxOpenConns int `json:"max_open_conns" yaml:"max_open_conns" koanf:"max_open_conns"` + // MaxIdleConns is the maximum number of idle connections in the pool. + MaxIdleConns int `json:"max_idle_conns" yaml:"max_idle_conns" koanf:"max_idle_conns"` + // ConnMaxLifetime is the maximum amount of time a connection may be + // reused, parsed as a Go duration string (e.g. "30m"). Empty means unlimited. + ConnMaxLifetime string `json:"conn_max_lifetime" yaml:"conn_max_lifetime" koanf:"conn_max_lifetime"` + // ConnMaxIdleTime is the maximum amount of time a connection may sit idle + // in the pool before being closed, parsed as a Go duration string (e.g. + // "5m"). This must be shorter than the server-side / proxy idle timeout + // (CloudSQL drops idle connections after ~10m) so the pool recycles a + // connection before the remote silently closes it — otherwise the first + // request after an idle period stalls waiting for a dead connection to time + // out. Empty means no idle limit. + ConnMaxIdleTime string `json:"conn_max_idle_time" yaml:"conn_max_idle_time" koanf:"conn_max_idle_time"` +} + +// ConnMaxLifetimeDuration parses ConnMaxLifetime into a time.Duration. +// An empty value yields 0 (unlimited). A malformed value returns an error. +func (d DatabaseConfig) ConnMaxLifetimeDuration() (time.Duration, error) { + if d.ConnMaxLifetime == "" { + return 0, nil + } + dur, err := time.ParseDuration(d.ConnMaxLifetime) + if err != nil { + return 0, fmt.Errorf("invalid conn_max_lifetime %q: %w", d.ConnMaxLifetime, err) + } + return dur, nil +} + +// ConnMaxIdleTimeDuration parses ConnMaxIdleTime into a time.Duration. +// An empty value yields 0 (no idle limit). A malformed value returns an error. +func (d DatabaseConfig) ConnMaxIdleTimeDuration() (time.Duration, error) { + if d.ConnMaxIdleTime == "" { + return 0, nil + } + dur, err := time.ParseDuration(d.ConnMaxIdleTime) + if err != nil { + return 0, fmt.Errorf("invalid conn_max_idle_time %q: %w", d.ConnMaxIdleTime, err) + } + return dur, nil } // DevAuthConfig holds development authentication settings. @@ -291,6 +335,13 @@ func DefaultGlobalConfig() GlobalConfig { Database: DatabaseConfig{ Driver: "sqlite", URL: "", // Will be set to default path if empty + // SQLite pool defaults. MaxOpenConns MUST stay 1 to serialize + // writes; postgres pool defaults are applied in + // applyDatabasePoolDefaults when Driver == "postgres". + MaxOpenConns: 1, + MaxIdleConns: 1, + ConnMaxLifetime: "0", + ConnMaxIdleTime: "0", }, Auth: DevAuthConfig{ Enabled: false, @@ -308,6 +359,59 @@ func DefaultGlobalConfig() GlobalConfig { } } +// applyDatabasePoolDefaults fills in driver-appropriate connection pool +// defaults for any pool field left unset. It is applied after config loading +// so that postgres deployments get sensible pool sizing without requiring +// every config file to specify it. +// +// For sqlite, MaxOpenConns is forced to 1: more than one open connection +// breaks write serialization and causes "database is locked" errors. +func applyDatabasePoolDefaults(db *DatabaseConfig) { + switch db.Driver { + case "postgres": + // NOTE: the struct-level default for these fields is 1 (the value SQLite + // REQUIRES to serialize writes — see DefaultGlobalConfig). For a postgres + // deployment configured purely via env/driver override, that 1 leaks + // through unchanged, and a plain `<= 0` guard would leave the pool at a + // single connection. A pool of 1 is pathological for postgres: a + // singleton scheduler handler that checks out the lone connection to hold + // an advisory lock then self-deadlocks waiting for a second connection to + // do its work, and every API request serializes behind it (~55s context + // deadlines). Treat the leaked SQLite default (<= 1) as "unset" so + // postgres always gets a real pool. An operator who genuinely wants a + // tiny pool can still request 2+. + if db.MaxOpenConns <= 1 { + // Conservative per-replica default so several replicas fit within a + // modest Postgres connection budget. The connection ceiling for N + // replicas is roughly N × (MaxOpenConns + event pool + 1 listener + + // brokers); see CONNECTION-BUDGET.md. Raise this only when the + // instance's max_connections (and any pooler) has headroom. + db.MaxOpenConns = 10 + } + if db.MaxIdleConns <= 1 { + db.MaxIdleConns = 5 + } + if db.ConnMaxLifetime == "" { + db.ConnMaxLifetime = "30m" + } + if db.ConnMaxIdleTime == "" { + // Shorter than CloudSQL's ~10m idle timeout so the pool recycles a + // connection before the remote silently drops it. + db.ConnMaxIdleTime = "5m" + } + case "sqlite": + // Load-bearing: SQLite must use a single open connection. + db.MaxOpenConns = 1 + if db.MaxIdleConns <= 0 { + db.MaxIdleConns = 1 + } + // No idle recycling for the single local SQLite connection. + if db.ConnMaxIdleTime == "" { + db.ConnMaxIdleTime = "0" + } + } +} + // LoadGlobalConfig loads global configuration using Koanf with priority: // 1. Embedded defaults // 2. Global config: settings.yaml (server key) OR server.yaml (~/.scion/) @@ -381,6 +485,7 @@ func loadGlobalConfigFromSettings(configPath string) (*GlobalConfig, bool) { if gc.Database.URL == "" && gc.Database.Driver == "sqlite" { gc.Database.URL = filepath.Join(globalDir, "hub.db") } + applyDatabasePoolDefaults(&gc.Database) return gc, true } @@ -511,6 +616,7 @@ func loadGlobalConfigLegacy(configPath string) (*GlobalConfig, error) { config.Database.URL = "hub.db" } } + applyDatabasePoolDefaults(&config.Database) // Fixup for list fields that might be loaded as a single comma-separated string from env vars. // This happens because koanf's env provider doesn't automatically split strings for slice fields. diff --git a/pkg/config/hub_config_test.go b/pkg/config/hub_config_test.go index 11d97176e..ed7f8edb0 100644 --- a/pkg/config/hub_config_test.go +++ b/pkg/config/hub_config_test.go @@ -699,3 +699,48 @@ hub: t.Fatal("expected not to find server config in settings.yaml") } } + +// TestApplyDatabasePoolDefaults_PostgresOverridesLeakedSqliteDefault is a +// regression test for the production incident where both hubs served every API +// request in ~55s. The struct-level default for MaxOpenConns/MaxIdleConns is 1 +// (required by SQLite to serialize writes). A postgres deployment configured via +// env/driver override inherits that 1, and the original `<= 0` guard left the +// pool at a single connection. With a pool of 1, a singleton scheduler handler +// that holds the lone connection for an advisory lock self-deadlocks waiting for +// a second connection to do its work, and all traffic serializes behind it. +func TestApplyDatabasePoolDefaults_PostgresOverridesLeakedSqliteDefault(t *testing.T) { + // Mirrors the production path: start from the embedded defaults (which set + // MaxOpenConns=1 for the SQLite default) and switch the driver to postgres. + db := DefaultGlobalConfig().Database + db.Driver = "postgres" + db.URL = "host=db port=5432 dbname=scion sslmode=require" + + applyDatabasePoolDefaults(&db) + + if db.MaxOpenConns < 2 { + t.Fatalf("postgres MaxOpenConns must be a real pool, got %d (leaked SQLite default of 1 not overridden)", db.MaxOpenConns) + } + if db.MaxIdleConns < 2 { + t.Fatalf("postgres MaxIdleConns must be > 1, got %d", db.MaxIdleConns) + } +} + +// TestApplyDatabasePoolDefaults_PostgresRespectsExplicitPool ensures an operator +// who explicitly sizes the pool (>= 2) is not clobbered by the default. +func TestApplyDatabasePoolDefaults_PostgresRespectsExplicitPool(t *testing.T) { + db := DatabaseConfig{Driver: "postgres", MaxOpenConns: 25, MaxIdleConns: 12} + applyDatabasePoolDefaults(&db) + if db.MaxOpenConns != 25 || db.MaxIdleConns != 12 { + t.Fatalf("explicit pool sizing clobbered: open=%d idle=%d", db.MaxOpenConns, db.MaxIdleConns) + } +} + +// TestApplyDatabasePoolDefaults_SqliteStaysSingleConnection guards the +// load-bearing invariant that SQLite always serializes through one connection. +func TestApplyDatabasePoolDefaults_SqliteStaysSingleConnection(t *testing.T) { + db := DefaultGlobalConfig().Database // Driver defaults to sqlite + applyDatabasePoolDefaults(&db) + if db.MaxOpenConns != 1 { + t.Fatalf("sqlite MaxOpenConns must be 1, got %d", db.MaxOpenConns) + } +} diff --git a/pkg/config/settings_v1.go b/pkg/config/settings_v1.go index 7246478dc..decb821e0 100644 --- a/pkg/config/settings_v1.go +++ b/pkg/config/settings_v1.go @@ -375,8 +375,12 @@ type V1BrokerConfig struct { // V1DatabaseConfig holds database settings. type V1DatabaseConfig struct { - Driver string `json:"driver,omitempty" yaml:"driver,omitempty" koanf:"driver"` - URL string `json:"url,omitempty" yaml:"url,omitempty" koanf:"url"` + Driver string `json:"driver,omitempty" yaml:"driver,omitempty" koanf:"driver"` + URL string `json:"url,omitempty" yaml:"url,omitempty" koanf:"url"` + MaxOpenConns int `json:"max_open_conns,omitempty" yaml:"max_open_conns,omitempty" koanf:"max_open_conns"` + MaxIdleConns int `json:"max_idle_conns,omitempty" yaml:"max_idle_conns,omitempty" koanf:"max_idle_conns"` + ConnMaxLifetime string `json:"conn_max_lifetime,omitempty" yaml:"conn_max_lifetime,omitempty" koanf:"conn_max_lifetime"` + ConnMaxIdleTime string `json:"conn_max_idle_time,omitempty" yaml:"conn_max_idle_time,omitempty" koanf:"conn_max_idle_time"` } // V1AuthConfig holds development authentication settings. @@ -1140,6 +1144,18 @@ func ConvertV1ServerToGlobalConfig(v1 *V1ServerConfig) *GlobalConfig { if v1.Database.URL != "" { gc.Database.URL = v1.Database.URL } + if v1.Database.MaxOpenConns != 0 { + gc.Database.MaxOpenConns = v1.Database.MaxOpenConns + } + if v1.Database.MaxIdleConns != 0 { + gc.Database.MaxIdleConns = v1.Database.MaxIdleConns + } + if v1.Database.ConnMaxLifetime != "" { + gc.Database.ConnMaxLifetime = v1.Database.ConnMaxLifetime + } + if v1.Database.ConnMaxIdleTime != "" { + gc.Database.ConnMaxIdleTime = v1.Database.ConnMaxIdleTime + } } // Auth config @@ -1291,8 +1307,12 @@ func ConvertGlobalToV1ServerConfig(gc *GlobalConfig) *V1ServerConfig { // Database config v1.Database = &V1DatabaseConfig{ - Driver: gc.Database.Driver, - URL: gc.Database.URL, + Driver: gc.Database.Driver, + URL: gc.Database.URL, + MaxOpenConns: gc.Database.MaxOpenConns, + MaxIdleConns: gc.Database.MaxIdleConns, + ConnMaxLifetime: gc.Database.ConnMaxLifetime, + ConnMaxIdleTime: gc.Database.ConnMaxIdleTime, } // Auth config diff --git a/pkg/ent/accesspolicy_create.go b/pkg/ent/accesspolicy_create.go index 700772a8a..e5959e2a9 100644 --- a/pkg/ent/accesspolicy_create.go +++ b/pkg/ent/accesspolicy_create.go @@ -8,6 +8,8 @@ import ( "fmt" "time" + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" "entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/schema/field" "github.com/GoogleCloudPlatform/scion/pkg/ent/accesspolicy" @@ -21,6 +23,7 @@ type AccessPolicyCreate struct { config mutation *AccessPolicyMutation hooks []Hook + conflict []sql.ConflictOption } // SetName sets the "name" field. @@ -325,6 +328,7 @@ func (_c *AccessPolicyCreate) createSpec() (*AccessPolicy, *sqlgraph.CreateSpec) _node = &AccessPolicy{config: _c.config} _spec = sqlgraph.NewCreateSpec(accesspolicy.Table, sqlgraph.NewFieldSpec(accesspolicy.FieldID, field.TypeUUID)) ) + _spec.OnConflict = _c.conflict if id, ok := _c.mutation.ID(); ok { _node.ID = id _spec.ID.Value = &id @@ -408,11 +412,631 @@ func (_c *AccessPolicyCreate) createSpec() (*AccessPolicy, *sqlgraph.CreateSpec) return _node, _spec } +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.AccessPolicy.Create(). +// SetName(v). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.AccessPolicyUpsert) { +// SetName(v+v). +// }). +// Exec(ctx) +func (_c *AccessPolicyCreate) OnConflict(opts ...sql.ConflictOption) *AccessPolicyUpsertOne { + _c.conflict = opts + return &AccessPolicyUpsertOne{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.AccessPolicy.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *AccessPolicyCreate) OnConflictColumns(columns ...string) *AccessPolicyUpsertOne { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &AccessPolicyUpsertOne{ + create: _c, + } +} + +type ( + // AccessPolicyUpsertOne is the builder for "upsert"-ing + // one AccessPolicy node. + AccessPolicyUpsertOne struct { + create *AccessPolicyCreate + } + + // AccessPolicyUpsert is the "OnConflict" setter. + AccessPolicyUpsert struct { + *sql.UpdateSet + } +) + +// SetName sets the "name" field. +func (u *AccessPolicyUpsert) SetName(v string) *AccessPolicyUpsert { + u.Set(accesspolicy.FieldName, v) + return u +} + +// UpdateName sets the "name" field to the value that was provided on create. +func (u *AccessPolicyUpsert) UpdateName() *AccessPolicyUpsert { + u.SetExcluded(accesspolicy.FieldName) + return u +} + +// SetDescription sets the "description" field. +func (u *AccessPolicyUpsert) SetDescription(v string) *AccessPolicyUpsert { + u.Set(accesspolicy.FieldDescription, v) + return u +} + +// UpdateDescription sets the "description" field to the value that was provided on create. +func (u *AccessPolicyUpsert) UpdateDescription() *AccessPolicyUpsert { + u.SetExcluded(accesspolicy.FieldDescription) + return u +} + +// ClearDescription clears the value of the "description" field. +func (u *AccessPolicyUpsert) ClearDescription() *AccessPolicyUpsert { + u.SetNull(accesspolicy.FieldDescription) + return u +} + +// SetScopeType sets the "scope_type" field. +func (u *AccessPolicyUpsert) SetScopeType(v accesspolicy.ScopeType) *AccessPolicyUpsert { + u.Set(accesspolicy.FieldScopeType, v) + return u +} + +// UpdateScopeType sets the "scope_type" field to the value that was provided on create. +func (u *AccessPolicyUpsert) UpdateScopeType() *AccessPolicyUpsert { + u.SetExcluded(accesspolicy.FieldScopeType) + return u +} + +// SetScopeID sets the "scope_id" field. +func (u *AccessPolicyUpsert) SetScopeID(v string) *AccessPolicyUpsert { + u.Set(accesspolicy.FieldScopeID, v) + return u +} + +// UpdateScopeID sets the "scope_id" field to the value that was provided on create. +func (u *AccessPolicyUpsert) UpdateScopeID() *AccessPolicyUpsert { + u.SetExcluded(accesspolicy.FieldScopeID) + return u +} + +// ClearScopeID clears the value of the "scope_id" field. +func (u *AccessPolicyUpsert) ClearScopeID() *AccessPolicyUpsert { + u.SetNull(accesspolicy.FieldScopeID) + return u +} + +// SetResourceType sets the "resource_type" field. +func (u *AccessPolicyUpsert) SetResourceType(v string) *AccessPolicyUpsert { + u.Set(accesspolicy.FieldResourceType, v) + return u +} + +// UpdateResourceType sets the "resource_type" field to the value that was provided on create. +func (u *AccessPolicyUpsert) UpdateResourceType() *AccessPolicyUpsert { + u.SetExcluded(accesspolicy.FieldResourceType) + return u +} + +// SetResourceID sets the "resource_id" field. +func (u *AccessPolicyUpsert) SetResourceID(v string) *AccessPolicyUpsert { + u.Set(accesspolicy.FieldResourceID, v) + return u +} + +// UpdateResourceID sets the "resource_id" field to the value that was provided on create. +func (u *AccessPolicyUpsert) UpdateResourceID() *AccessPolicyUpsert { + u.SetExcluded(accesspolicy.FieldResourceID) + return u +} + +// ClearResourceID clears the value of the "resource_id" field. +func (u *AccessPolicyUpsert) ClearResourceID() *AccessPolicyUpsert { + u.SetNull(accesspolicy.FieldResourceID) + return u +} + +// SetActions sets the "actions" field. +func (u *AccessPolicyUpsert) SetActions(v []string) *AccessPolicyUpsert { + u.Set(accesspolicy.FieldActions, v) + return u +} + +// UpdateActions sets the "actions" field to the value that was provided on create. +func (u *AccessPolicyUpsert) UpdateActions() *AccessPolicyUpsert { + u.SetExcluded(accesspolicy.FieldActions) + return u +} + +// ClearActions clears the value of the "actions" field. +func (u *AccessPolicyUpsert) ClearActions() *AccessPolicyUpsert { + u.SetNull(accesspolicy.FieldActions) + return u +} + +// SetEffect sets the "effect" field. +func (u *AccessPolicyUpsert) SetEffect(v accesspolicy.Effect) *AccessPolicyUpsert { + u.Set(accesspolicy.FieldEffect, v) + return u +} + +// UpdateEffect sets the "effect" field to the value that was provided on create. +func (u *AccessPolicyUpsert) UpdateEffect() *AccessPolicyUpsert { + u.SetExcluded(accesspolicy.FieldEffect) + return u +} + +// SetConditions sets the "conditions" field. +func (u *AccessPolicyUpsert) SetConditions(v *schema.PolicyConditions) *AccessPolicyUpsert { + u.Set(accesspolicy.FieldConditions, v) + return u +} + +// UpdateConditions sets the "conditions" field to the value that was provided on create. +func (u *AccessPolicyUpsert) UpdateConditions() *AccessPolicyUpsert { + u.SetExcluded(accesspolicy.FieldConditions) + return u +} + +// ClearConditions clears the value of the "conditions" field. +func (u *AccessPolicyUpsert) ClearConditions() *AccessPolicyUpsert { + u.SetNull(accesspolicy.FieldConditions) + return u +} + +// SetPriority sets the "priority" field. +func (u *AccessPolicyUpsert) SetPriority(v int) *AccessPolicyUpsert { + u.Set(accesspolicy.FieldPriority, v) + return u +} + +// UpdatePriority sets the "priority" field to the value that was provided on create. +func (u *AccessPolicyUpsert) UpdatePriority() *AccessPolicyUpsert { + u.SetExcluded(accesspolicy.FieldPriority) + return u +} + +// AddPriority adds v to the "priority" field. +func (u *AccessPolicyUpsert) AddPriority(v int) *AccessPolicyUpsert { + u.Add(accesspolicy.FieldPriority, v) + return u +} + +// SetLabels sets the "labels" field. +func (u *AccessPolicyUpsert) SetLabels(v map[string]string) *AccessPolicyUpsert { + u.Set(accesspolicy.FieldLabels, v) + return u +} + +// UpdateLabels sets the "labels" field to the value that was provided on create. +func (u *AccessPolicyUpsert) UpdateLabels() *AccessPolicyUpsert { + u.SetExcluded(accesspolicy.FieldLabels) + return u +} + +// ClearLabels clears the value of the "labels" field. +func (u *AccessPolicyUpsert) ClearLabels() *AccessPolicyUpsert { + u.SetNull(accesspolicy.FieldLabels) + return u +} + +// SetAnnotations sets the "annotations" field. +func (u *AccessPolicyUpsert) SetAnnotations(v map[string]string) *AccessPolicyUpsert { + u.Set(accesspolicy.FieldAnnotations, v) + return u +} + +// UpdateAnnotations sets the "annotations" field to the value that was provided on create. +func (u *AccessPolicyUpsert) UpdateAnnotations() *AccessPolicyUpsert { + u.SetExcluded(accesspolicy.FieldAnnotations) + return u +} + +// ClearAnnotations clears the value of the "annotations" field. +func (u *AccessPolicyUpsert) ClearAnnotations() *AccessPolicyUpsert { + u.SetNull(accesspolicy.FieldAnnotations) + return u +} + +// SetUpdated sets the "updated" field. +func (u *AccessPolicyUpsert) SetUpdated(v time.Time) *AccessPolicyUpsert { + u.Set(accesspolicy.FieldUpdated, v) + return u +} + +// UpdateUpdated sets the "updated" field to the value that was provided on create. +func (u *AccessPolicyUpsert) UpdateUpdated() *AccessPolicyUpsert { + u.SetExcluded(accesspolicy.FieldUpdated) + return u +} + +// SetCreatedBy sets the "created_by" field. +func (u *AccessPolicyUpsert) SetCreatedBy(v string) *AccessPolicyUpsert { + u.Set(accesspolicy.FieldCreatedBy, v) + return u +} + +// UpdateCreatedBy sets the "created_by" field to the value that was provided on create. +func (u *AccessPolicyUpsert) UpdateCreatedBy() *AccessPolicyUpsert { + u.SetExcluded(accesspolicy.FieldCreatedBy) + return u +} + +// ClearCreatedBy clears the value of the "created_by" field. +func (u *AccessPolicyUpsert) ClearCreatedBy() *AccessPolicyUpsert { + u.SetNull(accesspolicy.FieldCreatedBy) + return u +} + +// UpdateNewValues updates the mutable fields using the new values that were set on create except the ID field. +// Using this option is equivalent to using: +// +// client.AccessPolicy.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(accesspolicy.FieldID) +// }), +// ). +// Exec(ctx) +func (u *AccessPolicyUpsertOne) UpdateNewValues() *AccessPolicyUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + if _, exists := u.create.mutation.ID(); exists { + s.SetIgnore(accesspolicy.FieldID) + } + if _, exists := u.create.mutation.Created(); exists { + s.SetIgnore(accesspolicy.FieldCreated) + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.AccessPolicy.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *AccessPolicyUpsertOne) Ignore() *AccessPolicyUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *AccessPolicyUpsertOne) DoNothing() *AccessPolicyUpsertOne { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the AccessPolicyCreate.OnConflict +// documentation for more info. +func (u *AccessPolicyUpsertOne) Update(set func(*AccessPolicyUpsert)) *AccessPolicyUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&AccessPolicyUpsert{UpdateSet: update}) + })) + return u +} + +// SetName sets the "name" field. +func (u *AccessPolicyUpsertOne) SetName(v string) *AccessPolicyUpsertOne { + return u.Update(func(s *AccessPolicyUpsert) { + s.SetName(v) + }) +} + +// UpdateName sets the "name" field to the value that was provided on create. +func (u *AccessPolicyUpsertOne) UpdateName() *AccessPolicyUpsertOne { + return u.Update(func(s *AccessPolicyUpsert) { + s.UpdateName() + }) +} + +// SetDescription sets the "description" field. +func (u *AccessPolicyUpsertOne) SetDescription(v string) *AccessPolicyUpsertOne { + return u.Update(func(s *AccessPolicyUpsert) { + s.SetDescription(v) + }) +} + +// UpdateDescription sets the "description" field to the value that was provided on create. +func (u *AccessPolicyUpsertOne) UpdateDescription() *AccessPolicyUpsertOne { + return u.Update(func(s *AccessPolicyUpsert) { + s.UpdateDescription() + }) +} + +// ClearDescription clears the value of the "description" field. +func (u *AccessPolicyUpsertOne) ClearDescription() *AccessPolicyUpsertOne { + return u.Update(func(s *AccessPolicyUpsert) { + s.ClearDescription() + }) +} + +// SetScopeType sets the "scope_type" field. +func (u *AccessPolicyUpsertOne) SetScopeType(v accesspolicy.ScopeType) *AccessPolicyUpsertOne { + return u.Update(func(s *AccessPolicyUpsert) { + s.SetScopeType(v) + }) +} + +// UpdateScopeType sets the "scope_type" field to the value that was provided on create. +func (u *AccessPolicyUpsertOne) UpdateScopeType() *AccessPolicyUpsertOne { + return u.Update(func(s *AccessPolicyUpsert) { + s.UpdateScopeType() + }) +} + +// SetScopeID sets the "scope_id" field. +func (u *AccessPolicyUpsertOne) SetScopeID(v string) *AccessPolicyUpsertOne { + return u.Update(func(s *AccessPolicyUpsert) { + s.SetScopeID(v) + }) +} + +// UpdateScopeID sets the "scope_id" field to the value that was provided on create. +func (u *AccessPolicyUpsertOne) UpdateScopeID() *AccessPolicyUpsertOne { + return u.Update(func(s *AccessPolicyUpsert) { + s.UpdateScopeID() + }) +} + +// ClearScopeID clears the value of the "scope_id" field. +func (u *AccessPolicyUpsertOne) ClearScopeID() *AccessPolicyUpsertOne { + return u.Update(func(s *AccessPolicyUpsert) { + s.ClearScopeID() + }) +} + +// SetResourceType sets the "resource_type" field. +func (u *AccessPolicyUpsertOne) SetResourceType(v string) *AccessPolicyUpsertOne { + return u.Update(func(s *AccessPolicyUpsert) { + s.SetResourceType(v) + }) +} + +// UpdateResourceType sets the "resource_type" field to the value that was provided on create. +func (u *AccessPolicyUpsertOne) UpdateResourceType() *AccessPolicyUpsertOne { + return u.Update(func(s *AccessPolicyUpsert) { + s.UpdateResourceType() + }) +} + +// SetResourceID sets the "resource_id" field. +func (u *AccessPolicyUpsertOne) SetResourceID(v string) *AccessPolicyUpsertOne { + return u.Update(func(s *AccessPolicyUpsert) { + s.SetResourceID(v) + }) +} + +// UpdateResourceID sets the "resource_id" field to the value that was provided on create. +func (u *AccessPolicyUpsertOne) UpdateResourceID() *AccessPolicyUpsertOne { + return u.Update(func(s *AccessPolicyUpsert) { + s.UpdateResourceID() + }) +} + +// ClearResourceID clears the value of the "resource_id" field. +func (u *AccessPolicyUpsertOne) ClearResourceID() *AccessPolicyUpsertOne { + return u.Update(func(s *AccessPolicyUpsert) { + s.ClearResourceID() + }) +} + +// SetActions sets the "actions" field. +func (u *AccessPolicyUpsertOne) SetActions(v []string) *AccessPolicyUpsertOne { + return u.Update(func(s *AccessPolicyUpsert) { + s.SetActions(v) + }) +} + +// UpdateActions sets the "actions" field to the value that was provided on create. +func (u *AccessPolicyUpsertOne) UpdateActions() *AccessPolicyUpsertOne { + return u.Update(func(s *AccessPolicyUpsert) { + s.UpdateActions() + }) +} + +// ClearActions clears the value of the "actions" field. +func (u *AccessPolicyUpsertOne) ClearActions() *AccessPolicyUpsertOne { + return u.Update(func(s *AccessPolicyUpsert) { + s.ClearActions() + }) +} + +// SetEffect sets the "effect" field. +func (u *AccessPolicyUpsertOne) SetEffect(v accesspolicy.Effect) *AccessPolicyUpsertOne { + return u.Update(func(s *AccessPolicyUpsert) { + s.SetEffect(v) + }) +} + +// UpdateEffect sets the "effect" field to the value that was provided on create. +func (u *AccessPolicyUpsertOne) UpdateEffect() *AccessPolicyUpsertOne { + return u.Update(func(s *AccessPolicyUpsert) { + s.UpdateEffect() + }) +} + +// SetConditions sets the "conditions" field. +func (u *AccessPolicyUpsertOne) SetConditions(v *schema.PolicyConditions) *AccessPolicyUpsertOne { + return u.Update(func(s *AccessPolicyUpsert) { + s.SetConditions(v) + }) +} + +// UpdateConditions sets the "conditions" field to the value that was provided on create. +func (u *AccessPolicyUpsertOne) UpdateConditions() *AccessPolicyUpsertOne { + return u.Update(func(s *AccessPolicyUpsert) { + s.UpdateConditions() + }) +} + +// ClearConditions clears the value of the "conditions" field. +func (u *AccessPolicyUpsertOne) ClearConditions() *AccessPolicyUpsertOne { + return u.Update(func(s *AccessPolicyUpsert) { + s.ClearConditions() + }) +} + +// SetPriority sets the "priority" field. +func (u *AccessPolicyUpsertOne) SetPriority(v int) *AccessPolicyUpsertOne { + return u.Update(func(s *AccessPolicyUpsert) { + s.SetPriority(v) + }) +} + +// AddPriority adds v to the "priority" field. +func (u *AccessPolicyUpsertOne) AddPriority(v int) *AccessPolicyUpsertOne { + return u.Update(func(s *AccessPolicyUpsert) { + s.AddPriority(v) + }) +} + +// UpdatePriority sets the "priority" field to the value that was provided on create. +func (u *AccessPolicyUpsertOne) UpdatePriority() *AccessPolicyUpsertOne { + return u.Update(func(s *AccessPolicyUpsert) { + s.UpdatePriority() + }) +} + +// SetLabels sets the "labels" field. +func (u *AccessPolicyUpsertOne) SetLabels(v map[string]string) *AccessPolicyUpsertOne { + return u.Update(func(s *AccessPolicyUpsert) { + s.SetLabels(v) + }) +} + +// UpdateLabels sets the "labels" field to the value that was provided on create. +func (u *AccessPolicyUpsertOne) UpdateLabels() *AccessPolicyUpsertOne { + return u.Update(func(s *AccessPolicyUpsert) { + s.UpdateLabels() + }) +} + +// ClearLabels clears the value of the "labels" field. +func (u *AccessPolicyUpsertOne) ClearLabels() *AccessPolicyUpsertOne { + return u.Update(func(s *AccessPolicyUpsert) { + s.ClearLabels() + }) +} + +// SetAnnotations sets the "annotations" field. +func (u *AccessPolicyUpsertOne) SetAnnotations(v map[string]string) *AccessPolicyUpsertOne { + return u.Update(func(s *AccessPolicyUpsert) { + s.SetAnnotations(v) + }) +} + +// UpdateAnnotations sets the "annotations" field to the value that was provided on create. +func (u *AccessPolicyUpsertOne) UpdateAnnotations() *AccessPolicyUpsertOne { + return u.Update(func(s *AccessPolicyUpsert) { + s.UpdateAnnotations() + }) +} + +// ClearAnnotations clears the value of the "annotations" field. +func (u *AccessPolicyUpsertOne) ClearAnnotations() *AccessPolicyUpsertOne { + return u.Update(func(s *AccessPolicyUpsert) { + s.ClearAnnotations() + }) +} + +// SetUpdated sets the "updated" field. +func (u *AccessPolicyUpsertOne) SetUpdated(v time.Time) *AccessPolicyUpsertOne { + return u.Update(func(s *AccessPolicyUpsert) { + s.SetUpdated(v) + }) +} + +// UpdateUpdated sets the "updated" field to the value that was provided on create. +func (u *AccessPolicyUpsertOne) UpdateUpdated() *AccessPolicyUpsertOne { + return u.Update(func(s *AccessPolicyUpsert) { + s.UpdateUpdated() + }) +} + +// SetCreatedBy sets the "created_by" field. +func (u *AccessPolicyUpsertOne) SetCreatedBy(v string) *AccessPolicyUpsertOne { + return u.Update(func(s *AccessPolicyUpsert) { + s.SetCreatedBy(v) + }) +} + +// UpdateCreatedBy sets the "created_by" field to the value that was provided on create. +func (u *AccessPolicyUpsertOne) UpdateCreatedBy() *AccessPolicyUpsertOne { + return u.Update(func(s *AccessPolicyUpsert) { + s.UpdateCreatedBy() + }) +} + +// ClearCreatedBy clears the value of the "created_by" field. +func (u *AccessPolicyUpsertOne) ClearCreatedBy() *AccessPolicyUpsertOne { + return u.Update(func(s *AccessPolicyUpsert) { + s.ClearCreatedBy() + }) +} + +// Exec executes the query. +func (u *AccessPolicyUpsertOne) Exec(ctx context.Context) error { + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for AccessPolicyCreate.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *AccessPolicyUpsertOne) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} + +// Exec executes the UPSERT query and returns the inserted/updated ID. +func (u *AccessPolicyUpsertOne) ID(ctx context.Context) (id uuid.UUID, err error) { + if u.create.driver.Dialect() == dialect.MySQL { + // In case of "ON CONFLICT", there is no way to get back non-numeric ID + // fields from the database since MySQL does not support the RETURNING clause. + return id, errors.New("ent: AccessPolicyUpsertOne.ID is not supported by MySQL driver. Use AccessPolicyUpsertOne.Exec instead") + } + node, err := u.create.Save(ctx) + if err != nil { + return id, err + } + return node.ID, nil +} + +// IDX is like ID, but panics if an error occurs. +func (u *AccessPolicyUpsertOne) IDX(ctx context.Context) uuid.UUID { + id, err := u.ID(ctx) + if err != nil { + panic(err) + } + return id +} + // AccessPolicyCreateBulk is the builder for creating many AccessPolicy entities in bulk. type AccessPolicyCreateBulk struct { config err error builders []*AccessPolicyCreate + conflict []sql.ConflictOption } // Save creates the AccessPolicy entities in the database. @@ -442,6 +1066,7 @@ func (_c *AccessPolicyCreateBulk) Save(ctx context.Context) ([]*AccessPolicy, er _, err = mutators[i+1].Mutate(root, _c.builders[i+1].mutation) } else { spec := &sqlgraph.BatchCreateSpec{Nodes: specs} + spec.OnConflict = _c.conflict // Invoke the actual operation on the latest mutation in the chain. if err = sqlgraph.BatchCreate(ctx, _c.driver, spec); err != nil { if sqlgraph.IsConstraintError(err) { @@ -491,3 +1116,379 @@ func (_c *AccessPolicyCreateBulk) ExecX(ctx context.Context) { panic(err) } } + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.AccessPolicy.CreateBulk(builders...). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.AccessPolicyUpsert) { +// SetName(v+v). +// }). +// Exec(ctx) +func (_c *AccessPolicyCreateBulk) OnConflict(opts ...sql.ConflictOption) *AccessPolicyUpsertBulk { + _c.conflict = opts + return &AccessPolicyUpsertBulk{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.AccessPolicy.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *AccessPolicyCreateBulk) OnConflictColumns(columns ...string) *AccessPolicyUpsertBulk { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &AccessPolicyUpsertBulk{ + create: _c, + } +} + +// AccessPolicyUpsertBulk is the builder for "upsert"-ing +// a bulk of AccessPolicy nodes. +type AccessPolicyUpsertBulk struct { + create *AccessPolicyCreateBulk +} + +// UpdateNewValues updates the mutable fields using the new values that +// were set on create. Using this option is equivalent to using: +// +// client.AccessPolicy.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(accesspolicy.FieldID) +// }), +// ). +// Exec(ctx) +func (u *AccessPolicyUpsertBulk) UpdateNewValues() *AccessPolicyUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + for _, b := range u.create.builders { + if _, exists := b.mutation.ID(); exists { + s.SetIgnore(accesspolicy.FieldID) + } + if _, exists := b.mutation.Created(); exists { + s.SetIgnore(accesspolicy.FieldCreated) + } + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.AccessPolicy.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *AccessPolicyUpsertBulk) Ignore() *AccessPolicyUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *AccessPolicyUpsertBulk) DoNothing() *AccessPolicyUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the AccessPolicyCreateBulk.OnConflict +// documentation for more info. +func (u *AccessPolicyUpsertBulk) Update(set func(*AccessPolicyUpsert)) *AccessPolicyUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&AccessPolicyUpsert{UpdateSet: update}) + })) + return u +} + +// SetName sets the "name" field. +func (u *AccessPolicyUpsertBulk) SetName(v string) *AccessPolicyUpsertBulk { + return u.Update(func(s *AccessPolicyUpsert) { + s.SetName(v) + }) +} + +// UpdateName sets the "name" field to the value that was provided on create. +func (u *AccessPolicyUpsertBulk) UpdateName() *AccessPolicyUpsertBulk { + return u.Update(func(s *AccessPolicyUpsert) { + s.UpdateName() + }) +} + +// SetDescription sets the "description" field. +func (u *AccessPolicyUpsertBulk) SetDescription(v string) *AccessPolicyUpsertBulk { + return u.Update(func(s *AccessPolicyUpsert) { + s.SetDescription(v) + }) +} + +// UpdateDescription sets the "description" field to the value that was provided on create. +func (u *AccessPolicyUpsertBulk) UpdateDescription() *AccessPolicyUpsertBulk { + return u.Update(func(s *AccessPolicyUpsert) { + s.UpdateDescription() + }) +} + +// ClearDescription clears the value of the "description" field. +func (u *AccessPolicyUpsertBulk) ClearDescription() *AccessPolicyUpsertBulk { + return u.Update(func(s *AccessPolicyUpsert) { + s.ClearDescription() + }) +} + +// SetScopeType sets the "scope_type" field. +func (u *AccessPolicyUpsertBulk) SetScopeType(v accesspolicy.ScopeType) *AccessPolicyUpsertBulk { + return u.Update(func(s *AccessPolicyUpsert) { + s.SetScopeType(v) + }) +} + +// UpdateScopeType sets the "scope_type" field to the value that was provided on create. +func (u *AccessPolicyUpsertBulk) UpdateScopeType() *AccessPolicyUpsertBulk { + return u.Update(func(s *AccessPolicyUpsert) { + s.UpdateScopeType() + }) +} + +// SetScopeID sets the "scope_id" field. +func (u *AccessPolicyUpsertBulk) SetScopeID(v string) *AccessPolicyUpsertBulk { + return u.Update(func(s *AccessPolicyUpsert) { + s.SetScopeID(v) + }) +} + +// UpdateScopeID sets the "scope_id" field to the value that was provided on create. +func (u *AccessPolicyUpsertBulk) UpdateScopeID() *AccessPolicyUpsertBulk { + return u.Update(func(s *AccessPolicyUpsert) { + s.UpdateScopeID() + }) +} + +// ClearScopeID clears the value of the "scope_id" field. +func (u *AccessPolicyUpsertBulk) ClearScopeID() *AccessPolicyUpsertBulk { + return u.Update(func(s *AccessPolicyUpsert) { + s.ClearScopeID() + }) +} + +// SetResourceType sets the "resource_type" field. +func (u *AccessPolicyUpsertBulk) SetResourceType(v string) *AccessPolicyUpsertBulk { + return u.Update(func(s *AccessPolicyUpsert) { + s.SetResourceType(v) + }) +} + +// UpdateResourceType sets the "resource_type" field to the value that was provided on create. +func (u *AccessPolicyUpsertBulk) UpdateResourceType() *AccessPolicyUpsertBulk { + return u.Update(func(s *AccessPolicyUpsert) { + s.UpdateResourceType() + }) +} + +// SetResourceID sets the "resource_id" field. +func (u *AccessPolicyUpsertBulk) SetResourceID(v string) *AccessPolicyUpsertBulk { + return u.Update(func(s *AccessPolicyUpsert) { + s.SetResourceID(v) + }) +} + +// UpdateResourceID sets the "resource_id" field to the value that was provided on create. +func (u *AccessPolicyUpsertBulk) UpdateResourceID() *AccessPolicyUpsertBulk { + return u.Update(func(s *AccessPolicyUpsert) { + s.UpdateResourceID() + }) +} + +// ClearResourceID clears the value of the "resource_id" field. +func (u *AccessPolicyUpsertBulk) ClearResourceID() *AccessPolicyUpsertBulk { + return u.Update(func(s *AccessPolicyUpsert) { + s.ClearResourceID() + }) +} + +// SetActions sets the "actions" field. +func (u *AccessPolicyUpsertBulk) SetActions(v []string) *AccessPolicyUpsertBulk { + return u.Update(func(s *AccessPolicyUpsert) { + s.SetActions(v) + }) +} + +// UpdateActions sets the "actions" field to the value that was provided on create. +func (u *AccessPolicyUpsertBulk) UpdateActions() *AccessPolicyUpsertBulk { + return u.Update(func(s *AccessPolicyUpsert) { + s.UpdateActions() + }) +} + +// ClearActions clears the value of the "actions" field. +func (u *AccessPolicyUpsertBulk) ClearActions() *AccessPolicyUpsertBulk { + return u.Update(func(s *AccessPolicyUpsert) { + s.ClearActions() + }) +} + +// SetEffect sets the "effect" field. +func (u *AccessPolicyUpsertBulk) SetEffect(v accesspolicy.Effect) *AccessPolicyUpsertBulk { + return u.Update(func(s *AccessPolicyUpsert) { + s.SetEffect(v) + }) +} + +// UpdateEffect sets the "effect" field to the value that was provided on create. +func (u *AccessPolicyUpsertBulk) UpdateEffect() *AccessPolicyUpsertBulk { + return u.Update(func(s *AccessPolicyUpsert) { + s.UpdateEffect() + }) +} + +// SetConditions sets the "conditions" field. +func (u *AccessPolicyUpsertBulk) SetConditions(v *schema.PolicyConditions) *AccessPolicyUpsertBulk { + return u.Update(func(s *AccessPolicyUpsert) { + s.SetConditions(v) + }) +} + +// UpdateConditions sets the "conditions" field to the value that was provided on create. +func (u *AccessPolicyUpsertBulk) UpdateConditions() *AccessPolicyUpsertBulk { + return u.Update(func(s *AccessPolicyUpsert) { + s.UpdateConditions() + }) +} + +// ClearConditions clears the value of the "conditions" field. +func (u *AccessPolicyUpsertBulk) ClearConditions() *AccessPolicyUpsertBulk { + return u.Update(func(s *AccessPolicyUpsert) { + s.ClearConditions() + }) +} + +// SetPriority sets the "priority" field. +func (u *AccessPolicyUpsertBulk) SetPriority(v int) *AccessPolicyUpsertBulk { + return u.Update(func(s *AccessPolicyUpsert) { + s.SetPriority(v) + }) +} + +// AddPriority adds v to the "priority" field. +func (u *AccessPolicyUpsertBulk) AddPriority(v int) *AccessPolicyUpsertBulk { + return u.Update(func(s *AccessPolicyUpsert) { + s.AddPriority(v) + }) +} + +// UpdatePriority sets the "priority" field to the value that was provided on create. +func (u *AccessPolicyUpsertBulk) UpdatePriority() *AccessPolicyUpsertBulk { + return u.Update(func(s *AccessPolicyUpsert) { + s.UpdatePriority() + }) +} + +// SetLabels sets the "labels" field. +func (u *AccessPolicyUpsertBulk) SetLabels(v map[string]string) *AccessPolicyUpsertBulk { + return u.Update(func(s *AccessPolicyUpsert) { + s.SetLabels(v) + }) +} + +// UpdateLabels sets the "labels" field to the value that was provided on create. +func (u *AccessPolicyUpsertBulk) UpdateLabels() *AccessPolicyUpsertBulk { + return u.Update(func(s *AccessPolicyUpsert) { + s.UpdateLabels() + }) +} + +// ClearLabels clears the value of the "labels" field. +func (u *AccessPolicyUpsertBulk) ClearLabels() *AccessPolicyUpsertBulk { + return u.Update(func(s *AccessPolicyUpsert) { + s.ClearLabels() + }) +} + +// SetAnnotations sets the "annotations" field. +func (u *AccessPolicyUpsertBulk) SetAnnotations(v map[string]string) *AccessPolicyUpsertBulk { + return u.Update(func(s *AccessPolicyUpsert) { + s.SetAnnotations(v) + }) +} + +// UpdateAnnotations sets the "annotations" field to the value that was provided on create. +func (u *AccessPolicyUpsertBulk) UpdateAnnotations() *AccessPolicyUpsertBulk { + return u.Update(func(s *AccessPolicyUpsert) { + s.UpdateAnnotations() + }) +} + +// ClearAnnotations clears the value of the "annotations" field. +func (u *AccessPolicyUpsertBulk) ClearAnnotations() *AccessPolicyUpsertBulk { + return u.Update(func(s *AccessPolicyUpsert) { + s.ClearAnnotations() + }) +} + +// SetUpdated sets the "updated" field. +func (u *AccessPolicyUpsertBulk) SetUpdated(v time.Time) *AccessPolicyUpsertBulk { + return u.Update(func(s *AccessPolicyUpsert) { + s.SetUpdated(v) + }) +} + +// UpdateUpdated sets the "updated" field to the value that was provided on create. +func (u *AccessPolicyUpsertBulk) UpdateUpdated() *AccessPolicyUpsertBulk { + return u.Update(func(s *AccessPolicyUpsert) { + s.UpdateUpdated() + }) +} + +// SetCreatedBy sets the "created_by" field. +func (u *AccessPolicyUpsertBulk) SetCreatedBy(v string) *AccessPolicyUpsertBulk { + return u.Update(func(s *AccessPolicyUpsert) { + s.SetCreatedBy(v) + }) +} + +// UpdateCreatedBy sets the "created_by" field to the value that was provided on create. +func (u *AccessPolicyUpsertBulk) UpdateCreatedBy() *AccessPolicyUpsertBulk { + return u.Update(func(s *AccessPolicyUpsert) { + s.UpdateCreatedBy() + }) +} + +// ClearCreatedBy clears the value of the "created_by" field. +func (u *AccessPolicyUpsertBulk) ClearCreatedBy() *AccessPolicyUpsertBulk { + return u.Update(func(s *AccessPolicyUpsert) { + s.ClearCreatedBy() + }) +} + +// Exec executes the query. +func (u *AccessPolicyUpsertBulk) Exec(ctx context.Context) error { + if u.create.err != nil { + return u.create.err + } + for i, b := range u.create.builders { + if len(b.conflict) != 0 { + return fmt.Errorf("ent: OnConflict was set for builder %d. Set it on the AccessPolicyCreateBulk instead", i) + } + } + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for AccessPolicyCreateBulk.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *AccessPolicyUpsertBulk) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/pkg/ent/accesspolicy_query.go b/pkg/ent/accesspolicy_query.go index 7fcfbe3aa..30d525f02 100644 --- a/pkg/ent/accesspolicy_query.go +++ b/pkg/ent/accesspolicy_query.go @@ -9,6 +9,7 @@ import ( "math" "entgo.io/ent" + "entgo.io/ent/dialect" "entgo.io/ent/dialect/sql" "entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/schema/field" @@ -26,6 +27,7 @@ type AccessPolicyQuery struct { inters []Interceptor predicates []predicate.AccessPolicy withBindings *PolicyBindingQuery + modifiers []func(*sql.Selector) // intermediate query (i.e. traversal path). sql *sql.Selector path func(context.Context) (*sql.Selector, error) @@ -385,6 +387,9 @@ func (_q *AccessPolicyQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([] node.Edges.loadedTypes = loadedTypes return node.assignValues(columns, values) } + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } for i := range hooks { hooks[i](ctx, _spec) } @@ -440,6 +445,9 @@ func (_q *AccessPolicyQuery) loadBindings(ctx context.Context, query *PolicyBind func (_q *AccessPolicyQuery) sqlCount(ctx context.Context) (int, error) { _spec := _q.querySpec() + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } _spec.Node.Columns = _q.ctx.Fields if len(_q.ctx.Fields) > 0 { _spec.Unique = _q.ctx.Unique != nil && *_q.ctx.Unique @@ -502,6 +510,9 @@ func (_q *AccessPolicyQuery) sqlQuery(ctx context.Context) *sql.Selector { if _q.ctx.Unique != nil && *_q.ctx.Unique { selector.Distinct() } + for _, m := range _q.modifiers { + m(selector) + } for _, p := range _q.predicates { p(selector) } @@ -519,6 +530,32 @@ func (_q *AccessPolicyQuery) sqlQuery(ctx context.Context) *sql.Selector { return selector } +// ForUpdate locks the selected rows against concurrent updates, and prevent them from being +// updated, deleted or "selected ... for update" by other sessions, until the transaction is +// either committed or rolled-back. +func (_q *AccessPolicyQuery) ForUpdate(opts ...sql.LockOption) *AccessPolicyQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForUpdate(opts...) + }) + return _q +} + +// ForShare behaves similarly to ForUpdate, except that it acquires a shared mode lock +// on any rows that are read. Other sessions can read the rows, but cannot modify them +// until your transaction commits. +func (_q *AccessPolicyQuery) ForShare(opts ...sql.LockOption) *AccessPolicyQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForShare(opts...) + }) + return _q +} + // AccessPolicyGroupBy is the group-by builder for AccessPolicy entities. type AccessPolicyGroupBy struct { selector diff --git a/pkg/ent/agent.go b/pkg/ent/agent.go index 08b7bfa8c..5ec262305 100644 --- a/pkg/ent/agent.go +++ b/pkg/ent/agent.go @@ -3,6 +3,7 @@ package ent import ( + "encoding/json" "fmt" "strings" "time" @@ -38,10 +39,60 @@ type Agent struct { DelegationEnabled bool `json:"delegation_enabled,omitempty"` // Visibility holds the value of the "visibility" field. Visibility string `json:"visibility,omitempty"` + // Labels holds the value of the "labels" field. + Labels map[string]string `json:"labels,omitempty"` + // Annotations holds the value of the "annotations" field. + Annotations map[string]string `json:"annotations,omitempty"` + // Phase holds the value of the "phase" field. + Phase string `json:"phase,omitempty"` + // Activity holds the value of the "activity" field. + Activity string `json:"activity,omitempty"` + // ToolName holds the value of the "tool_name" field. + ToolName string `json:"tool_name,omitempty"` + // ConnectionState holds the value of the "connection_state" field. + ConnectionState string `json:"connection_state,omitempty"` + // ContainerStatus holds the value of the "container_status" field. + ContainerStatus string `json:"container_status,omitempty"` + // RuntimeState holds the value of the "runtime_state" field. + RuntimeState string `json:"runtime_state,omitempty"` + // StalledFromActivity holds the value of the "stalled_from_activity" field. + StalledFromActivity string `json:"stalled_from_activity,omitempty"` + // CurrentTurns holds the value of the "current_turns" field. + CurrentTurns int `json:"current_turns,omitempty"` + // CurrentModelCalls holds the value of the "current_model_calls" field. + CurrentModelCalls int `json:"current_model_calls,omitempty"` + // Image holds the value of the "image" field. + Image string `json:"image,omitempty"` + // Detached holds the value of the "detached" field. + Detached bool `json:"detached,omitempty"` + // Runtime holds the value of the "runtime" field. + Runtime string `json:"runtime,omitempty"` + // RuntimeBrokerID holds the value of the "runtime_broker_id" field. + RuntimeBrokerID string `json:"runtime_broker_id,omitempty"` + // WebPtyEnabled holds the value of the "web_pty_enabled" field. + WebPtyEnabled bool `json:"web_pty_enabled,omitempty"` + // TaskSummary holds the value of the "task_summary" field. + TaskSummary string `json:"task_summary,omitempty"` + // Message holds the value of the "message" field. + Message string `json:"message,omitempty"` + // AppliedConfig holds the value of the "applied_config" field. + AppliedConfig string `json:"applied_config,omitempty"` + // Ancestry holds the value of the "ancestry" field. + Ancestry []string `json:"ancestry,omitempty"` // Created holds the value of the "created" field. Created time.Time `json:"created,omitempty"` // Updated holds the value of the "updated" field. Updated time.Time `json:"updated,omitempty"` + // LastSeen holds the value of the "last_seen" field. + LastSeen *time.Time `json:"last_seen,omitempty"` + // LastActivityEvent holds the value of the "last_activity_event" field. + LastActivityEvent *time.Time `json:"last_activity_event,omitempty"` + // StartedAt holds the value of the "started_at" field. + StartedAt *time.Time `json:"started_at,omitempty"` + // DeletedAt holds the value of the "deleted_at" field. + DeletedAt *time.Time `json:"deleted_at,omitempty"` + // StateVersion holds the value of the "state_version" field. + StateVersion int64 `json:"state_version,omitempty"` // Edges holds the relations/edges for other nodes in the graph. // The values are being populated by the AgentQuery when eager-loading is set. Edges AgentEdges `json:"edges"` @@ -123,11 +174,15 @@ func (*Agent) scanValues(columns []string) ([]any, error) { switch columns[i] { case agent.FieldCreatedBy, agent.FieldOwnerID: values[i] = &sql.NullScanner{S: new(uuid.UUID)} - case agent.FieldDelegationEnabled: + case agent.FieldLabels, agent.FieldAnnotations, agent.FieldAncestry: + values[i] = new([]byte) + case agent.FieldDelegationEnabled, agent.FieldDetached, agent.FieldWebPtyEnabled: values[i] = new(sql.NullBool) - case agent.FieldSlug, agent.FieldName, agent.FieldTemplate, agent.FieldStatus, agent.FieldVisibility: + case agent.FieldCurrentTurns, agent.FieldCurrentModelCalls, agent.FieldStateVersion: + values[i] = new(sql.NullInt64) + case agent.FieldSlug, agent.FieldName, agent.FieldTemplate, agent.FieldStatus, agent.FieldVisibility, agent.FieldPhase, agent.FieldActivity, agent.FieldToolName, agent.FieldConnectionState, agent.FieldContainerStatus, agent.FieldRuntimeState, agent.FieldStalledFromActivity, agent.FieldImage, agent.FieldRuntime, agent.FieldRuntimeBrokerID, agent.FieldTaskSummary, agent.FieldMessage, agent.FieldAppliedConfig: values[i] = new(sql.NullString) - case agent.FieldCreated, agent.FieldUpdated: + case agent.FieldCreated, agent.FieldUpdated, agent.FieldLastSeen, agent.FieldLastActivityEvent, agent.FieldStartedAt, agent.FieldDeletedAt: values[i] = new(sql.NullTime) case agent.FieldID, agent.FieldProjectID: values[i] = new(uuid.UUID) @@ -208,6 +263,132 @@ func (_m *Agent) assignValues(columns []string, values []any) error { } else if value.Valid { _m.Visibility = value.String } + case agent.FieldLabels: + if value, ok := values[i].(*[]byte); !ok { + return fmt.Errorf("unexpected type %T for field labels", values[i]) + } else if value != nil && len(*value) > 0 { + if err := json.Unmarshal(*value, &_m.Labels); err != nil { + return fmt.Errorf("unmarshal field labels: %w", err) + } + } + case agent.FieldAnnotations: + if value, ok := values[i].(*[]byte); !ok { + return fmt.Errorf("unexpected type %T for field annotations", values[i]) + } else if value != nil && len(*value) > 0 { + if err := json.Unmarshal(*value, &_m.Annotations); err != nil { + return fmt.Errorf("unmarshal field annotations: %w", err) + } + } + case agent.FieldPhase: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field phase", values[i]) + } else if value.Valid { + _m.Phase = value.String + } + case agent.FieldActivity: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field activity", values[i]) + } else if value.Valid { + _m.Activity = value.String + } + case agent.FieldToolName: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field tool_name", values[i]) + } else if value.Valid { + _m.ToolName = value.String + } + case agent.FieldConnectionState: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field connection_state", values[i]) + } else if value.Valid { + _m.ConnectionState = value.String + } + case agent.FieldContainerStatus: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field container_status", values[i]) + } else if value.Valid { + _m.ContainerStatus = value.String + } + case agent.FieldRuntimeState: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field runtime_state", values[i]) + } else if value.Valid { + _m.RuntimeState = value.String + } + case agent.FieldStalledFromActivity: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field stalled_from_activity", values[i]) + } else if value.Valid { + _m.StalledFromActivity = value.String + } + case agent.FieldCurrentTurns: + if value, ok := values[i].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for field current_turns", values[i]) + } else if value.Valid { + _m.CurrentTurns = int(value.Int64) + } + case agent.FieldCurrentModelCalls: + if value, ok := values[i].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for field current_model_calls", values[i]) + } else if value.Valid { + _m.CurrentModelCalls = int(value.Int64) + } + case agent.FieldImage: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field image", values[i]) + } else if value.Valid { + _m.Image = value.String + } + case agent.FieldDetached: + if value, ok := values[i].(*sql.NullBool); !ok { + return fmt.Errorf("unexpected type %T for field detached", values[i]) + } else if value.Valid { + _m.Detached = value.Bool + } + case agent.FieldRuntime: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field runtime", values[i]) + } else if value.Valid { + _m.Runtime = value.String + } + case agent.FieldRuntimeBrokerID: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field runtime_broker_id", values[i]) + } else if value.Valid { + _m.RuntimeBrokerID = value.String + } + case agent.FieldWebPtyEnabled: + if value, ok := values[i].(*sql.NullBool); !ok { + return fmt.Errorf("unexpected type %T for field web_pty_enabled", values[i]) + } else if value.Valid { + _m.WebPtyEnabled = value.Bool + } + case agent.FieldTaskSummary: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field task_summary", values[i]) + } else if value.Valid { + _m.TaskSummary = value.String + } + case agent.FieldMessage: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field message", values[i]) + } else if value.Valid { + _m.Message = value.String + } + case agent.FieldAppliedConfig: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field applied_config", values[i]) + } else if value.Valid { + _m.AppliedConfig = value.String + } + case agent.FieldAncestry: + if value, ok := values[i].(*[]byte); !ok { + return fmt.Errorf("unexpected type %T for field ancestry", values[i]) + } else if value != nil && len(*value) > 0 { + if err := json.Unmarshal(*value, &_m.Ancestry); err != nil { + return fmt.Errorf("unmarshal field ancestry: %w", err) + } + } case agent.FieldCreated: if value, ok := values[i].(*sql.NullTime); !ok { return fmt.Errorf("unexpected type %T for field created", values[i]) @@ -220,6 +401,40 @@ func (_m *Agent) assignValues(columns []string, values []any) error { } else if value.Valid { _m.Updated = value.Time } + case agent.FieldLastSeen: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field last_seen", values[i]) + } else if value.Valid { + _m.LastSeen = new(time.Time) + *_m.LastSeen = value.Time + } + case agent.FieldLastActivityEvent: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field last_activity_event", values[i]) + } else if value.Valid { + _m.LastActivityEvent = new(time.Time) + *_m.LastActivityEvent = value.Time + } + case agent.FieldStartedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field started_at", values[i]) + } else if value.Valid { + _m.StartedAt = new(time.Time) + *_m.StartedAt = value.Time + } + case agent.FieldDeletedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field deleted_at", values[i]) + } else if value.Valid { + _m.DeletedAt = new(time.Time) + *_m.DeletedAt = value.Time + } + case agent.FieldStateVersion: + if value, ok := values[i].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for field state_version", values[i]) + } else if value.Valid { + _m.StateVersion = value.Int64 + } default: _m.selectValues.Set(columns[i], values[i]) } @@ -312,11 +527,94 @@ func (_m *Agent) String() string { builder.WriteString("visibility=") builder.WriteString(_m.Visibility) builder.WriteString(", ") + builder.WriteString("labels=") + builder.WriteString(fmt.Sprintf("%v", _m.Labels)) + builder.WriteString(", ") + builder.WriteString("annotations=") + builder.WriteString(fmt.Sprintf("%v", _m.Annotations)) + builder.WriteString(", ") + builder.WriteString("phase=") + builder.WriteString(_m.Phase) + builder.WriteString(", ") + builder.WriteString("activity=") + builder.WriteString(_m.Activity) + builder.WriteString(", ") + builder.WriteString("tool_name=") + builder.WriteString(_m.ToolName) + builder.WriteString(", ") + builder.WriteString("connection_state=") + builder.WriteString(_m.ConnectionState) + builder.WriteString(", ") + builder.WriteString("container_status=") + builder.WriteString(_m.ContainerStatus) + builder.WriteString(", ") + builder.WriteString("runtime_state=") + builder.WriteString(_m.RuntimeState) + builder.WriteString(", ") + builder.WriteString("stalled_from_activity=") + builder.WriteString(_m.StalledFromActivity) + builder.WriteString(", ") + builder.WriteString("current_turns=") + builder.WriteString(fmt.Sprintf("%v", _m.CurrentTurns)) + builder.WriteString(", ") + builder.WriteString("current_model_calls=") + builder.WriteString(fmt.Sprintf("%v", _m.CurrentModelCalls)) + builder.WriteString(", ") + builder.WriteString("image=") + builder.WriteString(_m.Image) + builder.WriteString(", ") + builder.WriteString("detached=") + builder.WriteString(fmt.Sprintf("%v", _m.Detached)) + builder.WriteString(", ") + builder.WriteString("runtime=") + builder.WriteString(_m.Runtime) + builder.WriteString(", ") + builder.WriteString("runtime_broker_id=") + builder.WriteString(_m.RuntimeBrokerID) + builder.WriteString(", ") + builder.WriteString("web_pty_enabled=") + builder.WriteString(fmt.Sprintf("%v", _m.WebPtyEnabled)) + builder.WriteString(", ") + builder.WriteString("task_summary=") + builder.WriteString(_m.TaskSummary) + builder.WriteString(", ") + builder.WriteString("message=") + builder.WriteString(_m.Message) + builder.WriteString(", ") + builder.WriteString("applied_config=") + builder.WriteString(_m.AppliedConfig) + builder.WriteString(", ") + builder.WriteString("ancestry=") + builder.WriteString(fmt.Sprintf("%v", _m.Ancestry)) + builder.WriteString(", ") builder.WriteString("created=") builder.WriteString(_m.Created.Format(time.ANSIC)) builder.WriteString(", ") builder.WriteString("updated=") builder.WriteString(_m.Updated.Format(time.ANSIC)) + builder.WriteString(", ") + if v := _m.LastSeen; v != nil { + builder.WriteString("last_seen=") + builder.WriteString(v.Format(time.ANSIC)) + } + builder.WriteString(", ") + if v := _m.LastActivityEvent; v != nil { + builder.WriteString("last_activity_event=") + builder.WriteString(v.Format(time.ANSIC)) + } + builder.WriteString(", ") + if v := _m.StartedAt; v != nil { + builder.WriteString("started_at=") + builder.WriteString(v.Format(time.ANSIC)) + } + builder.WriteString(", ") + if v := _m.DeletedAt; v != nil { + builder.WriteString("deleted_at=") + builder.WriteString(v.Format(time.ANSIC)) + } + builder.WriteString(", ") + builder.WriteString("state_version=") + builder.WriteString(fmt.Sprintf("%v", _m.StateVersion)) builder.WriteByte(')') return builder.String() } diff --git a/pkg/ent/agent/agent.go b/pkg/ent/agent/agent.go index 32a0b9297..4400f2a4d 100644 --- a/pkg/ent/agent/agent.go +++ b/pkg/ent/agent/agent.go @@ -34,10 +34,60 @@ const ( FieldDelegationEnabled = "delegation_enabled" // FieldVisibility holds the string denoting the visibility field in the database. FieldVisibility = "visibility" + // FieldLabels holds the string denoting the labels field in the database. + FieldLabels = "labels" + // FieldAnnotations holds the string denoting the annotations field in the database. + FieldAnnotations = "annotations" + // FieldPhase holds the string denoting the phase field in the database. + FieldPhase = "phase" + // FieldActivity holds the string denoting the activity field in the database. + FieldActivity = "activity" + // FieldToolName holds the string denoting the tool_name field in the database. + FieldToolName = "tool_name" + // FieldConnectionState holds the string denoting the connection_state field in the database. + FieldConnectionState = "connection_state" + // FieldContainerStatus holds the string denoting the container_status field in the database. + FieldContainerStatus = "container_status" + // FieldRuntimeState holds the string denoting the runtime_state field in the database. + FieldRuntimeState = "runtime_state" + // FieldStalledFromActivity holds the string denoting the stalled_from_activity field in the database. + FieldStalledFromActivity = "stalled_from_activity" + // FieldCurrentTurns holds the string denoting the current_turns field in the database. + FieldCurrentTurns = "current_turns" + // FieldCurrentModelCalls holds the string denoting the current_model_calls field in the database. + FieldCurrentModelCalls = "current_model_calls" + // FieldImage holds the string denoting the image field in the database. + FieldImage = "image" + // FieldDetached holds the string denoting the detached field in the database. + FieldDetached = "detached" + // FieldRuntime holds the string denoting the runtime field in the database. + FieldRuntime = "runtime" + // FieldRuntimeBrokerID holds the string denoting the runtime_broker_id field in the database. + FieldRuntimeBrokerID = "runtime_broker_id" + // FieldWebPtyEnabled holds the string denoting the web_pty_enabled field in the database. + FieldWebPtyEnabled = "web_pty_enabled" + // FieldTaskSummary holds the string denoting the task_summary field in the database. + FieldTaskSummary = "task_summary" + // FieldMessage holds the string denoting the message field in the database. + FieldMessage = "message" + // FieldAppliedConfig holds the string denoting the applied_config field in the database. + FieldAppliedConfig = "applied_config" + // FieldAncestry holds the string denoting the ancestry field in the database. + FieldAncestry = "ancestry" // FieldCreated holds the string denoting the created field in the database. FieldCreated = "created" // FieldUpdated holds the string denoting the updated field in the database. FieldUpdated = "updated" + // FieldLastSeen holds the string denoting the last_seen field in the database. + FieldLastSeen = "last_seen" + // FieldLastActivityEvent holds the string denoting the last_activity_event field in the database. + FieldLastActivityEvent = "last_activity_event" + // FieldStartedAt holds the string denoting the started_at field in the database. + FieldStartedAt = "started_at" + // FieldDeletedAt holds the string denoting the deleted_at field in the database. + FieldDeletedAt = "deleted_at" + // FieldStateVersion holds the string denoting the state_version field in the database. + FieldStateVersion = "state_version" // EdgeProject holds the string denoting the project edge name in mutations. EdgeProject = "project" // EdgeCreator holds the string denoting the creator edge name in mutations. @@ -99,8 +149,33 @@ var Columns = []string{ FieldOwnerID, FieldDelegationEnabled, FieldVisibility, + FieldLabels, + FieldAnnotations, + FieldPhase, + FieldActivity, + FieldToolName, + FieldConnectionState, + FieldContainerStatus, + FieldRuntimeState, + FieldStalledFromActivity, + FieldCurrentTurns, + FieldCurrentModelCalls, + FieldImage, + FieldDetached, + FieldRuntime, + FieldRuntimeBrokerID, + FieldWebPtyEnabled, + FieldTaskSummary, + FieldMessage, + FieldAppliedConfig, + FieldAncestry, FieldCreated, FieldUpdated, + FieldLastSeen, + FieldLastActivityEvent, + FieldStartedAt, + FieldDeletedAt, + FieldStateVersion, } // ValidColumn reports if the column name is valid (part of the table columns). @@ -122,12 +197,22 @@ var ( DefaultDelegationEnabled bool // DefaultVisibility holds the default value on creation for the "visibility" field. DefaultVisibility string + // DefaultCurrentTurns holds the default value on creation for the "current_turns" field. + DefaultCurrentTurns int + // DefaultCurrentModelCalls holds the default value on creation for the "current_model_calls" field. + DefaultCurrentModelCalls int + // DefaultDetached holds the default value on creation for the "detached" field. + DefaultDetached bool + // DefaultWebPtyEnabled holds the default value on creation for the "web_pty_enabled" field. + DefaultWebPtyEnabled bool // DefaultCreated holds the default value on creation for the "created" field. DefaultCreated func() time.Time // DefaultUpdated holds the default value on creation for the "updated" field. DefaultUpdated func() time.Time // UpdateDefaultUpdated holds the default value on update for the "updated" field. UpdateDefaultUpdated func() time.Time + // DefaultStateVersion holds the default value on creation for the "state_version" field. + DefaultStateVersion int64 // DefaultID holds the default value on creation for the "id" field. DefaultID func() uuid.UUID ) @@ -218,6 +303,91 @@ func ByVisibility(opts ...sql.OrderTermOption) OrderOption { return sql.OrderByField(FieldVisibility, opts...).ToFunc() } +// ByPhase orders the results by the phase field. +func ByPhase(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldPhase, opts...).ToFunc() +} + +// ByActivity orders the results by the activity field. +func ByActivity(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldActivity, opts...).ToFunc() +} + +// ByToolName orders the results by the tool_name field. +func ByToolName(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldToolName, opts...).ToFunc() +} + +// ByConnectionState orders the results by the connection_state field. +func ByConnectionState(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldConnectionState, opts...).ToFunc() +} + +// ByContainerStatus orders the results by the container_status field. +func ByContainerStatus(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldContainerStatus, opts...).ToFunc() +} + +// ByRuntimeState orders the results by the runtime_state field. +func ByRuntimeState(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldRuntimeState, opts...).ToFunc() +} + +// ByStalledFromActivity orders the results by the stalled_from_activity field. +func ByStalledFromActivity(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldStalledFromActivity, opts...).ToFunc() +} + +// ByCurrentTurns orders the results by the current_turns field. +func ByCurrentTurns(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCurrentTurns, opts...).ToFunc() +} + +// ByCurrentModelCalls orders the results by the current_model_calls field. +func ByCurrentModelCalls(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCurrentModelCalls, opts...).ToFunc() +} + +// ByImage orders the results by the image field. +func ByImage(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldImage, opts...).ToFunc() +} + +// ByDetached orders the results by the detached field. +func ByDetached(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldDetached, opts...).ToFunc() +} + +// ByRuntime orders the results by the runtime field. +func ByRuntime(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldRuntime, opts...).ToFunc() +} + +// ByRuntimeBrokerID orders the results by the runtime_broker_id field. +func ByRuntimeBrokerID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldRuntimeBrokerID, opts...).ToFunc() +} + +// ByWebPtyEnabled orders the results by the web_pty_enabled field. +func ByWebPtyEnabled(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldWebPtyEnabled, opts...).ToFunc() +} + +// ByTaskSummary orders the results by the task_summary field. +func ByTaskSummary(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldTaskSummary, opts...).ToFunc() +} + +// ByMessage orders the results by the message field. +func ByMessage(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldMessage, opts...).ToFunc() +} + +// ByAppliedConfig orders the results by the applied_config field. +func ByAppliedConfig(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldAppliedConfig, opts...).ToFunc() +} + // ByCreated orders the results by the created field. func ByCreated(opts ...sql.OrderTermOption) OrderOption { return sql.OrderByField(FieldCreated, opts...).ToFunc() @@ -228,6 +398,31 @@ func ByUpdated(opts ...sql.OrderTermOption) OrderOption { return sql.OrderByField(FieldUpdated, opts...).ToFunc() } +// ByLastSeen orders the results by the last_seen field. +func ByLastSeen(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldLastSeen, opts...).ToFunc() +} + +// ByLastActivityEvent orders the results by the last_activity_event field. +func ByLastActivityEvent(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldLastActivityEvent, opts...).ToFunc() +} + +// ByStartedAt orders the results by the started_at field. +func ByStartedAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldStartedAt, opts...).ToFunc() +} + +// ByDeletedAt orders the results by the deleted_at field. +func ByDeletedAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldDeletedAt, opts...).ToFunc() +} + +// ByStateVersion orders the results by the state_version field. +func ByStateVersion(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldStateVersion, opts...).ToFunc() +} + // ByProjectField orders the results by project field. func ByProjectField(field string, opts ...sql.OrderTermOption) OrderOption { return func(s *sql.Selector) { diff --git a/pkg/ent/agent/where.go b/pkg/ent/agent/where.go index 1ed475ed1..1a0fa9068 100644 --- a/pkg/ent/agent/where.go +++ b/pkg/ent/agent/where.go @@ -96,6 +96,91 @@ func Visibility(v string) predicate.Agent { return predicate.Agent(sql.FieldEQ(FieldVisibility, v)) } +// Phase applies equality check predicate on the "phase" field. It's identical to PhaseEQ. +func Phase(v string) predicate.Agent { + return predicate.Agent(sql.FieldEQ(FieldPhase, v)) +} + +// Activity applies equality check predicate on the "activity" field. It's identical to ActivityEQ. +func Activity(v string) predicate.Agent { + return predicate.Agent(sql.FieldEQ(FieldActivity, v)) +} + +// ToolName applies equality check predicate on the "tool_name" field. It's identical to ToolNameEQ. +func ToolName(v string) predicate.Agent { + return predicate.Agent(sql.FieldEQ(FieldToolName, v)) +} + +// ConnectionState applies equality check predicate on the "connection_state" field. It's identical to ConnectionStateEQ. +func ConnectionState(v string) predicate.Agent { + return predicate.Agent(sql.FieldEQ(FieldConnectionState, v)) +} + +// ContainerStatus applies equality check predicate on the "container_status" field. It's identical to ContainerStatusEQ. +func ContainerStatus(v string) predicate.Agent { + return predicate.Agent(sql.FieldEQ(FieldContainerStatus, v)) +} + +// RuntimeState applies equality check predicate on the "runtime_state" field. It's identical to RuntimeStateEQ. +func RuntimeState(v string) predicate.Agent { + return predicate.Agent(sql.FieldEQ(FieldRuntimeState, v)) +} + +// StalledFromActivity applies equality check predicate on the "stalled_from_activity" field. It's identical to StalledFromActivityEQ. +func StalledFromActivity(v string) predicate.Agent { + return predicate.Agent(sql.FieldEQ(FieldStalledFromActivity, v)) +} + +// CurrentTurns applies equality check predicate on the "current_turns" field. It's identical to CurrentTurnsEQ. +func CurrentTurns(v int) predicate.Agent { + return predicate.Agent(sql.FieldEQ(FieldCurrentTurns, v)) +} + +// CurrentModelCalls applies equality check predicate on the "current_model_calls" field. It's identical to CurrentModelCallsEQ. +func CurrentModelCalls(v int) predicate.Agent { + return predicate.Agent(sql.FieldEQ(FieldCurrentModelCalls, v)) +} + +// Image applies equality check predicate on the "image" field. It's identical to ImageEQ. +func Image(v string) predicate.Agent { + return predicate.Agent(sql.FieldEQ(FieldImage, v)) +} + +// Detached applies equality check predicate on the "detached" field. It's identical to DetachedEQ. +func Detached(v bool) predicate.Agent { + return predicate.Agent(sql.FieldEQ(FieldDetached, v)) +} + +// Runtime applies equality check predicate on the "runtime" field. It's identical to RuntimeEQ. +func Runtime(v string) predicate.Agent { + return predicate.Agent(sql.FieldEQ(FieldRuntime, v)) +} + +// RuntimeBrokerID applies equality check predicate on the "runtime_broker_id" field. It's identical to RuntimeBrokerIDEQ. +func RuntimeBrokerID(v string) predicate.Agent { + return predicate.Agent(sql.FieldEQ(FieldRuntimeBrokerID, v)) +} + +// WebPtyEnabled applies equality check predicate on the "web_pty_enabled" field. It's identical to WebPtyEnabledEQ. +func WebPtyEnabled(v bool) predicate.Agent { + return predicate.Agent(sql.FieldEQ(FieldWebPtyEnabled, v)) +} + +// TaskSummary applies equality check predicate on the "task_summary" field. It's identical to TaskSummaryEQ. +func TaskSummary(v string) predicate.Agent { + return predicate.Agent(sql.FieldEQ(FieldTaskSummary, v)) +} + +// Message applies equality check predicate on the "message" field. It's identical to MessageEQ. +func Message(v string) predicate.Agent { + return predicate.Agent(sql.FieldEQ(FieldMessage, v)) +} + +// AppliedConfig applies equality check predicate on the "applied_config" field. It's identical to AppliedConfigEQ. +func AppliedConfig(v string) predicate.Agent { + return predicate.Agent(sql.FieldEQ(FieldAppliedConfig, v)) +} + // Created applies equality check predicate on the "created" field. It's identical to CreatedEQ. func Created(v time.Time) predicate.Agent { return predicate.Agent(sql.FieldEQ(FieldCreated, v)) @@ -106,6 +191,31 @@ func Updated(v time.Time) predicate.Agent { return predicate.Agent(sql.FieldEQ(FieldUpdated, v)) } +// LastSeen applies equality check predicate on the "last_seen" field. It's identical to LastSeenEQ. +func LastSeen(v time.Time) predicate.Agent { + return predicate.Agent(sql.FieldEQ(FieldLastSeen, v)) +} + +// LastActivityEvent applies equality check predicate on the "last_activity_event" field. It's identical to LastActivityEventEQ. +func LastActivityEvent(v time.Time) predicate.Agent { + return predicate.Agent(sql.FieldEQ(FieldLastActivityEvent, v)) +} + +// StartedAt applies equality check predicate on the "started_at" field. It's identical to StartedAtEQ. +func StartedAt(v time.Time) predicate.Agent { + return predicate.Agent(sql.FieldEQ(FieldStartedAt, v)) +} + +// DeletedAt applies equality check predicate on the "deleted_at" field. It's identical to DeletedAtEQ. +func DeletedAt(v time.Time) predicate.Agent { + return predicate.Agent(sql.FieldEQ(FieldDeletedAt, v)) +} + +// StateVersion applies equality check predicate on the "state_version" field. It's identical to StateVersionEQ. +func StateVersion(v int64) predicate.Agent { + return predicate.Agent(sql.FieldEQ(FieldStateVersion, v)) +} + // SlugEQ applies the EQ predicate on the "slug" field. func SlugEQ(v string) predicate.Agent { return predicate.Agent(sql.FieldEQ(FieldSlug, v)) @@ -486,84 +596,1429 @@ func VisibilityContainsFold(v string) predicate.Agent { return predicate.Agent(sql.FieldContainsFold(FieldVisibility, v)) } -// CreatedEQ applies the EQ predicate on the "created" field. -func CreatedEQ(v time.Time) predicate.Agent { - return predicate.Agent(sql.FieldEQ(FieldCreated, v)) +// LabelsIsNil applies the IsNil predicate on the "labels" field. +func LabelsIsNil() predicate.Agent { + return predicate.Agent(sql.FieldIsNull(FieldLabels)) } -// CreatedNEQ applies the NEQ predicate on the "created" field. -func CreatedNEQ(v time.Time) predicate.Agent { - return predicate.Agent(sql.FieldNEQ(FieldCreated, v)) +// LabelsNotNil applies the NotNil predicate on the "labels" field. +func LabelsNotNil() predicate.Agent { + return predicate.Agent(sql.FieldNotNull(FieldLabels)) } -// CreatedIn applies the In predicate on the "created" field. -func CreatedIn(vs ...time.Time) predicate.Agent { - return predicate.Agent(sql.FieldIn(FieldCreated, vs...)) +// AnnotationsIsNil applies the IsNil predicate on the "annotations" field. +func AnnotationsIsNil() predicate.Agent { + return predicate.Agent(sql.FieldIsNull(FieldAnnotations)) } -// CreatedNotIn applies the NotIn predicate on the "created" field. -func CreatedNotIn(vs ...time.Time) predicate.Agent { - return predicate.Agent(sql.FieldNotIn(FieldCreated, vs...)) +// AnnotationsNotNil applies the NotNil predicate on the "annotations" field. +func AnnotationsNotNil() predicate.Agent { + return predicate.Agent(sql.FieldNotNull(FieldAnnotations)) } -// CreatedGT applies the GT predicate on the "created" field. -func CreatedGT(v time.Time) predicate.Agent { - return predicate.Agent(sql.FieldGT(FieldCreated, v)) +// PhaseEQ applies the EQ predicate on the "phase" field. +func PhaseEQ(v string) predicate.Agent { + return predicate.Agent(sql.FieldEQ(FieldPhase, v)) } -// CreatedGTE applies the GTE predicate on the "created" field. -func CreatedGTE(v time.Time) predicate.Agent { - return predicate.Agent(sql.FieldGTE(FieldCreated, v)) +// PhaseNEQ applies the NEQ predicate on the "phase" field. +func PhaseNEQ(v string) predicate.Agent { + return predicate.Agent(sql.FieldNEQ(FieldPhase, v)) } -// CreatedLT applies the LT predicate on the "created" field. -func CreatedLT(v time.Time) predicate.Agent { - return predicate.Agent(sql.FieldLT(FieldCreated, v)) +// PhaseIn applies the In predicate on the "phase" field. +func PhaseIn(vs ...string) predicate.Agent { + return predicate.Agent(sql.FieldIn(FieldPhase, vs...)) } -// CreatedLTE applies the LTE predicate on the "created" field. -func CreatedLTE(v time.Time) predicate.Agent { - return predicate.Agent(sql.FieldLTE(FieldCreated, v)) +// PhaseNotIn applies the NotIn predicate on the "phase" field. +func PhaseNotIn(vs ...string) predicate.Agent { + return predicate.Agent(sql.FieldNotIn(FieldPhase, vs...)) } -// UpdatedEQ applies the EQ predicate on the "updated" field. -func UpdatedEQ(v time.Time) predicate.Agent { - return predicate.Agent(sql.FieldEQ(FieldUpdated, v)) +// PhaseGT applies the GT predicate on the "phase" field. +func PhaseGT(v string) predicate.Agent { + return predicate.Agent(sql.FieldGT(FieldPhase, v)) } -// UpdatedNEQ applies the NEQ predicate on the "updated" field. -func UpdatedNEQ(v time.Time) predicate.Agent { - return predicate.Agent(sql.FieldNEQ(FieldUpdated, v)) +// PhaseGTE applies the GTE predicate on the "phase" field. +func PhaseGTE(v string) predicate.Agent { + return predicate.Agent(sql.FieldGTE(FieldPhase, v)) } -// UpdatedIn applies the In predicate on the "updated" field. -func UpdatedIn(vs ...time.Time) predicate.Agent { - return predicate.Agent(sql.FieldIn(FieldUpdated, vs...)) +// PhaseLT applies the LT predicate on the "phase" field. +func PhaseLT(v string) predicate.Agent { + return predicate.Agent(sql.FieldLT(FieldPhase, v)) } -// UpdatedNotIn applies the NotIn predicate on the "updated" field. -func UpdatedNotIn(vs ...time.Time) predicate.Agent { - return predicate.Agent(sql.FieldNotIn(FieldUpdated, vs...)) +// PhaseLTE applies the LTE predicate on the "phase" field. +func PhaseLTE(v string) predicate.Agent { + return predicate.Agent(sql.FieldLTE(FieldPhase, v)) } -// UpdatedGT applies the GT predicate on the "updated" field. -func UpdatedGT(v time.Time) predicate.Agent { - return predicate.Agent(sql.FieldGT(FieldUpdated, v)) +// PhaseContains applies the Contains predicate on the "phase" field. +func PhaseContains(v string) predicate.Agent { + return predicate.Agent(sql.FieldContains(FieldPhase, v)) } -// UpdatedGTE applies the GTE predicate on the "updated" field. -func UpdatedGTE(v time.Time) predicate.Agent { - return predicate.Agent(sql.FieldGTE(FieldUpdated, v)) +// PhaseHasPrefix applies the HasPrefix predicate on the "phase" field. +func PhaseHasPrefix(v string) predicate.Agent { + return predicate.Agent(sql.FieldHasPrefix(FieldPhase, v)) } -// UpdatedLT applies the LT predicate on the "updated" field. -func UpdatedLT(v time.Time) predicate.Agent { - return predicate.Agent(sql.FieldLT(FieldUpdated, v)) +// PhaseHasSuffix applies the HasSuffix predicate on the "phase" field. +func PhaseHasSuffix(v string) predicate.Agent { + return predicate.Agent(sql.FieldHasSuffix(FieldPhase, v)) } -// UpdatedLTE applies the LTE predicate on the "updated" field. -func UpdatedLTE(v time.Time) predicate.Agent { - return predicate.Agent(sql.FieldLTE(FieldUpdated, v)) +// PhaseIsNil applies the IsNil predicate on the "phase" field. +func PhaseIsNil() predicate.Agent { + return predicate.Agent(sql.FieldIsNull(FieldPhase)) +} + +// PhaseNotNil applies the NotNil predicate on the "phase" field. +func PhaseNotNil() predicate.Agent { + return predicate.Agent(sql.FieldNotNull(FieldPhase)) +} + +// PhaseEqualFold applies the EqualFold predicate on the "phase" field. +func PhaseEqualFold(v string) predicate.Agent { + return predicate.Agent(sql.FieldEqualFold(FieldPhase, v)) +} + +// PhaseContainsFold applies the ContainsFold predicate on the "phase" field. +func PhaseContainsFold(v string) predicate.Agent { + return predicate.Agent(sql.FieldContainsFold(FieldPhase, v)) +} + +// ActivityEQ applies the EQ predicate on the "activity" field. +func ActivityEQ(v string) predicate.Agent { + return predicate.Agent(sql.FieldEQ(FieldActivity, v)) +} + +// ActivityNEQ applies the NEQ predicate on the "activity" field. +func ActivityNEQ(v string) predicate.Agent { + return predicate.Agent(sql.FieldNEQ(FieldActivity, v)) +} + +// ActivityIn applies the In predicate on the "activity" field. +func ActivityIn(vs ...string) predicate.Agent { + return predicate.Agent(sql.FieldIn(FieldActivity, vs...)) +} + +// ActivityNotIn applies the NotIn predicate on the "activity" field. +func ActivityNotIn(vs ...string) predicate.Agent { + return predicate.Agent(sql.FieldNotIn(FieldActivity, vs...)) +} + +// ActivityGT applies the GT predicate on the "activity" field. +func ActivityGT(v string) predicate.Agent { + return predicate.Agent(sql.FieldGT(FieldActivity, v)) +} + +// ActivityGTE applies the GTE predicate on the "activity" field. +func ActivityGTE(v string) predicate.Agent { + return predicate.Agent(sql.FieldGTE(FieldActivity, v)) +} + +// ActivityLT applies the LT predicate on the "activity" field. +func ActivityLT(v string) predicate.Agent { + return predicate.Agent(sql.FieldLT(FieldActivity, v)) +} + +// ActivityLTE applies the LTE predicate on the "activity" field. +func ActivityLTE(v string) predicate.Agent { + return predicate.Agent(sql.FieldLTE(FieldActivity, v)) +} + +// ActivityContains applies the Contains predicate on the "activity" field. +func ActivityContains(v string) predicate.Agent { + return predicate.Agent(sql.FieldContains(FieldActivity, v)) +} + +// ActivityHasPrefix applies the HasPrefix predicate on the "activity" field. +func ActivityHasPrefix(v string) predicate.Agent { + return predicate.Agent(sql.FieldHasPrefix(FieldActivity, v)) +} + +// ActivityHasSuffix applies the HasSuffix predicate on the "activity" field. +func ActivityHasSuffix(v string) predicate.Agent { + return predicate.Agent(sql.FieldHasSuffix(FieldActivity, v)) +} + +// ActivityIsNil applies the IsNil predicate on the "activity" field. +func ActivityIsNil() predicate.Agent { + return predicate.Agent(sql.FieldIsNull(FieldActivity)) +} + +// ActivityNotNil applies the NotNil predicate on the "activity" field. +func ActivityNotNil() predicate.Agent { + return predicate.Agent(sql.FieldNotNull(FieldActivity)) +} + +// ActivityEqualFold applies the EqualFold predicate on the "activity" field. +func ActivityEqualFold(v string) predicate.Agent { + return predicate.Agent(sql.FieldEqualFold(FieldActivity, v)) +} + +// ActivityContainsFold applies the ContainsFold predicate on the "activity" field. +func ActivityContainsFold(v string) predicate.Agent { + return predicate.Agent(sql.FieldContainsFold(FieldActivity, v)) +} + +// ToolNameEQ applies the EQ predicate on the "tool_name" field. +func ToolNameEQ(v string) predicate.Agent { + return predicate.Agent(sql.FieldEQ(FieldToolName, v)) +} + +// ToolNameNEQ applies the NEQ predicate on the "tool_name" field. +func ToolNameNEQ(v string) predicate.Agent { + return predicate.Agent(sql.FieldNEQ(FieldToolName, v)) +} + +// ToolNameIn applies the In predicate on the "tool_name" field. +func ToolNameIn(vs ...string) predicate.Agent { + return predicate.Agent(sql.FieldIn(FieldToolName, vs...)) +} + +// ToolNameNotIn applies the NotIn predicate on the "tool_name" field. +func ToolNameNotIn(vs ...string) predicate.Agent { + return predicate.Agent(sql.FieldNotIn(FieldToolName, vs...)) +} + +// ToolNameGT applies the GT predicate on the "tool_name" field. +func ToolNameGT(v string) predicate.Agent { + return predicate.Agent(sql.FieldGT(FieldToolName, v)) +} + +// ToolNameGTE applies the GTE predicate on the "tool_name" field. +func ToolNameGTE(v string) predicate.Agent { + return predicate.Agent(sql.FieldGTE(FieldToolName, v)) +} + +// ToolNameLT applies the LT predicate on the "tool_name" field. +func ToolNameLT(v string) predicate.Agent { + return predicate.Agent(sql.FieldLT(FieldToolName, v)) +} + +// ToolNameLTE applies the LTE predicate on the "tool_name" field. +func ToolNameLTE(v string) predicate.Agent { + return predicate.Agent(sql.FieldLTE(FieldToolName, v)) +} + +// ToolNameContains applies the Contains predicate on the "tool_name" field. +func ToolNameContains(v string) predicate.Agent { + return predicate.Agent(sql.FieldContains(FieldToolName, v)) +} + +// ToolNameHasPrefix applies the HasPrefix predicate on the "tool_name" field. +func ToolNameHasPrefix(v string) predicate.Agent { + return predicate.Agent(sql.FieldHasPrefix(FieldToolName, v)) +} + +// ToolNameHasSuffix applies the HasSuffix predicate on the "tool_name" field. +func ToolNameHasSuffix(v string) predicate.Agent { + return predicate.Agent(sql.FieldHasSuffix(FieldToolName, v)) +} + +// ToolNameIsNil applies the IsNil predicate on the "tool_name" field. +func ToolNameIsNil() predicate.Agent { + return predicate.Agent(sql.FieldIsNull(FieldToolName)) +} + +// ToolNameNotNil applies the NotNil predicate on the "tool_name" field. +func ToolNameNotNil() predicate.Agent { + return predicate.Agent(sql.FieldNotNull(FieldToolName)) +} + +// ToolNameEqualFold applies the EqualFold predicate on the "tool_name" field. +func ToolNameEqualFold(v string) predicate.Agent { + return predicate.Agent(sql.FieldEqualFold(FieldToolName, v)) +} + +// ToolNameContainsFold applies the ContainsFold predicate on the "tool_name" field. +func ToolNameContainsFold(v string) predicate.Agent { + return predicate.Agent(sql.FieldContainsFold(FieldToolName, v)) +} + +// ConnectionStateEQ applies the EQ predicate on the "connection_state" field. +func ConnectionStateEQ(v string) predicate.Agent { + return predicate.Agent(sql.FieldEQ(FieldConnectionState, v)) +} + +// ConnectionStateNEQ applies the NEQ predicate on the "connection_state" field. +func ConnectionStateNEQ(v string) predicate.Agent { + return predicate.Agent(sql.FieldNEQ(FieldConnectionState, v)) +} + +// ConnectionStateIn applies the In predicate on the "connection_state" field. +func ConnectionStateIn(vs ...string) predicate.Agent { + return predicate.Agent(sql.FieldIn(FieldConnectionState, vs...)) +} + +// ConnectionStateNotIn applies the NotIn predicate on the "connection_state" field. +func ConnectionStateNotIn(vs ...string) predicate.Agent { + return predicate.Agent(sql.FieldNotIn(FieldConnectionState, vs...)) +} + +// ConnectionStateGT applies the GT predicate on the "connection_state" field. +func ConnectionStateGT(v string) predicate.Agent { + return predicate.Agent(sql.FieldGT(FieldConnectionState, v)) +} + +// ConnectionStateGTE applies the GTE predicate on the "connection_state" field. +func ConnectionStateGTE(v string) predicate.Agent { + return predicate.Agent(sql.FieldGTE(FieldConnectionState, v)) +} + +// ConnectionStateLT applies the LT predicate on the "connection_state" field. +func ConnectionStateLT(v string) predicate.Agent { + return predicate.Agent(sql.FieldLT(FieldConnectionState, v)) +} + +// ConnectionStateLTE applies the LTE predicate on the "connection_state" field. +func ConnectionStateLTE(v string) predicate.Agent { + return predicate.Agent(sql.FieldLTE(FieldConnectionState, v)) +} + +// ConnectionStateContains applies the Contains predicate on the "connection_state" field. +func ConnectionStateContains(v string) predicate.Agent { + return predicate.Agent(sql.FieldContains(FieldConnectionState, v)) +} + +// ConnectionStateHasPrefix applies the HasPrefix predicate on the "connection_state" field. +func ConnectionStateHasPrefix(v string) predicate.Agent { + return predicate.Agent(sql.FieldHasPrefix(FieldConnectionState, v)) +} + +// ConnectionStateHasSuffix applies the HasSuffix predicate on the "connection_state" field. +func ConnectionStateHasSuffix(v string) predicate.Agent { + return predicate.Agent(sql.FieldHasSuffix(FieldConnectionState, v)) +} + +// ConnectionStateIsNil applies the IsNil predicate on the "connection_state" field. +func ConnectionStateIsNil() predicate.Agent { + return predicate.Agent(sql.FieldIsNull(FieldConnectionState)) +} + +// ConnectionStateNotNil applies the NotNil predicate on the "connection_state" field. +func ConnectionStateNotNil() predicate.Agent { + return predicate.Agent(sql.FieldNotNull(FieldConnectionState)) +} + +// ConnectionStateEqualFold applies the EqualFold predicate on the "connection_state" field. +func ConnectionStateEqualFold(v string) predicate.Agent { + return predicate.Agent(sql.FieldEqualFold(FieldConnectionState, v)) +} + +// ConnectionStateContainsFold applies the ContainsFold predicate on the "connection_state" field. +func ConnectionStateContainsFold(v string) predicate.Agent { + return predicate.Agent(sql.FieldContainsFold(FieldConnectionState, v)) +} + +// ContainerStatusEQ applies the EQ predicate on the "container_status" field. +func ContainerStatusEQ(v string) predicate.Agent { + return predicate.Agent(sql.FieldEQ(FieldContainerStatus, v)) +} + +// ContainerStatusNEQ applies the NEQ predicate on the "container_status" field. +func ContainerStatusNEQ(v string) predicate.Agent { + return predicate.Agent(sql.FieldNEQ(FieldContainerStatus, v)) +} + +// ContainerStatusIn applies the In predicate on the "container_status" field. +func ContainerStatusIn(vs ...string) predicate.Agent { + return predicate.Agent(sql.FieldIn(FieldContainerStatus, vs...)) +} + +// ContainerStatusNotIn applies the NotIn predicate on the "container_status" field. +func ContainerStatusNotIn(vs ...string) predicate.Agent { + return predicate.Agent(sql.FieldNotIn(FieldContainerStatus, vs...)) +} + +// ContainerStatusGT applies the GT predicate on the "container_status" field. +func ContainerStatusGT(v string) predicate.Agent { + return predicate.Agent(sql.FieldGT(FieldContainerStatus, v)) +} + +// ContainerStatusGTE applies the GTE predicate on the "container_status" field. +func ContainerStatusGTE(v string) predicate.Agent { + return predicate.Agent(sql.FieldGTE(FieldContainerStatus, v)) +} + +// ContainerStatusLT applies the LT predicate on the "container_status" field. +func ContainerStatusLT(v string) predicate.Agent { + return predicate.Agent(sql.FieldLT(FieldContainerStatus, v)) +} + +// ContainerStatusLTE applies the LTE predicate on the "container_status" field. +func ContainerStatusLTE(v string) predicate.Agent { + return predicate.Agent(sql.FieldLTE(FieldContainerStatus, v)) +} + +// ContainerStatusContains applies the Contains predicate on the "container_status" field. +func ContainerStatusContains(v string) predicate.Agent { + return predicate.Agent(sql.FieldContains(FieldContainerStatus, v)) +} + +// ContainerStatusHasPrefix applies the HasPrefix predicate on the "container_status" field. +func ContainerStatusHasPrefix(v string) predicate.Agent { + return predicate.Agent(sql.FieldHasPrefix(FieldContainerStatus, v)) +} + +// ContainerStatusHasSuffix applies the HasSuffix predicate on the "container_status" field. +func ContainerStatusHasSuffix(v string) predicate.Agent { + return predicate.Agent(sql.FieldHasSuffix(FieldContainerStatus, v)) +} + +// ContainerStatusIsNil applies the IsNil predicate on the "container_status" field. +func ContainerStatusIsNil() predicate.Agent { + return predicate.Agent(sql.FieldIsNull(FieldContainerStatus)) +} + +// ContainerStatusNotNil applies the NotNil predicate on the "container_status" field. +func ContainerStatusNotNil() predicate.Agent { + return predicate.Agent(sql.FieldNotNull(FieldContainerStatus)) +} + +// ContainerStatusEqualFold applies the EqualFold predicate on the "container_status" field. +func ContainerStatusEqualFold(v string) predicate.Agent { + return predicate.Agent(sql.FieldEqualFold(FieldContainerStatus, v)) +} + +// ContainerStatusContainsFold applies the ContainsFold predicate on the "container_status" field. +func ContainerStatusContainsFold(v string) predicate.Agent { + return predicate.Agent(sql.FieldContainsFold(FieldContainerStatus, v)) +} + +// RuntimeStateEQ applies the EQ predicate on the "runtime_state" field. +func RuntimeStateEQ(v string) predicate.Agent { + return predicate.Agent(sql.FieldEQ(FieldRuntimeState, v)) +} + +// RuntimeStateNEQ applies the NEQ predicate on the "runtime_state" field. +func RuntimeStateNEQ(v string) predicate.Agent { + return predicate.Agent(sql.FieldNEQ(FieldRuntimeState, v)) +} + +// RuntimeStateIn applies the In predicate on the "runtime_state" field. +func RuntimeStateIn(vs ...string) predicate.Agent { + return predicate.Agent(sql.FieldIn(FieldRuntimeState, vs...)) +} + +// RuntimeStateNotIn applies the NotIn predicate on the "runtime_state" field. +func RuntimeStateNotIn(vs ...string) predicate.Agent { + return predicate.Agent(sql.FieldNotIn(FieldRuntimeState, vs...)) +} + +// RuntimeStateGT applies the GT predicate on the "runtime_state" field. +func RuntimeStateGT(v string) predicate.Agent { + return predicate.Agent(sql.FieldGT(FieldRuntimeState, v)) +} + +// RuntimeStateGTE applies the GTE predicate on the "runtime_state" field. +func RuntimeStateGTE(v string) predicate.Agent { + return predicate.Agent(sql.FieldGTE(FieldRuntimeState, v)) +} + +// RuntimeStateLT applies the LT predicate on the "runtime_state" field. +func RuntimeStateLT(v string) predicate.Agent { + return predicate.Agent(sql.FieldLT(FieldRuntimeState, v)) +} + +// RuntimeStateLTE applies the LTE predicate on the "runtime_state" field. +func RuntimeStateLTE(v string) predicate.Agent { + return predicate.Agent(sql.FieldLTE(FieldRuntimeState, v)) +} + +// RuntimeStateContains applies the Contains predicate on the "runtime_state" field. +func RuntimeStateContains(v string) predicate.Agent { + return predicate.Agent(sql.FieldContains(FieldRuntimeState, v)) +} + +// RuntimeStateHasPrefix applies the HasPrefix predicate on the "runtime_state" field. +func RuntimeStateHasPrefix(v string) predicate.Agent { + return predicate.Agent(sql.FieldHasPrefix(FieldRuntimeState, v)) +} + +// RuntimeStateHasSuffix applies the HasSuffix predicate on the "runtime_state" field. +func RuntimeStateHasSuffix(v string) predicate.Agent { + return predicate.Agent(sql.FieldHasSuffix(FieldRuntimeState, v)) +} + +// RuntimeStateIsNil applies the IsNil predicate on the "runtime_state" field. +func RuntimeStateIsNil() predicate.Agent { + return predicate.Agent(sql.FieldIsNull(FieldRuntimeState)) +} + +// RuntimeStateNotNil applies the NotNil predicate on the "runtime_state" field. +func RuntimeStateNotNil() predicate.Agent { + return predicate.Agent(sql.FieldNotNull(FieldRuntimeState)) +} + +// RuntimeStateEqualFold applies the EqualFold predicate on the "runtime_state" field. +func RuntimeStateEqualFold(v string) predicate.Agent { + return predicate.Agent(sql.FieldEqualFold(FieldRuntimeState, v)) +} + +// RuntimeStateContainsFold applies the ContainsFold predicate on the "runtime_state" field. +func RuntimeStateContainsFold(v string) predicate.Agent { + return predicate.Agent(sql.FieldContainsFold(FieldRuntimeState, v)) +} + +// StalledFromActivityEQ applies the EQ predicate on the "stalled_from_activity" field. +func StalledFromActivityEQ(v string) predicate.Agent { + return predicate.Agent(sql.FieldEQ(FieldStalledFromActivity, v)) +} + +// StalledFromActivityNEQ applies the NEQ predicate on the "stalled_from_activity" field. +func StalledFromActivityNEQ(v string) predicate.Agent { + return predicate.Agent(sql.FieldNEQ(FieldStalledFromActivity, v)) +} + +// StalledFromActivityIn applies the In predicate on the "stalled_from_activity" field. +func StalledFromActivityIn(vs ...string) predicate.Agent { + return predicate.Agent(sql.FieldIn(FieldStalledFromActivity, vs...)) +} + +// StalledFromActivityNotIn applies the NotIn predicate on the "stalled_from_activity" field. +func StalledFromActivityNotIn(vs ...string) predicate.Agent { + return predicate.Agent(sql.FieldNotIn(FieldStalledFromActivity, vs...)) +} + +// StalledFromActivityGT applies the GT predicate on the "stalled_from_activity" field. +func StalledFromActivityGT(v string) predicate.Agent { + return predicate.Agent(sql.FieldGT(FieldStalledFromActivity, v)) +} + +// StalledFromActivityGTE applies the GTE predicate on the "stalled_from_activity" field. +func StalledFromActivityGTE(v string) predicate.Agent { + return predicate.Agent(sql.FieldGTE(FieldStalledFromActivity, v)) +} + +// StalledFromActivityLT applies the LT predicate on the "stalled_from_activity" field. +func StalledFromActivityLT(v string) predicate.Agent { + return predicate.Agent(sql.FieldLT(FieldStalledFromActivity, v)) +} + +// StalledFromActivityLTE applies the LTE predicate on the "stalled_from_activity" field. +func StalledFromActivityLTE(v string) predicate.Agent { + return predicate.Agent(sql.FieldLTE(FieldStalledFromActivity, v)) +} + +// StalledFromActivityContains applies the Contains predicate on the "stalled_from_activity" field. +func StalledFromActivityContains(v string) predicate.Agent { + return predicate.Agent(sql.FieldContains(FieldStalledFromActivity, v)) +} + +// StalledFromActivityHasPrefix applies the HasPrefix predicate on the "stalled_from_activity" field. +func StalledFromActivityHasPrefix(v string) predicate.Agent { + return predicate.Agent(sql.FieldHasPrefix(FieldStalledFromActivity, v)) +} + +// StalledFromActivityHasSuffix applies the HasSuffix predicate on the "stalled_from_activity" field. +func StalledFromActivityHasSuffix(v string) predicate.Agent { + return predicate.Agent(sql.FieldHasSuffix(FieldStalledFromActivity, v)) +} + +// StalledFromActivityIsNil applies the IsNil predicate on the "stalled_from_activity" field. +func StalledFromActivityIsNil() predicate.Agent { + return predicate.Agent(sql.FieldIsNull(FieldStalledFromActivity)) +} + +// StalledFromActivityNotNil applies the NotNil predicate on the "stalled_from_activity" field. +func StalledFromActivityNotNil() predicate.Agent { + return predicate.Agent(sql.FieldNotNull(FieldStalledFromActivity)) +} + +// StalledFromActivityEqualFold applies the EqualFold predicate on the "stalled_from_activity" field. +func StalledFromActivityEqualFold(v string) predicate.Agent { + return predicate.Agent(sql.FieldEqualFold(FieldStalledFromActivity, v)) +} + +// StalledFromActivityContainsFold applies the ContainsFold predicate on the "stalled_from_activity" field. +func StalledFromActivityContainsFold(v string) predicate.Agent { + return predicate.Agent(sql.FieldContainsFold(FieldStalledFromActivity, v)) +} + +// CurrentTurnsEQ applies the EQ predicate on the "current_turns" field. +func CurrentTurnsEQ(v int) predicate.Agent { + return predicate.Agent(sql.FieldEQ(FieldCurrentTurns, v)) +} + +// CurrentTurnsNEQ applies the NEQ predicate on the "current_turns" field. +func CurrentTurnsNEQ(v int) predicate.Agent { + return predicate.Agent(sql.FieldNEQ(FieldCurrentTurns, v)) +} + +// CurrentTurnsIn applies the In predicate on the "current_turns" field. +func CurrentTurnsIn(vs ...int) predicate.Agent { + return predicate.Agent(sql.FieldIn(FieldCurrentTurns, vs...)) +} + +// CurrentTurnsNotIn applies the NotIn predicate on the "current_turns" field. +func CurrentTurnsNotIn(vs ...int) predicate.Agent { + return predicate.Agent(sql.FieldNotIn(FieldCurrentTurns, vs...)) +} + +// CurrentTurnsGT applies the GT predicate on the "current_turns" field. +func CurrentTurnsGT(v int) predicate.Agent { + return predicate.Agent(sql.FieldGT(FieldCurrentTurns, v)) +} + +// CurrentTurnsGTE applies the GTE predicate on the "current_turns" field. +func CurrentTurnsGTE(v int) predicate.Agent { + return predicate.Agent(sql.FieldGTE(FieldCurrentTurns, v)) +} + +// CurrentTurnsLT applies the LT predicate on the "current_turns" field. +func CurrentTurnsLT(v int) predicate.Agent { + return predicate.Agent(sql.FieldLT(FieldCurrentTurns, v)) +} + +// CurrentTurnsLTE applies the LTE predicate on the "current_turns" field. +func CurrentTurnsLTE(v int) predicate.Agent { + return predicate.Agent(sql.FieldLTE(FieldCurrentTurns, v)) +} + +// CurrentModelCallsEQ applies the EQ predicate on the "current_model_calls" field. +func CurrentModelCallsEQ(v int) predicate.Agent { + return predicate.Agent(sql.FieldEQ(FieldCurrentModelCalls, v)) +} + +// CurrentModelCallsNEQ applies the NEQ predicate on the "current_model_calls" field. +func CurrentModelCallsNEQ(v int) predicate.Agent { + return predicate.Agent(sql.FieldNEQ(FieldCurrentModelCalls, v)) +} + +// CurrentModelCallsIn applies the In predicate on the "current_model_calls" field. +func CurrentModelCallsIn(vs ...int) predicate.Agent { + return predicate.Agent(sql.FieldIn(FieldCurrentModelCalls, vs...)) +} + +// CurrentModelCallsNotIn applies the NotIn predicate on the "current_model_calls" field. +func CurrentModelCallsNotIn(vs ...int) predicate.Agent { + return predicate.Agent(sql.FieldNotIn(FieldCurrentModelCalls, vs...)) +} + +// CurrentModelCallsGT applies the GT predicate on the "current_model_calls" field. +func CurrentModelCallsGT(v int) predicate.Agent { + return predicate.Agent(sql.FieldGT(FieldCurrentModelCalls, v)) +} + +// CurrentModelCallsGTE applies the GTE predicate on the "current_model_calls" field. +func CurrentModelCallsGTE(v int) predicate.Agent { + return predicate.Agent(sql.FieldGTE(FieldCurrentModelCalls, v)) +} + +// CurrentModelCallsLT applies the LT predicate on the "current_model_calls" field. +func CurrentModelCallsLT(v int) predicate.Agent { + return predicate.Agent(sql.FieldLT(FieldCurrentModelCalls, v)) +} + +// CurrentModelCallsLTE applies the LTE predicate on the "current_model_calls" field. +func CurrentModelCallsLTE(v int) predicate.Agent { + return predicate.Agent(sql.FieldLTE(FieldCurrentModelCalls, v)) +} + +// ImageEQ applies the EQ predicate on the "image" field. +func ImageEQ(v string) predicate.Agent { + return predicate.Agent(sql.FieldEQ(FieldImage, v)) +} + +// ImageNEQ applies the NEQ predicate on the "image" field. +func ImageNEQ(v string) predicate.Agent { + return predicate.Agent(sql.FieldNEQ(FieldImage, v)) +} + +// ImageIn applies the In predicate on the "image" field. +func ImageIn(vs ...string) predicate.Agent { + return predicate.Agent(sql.FieldIn(FieldImage, vs...)) +} + +// ImageNotIn applies the NotIn predicate on the "image" field. +func ImageNotIn(vs ...string) predicate.Agent { + return predicate.Agent(sql.FieldNotIn(FieldImage, vs...)) +} + +// ImageGT applies the GT predicate on the "image" field. +func ImageGT(v string) predicate.Agent { + return predicate.Agent(sql.FieldGT(FieldImage, v)) +} + +// ImageGTE applies the GTE predicate on the "image" field. +func ImageGTE(v string) predicate.Agent { + return predicate.Agent(sql.FieldGTE(FieldImage, v)) +} + +// ImageLT applies the LT predicate on the "image" field. +func ImageLT(v string) predicate.Agent { + return predicate.Agent(sql.FieldLT(FieldImage, v)) +} + +// ImageLTE applies the LTE predicate on the "image" field. +func ImageLTE(v string) predicate.Agent { + return predicate.Agent(sql.FieldLTE(FieldImage, v)) +} + +// ImageContains applies the Contains predicate on the "image" field. +func ImageContains(v string) predicate.Agent { + return predicate.Agent(sql.FieldContains(FieldImage, v)) +} + +// ImageHasPrefix applies the HasPrefix predicate on the "image" field. +func ImageHasPrefix(v string) predicate.Agent { + return predicate.Agent(sql.FieldHasPrefix(FieldImage, v)) +} + +// ImageHasSuffix applies the HasSuffix predicate on the "image" field. +func ImageHasSuffix(v string) predicate.Agent { + return predicate.Agent(sql.FieldHasSuffix(FieldImage, v)) +} + +// ImageIsNil applies the IsNil predicate on the "image" field. +func ImageIsNil() predicate.Agent { + return predicate.Agent(sql.FieldIsNull(FieldImage)) +} + +// ImageNotNil applies the NotNil predicate on the "image" field. +func ImageNotNil() predicate.Agent { + return predicate.Agent(sql.FieldNotNull(FieldImage)) +} + +// ImageEqualFold applies the EqualFold predicate on the "image" field. +func ImageEqualFold(v string) predicate.Agent { + return predicate.Agent(sql.FieldEqualFold(FieldImage, v)) +} + +// ImageContainsFold applies the ContainsFold predicate on the "image" field. +func ImageContainsFold(v string) predicate.Agent { + return predicate.Agent(sql.FieldContainsFold(FieldImage, v)) +} + +// DetachedEQ applies the EQ predicate on the "detached" field. +func DetachedEQ(v bool) predicate.Agent { + return predicate.Agent(sql.FieldEQ(FieldDetached, v)) +} + +// DetachedNEQ applies the NEQ predicate on the "detached" field. +func DetachedNEQ(v bool) predicate.Agent { + return predicate.Agent(sql.FieldNEQ(FieldDetached, v)) +} + +// RuntimeEQ applies the EQ predicate on the "runtime" field. +func RuntimeEQ(v string) predicate.Agent { + return predicate.Agent(sql.FieldEQ(FieldRuntime, v)) +} + +// RuntimeNEQ applies the NEQ predicate on the "runtime" field. +func RuntimeNEQ(v string) predicate.Agent { + return predicate.Agent(sql.FieldNEQ(FieldRuntime, v)) +} + +// RuntimeIn applies the In predicate on the "runtime" field. +func RuntimeIn(vs ...string) predicate.Agent { + return predicate.Agent(sql.FieldIn(FieldRuntime, vs...)) +} + +// RuntimeNotIn applies the NotIn predicate on the "runtime" field. +func RuntimeNotIn(vs ...string) predicate.Agent { + return predicate.Agent(sql.FieldNotIn(FieldRuntime, vs...)) +} + +// RuntimeGT applies the GT predicate on the "runtime" field. +func RuntimeGT(v string) predicate.Agent { + return predicate.Agent(sql.FieldGT(FieldRuntime, v)) +} + +// RuntimeGTE applies the GTE predicate on the "runtime" field. +func RuntimeGTE(v string) predicate.Agent { + return predicate.Agent(sql.FieldGTE(FieldRuntime, v)) +} + +// RuntimeLT applies the LT predicate on the "runtime" field. +func RuntimeLT(v string) predicate.Agent { + return predicate.Agent(sql.FieldLT(FieldRuntime, v)) +} + +// RuntimeLTE applies the LTE predicate on the "runtime" field. +func RuntimeLTE(v string) predicate.Agent { + return predicate.Agent(sql.FieldLTE(FieldRuntime, v)) +} + +// RuntimeContains applies the Contains predicate on the "runtime" field. +func RuntimeContains(v string) predicate.Agent { + return predicate.Agent(sql.FieldContains(FieldRuntime, v)) +} + +// RuntimeHasPrefix applies the HasPrefix predicate on the "runtime" field. +func RuntimeHasPrefix(v string) predicate.Agent { + return predicate.Agent(sql.FieldHasPrefix(FieldRuntime, v)) +} + +// RuntimeHasSuffix applies the HasSuffix predicate on the "runtime" field. +func RuntimeHasSuffix(v string) predicate.Agent { + return predicate.Agent(sql.FieldHasSuffix(FieldRuntime, v)) +} + +// RuntimeIsNil applies the IsNil predicate on the "runtime" field. +func RuntimeIsNil() predicate.Agent { + return predicate.Agent(sql.FieldIsNull(FieldRuntime)) +} + +// RuntimeNotNil applies the NotNil predicate on the "runtime" field. +func RuntimeNotNil() predicate.Agent { + return predicate.Agent(sql.FieldNotNull(FieldRuntime)) +} + +// RuntimeEqualFold applies the EqualFold predicate on the "runtime" field. +func RuntimeEqualFold(v string) predicate.Agent { + return predicate.Agent(sql.FieldEqualFold(FieldRuntime, v)) +} + +// RuntimeContainsFold applies the ContainsFold predicate on the "runtime" field. +func RuntimeContainsFold(v string) predicate.Agent { + return predicate.Agent(sql.FieldContainsFold(FieldRuntime, v)) +} + +// RuntimeBrokerIDEQ applies the EQ predicate on the "runtime_broker_id" field. +func RuntimeBrokerIDEQ(v string) predicate.Agent { + return predicate.Agent(sql.FieldEQ(FieldRuntimeBrokerID, v)) +} + +// RuntimeBrokerIDNEQ applies the NEQ predicate on the "runtime_broker_id" field. +func RuntimeBrokerIDNEQ(v string) predicate.Agent { + return predicate.Agent(sql.FieldNEQ(FieldRuntimeBrokerID, v)) +} + +// RuntimeBrokerIDIn applies the In predicate on the "runtime_broker_id" field. +func RuntimeBrokerIDIn(vs ...string) predicate.Agent { + return predicate.Agent(sql.FieldIn(FieldRuntimeBrokerID, vs...)) +} + +// RuntimeBrokerIDNotIn applies the NotIn predicate on the "runtime_broker_id" field. +func RuntimeBrokerIDNotIn(vs ...string) predicate.Agent { + return predicate.Agent(sql.FieldNotIn(FieldRuntimeBrokerID, vs...)) +} + +// RuntimeBrokerIDGT applies the GT predicate on the "runtime_broker_id" field. +func RuntimeBrokerIDGT(v string) predicate.Agent { + return predicate.Agent(sql.FieldGT(FieldRuntimeBrokerID, v)) +} + +// RuntimeBrokerIDGTE applies the GTE predicate on the "runtime_broker_id" field. +func RuntimeBrokerIDGTE(v string) predicate.Agent { + return predicate.Agent(sql.FieldGTE(FieldRuntimeBrokerID, v)) +} + +// RuntimeBrokerIDLT applies the LT predicate on the "runtime_broker_id" field. +func RuntimeBrokerIDLT(v string) predicate.Agent { + return predicate.Agent(sql.FieldLT(FieldRuntimeBrokerID, v)) +} + +// RuntimeBrokerIDLTE applies the LTE predicate on the "runtime_broker_id" field. +func RuntimeBrokerIDLTE(v string) predicate.Agent { + return predicate.Agent(sql.FieldLTE(FieldRuntimeBrokerID, v)) +} + +// RuntimeBrokerIDContains applies the Contains predicate on the "runtime_broker_id" field. +func RuntimeBrokerIDContains(v string) predicate.Agent { + return predicate.Agent(sql.FieldContains(FieldRuntimeBrokerID, v)) +} + +// RuntimeBrokerIDHasPrefix applies the HasPrefix predicate on the "runtime_broker_id" field. +func RuntimeBrokerIDHasPrefix(v string) predicate.Agent { + return predicate.Agent(sql.FieldHasPrefix(FieldRuntimeBrokerID, v)) +} + +// RuntimeBrokerIDHasSuffix applies the HasSuffix predicate on the "runtime_broker_id" field. +func RuntimeBrokerIDHasSuffix(v string) predicate.Agent { + return predicate.Agent(sql.FieldHasSuffix(FieldRuntimeBrokerID, v)) +} + +// RuntimeBrokerIDIsNil applies the IsNil predicate on the "runtime_broker_id" field. +func RuntimeBrokerIDIsNil() predicate.Agent { + return predicate.Agent(sql.FieldIsNull(FieldRuntimeBrokerID)) +} + +// RuntimeBrokerIDNotNil applies the NotNil predicate on the "runtime_broker_id" field. +func RuntimeBrokerIDNotNil() predicate.Agent { + return predicate.Agent(sql.FieldNotNull(FieldRuntimeBrokerID)) +} + +// RuntimeBrokerIDEqualFold applies the EqualFold predicate on the "runtime_broker_id" field. +func RuntimeBrokerIDEqualFold(v string) predicate.Agent { + return predicate.Agent(sql.FieldEqualFold(FieldRuntimeBrokerID, v)) +} + +// RuntimeBrokerIDContainsFold applies the ContainsFold predicate on the "runtime_broker_id" field. +func RuntimeBrokerIDContainsFold(v string) predicate.Agent { + return predicate.Agent(sql.FieldContainsFold(FieldRuntimeBrokerID, v)) +} + +// WebPtyEnabledEQ applies the EQ predicate on the "web_pty_enabled" field. +func WebPtyEnabledEQ(v bool) predicate.Agent { + return predicate.Agent(sql.FieldEQ(FieldWebPtyEnabled, v)) +} + +// WebPtyEnabledNEQ applies the NEQ predicate on the "web_pty_enabled" field. +func WebPtyEnabledNEQ(v bool) predicate.Agent { + return predicate.Agent(sql.FieldNEQ(FieldWebPtyEnabled, v)) +} + +// TaskSummaryEQ applies the EQ predicate on the "task_summary" field. +func TaskSummaryEQ(v string) predicate.Agent { + return predicate.Agent(sql.FieldEQ(FieldTaskSummary, v)) +} + +// TaskSummaryNEQ applies the NEQ predicate on the "task_summary" field. +func TaskSummaryNEQ(v string) predicate.Agent { + return predicate.Agent(sql.FieldNEQ(FieldTaskSummary, v)) +} + +// TaskSummaryIn applies the In predicate on the "task_summary" field. +func TaskSummaryIn(vs ...string) predicate.Agent { + return predicate.Agent(sql.FieldIn(FieldTaskSummary, vs...)) +} + +// TaskSummaryNotIn applies the NotIn predicate on the "task_summary" field. +func TaskSummaryNotIn(vs ...string) predicate.Agent { + return predicate.Agent(sql.FieldNotIn(FieldTaskSummary, vs...)) +} + +// TaskSummaryGT applies the GT predicate on the "task_summary" field. +func TaskSummaryGT(v string) predicate.Agent { + return predicate.Agent(sql.FieldGT(FieldTaskSummary, v)) +} + +// TaskSummaryGTE applies the GTE predicate on the "task_summary" field. +func TaskSummaryGTE(v string) predicate.Agent { + return predicate.Agent(sql.FieldGTE(FieldTaskSummary, v)) +} + +// TaskSummaryLT applies the LT predicate on the "task_summary" field. +func TaskSummaryLT(v string) predicate.Agent { + return predicate.Agent(sql.FieldLT(FieldTaskSummary, v)) +} + +// TaskSummaryLTE applies the LTE predicate on the "task_summary" field. +func TaskSummaryLTE(v string) predicate.Agent { + return predicate.Agent(sql.FieldLTE(FieldTaskSummary, v)) +} + +// TaskSummaryContains applies the Contains predicate on the "task_summary" field. +func TaskSummaryContains(v string) predicate.Agent { + return predicate.Agent(sql.FieldContains(FieldTaskSummary, v)) +} + +// TaskSummaryHasPrefix applies the HasPrefix predicate on the "task_summary" field. +func TaskSummaryHasPrefix(v string) predicate.Agent { + return predicate.Agent(sql.FieldHasPrefix(FieldTaskSummary, v)) +} + +// TaskSummaryHasSuffix applies the HasSuffix predicate on the "task_summary" field. +func TaskSummaryHasSuffix(v string) predicate.Agent { + return predicate.Agent(sql.FieldHasSuffix(FieldTaskSummary, v)) +} + +// TaskSummaryIsNil applies the IsNil predicate on the "task_summary" field. +func TaskSummaryIsNil() predicate.Agent { + return predicate.Agent(sql.FieldIsNull(FieldTaskSummary)) +} + +// TaskSummaryNotNil applies the NotNil predicate on the "task_summary" field. +func TaskSummaryNotNil() predicate.Agent { + return predicate.Agent(sql.FieldNotNull(FieldTaskSummary)) +} + +// TaskSummaryEqualFold applies the EqualFold predicate on the "task_summary" field. +func TaskSummaryEqualFold(v string) predicate.Agent { + return predicate.Agent(sql.FieldEqualFold(FieldTaskSummary, v)) +} + +// TaskSummaryContainsFold applies the ContainsFold predicate on the "task_summary" field. +func TaskSummaryContainsFold(v string) predicate.Agent { + return predicate.Agent(sql.FieldContainsFold(FieldTaskSummary, v)) +} + +// MessageEQ applies the EQ predicate on the "message" field. +func MessageEQ(v string) predicate.Agent { + return predicate.Agent(sql.FieldEQ(FieldMessage, v)) +} + +// MessageNEQ applies the NEQ predicate on the "message" field. +func MessageNEQ(v string) predicate.Agent { + return predicate.Agent(sql.FieldNEQ(FieldMessage, v)) +} + +// MessageIn applies the In predicate on the "message" field. +func MessageIn(vs ...string) predicate.Agent { + return predicate.Agent(sql.FieldIn(FieldMessage, vs...)) +} + +// MessageNotIn applies the NotIn predicate on the "message" field. +func MessageNotIn(vs ...string) predicate.Agent { + return predicate.Agent(sql.FieldNotIn(FieldMessage, vs...)) +} + +// MessageGT applies the GT predicate on the "message" field. +func MessageGT(v string) predicate.Agent { + return predicate.Agent(sql.FieldGT(FieldMessage, v)) +} + +// MessageGTE applies the GTE predicate on the "message" field. +func MessageGTE(v string) predicate.Agent { + return predicate.Agent(sql.FieldGTE(FieldMessage, v)) +} + +// MessageLT applies the LT predicate on the "message" field. +func MessageLT(v string) predicate.Agent { + return predicate.Agent(sql.FieldLT(FieldMessage, v)) +} + +// MessageLTE applies the LTE predicate on the "message" field. +func MessageLTE(v string) predicate.Agent { + return predicate.Agent(sql.FieldLTE(FieldMessage, v)) +} + +// MessageContains applies the Contains predicate on the "message" field. +func MessageContains(v string) predicate.Agent { + return predicate.Agent(sql.FieldContains(FieldMessage, v)) +} + +// MessageHasPrefix applies the HasPrefix predicate on the "message" field. +func MessageHasPrefix(v string) predicate.Agent { + return predicate.Agent(sql.FieldHasPrefix(FieldMessage, v)) +} + +// MessageHasSuffix applies the HasSuffix predicate on the "message" field. +func MessageHasSuffix(v string) predicate.Agent { + return predicate.Agent(sql.FieldHasSuffix(FieldMessage, v)) +} + +// MessageIsNil applies the IsNil predicate on the "message" field. +func MessageIsNil() predicate.Agent { + return predicate.Agent(sql.FieldIsNull(FieldMessage)) +} + +// MessageNotNil applies the NotNil predicate on the "message" field. +func MessageNotNil() predicate.Agent { + return predicate.Agent(sql.FieldNotNull(FieldMessage)) +} + +// MessageEqualFold applies the EqualFold predicate on the "message" field. +func MessageEqualFold(v string) predicate.Agent { + return predicate.Agent(sql.FieldEqualFold(FieldMessage, v)) +} + +// MessageContainsFold applies the ContainsFold predicate on the "message" field. +func MessageContainsFold(v string) predicate.Agent { + return predicate.Agent(sql.FieldContainsFold(FieldMessage, v)) +} + +// AppliedConfigEQ applies the EQ predicate on the "applied_config" field. +func AppliedConfigEQ(v string) predicate.Agent { + return predicate.Agent(sql.FieldEQ(FieldAppliedConfig, v)) +} + +// AppliedConfigNEQ applies the NEQ predicate on the "applied_config" field. +func AppliedConfigNEQ(v string) predicate.Agent { + return predicate.Agent(sql.FieldNEQ(FieldAppliedConfig, v)) +} + +// AppliedConfigIn applies the In predicate on the "applied_config" field. +func AppliedConfigIn(vs ...string) predicate.Agent { + return predicate.Agent(sql.FieldIn(FieldAppliedConfig, vs...)) +} + +// AppliedConfigNotIn applies the NotIn predicate on the "applied_config" field. +func AppliedConfigNotIn(vs ...string) predicate.Agent { + return predicate.Agent(sql.FieldNotIn(FieldAppliedConfig, vs...)) +} + +// AppliedConfigGT applies the GT predicate on the "applied_config" field. +func AppliedConfigGT(v string) predicate.Agent { + return predicate.Agent(sql.FieldGT(FieldAppliedConfig, v)) +} + +// AppliedConfigGTE applies the GTE predicate on the "applied_config" field. +func AppliedConfigGTE(v string) predicate.Agent { + return predicate.Agent(sql.FieldGTE(FieldAppliedConfig, v)) +} + +// AppliedConfigLT applies the LT predicate on the "applied_config" field. +func AppliedConfigLT(v string) predicate.Agent { + return predicate.Agent(sql.FieldLT(FieldAppliedConfig, v)) +} + +// AppliedConfigLTE applies the LTE predicate on the "applied_config" field. +func AppliedConfigLTE(v string) predicate.Agent { + return predicate.Agent(sql.FieldLTE(FieldAppliedConfig, v)) +} + +// AppliedConfigContains applies the Contains predicate on the "applied_config" field. +func AppliedConfigContains(v string) predicate.Agent { + return predicate.Agent(sql.FieldContains(FieldAppliedConfig, v)) +} + +// AppliedConfigHasPrefix applies the HasPrefix predicate on the "applied_config" field. +func AppliedConfigHasPrefix(v string) predicate.Agent { + return predicate.Agent(sql.FieldHasPrefix(FieldAppliedConfig, v)) +} + +// AppliedConfigHasSuffix applies the HasSuffix predicate on the "applied_config" field. +func AppliedConfigHasSuffix(v string) predicate.Agent { + return predicate.Agent(sql.FieldHasSuffix(FieldAppliedConfig, v)) +} + +// AppliedConfigIsNil applies the IsNil predicate on the "applied_config" field. +func AppliedConfigIsNil() predicate.Agent { + return predicate.Agent(sql.FieldIsNull(FieldAppliedConfig)) +} + +// AppliedConfigNotNil applies the NotNil predicate on the "applied_config" field. +func AppliedConfigNotNil() predicate.Agent { + return predicate.Agent(sql.FieldNotNull(FieldAppliedConfig)) +} + +// AppliedConfigEqualFold applies the EqualFold predicate on the "applied_config" field. +func AppliedConfigEqualFold(v string) predicate.Agent { + return predicate.Agent(sql.FieldEqualFold(FieldAppliedConfig, v)) +} + +// AppliedConfigContainsFold applies the ContainsFold predicate on the "applied_config" field. +func AppliedConfigContainsFold(v string) predicate.Agent { + return predicate.Agent(sql.FieldContainsFold(FieldAppliedConfig, v)) +} + +// AncestryIsNil applies the IsNil predicate on the "ancestry" field. +func AncestryIsNil() predicate.Agent { + return predicate.Agent(sql.FieldIsNull(FieldAncestry)) +} + +// AncestryNotNil applies the NotNil predicate on the "ancestry" field. +func AncestryNotNil() predicate.Agent { + return predicate.Agent(sql.FieldNotNull(FieldAncestry)) +} + +// CreatedEQ applies the EQ predicate on the "created" field. +func CreatedEQ(v time.Time) predicate.Agent { + return predicate.Agent(sql.FieldEQ(FieldCreated, v)) +} + +// CreatedNEQ applies the NEQ predicate on the "created" field. +func CreatedNEQ(v time.Time) predicate.Agent { + return predicate.Agent(sql.FieldNEQ(FieldCreated, v)) +} + +// CreatedIn applies the In predicate on the "created" field. +func CreatedIn(vs ...time.Time) predicate.Agent { + return predicate.Agent(sql.FieldIn(FieldCreated, vs...)) +} + +// CreatedNotIn applies the NotIn predicate on the "created" field. +func CreatedNotIn(vs ...time.Time) predicate.Agent { + return predicate.Agent(sql.FieldNotIn(FieldCreated, vs...)) +} + +// CreatedGT applies the GT predicate on the "created" field. +func CreatedGT(v time.Time) predicate.Agent { + return predicate.Agent(sql.FieldGT(FieldCreated, v)) +} + +// CreatedGTE applies the GTE predicate on the "created" field. +func CreatedGTE(v time.Time) predicate.Agent { + return predicate.Agent(sql.FieldGTE(FieldCreated, v)) +} + +// CreatedLT applies the LT predicate on the "created" field. +func CreatedLT(v time.Time) predicate.Agent { + return predicate.Agent(sql.FieldLT(FieldCreated, v)) +} + +// CreatedLTE applies the LTE predicate on the "created" field. +func CreatedLTE(v time.Time) predicate.Agent { + return predicate.Agent(sql.FieldLTE(FieldCreated, v)) +} + +// UpdatedEQ applies the EQ predicate on the "updated" field. +func UpdatedEQ(v time.Time) predicate.Agent { + return predicate.Agent(sql.FieldEQ(FieldUpdated, v)) +} + +// UpdatedNEQ applies the NEQ predicate on the "updated" field. +func UpdatedNEQ(v time.Time) predicate.Agent { + return predicate.Agent(sql.FieldNEQ(FieldUpdated, v)) +} + +// UpdatedIn applies the In predicate on the "updated" field. +func UpdatedIn(vs ...time.Time) predicate.Agent { + return predicate.Agent(sql.FieldIn(FieldUpdated, vs...)) +} + +// UpdatedNotIn applies the NotIn predicate on the "updated" field. +func UpdatedNotIn(vs ...time.Time) predicate.Agent { + return predicate.Agent(sql.FieldNotIn(FieldUpdated, vs...)) +} + +// UpdatedGT applies the GT predicate on the "updated" field. +func UpdatedGT(v time.Time) predicate.Agent { + return predicate.Agent(sql.FieldGT(FieldUpdated, v)) +} + +// UpdatedGTE applies the GTE predicate on the "updated" field. +func UpdatedGTE(v time.Time) predicate.Agent { + return predicate.Agent(sql.FieldGTE(FieldUpdated, v)) +} + +// UpdatedLT applies the LT predicate on the "updated" field. +func UpdatedLT(v time.Time) predicate.Agent { + return predicate.Agent(sql.FieldLT(FieldUpdated, v)) +} + +// UpdatedLTE applies the LTE predicate on the "updated" field. +func UpdatedLTE(v time.Time) predicate.Agent { + return predicate.Agent(sql.FieldLTE(FieldUpdated, v)) +} + +// LastSeenEQ applies the EQ predicate on the "last_seen" field. +func LastSeenEQ(v time.Time) predicate.Agent { + return predicate.Agent(sql.FieldEQ(FieldLastSeen, v)) +} + +// LastSeenNEQ applies the NEQ predicate on the "last_seen" field. +func LastSeenNEQ(v time.Time) predicate.Agent { + return predicate.Agent(sql.FieldNEQ(FieldLastSeen, v)) +} + +// LastSeenIn applies the In predicate on the "last_seen" field. +func LastSeenIn(vs ...time.Time) predicate.Agent { + return predicate.Agent(sql.FieldIn(FieldLastSeen, vs...)) +} + +// LastSeenNotIn applies the NotIn predicate on the "last_seen" field. +func LastSeenNotIn(vs ...time.Time) predicate.Agent { + return predicate.Agent(sql.FieldNotIn(FieldLastSeen, vs...)) +} + +// LastSeenGT applies the GT predicate on the "last_seen" field. +func LastSeenGT(v time.Time) predicate.Agent { + return predicate.Agent(sql.FieldGT(FieldLastSeen, v)) +} + +// LastSeenGTE applies the GTE predicate on the "last_seen" field. +func LastSeenGTE(v time.Time) predicate.Agent { + return predicate.Agent(sql.FieldGTE(FieldLastSeen, v)) +} + +// LastSeenLT applies the LT predicate on the "last_seen" field. +func LastSeenLT(v time.Time) predicate.Agent { + return predicate.Agent(sql.FieldLT(FieldLastSeen, v)) +} + +// LastSeenLTE applies the LTE predicate on the "last_seen" field. +func LastSeenLTE(v time.Time) predicate.Agent { + return predicate.Agent(sql.FieldLTE(FieldLastSeen, v)) +} + +// LastSeenIsNil applies the IsNil predicate on the "last_seen" field. +func LastSeenIsNil() predicate.Agent { + return predicate.Agent(sql.FieldIsNull(FieldLastSeen)) +} + +// LastSeenNotNil applies the NotNil predicate on the "last_seen" field. +func LastSeenNotNil() predicate.Agent { + return predicate.Agent(sql.FieldNotNull(FieldLastSeen)) +} + +// LastActivityEventEQ applies the EQ predicate on the "last_activity_event" field. +func LastActivityEventEQ(v time.Time) predicate.Agent { + return predicate.Agent(sql.FieldEQ(FieldLastActivityEvent, v)) +} + +// LastActivityEventNEQ applies the NEQ predicate on the "last_activity_event" field. +func LastActivityEventNEQ(v time.Time) predicate.Agent { + return predicate.Agent(sql.FieldNEQ(FieldLastActivityEvent, v)) +} + +// LastActivityEventIn applies the In predicate on the "last_activity_event" field. +func LastActivityEventIn(vs ...time.Time) predicate.Agent { + return predicate.Agent(sql.FieldIn(FieldLastActivityEvent, vs...)) +} + +// LastActivityEventNotIn applies the NotIn predicate on the "last_activity_event" field. +func LastActivityEventNotIn(vs ...time.Time) predicate.Agent { + return predicate.Agent(sql.FieldNotIn(FieldLastActivityEvent, vs...)) +} + +// LastActivityEventGT applies the GT predicate on the "last_activity_event" field. +func LastActivityEventGT(v time.Time) predicate.Agent { + return predicate.Agent(sql.FieldGT(FieldLastActivityEvent, v)) +} + +// LastActivityEventGTE applies the GTE predicate on the "last_activity_event" field. +func LastActivityEventGTE(v time.Time) predicate.Agent { + return predicate.Agent(sql.FieldGTE(FieldLastActivityEvent, v)) +} + +// LastActivityEventLT applies the LT predicate on the "last_activity_event" field. +func LastActivityEventLT(v time.Time) predicate.Agent { + return predicate.Agent(sql.FieldLT(FieldLastActivityEvent, v)) +} + +// LastActivityEventLTE applies the LTE predicate on the "last_activity_event" field. +func LastActivityEventLTE(v time.Time) predicate.Agent { + return predicate.Agent(sql.FieldLTE(FieldLastActivityEvent, v)) +} + +// LastActivityEventIsNil applies the IsNil predicate on the "last_activity_event" field. +func LastActivityEventIsNil() predicate.Agent { + return predicate.Agent(sql.FieldIsNull(FieldLastActivityEvent)) +} + +// LastActivityEventNotNil applies the NotNil predicate on the "last_activity_event" field. +func LastActivityEventNotNil() predicate.Agent { + return predicate.Agent(sql.FieldNotNull(FieldLastActivityEvent)) +} + +// StartedAtEQ applies the EQ predicate on the "started_at" field. +func StartedAtEQ(v time.Time) predicate.Agent { + return predicate.Agent(sql.FieldEQ(FieldStartedAt, v)) +} + +// StartedAtNEQ applies the NEQ predicate on the "started_at" field. +func StartedAtNEQ(v time.Time) predicate.Agent { + return predicate.Agent(sql.FieldNEQ(FieldStartedAt, v)) +} + +// StartedAtIn applies the In predicate on the "started_at" field. +func StartedAtIn(vs ...time.Time) predicate.Agent { + return predicate.Agent(sql.FieldIn(FieldStartedAt, vs...)) +} + +// StartedAtNotIn applies the NotIn predicate on the "started_at" field. +func StartedAtNotIn(vs ...time.Time) predicate.Agent { + return predicate.Agent(sql.FieldNotIn(FieldStartedAt, vs...)) +} + +// StartedAtGT applies the GT predicate on the "started_at" field. +func StartedAtGT(v time.Time) predicate.Agent { + return predicate.Agent(sql.FieldGT(FieldStartedAt, v)) +} + +// StartedAtGTE applies the GTE predicate on the "started_at" field. +func StartedAtGTE(v time.Time) predicate.Agent { + return predicate.Agent(sql.FieldGTE(FieldStartedAt, v)) +} + +// StartedAtLT applies the LT predicate on the "started_at" field. +func StartedAtLT(v time.Time) predicate.Agent { + return predicate.Agent(sql.FieldLT(FieldStartedAt, v)) +} + +// StartedAtLTE applies the LTE predicate on the "started_at" field. +func StartedAtLTE(v time.Time) predicate.Agent { + return predicate.Agent(sql.FieldLTE(FieldStartedAt, v)) +} + +// StartedAtIsNil applies the IsNil predicate on the "started_at" field. +func StartedAtIsNil() predicate.Agent { + return predicate.Agent(sql.FieldIsNull(FieldStartedAt)) +} + +// StartedAtNotNil applies the NotNil predicate on the "started_at" field. +func StartedAtNotNil() predicate.Agent { + return predicate.Agent(sql.FieldNotNull(FieldStartedAt)) +} + +// DeletedAtEQ applies the EQ predicate on the "deleted_at" field. +func DeletedAtEQ(v time.Time) predicate.Agent { + return predicate.Agent(sql.FieldEQ(FieldDeletedAt, v)) +} + +// DeletedAtNEQ applies the NEQ predicate on the "deleted_at" field. +func DeletedAtNEQ(v time.Time) predicate.Agent { + return predicate.Agent(sql.FieldNEQ(FieldDeletedAt, v)) +} + +// DeletedAtIn applies the In predicate on the "deleted_at" field. +func DeletedAtIn(vs ...time.Time) predicate.Agent { + return predicate.Agent(sql.FieldIn(FieldDeletedAt, vs...)) +} + +// DeletedAtNotIn applies the NotIn predicate on the "deleted_at" field. +func DeletedAtNotIn(vs ...time.Time) predicate.Agent { + return predicate.Agent(sql.FieldNotIn(FieldDeletedAt, vs...)) +} + +// DeletedAtGT applies the GT predicate on the "deleted_at" field. +func DeletedAtGT(v time.Time) predicate.Agent { + return predicate.Agent(sql.FieldGT(FieldDeletedAt, v)) +} + +// DeletedAtGTE applies the GTE predicate on the "deleted_at" field. +func DeletedAtGTE(v time.Time) predicate.Agent { + return predicate.Agent(sql.FieldGTE(FieldDeletedAt, v)) +} + +// DeletedAtLT applies the LT predicate on the "deleted_at" field. +func DeletedAtLT(v time.Time) predicate.Agent { + return predicate.Agent(sql.FieldLT(FieldDeletedAt, v)) +} + +// DeletedAtLTE applies the LTE predicate on the "deleted_at" field. +func DeletedAtLTE(v time.Time) predicate.Agent { + return predicate.Agent(sql.FieldLTE(FieldDeletedAt, v)) +} + +// DeletedAtIsNil applies the IsNil predicate on the "deleted_at" field. +func DeletedAtIsNil() predicate.Agent { + return predicate.Agent(sql.FieldIsNull(FieldDeletedAt)) +} + +// DeletedAtNotNil applies the NotNil predicate on the "deleted_at" field. +func DeletedAtNotNil() predicate.Agent { + return predicate.Agent(sql.FieldNotNull(FieldDeletedAt)) +} + +// StateVersionEQ applies the EQ predicate on the "state_version" field. +func StateVersionEQ(v int64) predicate.Agent { + return predicate.Agent(sql.FieldEQ(FieldStateVersion, v)) +} + +// StateVersionNEQ applies the NEQ predicate on the "state_version" field. +func StateVersionNEQ(v int64) predicate.Agent { + return predicate.Agent(sql.FieldNEQ(FieldStateVersion, v)) +} + +// StateVersionIn applies the In predicate on the "state_version" field. +func StateVersionIn(vs ...int64) predicate.Agent { + return predicate.Agent(sql.FieldIn(FieldStateVersion, vs...)) +} + +// StateVersionNotIn applies the NotIn predicate on the "state_version" field. +func StateVersionNotIn(vs ...int64) predicate.Agent { + return predicate.Agent(sql.FieldNotIn(FieldStateVersion, vs...)) +} + +// StateVersionGT applies the GT predicate on the "state_version" field. +func StateVersionGT(v int64) predicate.Agent { + return predicate.Agent(sql.FieldGT(FieldStateVersion, v)) +} + +// StateVersionGTE applies the GTE predicate on the "state_version" field. +func StateVersionGTE(v int64) predicate.Agent { + return predicate.Agent(sql.FieldGTE(FieldStateVersion, v)) +} + +// StateVersionLT applies the LT predicate on the "state_version" field. +func StateVersionLT(v int64) predicate.Agent { + return predicate.Agent(sql.FieldLT(FieldStateVersion, v)) +} + +// StateVersionLTE applies the LTE predicate on the "state_version" field. +func StateVersionLTE(v int64) predicate.Agent { + return predicate.Agent(sql.FieldLTE(FieldStateVersion, v)) } // HasProject applies the HasEdge predicate on the "project" edge. diff --git a/pkg/ent/agent_create.go b/pkg/ent/agent_create.go index 39a081285..1b0a575c4 100644 --- a/pkg/ent/agent_create.go +++ b/pkg/ent/agent_create.go @@ -8,6 +8,8 @@ import ( "fmt" "time" + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" "entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/schema/field" "github.com/GoogleCloudPlatform/scion/pkg/ent/agent" @@ -23,6 +25,7 @@ type AgentCreate struct { config mutation *AgentMutation hooks []Hook + conflict []sql.ConflictOption } // SetSlug sets the "slug" field. @@ -127,6 +130,262 @@ func (_c *AgentCreate) SetNillableVisibility(v *string) *AgentCreate { return _c } +// SetLabels sets the "labels" field. +func (_c *AgentCreate) SetLabels(v map[string]string) *AgentCreate { + _c.mutation.SetLabels(v) + return _c +} + +// SetAnnotations sets the "annotations" field. +func (_c *AgentCreate) SetAnnotations(v map[string]string) *AgentCreate { + _c.mutation.SetAnnotations(v) + return _c +} + +// SetPhase sets the "phase" field. +func (_c *AgentCreate) SetPhase(v string) *AgentCreate { + _c.mutation.SetPhase(v) + return _c +} + +// SetNillablePhase sets the "phase" field if the given value is not nil. +func (_c *AgentCreate) SetNillablePhase(v *string) *AgentCreate { + if v != nil { + _c.SetPhase(*v) + } + return _c +} + +// SetActivity sets the "activity" field. +func (_c *AgentCreate) SetActivity(v string) *AgentCreate { + _c.mutation.SetActivity(v) + return _c +} + +// SetNillableActivity sets the "activity" field if the given value is not nil. +func (_c *AgentCreate) SetNillableActivity(v *string) *AgentCreate { + if v != nil { + _c.SetActivity(*v) + } + return _c +} + +// SetToolName sets the "tool_name" field. +func (_c *AgentCreate) SetToolName(v string) *AgentCreate { + _c.mutation.SetToolName(v) + return _c +} + +// SetNillableToolName sets the "tool_name" field if the given value is not nil. +func (_c *AgentCreate) SetNillableToolName(v *string) *AgentCreate { + if v != nil { + _c.SetToolName(*v) + } + return _c +} + +// SetConnectionState sets the "connection_state" field. +func (_c *AgentCreate) SetConnectionState(v string) *AgentCreate { + _c.mutation.SetConnectionState(v) + return _c +} + +// SetNillableConnectionState sets the "connection_state" field if the given value is not nil. +func (_c *AgentCreate) SetNillableConnectionState(v *string) *AgentCreate { + if v != nil { + _c.SetConnectionState(*v) + } + return _c +} + +// SetContainerStatus sets the "container_status" field. +func (_c *AgentCreate) SetContainerStatus(v string) *AgentCreate { + _c.mutation.SetContainerStatus(v) + return _c +} + +// SetNillableContainerStatus sets the "container_status" field if the given value is not nil. +func (_c *AgentCreate) SetNillableContainerStatus(v *string) *AgentCreate { + if v != nil { + _c.SetContainerStatus(*v) + } + return _c +} + +// SetRuntimeState sets the "runtime_state" field. +func (_c *AgentCreate) SetRuntimeState(v string) *AgentCreate { + _c.mutation.SetRuntimeState(v) + return _c +} + +// SetNillableRuntimeState sets the "runtime_state" field if the given value is not nil. +func (_c *AgentCreate) SetNillableRuntimeState(v *string) *AgentCreate { + if v != nil { + _c.SetRuntimeState(*v) + } + return _c +} + +// SetStalledFromActivity sets the "stalled_from_activity" field. +func (_c *AgentCreate) SetStalledFromActivity(v string) *AgentCreate { + _c.mutation.SetStalledFromActivity(v) + return _c +} + +// SetNillableStalledFromActivity sets the "stalled_from_activity" field if the given value is not nil. +func (_c *AgentCreate) SetNillableStalledFromActivity(v *string) *AgentCreate { + if v != nil { + _c.SetStalledFromActivity(*v) + } + return _c +} + +// SetCurrentTurns sets the "current_turns" field. +func (_c *AgentCreate) SetCurrentTurns(v int) *AgentCreate { + _c.mutation.SetCurrentTurns(v) + return _c +} + +// SetNillableCurrentTurns sets the "current_turns" field if the given value is not nil. +func (_c *AgentCreate) SetNillableCurrentTurns(v *int) *AgentCreate { + if v != nil { + _c.SetCurrentTurns(*v) + } + return _c +} + +// SetCurrentModelCalls sets the "current_model_calls" field. +func (_c *AgentCreate) SetCurrentModelCalls(v int) *AgentCreate { + _c.mutation.SetCurrentModelCalls(v) + return _c +} + +// SetNillableCurrentModelCalls sets the "current_model_calls" field if the given value is not nil. +func (_c *AgentCreate) SetNillableCurrentModelCalls(v *int) *AgentCreate { + if v != nil { + _c.SetCurrentModelCalls(*v) + } + return _c +} + +// SetImage sets the "image" field. +func (_c *AgentCreate) SetImage(v string) *AgentCreate { + _c.mutation.SetImage(v) + return _c +} + +// SetNillableImage sets the "image" field if the given value is not nil. +func (_c *AgentCreate) SetNillableImage(v *string) *AgentCreate { + if v != nil { + _c.SetImage(*v) + } + return _c +} + +// SetDetached sets the "detached" field. +func (_c *AgentCreate) SetDetached(v bool) *AgentCreate { + _c.mutation.SetDetached(v) + return _c +} + +// SetNillableDetached sets the "detached" field if the given value is not nil. +func (_c *AgentCreate) SetNillableDetached(v *bool) *AgentCreate { + if v != nil { + _c.SetDetached(*v) + } + return _c +} + +// SetRuntime sets the "runtime" field. +func (_c *AgentCreate) SetRuntime(v string) *AgentCreate { + _c.mutation.SetRuntime(v) + return _c +} + +// SetNillableRuntime sets the "runtime" field if the given value is not nil. +func (_c *AgentCreate) SetNillableRuntime(v *string) *AgentCreate { + if v != nil { + _c.SetRuntime(*v) + } + return _c +} + +// SetRuntimeBrokerID sets the "runtime_broker_id" field. +func (_c *AgentCreate) SetRuntimeBrokerID(v string) *AgentCreate { + _c.mutation.SetRuntimeBrokerID(v) + return _c +} + +// SetNillableRuntimeBrokerID sets the "runtime_broker_id" field if the given value is not nil. +func (_c *AgentCreate) SetNillableRuntimeBrokerID(v *string) *AgentCreate { + if v != nil { + _c.SetRuntimeBrokerID(*v) + } + return _c +} + +// SetWebPtyEnabled sets the "web_pty_enabled" field. +func (_c *AgentCreate) SetWebPtyEnabled(v bool) *AgentCreate { + _c.mutation.SetWebPtyEnabled(v) + return _c +} + +// SetNillableWebPtyEnabled sets the "web_pty_enabled" field if the given value is not nil. +func (_c *AgentCreate) SetNillableWebPtyEnabled(v *bool) *AgentCreate { + if v != nil { + _c.SetWebPtyEnabled(*v) + } + return _c +} + +// SetTaskSummary sets the "task_summary" field. +func (_c *AgentCreate) SetTaskSummary(v string) *AgentCreate { + _c.mutation.SetTaskSummary(v) + return _c +} + +// SetNillableTaskSummary sets the "task_summary" field if the given value is not nil. +func (_c *AgentCreate) SetNillableTaskSummary(v *string) *AgentCreate { + if v != nil { + _c.SetTaskSummary(*v) + } + return _c +} + +// SetMessage sets the "message" field. +func (_c *AgentCreate) SetMessage(v string) *AgentCreate { + _c.mutation.SetMessage(v) + return _c +} + +// SetNillableMessage sets the "message" field if the given value is not nil. +func (_c *AgentCreate) SetNillableMessage(v *string) *AgentCreate { + if v != nil { + _c.SetMessage(*v) + } + return _c +} + +// SetAppliedConfig sets the "applied_config" field. +func (_c *AgentCreate) SetAppliedConfig(v string) *AgentCreate { + _c.mutation.SetAppliedConfig(v) + return _c +} + +// SetNillableAppliedConfig sets the "applied_config" field if the given value is not nil. +func (_c *AgentCreate) SetNillableAppliedConfig(v *string) *AgentCreate { + if v != nil { + _c.SetAppliedConfig(*v) + } + return _c +} + +// SetAncestry sets the "ancestry" field. +func (_c *AgentCreate) SetAncestry(v []string) *AgentCreate { + _c.mutation.SetAncestry(v) + return _c +} + // SetCreated sets the "created" field. func (_c *AgentCreate) SetCreated(v time.Time) *AgentCreate { _c.mutation.SetCreated(v) @@ -155,6 +414,76 @@ func (_c *AgentCreate) SetNillableUpdated(v *time.Time) *AgentCreate { return _c } +// SetLastSeen sets the "last_seen" field. +func (_c *AgentCreate) SetLastSeen(v time.Time) *AgentCreate { + _c.mutation.SetLastSeen(v) + return _c +} + +// SetNillableLastSeen sets the "last_seen" field if the given value is not nil. +func (_c *AgentCreate) SetNillableLastSeen(v *time.Time) *AgentCreate { + if v != nil { + _c.SetLastSeen(*v) + } + return _c +} + +// SetLastActivityEvent sets the "last_activity_event" field. +func (_c *AgentCreate) SetLastActivityEvent(v time.Time) *AgentCreate { + _c.mutation.SetLastActivityEvent(v) + return _c +} + +// SetNillableLastActivityEvent sets the "last_activity_event" field if the given value is not nil. +func (_c *AgentCreate) SetNillableLastActivityEvent(v *time.Time) *AgentCreate { + if v != nil { + _c.SetLastActivityEvent(*v) + } + return _c +} + +// SetStartedAt sets the "started_at" field. +func (_c *AgentCreate) SetStartedAt(v time.Time) *AgentCreate { + _c.mutation.SetStartedAt(v) + return _c +} + +// SetNillableStartedAt sets the "started_at" field if the given value is not nil. +func (_c *AgentCreate) SetNillableStartedAt(v *time.Time) *AgentCreate { + if v != nil { + _c.SetStartedAt(*v) + } + return _c +} + +// SetDeletedAt sets the "deleted_at" field. +func (_c *AgentCreate) SetDeletedAt(v time.Time) *AgentCreate { + _c.mutation.SetDeletedAt(v) + return _c +} + +// SetNillableDeletedAt sets the "deleted_at" field if the given value is not nil. +func (_c *AgentCreate) SetNillableDeletedAt(v *time.Time) *AgentCreate { + if v != nil { + _c.SetDeletedAt(*v) + } + return _c +} + +// SetStateVersion sets the "state_version" field. +func (_c *AgentCreate) SetStateVersion(v int64) *AgentCreate { + _c.mutation.SetStateVersion(v) + return _c +} + +// SetNillableStateVersion sets the "state_version" field if the given value is not nil. +func (_c *AgentCreate) SetNillableStateVersion(v *int64) *AgentCreate { + if v != nil { + _c.SetStateVersion(*v) + } + return _c +} + // SetID sets the "id" field. func (_c *AgentCreate) SetID(v uuid.UUID) *AgentCreate { _c.mutation.SetID(v) @@ -275,6 +604,22 @@ func (_c *AgentCreate) defaults() { v := agent.DefaultVisibility _c.mutation.SetVisibility(v) } + if _, ok := _c.mutation.CurrentTurns(); !ok { + v := agent.DefaultCurrentTurns + _c.mutation.SetCurrentTurns(v) + } + if _, ok := _c.mutation.CurrentModelCalls(); !ok { + v := agent.DefaultCurrentModelCalls + _c.mutation.SetCurrentModelCalls(v) + } + if _, ok := _c.mutation.Detached(); !ok { + v := agent.DefaultDetached + _c.mutation.SetDetached(v) + } + if _, ok := _c.mutation.WebPtyEnabled(); !ok { + v := agent.DefaultWebPtyEnabled + _c.mutation.SetWebPtyEnabled(v) + } if _, ok := _c.mutation.Created(); !ok { v := agent.DefaultCreated() _c.mutation.SetCreated(v) @@ -283,6 +628,10 @@ func (_c *AgentCreate) defaults() { v := agent.DefaultUpdated() _c.mutation.SetUpdated(v) } + if _, ok := _c.mutation.StateVersion(); !ok { + v := agent.DefaultStateVersion + _c.mutation.SetStateVersion(v) + } if _, ok := _c.mutation.ID(); !ok { v := agent.DefaultID() _c.mutation.SetID(v) @@ -324,12 +673,27 @@ func (_c *AgentCreate) check() error { if _, ok := _c.mutation.Visibility(); !ok { return &ValidationError{Name: "visibility", err: errors.New(`ent: missing required field "Agent.visibility"`)} } + if _, ok := _c.mutation.CurrentTurns(); !ok { + return &ValidationError{Name: "current_turns", err: errors.New(`ent: missing required field "Agent.current_turns"`)} + } + if _, ok := _c.mutation.CurrentModelCalls(); !ok { + return &ValidationError{Name: "current_model_calls", err: errors.New(`ent: missing required field "Agent.current_model_calls"`)} + } + if _, ok := _c.mutation.Detached(); !ok { + return &ValidationError{Name: "detached", err: errors.New(`ent: missing required field "Agent.detached"`)} + } + if _, ok := _c.mutation.WebPtyEnabled(); !ok { + return &ValidationError{Name: "web_pty_enabled", err: errors.New(`ent: missing required field "Agent.web_pty_enabled"`)} + } if _, ok := _c.mutation.Created(); !ok { return &ValidationError{Name: "created", err: errors.New(`ent: missing required field "Agent.created"`)} } if _, ok := _c.mutation.Updated(); !ok { return &ValidationError{Name: "updated", err: errors.New(`ent: missing required field "Agent.updated"`)} } + if _, ok := _c.mutation.StateVersion(); !ok { + return &ValidationError{Name: "state_version", err: errors.New(`ent: missing required field "Agent.state_version"`)} + } if len(_c.mutation.ProjectIDs()) == 0 { return &ValidationError{Name: "project", err: errors.New(`ent: missing required edge "Agent.project"`)} } @@ -364,6 +728,7 @@ func (_c *AgentCreate) createSpec() (*Agent, *sqlgraph.CreateSpec) { _node = &Agent{config: _c.config} _spec = sqlgraph.NewCreateSpec(agent.Table, sqlgraph.NewFieldSpec(agent.FieldID, field.TypeUUID)) ) + _spec.OnConflict = _c.conflict if id, ok := _c.mutation.ID(); ok { _node.ID = id _spec.ID.Value = &id @@ -392,6 +757,86 @@ func (_c *AgentCreate) createSpec() (*Agent, *sqlgraph.CreateSpec) { _spec.SetField(agent.FieldVisibility, field.TypeString, value) _node.Visibility = value } + if value, ok := _c.mutation.Labels(); ok { + _spec.SetField(agent.FieldLabels, field.TypeJSON, value) + _node.Labels = value + } + if value, ok := _c.mutation.Annotations(); ok { + _spec.SetField(agent.FieldAnnotations, field.TypeJSON, value) + _node.Annotations = value + } + if value, ok := _c.mutation.Phase(); ok { + _spec.SetField(agent.FieldPhase, field.TypeString, value) + _node.Phase = value + } + if value, ok := _c.mutation.Activity(); ok { + _spec.SetField(agent.FieldActivity, field.TypeString, value) + _node.Activity = value + } + if value, ok := _c.mutation.ToolName(); ok { + _spec.SetField(agent.FieldToolName, field.TypeString, value) + _node.ToolName = value + } + if value, ok := _c.mutation.ConnectionState(); ok { + _spec.SetField(agent.FieldConnectionState, field.TypeString, value) + _node.ConnectionState = value + } + if value, ok := _c.mutation.ContainerStatus(); ok { + _spec.SetField(agent.FieldContainerStatus, field.TypeString, value) + _node.ContainerStatus = value + } + if value, ok := _c.mutation.RuntimeState(); ok { + _spec.SetField(agent.FieldRuntimeState, field.TypeString, value) + _node.RuntimeState = value + } + if value, ok := _c.mutation.StalledFromActivity(); ok { + _spec.SetField(agent.FieldStalledFromActivity, field.TypeString, value) + _node.StalledFromActivity = value + } + if value, ok := _c.mutation.CurrentTurns(); ok { + _spec.SetField(agent.FieldCurrentTurns, field.TypeInt, value) + _node.CurrentTurns = value + } + if value, ok := _c.mutation.CurrentModelCalls(); ok { + _spec.SetField(agent.FieldCurrentModelCalls, field.TypeInt, value) + _node.CurrentModelCalls = value + } + if value, ok := _c.mutation.Image(); ok { + _spec.SetField(agent.FieldImage, field.TypeString, value) + _node.Image = value + } + if value, ok := _c.mutation.Detached(); ok { + _spec.SetField(agent.FieldDetached, field.TypeBool, value) + _node.Detached = value + } + if value, ok := _c.mutation.Runtime(); ok { + _spec.SetField(agent.FieldRuntime, field.TypeString, value) + _node.Runtime = value + } + if value, ok := _c.mutation.RuntimeBrokerID(); ok { + _spec.SetField(agent.FieldRuntimeBrokerID, field.TypeString, value) + _node.RuntimeBrokerID = value + } + if value, ok := _c.mutation.WebPtyEnabled(); ok { + _spec.SetField(agent.FieldWebPtyEnabled, field.TypeBool, value) + _node.WebPtyEnabled = value + } + if value, ok := _c.mutation.TaskSummary(); ok { + _spec.SetField(agent.FieldTaskSummary, field.TypeString, value) + _node.TaskSummary = value + } + if value, ok := _c.mutation.Message(); ok { + _spec.SetField(agent.FieldMessage, field.TypeString, value) + _node.Message = value + } + if value, ok := _c.mutation.AppliedConfig(); ok { + _spec.SetField(agent.FieldAppliedConfig, field.TypeString, value) + _node.AppliedConfig = value + } + if value, ok := _c.mutation.Ancestry(); ok { + _spec.SetField(agent.FieldAncestry, field.TypeJSON, value) + _node.Ancestry = value + } if value, ok := _c.mutation.Created(); ok { _spec.SetField(agent.FieldCreated, field.TypeTime, value) _node.Created = value @@ -400,6 +845,26 @@ func (_c *AgentCreate) createSpec() (*Agent, *sqlgraph.CreateSpec) { _spec.SetField(agent.FieldUpdated, field.TypeTime, value) _node.Updated = value } + if value, ok := _c.mutation.LastSeen(); ok { + _spec.SetField(agent.FieldLastSeen, field.TypeTime, value) + _node.LastSeen = &value + } + if value, ok := _c.mutation.LastActivityEvent(); ok { + _spec.SetField(agent.FieldLastActivityEvent, field.TypeTime, value) + _node.LastActivityEvent = &value + } + if value, ok := _c.mutation.StartedAt(); ok { + _spec.SetField(agent.FieldStartedAt, field.TypeTime, value) + _node.StartedAt = &value + } + if value, ok := _c.mutation.DeletedAt(); ok { + _spec.SetField(agent.FieldDeletedAt, field.TypeTime, value) + _node.DeletedAt = &value + } + if value, ok := _c.mutation.StateVersion(); ok { + _spec.SetField(agent.FieldStateVersion, field.TypeInt64, value) + _node.StateVersion = value + } if nodes := _c.mutation.ProjectIDs(); len(nodes) > 0 { edge := &sqlgraph.EdgeSpec{ Rel: sqlgraph.M2O, @@ -486,44 +951,1432 @@ func (_c *AgentCreate) createSpec() (*Agent, *sqlgraph.CreateSpec) { return _node, _spec } -// AgentCreateBulk is the builder for creating many Agent entities in bulk. -type AgentCreateBulk struct { - config - err error - builders []*AgentCreate +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.Agent.Create(). +// SetSlug(v). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.AgentUpsert) { +// SetSlug(v+v). +// }). +// Exec(ctx) +func (_c *AgentCreate) OnConflict(opts ...sql.ConflictOption) *AgentUpsertOne { + _c.conflict = opts + return &AgentUpsertOne{ + create: _c, + } } -// Save creates the Agent entities in the database. -func (_c *AgentCreateBulk) Save(ctx context.Context) ([]*Agent, error) { - if _c.err != nil { - return nil, _c.err +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.Agent.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *AgentCreate) OnConflictColumns(columns ...string) *AgentUpsertOne { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &AgentUpsertOne{ + create: _c, } - specs := make([]*sqlgraph.CreateSpec, len(_c.builders)) - nodes := make([]*Agent, len(_c.builders)) - mutators := make([]Mutator, len(_c.builders)) - for i := range _c.builders { - func(i int, root context.Context) { - builder := _c.builders[i] - builder.defaults() - var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { - mutation, ok := m.(*AgentMutation) - if !ok { - return nil, fmt.Errorf("unexpected mutation type %T", m) - } - if err := builder.check(); err != nil { - return nil, err - } - builder.mutation = mutation - var err error - nodes[i], specs[i] = builder.createSpec() - if i < len(mutators)-1 { - _, err = mutators[i+1].Mutate(root, _c.builders[i+1].mutation) - } else { - spec := &sqlgraph.BatchCreateSpec{Nodes: specs} - // Invoke the actual operation on the latest mutation in the chain. - if err = sqlgraph.BatchCreate(ctx, _c.driver, spec); err != nil { - if sqlgraph.IsConstraintError(err) { - err = &ConstraintError{msg: err.Error(), wrap: err} +} + +type ( + // AgentUpsertOne is the builder for "upsert"-ing + // one Agent node. + AgentUpsertOne struct { + create *AgentCreate + } + + // AgentUpsert is the "OnConflict" setter. + AgentUpsert struct { + *sql.UpdateSet + } +) + +// SetSlug sets the "slug" field. +func (u *AgentUpsert) SetSlug(v string) *AgentUpsert { + u.Set(agent.FieldSlug, v) + return u +} + +// UpdateSlug sets the "slug" field to the value that was provided on create. +func (u *AgentUpsert) UpdateSlug() *AgentUpsert { + u.SetExcluded(agent.FieldSlug) + return u +} + +// SetName sets the "name" field. +func (u *AgentUpsert) SetName(v string) *AgentUpsert { + u.Set(agent.FieldName, v) + return u +} + +// UpdateName sets the "name" field to the value that was provided on create. +func (u *AgentUpsert) UpdateName() *AgentUpsert { + u.SetExcluded(agent.FieldName) + return u +} + +// SetTemplate sets the "template" field. +func (u *AgentUpsert) SetTemplate(v string) *AgentUpsert { + u.Set(agent.FieldTemplate, v) + return u +} + +// UpdateTemplate sets the "template" field to the value that was provided on create. +func (u *AgentUpsert) UpdateTemplate() *AgentUpsert { + u.SetExcluded(agent.FieldTemplate) + return u +} + +// ClearTemplate clears the value of the "template" field. +func (u *AgentUpsert) ClearTemplate() *AgentUpsert { + u.SetNull(agent.FieldTemplate) + return u +} + +// SetProjectID sets the "project_id" field. +func (u *AgentUpsert) SetProjectID(v uuid.UUID) *AgentUpsert { + u.Set(agent.FieldProjectID, v) + return u +} + +// UpdateProjectID sets the "project_id" field to the value that was provided on create. +func (u *AgentUpsert) UpdateProjectID() *AgentUpsert { + u.SetExcluded(agent.FieldProjectID) + return u +} + +// SetStatus sets the "status" field. +func (u *AgentUpsert) SetStatus(v agent.Status) *AgentUpsert { + u.Set(agent.FieldStatus, v) + return u +} + +// UpdateStatus sets the "status" field to the value that was provided on create. +func (u *AgentUpsert) UpdateStatus() *AgentUpsert { + u.SetExcluded(agent.FieldStatus) + return u +} + +// SetCreatedBy sets the "created_by" field. +func (u *AgentUpsert) SetCreatedBy(v uuid.UUID) *AgentUpsert { + u.Set(agent.FieldCreatedBy, v) + return u +} + +// UpdateCreatedBy sets the "created_by" field to the value that was provided on create. +func (u *AgentUpsert) UpdateCreatedBy() *AgentUpsert { + u.SetExcluded(agent.FieldCreatedBy) + return u +} + +// ClearCreatedBy clears the value of the "created_by" field. +func (u *AgentUpsert) ClearCreatedBy() *AgentUpsert { + u.SetNull(agent.FieldCreatedBy) + return u +} + +// SetOwnerID sets the "owner_id" field. +func (u *AgentUpsert) SetOwnerID(v uuid.UUID) *AgentUpsert { + u.Set(agent.FieldOwnerID, v) + return u +} + +// UpdateOwnerID sets the "owner_id" field to the value that was provided on create. +func (u *AgentUpsert) UpdateOwnerID() *AgentUpsert { + u.SetExcluded(agent.FieldOwnerID) + return u +} + +// ClearOwnerID clears the value of the "owner_id" field. +func (u *AgentUpsert) ClearOwnerID() *AgentUpsert { + u.SetNull(agent.FieldOwnerID) + return u +} + +// SetDelegationEnabled sets the "delegation_enabled" field. +func (u *AgentUpsert) SetDelegationEnabled(v bool) *AgentUpsert { + u.Set(agent.FieldDelegationEnabled, v) + return u +} + +// UpdateDelegationEnabled sets the "delegation_enabled" field to the value that was provided on create. +func (u *AgentUpsert) UpdateDelegationEnabled() *AgentUpsert { + u.SetExcluded(agent.FieldDelegationEnabled) + return u +} + +// SetVisibility sets the "visibility" field. +func (u *AgentUpsert) SetVisibility(v string) *AgentUpsert { + u.Set(agent.FieldVisibility, v) + return u +} + +// UpdateVisibility sets the "visibility" field to the value that was provided on create. +func (u *AgentUpsert) UpdateVisibility() *AgentUpsert { + u.SetExcluded(agent.FieldVisibility) + return u +} + +// SetLabels sets the "labels" field. +func (u *AgentUpsert) SetLabels(v map[string]string) *AgentUpsert { + u.Set(agent.FieldLabels, v) + return u +} + +// UpdateLabels sets the "labels" field to the value that was provided on create. +func (u *AgentUpsert) UpdateLabels() *AgentUpsert { + u.SetExcluded(agent.FieldLabels) + return u +} + +// ClearLabels clears the value of the "labels" field. +func (u *AgentUpsert) ClearLabels() *AgentUpsert { + u.SetNull(agent.FieldLabels) + return u +} + +// SetAnnotations sets the "annotations" field. +func (u *AgentUpsert) SetAnnotations(v map[string]string) *AgentUpsert { + u.Set(agent.FieldAnnotations, v) + return u +} + +// UpdateAnnotations sets the "annotations" field to the value that was provided on create. +func (u *AgentUpsert) UpdateAnnotations() *AgentUpsert { + u.SetExcluded(agent.FieldAnnotations) + return u +} + +// ClearAnnotations clears the value of the "annotations" field. +func (u *AgentUpsert) ClearAnnotations() *AgentUpsert { + u.SetNull(agent.FieldAnnotations) + return u +} + +// SetPhase sets the "phase" field. +func (u *AgentUpsert) SetPhase(v string) *AgentUpsert { + u.Set(agent.FieldPhase, v) + return u +} + +// UpdatePhase sets the "phase" field to the value that was provided on create. +func (u *AgentUpsert) UpdatePhase() *AgentUpsert { + u.SetExcluded(agent.FieldPhase) + return u +} + +// ClearPhase clears the value of the "phase" field. +func (u *AgentUpsert) ClearPhase() *AgentUpsert { + u.SetNull(agent.FieldPhase) + return u +} + +// SetActivity sets the "activity" field. +func (u *AgentUpsert) SetActivity(v string) *AgentUpsert { + u.Set(agent.FieldActivity, v) + return u +} + +// UpdateActivity sets the "activity" field to the value that was provided on create. +func (u *AgentUpsert) UpdateActivity() *AgentUpsert { + u.SetExcluded(agent.FieldActivity) + return u +} + +// ClearActivity clears the value of the "activity" field. +func (u *AgentUpsert) ClearActivity() *AgentUpsert { + u.SetNull(agent.FieldActivity) + return u +} + +// SetToolName sets the "tool_name" field. +func (u *AgentUpsert) SetToolName(v string) *AgentUpsert { + u.Set(agent.FieldToolName, v) + return u +} + +// UpdateToolName sets the "tool_name" field to the value that was provided on create. +func (u *AgentUpsert) UpdateToolName() *AgentUpsert { + u.SetExcluded(agent.FieldToolName) + return u +} + +// ClearToolName clears the value of the "tool_name" field. +func (u *AgentUpsert) ClearToolName() *AgentUpsert { + u.SetNull(agent.FieldToolName) + return u +} + +// SetConnectionState sets the "connection_state" field. +func (u *AgentUpsert) SetConnectionState(v string) *AgentUpsert { + u.Set(agent.FieldConnectionState, v) + return u +} + +// UpdateConnectionState sets the "connection_state" field to the value that was provided on create. +func (u *AgentUpsert) UpdateConnectionState() *AgentUpsert { + u.SetExcluded(agent.FieldConnectionState) + return u +} + +// ClearConnectionState clears the value of the "connection_state" field. +func (u *AgentUpsert) ClearConnectionState() *AgentUpsert { + u.SetNull(agent.FieldConnectionState) + return u +} + +// SetContainerStatus sets the "container_status" field. +func (u *AgentUpsert) SetContainerStatus(v string) *AgentUpsert { + u.Set(agent.FieldContainerStatus, v) + return u +} + +// UpdateContainerStatus sets the "container_status" field to the value that was provided on create. +func (u *AgentUpsert) UpdateContainerStatus() *AgentUpsert { + u.SetExcluded(agent.FieldContainerStatus) + return u +} + +// ClearContainerStatus clears the value of the "container_status" field. +func (u *AgentUpsert) ClearContainerStatus() *AgentUpsert { + u.SetNull(agent.FieldContainerStatus) + return u +} + +// SetRuntimeState sets the "runtime_state" field. +func (u *AgentUpsert) SetRuntimeState(v string) *AgentUpsert { + u.Set(agent.FieldRuntimeState, v) + return u +} + +// UpdateRuntimeState sets the "runtime_state" field to the value that was provided on create. +func (u *AgentUpsert) UpdateRuntimeState() *AgentUpsert { + u.SetExcluded(agent.FieldRuntimeState) + return u +} + +// ClearRuntimeState clears the value of the "runtime_state" field. +func (u *AgentUpsert) ClearRuntimeState() *AgentUpsert { + u.SetNull(agent.FieldRuntimeState) + return u +} + +// SetStalledFromActivity sets the "stalled_from_activity" field. +func (u *AgentUpsert) SetStalledFromActivity(v string) *AgentUpsert { + u.Set(agent.FieldStalledFromActivity, v) + return u +} + +// UpdateStalledFromActivity sets the "stalled_from_activity" field to the value that was provided on create. +func (u *AgentUpsert) UpdateStalledFromActivity() *AgentUpsert { + u.SetExcluded(agent.FieldStalledFromActivity) + return u +} + +// ClearStalledFromActivity clears the value of the "stalled_from_activity" field. +func (u *AgentUpsert) ClearStalledFromActivity() *AgentUpsert { + u.SetNull(agent.FieldStalledFromActivity) + return u +} + +// SetCurrentTurns sets the "current_turns" field. +func (u *AgentUpsert) SetCurrentTurns(v int) *AgentUpsert { + u.Set(agent.FieldCurrentTurns, v) + return u +} + +// UpdateCurrentTurns sets the "current_turns" field to the value that was provided on create. +func (u *AgentUpsert) UpdateCurrentTurns() *AgentUpsert { + u.SetExcluded(agent.FieldCurrentTurns) + return u +} + +// AddCurrentTurns adds v to the "current_turns" field. +func (u *AgentUpsert) AddCurrentTurns(v int) *AgentUpsert { + u.Add(agent.FieldCurrentTurns, v) + return u +} + +// SetCurrentModelCalls sets the "current_model_calls" field. +func (u *AgentUpsert) SetCurrentModelCalls(v int) *AgentUpsert { + u.Set(agent.FieldCurrentModelCalls, v) + return u +} + +// UpdateCurrentModelCalls sets the "current_model_calls" field to the value that was provided on create. +func (u *AgentUpsert) UpdateCurrentModelCalls() *AgentUpsert { + u.SetExcluded(agent.FieldCurrentModelCalls) + return u +} + +// AddCurrentModelCalls adds v to the "current_model_calls" field. +func (u *AgentUpsert) AddCurrentModelCalls(v int) *AgentUpsert { + u.Add(agent.FieldCurrentModelCalls, v) + return u +} + +// SetImage sets the "image" field. +func (u *AgentUpsert) SetImage(v string) *AgentUpsert { + u.Set(agent.FieldImage, v) + return u +} + +// UpdateImage sets the "image" field to the value that was provided on create. +func (u *AgentUpsert) UpdateImage() *AgentUpsert { + u.SetExcluded(agent.FieldImage) + return u +} + +// ClearImage clears the value of the "image" field. +func (u *AgentUpsert) ClearImage() *AgentUpsert { + u.SetNull(agent.FieldImage) + return u +} + +// SetDetached sets the "detached" field. +func (u *AgentUpsert) SetDetached(v bool) *AgentUpsert { + u.Set(agent.FieldDetached, v) + return u +} + +// UpdateDetached sets the "detached" field to the value that was provided on create. +func (u *AgentUpsert) UpdateDetached() *AgentUpsert { + u.SetExcluded(agent.FieldDetached) + return u +} + +// SetRuntime sets the "runtime" field. +func (u *AgentUpsert) SetRuntime(v string) *AgentUpsert { + u.Set(agent.FieldRuntime, v) + return u +} + +// UpdateRuntime sets the "runtime" field to the value that was provided on create. +func (u *AgentUpsert) UpdateRuntime() *AgentUpsert { + u.SetExcluded(agent.FieldRuntime) + return u +} + +// ClearRuntime clears the value of the "runtime" field. +func (u *AgentUpsert) ClearRuntime() *AgentUpsert { + u.SetNull(agent.FieldRuntime) + return u +} + +// SetRuntimeBrokerID sets the "runtime_broker_id" field. +func (u *AgentUpsert) SetRuntimeBrokerID(v string) *AgentUpsert { + u.Set(agent.FieldRuntimeBrokerID, v) + return u +} + +// UpdateRuntimeBrokerID sets the "runtime_broker_id" field to the value that was provided on create. +func (u *AgentUpsert) UpdateRuntimeBrokerID() *AgentUpsert { + u.SetExcluded(agent.FieldRuntimeBrokerID) + return u +} + +// ClearRuntimeBrokerID clears the value of the "runtime_broker_id" field. +func (u *AgentUpsert) ClearRuntimeBrokerID() *AgentUpsert { + u.SetNull(agent.FieldRuntimeBrokerID) + return u +} + +// SetWebPtyEnabled sets the "web_pty_enabled" field. +func (u *AgentUpsert) SetWebPtyEnabled(v bool) *AgentUpsert { + u.Set(agent.FieldWebPtyEnabled, v) + return u +} + +// UpdateWebPtyEnabled sets the "web_pty_enabled" field to the value that was provided on create. +func (u *AgentUpsert) UpdateWebPtyEnabled() *AgentUpsert { + u.SetExcluded(agent.FieldWebPtyEnabled) + return u +} + +// SetTaskSummary sets the "task_summary" field. +func (u *AgentUpsert) SetTaskSummary(v string) *AgentUpsert { + u.Set(agent.FieldTaskSummary, v) + return u +} + +// UpdateTaskSummary sets the "task_summary" field to the value that was provided on create. +func (u *AgentUpsert) UpdateTaskSummary() *AgentUpsert { + u.SetExcluded(agent.FieldTaskSummary) + return u +} + +// ClearTaskSummary clears the value of the "task_summary" field. +func (u *AgentUpsert) ClearTaskSummary() *AgentUpsert { + u.SetNull(agent.FieldTaskSummary) + return u +} + +// SetMessage sets the "message" field. +func (u *AgentUpsert) SetMessage(v string) *AgentUpsert { + u.Set(agent.FieldMessage, v) + return u +} + +// UpdateMessage sets the "message" field to the value that was provided on create. +func (u *AgentUpsert) UpdateMessage() *AgentUpsert { + u.SetExcluded(agent.FieldMessage) + return u +} + +// ClearMessage clears the value of the "message" field. +func (u *AgentUpsert) ClearMessage() *AgentUpsert { + u.SetNull(agent.FieldMessage) + return u +} + +// SetAppliedConfig sets the "applied_config" field. +func (u *AgentUpsert) SetAppliedConfig(v string) *AgentUpsert { + u.Set(agent.FieldAppliedConfig, v) + return u +} + +// UpdateAppliedConfig sets the "applied_config" field to the value that was provided on create. +func (u *AgentUpsert) UpdateAppliedConfig() *AgentUpsert { + u.SetExcluded(agent.FieldAppliedConfig) + return u +} + +// ClearAppliedConfig clears the value of the "applied_config" field. +func (u *AgentUpsert) ClearAppliedConfig() *AgentUpsert { + u.SetNull(agent.FieldAppliedConfig) + return u +} + +// SetAncestry sets the "ancestry" field. +func (u *AgentUpsert) SetAncestry(v []string) *AgentUpsert { + u.Set(agent.FieldAncestry, v) + return u +} + +// UpdateAncestry sets the "ancestry" field to the value that was provided on create. +func (u *AgentUpsert) UpdateAncestry() *AgentUpsert { + u.SetExcluded(agent.FieldAncestry) + return u +} + +// ClearAncestry clears the value of the "ancestry" field. +func (u *AgentUpsert) ClearAncestry() *AgentUpsert { + u.SetNull(agent.FieldAncestry) + return u +} + +// SetUpdated sets the "updated" field. +func (u *AgentUpsert) SetUpdated(v time.Time) *AgentUpsert { + u.Set(agent.FieldUpdated, v) + return u +} + +// UpdateUpdated sets the "updated" field to the value that was provided on create. +func (u *AgentUpsert) UpdateUpdated() *AgentUpsert { + u.SetExcluded(agent.FieldUpdated) + return u +} + +// SetLastSeen sets the "last_seen" field. +func (u *AgentUpsert) SetLastSeen(v time.Time) *AgentUpsert { + u.Set(agent.FieldLastSeen, v) + return u +} + +// UpdateLastSeen sets the "last_seen" field to the value that was provided on create. +func (u *AgentUpsert) UpdateLastSeen() *AgentUpsert { + u.SetExcluded(agent.FieldLastSeen) + return u +} + +// ClearLastSeen clears the value of the "last_seen" field. +func (u *AgentUpsert) ClearLastSeen() *AgentUpsert { + u.SetNull(agent.FieldLastSeen) + return u +} + +// SetLastActivityEvent sets the "last_activity_event" field. +func (u *AgentUpsert) SetLastActivityEvent(v time.Time) *AgentUpsert { + u.Set(agent.FieldLastActivityEvent, v) + return u +} + +// UpdateLastActivityEvent sets the "last_activity_event" field to the value that was provided on create. +func (u *AgentUpsert) UpdateLastActivityEvent() *AgentUpsert { + u.SetExcluded(agent.FieldLastActivityEvent) + return u +} + +// ClearLastActivityEvent clears the value of the "last_activity_event" field. +func (u *AgentUpsert) ClearLastActivityEvent() *AgentUpsert { + u.SetNull(agent.FieldLastActivityEvent) + return u +} + +// SetStartedAt sets the "started_at" field. +func (u *AgentUpsert) SetStartedAt(v time.Time) *AgentUpsert { + u.Set(agent.FieldStartedAt, v) + return u +} + +// UpdateStartedAt sets the "started_at" field to the value that was provided on create. +func (u *AgentUpsert) UpdateStartedAt() *AgentUpsert { + u.SetExcluded(agent.FieldStartedAt) + return u +} + +// ClearStartedAt clears the value of the "started_at" field. +func (u *AgentUpsert) ClearStartedAt() *AgentUpsert { + u.SetNull(agent.FieldStartedAt) + return u +} + +// SetDeletedAt sets the "deleted_at" field. +func (u *AgentUpsert) SetDeletedAt(v time.Time) *AgentUpsert { + u.Set(agent.FieldDeletedAt, v) + return u +} + +// UpdateDeletedAt sets the "deleted_at" field to the value that was provided on create. +func (u *AgentUpsert) UpdateDeletedAt() *AgentUpsert { + u.SetExcluded(agent.FieldDeletedAt) + return u +} + +// ClearDeletedAt clears the value of the "deleted_at" field. +func (u *AgentUpsert) ClearDeletedAt() *AgentUpsert { + u.SetNull(agent.FieldDeletedAt) + return u +} + +// SetStateVersion sets the "state_version" field. +func (u *AgentUpsert) SetStateVersion(v int64) *AgentUpsert { + u.Set(agent.FieldStateVersion, v) + return u +} + +// UpdateStateVersion sets the "state_version" field to the value that was provided on create. +func (u *AgentUpsert) UpdateStateVersion() *AgentUpsert { + u.SetExcluded(agent.FieldStateVersion) + return u +} + +// AddStateVersion adds v to the "state_version" field. +func (u *AgentUpsert) AddStateVersion(v int64) *AgentUpsert { + u.Add(agent.FieldStateVersion, v) + return u +} + +// UpdateNewValues updates the mutable fields using the new values that were set on create except the ID field. +// Using this option is equivalent to using: +// +// client.Agent.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(agent.FieldID) +// }), +// ). +// Exec(ctx) +func (u *AgentUpsertOne) UpdateNewValues() *AgentUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + if _, exists := u.create.mutation.ID(); exists { + s.SetIgnore(agent.FieldID) + } + if _, exists := u.create.mutation.Created(); exists { + s.SetIgnore(agent.FieldCreated) + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.Agent.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *AgentUpsertOne) Ignore() *AgentUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *AgentUpsertOne) DoNothing() *AgentUpsertOne { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the AgentCreate.OnConflict +// documentation for more info. +func (u *AgentUpsertOne) Update(set func(*AgentUpsert)) *AgentUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&AgentUpsert{UpdateSet: update}) + })) + return u +} + +// SetSlug sets the "slug" field. +func (u *AgentUpsertOne) SetSlug(v string) *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.SetSlug(v) + }) +} + +// UpdateSlug sets the "slug" field to the value that was provided on create. +func (u *AgentUpsertOne) UpdateSlug() *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.UpdateSlug() + }) +} + +// SetName sets the "name" field. +func (u *AgentUpsertOne) SetName(v string) *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.SetName(v) + }) +} + +// UpdateName sets the "name" field to the value that was provided on create. +func (u *AgentUpsertOne) UpdateName() *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.UpdateName() + }) +} + +// SetTemplate sets the "template" field. +func (u *AgentUpsertOne) SetTemplate(v string) *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.SetTemplate(v) + }) +} + +// UpdateTemplate sets the "template" field to the value that was provided on create. +func (u *AgentUpsertOne) UpdateTemplate() *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.UpdateTemplate() + }) +} + +// ClearTemplate clears the value of the "template" field. +func (u *AgentUpsertOne) ClearTemplate() *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.ClearTemplate() + }) +} + +// SetProjectID sets the "project_id" field. +func (u *AgentUpsertOne) SetProjectID(v uuid.UUID) *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.SetProjectID(v) + }) +} + +// UpdateProjectID sets the "project_id" field to the value that was provided on create. +func (u *AgentUpsertOne) UpdateProjectID() *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.UpdateProjectID() + }) +} + +// SetStatus sets the "status" field. +func (u *AgentUpsertOne) SetStatus(v agent.Status) *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.SetStatus(v) + }) +} + +// UpdateStatus sets the "status" field to the value that was provided on create. +func (u *AgentUpsertOne) UpdateStatus() *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.UpdateStatus() + }) +} + +// SetCreatedBy sets the "created_by" field. +func (u *AgentUpsertOne) SetCreatedBy(v uuid.UUID) *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.SetCreatedBy(v) + }) +} + +// UpdateCreatedBy sets the "created_by" field to the value that was provided on create. +func (u *AgentUpsertOne) UpdateCreatedBy() *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.UpdateCreatedBy() + }) +} + +// ClearCreatedBy clears the value of the "created_by" field. +func (u *AgentUpsertOne) ClearCreatedBy() *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.ClearCreatedBy() + }) +} + +// SetOwnerID sets the "owner_id" field. +func (u *AgentUpsertOne) SetOwnerID(v uuid.UUID) *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.SetOwnerID(v) + }) +} + +// UpdateOwnerID sets the "owner_id" field to the value that was provided on create. +func (u *AgentUpsertOne) UpdateOwnerID() *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.UpdateOwnerID() + }) +} + +// ClearOwnerID clears the value of the "owner_id" field. +func (u *AgentUpsertOne) ClearOwnerID() *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.ClearOwnerID() + }) +} + +// SetDelegationEnabled sets the "delegation_enabled" field. +func (u *AgentUpsertOne) SetDelegationEnabled(v bool) *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.SetDelegationEnabled(v) + }) +} + +// UpdateDelegationEnabled sets the "delegation_enabled" field to the value that was provided on create. +func (u *AgentUpsertOne) UpdateDelegationEnabled() *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.UpdateDelegationEnabled() + }) +} + +// SetVisibility sets the "visibility" field. +func (u *AgentUpsertOne) SetVisibility(v string) *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.SetVisibility(v) + }) +} + +// UpdateVisibility sets the "visibility" field to the value that was provided on create. +func (u *AgentUpsertOne) UpdateVisibility() *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.UpdateVisibility() + }) +} + +// SetLabels sets the "labels" field. +func (u *AgentUpsertOne) SetLabels(v map[string]string) *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.SetLabels(v) + }) +} + +// UpdateLabels sets the "labels" field to the value that was provided on create. +func (u *AgentUpsertOne) UpdateLabels() *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.UpdateLabels() + }) +} + +// ClearLabels clears the value of the "labels" field. +func (u *AgentUpsertOne) ClearLabels() *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.ClearLabels() + }) +} + +// SetAnnotations sets the "annotations" field. +func (u *AgentUpsertOne) SetAnnotations(v map[string]string) *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.SetAnnotations(v) + }) +} + +// UpdateAnnotations sets the "annotations" field to the value that was provided on create. +func (u *AgentUpsertOne) UpdateAnnotations() *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.UpdateAnnotations() + }) +} + +// ClearAnnotations clears the value of the "annotations" field. +func (u *AgentUpsertOne) ClearAnnotations() *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.ClearAnnotations() + }) +} + +// SetPhase sets the "phase" field. +func (u *AgentUpsertOne) SetPhase(v string) *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.SetPhase(v) + }) +} + +// UpdatePhase sets the "phase" field to the value that was provided on create. +func (u *AgentUpsertOne) UpdatePhase() *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.UpdatePhase() + }) +} + +// ClearPhase clears the value of the "phase" field. +func (u *AgentUpsertOne) ClearPhase() *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.ClearPhase() + }) +} + +// SetActivity sets the "activity" field. +func (u *AgentUpsertOne) SetActivity(v string) *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.SetActivity(v) + }) +} + +// UpdateActivity sets the "activity" field to the value that was provided on create. +func (u *AgentUpsertOne) UpdateActivity() *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.UpdateActivity() + }) +} + +// ClearActivity clears the value of the "activity" field. +func (u *AgentUpsertOne) ClearActivity() *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.ClearActivity() + }) +} + +// SetToolName sets the "tool_name" field. +func (u *AgentUpsertOne) SetToolName(v string) *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.SetToolName(v) + }) +} + +// UpdateToolName sets the "tool_name" field to the value that was provided on create. +func (u *AgentUpsertOne) UpdateToolName() *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.UpdateToolName() + }) +} + +// ClearToolName clears the value of the "tool_name" field. +func (u *AgentUpsertOne) ClearToolName() *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.ClearToolName() + }) +} + +// SetConnectionState sets the "connection_state" field. +func (u *AgentUpsertOne) SetConnectionState(v string) *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.SetConnectionState(v) + }) +} + +// UpdateConnectionState sets the "connection_state" field to the value that was provided on create. +func (u *AgentUpsertOne) UpdateConnectionState() *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.UpdateConnectionState() + }) +} + +// ClearConnectionState clears the value of the "connection_state" field. +func (u *AgentUpsertOne) ClearConnectionState() *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.ClearConnectionState() + }) +} + +// SetContainerStatus sets the "container_status" field. +func (u *AgentUpsertOne) SetContainerStatus(v string) *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.SetContainerStatus(v) + }) +} + +// UpdateContainerStatus sets the "container_status" field to the value that was provided on create. +func (u *AgentUpsertOne) UpdateContainerStatus() *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.UpdateContainerStatus() + }) +} + +// ClearContainerStatus clears the value of the "container_status" field. +func (u *AgentUpsertOne) ClearContainerStatus() *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.ClearContainerStatus() + }) +} + +// SetRuntimeState sets the "runtime_state" field. +func (u *AgentUpsertOne) SetRuntimeState(v string) *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.SetRuntimeState(v) + }) +} + +// UpdateRuntimeState sets the "runtime_state" field to the value that was provided on create. +func (u *AgentUpsertOne) UpdateRuntimeState() *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.UpdateRuntimeState() + }) +} + +// ClearRuntimeState clears the value of the "runtime_state" field. +func (u *AgentUpsertOne) ClearRuntimeState() *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.ClearRuntimeState() + }) +} + +// SetStalledFromActivity sets the "stalled_from_activity" field. +func (u *AgentUpsertOne) SetStalledFromActivity(v string) *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.SetStalledFromActivity(v) + }) +} + +// UpdateStalledFromActivity sets the "stalled_from_activity" field to the value that was provided on create. +func (u *AgentUpsertOne) UpdateStalledFromActivity() *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.UpdateStalledFromActivity() + }) +} + +// ClearStalledFromActivity clears the value of the "stalled_from_activity" field. +func (u *AgentUpsertOne) ClearStalledFromActivity() *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.ClearStalledFromActivity() + }) +} + +// SetCurrentTurns sets the "current_turns" field. +func (u *AgentUpsertOne) SetCurrentTurns(v int) *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.SetCurrentTurns(v) + }) +} + +// AddCurrentTurns adds v to the "current_turns" field. +func (u *AgentUpsertOne) AddCurrentTurns(v int) *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.AddCurrentTurns(v) + }) +} + +// UpdateCurrentTurns sets the "current_turns" field to the value that was provided on create. +func (u *AgentUpsertOne) UpdateCurrentTurns() *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.UpdateCurrentTurns() + }) +} + +// SetCurrentModelCalls sets the "current_model_calls" field. +func (u *AgentUpsertOne) SetCurrentModelCalls(v int) *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.SetCurrentModelCalls(v) + }) +} + +// AddCurrentModelCalls adds v to the "current_model_calls" field. +func (u *AgentUpsertOne) AddCurrentModelCalls(v int) *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.AddCurrentModelCalls(v) + }) +} + +// UpdateCurrentModelCalls sets the "current_model_calls" field to the value that was provided on create. +func (u *AgentUpsertOne) UpdateCurrentModelCalls() *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.UpdateCurrentModelCalls() + }) +} + +// SetImage sets the "image" field. +func (u *AgentUpsertOne) SetImage(v string) *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.SetImage(v) + }) +} + +// UpdateImage sets the "image" field to the value that was provided on create. +func (u *AgentUpsertOne) UpdateImage() *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.UpdateImage() + }) +} + +// ClearImage clears the value of the "image" field. +func (u *AgentUpsertOne) ClearImage() *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.ClearImage() + }) +} + +// SetDetached sets the "detached" field. +func (u *AgentUpsertOne) SetDetached(v bool) *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.SetDetached(v) + }) +} + +// UpdateDetached sets the "detached" field to the value that was provided on create. +func (u *AgentUpsertOne) UpdateDetached() *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.UpdateDetached() + }) +} + +// SetRuntime sets the "runtime" field. +func (u *AgentUpsertOne) SetRuntime(v string) *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.SetRuntime(v) + }) +} + +// UpdateRuntime sets the "runtime" field to the value that was provided on create. +func (u *AgentUpsertOne) UpdateRuntime() *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.UpdateRuntime() + }) +} + +// ClearRuntime clears the value of the "runtime" field. +func (u *AgentUpsertOne) ClearRuntime() *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.ClearRuntime() + }) +} + +// SetRuntimeBrokerID sets the "runtime_broker_id" field. +func (u *AgentUpsertOne) SetRuntimeBrokerID(v string) *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.SetRuntimeBrokerID(v) + }) +} + +// UpdateRuntimeBrokerID sets the "runtime_broker_id" field to the value that was provided on create. +func (u *AgentUpsertOne) UpdateRuntimeBrokerID() *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.UpdateRuntimeBrokerID() + }) +} + +// ClearRuntimeBrokerID clears the value of the "runtime_broker_id" field. +func (u *AgentUpsertOne) ClearRuntimeBrokerID() *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.ClearRuntimeBrokerID() + }) +} + +// SetWebPtyEnabled sets the "web_pty_enabled" field. +func (u *AgentUpsertOne) SetWebPtyEnabled(v bool) *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.SetWebPtyEnabled(v) + }) +} + +// UpdateWebPtyEnabled sets the "web_pty_enabled" field to the value that was provided on create. +func (u *AgentUpsertOne) UpdateWebPtyEnabled() *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.UpdateWebPtyEnabled() + }) +} + +// SetTaskSummary sets the "task_summary" field. +func (u *AgentUpsertOne) SetTaskSummary(v string) *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.SetTaskSummary(v) + }) +} + +// UpdateTaskSummary sets the "task_summary" field to the value that was provided on create. +func (u *AgentUpsertOne) UpdateTaskSummary() *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.UpdateTaskSummary() + }) +} + +// ClearTaskSummary clears the value of the "task_summary" field. +func (u *AgentUpsertOne) ClearTaskSummary() *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.ClearTaskSummary() + }) +} + +// SetMessage sets the "message" field. +func (u *AgentUpsertOne) SetMessage(v string) *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.SetMessage(v) + }) +} + +// UpdateMessage sets the "message" field to the value that was provided on create. +func (u *AgentUpsertOne) UpdateMessage() *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.UpdateMessage() + }) +} + +// ClearMessage clears the value of the "message" field. +func (u *AgentUpsertOne) ClearMessage() *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.ClearMessage() + }) +} + +// SetAppliedConfig sets the "applied_config" field. +func (u *AgentUpsertOne) SetAppliedConfig(v string) *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.SetAppliedConfig(v) + }) +} + +// UpdateAppliedConfig sets the "applied_config" field to the value that was provided on create. +func (u *AgentUpsertOne) UpdateAppliedConfig() *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.UpdateAppliedConfig() + }) +} + +// ClearAppliedConfig clears the value of the "applied_config" field. +func (u *AgentUpsertOne) ClearAppliedConfig() *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.ClearAppliedConfig() + }) +} + +// SetAncestry sets the "ancestry" field. +func (u *AgentUpsertOne) SetAncestry(v []string) *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.SetAncestry(v) + }) +} + +// UpdateAncestry sets the "ancestry" field to the value that was provided on create. +func (u *AgentUpsertOne) UpdateAncestry() *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.UpdateAncestry() + }) +} + +// ClearAncestry clears the value of the "ancestry" field. +func (u *AgentUpsertOne) ClearAncestry() *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.ClearAncestry() + }) +} + +// SetUpdated sets the "updated" field. +func (u *AgentUpsertOne) SetUpdated(v time.Time) *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.SetUpdated(v) + }) +} + +// UpdateUpdated sets the "updated" field to the value that was provided on create. +func (u *AgentUpsertOne) UpdateUpdated() *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.UpdateUpdated() + }) +} + +// SetLastSeen sets the "last_seen" field. +func (u *AgentUpsertOne) SetLastSeen(v time.Time) *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.SetLastSeen(v) + }) +} + +// UpdateLastSeen sets the "last_seen" field to the value that was provided on create. +func (u *AgentUpsertOne) UpdateLastSeen() *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.UpdateLastSeen() + }) +} + +// ClearLastSeen clears the value of the "last_seen" field. +func (u *AgentUpsertOne) ClearLastSeen() *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.ClearLastSeen() + }) +} + +// SetLastActivityEvent sets the "last_activity_event" field. +func (u *AgentUpsertOne) SetLastActivityEvent(v time.Time) *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.SetLastActivityEvent(v) + }) +} + +// UpdateLastActivityEvent sets the "last_activity_event" field to the value that was provided on create. +func (u *AgentUpsertOne) UpdateLastActivityEvent() *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.UpdateLastActivityEvent() + }) +} + +// ClearLastActivityEvent clears the value of the "last_activity_event" field. +func (u *AgentUpsertOne) ClearLastActivityEvent() *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.ClearLastActivityEvent() + }) +} + +// SetStartedAt sets the "started_at" field. +func (u *AgentUpsertOne) SetStartedAt(v time.Time) *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.SetStartedAt(v) + }) +} + +// UpdateStartedAt sets the "started_at" field to the value that was provided on create. +func (u *AgentUpsertOne) UpdateStartedAt() *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.UpdateStartedAt() + }) +} + +// ClearStartedAt clears the value of the "started_at" field. +func (u *AgentUpsertOne) ClearStartedAt() *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.ClearStartedAt() + }) +} + +// SetDeletedAt sets the "deleted_at" field. +func (u *AgentUpsertOne) SetDeletedAt(v time.Time) *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.SetDeletedAt(v) + }) +} + +// UpdateDeletedAt sets the "deleted_at" field to the value that was provided on create. +func (u *AgentUpsertOne) UpdateDeletedAt() *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.UpdateDeletedAt() + }) +} + +// ClearDeletedAt clears the value of the "deleted_at" field. +func (u *AgentUpsertOne) ClearDeletedAt() *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.ClearDeletedAt() + }) +} + +// SetStateVersion sets the "state_version" field. +func (u *AgentUpsertOne) SetStateVersion(v int64) *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.SetStateVersion(v) + }) +} + +// AddStateVersion adds v to the "state_version" field. +func (u *AgentUpsertOne) AddStateVersion(v int64) *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.AddStateVersion(v) + }) +} + +// UpdateStateVersion sets the "state_version" field to the value that was provided on create. +func (u *AgentUpsertOne) UpdateStateVersion() *AgentUpsertOne { + return u.Update(func(s *AgentUpsert) { + s.UpdateStateVersion() + }) +} + +// Exec executes the query. +func (u *AgentUpsertOne) Exec(ctx context.Context) error { + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for AgentCreate.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *AgentUpsertOne) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} + +// Exec executes the UPSERT query and returns the inserted/updated ID. +func (u *AgentUpsertOne) ID(ctx context.Context) (id uuid.UUID, err error) { + if u.create.driver.Dialect() == dialect.MySQL { + // In case of "ON CONFLICT", there is no way to get back non-numeric ID + // fields from the database since MySQL does not support the RETURNING clause. + return id, errors.New("ent: AgentUpsertOne.ID is not supported by MySQL driver. Use AgentUpsertOne.Exec instead") + } + node, err := u.create.Save(ctx) + if err != nil { + return id, err + } + return node.ID, nil +} + +// IDX is like ID, but panics if an error occurs. +func (u *AgentUpsertOne) IDX(ctx context.Context) uuid.UUID { + id, err := u.ID(ctx) + if err != nil { + panic(err) + } + return id +} + +// AgentCreateBulk is the builder for creating many Agent entities in bulk. +type AgentCreateBulk struct { + config + err error + builders []*AgentCreate + conflict []sql.ConflictOption +} + +// Save creates the Agent entities in the database. +func (_c *AgentCreateBulk) Save(ctx context.Context) ([]*Agent, error) { + if _c.err != nil { + return nil, _c.err + } + specs := make([]*sqlgraph.CreateSpec, len(_c.builders)) + nodes := make([]*Agent, len(_c.builders)) + mutators := make([]Mutator, len(_c.builders)) + for i := range _c.builders { + func(i int, root context.Context) { + builder := _c.builders[i] + builder.defaults() + var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { + mutation, ok := m.(*AgentMutation) + if !ok { + return nil, fmt.Errorf("unexpected mutation type %T", m) + } + if err := builder.check(); err != nil { + return nil, err + } + builder.mutation = mutation + var err error + nodes[i], specs[i] = builder.createSpec() + if i < len(mutators)-1 { + _, err = mutators[i+1].Mutate(root, _c.builders[i+1].mutation) + } else { + spec := &sqlgraph.BatchCreateSpec{Nodes: specs} + spec.OnConflict = _c.conflict + // Invoke the actual operation on the latest mutation in the chain. + if err = sqlgraph.BatchCreate(ctx, _c.driver, spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} } } } @@ -569,3 +2422,792 @@ func (_c *AgentCreateBulk) ExecX(ctx context.Context) { panic(err) } } + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.Agent.CreateBulk(builders...). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.AgentUpsert) { +// SetSlug(v+v). +// }). +// Exec(ctx) +func (_c *AgentCreateBulk) OnConflict(opts ...sql.ConflictOption) *AgentUpsertBulk { + _c.conflict = opts + return &AgentUpsertBulk{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.Agent.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *AgentCreateBulk) OnConflictColumns(columns ...string) *AgentUpsertBulk { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &AgentUpsertBulk{ + create: _c, + } +} + +// AgentUpsertBulk is the builder for "upsert"-ing +// a bulk of Agent nodes. +type AgentUpsertBulk struct { + create *AgentCreateBulk +} + +// UpdateNewValues updates the mutable fields using the new values that +// were set on create. Using this option is equivalent to using: +// +// client.Agent.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(agent.FieldID) +// }), +// ). +// Exec(ctx) +func (u *AgentUpsertBulk) UpdateNewValues() *AgentUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + for _, b := range u.create.builders { + if _, exists := b.mutation.ID(); exists { + s.SetIgnore(agent.FieldID) + } + if _, exists := b.mutation.Created(); exists { + s.SetIgnore(agent.FieldCreated) + } + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.Agent.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *AgentUpsertBulk) Ignore() *AgentUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *AgentUpsertBulk) DoNothing() *AgentUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the AgentCreateBulk.OnConflict +// documentation for more info. +func (u *AgentUpsertBulk) Update(set func(*AgentUpsert)) *AgentUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&AgentUpsert{UpdateSet: update}) + })) + return u +} + +// SetSlug sets the "slug" field. +func (u *AgentUpsertBulk) SetSlug(v string) *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.SetSlug(v) + }) +} + +// UpdateSlug sets the "slug" field to the value that was provided on create. +func (u *AgentUpsertBulk) UpdateSlug() *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.UpdateSlug() + }) +} + +// SetName sets the "name" field. +func (u *AgentUpsertBulk) SetName(v string) *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.SetName(v) + }) +} + +// UpdateName sets the "name" field to the value that was provided on create. +func (u *AgentUpsertBulk) UpdateName() *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.UpdateName() + }) +} + +// SetTemplate sets the "template" field. +func (u *AgentUpsertBulk) SetTemplate(v string) *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.SetTemplate(v) + }) +} + +// UpdateTemplate sets the "template" field to the value that was provided on create. +func (u *AgentUpsertBulk) UpdateTemplate() *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.UpdateTemplate() + }) +} + +// ClearTemplate clears the value of the "template" field. +func (u *AgentUpsertBulk) ClearTemplate() *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.ClearTemplate() + }) +} + +// SetProjectID sets the "project_id" field. +func (u *AgentUpsertBulk) SetProjectID(v uuid.UUID) *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.SetProjectID(v) + }) +} + +// UpdateProjectID sets the "project_id" field to the value that was provided on create. +func (u *AgentUpsertBulk) UpdateProjectID() *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.UpdateProjectID() + }) +} + +// SetStatus sets the "status" field. +func (u *AgentUpsertBulk) SetStatus(v agent.Status) *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.SetStatus(v) + }) +} + +// UpdateStatus sets the "status" field to the value that was provided on create. +func (u *AgentUpsertBulk) UpdateStatus() *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.UpdateStatus() + }) +} + +// SetCreatedBy sets the "created_by" field. +func (u *AgentUpsertBulk) SetCreatedBy(v uuid.UUID) *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.SetCreatedBy(v) + }) +} + +// UpdateCreatedBy sets the "created_by" field to the value that was provided on create. +func (u *AgentUpsertBulk) UpdateCreatedBy() *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.UpdateCreatedBy() + }) +} + +// ClearCreatedBy clears the value of the "created_by" field. +func (u *AgentUpsertBulk) ClearCreatedBy() *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.ClearCreatedBy() + }) +} + +// SetOwnerID sets the "owner_id" field. +func (u *AgentUpsertBulk) SetOwnerID(v uuid.UUID) *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.SetOwnerID(v) + }) +} + +// UpdateOwnerID sets the "owner_id" field to the value that was provided on create. +func (u *AgentUpsertBulk) UpdateOwnerID() *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.UpdateOwnerID() + }) +} + +// ClearOwnerID clears the value of the "owner_id" field. +func (u *AgentUpsertBulk) ClearOwnerID() *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.ClearOwnerID() + }) +} + +// SetDelegationEnabled sets the "delegation_enabled" field. +func (u *AgentUpsertBulk) SetDelegationEnabled(v bool) *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.SetDelegationEnabled(v) + }) +} + +// UpdateDelegationEnabled sets the "delegation_enabled" field to the value that was provided on create. +func (u *AgentUpsertBulk) UpdateDelegationEnabled() *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.UpdateDelegationEnabled() + }) +} + +// SetVisibility sets the "visibility" field. +func (u *AgentUpsertBulk) SetVisibility(v string) *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.SetVisibility(v) + }) +} + +// UpdateVisibility sets the "visibility" field to the value that was provided on create. +func (u *AgentUpsertBulk) UpdateVisibility() *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.UpdateVisibility() + }) +} + +// SetLabels sets the "labels" field. +func (u *AgentUpsertBulk) SetLabels(v map[string]string) *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.SetLabels(v) + }) +} + +// UpdateLabels sets the "labels" field to the value that was provided on create. +func (u *AgentUpsertBulk) UpdateLabels() *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.UpdateLabels() + }) +} + +// ClearLabels clears the value of the "labels" field. +func (u *AgentUpsertBulk) ClearLabels() *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.ClearLabels() + }) +} + +// SetAnnotations sets the "annotations" field. +func (u *AgentUpsertBulk) SetAnnotations(v map[string]string) *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.SetAnnotations(v) + }) +} + +// UpdateAnnotations sets the "annotations" field to the value that was provided on create. +func (u *AgentUpsertBulk) UpdateAnnotations() *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.UpdateAnnotations() + }) +} + +// ClearAnnotations clears the value of the "annotations" field. +func (u *AgentUpsertBulk) ClearAnnotations() *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.ClearAnnotations() + }) +} + +// SetPhase sets the "phase" field. +func (u *AgentUpsertBulk) SetPhase(v string) *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.SetPhase(v) + }) +} + +// UpdatePhase sets the "phase" field to the value that was provided on create. +func (u *AgentUpsertBulk) UpdatePhase() *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.UpdatePhase() + }) +} + +// ClearPhase clears the value of the "phase" field. +func (u *AgentUpsertBulk) ClearPhase() *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.ClearPhase() + }) +} + +// SetActivity sets the "activity" field. +func (u *AgentUpsertBulk) SetActivity(v string) *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.SetActivity(v) + }) +} + +// UpdateActivity sets the "activity" field to the value that was provided on create. +func (u *AgentUpsertBulk) UpdateActivity() *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.UpdateActivity() + }) +} + +// ClearActivity clears the value of the "activity" field. +func (u *AgentUpsertBulk) ClearActivity() *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.ClearActivity() + }) +} + +// SetToolName sets the "tool_name" field. +func (u *AgentUpsertBulk) SetToolName(v string) *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.SetToolName(v) + }) +} + +// UpdateToolName sets the "tool_name" field to the value that was provided on create. +func (u *AgentUpsertBulk) UpdateToolName() *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.UpdateToolName() + }) +} + +// ClearToolName clears the value of the "tool_name" field. +func (u *AgentUpsertBulk) ClearToolName() *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.ClearToolName() + }) +} + +// SetConnectionState sets the "connection_state" field. +func (u *AgentUpsertBulk) SetConnectionState(v string) *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.SetConnectionState(v) + }) +} + +// UpdateConnectionState sets the "connection_state" field to the value that was provided on create. +func (u *AgentUpsertBulk) UpdateConnectionState() *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.UpdateConnectionState() + }) +} + +// ClearConnectionState clears the value of the "connection_state" field. +func (u *AgentUpsertBulk) ClearConnectionState() *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.ClearConnectionState() + }) +} + +// SetContainerStatus sets the "container_status" field. +func (u *AgentUpsertBulk) SetContainerStatus(v string) *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.SetContainerStatus(v) + }) +} + +// UpdateContainerStatus sets the "container_status" field to the value that was provided on create. +func (u *AgentUpsertBulk) UpdateContainerStatus() *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.UpdateContainerStatus() + }) +} + +// ClearContainerStatus clears the value of the "container_status" field. +func (u *AgentUpsertBulk) ClearContainerStatus() *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.ClearContainerStatus() + }) +} + +// SetRuntimeState sets the "runtime_state" field. +func (u *AgentUpsertBulk) SetRuntimeState(v string) *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.SetRuntimeState(v) + }) +} + +// UpdateRuntimeState sets the "runtime_state" field to the value that was provided on create. +func (u *AgentUpsertBulk) UpdateRuntimeState() *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.UpdateRuntimeState() + }) +} + +// ClearRuntimeState clears the value of the "runtime_state" field. +func (u *AgentUpsertBulk) ClearRuntimeState() *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.ClearRuntimeState() + }) +} + +// SetStalledFromActivity sets the "stalled_from_activity" field. +func (u *AgentUpsertBulk) SetStalledFromActivity(v string) *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.SetStalledFromActivity(v) + }) +} + +// UpdateStalledFromActivity sets the "stalled_from_activity" field to the value that was provided on create. +func (u *AgentUpsertBulk) UpdateStalledFromActivity() *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.UpdateStalledFromActivity() + }) +} + +// ClearStalledFromActivity clears the value of the "stalled_from_activity" field. +func (u *AgentUpsertBulk) ClearStalledFromActivity() *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.ClearStalledFromActivity() + }) +} + +// SetCurrentTurns sets the "current_turns" field. +func (u *AgentUpsertBulk) SetCurrentTurns(v int) *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.SetCurrentTurns(v) + }) +} + +// AddCurrentTurns adds v to the "current_turns" field. +func (u *AgentUpsertBulk) AddCurrentTurns(v int) *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.AddCurrentTurns(v) + }) +} + +// UpdateCurrentTurns sets the "current_turns" field to the value that was provided on create. +func (u *AgentUpsertBulk) UpdateCurrentTurns() *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.UpdateCurrentTurns() + }) +} + +// SetCurrentModelCalls sets the "current_model_calls" field. +func (u *AgentUpsertBulk) SetCurrentModelCalls(v int) *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.SetCurrentModelCalls(v) + }) +} + +// AddCurrentModelCalls adds v to the "current_model_calls" field. +func (u *AgentUpsertBulk) AddCurrentModelCalls(v int) *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.AddCurrentModelCalls(v) + }) +} + +// UpdateCurrentModelCalls sets the "current_model_calls" field to the value that was provided on create. +func (u *AgentUpsertBulk) UpdateCurrentModelCalls() *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.UpdateCurrentModelCalls() + }) +} + +// SetImage sets the "image" field. +func (u *AgentUpsertBulk) SetImage(v string) *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.SetImage(v) + }) +} + +// UpdateImage sets the "image" field to the value that was provided on create. +func (u *AgentUpsertBulk) UpdateImage() *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.UpdateImage() + }) +} + +// ClearImage clears the value of the "image" field. +func (u *AgentUpsertBulk) ClearImage() *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.ClearImage() + }) +} + +// SetDetached sets the "detached" field. +func (u *AgentUpsertBulk) SetDetached(v bool) *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.SetDetached(v) + }) +} + +// UpdateDetached sets the "detached" field to the value that was provided on create. +func (u *AgentUpsertBulk) UpdateDetached() *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.UpdateDetached() + }) +} + +// SetRuntime sets the "runtime" field. +func (u *AgentUpsertBulk) SetRuntime(v string) *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.SetRuntime(v) + }) +} + +// UpdateRuntime sets the "runtime" field to the value that was provided on create. +func (u *AgentUpsertBulk) UpdateRuntime() *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.UpdateRuntime() + }) +} + +// ClearRuntime clears the value of the "runtime" field. +func (u *AgentUpsertBulk) ClearRuntime() *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.ClearRuntime() + }) +} + +// SetRuntimeBrokerID sets the "runtime_broker_id" field. +func (u *AgentUpsertBulk) SetRuntimeBrokerID(v string) *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.SetRuntimeBrokerID(v) + }) +} + +// UpdateRuntimeBrokerID sets the "runtime_broker_id" field to the value that was provided on create. +func (u *AgentUpsertBulk) UpdateRuntimeBrokerID() *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.UpdateRuntimeBrokerID() + }) +} + +// ClearRuntimeBrokerID clears the value of the "runtime_broker_id" field. +func (u *AgentUpsertBulk) ClearRuntimeBrokerID() *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.ClearRuntimeBrokerID() + }) +} + +// SetWebPtyEnabled sets the "web_pty_enabled" field. +func (u *AgentUpsertBulk) SetWebPtyEnabled(v bool) *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.SetWebPtyEnabled(v) + }) +} + +// UpdateWebPtyEnabled sets the "web_pty_enabled" field to the value that was provided on create. +func (u *AgentUpsertBulk) UpdateWebPtyEnabled() *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.UpdateWebPtyEnabled() + }) +} + +// SetTaskSummary sets the "task_summary" field. +func (u *AgentUpsertBulk) SetTaskSummary(v string) *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.SetTaskSummary(v) + }) +} + +// UpdateTaskSummary sets the "task_summary" field to the value that was provided on create. +func (u *AgentUpsertBulk) UpdateTaskSummary() *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.UpdateTaskSummary() + }) +} + +// ClearTaskSummary clears the value of the "task_summary" field. +func (u *AgentUpsertBulk) ClearTaskSummary() *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.ClearTaskSummary() + }) +} + +// SetMessage sets the "message" field. +func (u *AgentUpsertBulk) SetMessage(v string) *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.SetMessage(v) + }) +} + +// UpdateMessage sets the "message" field to the value that was provided on create. +func (u *AgentUpsertBulk) UpdateMessage() *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.UpdateMessage() + }) +} + +// ClearMessage clears the value of the "message" field. +func (u *AgentUpsertBulk) ClearMessage() *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.ClearMessage() + }) +} + +// SetAppliedConfig sets the "applied_config" field. +func (u *AgentUpsertBulk) SetAppliedConfig(v string) *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.SetAppliedConfig(v) + }) +} + +// UpdateAppliedConfig sets the "applied_config" field to the value that was provided on create. +func (u *AgentUpsertBulk) UpdateAppliedConfig() *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.UpdateAppliedConfig() + }) +} + +// ClearAppliedConfig clears the value of the "applied_config" field. +func (u *AgentUpsertBulk) ClearAppliedConfig() *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.ClearAppliedConfig() + }) +} + +// SetAncestry sets the "ancestry" field. +func (u *AgentUpsertBulk) SetAncestry(v []string) *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.SetAncestry(v) + }) +} + +// UpdateAncestry sets the "ancestry" field to the value that was provided on create. +func (u *AgentUpsertBulk) UpdateAncestry() *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.UpdateAncestry() + }) +} + +// ClearAncestry clears the value of the "ancestry" field. +func (u *AgentUpsertBulk) ClearAncestry() *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.ClearAncestry() + }) +} + +// SetUpdated sets the "updated" field. +func (u *AgentUpsertBulk) SetUpdated(v time.Time) *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.SetUpdated(v) + }) +} + +// UpdateUpdated sets the "updated" field to the value that was provided on create. +func (u *AgentUpsertBulk) UpdateUpdated() *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.UpdateUpdated() + }) +} + +// SetLastSeen sets the "last_seen" field. +func (u *AgentUpsertBulk) SetLastSeen(v time.Time) *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.SetLastSeen(v) + }) +} + +// UpdateLastSeen sets the "last_seen" field to the value that was provided on create. +func (u *AgentUpsertBulk) UpdateLastSeen() *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.UpdateLastSeen() + }) +} + +// ClearLastSeen clears the value of the "last_seen" field. +func (u *AgentUpsertBulk) ClearLastSeen() *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.ClearLastSeen() + }) +} + +// SetLastActivityEvent sets the "last_activity_event" field. +func (u *AgentUpsertBulk) SetLastActivityEvent(v time.Time) *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.SetLastActivityEvent(v) + }) +} + +// UpdateLastActivityEvent sets the "last_activity_event" field to the value that was provided on create. +func (u *AgentUpsertBulk) UpdateLastActivityEvent() *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.UpdateLastActivityEvent() + }) +} + +// ClearLastActivityEvent clears the value of the "last_activity_event" field. +func (u *AgentUpsertBulk) ClearLastActivityEvent() *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.ClearLastActivityEvent() + }) +} + +// SetStartedAt sets the "started_at" field. +func (u *AgentUpsertBulk) SetStartedAt(v time.Time) *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.SetStartedAt(v) + }) +} + +// UpdateStartedAt sets the "started_at" field to the value that was provided on create. +func (u *AgentUpsertBulk) UpdateStartedAt() *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.UpdateStartedAt() + }) +} + +// ClearStartedAt clears the value of the "started_at" field. +func (u *AgentUpsertBulk) ClearStartedAt() *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.ClearStartedAt() + }) +} + +// SetDeletedAt sets the "deleted_at" field. +func (u *AgentUpsertBulk) SetDeletedAt(v time.Time) *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.SetDeletedAt(v) + }) +} + +// UpdateDeletedAt sets the "deleted_at" field to the value that was provided on create. +func (u *AgentUpsertBulk) UpdateDeletedAt() *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.UpdateDeletedAt() + }) +} + +// ClearDeletedAt clears the value of the "deleted_at" field. +func (u *AgentUpsertBulk) ClearDeletedAt() *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.ClearDeletedAt() + }) +} + +// SetStateVersion sets the "state_version" field. +func (u *AgentUpsertBulk) SetStateVersion(v int64) *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.SetStateVersion(v) + }) +} + +// AddStateVersion adds v to the "state_version" field. +func (u *AgentUpsertBulk) AddStateVersion(v int64) *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.AddStateVersion(v) + }) +} + +// UpdateStateVersion sets the "state_version" field to the value that was provided on create. +func (u *AgentUpsertBulk) UpdateStateVersion() *AgentUpsertBulk { + return u.Update(func(s *AgentUpsert) { + s.UpdateStateVersion() + }) +} + +// Exec executes the query. +func (u *AgentUpsertBulk) Exec(ctx context.Context) error { + if u.create.err != nil { + return u.create.err + } + for i, b := range u.create.builders { + if len(b.conflict) != 0 { + return fmt.Errorf("ent: OnConflict was set for builder %d. Set it on the AgentCreateBulk instead", i) + } + } + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for AgentCreateBulk.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *AgentUpsertBulk) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/pkg/ent/agent_query.go b/pkg/ent/agent_query.go index c349c6c07..fcb68b6b2 100644 --- a/pkg/ent/agent_query.go +++ b/pkg/ent/agent_query.go @@ -9,6 +9,7 @@ import ( "math" "entgo.io/ent" + "entgo.io/ent/dialect" "entgo.io/ent/dialect/sql" "entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/schema/field" @@ -33,6 +34,7 @@ type AgentQuery struct { withOwner *UserQuery withMemberships *GroupMembershipQuery withPolicyBindings *PolicyBindingQuery + modifiers []func(*sql.Selector) // intermediate query (i.e. traversal path). sql *sql.Selector path func(context.Context) (*sql.Selector, error) @@ -532,6 +534,9 @@ func (_q *AgentQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*Agent, node.Edges.loadedTypes = loadedTypes return node.assignValues(columns, values) } + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } for i := range hooks { hooks[i](ctx, _spec) } @@ -738,6 +743,9 @@ func (_q *AgentQuery) loadPolicyBindings(ctx context.Context, query *PolicyBindi func (_q *AgentQuery) sqlCount(ctx context.Context) (int, error) { _spec := _q.querySpec() + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } _spec.Node.Columns = _q.ctx.Fields if len(_q.ctx.Fields) > 0 { _spec.Unique = _q.ctx.Unique != nil && *_q.ctx.Unique @@ -809,6 +817,9 @@ func (_q *AgentQuery) sqlQuery(ctx context.Context) *sql.Selector { if _q.ctx.Unique != nil && *_q.ctx.Unique { selector.Distinct() } + for _, m := range _q.modifiers { + m(selector) + } for _, p := range _q.predicates { p(selector) } @@ -826,6 +837,32 @@ func (_q *AgentQuery) sqlQuery(ctx context.Context) *sql.Selector { return selector } +// ForUpdate locks the selected rows against concurrent updates, and prevent them from being +// updated, deleted or "selected ... for update" by other sessions, until the transaction is +// either committed or rolled-back. +func (_q *AgentQuery) ForUpdate(opts ...sql.LockOption) *AgentQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForUpdate(opts...) + }) + return _q +} + +// ForShare behaves similarly to ForUpdate, except that it acquires a shared mode lock +// on any rows that are read. Other sessions can read the rows, but cannot modify them +// until your transaction commits. +func (_q *AgentQuery) ForShare(opts ...sql.LockOption) *AgentQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForShare(opts...) + }) + return _q +} + // AgentGroupBy is the group-by builder for Agent entities. type AgentGroupBy struct { selector diff --git a/pkg/ent/agent_update.go b/pkg/ent/agent_update.go index b9aa2f07c..d848bfb25 100644 --- a/pkg/ent/agent_update.go +++ b/pkg/ent/agent_update.go @@ -10,6 +10,7 @@ import ( "entgo.io/ent/dialect/sql" "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/dialect/sql/sqljson" "entgo.io/ent/schema/field" "github.com/GoogleCloudPlatform/scion/pkg/ent/agent" "github.com/GoogleCloudPlatform/scion/pkg/ent/groupmembership" @@ -177,12 +178,485 @@ func (_u *AgentUpdate) SetNillableVisibility(v *string) *AgentUpdate { return _u } +// SetLabels sets the "labels" field. +func (_u *AgentUpdate) SetLabels(v map[string]string) *AgentUpdate { + _u.mutation.SetLabels(v) + return _u +} + +// ClearLabels clears the value of the "labels" field. +func (_u *AgentUpdate) ClearLabels() *AgentUpdate { + _u.mutation.ClearLabels() + return _u +} + +// SetAnnotations sets the "annotations" field. +func (_u *AgentUpdate) SetAnnotations(v map[string]string) *AgentUpdate { + _u.mutation.SetAnnotations(v) + return _u +} + +// ClearAnnotations clears the value of the "annotations" field. +func (_u *AgentUpdate) ClearAnnotations() *AgentUpdate { + _u.mutation.ClearAnnotations() + return _u +} + +// SetPhase sets the "phase" field. +func (_u *AgentUpdate) SetPhase(v string) *AgentUpdate { + _u.mutation.SetPhase(v) + return _u +} + +// SetNillablePhase sets the "phase" field if the given value is not nil. +func (_u *AgentUpdate) SetNillablePhase(v *string) *AgentUpdate { + if v != nil { + _u.SetPhase(*v) + } + return _u +} + +// ClearPhase clears the value of the "phase" field. +func (_u *AgentUpdate) ClearPhase() *AgentUpdate { + _u.mutation.ClearPhase() + return _u +} + +// SetActivity sets the "activity" field. +func (_u *AgentUpdate) SetActivity(v string) *AgentUpdate { + _u.mutation.SetActivity(v) + return _u +} + +// SetNillableActivity sets the "activity" field if the given value is not nil. +func (_u *AgentUpdate) SetNillableActivity(v *string) *AgentUpdate { + if v != nil { + _u.SetActivity(*v) + } + return _u +} + +// ClearActivity clears the value of the "activity" field. +func (_u *AgentUpdate) ClearActivity() *AgentUpdate { + _u.mutation.ClearActivity() + return _u +} + +// SetToolName sets the "tool_name" field. +func (_u *AgentUpdate) SetToolName(v string) *AgentUpdate { + _u.mutation.SetToolName(v) + return _u +} + +// SetNillableToolName sets the "tool_name" field if the given value is not nil. +func (_u *AgentUpdate) SetNillableToolName(v *string) *AgentUpdate { + if v != nil { + _u.SetToolName(*v) + } + return _u +} + +// ClearToolName clears the value of the "tool_name" field. +func (_u *AgentUpdate) ClearToolName() *AgentUpdate { + _u.mutation.ClearToolName() + return _u +} + +// SetConnectionState sets the "connection_state" field. +func (_u *AgentUpdate) SetConnectionState(v string) *AgentUpdate { + _u.mutation.SetConnectionState(v) + return _u +} + +// SetNillableConnectionState sets the "connection_state" field if the given value is not nil. +func (_u *AgentUpdate) SetNillableConnectionState(v *string) *AgentUpdate { + if v != nil { + _u.SetConnectionState(*v) + } + return _u +} + +// ClearConnectionState clears the value of the "connection_state" field. +func (_u *AgentUpdate) ClearConnectionState() *AgentUpdate { + _u.mutation.ClearConnectionState() + return _u +} + +// SetContainerStatus sets the "container_status" field. +func (_u *AgentUpdate) SetContainerStatus(v string) *AgentUpdate { + _u.mutation.SetContainerStatus(v) + return _u +} + +// SetNillableContainerStatus sets the "container_status" field if the given value is not nil. +func (_u *AgentUpdate) SetNillableContainerStatus(v *string) *AgentUpdate { + if v != nil { + _u.SetContainerStatus(*v) + } + return _u +} + +// ClearContainerStatus clears the value of the "container_status" field. +func (_u *AgentUpdate) ClearContainerStatus() *AgentUpdate { + _u.mutation.ClearContainerStatus() + return _u +} + +// SetRuntimeState sets the "runtime_state" field. +func (_u *AgentUpdate) SetRuntimeState(v string) *AgentUpdate { + _u.mutation.SetRuntimeState(v) + return _u +} + +// SetNillableRuntimeState sets the "runtime_state" field if the given value is not nil. +func (_u *AgentUpdate) SetNillableRuntimeState(v *string) *AgentUpdate { + if v != nil { + _u.SetRuntimeState(*v) + } + return _u +} + +// ClearRuntimeState clears the value of the "runtime_state" field. +func (_u *AgentUpdate) ClearRuntimeState() *AgentUpdate { + _u.mutation.ClearRuntimeState() + return _u +} + +// SetStalledFromActivity sets the "stalled_from_activity" field. +func (_u *AgentUpdate) SetStalledFromActivity(v string) *AgentUpdate { + _u.mutation.SetStalledFromActivity(v) + return _u +} + +// SetNillableStalledFromActivity sets the "stalled_from_activity" field if the given value is not nil. +func (_u *AgentUpdate) SetNillableStalledFromActivity(v *string) *AgentUpdate { + if v != nil { + _u.SetStalledFromActivity(*v) + } + return _u +} + +// ClearStalledFromActivity clears the value of the "stalled_from_activity" field. +func (_u *AgentUpdate) ClearStalledFromActivity() *AgentUpdate { + _u.mutation.ClearStalledFromActivity() + return _u +} + +// SetCurrentTurns sets the "current_turns" field. +func (_u *AgentUpdate) SetCurrentTurns(v int) *AgentUpdate { + _u.mutation.ResetCurrentTurns() + _u.mutation.SetCurrentTurns(v) + return _u +} + +// SetNillableCurrentTurns sets the "current_turns" field if the given value is not nil. +func (_u *AgentUpdate) SetNillableCurrentTurns(v *int) *AgentUpdate { + if v != nil { + _u.SetCurrentTurns(*v) + } + return _u +} + +// AddCurrentTurns adds value to the "current_turns" field. +func (_u *AgentUpdate) AddCurrentTurns(v int) *AgentUpdate { + _u.mutation.AddCurrentTurns(v) + return _u +} + +// SetCurrentModelCalls sets the "current_model_calls" field. +func (_u *AgentUpdate) SetCurrentModelCalls(v int) *AgentUpdate { + _u.mutation.ResetCurrentModelCalls() + _u.mutation.SetCurrentModelCalls(v) + return _u +} + +// SetNillableCurrentModelCalls sets the "current_model_calls" field if the given value is not nil. +func (_u *AgentUpdate) SetNillableCurrentModelCalls(v *int) *AgentUpdate { + if v != nil { + _u.SetCurrentModelCalls(*v) + } + return _u +} + +// AddCurrentModelCalls adds value to the "current_model_calls" field. +func (_u *AgentUpdate) AddCurrentModelCalls(v int) *AgentUpdate { + _u.mutation.AddCurrentModelCalls(v) + return _u +} + +// SetImage sets the "image" field. +func (_u *AgentUpdate) SetImage(v string) *AgentUpdate { + _u.mutation.SetImage(v) + return _u +} + +// SetNillableImage sets the "image" field if the given value is not nil. +func (_u *AgentUpdate) SetNillableImage(v *string) *AgentUpdate { + if v != nil { + _u.SetImage(*v) + } + return _u +} + +// ClearImage clears the value of the "image" field. +func (_u *AgentUpdate) ClearImage() *AgentUpdate { + _u.mutation.ClearImage() + return _u +} + +// SetDetached sets the "detached" field. +func (_u *AgentUpdate) SetDetached(v bool) *AgentUpdate { + _u.mutation.SetDetached(v) + return _u +} + +// SetNillableDetached sets the "detached" field if the given value is not nil. +func (_u *AgentUpdate) SetNillableDetached(v *bool) *AgentUpdate { + if v != nil { + _u.SetDetached(*v) + } + return _u +} + +// SetRuntime sets the "runtime" field. +func (_u *AgentUpdate) SetRuntime(v string) *AgentUpdate { + _u.mutation.SetRuntime(v) + return _u +} + +// SetNillableRuntime sets the "runtime" field if the given value is not nil. +func (_u *AgentUpdate) SetNillableRuntime(v *string) *AgentUpdate { + if v != nil { + _u.SetRuntime(*v) + } + return _u +} + +// ClearRuntime clears the value of the "runtime" field. +func (_u *AgentUpdate) ClearRuntime() *AgentUpdate { + _u.mutation.ClearRuntime() + return _u +} + +// SetRuntimeBrokerID sets the "runtime_broker_id" field. +func (_u *AgentUpdate) SetRuntimeBrokerID(v string) *AgentUpdate { + _u.mutation.SetRuntimeBrokerID(v) + return _u +} + +// SetNillableRuntimeBrokerID sets the "runtime_broker_id" field if the given value is not nil. +func (_u *AgentUpdate) SetNillableRuntimeBrokerID(v *string) *AgentUpdate { + if v != nil { + _u.SetRuntimeBrokerID(*v) + } + return _u +} + +// ClearRuntimeBrokerID clears the value of the "runtime_broker_id" field. +func (_u *AgentUpdate) ClearRuntimeBrokerID() *AgentUpdate { + _u.mutation.ClearRuntimeBrokerID() + return _u +} + +// SetWebPtyEnabled sets the "web_pty_enabled" field. +func (_u *AgentUpdate) SetWebPtyEnabled(v bool) *AgentUpdate { + _u.mutation.SetWebPtyEnabled(v) + return _u +} + +// SetNillableWebPtyEnabled sets the "web_pty_enabled" field if the given value is not nil. +func (_u *AgentUpdate) SetNillableWebPtyEnabled(v *bool) *AgentUpdate { + if v != nil { + _u.SetWebPtyEnabled(*v) + } + return _u +} + +// SetTaskSummary sets the "task_summary" field. +func (_u *AgentUpdate) SetTaskSummary(v string) *AgentUpdate { + _u.mutation.SetTaskSummary(v) + return _u +} + +// SetNillableTaskSummary sets the "task_summary" field if the given value is not nil. +func (_u *AgentUpdate) SetNillableTaskSummary(v *string) *AgentUpdate { + if v != nil { + _u.SetTaskSummary(*v) + } + return _u +} + +// ClearTaskSummary clears the value of the "task_summary" field. +func (_u *AgentUpdate) ClearTaskSummary() *AgentUpdate { + _u.mutation.ClearTaskSummary() + return _u +} + +// SetMessage sets the "message" field. +func (_u *AgentUpdate) SetMessage(v string) *AgentUpdate { + _u.mutation.SetMessage(v) + return _u +} + +// SetNillableMessage sets the "message" field if the given value is not nil. +func (_u *AgentUpdate) SetNillableMessage(v *string) *AgentUpdate { + if v != nil { + _u.SetMessage(*v) + } + return _u +} + +// ClearMessage clears the value of the "message" field. +func (_u *AgentUpdate) ClearMessage() *AgentUpdate { + _u.mutation.ClearMessage() + return _u +} + +// SetAppliedConfig sets the "applied_config" field. +func (_u *AgentUpdate) SetAppliedConfig(v string) *AgentUpdate { + _u.mutation.SetAppliedConfig(v) + return _u +} + +// SetNillableAppliedConfig sets the "applied_config" field if the given value is not nil. +func (_u *AgentUpdate) SetNillableAppliedConfig(v *string) *AgentUpdate { + if v != nil { + _u.SetAppliedConfig(*v) + } + return _u +} + +// ClearAppliedConfig clears the value of the "applied_config" field. +func (_u *AgentUpdate) ClearAppliedConfig() *AgentUpdate { + _u.mutation.ClearAppliedConfig() + return _u +} + +// SetAncestry sets the "ancestry" field. +func (_u *AgentUpdate) SetAncestry(v []string) *AgentUpdate { + _u.mutation.SetAncestry(v) + return _u +} + +// AppendAncestry appends value to the "ancestry" field. +func (_u *AgentUpdate) AppendAncestry(v []string) *AgentUpdate { + _u.mutation.AppendAncestry(v) + return _u +} + +// ClearAncestry clears the value of the "ancestry" field. +func (_u *AgentUpdate) ClearAncestry() *AgentUpdate { + _u.mutation.ClearAncestry() + return _u +} + // SetUpdated sets the "updated" field. func (_u *AgentUpdate) SetUpdated(v time.Time) *AgentUpdate { _u.mutation.SetUpdated(v) return _u } +// SetLastSeen sets the "last_seen" field. +func (_u *AgentUpdate) SetLastSeen(v time.Time) *AgentUpdate { + _u.mutation.SetLastSeen(v) + return _u +} + +// SetNillableLastSeen sets the "last_seen" field if the given value is not nil. +func (_u *AgentUpdate) SetNillableLastSeen(v *time.Time) *AgentUpdate { + if v != nil { + _u.SetLastSeen(*v) + } + return _u +} + +// ClearLastSeen clears the value of the "last_seen" field. +func (_u *AgentUpdate) ClearLastSeen() *AgentUpdate { + _u.mutation.ClearLastSeen() + return _u +} + +// SetLastActivityEvent sets the "last_activity_event" field. +func (_u *AgentUpdate) SetLastActivityEvent(v time.Time) *AgentUpdate { + _u.mutation.SetLastActivityEvent(v) + return _u +} + +// SetNillableLastActivityEvent sets the "last_activity_event" field if the given value is not nil. +func (_u *AgentUpdate) SetNillableLastActivityEvent(v *time.Time) *AgentUpdate { + if v != nil { + _u.SetLastActivityEvent(*v) + } + return _u +} + +// ClearLastActivityEvent clears the value of the "last_activity_event" field. +func (_u *AgentUpdate) ClearLastActivityEvent() *AgentUpdate { + _u.mutation.ClearLastActivityEvent() + return _u +} + +// SetStartedAt sets the "started_at" field. +func (_u *AgentUpdate) SetStartedAt(v time.Time) *AgentUpdate { + _u.mutation.SetStartedAt(v) + return _u +} + +// SetNillableStartedAt sets the "started_at" field if the given value is not nil. +func (_u *AgentUpdate) SetNillableStartedAt(v *time.Time) *AgentUpdate { + if v != nil { + _u.SetStartedAt(*v) + } + return _u +} + +// ClearStartedAt clears the value of the "started_at" field. +func (_u *AgentUpdate) ClearStartedAt() *AgentUpdate { + _u.mutation.ClearStartedAt() + return _u +} + +// SetDeletedAt sets the "deleted_at" field. +func (_u *AgentUpdate) SetDeletedAt(v time.Time) *AgentUpdate { + _u.mutation.SetDeletedAt(v) + return _u +} + +// SetNillableDeletedAt sets the "deleted_at" field if the given value is not nil. +func (_u *AgentUpdate) SetNillableDeletedAt(v *time.Time) *AgentUpdate { + if v != nil { + _u.SetDeletedAt(*v) + } + return _u +} + +// ClearDeletedAt clears the value of the "deleted_at" field. +func (_u *AgentUpdate) ClearDeletedAt() *AgentUpdate { + _u.mutation.ClearDeletedAt() + return _u +} + +// SetStateVersion sets the "state_version" field. +func (_u *AgentUpdate) SetStateVersion(v int64) *AgentUpdate { + _u.mutation.ResetStateVersion() + _u.mutation.SetStateVersion(v) + return _u +} + +// SetNillableStateVersion sets the "state_version" field if the given value is not nil. +func (_u *AgentUpdate) SetNillableStateVersion(v *int64) *AgentUpdate { + if v != nil { + _u.SetStateVersion(*v) + } + return _u +} + +// AddStateVersion adds value to the "state_version" field. +func (_u *AgentUpdate) AddStateVersion(v int64) *AgentUpdate { + _u.mutation.AddStateVersion(v) + return _u +} + // SetProject sets the "project" edge to the Project entity. func (_u *AgentUpdate) SetProject(v *Project) *AgentUpdate { return _u.SetProjectID(v.ID) @@ -384,24 +858,173 @@ func (_u *AgentUpdate) sqlSave(ctx context.Context) (_node int, err error) { if value, ok := _u.mutation.Name(); ok { _spec.SetField(agent.FieldName, field.TypeString, value) } - if value, ok := _u.mutation.Template(); ok { - _spec.SetField(agent.FieldTemplate, field.TypeString, value) + if value, ok := _u.mutation.Template(); ok { + _spec.SetField(agent.FieldTemplate, field.TypeString, value) + } + if _u.mutation.TemplateCleared() { + _spec.ClearField(agent.FieldTemplate, field.TypeString) + } + if value, ok := _u.mutation.Status(); ok { + _spec.SetField(agent.FieldStatus, field.TypeEnum, value) + } + if value, ok := _u.mutation.DelegationEnabled(); ok { + _spec.SetField(agent.FieldDelegationEnabled, field.TypeBool, value) + } + if value, ok := _u.mutation.Visibility(); ok { + _spec.SetField(agent.FieldVisibility, field.TypeString, value) + } + if value, ok := _u.mutation.Labels(); ok { + _spec.SetField(agent.FieldLabels, field.TypeJSON, value) + } + if _u.mutation.LabelsCleared() { + _spec.ClearField(agent.FieldLabels, field.TypeJSON) + } + if value, ok := _u.mutation.Annotations(); ok { + _spec.SetField(agent.FieldAnnotations, field.TypeJSON, value) + } + if _u.mutation.AnnotationsCleared() { + _spec.ClearField(agent.FieldAnnotations, field.TypeJSON) + } + if value, ok := _u.mutation.Phase(); ok { + _spec.SetField(agent.FieldPhase, field.TypeString, value) + } + if _u.mutation.PhaseCleared() { + _spec.ClearField(agent.FieldPhase, field.TypeString) + } + if value, ok := _u.mutation.Activity(); ok { + _spec.SetField(agent.FieldActivity, field.TypeString, value) + } + if _u.mutation.ActivityCleared() { + _spec.ClearField(agent.FieldActivity, field.TypeString) + } + if value, ok := _u.mutation.ToolName(); ok { + _spec.SetField(agent.FieldToolName, field.TypeString, value) + } + if _u.mutation.ToolNameCleared() { + _spec.ClearField(agent.FieldToolName, field.TypeString) + } + if value, ok := _u.mutation.ConnectionState(); ok { + _spec.SetField(agent.FieldConnectionState, field.TypeString, value) + } + if _u.mutation.ConnectionStateCleared() { + _spec.ClearField(agent.FieldConnectionState, field.TypeString) + } + if value, ok := _u.mutation.ContainerStatus(); ok { + _spec.SetField(agent.FieldContainerStatus, field.TypeString, value) + } + if _u.mutation.ContainerStatusCleared() { + _spec.ClearField(agent.FieldContainerStatus, field.TypeString) + } + if value, ok := _u.mutation.RuntimeState(); ok { + _spec.SetField(agent.FieldRuntimeState, field.TypeString, value) + } + if _u.mutation.RuntimeStateCleared() { + _spec.ClearField(agent.FieldRuntimeState, field.TypeString) + } + if value, ok := _u.mutation.StalledFromActivity(); ok { + _spec.SetField(agent.FieldStalledFromActivity, field.TypeString, value) + } + if _u.mutation.StalledFromActivityCleared() { + _spec.ClearField(agent.FieldStalledFromActivity, field.TypeString) + } + if value, ok := _u.mutation.CurrentTurns(); ok { + _spec.SetField(agent.FieldCurrentTurns, field.TypeInt, value) + } + if value, ok := _u.mutation.AddedCurrentTurns(); ok { + _spec.AddField(agent.FieldCurrentTurns, field.TypeInt, value) + } + if value, ok := _u.mutation.CurrentModelCalls(); ok { + _spec.SetField(agent.FieldCurrentModelCalls, field.TypeInt, value) + } + if value, ok := _u.mutation.AddedCurrentModelCalls(); ok { + _spec.AddField(agent.FieldCurrentModelCalls, field.TypeInt, value) + } + if value, ok := _u.mutation.Image(); ok { + _spec.SetField(agent.FieldImage, field.TypeString, value) + } + if _u.mutation.ImageCleared() { + _spec.ClearField(agent.FieldImage, field.TypeString) + } + if value, ok := _u.mutation.Detached(); ok { + _spec.SetField(agent.FieldDetached, field.TypeBool, value) + } + if value, ok := _u.mutation.Runtime(); ok { + _spec.SetField(agent.FieldRuntime, field.TypeString, value) + } + if _u.mutation.RuntimeCleared() { + _spec.ClearField(agent.FieldRuntime, field.TypeString) + } + if value, ok := _u.mutation.RuntimeBrokerID(); ok { + _spec.SetField(agent.FieldRuntimeBrokerID, field.TypeString, value) + } + if _u.mutation.RuntimeBrokerIDCleared() { + _spec.ClearField(agent.FieldRuntimeBrokerID, field.TypeString) + } + if value, ok := _u.mutation.WebPtyEnabled(); ok { + _spec.SetField(agent.FieldWebPtyEnabled, field.TypeBool, value) + } + if value, ok := _u.mutation.TaskSummary(); ok { + _spec.SetField(agent.FieldTaskSummary, field.TypeString, value) + } + if _u.mutation.TaskSummaryCleared() { + _spec.ClearField(agent.FieldTaskSummary, field.TypeString) + } + if value, ok := _u.mutation.Message(); ok { + _spec.SetField(agent.FieldMessage, field.TypeString, value) + } + if _u.mutation.MessageCleared() { + _spec.ClearField(agent.FieldMessage, field.TypeString) + } + if value, ok := _u.mutation.AppliedConfig(); ok { + _spec.SetField(agent.FieldAppliedConfig, field.TypeString, value) } - if _u.mutation.TemplateCleared() { - _spec.ClearField(agent.FieldTemplate, field.TypeString) + if _u.mutation.AppliedConfigCleared() { + _spec.ClearField(agent.FieldAppliedConfig, field.TypeString) } - if value, ok := _u.mutation.Status(); ok { - _spec.SetField(agent.FieldStatus, field.TypeEnum, value) + if value, ok := _u.mutation.Ancestry(); ok { + _spec.SetField(agent.FieldAncestry, field.TypeJSON, value) } - if value, ok := _u.mutation.DelegationEnabled(); ok { - _spec.SetField(agent.FieldDelegationEnabled, field.TypeBool, value) + if value, ok := _u.mutation.AppendedAncestry(); ok { + _spec.AddModifier(func(u *sql.UpdateBuilder) { + sqljson.Append(u, agent.FieldAncestry, value) + }) } - if value, ok := _u.mutation.Visibility(); ok { - _spec.SetField(agent.FieldVisibility, field.TypeString, value) + if _u.mutation.AncestryCleared() { + _spec.ClearField(agent.FieldAncestry, field.TypeJSON) } if value, ok := _u.mutation.Updated(); ok { _spec.SetField(agent.FieldUpdated, field.TypeTime, value) } + if value, ok := _u.mutation.LastSeen(); ok { + _spec.SetField(agent.FieldLastSeen, field.TypeTime, value) + } + if _u.mutation.LastSeenCleared() { + _spec.ClearField(agent.FieldLastSeen, field.TypeTime) + } + if value, ok := _u.mutation.LastActivityEvent(); ok { + _spec.SetField(agent.FieldLastActivityEvent, field.TypeTime, value) + } + if _u.mutation.LastActivityEventCleared() { + _spec.ClearField(agent.FieldLastActivityEvent, field.TypeTime) + } + if value, ok := _u.mutation.StartedAt(); ok { + _spec.SetField(agent.FieldStartedAt, field.TypeTime, value) + } + if _u.mutation.StartedAtCleared() { + _spec.ClearField(agent.FieldStartedAt, field.TypeTime) + } + if value, ok := _u.mutation.DeletedAt(); ok { + _spec.SetField(agent.FieldDeletedAt, field.TypeTime, value) + } + if _u.mutation.DeletedAtCleared() { + _spec.ClearField(agent.FieldDeletedAt, field.TypeTime) + } + if value, ok := _u.mutation.StateVersion(); ok { + _spec.SetField(agent.FieldStateVersion, field.TypeInt64, value) + } + if value, ok := _u.mutation.AddedStateVersion(); ok { + _spec.AddField(agent.FieldStateVersion, field.TypeInt64, value) + } if _u.mutation.ProjectCleared() { edge := &sqlgraph.EdgeSpec{ Rel: sqlgraph.M2O, @@ -563,192 +1186,665 @@ func (_u *AgentUpdate) sqlSave(ctx context.Context) (_node int, err error) { } _spec.Edges.Clear = append(_spec.Edges.Clear, edge) } - if nodes := _u.mutation.PolicyBindingsIDs(); len(nodes) > 0 { - edge := &sqlgraph.EdgeSpec{ - Rel: sqlgraph.O2M, - Inverse: true, - Table: agent.PolicyBindingsTable, - Columns: []string{agent.PolicyBindingsColumn}, - Bidi: false, - Target: &sqlgraph.EdgeTarget{ - IDSpec: sqlgraph.NewFieldSpec(policybinding.FieldID, field.TypeUUID), - }, - } - for _, k := range nodes { - edge.Target.Nodes = append(edge.Target.Nodes, k) - } - _spec.Edges.Add = append(_spec.Edges.Add, edge) + if nodes := _u.mutation.PolicyBindingsIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: true, + Table: agent.PolicyBindingsTable, + Columns: []string{agent.PolicyBindingsColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(policybinding.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{agent.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + _u.mutation.done = true + return _node, nil +} + +// AgentUpdateOne is the builder for updating a single Agent entity. +type AgentUpdateOne struct { + config + fields []string + hooks []Hook + mutation *AgentMutation +} + +// SetSlug sets the "slug" field. +func (_u *AgentUpdateOne) SetSlug(v string) *AgentUpdateOne { + _u.mutation.SetSlug(v) + return _u +} + +// SetNillableSlug sets the "slug" field if the given value is not nil. +func (_u *AgentUpdateOne) SetNillableSlug(v *string) *AgentUpdateOne { + if v != nil { + _u.SetSlug(*v) + } + return _u +} + +// SetName sets the "name" field. +func (_u *AgentUpdateOne) SetName(v string) *AgentUpdateOne { + _u.mutation.SetName(v) + return _u +} + +// SetNillableName sets the "name" field if the given value is not nil. +func (_u *AgentUpdateOne) SetNillableName(v *string) *AgentUpdateOne { + if v != nil { + _u.SetName(*v) + } + return _u +} + +// SetTemplate sets the "template" field. +func (_u *AgentUpdateOne) SetTemplate(v string) *AgentUpdateOne { + _u.mutation.SetTemplate(v) + return _u +} + +// SetNillableTemplate sets the "template" field if the given value is not nil. +func (_u *AgentUpdateOne) SetNillableTemplate(v *string) *AgentUpdateOne { + if v != nil { + _u.SetTemplate(*v) + } + return _u +} + +// ClearTemplate clears the value of the "template" field. +func (_u *AgentUpdateOne) ClearTemplate() *AgentUpdateOne { + _u.mutation.ClearTemplate() + return _u +} + +// SetProjectID sets the "project_id" field. +func (_u *AgentUpdateOne) SetProjectID(v uuid.UUID) *AgentUpdateOne { + _u.mutation.SetProjectID(v) + return _u +} + +// SetNillableProjectID sets the "project_id" field if the given value is not nil. +func (_u *AgentUpdateOne) SetNillableProjectID(v *uuid.UUID) *AgentUpdateOne { + if v != nil { + _u.SetProjectID(*v) + } + return _u +} + +// SetStatus sets the "status" field. +func (_u *AgentUpdateOne) SetStatus(v agent.Status) *AgentUpdateOne { + _u.mutation.SetStatus(v) + return _u +} + +// SetNillableStatus sets the "status" field if the given value is not nil. +func (_u *AgentUpdateOne) SetNillableStatus(v *agent.Status) *AgentUpdateOne { + if v != nil { + _u.SetStatus(*v) + } + return _u +} + +// SetCreatedBy sets the "created_by" field. +func (_u *AgentUpdateOne) SetCreatedBy(v uuid.UUID) *AgentUpdateOne { + _u.mutation.SetCreatedBy(v) + return _u +} + +// SetNillableCreatedBy sets the "created_by" field if the given value is not nil. +func (_u *AgentUpdateOne) SetNillableCreatedBy(v *uuid.UUID) *AgentUpdateOne { + if v != nil { + _u.SetCreatedBy(*v) + } + return _u +} + +// ClearCreatedBy clears the value of the "created_by" field. +func (_u *AgentUpdateOne) ClearCreatedBy() *AgentUpdateOne { + _u.mutation.ClearCreatedBy() + return _u +} + +// SetOwnerID sets the "owner_id" field. +func (_u *AgentUpdateOne) SetOwnerID(v uuid.UUID) *AgentUpdateOne { + _u.mutation.SetOwnerID(v) + return _u +} + +// SetNillableOwnerID sets the "owner_id" field if the given value is not nil. +func (_u *AgentUpdateOne) SetNillableOwnerID(v *uuid.UUID) *AgentUpdateOne { + if v != nil { + _u.SetOwnerID(*v) + } + return _u +} + +// ClearOwnerID clears the value of the "owner_id" field. +func (_u *AgentUpdateOne) ClearOwnerID() *AgentUpdateOne { + _u.mutation.ClearOwnerID() + return _u +} + +// SetDelegationEnabled sets the "delegation_enabled" field. +func (_u *AgentUpdateOne) SetDelegationEnabled(v bool) *AgentUpdateOne { + _u.mutation.SetDelegationEnabled(v) + return _u +} + +// SetNillableDelegationEnabled sets the "delegation_enabled" field if the given value is not nil. +func (_u *AgentUpdateOne) SetNillableDelegationEnabled(v *bool) *AgentUpdateOne { + if v != nil { + _u.SetDelegationEnabled(*v) + } + return _u +} + +// SetVisibility sets the "visibility" field. +func (_u *AgentUpdateOne) SetVisibility(v string) *AgentUpdateOne { + _u.mutation.SetVisibility(v) + return _u +} + +// SetNillableVisibility sets the "visibility" field if the given value is not nil. +func (_u *AgentUpdateOne) SetNillableVisibility(v *string) *AgentUpdateOne { + if v != nil { + _u.SetVisibility(*v) + } + return _u +} + +// SetLabels sets the "labels" field. +func (_u *AgentUpdateOne) SetLabels(v map[string]string) *AgentUpdateOne { + _u.mutation.SetLabels(v) + return _u +} + +// ClearLabels clears the value of the "labels" field. +func (_u *AgentUpdateOne) ClearLabels() *AgentUpdateOne { + _u.mutation.ClearLabels() + return _u +} + +// SetAnnotations sets the "annotations" field. +func (_u *AgentUpdateOne) SetAnnotations(v map[string]string) *AgentUpdateOne { + _u.mutation.SetAnnotations(v) + return _u +} + +// ClearAnnotations clears the value of the "annotations" field. +func (_u *AgentUpdateOne) ClearAnnotations() *AgentUpdateOne { + _u.mutation.ClearAnnotations() + return _u +} + +// SetPhase sets the "phase" field. +func (_u *AgentUpdateOne) SetPhase(v string) *AgentUpdateOne { + _u.mutation.SetPhase(v) + return _u +} + +// SetNillablePhase sets the "phase" field if the given value is not nil. +func (_u *AgentUpdateOne) SetNillablePhase(v *string) *AgentUpdateOne { + if v != nil { + _u.SetPhase(*v) + } + return _u +} + +// ClearPhase clears the value of the "phase" field. +func (_u *AgentUpdateOne) ClearPhase() *AgentUpdateOne { + _u.mutation.ClearPhase() + return _u +} + +// SetActivity sets the "activity" field. +func (_u *AgentUpdateOne) SetActivity(v string) *AgentUpdateOne { + _u.mutation.SetActivity(v) + return _u +} + +// SetNillableActivity sets the "activity" field if the given value is not nil. +func (_u *AgentUpdateOne) SetNillableActivity(v *string) *AgentUpdateOne { + if v != nil { + _u.SetActivity(*v) + } + return _u +} + +// ClearActivity clears the value of the "activity" field. +func (_u *AgentUpdateOne) ClearActivity() *AgentUpdateOne { + _u.mutation.ClearActivity() + return _u +} + +// SetToolName sets the "tool_name" field. +func (_u *AgentUpdateOne) SetToolName(v string) *AgentUpdateOne { + _u.mutation.SetToolName(v) + return _u +} + +// SetNillableToolName sets the "tool_name" field if the given value is not nil. +func (_u *AgentUpdateOne) SetNillableToolName(v *string) *AgentUpdateOne { + if v != nil { + _u.SetToolName(*v) + } + return _u +} + +// ClearToolName clears the value of the "tool_name" field. +func (_u *AgentUpdateOne) ClearToolName() *AgentUpdateOne { + _u.mutation.ClearToolName() + return _u +} + +// SetConnectionState sets the "connection_state" field. +func (_u *AgentUpdateOne) SetConnectionState(v string) *AgentUpdateOne { + _u.mutation.SetConnectionState(v) + return _u +} + +// SetNillableConnectionState sets the "connection_state" field if the given value is not nil. +func (_u *AgentUpdateOne) SetNillableConnectionState(v *string) *AgentUpdateOne { + if v != nil { + _u.SetConnectionState(*v) + } + return _u +} + +// ClearConnectionState clears the value of the "connection_state" field. +func (_u *AgentUpdateOne) ClearConnectionState() *AgentUpdateOne { + _u.mutation.ClearConnectionState() + return _u +} + +// SetContainerStatus sets the "container_status" field. +func (_u *AgentUpdateOne) SetContainerStatus(v string) *AgentUpdateOne { + _u.mutation.SetContainerStatus(v) + return _u +} + +// SetNillableContainerStatus sets the "container_status" field if the given value is not nil. +func (_u *AgentUpdateOne) SetNillableContainerStatus(v *string) *AgentUpdateOne { + if v != nil { + _u.SetContainerStatus(*v) + } + return _u +} + +// ClearContainerStatus clears the value of the "container_status" field. +func (_u *AgentUpdateOne) ClearContainerStatus() *AgentUpdateOne { + _u.mutation.ClearContainerStatus() + return _u +} + +// SetRuntimeState sets the "runtime_state" field. +func (_u *AgentUpdateOne) SetRuntimeState(v string) *AgentUpdateOne { + _u.mutation.SetRuntimeState(v) + return _u +} + +// SetNillableRuntimeState sets the "runtime_state" field if the given value is not nil. +func (_u *AgentUpdateOne) SetNillableRuntimeState(v *string) *AgentUpdateOne { + if v != nil { + _u.SetRuntimeState(*v) } - if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil { - if _, ok := err.(*sqlgraph.NotFoundError); ok { - err = &NotFoundError{agent.Label} - } else if sqlgraph.IsConstraintError(err) { - err = &ConstraintError{msg: err.Error(), wrap: err} - } - return 0, err + return _u +} + +// ClearRuntimeState clears the value of the "runtime_state" field. +func (_u *AgentUpdateOne) ClearRuntimeState() *AgentUpdateOne { + _u.mutation.ClearRuntimeState() + return _u +} + +// SetStalledFromActivity sets the "stalled_from_activity" field. +func (_u *AgentUpdateOne) SetStalledFromActivity(v string) *AgentUpdateOne { + _u.mutation.SetStalledFromActivity(v) + return _u +} + +// SetNillableStalledFromActivity sets the "stalled_from_activity" field if the given value is not nil. +func (_u *AgentUpdateOne) SetNillableStalledFromActivity(v *string) *AgentUpdateOne { + if v != nil { + _u.SetStalledFromActivity(*v) } - _u.mutation.done = true - return _node, nil + return _u } -// AgentUpdateOne is the builder for updating a single Agent entity. -type AgentUpdateOne struct { - config - fields []string - hooks []Hook - mutation *AgentMutation +// ClearStalledFromActivity clears the value of the "stalled_from_activity" field. +func (_u *AgentUpdateOne) ClearStalledFromActivity() *AgentUpdateOne { + _u.mutation.ClearStalledFromActivity() + return _u } -// SetSlug sets the "slug" field. -func (_u *AgentUpdateOne) SetSlug(v string) *AgentUpdateOne { - _u.mutation.SetSlug(v) +// SetCurrentTurns sets the "current_turns" field. +func (_u *AgentUpdateOne) SetCurrentTurns(v int) *AgentUpdateOne { + _u.mutation.ResetCurrentTurns() + _u.mutation.SetCurrentTurns(v) return _u } -// SetNillableSlug sets the "slug" field if the given value is not nil. -func (_u *AgentUpdateOne) SetNillableSlug(v *string) *AgentUpdateOne { +// SetNillableCurrentTurns sets the "current_turns" field if the given value is not nil. +func (_u *AgentUpdateOne) SetNillableCurrentTurns(v *int) *AgentUpdateOne { if v != nil { - _u.SetSlug(*v) + _u.SetCurrentTurns(*v) } return _u } -// SetName sets the "name" field. -func (_u *AgentUpdateOne) SetName(v string) *AgentUpdateOne { - _u.mutation.SetName(v) +// AddCurrentTurns adds value to the "current_turns" field. +func (_u *AgentUpdateOne) AddCurrentTurns(v int) *AgentUpdateOne { + _u.mutation.AddCurrentTurns(v) return _u } -// SetNillableName sets the "name" field if the given value is not nil. -func (_u *AgentUpdateOne) SetNillableName(v *string) *AgentUpdateOne { +// SetCurrentModelCalls sets the "current_model_calls" field. +func (_u *AgentUpdateOne) SetCurrentModelCalls(v int) *AgentUpdateOne { + _u.mutation.ResetCurrentModelCalls() + _u.mutation.SetCurrentModelCalls(v) + return _u +} + +// SetNillableCurrentModelCalls sets the "current_model_calls" field if the given value is not nil. +func (_u *AgentUpdateOne) SetNillableCurrentModelCalls(v *int) *AgentUpdateOne { if v != nil { - _u.SetName(*v) + _u.SetCurrentModelCalls(*v) } return _u } -// SetTemplate sets the "template" field. -func (_u *AgentUpdateOne) SetTemplate(v string) *AgentUpdateOne { - _u.mutation.SetTemplate(v) +// AddCurrentModelCalls adds value to the "current_model_calls" field. +func (_u *AgentUpdateOne) AddCurrentModelCalls(v int) *AgentUpdateOne { + _u.mutation.AddCurrentModelCalls(v) return _u } -// SetNillableTemplate sets the "template" field if the given value is not nil. -func (_u *AgentUpdateOne) SetNillableTemplate(v *string) *AgentUpdateOne { +// SetImage sets the "image" field. +func (_u *AgentUpdateOne) SetImage(v string) *AgentUpdateOne { + _u.mutation.SetImage(v) + return _u +} + +// SetNillableImage sets the "image" field if the given value is not nil. +func (_u *AgentUpdateOne) SetNillableImage(v *string) *AgentUpdateOne { if v != nil { - _u.SetTemplate(*v) + _u.SetImage(*v) } return _u } -// ClearTemplate clears the value of the "template" field. -func (_u *AgentUpdateOne) ClearTemplate() *AgentUpdateOne { - _u.mutation.ClearTemplate() +// ClearImage clears the value of the "image" field. +func (_u *AgentUpdateOne) ClearImage() *AgentUpdateOne { + _u.mutation.ClearImage() return _u } -// SetProjectID sets the "project_id" field. -func (_u *AgentUpdateOne) SetProjectID(v uuid.UUID) *AgentUpdateOne { - _u.mutation.SetProjectID(v) +// SetDetached sets the "detached" field. +func (_u *AgentUpdateOne) SetDetached(v bool) *AgentUpdateOne { + _u.mutation.SetDetached(v) return _u } -// SetNillableProjectID sets the "project_id" field if the given value is not nil. -func (_u *AgentUpdateOne) SetNillableProjectID(v *uuid.UUID) *AgentUpdateOne { +// SetNillableDetached sets the "detached" field if the given value is not nil. +func (_u *AgentUpdateOne) SetNillableDetached(v *bool) *AgentUpdateOne { if v != nil { - _u.SetProjectID(*v) + _u.SetDetached(*v) } return _u } -// SetStatus sets the "status" field. -func (_u *AgentUpdateOne) SetStatus(v agent.Status) *AgentUpdateOne { - _u.mutation.SetStatus(v) +// SetRuntime sets the "runtime" field. +func (_u *AgentUpdateOne) SetRuntime(v string) *AgentUpdateOne { + _u.mutation.SetRuntime(v) return _u } -// SetNillableStatus sets the "status" field if the given value is not nil. -func (_u *AgentUpdateOne) SetNillableStatus(v *agent.Status) *AgentUpdateOne { +// SetNillableRuntime sets the "runtime" field if the given value is not nil. +func (_u *AgentUpdateOne) SetNillableRuntime(v *string) *AgentUpdateOne { if v != nil { - _u.SetStatus(*v) + _u.SetRuntime(*v) } return _u } -// SetCreatedBy sets the "created_by" field. -func (_u *AgentUpdateOne) SetCreatedBy(v uuid.UUID) *AgentUpdateOne { - _u.mutation.SetCreatedBy(v) +// ClearRuntime clears the value of the "runtime" field. +func (_u *AgentUpdateOne) ClearRuntime() *AgentUpdateOne { + _u.mutation.ClearRuntime() return _u } -// SetNillableCreatedBy sets the "created_by" field if the given value is not nil. -func (_u *AgentUpdateOne) SetNillableCreatedBy(v *uuid.UUID) *AgentUpdateOne { +// SetRuntimeBrokerID sets the "runtime_broker_id" field. +func (_u *AgentUpdateOne) SetRuntimeBrokerID(v string) *AgentUpdateOne { + _u.mutation.SetRuntimeBrokerID(v) + return _u +} + +// SetNillableRuntimeBrokerID sets the "runtime_broker_id" field if the given value is not nil. +func (_u *AgentUpdateOne) SetNillableRuntimeBrokerID(v *string) *AgentUpdateOne { if v != nil { - _u.SetCreatedBy(*v) + _u.SetRuntimeBrokerID(*v) } return _u } -// ClearCreatedBy clears the value of the "created_by" field. -func (_u *AgentUpdateOne) ClearCreatedBy() *AgentUpdateOne { - _u.mutation.ClearCreatedBy() +// ClearRuntimeBrokerID clears the value of the "runtime_broker_id" field. +func (_u *AgentUpdateOne) ClearRuntimeBrokerID() *AgentUpdateOne { + _u.mutation.ClearRuntimeBrokerID() return _u } -// SetOwnerID sets the "owner_id" field. -func (_u *AgentUpdateOne) SetOwnerID(v uuid.UUID) *AgentUpdateOne { - _u.mutation.SetOwnerID(v) +// SetWebPtyEnabled sets the "web_pty_enabled" field. +func (_u *AgentUpdateOne) SetWebPtyEnabled(v bool) *AgentUpdateOne { + _u.mutation.SetWebPtyEnabled(v) return _u } -// SetNillableOwnerID sets the "owner_id" field if the given value is not nil. -func (_u *AgentUpdateOne) SetNillableOwnerID(v *uuid.UUID) *AgentUpdateOne { +// SetNillableWebPtyEnabled sets the "web_pty_enabled" field if the given value is not nil. +func (_u *AgentUpdateOne) SetNillableWebPtyEnabled(v *bool) *AgentUpdateOne { if v != nil { - _u.SetOwnerID(*v) + _u.SetWebPtyEnabled(*v) } return _u } -// ClearOwnerID clears the value of the "owner_id" field. -func (_u *AgentUpdateOne) ClearOwnerID() *AgentUpdateOne { - _u.mutation.ClearOwnerID() +// SetTaskSummary sets the "task_summary" field. +func (_u *AgentUpdateOne) SetTaskSummary(v string) *AgentUpdateOne { + _u.mutation.SetTaskSummary(v) return _u } -// SetDelegationEnabled sets the "delegation_enabled" field. -func (_u *AgentUpdateOne) SetDelegationEnabled(v bool) *AgentUpdateOne { - _u.mutation.SetDelegationEnabled(v) +// SetNillableTaskSummary sets the "task_summary" field if the given value is not nil. +func (_u *AgentUpdateOne) SetNillableTaskSummary(v *string) *AgentUpdateOne { + if v != nil { + _u.SetTaskSummary(*v) + } return _u } -// SetNillableDelegationEnabled sets the "delegation_enabled" field if the given value is not nil. -func (_u *AgentUpdateOne) SetNillableDelegationEnabled(v *bool) *AgentUpdateOne { +// ClearTaskSummary clears the value of the "task_summary" field. +func (_u *AgentUpdateOne) ClearTaskSummary() *AgentUpdateOne { + _u.mutation.ClearTaskSummary() + return _u +} + +// SetMessage sets the "message" field. +func (_u *AgentUpdateOne) SetMessage(v string) *AgentUpdateOne { + _u.mutation.SetMessage(v) + return _u +} + +// SetNillableMessage sets the "message" field if the given value is not nil. +func (_u *AgentUpdateOne) SetNillableMessage(v *string) *AgentUpdateOne { if v != nil { - _u.SetDelegationEnabled(*v) + _u.SetMessage(*v) } return _u } -// SetVisibility sets the "visibility" field. -func (_u *AgentUpdateOne) SetVisibility(v string) *AgentUpdateOne { - _u.mutation.SetVisibility(v) +// ClearMessage clears the value of the "message" field. +func (_u *AgentUpdateOne) ClearMessage() *AgentUpdateOne { + _u.mutation.ClearMessage() return _u } -// SetNillableVisibility sets the "visibility" field if the given value is not nil. -func (_u *AgentUpdateOne) SetNillableVisibility(v *string) *AgentUpdateOne { +// SetAppliedConfig sets the "applied_config" field. +func (_u *AgentUpdateOne) SetAppliedConfig(v string) *AgentUpdateOne { + _u.mutation.SetAppliedConfig(v) + return _u +} + +// SetNillableAppliedConfig sets the "applied_config" field if the given value is not nil. +func (_u *AgentUpdateOne) SetNillableAppliedConfig(v *string) *AgentUpdateOne { if v != nil { - _u.SetVisibility(*v) + _u.SetAppliedConfig(*v) } return _u } +// ClearAppliedConfig clears the value of the "applied_config" field. +func (_u *AgentUpdateOne) ClearAppliedConfig() *AgentUpdateOne { + _u.mutation.ClearAppliedConfig() + return _u +} + +// SetAncestry sets the "ancestry" field. +func (_u *AgentUpdateOne) SetAncestry(v []string) *AgentUpdateOne { + _u.mutation.SetAncestry(v) + return _u +} + +// AppendAncestry appends value to the "ancestry" field. +func (_u *AgentUpdateOne) AppendAncestry(v []string) *AgentUpdateOne { + _u.mutation.AppendAncestry(v) + return _u +} + +// ClearAncestry clears the value of the "ancestry" field. +func (_u *AgentUpdateOne) ClearAncestry() *AgentUpdateOne { + _u.mutation.ClearAncestry() + return _u +} + // SetUpdated sets the "updated" field. func (_u *AgentUpdateOne) SetUpdated(v time.Time) *AgentUpdateOne { _u.mutation.SetUpdated(v) return _u } +// SetLastSeen sets the "last_seen" field. +func (_u *AgentUpdateOne) SetLastSeen(v time.Time) *AgentUpdateOne { + _u.mutation.SetLastSeen(v) + return _u +} + +// SetNillableLastSeen sets the "last_seen" field if the given value is not nil. +func (_u *AgentUpdateOne) SetNillableLastSeen(v *time.Time) *AgentUpdateOne { + if v != nil { + _u.SetLastSeen(*v) + } + return _u +} + +// ClearLastSeen clears the value of the "last_seen" field. +func (_u *AgentUpdateOne) ClearLastSeen() *AgentUpdateOne { + _u.mutation.ClearLastSeen() + return _u +} + +// SetLastActivityEvent sets the "last_activity_event" field. +func (_u *AgentUpdateOne) SetLastActivityEvent(v time.Time) *AgentUpdateOne { + _u.mutation.SetLastActivityEvent(v) + return _u +} + +// SetNillableLastActivityEvent sets the "last_activity_event" field if the given value is not nil. +func (_u *AgentUpdateOne) SetNillableLastActivityEvent(v *time.Time) *AgentUpdateOne { + if v != nil { + _u.SetLastActivityEvent(*v) + } + return _u +} + +// ClearLastActivityEvent clears the value of the "last_activity_event" field. +func (_u *AgentUpdateOne) ClearLastActivityEvent() *AgentUpdateOne { + _u.mutation.ClearLastActivityEvent() + return _u +} + +// SetStartedAt sets the "started_at" field. +func (_u *AgentUpdateOne) SetStartedAt(v time.Time) *AgentUpdateOne { + _u.mutation.SetStartedAt(v) + return _u +} + +// SetNillableStartedAt sets the "started_at" field if the given value is not nil. +func (_u *AgentUpdateOne) SetNillableStartedAt(v *time.Time) *AgentUpdateOne { + if v != nil { + _u.SetStartedAt(*v) + } + return _u +} + +// ClearStartedAt clears the value of the "started_at" field. +func (_u *AgentUpdateOne) ClearStartedAt() *AgentUpdateOne { + _u.mutation.ClearStartedAt() + return _u +} + +// SetDeletedAt sets the "deleted_at" field. +func (_u *AgentUpdateOne) SetDeletedAt(v time.Time) *AgentUpdateOne { + _u.mutation.SetDeletedAt(v) + return _u +} + +// SetNillableDeletedAt sets the "deleted_at" field if the given value is not nil. +func (_u *AgentUpdateOne) SetNillableDeletedAt(v *time.Time) *AgentUpdateOne { + if v != nil { + _u.SetDeletedAt(*v) + } + return _u +} + +// ClearDeletedAt clears the value of the "deleted_at" field. +func (_u *AgentUpdateOne) ClearDeletedAt() *AgentUpdateOne { + _u.mutation.ClearDeletedAt() + return _u +} + +// SetStateVersion sets the "state_version" field. +func (_u *AgentUpdateOne) SetStateVersion(v int64) *AgentUpdateOne { + _u.mutation.ResetStateVersion() + _u.mutation.SetStateVersion(v) + return _u +} + +// SetNillableStateVersion sets the "state_version" field if the given value is not nil. +func (_u *AgentUpdateOne) SetNillableStateVersion(v *int64) *AgentUpdateOne { + if v != nil { + _u.SetStateVersion(*v) + } + return _u +} + +// AddStateVersion adds value to the "state_version" field. +func (_u *AgentUpdateOne) AddStateVersion(v int64) *AgentUpdateOne { + _u.mutation.AddStateVersion(v) + return _u +} + // SetProject sets the "project" edge to the Project entity. func (_u *AgentUpdateOne) SetProject(v *Project) *AgentUpdateOne { return _u.SetProjectID(v.ID) @@ -995,9 +2091,158 @@ func (_u *AgentUpdateOne) sqlSave(ctx context.Context) (_node *Agent, err error) if value, ok := _u.mutation.Visibility(); ok { _spec.SetField(agent.FieldVisibility, field.TypeString, value) } + if value, ok := _u.mutation.Labels(); ok { + _spec.SetField(agent.FieldLabels, field.TypeJSON, value) + } + if _u.mutation.LabelsCleared() { + _spec.ClearField(agent.FieldLabels, field.TypeJSON) + } + if value, ok := _u.mutation.Annotations(); ok { + _spec.SetField(agent.FieldAnnotations, field.TypeJSON, value) + } + if _u.mutation.AnnotationsCleared() { + _spec.ClearField(agent.FieldAnnotations, field.TypeJSON) + } + if value, ok := _u.mutation.Phase(); ok { + _spec.SetField(agent.FieldPhase, field.TypeString, value) + } + if _u.mutation.PhaseCleared() { + _spec.ClearField(agent.FieldPhase, field.TypeString) + } + if value, ok := _u.mutation.Activity(); ok { + _spec.SetField(agent.FieldActivity, field.TypeString, value) + } + if _u.mutation.ActivityCleared() { + _spec.ClearField(agent.FieldActivity, field.TypeString) + } + if value, ok := _u.mutation.ToolName(); ok { + _spec.SetField(agent.FieldToolName, field.TypeString, value) + } + if _u.mutation.ToolNameCleared() { + _spec.ClearField(agent.FieldToolName, field.TypeString) + } + if value, ok := _u.mutation.ConnectionState(); ok { + _spec.SetField(agent.FieldConnectionState, field.TypeString, value) + } + if _u.mutation.ConnectionStateCleared() { + _spec.ClearField(agent.FieldConnectionState, field.TypeString) + } + if value, ok := _u.mutation.ContainerStatus(); ok { + _spec.SetField(agent.FieldContainerStatus, field.TypeString, value) + } + if _u.mutation.ContainerStatusCleared() { + _spec.ClearField(agent.FieldContainerStatus, field.TypeString) + } + if value, ok := _u.mutation.RuntimeState(); ok { + _spec.SetField(agent.FieldRuntimeState, field.TypeString, value) + } + if _u.mutation.RuntimeStateCleared() { + _spec.ClearField(agent.FieldRuntimeState, field.TypeString) + } + if value, ok := _u.mutation.StalledFromActivity(); ok { + _spec.SetField(agent.FieldStalledFromActivity, field.TypeString, value) + } + if _u.mutation.StalledFromActivityCleared() { + _spec.ClearField(agent.FieldStalledFromActivity, field.TypeString) + } + if value, ok := _u.mutation.CurrentTurns(); ok { + _spec.SetField(agent.FieldCurrentTurns, field.TypeInt, value) + } + if value, ok := _u.mutation.AddedCurrentTurns(); ok { + _spec.AddField(agent.FieldCurrentTurns, field.TypeInt, value) + } + if value, ok := _u.mutation.CurrentModelCalls(); ok { + _spec.SetField(agent.FieldCurrentModelCalls, field.TypeInt, value) + } + if value, ok := _u.mutation.AddedCurrentModelCalls(); ok { + _spec.AddField(agent.FieldCurrentModelCalls, field.TypeInt, value) + } + if value, ok := _u.mutation.Image(); ok { + _spec.SetField(agent.FieldImage, field.TypeString, value) + } + if _u.mutation.ImageCleared() { + _spec.ClearField(agent.FieldImage, field.TypeString) + } + if value, ok := _u.mutation.Detached(); ok { + _spec.SetField(agent.FieldDetached, field.TypeBool, value) + } + if value, ok := _u.mutation.Runtime(); ok { + _spec.SetField(agent.FieldRuntime, field.TypeString, value) + } + if _u.mutation.RuntimeCleared() { + _spec.ClearField(agent.FieldRuntime, field.TypeString) + } + if value, ok := _u.mutation.RuntimeBrokerID(); ok { + _spec.SetField(agent.FieldRuntimeBrokerID, field.TypeString, value) + } + if _u.mutation.RuntimeBrokerIDCleared() { + _spec.ClearField(agent.FieldRuntimeBrokerID, field.TypeString) + } + if value, ok := _u.mutation.WebPtyEnabled(); ok { + _spec.SetField(agent.FieldWebPtyEnabled, field.TypeBool, value) + } + if value, ok := _u.mutation.TaskSummary(); ok { + _spec.SetField(agent.FieldTaskSummary, field.TypeString, value) + } + if _u.mutation.TaskSummaryCleared() { + _spec.ClearField(agent.FieldTaskSummary, field.TypeString) + } + if value, ok := _u.mutation.Message(); ok { + _spec.SetField(agent.FieldMessage, field.TypeString, value) + } + if _u.mutation.MessageCleared() { + _spec.ClearField(agent.FieldMessage, field.TypeString) + } + if value, ok := _u.mutation.AppliedConfig(); ok { + _spec.SetField(agent.FieldAppliedConfig, field.TypeString, value) + } + if _u.mutation.AppliedConfigCleared() { + _spec.ClearField(agent.FieldAppliedConfig, field.TypeString) + } + if value, ok := _u.mutation.Ancestry(); ok { + _spec.SetField(agent.FieldAncestry, field.TypeJSON, value) + } + if value, ok := _u.mutation.AppendedAncestry(); ok { + _spec.AddModifier(func(u *sql.UpdateBuilder) { + sqljson.Append(u, agent.FieldAncestry, value) + }) + } + if _u.mutation.AncestryCleared() { + _spec.ClearField(agent.FieldAncestry, field.TypeJSON) + } if value, ok := _u.mutation.Updated(); ok { _spec.SetField(agent.FieldUpdated, field.TypeTime, value) } + if value, ok := _u.mutation.LastSeen(); ok { + _spec.SetField(agent.FieldLastSeen, field.TypeTime, value) + } + if _u.mutation.LastSeenCleared() { + _spec.ClearField(agent.FieldLastSeen, field.TypeTime) + } + if value, ok := _u.mutation.LastActivityEvent(); ok { + _spec.SetField(agent.FieldLastActivityEvent, field.TypeTime, value) + } + if _u.mutation.LastActivityEventCleared() { + _spec.ClearField(agent.FieldLastActivityEvent, field.TypeTime) + } + if value, ok := _u.mutation.StartedAt(); ok { + _spec.SetField(agent.FieldStartedAt, field.TypeTime, value) + } + if _u.mutation.StartedAtCleared() { + _spec.ClearField(agent.FieldStartedAt, field.TypeTime) + } + if value, ok := _u.mutation.DeletedAt(); ok { + _spec.SetField(agent.FieldDeletedAt, field.TypeTime, value) + } + if _u.mutation.DeletedAtCleared() { + _spec.ClearField(agent.FieldDeletedAt, field.TypeTime) + } + if value, ok := _u.mutation.StateVersion(); ok { + _spec.SetField(agent.FieldStateVersion, field.TypeInt64, value) + } + if value, ok := _u.mutation.AddedStateVersion(); ok { + _spec.AddField(agent.FieldStateVersion, field.TypeInt64, value) + } if _u.mutation.ProjectCleared() { edge := &sqlgraph.EdgeSpec{ Rel: sqlgraph.M2O, diff --git a/pkg/ent/allowlistentry.go b/pkg/ent/allowlistentry.go new file mode 100644 index 000000000..1ff32f844 --- /dev/null +++ b/pkg/ent/allowlistentry.go @@ -0,0 +1,151 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "fmt" + "strings" + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "github.com/GoogleCloudPlatform/scion/pkg/ent/allowlistentry" + "github.com/google/uuid" +) + +// AllowListEntry is the model entity for the AllowListEntry schema. +type AllowListEntry struct { + config `json:"-"` + // ID of the ent. + ID uuid.UUID `json:"id,omitempty"` + // Email holds the value of the "email" field. + Email string `json:"email,omitempty"` + // Note holds the value of the "note" field. + Note string `json:"note,omitempty"` + // AddedBy holds the value of the "added_by" field. + AddedBy string `json:"added_by,omitempty"` + // InviteID holds the value of the "invite_id" field. + InviteID string `json:"invite_id,omitempty"` + // Created holds the value of the "created" field. + Created time.Time `json:"created,omitempty"` + selectValues sql.SelectValues +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*AllowListEntry) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case allowlistentry.FieldEmail, allowlistentry.FieldNote, allowlistentry.FieldAddedBy, allowlistentry.FieldInviteID: + values[i] = new(sql.NullString) + case allowlistentry.FieldCreated: + values[i] = new(sql.NullTime) + case allowlistentry.FieldID: + values[i] = new(uuid.UUID) + default: + values[i] = new(sql.UnknownType) + } + } + return values, nil +} + +// assignValues assigns the values that were returned from sql.Rows (after scanning) +// to the AllowListEntry fields. +func (_m *AllowListEntry) assignValues(columns []string, values []any) error { + if m, n := len(values), len(columns); m < n { + return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) + } + for i := range columns { + switch columns[i] { + case allowlistentry.FieldID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field id", values[i]) + } else if value != nil { + _m.ID = *value + } + case allowlistentry.FieldEmail: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field email", values[i]) + } else if value.Valid { + _m.Email = value.String + } + case allowlistentry.FieldNote: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field note", values[i]) + } else if value.Valid { + _m.Note = value.String + } + case allowlistentry.FieldAddedBy: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field added_by", values[i]) + } else if value.Valid { + _m.AddedBy = value.String + } + case allowlistentry.FieldInviteID: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field invite_id", values[i]) + } else if value.Valid { + _m.InviteID = value.String + } + case allowlistentry.FieldCreated: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field created", values[i]) + } else if value.Valid { + _m.Created = value.Time + } + default: + _m.selectValues.Set(columns[i], values[i]) + } + } + return nil +} + +// Value returns the ent.Value that was dynamically selected and assigned to the AllowListEntry. +// This includes values selected through modifiers, order, etc. +func (_m *AllowListEntry) Value(name string) (ent.Value, error) { + return _m.selectValues.Get(name) +} + +// Update returns a builder for updating this AllowListEntry. +// Note that you need to call AllowListEntry.Unwrap() before calling this method if this AllowListEntry +// was returned from a transaction, and the transaction was committed or rolled back. +func (_m *AllowListEntry) Update() *AllowListEntryUpdateOne { + return NewAllowListEntryClient(_m.config).UpdateOne(_m) +} + +// Unwrap unwraps the AllowListEntry entity that was returned from a transaction after it was closed, +// so that all future queries will be executed through the driver which created the transaction. +func (_m *AllowListEntry) Unwrap() *AllowListEntry { + _tx, ok := _m.config.driver.(*txDriver) + if !ok { + panic("ent: AllowListEntry is not a transactional entity") + } + _m.config.driver = _tx.drv + return _m +} + +// String implements the fmt.Stringer. +func (_m *AllowListEntry) String() string { + var builder strings.Builder + builder.WriteString("AllowListEntry(") + builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID)) + builder.WriteString("email=") + builder.WriteString(_m.Email) + builder.WriteString(", ") + builder.WriteString("note=") + builder.WriteString(_m.Note) + builder.WriteString(", ") + builder.WriteString("added_by=") + builder.WriteString(_m.AddedBy) + builder.WriteString(", ") + builder.WriteString("invite_id=") + builder.WriteString(_m.InviteID) + builder.WriteString(", ") + builder.WriteString("created=") + builder.WriteString(_m.Created.Format(time.ANSIC)) + builder.WriteByte(')') + return builder.String() +} + +// AllowListEntries is a parsable slice of AllowListEntry. +type AllowListEntries []*AllowListEntry diff --git a/pkg/ent/allowlistentry/allowlistentry.go b/pkg/ent/allowlistentry/allowlistentry.go new file mode 100644 index 000000000..d62d41de5 --- /dev/null +++ b/pkg/ent/allowlistentry/allowlistentry.go @@ -0,0 +1,95 @@ +// Code generated by ent, DO NOT EDIT. + +package allowlistentry + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "github.com/google/uuid" +) + +const ( + // Label holds the string label denoting the allowlistentry type in the database. + Label = "allow_list_entry" + // FieldID holds the string denoting the id field in the database. + FieldID = "id" + // FieldEmail holds the string denoting the email field in the database. + FieldEmail = "email" + // FieldNote holds the string denoting the note field in the database. + FieldNote = "note" + // FieldAddedBy holds the string denoting the added_by field in the database. + FieldAddedBy = "added_by" + // FieldInviteID holds the string denoting the invite_id field in the database. + FieldInviteID = "invite_id" + // FieldCreated holds the string denoting the created field in the database. + FieldCreated = "created" + // Table holds the table name of the allowlistentry in the database. + Table = "allow_list" +) + +// Columns holds all SQL columns for allowlistentry fields. +var Columns = []string{ + FieldID, + FieldEmail, + FieldNote, + FieldAddedBy, + FieldInviteID, + FieldCreated, +} + +// ValidColumn reports if the column name is valid (part of the table columns). +func ValidColumn(column string) bool { + for i := range Columns { + if column == Columns[i] { + return true + } + } + return false +} + +var ( + // EmailValidator is a validator for the "email" field. It is called by the builders before save. + EmailValidator func(string) error + // DefaultNote holds the default value on creation for the "note" field. + DefaultNote string + // AddedByValidator is a validator for the "added_by" field. It is called by the builders before save. + AddedByValidator func(string) error + // DefaultCreated holds the default value on creation for the "created" field. + DefaultCreated func() time.Time + // DefaultID holds the default value on creation for the "id" field. + DefaultID func() uuid.UUID +) + +// OrderOption defines the ordering options for the AllowListEntry queries. +type OrderOption func(*sql.Selector) + +// ByID orders the results by the id field. +func ByID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldID, opts...).ToFunc() +} + +// ByEmail orders the results by the email field. +func ByEmail(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldEmail, opts...).ToFunc() +} + +// ByNote orders the results by the note field. +func ByNote(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldNote, opts...).ToFunc() +} + +// ByAddedBy orders the results by the added_by field. +func ByAddedBy(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldAddedBy, opts...).ToFunc() +} + +// ByInviteID orders the results by the invite_id field. +func ByInviteID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldInviteID, opts...).ToFunc() +} + +// ByCreated orders the results by the created field. +func ByCreated(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCreated, opts...).ToFunc() +} diff --git a/pkg/ent/allowlistentry/where.go b/pkg/ent/allowlistentry/where.go new file mode 100644 index 000000000..986373109 --- /dev/null +++ b/pkg/ent/allowlistentry/where.go @@ -0,0 +1,406 @@ +// Code generated by ent, DO NOT EDIT. + +package allowlistentry + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/google/uuid" +) + +// ID filters vertices based on their ID field. +func ID(id uuid.UUID) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldEQ(FieldID, id)) +} + +// IDEQ applies the EQ predicate on the ID field. +func IDEQ(id uuid.UUID) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldEQ(FieldID, id)) +} + +// IDNEQ applies the NEQ predicate on the ID field. +func IDNEQ(id uuid.UUID) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldNEQ(FieldID, id)) +} + +// IDIn applies the In predicate on the ID field. +func IDIn(ids ...uuid.UUID) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldIn(FieldID, ids...)) +} + +// IDNotIn applies the NotIn predicate on the ID field. +func IDNotIn(ids ...uuid.UUID) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldNotIn(FieldID, ids...)) +} + +// IDGT applies the GT predicate on the ID field. +func IDGT(id uuid.UUID) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldGT(FieldID, id)) +} + +// IDGTE applies the GTE predicate on the ID field. +func IDGTE(id uuid.UUID) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldGTE(FieldID, id)) +} + +// IDLT applies the LT predicate on the ID field. +func IDLT(id uuid.UUID) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldLT(FieldID, id)) +} + +// IDLTE applies the LTE predicate on the ID field. +func IDLTE(id uuid.UUID) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldLTE(FieldID, id)) +} + +// Email applies equality check predicate on the "email" field. It's identical to EmailEQ. +func Email(v string) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldEQ(FieldEmail, v)) +} + +// Note applies equality check predicate on the "note" field. It's identical to NoteEQ. +func Note(v string) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldEQ(FieldNote, v)) +} + +// AddedBy applies equality check predicate on the "added_by" field. It's identical to AddedByEQ. +func AddedBy(v string) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldEQ(FieldAddedBy, v)) +} + +// InviteID applies equality check predicate on the "invite_id" field. It's identical to InviteIDEQ. +func InviteID(v string) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldEQ(FieldInviteID, v)) +} + +// Created applies equality check predicate on the "created" field. It's identical to CreatedEQ. +func Created(v time.Time) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldEQ(FieldCreated, v)) +} + +// EmailEQ applies the EQ predicate on the "email" field. +func EmailEQ(v string) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldEQ(FieldEmail, v)) +} + +// EmailNEQ applies the NEQ predicate on the "email" field. +func EmailNEQ(v string) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldNEQ(FieldEmail, v)) +} + +// EmailIn applies the In predicate on the "email" field. +func EmailIn(vs ...string) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldIn(FieldEmail, vs...)) +} + +// EmailNotIn applies the NotIn predicate on the "email" field. +func EmailNotIn(vs ...string) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldNotIn(FieldEmail, vs...)) +} + +// EmailGT applies the GT predicate on the "email" field. +func EmailGT(v string) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldGT(FieldEmail, v)) +} + +// EmailGTE applies the GTE predicate on the "email" field. +func EmailGTE(v string) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldGTE(FieldEmail, v)) +} + +// EmailLT applies the LT predicate on the "email" field. +func EmailLT(v string) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldLT(FieldEmail, v)) +} + +// EmailLTE applies the LTE predicate on the "email" field. +func EmailLTE(v string) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldLTE(FieldEmail, v)) +} + +// EmailContains applies the Contains predicate on the "email" field. +func EmailContains(v string) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldContains(FieldEmail, v)) +} + +// EmailHasPrefix applies the HasPrefix predicate on the "email" field. +func EmailHasPrefix(v string) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldHasPrefix(FieldEmail, v)) +} + +// EmailHasSuffix applies the HasSuffix predicate on the "email" field. +func EmailHasSuffix(v string) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldHasSuffix(FieldEmail, v)) +} + +// EmailEqualFold applies the EqualFold predicate on the "email" field. +func EmailEqualFold(v string) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldEqualFold(FieldEmail, v)) +} + +// EmailContainsFold applies the ContainsFold predicate on the "email" field. +func EmailContainsFold(v string) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldContainsFold(FieldEmail, v)) +} + +// NoteEQ applies the EQ predicate on the "note" field. +func NoteEQ(v string) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldEQ(FieldNote, v)) +} + +// NoteNEQ applies the NEQ predicate on the "note" field. +func NoteNEQ(v string) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldNEQ(FieldNote, v)) +} + +// NoteIn applies the In predicate on the "note" field. +func NoteIn(vs ...string) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldIn(FieldNote, vs...)) +} + +// NoteNotIn applies the NotIn predicate on the "note" field. +func NoteNotIn(vs ...string) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldNotIn(FieldNote, vs...)) +} + +// NoteGT applies the GT predicate on the "note" field. +func NoteGT(v string) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldGT(FieldNote, v)) +} + +// NoteGTE applies the GTE predicate on the "note" field. +func NoteGTE(v string) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldGTE(FieldNote, v)) +} + +// NoteLT applies the LT predicate on the "note" field. +func NoteLT(v string) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldLT(FieldNote, v)) +} + +// NoteLTE applies the LTE predicate on the "note" field. +func NoteLTE(v string) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldLTE(FieldNote, v)) +} + +// NoteContains applies the Contains predicate on the "note" field. +func NoteContains(v string) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldContains(FieldNote, v)) +} + +// NoteHasPrefix applies the HasPrefix predicate on the "note" field. +func NoteHasPrefix(v string) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldHasPrefix(FieldNote, v)) +} + +// NoteHasSuffix applies the HasSuffix predicate on the "note" field. +func NoteHasSuffix(v string) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldHasSuffix(FieldNote, v)) +} + +// NoteEqualFold applies the EqualFold predicate on the "note" field. +func NoteEqualFold(v string) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldEqualFold(FieldNote, v)) +} + +// NoteContainsFold applies the ContainsFold predicate on the "note" field. +func NoteContainsFold(v string) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldContainsFold(FieldNote, v)) +} + +// AddedByEQ applies the EQ predicate on the "added_by" field. +func AddedByEQ(v string) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldEQ(FieldAddedBy, v)) +} + +// AddedByNEQ applies the NEQ predicate on the "added_by" field. +func AddedByNEQ(v string) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldNEQ(FieldAddedBy, v)) +} + +// AddedByIn applies the In predicate on the "added_by" field. +func AddedByIn(vs ...string) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldIn(FieldAddedBy, vs...)) +} + +// AddedByNotIn applies the NotIn predicate on the "added_by" field. +func AddedByNotIn(vs ...string) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldNotIn(FieldAddedBy, vs...)) +} + +// AddedByGT applies the GT predicate on the "added_by" field. +func AddedByGT(v string) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldGT(FieldAddedBy, v)) +} + +// AddedByGTE applies the GTE predicate on the "added_by" field. +func AddedByGTE(v string) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldGTE(FieldAddedBy, v)) +} + +// AddedByLT applies the LT predicate on the "added_by" field. +func AddedByLT(v string) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldLT(FieldAddedBy, v)) +} + +// AddedByLTE applies the LTE predicate on the "added_by" field. +func AddedByLTE(v string) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldLTE(FieldAddedBy, v)) +} + +// AddedByContains applies the Contains predicate on the "added_by" field. +func AddedByContains(v string) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldContains(FieldAddedBy, v)) +} + +// AddedByHasPrefix applies the HasPrefix predicate on the "added_by" field. +func AddedByHasPrefix(v string) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldHasPrefix(FieldAddedBy, v)) +} + +// AddedByHasSuffix applies the HasSuffix predicate on the "added_by" field. +func AddedByHasSuffix(v string) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldHasSuffix(FieldAddedBy, v)) +} + +// AddedByEqualFold applies the EqualFold predicate on the "added_by" field. +func AddedByEqualFold(v string) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldEqualFold(FieldAddedBy, v)) +} + +// AddedByContainsFold applies the ContainsFold predicate on the "added_by" field. +func AddedByContainsFold(v string) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldContainsFold(FieldAddedBy, v)) +} + +// InviteIDEQ applies the EQ predicate on the "invite_id" field. +func InviteIDEQ(v string) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldEQ(FieldInviteID, v)) +} + +// InviteIDNEQ applies the NEQ predicate on the "invite_id" field. +func InviteIDNEQ(v string) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldNEQ(FieldInviteID, v)) +} + +// InviteIDIn applies the In predicate on the "invite_id" field. +func InviteIDIn(vs ...string) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldIn(FieldInviteID, vs...)) +} + +// InviteIDNotIn applies the NotIn predicate on the "invite_id" field. +func InviteIDNotIn(vs ...string) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldNotIn(FieldInviteID, vs...)) +} + +// InviteIDGT applies the GT predicate on the "invite_id" field. +func InviteIDGT(v string) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldGT(FieldInviteID, v)) +} + +// InviteIDGTE applies the GTE predicate on the "invite_id" field. +func InviteIDGTE(v string) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldGTE(FieldInviteID, v)) +} + +// InviteIDLT applies the LT predicate on the "invite_id" field. +func InviteIDLT(v string) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldLT(FieldInviteID, v)) +} + +// InviteIDLTE applies the LTE predicate on the "invite_id" field. +func InviteIDLTE(v string) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldLTE(FieldInviteID, v)) +} + +// InviteIDContains applies the Contains predicate on the "invite_id" field. +func InviteIDContains(v string) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldContains(FieldInviteID, v)) +} + +// InviteIDHasPrefix applies the HasPrefix predicate on the "invite_id" field. +func InviteIDHasPrefix(v string) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldHasPrefix(FieldInviteID, v)) +} + +// InviteIDHasSuffix applies the HasSuffix predicate on the "invite_id" field. +func InviteIDHasSuffix(v string) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldHasSuffix(FieldInviteID, v)) +} + +// InviteIDIsNil applies the IsNil predicate on the "invite_id" field. +func InviteIDIsNil() predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldIsNull(FieldInviteID)) +} + +// InviteIDNotNil applies the NotNil predicate on the "invite_id" field. +func InviteIDNotNil() predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldNotNull(FieldInviteID)) +} + +// InviteIDEqualFold applies the EqualFold predicate on the "invite_id" field. +func InviteIDEqualFold(v string) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldEqualFold(FieldInviteID, v)) +} + +// InviteIDContainsFold applies the ContainsFold predicate on the "invite_id" field. +func InviteIDContainsFold(v string) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldContainsFold(FieldInviteID, v)) +} + +// CreatedEQ applies the EQ predicate on the "created" field. +func CreatedEQ(v time.Time) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldEQ(FieldCreated, v)) +} + +// CreatedNEQ applies the NEQ predicate on the "created" field. +func CreatedNEQ(v time.Time) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldNEQ(FieldCreated, v)) +} + +// CreatedIn applies the In predicate on the "created" field. +func CreatedIn(vs ...time.Time) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldIn(FieldCreated, vs...)) +} + +// CreatedNotIn applies the NotIn predicate on the "created" field. +func CreatedNotIn(vs ...time.Time) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldNotIn(FieldCreated, vs...)) +} + +// CreatedGT applies the GT predicate on the "created" field. +func CreatedGT(v time.Time) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldGT(FieldCreated, v)) +} + +// CreatedGTE applies the GTE predicate on the "created" field. +func CreatedGTE(v time.Time) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldGTE(FieldCreated, v)) +} + +// CreatedLT applies the LT predicate on the "created" field. +func CreatedLT(v time.Time) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldLT(FieldCreated, v)) +} + +// CreatedLTE applies the LTE predicate on the "created" field. +func CreatedLTE(v time.Time) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.FieldLTE(FieldCreated, v)) +} + +// And groups predicates with the AND operator between them. +func And(predicates ...predicate.AllowListEntry) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.AndPredicates(predicates...)) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.AllowListEntry) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.OrPredicates(predicates...)) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.AllowListEntry) predicate.AllowListEntry { + return predicate.AllowListEntry(sql.NotPredicates(p)) +} diff --git a/pkg/ent/allowlistentry_create.go b/pkg/ent/allowlistentry_create.go new file mode 100644 index 000000000..a368b6d9a --- /dev/null +++ b/pkg/ent/allowlistentry_create.go @@ -0,0 +1,746 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/allowlistentry" + "github.com/google/uuid" +) + +// AllowListEntryCreate is the builder for creating a AllowListEntry entity. +type AllowListEntryCreate struct { + config + mutation *AllowListEntryMutation + hooks []Hook + conflict []sql.ConflictOption +} + +// SetEmail sets the "email" field. +func (_c *AllowListEntryCreate) SetEmail(v string) *AllowListEntryCreate { + _c.mutation.SetEmail(v) + return _c +} + +// SetNote sets the "note" field. +func (_c *AllowListEntryCreate) SetNote(v string) *AllowListEntryCreate { + _c.mutation.SetNote(v) + return _c +} + +// SetNillableNote sets the "note" field if the given value is not nil. +func (_c *AllowListEntryCreate) SetNillableNote(v *string) *AllowListEntryCreate { + if v != nil { + _c.SetNote(*v) + } + return _c +} + +// SetAddedBy sets the "added_by" field. +func (_c *AllowListEntryCreate) SetAddedBy(v string) *AllowListEntryCreate { + _c.mutation.SetAddedBy(v) + return _c +} + +// SetInviteID sets the "invite_id" field. +func (_c *AllowListEntryCreate) SetInviteID(v string) *AllowListEntryCreate { + _c.mutation.SetInviteID(v) + return _c +} + +// SetNillableInviteID sets the "invite_id" field if the given value is not nil. +func (_c *AllowListEntryCreate) SetNillableInviteID(v *string) *AllowListEntryCreate { + if v != nil { + _c.SetInviteID(*v) + } + return _c +} + +// SetCreated sets the "created" field. +func (_c *AllowListEntryCreate) SetCreated(v time.Time) *AllowListEntryCreate { + _c.mutation.SetCreated(v) + return _c +} + +// SetNillableCreated sets the "created" field if the given value is not nil. +func (_c *AllowListEntryCreate) SetNillableCreated(v *time.Time) *AllowListEntryCreate { + if v != nil { + _c.SetCreated(*v) + } + return _c +} + +// SetID sets the "id" field. +func (_c *AllowListEntryCreate) SetID(v uuid.UUID) *AllowListEntryCreate { + _c.mutation.SetID(v) + return _c +} + +// SetNillableID sets the "id" field if the given value is not nil. +func (_c *AllowListEntryCreate) SetNillableID(v *uuid.UUID) *AllowListEntryCreate { + if v != nil { + _c.SetID(*v) + } + return _c +} + +// Mutation returns the AllowListEntryMutation object of the builder. +func (_c *AllowListEntryCreate) Mutation() *AllowListEntryMutation { + return _c.mutation +} + +// Save creates the AllowListEntry in the database. +func (_c *AllowListEntryCreate) Save(ctx context.Context) (*AllowListEntry, error) { + _c.defaults() + return withHooks(ctx, _c.sqlSave, _c.mutation, _c.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (_c *AllowListEntryCreate) SaveX(ctx context.Context) *AllowListEntry { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *AllowListEntryCreate) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *AllowListEntryCreate) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_c *AllowListEntryCreate) defaults() { + if _, ok := _c.mutation.Note(); !ok { + v := allowlistentry.DefaultNote + _c.mutation.SetNote(v) + } + if _, ok := _c.mutation.Created(); !ok { + v := allowlistentry.DefaultCreated() + _c.mutation.SetCreated(v) + } + if _, ok := _c.mutation.ID(); !ok { + v := allowlistentry.DefaultID() + _c.mutation.SetID(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_c *AllowListEntryCreate) check() error { + if _, ok := _c.mutation.Email(); !ok { + return &ValidationError{Name: "email", err: errors.New(`ent: missing required field "AllowListEntry.email"`)} + } + if v, ok := _c.mutation.Email(); ok { + if err := allowlistentry.EmailValidator(v); err != nil { + return &ValidationError{Name: "email", err: fmt.Errorf(`ent: validator failed for field "AllowListEntry.email": %w`, err)} + } + } + if _, ok := _c.mutation.Note(); !ok { + return &ValidationError{Name: "note", err: errors.New(`ent: missing required field "AllowListEntry.note"`)} + } + if _, ok := _c.mutation.AddedBy(); !ok { + return &ValidationError{Name: "added_by", err: errors.New(`ent: missing required field "AllowListEntry.added_by"`)} + } + if v, ok := _c.mutation.AddedBy(); ok { + if err := allowlistentry.AddedByValidator(v); err != nil { + return &ValidationError{Name: "added_by", err: fmt.Errorf(`ent: validator failed for field "AllowListEntry.added_by": %w`, err)} + } + } + if _, ok := _c.mutation.Created(); !ok { + return &ValidationError{Name: "created", err: errors.New(`ent: missing required field "AllowListEntry.created"`)} + } + return nil +} + +func (_c *AllowListEntryCreate) sqlSave(ctx context.Context) (*AllowListEntry, error) { + if err := _c.check(); err != nil { + return nil, err + } + _node, _spec := _c.createSpec() + if err := sqlgraph.CreateNode(ctx, _c.driver, _spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + if _spec.ID.Value != nil { + if id, ok := _spec.ID.Value.(*uuid.UUID); ok { + _node.ID = *id + } else if err := _node.ID.Scan(_spec.ID.Value); err != nil { + return nil, err + } + } + _c.mutation.id = &_node.ID + _c.mutation.done = true + return _node, nil +} + +func (_c *AllowListEntryCreate) createSpec() (*AllowListEntry, *sqlgraph.CreateSpec) { + var ( + _node = &AllowListEntry{config: _c.config} + _spec = sqlgraph.NewCreateSpec(allowlistentry.Table, sqlgraph.NewFieldSpec(allowlistentry.FieldID, field.TypeUUID)) + ) + _spec.OnConflict = _c.conflict + if id, ok := _c.mutation.ID(); ok { + _node.ID = id + _spec.ID.Value = &id + } + if value, ok := _c.mutation.Email(); ok { + _spec.SetField(allowlistentry.FieldEmail, field.TypeString, value) + _node.Email = value + } + if value, ok := _c.mutation.Note(); ok { + _spec.SetField(allowlistentry.FieldNote, field.TypeString, value) + _node.Note = value + } + if value, ok := _c.mutation.AddedBy(); ok { + _spec.SetField(allowlistentry.FieldAddedBy, field.TypeString, value) + _node.AddedBy = value + } + if value, ok := _c.mutation.InviteID(); ok { + _spec.SetField(allowlistentry.FieldInviteID, field.TypeString, value) + _node.InviteID = value + } + if value, ok := _c.mutation.Created(); ok { + _spec.SetField(allowlistentry.FieldCreated, field.TypeTime, value) + _node.Created = value + } + return _node, _spec +} + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.AllowListEntry.Create(). +// SetEmail(v). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.AllowListEntryUpsert) { +// SetEmail(v+v). +// }). +// Exec(ctx) +func (_c *AllowListEntryCreate) OnConflict(opts ...sql.ConflictOption) *AllowListEntryUpsertOne { + _c.conflict = opts + return &AllowListEntryUpsertOne{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.AllowListEntry.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *AllowListEntryCreate) OnConflictColumns(columns ...string) *AllowListEntryUpsertOne { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &AllowListEntryUpsertOne{ + create: _c, + } +} + +type ( + // AllowListEntryUpsertOne is the builder for "upsert"-ing + // one AllowListEntry node. + AllowListEntryUpsertOne struct { + create *AllowListEntryCreate + } + + // AllowListEntryUpsert is the "OnConflict" setter. + AllowListEntryUpsert struct { + *sql.UpdateSet + } +) + +// SetEmail sets the "email" field. +func (u *AllowListEntryUpsert) SetEmail(v string) *AllowListEntryUpsert { + u.Set(allowlistentry.FieldEmail, v) + return u +} + +// UpdateEmail sets the "email" field to the value that was provided on create. +func (u *AllowListEntryUpsert) UpdateEmail() *AllowListEntryUpsert { + u.SetExcluded(allowlistentry.FieldEmail) + return u +} + +// SetNote sets the "note" field. +func (u *AllowListEntryUpsert) SetNote(v string) *AllowListEntryUpsert { + u.Set(allowlistentry.FieldNote, v) + return u +} + +// UpdateNote sets the "note" field to the value that was provided on create. +func (u *AllowListEntryUpsert) UpdateNote() *AllowListEntryUpsert { + u.SetExcluded(allowlistentry.FieldNote) + return u +} + +// SetAddedBy sets the "added_by" field. +func (u *AllowListEntryUpsert) SetAddedBy(v string) *AllowListEntryUpsert { + u.Set(allowlistentry.FieldAddedBy, v) + return u +} + +// UpdateAddedBy sets the "added_by" field to the value that was provided on create. +func (u *AllowListEntryUpsert) UpdateAddedBy() *AllowListEntryUpsert { + u.SetExcluded(allowlistentry.FieldAddedBy) + return u +} + +// SetInviteID sets the "invite_id" field. +func (u *AllowListEntryUpsert) SetInviteID(v string) *AllowListEntryUpsert { + u.Set(allowlistentry.FieldInviteID, v) + return u +} + +// UpdateInviteID sets the "invite_id" field to the value that was provided on create. +func (u *AllowListEntryUpsert) UpdateInviteID() *AllowListEntryUpsert { + u.SetExcluded(allowlistentry.FieldInviteID) + return u +} + +// ClearInviteID clears the value of the "invite_id" field. +func (u *AllowListEntryUpsert) ClearInviteID() *AllowListEntryUpsert { + u.SetNull(allowlistentry.FieldInviteID) + return u +} + +// UpdateNewValues updates the mutable fields using the new values that were set on create except the ID field. +// Using this option is equivalent to using: +// +// client.AllowListEntry.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(allowlistentry.FieldID) +// }), +// ). +// Exec(ctx) +func (u *AllowListEntryUpsertOne) UpdateNewValues() *AllowListEntryUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + if _, exists := u.create.mutation.ID(); exists { + s.SetIgnore(allowlistentry.FieldID) + } + if _, exists := u.create.mutation.Created(); exists { + s.SetIgnore(allowlistentry.FieldCreated) + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.AllowListEntry.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *AllowListEntryUpsertOne) Ignore() *AllowListEntryUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *AllowListEntryUpsertOne) DoNothing() *AllowListEntryUpsertOne { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the AllowListEntryCreate.OnConflict +// documentation for more info. +func (u *AllowListEntryUpsertOne) Update(set func(*AllowListEntryUpsert)) *AllowListEntryUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&AllowListEntryUpsert{UpdateSet: update}) + })) + return u +} + +// SetEmail sets the "email" field. +func (u *AllowListEntryUpsertOne) SetEmail(v string) *AllowListEntryUpsertOne { + return u.Update(func(s *AllowListEntryUpsert) { + s.SetEmail(v) + }) +} + +// UpdateEmail sets the "email" field to the value that was provided on create. +func (u *AllowListEntryUpsertOne) UpdateEmail() *AllowListEntryUpsertOne { + return u.Update(func(s *AllowListEntryUpsert) { + s.UpdateEmail() + }) +} + +// SetNote sets the "note" field. +func (u *AllowListEntryUpsertOne) SetNote(v string) *AllowListEntryUpsertOne { + return u.Update(func(s *AllowListEntryUpsert) { + s.SetNote(v) + }) +} + +// UpdateNote sets the "note" field to the value that was provided on create. +func (u *AllowListEntryUpsertOne) UpdateNote() *AllowListEntryUpsertOne { + return u.Update(func(s *AllowListEntryUpsert) { + s.UpdateNote() + }) +} + +// SetAddedBy sets the "added_by" field. +func (u *AllowListEntryUpsertOne) SetAddedBy(v string) *AllowListEntryUpsertOne { + return u.Update(func(s *AllowListEntryUpsert) { + s.SetAddedBy(v) + }) +} + +// UpdateAddedBy sets the "added_by" field to the value that was provided on create. +func (u *AllowListEntryUpsertOne) UpdateAddedBy() *AllowListEntryUpsertOne { + return u.Update(func(s *AllowListEntryUpsert) { + s.UpdateAddedBy() + }) +} + +// SetInviteID sets the "invite_id" field. +func (u *AllowListEntryUpsertOne) SetInviteID(v string) *AllowListEntryUpsertOne { + return u.Update(func(s *AllowListEntryUpsert) { + s.SetInviteID(v) + }) +} + +// UpdateInviteID sets the "invite_id" field to the value that was provided on create. +func (u *AllowListEntryUpsertOne) UpdateInviteID() *AllowListEntryUpsertOne { + return u.Update(func(s *AllowListEntryUpsert) { + s.UpdateInviteID() + }) +} + +// ClearInviteID clears the value of the "invite_id" field. +func (u *AllowListEntryUpsertOne) ClearInviteID() *AllowListEntryUpsertOne { + return u.Update(func(s *AllowListEntryUpsert) { + s.ClearInviteID() + }) +} + +// Exec executes the query. +func (u *AllowListEntryUpsertOne) Exec(ctx context.Context) error { + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for AllowListEntryCreate.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *AllowListEntryUpsertOne) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} + +// Exec executes the UPSERT query and returns the inserted/updated ID. +func (u *AllowListEntryUpsertOne) ID(ctx context.Context) (id uuid.UUID, err error) { + if u.create.driver.Dialect() == dialect.MySQL { + // In case of "ON CONFLICT", there is no way to get back non-numeric ID + // fields from the database since MySQL does not support the RETURNING clause. + return id, errors.New("ent: AllowListEntryUpsertOne.ID is not supported by MySQL driver. Use AllowListEntryUpsertOne.Exec instead") + } + node, err := u.create.Save(ctx) + if err != nil { + return id, err + } + return node.ID, nil +} + +// IDX is like ID, but panics if an error occurs. +func (u *AllowListEntryUpsertOne) IDX(ctx context.Context) uuid.UUID { + id, err := u.ID(ctx) + if err != nil { + panic(err) + } + return id +} + +// AllowListEntryCreateBulk is the builder for creating many AllowListEntry entities in bulk. +type AllowListEntryCreateBulk struct { + config + err error + builders []*AllowListEntryCreate + conflict []sql.ConflictOption +} + +// Save creates the AllowListEntry entities in the database. +func (_c *AllowListEntryCreateBulk) Save(ctx context.Context) ([]*AllowListEntry, error) { + if _c.err != nil { + return nil, _c.err + } + specs := make([]*sqlgraph.CreateSpec, len(_c.builders)) + nodes := make([]*AllowListEntry, len(_c.builders)) + mutators := make([]Mutator, len(_c.builders)) + for i := range _c.builders { + func(i int, root context.Context) { + builder := _c.builders[i] + builder.defaults() + var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { + mutation, ok := m.(*AllowListEntryMutation) + if !ok { + return nil, fmt.Errorf("unexpected mutation type %T", m) + } + if err := builder.check(); err != nil { + return nil, err + } + builder.mutation = mutation + var err error + nodes[i], specs[i] = builder.createSpec() + if i < len(mutators)-1 { + _, err = mutators[i+1].Mutate(root, _c.builders[i+1].mutation) + } else { + spec := &sqlgraph.BatchCreateSpec{Nodes: specs} + spec.OnConflict = _c.conflict + // Invoke the actual operation on the latest mutation in the chain. + if err = sqlgraph.BatchCreate(ctx, _c.driver, spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + } + } + if err != nil { + return nil, err + } + mutation.id = &nodes[i].ID + mutation.done = true + return nodes[i], nil + }) + for i := len(builder.hooks) - 1; i >= 0; i-- { + mut = builder.hooks[i](mut) + } + mutators[i] = mut + }(i, ctx) + } + if len(mutators) > 0 { + if _, err := mutators[0].Mutate(ctx, _c.builders[0].mutation); err != nil { + return nil, err + } + } + return nodes, nil +} + +// SaveX is like Save, but panics if an error occurs. +func (_c *AllowListEntryCreateBulk) SaveX(ctx context.Context) []*AllowListEntry { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *AllowListEntryCreateBulk) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *AllowListEntryCreateBulk) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.AllowListEntry.CreateBulk(builders...). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.AllowListEntryUpsert) { +// SetEmail(v+v). +// }). +// Exec(ctx) +func (_c *AllowListEntryCreateBulk) OnConflict(opts ...sql.ConflictOption) *AllowListEntryUpsertBulk { + _c.conflict = opts + return &AllowListEntryUpsertBulk{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.AllowListEntry.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *AllowListEntryCreateBulk) OnConflictColumns(columns ...string) *AllowListEntryUpsertBulk { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &AllowListEntryUpsertBulk{ + create: _c, + } +} + +// AllowListEntryUpsertBulk is the builder for "upsert"-ing +// a bulk of AllowListEntry nodes. +type AllowListEntryUpsertBulk struct { + create *AllowListEntryCreateBulk +} + +// UpdateNewValues updates the mutable fields using the new values that +// were set on create. Using this option is equivalent to using: +// +// client.AllowListEntry.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(allowlistentry.FieldID) +// }), +// ). +// Exec(ctx) +func (u *AllowListEntryUpsertBulk) UpdateNewValues() *AllowListEntryUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + for _, b := range u.create.builders { + if _, exists := b.mutation.ID(); exists { + s.SetIgnore(allowlistentry.FieldID) + } + if _, exists := b.mutation.Created(); exists { + s.SetIgnore(allowlistentry.FieldCreated) + } + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.AllowListEntry.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *AllowListEntryUpsertBulk) Ignore() *AllowListEntryUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *AllowListEntryUpsertBulk) DoNothing() *AllowListEntryUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the AllowListEntryCreateBulk.OnConflict +// documentation for more info. +func (u *AllowListEntryUpsertBulk) Update(set func(*AllowListEntryUpsert)) *AllowListEntryUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&AllowListEntryUpsert{UpdateSet: update}) + })) + return u +} + +// SetEmail sets the "email" field. +func (u *AllowListEntryUpsertBulk) SetEmail(v string) *AllowListEntryUpsertBulk { + return u.Update(func(s *AllowListEntryUpsert) { + s.SetEmail(v) + }) +} + +// UpdateEmail sets the "email" field to the value that was provided on create. +func (u *AllowListEntryUpsertBulk) UpdateEmail() *AllowListEntryUpsertBulk { + return u.Update(func(s *AllowListEntryUpsert) { + s.UpdateEmail() + }) +} + +// SetNote sets the "note" field. +func (u *AllowListEntryUpsertBulk) SetNote(v string) *AllowListEntryUpsertBulk { + return u.Update(func(s *AllowListEntryUpsert) { + s.SetNote(v) + }) +} + +// UpdateNote sets the "note" field to the value that was provided on create. +func (u *AllowListEntryUpsertBulk) UpdateNote() *AllowListEntryUpsertBulk { + return u.Update(func(s *AllowListEntryUpsert) { + s.UpdateNote() + }) +} + +// SetAddedBy sets the "added_by" field. +func (u *AllowListEntryUpsertBulk) SetAddedBy(v string) *AllowListEntryUpsertBulk { + return u.Update(func(s *AllowListEntryUpsert) { + s.SetAddedBy(v) + }) +} + +// UpdateAddedBy sets the "added_by" field to the value that was provided on create. +func (u *AllowListEntryUpsertBulk) UpdateAddedBy() *AllowListEntryUpsertBulk { + return u.Update(func(s *AllowListEntryUpsert) { + s.UpdateAddedBy() + }) +} + +// SetInviteID sets the "invite_id" field. +func (u *AllowListEntryUpsertBulk) SetInviteID(v string) *AllowListEntryUpsertBulk { + return u.Update(func(s *AllowListEntryUpsert) { + s.SetInviteID(v) + }) +} + +// UpdateInviteID sets the "invite_id" field to the value that was provided on create. +func (u *AllowListEntryUpsertBulk) UpdateInviteID() *AllowListEntryUpsertBulk { + return u.Update(func(s *AllowListEntryUpsert) { + s.UpdateInviteID() + }) +} + +// ClearInviteID clears the value of the "invite_id" field. +func (u *AllowListEntryUpsertBulk) ClearInviteID() *AllowListEntryUpsertBulk { + return u.Update(func(s *AllowListEntryUpsert) { + s.ClearInviteID() + }) +} + +// Exec executes the query. +func (u *AllowListEntryUpsertBulk) Exec(ctx context.Context) error { + if u.create.err != nil { + return u.create.err + } + for i, b := range u.create.builders { + if len(b.conflict) != 0 { + return fmt.Errorf("ent: OnConflict was set for builder %d. Set it on the AllowListEntryCreateBulk instead", i) + } + } + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for AllowListEntryCreateBulk.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *AllowListEntryUpsertBulk) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/pkg/ent/allowlistentry_delete.go b/pkg/ent/allowlistentry_delete.go new file mode 100644 index 000000000..d4a9375cc --- /dev/null +++ b/pkg/ent/allowlistentry_delete.go @@ -0,0 +1,88 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/allowlistentry" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" +) + +// AllowListEntryDelete is the builder for deleting a AllowListEntry entity. +type AllowListEntryDelete struct { + config + hooks []Hook + mutation *AllowListEntryMutation +} + +// Where appends a list predicates to the AllowListEntryDelete builder. +func (_d *AllowListEntryDelete) Where(ps ...predicate.AllowListEntry) *AllowListEntryDelete { + _d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (_d *AllowListEntryDelete) Exec(ctx context.Context) (int, error) { + return withHooks(ctx, _d.sqlExec, _d.mutation, _d.hooks) +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *AllowListEntryDelete) ExecX(ctx context.Context) int { + n, err := _d.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (_d *AllowListEntryDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(allowlistentry.Table, sqlgraph.NewFieldSpec(allowlistentry.FieldID, field.TypeUUID)) + if ps := _d.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + affected, err := sqlgraph.DeleteNodes(ctx, _d.driver, _spec) + if err != nil && sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + _d.mutation.done = true + return affected, err +} + +// AllowListEntryDeleteOne is the builder for deleting a single AllowListEntry entity. +type AllowListEntryDeleteOne struct { + _d *AllowListEntryDelete +} + +// Where appends a list predicates to the AllowListEntryDelete builder. +func (_d *AllowListEntryDeleteOne) Where(ps ...predicate.AllowListEntry) *AllowListEntryDeleteOne { + _d._d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query. +func (_d *AllowListEntryDeleteOne) Exec(ctx context.Context) error { + n, err := _d._d.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{allowlistentry.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *AllowListEntryDeleteOne) ExecX(ctx context.Context) { + if err := _d.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/pkg/ent/allowlistentry_query.go b/pkg/ent/allowlistentry_query.go new file mode 100644 index 000000000..4dd9da256 --- /dev/null +++ b/pkg/ent/allowlistentry_query.go @@ -0,0 +1,565 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "fmt" + "math" + + "entgo.io/ent" + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/allowlistentry" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/google/uuid" +) + +// AllowListEntryQuery is the builder for querying AllowListEntry entities. +type AllowListEntryQuery struct { + config + ctx *QueryContext + order []allowlistentry.OrderOption + inters []Interceptor + predicates []predicate.AllowListEntry + modifiers []func(*sql.Selector) + // intermediate query (i.e. traversal path). + sql *sql.Selector + path func(context.Context) (*sql.Selector, error) +} + +// Where adds a new predicate for the AllowListEntryQuery builder. +func (_q *AllowListEntryQuery) Where(ps ...predicate.AllowListEntry) *AllowListEntryQuery { + _q.predicates = append(_q.predicates, ps...) + return _q +} + +// Limit the number of records to be returned by this query. +func (_q *AllowListEntryQuery) Limit(limit int) *AllowListEntryQuery { + _q.ctx.Limit = &limit + return _q +} + +// Offset to start from. +func (_q *AllowListEntryQuery) Offset(offset int) *AllowListEntryQuery { + _q.ctx.Offset = &offset + return _q +} + +// Unique configures the query builder to filter duplicate records on query. +// By default, unique is set to true, and can be disabled using this method. +func (_q *AllowListEntryQuery) Unique(unique bool) *AllowListEntryQuery { + _q.ctx.Unique = &unique + return _q +} + +// Order specifies how the records should be ordered. +func (_q *AllowListEntryQuery) Order(o ...allowlistentry.OrderOption) *AllowListEntryQuery { + _q.order = append(_q.order, o...) + return _q +} + +// First returns the first AllowListEntry entity from the query. +// Returns a *NotFoundError when no AllowListEntry was found. +func (_q *AllowListEntryQuery) First(ctx context.Context) (*AllowListEntry, error) { + nodes, err := _q.Limit(1).All(setContextOp(ctx, _q.ctx, ent.OpQueryFirst)) + if err != nil { + return nil, err + } + if len(nodes) == 0 { + return nil, &NotFoundError{allowlistentry.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (_q *AllowListEntryQuery) FirstX(ctx context.Context) *AllowListEntry { + node, err := _q.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// FirstID returns the first AllowListEntry ID from the query. +// Returns a *NotFoundError when no AllowListEntry ID was found. +func (_q *AllowListEntryQuery) FirstID(ctx context.Context) (id uuid.UUID, err error) { + var ids []uuid.UUID + if ids, err = _q.Limit(1).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryFirstID)); err != nil { + return + } + if len(ids) == 0 { + err = &NotFoundError{allowlistentry.Label} + return + } + return ids[0], nil +} + +// FirstIDX is like FirstID, but panics if an error occurs. +func (_q *AllowListEntryQuery) FirstIDX(ctx context.Context) uuid.UUID { + id, err := _q.FirstID(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return id +} + +// Only returns a single AllowListEntry entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one AllowListEntry entity is found. +// Returns a *NotFoundError when no AllowListEntry entities are found. +func (_q *AllowListEntryQuery) Only(ctx context.Context) (*AllowListEntry, error) { + nodes, err := _q.Limit(2).All(setContextOp(ctx, _q.ctx, ent.OpQueryOnly)) + if err != nil { + return nil, err + } + switch len(nodes) { + case 1: + return nodes[0], nil + case 0: + return nil, &NotFoundError{allowlistentry.Label} + default: + return nil, &NotSingularError{allowlistentry.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (_q *AllowListEntryQuery) OnlyX(ctx context.Context) *AllowListEntry { + node, err := _q.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// OnlyID is like Only, but returns the only AllowListEntry ID in the query. +// Returns a *NotSingularError when more than one AllowListEntry ID is found. +// Returns a *NotFoundError when no entities are found. +func (_q *AllowListEntryQuery) OnlyID(ctx context.Context) (id uuid.UUID, err error) { + var ids []uuid.UUID + if ids, err = _q.Limit(2).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryOnlyID)); err != nil { + return + } + switch len(ids) { + case 1: + id = ids[0] + case 0: + err = &NotFoundError{allowlistentry.Label} + default: + err = &NotSingularError{allowlistentry.Label} + } + return +} + +// OnlyIDX is like OnlyID, but panics if an error occurs. +func (_q *AllowListEntryQuery) OnlyIDX(ctx context.Context) uuid.UUID { + id, err := _q.OnlyID(ctx) + if err != nil { + panic(err) + } + return id +} + +// All executes the query and returns a list of AllowListEntries. +func (_q *AllowListEntryQuery) All(ctx context.Context) ([]*AllowListEntry, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryAll) + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*AllowListEntry, *AllowListEntryQuery]() + return withInterceptors[[]*AllowListEntry](ctx, _q, qr, _q.inters) +} + +// AllX is like All, but panics if an error occurs. +func (_q *AllowListEntryQuery) AllX(ctx context.Context) []*AllowListEntry { + nodes, err := _q.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// IDs executes the query and returns a list of AllowListEntry IDs. +func (_q *AllowListEntryQuery) IDs(ctx context.Context) (ids []uuid.UUID, err error) { + if _q.ctx.Unique == nil && _q.path != nil { + _q.Unique(true) + } + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryIDs) + if err = _q.Select(allowlistentry.FieldID).Scan(ctx, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// IDsX is like IDs, but panics if an error occurs. +func (_q *AllowListEntryQuery) IDsX(ctx context.Context) []uuid.UUID { + ids, err := _q.IDs(ctx) + if err != nil { + panic(err) + } + return ids +} + +// Count returns the count of the given query. +func (_q *AllowListEntryQuery) Count(ctx context.Context) (int, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryCount) + if err := _q.prepareQuery(ctx); err != nil { + return 0, err + } + return withInterceptors[int](ctx, _q, querierCount[*AllowListEntryQuery](), _q.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (_q *AllowListEntryQuery) CountX(ctx context.Context) int { + count, err := _q.Count(ctx) + if err != nil { + panic(err) + } + return count +} + +// Exist returns true if the query has elements in the graph. +func (_q *AllowListEntryQuery) Exist(ctx context.Context) (bool, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryExist) + switch _, err := _q.FirstID(ctx); { + case IsNotFound(err): + return false, nil + case err != nil: + return false, fmt.Errorf("ent: check existence: %w", err) + default: + return true, nil + } +} + +// ExistX is like Exist, but panics if an error occurs. +func (_q *AllowListEntryQuery) ExistX(ctx context.Context) bool { + exist, err := _q.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the AllowListEntryQuery builder, including all associated steps. It can be +// used to prepare common query builders and use them differently after the clone is made. +func (_q *AllowListEntryQuery) Clone() *AllowListEntryQuery { + if _q == nil { + return nil + } + return &AllowListEntryQuery{ + config: _q.config, + ctx: _q.ctx.Clone(), + order: append([]allowlistentry.OrderOption{}, _q.order...), + inters: append([]Interceptor{}, _q.inters...), + predicates: append([]predicate.AllowListEntry{}, _q.predicates...), + // clone intermediate query. + sql: _q.sql.Clone(), + path: _q.path, + } +} + +// GroupBy is used to group vertices by one or more fields/columns. +// It is often used with aggregate functions, like: count, max, mean, min, sum. +// +// Example: +// +// var v []struct { +// Email string `json:"email,omitempty"` +// Count int `json:"count,omitempty"` +// } +// +// client.AllowListEntry.Query(). +// GroupBy(allowlistentry.FieldEmail). +// Aggregate(ent.Count()). +// Scan(ctx, &v) +func (_q *AllowListEntryQuery) GroupBy(field string, fields ...string) *AllowListEntryGroupBy { + _q.ctx.Fields = append([]string{field}, fields...) + grbuild := &AllowListEntryGroupBy{build: _q} + grbuild.flds = &_q.ctx.Fields + grbuild.label = allowlistentry.Label + grbuild.scan = grbuild.Scan + return grbuild +} + +// Select allows the selection one or more fields/columns for the given query, +// instead of selecting all fields in the entity. +// +// Example: +// +// var v []struct { +// Email string `json:"email,omitempty"` +// } +// +// client.AllowListEntry.Query(). +// Select(allowlistentry.FieldEmail). +// Scan(ctx, &v) +func (_q *AllowListEntryQuery) Select(fields ...string) *AllowListEntrySelect { + _q.ctx.Fields = append(_q.ctx.Fields, fields...) + sbuild := &AllowListEntrySelect{AllowListEntryQuery: _q} + sbuild.label = allowlistentry.Label + sbuild.flds, sbuild.scan = &_q.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a AllowListEntrySelect configured with the given aggregations. +func (_q *AllowListEntryQuery) Aggregate(fns ...AggregateFunc) *AllowListEntrySelect { + return _q.Select().Aggregate(fns...) +} + +func (_q *AllowListEntryQuery) prepareQuery(ctx context.Context) error { + for _, inter := range _q.inters { + if inter == nil { + return fmt.Errorf("ent: uninitialized interceptor (forgotten import ent/runtime?)") + } + if trv, ok := inter.(Traverser); ok { + if err := trv.Traverse(ctx, _q); err != nil { + return err + } + } + } + for _, f := range _q.ctx.Fields { + if !allowlistentry.ValidColumn(f) { + return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + } + if _q.path != nil { + prev, err := _q.path(ctx) + if err != nil { + return err + } + _q.sql = prev + } + return nil +} + +func (_q *AllowListEntryQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*AllowListEntry, error) { + var ( + nodes = []*AllowListEntry{} + _spec = _q.querySpec() + ) + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*AllowListEntry).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &AllowListEntry{config: _q.config} + nodes = append(nodes, node) + return node.assignValues(columns, values) + } + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } + for i := range hooks { + hooks[i](ctx, _spec) + } + if err := sqlgraph.QueryNodes(ctx, _q.driver, _spec); err != nil { + return nil, err + } + if len(nodes) == 0 { + return nodes, nil + } + return nodes, nil +} + +func (_q *AllowListEntryQuery) sqlCount(ctx context.Context) (int, error) { + _spec := _q.querySpec() + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } + _spec.Node.Columns = _q.ctx.Fields + if len(_q.ctx.Fields) > 0 { + _spec.Unique = _q.ctx.Unique != nil && *_q.ctx.Unique + } + return sqlgraph.CountNodes(ctx, _q.driver, _spec) +} + +func (_q *AllowListEntryQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(allowlistentry.Table, allowlistentry.Columns, sqlgraph.NewFieldSpec(allowlistentry.FieldID, field.TypeUUID)) + _spec.From = _q.sql + if unique := _q.ctx.Unique; unique != nil { + _spec.Unique = *unique + } else if _q.path != nil { + _spec.Unique = true + } + if fields := _q.ctx.Fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, allowlistentry.FieldID) + for i := range fields { + if fields[i] != allowlistentry.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, fields[i]) + } + } + } + if ps := _q.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if limit := _q.ctx.Limit; limit != nil { + _spec.Limit = *limit + } + if offset := _q.ctx.Offset; offset != nil { + _spec.Offset = *offset + } + if ps := _q.order; len(ps) > 0 { + _spec.Order = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + return _spec +} + +func (_q *AllowListEntryQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(_q.driver.Dialect()) + t1 := builder.Table(allowlistentry.Table) + columns := _q.ctx.Fields + if len(columns) == 0 { + columns = allowlistentry.Columns + } + selector := builder.Select(t1.Columns(columns...)...).From(t1) + if _q.sql != nil { + selector = _q.sql + selector.Select(selector.Columns(columns...)...) + } + if _q.ctx.Unique != nil && *_q.ctx.Unique { + selector.Distinct() + } + for _, m := range _q.modifiers { + m(selector) + } + for _, p := range _q.predicates { + p(selector) + } + for _, p := range _q.order { + p(selector) + } + if offset := _q.ctx.Offset; offset != nil { + // limit is mandatory for offset clause. We start + // with default value, and override it below if needed. + selector.Offset(*offset).Limit(math.MaxInt32) + } + if limit := _q.ctx.Limit; limit != nil { + selector.Limit(*limit) + } + return selector +} + +// ForUpdate locks the selected rows against concurrent updates, and prevent them from being +// updated, deleted or "selected ... for update" by other sessions, until the transaction is +// either committed or rolled-back. +func (_q *AllowListEntryQuery) ForUpdate(opts ...sql.LockOption) *AllowListEntryQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForUpdate(opts...) + }) + return _q +} + +// ForShare behaves similarly to ForUpdate, except that it acquires a shared mode lock +// on any rows that are read. Other sessions can read the rows, but cannot modify them +// until your transaction commits. +func (_q *AllowListEntryQuery) ForShare(opts ...sql.LockOption) *AllowListEntryQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForShare(opts...) + }) + return _q +} + +// AllowListEntryGroupBy is the group-by builder for AllowListEntry entities. +type AllowListEntryGroupBy struct { + selector + build *AllowListEntryQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (_g *AllowListEntryGroupBy) Aggregate(fns ...AggregateFunc) *AllowListEntryGroupBy { + _g.fns = append(_g.fns, fns...) + return _g +} + +// Scan applies the selector query and scans the result into the given value. +func (_g *AllowListEntryGroupBy) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, _g.build.ctx, ent.OpQueryGroupBy) + if err := _g.build.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*AllowListEntryQuery, *AllowListEntryGroupBy](ctx, _g.build, _g, _g.build.inters, v) +} + +func (_g *AllowListEntryGroupBy) sqlScan(ctx context.Context, root *AllowListEntryQuery, v any) error { + selector := root.sqlQuery(ctx).Select() + aggregation := make([]string, 0, len(_g.fns)) + for _, fn := range _g.fns { + aggregation = append(aggregation, fn(selector)) + } + if len(selector.SelectedColumns()) == 0 { + columns := make([]string, 0, len(*_g.flds)+len(_g.fns)) + for _, f := range *_g.flds { + columns = append(columns, selector.C(f)) + } + columns = append(columns, aggregation...) + selector.Select(columns...) + } + selector.GroupBy(selector.Columns(*_g.flds...)...) + if err := selector.Err(); err != nil { + return err + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := _g.build.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} + +// AllowListEntrySelect is the builder for selecting fields of AllowListEntry entities. +type AllowListEntrySelect struct { + *AllowListEntryQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (_s *AllowListEntrySelect) Aggregate(fns ...AggregateFunc) *AllowListEntrySelect { + _s.fns = append(_s.fns, fns...) + return _s +} + +// Scan applies the selector query and scans the result into the given value. +func (_s *AllowListEntrySelect) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, _s.ctx, ent.OpQuerySelect) + if err := _s.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*AllowListEntryQuery, *AllowListEntrySelect](ctx, _s.AllowListEntryQuery, _s, _s.inters, v) +} + +func (_s *AllowListEntrySelect) sqlScan(ctx context.Context, root *AllowListEntryQuery, v any) error { + selector := root.sqlQuery(ctx) + aggregation := make([]string, 0, len(_s.fns)) + for _, fn := range _s.fns { + aggregation = append(aggregation, fn(selector)) + } + switch n := len(*_s.selector.flds); { + case n == 0 && len(aggregation) > 0: + selector.Select(aggregation...) + case n != 0 && len(aggregation) > 0: + selector.AppendSelect(aggregation...) + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := _s.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} diff --git a/pkg/ent/allowlistentry_update.go b/pkg/ent/allowlistentry_update.go new file mode 100644 index 000000000..c7dcbf88b --- /dev/null +++ b/pkg/ent/allowlistentry_update.go @@ -0,0 +1,365 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/allowlistentry" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" +) + +// AllowListEntryUpdate is the builder for updating AllowListEntry entities. +type AllowListEntryUpdate struct { + config + hooks []Hook + mutation *AllowListEntryMutation +} + +// Where appends a list predicates to the AllowListEntryUpdate builder. +func (_u *AllowListEntryUpdate) Where(ps ...predicate.AllowListEntry) *AllowListEntryUpdate { + _u.mutation.Where(ps...) + return _u +} + +// SetEmail sets the "email" field. +func (_u *AllowListEntryUpdate) SetEmail(v string) *AllowListEntryUpdate { + _u.mutation.SetEmail(v) + return _u +} + +// SetNillableEmail sets the "email" field if the given value is not nil. +func (_u *AllowListEntryUpdate) SetNillableEmail(v *string) *AllowListEntryUpdate { + if v != nil { + _u.SetEmail(*v) + } + return _u +} + +// SetNote sets the "note" field. +func (_u *AllowListEntryUpdate) SetNote(v string) *AllowListEntryUpdate { + _u.mutation.SetNote(v) + return _u +} + +// SetNillableNote sets the "note" field if the given value is not nil. +func (_u *AllowListEntryUpdate) SetNillableNote(v *string) *AllowListEntryUpdate { + if v != nil { + _u.SetNote(*v) + } + return _u +} + +// SetAddedBy sets the "added_by" field. +func (_u *AllowListEntryUpdate) SetAddedBy(v string) *AllowListEntryUpdate { + _u.mutation.SetAddedBy(v) + return _u +} + +// SetNillableAddedBy sets the "added_by" field if the given value is not nil. +func (_u *AllowListEntryUpdate) SetNillableAddedBy(v *string) *AllowListEntryUpdate { + if v != nil { + _u.SetAddedBy(*v) + } + return _u +} + +// SetInviteID sets the "invite_id" field. +func (_u *AllowListEntryUpdate) SetInviteID(v string) *AllowListEntryUpdate { + _u.mutation.SetInviteID(v) + return _u +} + +// SetNillableInviteID sets the "invite_id" field if the given value is not nil. +func (_u *AllowListEntryUpdate) SetNillableInviteID(v *string) *AllowListEntryUpdate { + if v != nil { + _u.SetInviteID(*v) + } + return _u +} + +// ClearInviteID clears the value of the "invite_id" field. +func (_u *AllowListEntryUpdate) ClearInviteID() *AllowListEntryUpdate { + _u.mutation.ClearInviteID() + return _u +} + +// Mutation returns the AllowListEntryMutation object of the builder. +func (_u *AllowListEntryUpdate) Mutation() *AllowListEntryMutation { + return _u.mutation +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (_u *AllowListEntryUpdate) Save(ctx context.Context) (int, error) { + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *AllowListEntryUpdate) SaveX(ctx context.Context) int { + affected, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (_u *AllowListEntryUpdate) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *AllowListEntryUpdate) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_u *AllowListEntryUpdate) check() error { + if v, ok := _u.mutation.Email(); ok { + if err := allowlistentry.EmailValidator(v); err != nil { + return &ValidationError{Name: "email", err: fmt.Errorf(`ent: validator failed for field "AllowListEntry.email": %w`, err)} + } + } + if v, ok := _u.mutation.AddedBy(); ok { + if err := allowlistentry.AddedByValidator(v); err != nil { + return &ValidationError{Name: "added_by", err: fmt.Errorf(`ent: validator failed for field "AllowListEntry.added_by": %w`, err)} + } + } + return nil +} + +func (_u *AllowListEntryUpdate) sqlSave(ctx context.Context) (_node int, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(allowlistentry.Table, allowlistentry.Columns, sqlgraph.NewFieldSpec(allowlistentry.FieldID, field.TypeUUID)) + if ps := _u.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := _u.mutation.Email(); ok { + _spec.SetField(allowlistentry.FieldEmail, field.TypeString, value) + } + if value, ok := _u.mutation.Note(); ok { + _spec.SetField(allowlistentry.FieldNote, field.TypeString, value) + } + if value, ok := _u.mutation.AddedBy(); ok { + _spec.SetField(allowlistentry.FieldAddedBy, field.TypeString, value) + } + if value, ok := _u.mutation.InviteID(); ok { + _spec.SetField(allowlistentry.FieldInviteID, field.TypeString, value) + } + if _u.mutation.InviteIDCleared() { + _spec.ClearField(allowlistentry.FieldInviteID, field.TypeString) + } + if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{allowlistentry.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + _u.mutation.done = true + return _node, nil +} + +// AllowListEntryUpdateOne is the builder for updating a single AllowListEntry entity. +type AllowListEntryUpdateOne struct { + config + fields []string + hooks []Hook + mutation *AllowListEntryMutation +} + +// SetEmail sets the "email" field. +func (_u *AllowListEntryUpdateOne) SetEmail(v string) *AllowListEntryUpdateOne { + _u.mutation.SetEmail(v) + return _u +} + +// SetNillableEmail sets the "email" field if the given value is not nil. +func (_u *AllowListEntryUpdateOne) SetNillableEmail(v *string) *AllowListEntryUpdateOne { + if v != nil { + _u.SetEmail(*v) + } + return _u +} + +// SetNote sets the "note" field. +func (_u *AllowListEntryUpdateOne) SetNote(v string) *AllowListEntryUpdateOne { + _u.mutation.SetNote(v) + return _u +} + +// SetNillableNote sets the "note" field if the given value is not nil. +func (_u *AllowListEntryUpdateOne) SetNillableNote(v *string) *AllowListEntryUpdateOne { + if v != nil { + _u.SetNote(*v) + } + return _u +} + +// SetAddedBy sets the "added_by" field. +func (_u *AllowListEntryUpdateOne) SetAddedBy(v string) *AllowListEntryUpdateOne { + _u.mutation.SetAddedBy(v) + return _u +} + +// SetNillableAddedBy sets the "added_by" field if the given value is not nil. +func (_u *AllowListEntryUpdateOne) SetNillableAddedBy(v *string) *AllowListEntryUpdateOne { + if v != nil { + _u.SetAddedBy(*v) + } + return _u +} + +// SetInviteID sets the "invite_id" field. +func (_u *AllowListEntryUpdateOne) SetInviteID(v string) *AllowListEntryUpdateOne { + _u.mutation.SetInviteID(v) + return _u +} + +// SetNillableInviteID sets the "invite_id" field if the given value is not nil. +func (_u *AllowListEntryUpdateOne) SetNillableInviteID(v *string) *AllowListEntryUpdateOne { + if v != nil { + _u.SetInviteID(*v) + } + return _u +} + +// ClearInviteID clears the value of the "invite_id" field. +func (_u *AllowListEntryUpdateOne) ClearInviteID() *AllowListEntryUpdateOne { + _u.mutation.ClearInviteID() + return _u +} + +// Mutation returns the AllowListEntryMutation object of the builder. +func (_u *AllowListEntryUpdateOne) Mutation() *AllowListEntryMutation { + return _u.mutation +} + +// Where appends a list predicates to the AllowListEntryUpdate builder. +func (_u *AllowListEntryUpdateOne) Where(ps ...predicate.AllowListEntry) *AllowListEntryUpdateOne { + _u.mutation.Where(ps...) + return _u +} + +// Select allows selecting one or more fields (columns) of the returned entity. +// The default is selecting all fields defined in the entity schema. +func (_u *AllowListEntryUpdateOne) Select(field string, fields ...string) *AllowListEntryUpdateOne { + _u.fields = append([]string{field}, fields...) + return _u +} + +// Save executes the query and returns the updated AllowListEntry entity. +func (_u *AllowListEntryUpdateOne) Save(ctx context.Context) (*AllowListEntry, error) { + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *AllowListEntryUpdateOne) SaveX(ctx context.Context) *AllowListEntry { + node, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (_u *AllowListEntryUpdateOne) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *AllowListEntryUpdateOne) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_u *AllowListEntryUpdateOne) check() error { + if v, ok := _u.mutation.Email(); ok { + if err := allowlistentry.EmailValidator(v); err != nil { + return &ValidationError{Name: "email", err: fmt.Errorf(`ent: validator failed for field "AllowListEntry.email": %w`, err)} + } + } + if v, ok := _u.mutation.AddedBy(); ok { + if err := allowlistentry.AddedByValidator(v); err != nil { + return &ValidationError{Name: "added_by", err: fmt.Errorf(`ent: validator failed for field "AllowListEntry.added_by": %w`, err)} + } + } + return nil +} + +func (_u *AllowListEntryUpdateOne) sqlSave(ctx context.Context) (_node *AllowListEntry, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(allowlistentry.Table, allowlistentry.Columns, sqlgraph.NewFieldSpec(allowlistentry.FieldID, field.TypeUUID)) + id, ok := _u.mutation.ID() + if !ok { + return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "AllowListEntry.id" for update`)} + } + _spec.Node.ID.Value = id + if fields := _u.fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, allowlistentry.FieldID) + for _, f := range fields { + if !allowlistentry.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + if f != allowlistentry.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, f) + } + } + } + if ps := _u.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := _u.mutation.Email(); ok { + _spec.SetField(allowlistentry.FieldEmail, field.TypeString, value) + } + if value, ok := _u.mutation.Note(); ok { + _spec.SetField(allowlistentry.FieldNote, field.TypeString, value) + } + if value, ok := _u.mutation.AddedBy(); ok { + _spec.SetField(allowlistentry.FieldAddedBy, field.TypeString, value) + } + if value, ok := _u.mutation.InviteID(); ok { + _spec.SetField(allowlistentry.FieldInviteID, field.TypeString, value) + } + if _u.mutation.InviteIDCleared() { + _spec.ClearField(allowlistentry.FieldInviteID, field.TypeString) + } + _node = &AllowListEntry{config: _u.config} + _spec.Assign = _node.assignValues + _spec.ScanValues = _node.scanValues + if err = sqlgraph.UpdateNode(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{allowlistentry.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + _u.mutation.done = true + return _node, nil +} diff --git a/pkg/ent/apikey.go b/pkg/ent/apikey.go new file mode 100644 index 000000000..df11947b3 --- /dev/null +++ b/pkg/ent/apikey.go @@ -0,0 +1,202 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "fmt" + "strings" + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "github.com/GoogleCloudPlatform/scion/pkg/ent/apikey" + "github.com/google/uuid" +) + +// ApiKey is the model entity for the ApiKey schema. +type ApiKey struct { + config `json:"-"` + // ID of the ent. + ID uuid.UUID `json:"id,omitempty"` + // UserID holds the value of the "user_id" field. + UserID uuid.UUID `json:"user_id,omitempty"` + // Name holds the value of the "name" field. + Name string `json:"name,omitempty"` + // Prefix holds the value of the "prefix" field. + Prefix string `json:"prefix,omitempty"` + // KeyHash holds the value of the "key_hash" field. + KeyHash string `json:"-"` + // Scopes holds the value of the "scopes" field. + Scopes string `json:"scopes,omitempty"` + // Revoked holds the value of the "revoked" field. + Revoked bool `json:"revoked,omitempty"` + // ExpiresAt holds the value of the "expires_at" field. + ExpiresAt *time.Time `json:"expires_at,omitempty"` + // LastUsed holds the value of the "last_used" field. + LastUsed *time.Time `json:"last_used,omitempty"` + // Created holds the value of the "created" field. + Created time.Time `json:"created,omitempty"` + selectValues sql.SelectValues +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*ApiKey) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case apikey.FieldRevoked: + values[i] = new(sql.NullBool) + case apikey.FieldName, apikey.FieldPrefix, apikey.FieldKeyHash, apikey.FieldScopes: + values[i] = new(sql.NullString) + case apikey.FieldExpiresAt, apikey.FieldLastUsed, apikey.FieldCreated: + values[i] = new(sql.NullTime) + case apikey.FieldID, apikey.FieldUserID: + values[i] = new(uuid.UUID) + default: + values[i] = new(sql.UnknownType) + } + } + return values, nil +} + +// assignValues assigns the values that were returned from sql.Rows (after scanning) +// to the ApiKey fields. +func (_m *ApiKey) assignValues(columns []string, values []any) error { + if m, n := len(values), len(columns); m < n { + return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) + } + for i := range columns { + switch columns[i] { + case apikey.FieldID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field id", values[i]) + } else if value != nil { + _m.ID = *value + } + case apikey.FieldUserID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field user_id", values[i]) + } else if value != nil { + _m.UserID = *value + } + case apikey.FieldName: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field name", values[i]) + } else if value.Valid { + _m.Name = value.String + } + case apikey.FieldPrefix: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field prefix", values[i]) + } else if value.Valid { + _m.Prefix = value.String + } + case apikey.FieldKeyHash: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field key_hash", values[i]) + } else if value.Valid { + _m.KeyHash = value.String + } + case apikey.FieldScopes: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field scopes", values[i]) + } else if value.Valid { + _m.Scopes = value.String + } + case apikey.FieldRevoked: + if value, ok := values[i].(*sql.NullBool); !ok { + return fmt.Errorf("unexpected type %T for field revoked", values[i]) + } else if value.Valid { + _m.Revoked = value.Bool + } + case apikey.FieldExpiresAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field expires_at", values[i]) + } else if value.Valid { + _m.ExpiresAt = new(time.Time) + *_m.ExpiresAt = value.Time + } + case apikey.FieldLastUsed: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field last_used", values[i]) + } else if value.Valid { + _m.LastUsed = new(time.Time) + *_m.LastUsed = value.Time + } + case apikey.FieldCreated: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field created", values[i]) + } else if value.Valid { + _m.Created = value.Time + } + default: + _m.selectValues.Set(columns[i], values[i]) + } + } + return nil +} + +// Value returns the ent.Value that was dynamically selected and assigned to the ApiKey. +// This includes values selected through modifiers, order, etc. +func (_m *ApiKey) Value(name string) (ent.Value, error) { + return _m.selectValues.Get(name) +} + +// Update returns a builder for updating this ApiKey. +// Note that you need to call ApiKey.Unwrap() before calling this method if this ApiKey +// was returned from a transaction, and the transaction was committed or rolled back. +func (_m *ApiKey) Update() *ApiKeyUpdateOne { + return NewApiKeyClient(_m.config).UpdateOne(_m) +} + +// Unwrap unwraps the ApiKey entity that was returned from a transaction after it was closed, +// so that all future queries will be executed through the driver which created the transaction. +func (_m *ApiKey) Unwrap() *ApiKey { + _tx, ok := _m.config.driver.(*txDriver) + if !ok { + panic("ent: ApiKey is not a transactional entity") + } + _m.config.driver = _tx.drv + return _m +} + +// String implements the fmt.Stringer. +func (_m *ApiKey) String() string { + var builder strings.Builder + builder.WriteString("ApiKey(") + builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID)) + builder.WriteString("user_id=") + builder.WriteString(fmt.Sprintf("%v", _m.UserID)) + builder.WriteString(", ") + builder.WriteString("name=") + builder.WriteString(_m.Name) + builder.WriteString(", ") + builder.WriteString("prefix=") + builder.WriteString(_m.Prefix) + builder.WriteString(", ") + builder.WriteString("key_hash=") + builder.WriteString(", ") + builder.WriteString("scopes=") + builder.WriteString(_m.Scopes) + builder.WriteString(", ") + builder.WriteString("revoked=") + builder.WriteString(fmt.Sprintf("%v", _m.Revoked)) + builder.WriteString(", ") + if v := _m.ExpiresAt; v != nil { + builder.WriteString("expires_at=") + builder.WriteString(v.Format(time.ANSIC)) + } + builder.WriteString(", ") + if v := _m.LastUsed; v != nil { + builder.WriteString("last_used=") + builder.WriteString(v.Format(time.ANSIC)) + } + builder.WriteString(", ") + builder.WriteString("created=") + builder.WriteString(_m.Created.Format(time.ANSIC)) + builder.WriteByte(')') + return builder.String() +} + +// ApiKeys is a parsable slice of ApiKey. +type ApiKeys []*ApiKey diff --git a/pkg/ent/apikey/apikey.go b/pkg/ent/apikey/apikey.go new file mode 100644 index 000000000..56263a2dd --- /dev/null +++ b/pkg/ent/apikey/apikey.go @@ -0,0 +1,125 @@ +// Code generated by ent, DO NOT EDIT. + +package apikey + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "github.com/google/uuid" +) + +const ( + // Label holds the string label denoting the apikey type in the database. + Label = "api_key" + // FieldID holds the string denoting the id field in the database. + FieldID = "id" + // FieldUserID holds the string denoting the user_id field in the database. + FieldUserID = "user_id" + // FieldName holds the string denoting the name field in the database. + FieldName = "name" + // FieldPrefix holds the string denoting the prefix field in the database. + FieldPrefix = "prefix" + // FieldKeyHash holds the string denoting the key_hash field in the database. + FieldKeyHash = "key_hash" + // FieldScopes holds the string denoting the scopes field in the database. + FieldScopes = "scopes" + // FieldRevoked holds the string denoting the revoked field in the database. + FieldRevoked = "revoked" + // FieldExpiresAt holds the string denoting the expires_at field in the database. + FieldExpiresAt = "expires_at" + // FieldLastUsed holds the string denoting the last_used field in the database. + FieldLastUsed = "last_used" + // FieldCreated holds the string denoting the created field in the database. + FieldCreated = "created" + // Table holds the table name of the apikey in the database. + Table = "api_keys" +) + +// Columns holds all SQL columns for apikey fields. +var Columns = []string{ + FieldID, + FieldUserID, + FieldName, + FieldPrefix, + FieldKeyHash, + FieldScopes, + FieldRevoked, + FieldExpiresAt, + FieldLastUsed, + FieldCreated, +} + +// ValidColumn reports if the column name is valid (part of the table columns). +func ValidColumn(column string) bool { + for i := range Columns { + if column == Columns[i] { + return true + } + } + return false +} + +var ( + // KeyHashValidator is a validator for the "key_hash" field. It is called by the builders before save. + KeyHashValidator func(string) error + // DefaultRevoked holds the default value on creation for the "revoked" field. + DefaultRevoked bool + // DefaultCreated holds the default value on creation for the "created" field. + DefaultCreated func() time.Time + // DefaultID holds the default value on creation for the "id" field. + DefaultID func() uuid.UUID +) + +// OrderOption defines the ordering options for the ApiKey queries. +type OrderOption func(*sql.Selector) + +// ByID orders the results by the id field. +func ByID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldID, opts...).ToFunc() +} + +// ByUserID orders the results by the user_id field. +func ByUserID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldUserID, opts...).ToFunc() +} + +// ByName orders the results by the name field. +func ByName(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldName, opts...).ToFunc() +} + +// ByPrefix orders the results by the prefix field. +func ByPrefix(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldPrefix, opts...).ToFunc() +} + +// ByKeyHash orders the results by the key_hash field. +func ByKeyHash(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldKeyHash, opts...).ToFunc() +} + +// ByScopes orders the results by the scopes field. +func ByScopes(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldScopes, opts...).ToFunc() +} + +// ByRevoked orders the results by the revoked field. +func ByRevoked(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldRevoked, opts...).ToFunc() +} + +// ByExpiresAt orders the results by the expires_at field. +func ByExpiresAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldExpiresAt, opts...).ToFunc() +} + +// ByLastUsed orders the results by the last_used field. +func ByLastUsed(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldLastUsed, opts...).ToFunc() +} + +// ByCreated orders the results by the created field. +func ByCreated(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCreated, opts...).ToFunc() +} diff --git a/pkg/ent/apikey/where.go b/pkg/ent/apikey/where.go new file mode 100644 index 000000000..7683a7cc3 --- /dev/null +++ b/pkg/ent/apikey/where.go @@ -0,0 +1,596 @@ +// Code generated by ent, DO NOT EDIT. + +package apikey + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/google/uuid" +) + +// ID filters vertices based on their ID field. +func ID(id uuid.UUID) predicate.ApiKey { + return predicate.ApiKey(sql.FieldEQ(FieldID, id)) +} + +// IDEQ applies the EQ predicate on the ID field. +func IDEQ(id uuid.UUID) predicate.ApiKey { + return predicate.ApiKey(sql.FieldEQ(FieldID, id)) +} + +// IDNEQ applies the NEQ predicate on the ID field. +func IDNEQ(id uuid.UUID) predicate.ApiKey { + return predicate.ApiKey(sql.FieldNEQ(FieldID, id)) +} + +// IDIn applies the In predicate on the ID field. +func IDIn(ids ...uuid.UUID) predicate.ApiKey { + return predicate.ApiKey(sql.FieldIn(FieldID, ids...)) +} + +// IDNotIn applies the NotIn predicate on the ID field. +func IDNotIn(ids ...uuid.UUID) predicate.ApiKey { + return predicate.ApiKey(sql.FieldNotIn(FieldID, ids...)) +} + +// IDGT applies the GT predicate on the ID field. +func IDGT(id uuid.UUID) predicate.ApiKey { + return predicate.ApiKey(sql.FieldGT(FieldID, id)) +} + +// IDGTE applies the GTE predicate on the ID field. +func IDGTE(id uuid.UUID) predicate.ApiKey { + return predicate.ApiKey(sql.FieldGTE(FieldID, id)) +} + +// IDLT applies the LT predicate on the ID field. +func IDLT(id uuid.UUID) predicate.ApiKey { + return predicate.ApiKey(sql.FieldLT(FieldID, id)) +} + +// IDLTE applies the LTE predicate on the ID field. +func IDLTE(id uuid.UUID) predicate.ApiKey { + return predicate.ApiKey(sql.FieldLTE(FieldID, id)) +} + +// UserID applies equality check predicate on the "user_id" field. It's identical to UserIDEQ. +func UserID(v uuid.UUID) predicate.ApiKey { + return predicate.ApiKey(sql.FieldEQ(FieldUserID, v)) +} + +// Name applies equality check predicate on the "name" field. It's identical to NameEQ. +func Name(v string) predicate.ApiKey { + return predicate.ApiKey(sql.FieldEQ(FieldName, v)) +} + +// Prefix applies equality check predicate on the "prefix" field. It's identical to PrefixEQ. +func Prefix(v string) predicate.ApiKey { + return predicate.ApiKey(sql.FieldEQ(FieldPrefix, v)) +} + +// KeyHash applies equality check predicate on the "key_hash" field. It's identical to KeyHashEQ. +func KeyHash(v string) predicate.ApiKey { + return predicate.ApiKey(sql.FieldEQ(FieldKeyHash, v)) +} + +// Scopes applies equality check predicate on the "scopes" field. It's identical to ScopesEQ. +func Scopes(v string) predicate.ApiKey { + return predicate.ApiKey(sql.FieldEQ(FieldScopes, v)) +} + +// Revoked applies equality check predicate on the "revoked" field. It's identical to RevokedEQ. +func Revoked(v bool) predicate.ApiKey { + return predicate.ApiKey(sql.FieldEQ(FieldRevoked, v)) +} + +// ExpiresAt applies equality check predicate on the "expires_at" field. It's identical to ExpiresAtEQ. +func ExpiresAt(v time.Time) predicate.ApiKey { + return predicate.ApiKey(sql.FieldEQ(FieldExpiresAt, v)) +} + +// LastUsed applies equality check predicate on the "last_used" field. It's identical to LastUsedEQ. +func LastUsed(v time.Time) predicate.ApiKey { + return predicate.ApiKey(sql.FieldEQ(FieldLastUsed, v)) +} + +// Created applies equality check predicate on the "created" field. It's identical to CreatedEQ. +func Created(v time.Time) predicate.ApiKey { + return predicate.ApiKey(sql.FieldEQ(FieldCreated, v)) +} + +// UserIDEQ applies the EQ predicate on the "user_id" field. +func UserIDEQ(v uuid.UUID) predicate.ApiKey { + return predicate.ApiKey(sql.FieldEQ(FieldUserID, v)) +} + +// UserIDNEQ applies the NEQ predicate on the "user_id" field. +func UserIDNEQ(v uuid.UUID) predicate.ApiKey { + return predicate.ApiKey(sql.FieldNEQ(FieldUserID, v)) +} + +// UserIDIn applies the In predicate on the "user_id" field. +func UserIDIn(vs ...uuid.UUID) predicate.ApiKey { + return predicate.ApiKey(sql.FieldIn(FieldUserID, vs...)) +} + +// UserIDNotIn applies the NotIn predicate on the "user_id" field. +func UserIDNotIn(vs ...uuid.UUID) predicate.ApiKey { + return predicate.ApiKey(sql.FieldNotIn(FieldUserID, vs...)) +} + +// UserIDGT applies the GT predicate on the "user_id" field. +func UserIDGT(v uuid.UUID) predicate.ApiKey { + return predicate.ApiKey(sql.FieldGT(FieldUserID, v)) +} + +// UserIDGTE applies the GTE predicate on the "user_id" field. +func UserIDGTE(v uuid.UUID) predicate.ApiKey { + return predicate.ApiKey(sql.FieldGTE(FieldUserID, v)) +} + +// UserIDLT applies the LT predicate on the "user_id" field. +func UserIDLT(v uuid.UUID) predicate.ApiKey { + return predicate.ApiKey(sql.FieldLT(FieldUserID, v)) +} + +// UserIDLTE applies the LTE predicate on the "user_id" field. +func UserIDLTE(v uuid.UUID) predicate.ApiKey { + return predicate.ApiKey(sql.FieldLTE(FieldUserID, v)) +} + +// NameEQ applies the EQ predicate on the "name" field. +func NameEQ(v string) predicate.ApiKey { + return predicate.ApiKey(sql.FieldEQ(FieldName, v)) +} + +// NameNEQ applies the NEQ predicate on the "name" field. +func NameNEQ(v string) predicate.ApiKey { + return predicate.ApiKey(sql.FieldNEQ(FieldName, v)) +} + +// NameIn applies the In predicate on the "name" field. +func NameIn(vs ...string) predicate.ApiKey { + return predicate.ApiKey(sql.FieldIn(FieldName, vs...)) +} + +// NameNotIn applies the NotIn predicate on the "name" field. +func NameNotIn(vs ...string) predicate.ApiKey { + return predicate.ApiKey(sql.FieldNotIn(FieldName, vs...)) +} + +// NameGT applies the GT predicate on the "name" field. +func NameGT(v string) predicate.ApiKey { + return predicate.ApiKey(sql.FieldGT(FieldName, v)) +} + +// NameGTE applies the GTE predicate on the "name" field. +func NameGTE(v string) predicate.ApiKey { + return predicate.ApiKey(sql.FieldGTE(FieldName, v)) +} + +// NameLT applies the LT predicate on the "name" field. +func NameLT(v string) predicate.ApiKey { + return predicate.ApiKey(sql.FieldLT(FieldName, v)) +} + +// NameLTE applies the LTE predicate on the "name" field. +func NameLTE(v string) predicate.ApiKey { + return predicate.ApiKey(sql.FieldLTE(FieldName, v)) +} + +// NameContains applies the Contains predicate on the "name" field. +func NameContains(v string) predicate.ApiKey { + return predicate.ApiKey(sql.FieldContains(FieldName, v)) +} + +// NameHasPrefix applies the HasPrefix predicate on the "name" field. +func NameHasPrefix(v string) predicate.ApiKey { + return predicate.ApiKey(sql.FieldHasPrefix(FieldName, v)) +} + +// NameHasSuffix applies the HasSuffix predicate on the "name" field. +func NameHasSuffix(v string) predicate.ApiKey { + return predicate.ApiKey(sql.FieldHasSuffix(FieldName, v)) +} + +// NameIsNil applies the IsNil predicate on the "name" field. +func NameIsNil() predicate.ApiKey { + return predicate.ApiKey(sql.FieldIsNull(FieldName)) +} + +// NameNotNil applies the NotNil predicate on the "name" field. +func NameNotNil() predicate.ApiKey { + return predicate.ApiKey(sql.FieldNotNull(FieldName)) +} + +// NameEqualFold applies the EqualFold predicate on the "name" field. +func NameEqualFold(v string) predicate.ApiKey { + return predicate.ApiKey(sql.FieldEqualFold(FieldName, v)) +} + +// NameContainsFold applies the ContainsFold predicate on the "name" field. +func NameContainsFold(v string) predicate.ApiKey { + return predicate.ApiKey(sql.FieldContainsFold(FieldName, v)) +} + +// PrefixEQ applies the EQ predicate on the "prefix" field. +func PrefixEQ(v string) predicate.ApiKey { + return predicate.ApiKey(sql.FieldEQ(FieldPrefix, v)) +} + +// PrefixNEQ applies the NEQ predicate on the "prefix" field. +func PrefixNEQ(v string) predicate.ApiKey { + return predicate.ApiKey(sql.FieldNEQ(FieldPrefix, v)) +} + +// PrefixIn applies the In predicate on the "prefix" field. +func PrefixIn(vs ...string) predicate.ApiKey { + return predicate.ApiKey(sql.FieldIn(FieldPrefix, vs...)) +} + +// PrefixNotIn applies the NotIn predicate on the "prefix" field. +func PrefixNotIn(vs ...string) predicate.ApiKey { + return predicate.ApiKey(sql.FieldNotIn(FieldPrefix, vs...)) +} + +// PrefixGT applies the GT predicate on the "prefix" field. +func PrefixGT(v string) predicate.ApiKey { + return predicate.ApiKey(sql.FieldGT(FieldPrefix, v)) +} + +// PrefixGTE applies the GTE predicate on the "prefix" field. +func PrefixGTE(v string) predicate.ApiKey { + return predicate.ApiKey(sql.FieldGTE(FieldPrefix, v)) +} + +// PrefixLT applies the LT predicate on the "prefix" field. +func PrefixLT(v string) predicate.ApiKey { + return predicate.ApiKey(sql.FieldLT(FieldPrefix, v)) +} + +// PrefixLTE applies the LTE predicate on the "prefix" field. +func PrefixLTE(v string) predicate.ApiKey { + return predicate.ApiKey(sql.FieldLTE(FieldPrefix, v)) +} + +// PrefixContains applies the Contains predicate on the "prefix" field. +func PrefixContains(v string) predicate.ApiKey { + return predicate.ApiKey(sql.FieldContains(FieldPrefix, v)) +} + +// PrefixHasPrefix applies the HasPrefix predicate on the "prefix" field. +func PrefixHasPrefix(v string) predicate.ApiKey { + return predicate.ApiKey(sql.FieldHasPrefix(FieldPrefix, v)) +} + +// PrefixHasSuffix applies the HasSuffix predicate on the "prefix" field. +func PrefixHasSuffix(v string) predicate.ApiKey { + return predicate.ApiKey(sql.FieldHasSuffix(FieldPrefix, v)) +} + +// PrefixIsNil applies the IsNil predicate on the "prefix" field. +func PrefixIsNil() predicate.ApiKey { + return predicate.ApiKey(sql.FieldIsNull(FieldPrefix)) +} + +// PrefixNotNil applies the NotNil predicate on the "prefix" field. +func PrefixNotNil() predicate.ApiKey { + return predicate.ApiKey(sql.FieldNotNull(FieldPrefix)) +} + +// PrefixEqualFold applies the EqualFold predicate on the "prefix" field. +func PrefixEqualFold(v string) predicate.ApiKey { + return predicate.ApiKey(sql.FieldEqualFold(FieldPrefix, v)) +} + +// PrefixContainsFold applies the ContainsFold predicate on the "prefix" field. +func PrefixContainsFold(v string) predicate.ApiKey { + return predicate.ApiKey(sql.FieldContainsFold(FieldPrefix, v)) +} + +// KeyHashEQ applies the EQ predicate on the "key_hash" field. +func KeyHashEQ(v string) predicate.ApiKey { + return predicate.ApiKey(sql.FieldEQ(FieldKeyHash, v)) +} + +// KeyHashNEQ applies the NEQ predicate on the "key_hash" field. +func KeyHashNEQ(v string) predicate.ApiKey { + return predicate.ApiKey(sql.FieldNEQ(FieldKeyHash, v)) +} + +// KeyHashIn applies the In predicate on the "key_hash" field. +func KeyHashIn(vs ...string) predicate.ApiKey { + return predicate.ApiKey(sql.FieldIn(FieldKeyHash, vs...)) +} + +// KeyHashNotIn applies the NotIn predicate on the "key_hash" field. +func KeyHashNotIn(vs ...string) predicate.ApiKey { + return predicate.ApiKey(sql.FieldNotIn(FieldKeyHash, vs...)) +} + +// KeyHashGT applies the GT predicate on the "key_hash" field. +func KeyHashGT(v string) predicate.ApiKey { + return predicate.ApiKey(sql.FieldGT(FieldKeyHash, v)) +} + +// KeyHashGTE applies the GTE predicate on the "key_hash" field. +func KeyHashGTE(v string) predicate.ApiKey { + return predicate.ApiKey(sql.FieldGTE(FieldKeyHash, v)) +} + +// KeyHashLT applies the LT predicate on the "key_hash" field. +func KeyHashLT(v string) predicate.ApiKey { + return predicate.ApiKey(sql.FieldLT(FieldKeyHash, v)) +} + +// KeyHashLTE applies the LTE predicate on the "key_hash" field. +func KeyHashLTE(v string) predicate.ApiKey { + return predicate.ApiKey(sql.FieldLTE(FieldKeyHash, v)) +} + +// KeyHashContains applies the Contains predicate on the "key_hash" field. +func KeyHashContains(v string) predicate.ApiKey { + return predicate.ApiKey(sql.FieldContains(FieldKeyHash, v)) +} + +// KeyHashHasPrefix applies the HasPrefix predicate on the "key_hash" field. +func KeyHashHasPrefix(v string) predicate.ApiKey { + return predicate.ApiKey(sql.FieldHasPrefix(FieldKeyHash, v)) +} + +// KeyHashHasSuffix applies the HasSuffix predicate on the "key_hash" field. +func KeyHashHasSuffix(v string) predicate.ApiKey { + return predicate.ApiKey(sql.FieldHasSuffix(FieldKeyHash, v)) +} + +// KeyHashEqualFold applies the EqualFold predicate on the "key_hash" field. +func KeyHashEqualFold(v string) predicate.ApiKey { + return predicate.ApiKey(sql.FieldEqualFold(FieldKeyHash, v)) +} + +// KeyHashContainsFold applies the ContainsFold predicate on the "key_hash" field. +func KeyHashContainsFold(v string) predicate.ApiKey { + return predicate.ApiKey(sql.FieldContainsFold(FieldKeyHash, v)) +} + +// ScopesEQ applies the EQ predicate on the "scopes" field. +func ScopesEQ(v string) predicate.ApiKey { + return predicate.ApiKey(sql.FieldEQ(FieldScopes, v)) +} + +// ScopesNEQ applies the NEQ predicate on the "scopes" field. +func ScopesNEQ(v string) predicate.ApiKey { + return predicate.ApiKey(sql.FieldNEQ(FieldScopes, v)) +} + +// ScopesIn applies the In predicate on the "scopes" field. +func ScopesIn(vs ...string) predicate.ApiKey { + return predicate.ApiKey(sql.FieldIn(FieldScopes, vs...)) +} + +// ScopesNotIn applies the NotIn predicate on the "scopes" field. +func ScopesNotIn(vs ...string) predicate.ApiKey { + return predicate.ApiKey(sql.FieldNotIn(FieldScopes, vs...)) +} + +// ScopesGT applies the GT predicate on the "scopes" field. +func ScopesGT(v string) predicate.ApiKey { + return predicate.ApiKey(sql.FieldGT(FieldScopes, v)) +} + +// ScopesGTE applies the GTE predicate on the "scopes" field. +func ScopesGTE(v string) predicate.ApiKey { + return predicate.ApiKey(sql.FieldGTE(FieldScopes, v)) +} + +// ScopesLT applies the LT predicate on the "scopes" field. +func ScopesLT(v string) predicate.ApiKey { + return predicate.ApiKey(sql.FieldLT(FieldScopes, v)) +} + +// ScopesLTE applies the LTE predicate on the "scopes" field. +func ScopesLTE(v string) predicate.ApiKey { + return predicate.ApiKey(sql.FieldLTE(FieldScopes, v)) +} + +// ScopesContains applies the Contains predicate on the "scopes" field. +func ScopesContains(v string) predicate.ApiKey { + return predicate.ApiKey(sql.FieldContains(FieldScopes, v)) +} + +// ScopesHasPrefix applies the HasPrefix predicate on the "scopes" field. +func ScopesHasPrefix(v string) predicate.ApiKey { + return predicate.ApiKey(sql.FieldHasPrefix(FieldScopes, v)) +} + +// ScopesHasSuffix applies the HasSuffix predicate on the "scopes" field. +func ScopesHasSuffix(v string) predicate.ApiKey { + return predicate.ApiKey(sql.FieldHasSuffix(FieldScopes, v)) +} + +// ScopesIsNil applies the IsNil predicate on the "scopes" field. +func ScopesIsNil() predicate.ApiKey { + return predicate.ApiKey(sql.FieldIsNull(FieldScopes)) +} + +// ScopesNotNil applies the NotNil predicate on the "scopes" field. +func ScopesNotNil() predicate.ApiKey { + return predicate.ApiKey(sql.FieldNotNull(FieldScopes)) +} + +// ScopesEqualFold applies the EqualFold predicate on the "scopes" field. +func ScopesEqualFold(v string) predicate.ApiKey { + return predicate.ApiKey(sql.FieldEqualFold(FieldScopes, v)) +} + +// ScopesContainsFold applies the ContainsFold predicate on the "scopes" field. +func ScopesContainsFold(v string) predicate.ApiKey { + return predicate.ApiKey(sql.FieldContainsFold(FieldScopes, v)) +} + +// RevokedEQ applies the EQ predicate on the "revoked" field. +func RevokedEQ(v bool) predicate.ApiKey { + return predicate.ApiKey(sql.FieldEQ(FieldRevoked, v)) +} + +// RevokedNEQ applies the NEQ predicate on the "revoked" field. +func RevokedNEQ(v bool) predicate.ApiKey { + return predicate.ApiKey(sql.FieldNEQ(FieldRevoked, v)) +} + +// ExpiresAtEQ applies the EQ predicate on the "expires_at" field. +func ExpiresAtEQ(v time.Time) predicate.ApiKey { + return predicate.ApiKey(sql.FieldEQ(FieldExpiresAt, v)) +} + +// ExpiresAtNEQ applies the NEQ predicate on the "expires_at" field. +func ExpiresAtNEQ(v time.Time) predicate.ApiKey { + return predicate.ApiKey(sql.FieldNEQ(FieldExpiresAt, v)) +} + +// ExpiresAtIn applies the In predicate on the "expires_at" field. +func ExpiresAtIn(vs ...time.Time) predicate.ApiKey { + return predicate.ApiKey(sql.FieldIn(FieldExpiresAt, vs...)) +} + +// ExpiresAtNotIn applies the NotIn predicate on the "expires_at" field. +func ExpiresAtNotIn(vs ...time.Time) predicate.ApiKey { + return predicate.ApiKey(sql.FieldNotIn(FieldExpiresAt, vs...)) +} + +// ExpiresAtGT applies the GT predicate on the "expires_at" field. +func ExpiresAtGT(v time.Time) predicate.ApiKey { + return predicate.ApiKey(sql.FieldGT(FieldExpiresAt, v)) +} + +// ExpiresAtGTE applies the GTE predicate on the "expires_at" field. +func ExpiresAtGTE(v time.Time) predicate.ApiKey { + return predicate.ApiKey(sql.FieldGTE(FieldExpiresAt, v)) +} + +// ExpiresAtLT applies the LT predicate on the "expires_at" field. +func ExpiresAtLT(v time.Time) predicate.ApiKey { + return predicate.ApiKey(sql.FieldLT(FieldExpiresAt, v)) +} + +// ExpiresAtLTE applies the LTE predicate on the "expires_at" field. +func ExpiresAtLTE(v time.Time) predicate.ApiKey { + return predicate.ApiKey(sql.FieldLTE(FieldExpiresAt, v)) +} + +// ExpiresAtIsNil applies the IsNil predicate on the "expires_at" field. +func ExpiresAtIsNil() predicate.ApiKey { + return predicate.ApiKey(sql.FieldIsNull(FieldExpiresAt)) +} + +// ExpiresAtNotNil applies the NotNil predicate on the "expires_at" field. +func ExpiresAtNotNil() predicate.ApiKey { + return predicate.ApiKey(sql.FieldNotNull(FieldExpiresAt)) +} + +// LastUsedEQ applies the EQ predicate on the "last_used" field. +func LastUsedEQ(v time.Time) predicate.ApiKey { + return predicate.ApiKey(sql.FieldEQ(FieldLastUsed, v)) +} + +// LastUsedNEQ applies the NEQ predicate on the "last_used" field. +func LastUsedNEQ(v time.Time) predicate.ApiKey { + return predicate.ApiKey(sql.FieldNEQ(FieldLastUsed, v)) +} + +// LastUsedIn applies the In predicate on the "last_used" field. +func LastUsedIn(vs ...time.Time) predicate.ApiKey { + return predicate.ApiKey(sql.FieldIn(FieldLastUsed, vs...)) +} + +// LastUsedNotIn applies the NotIn predicate on the "last_used" field. +func LastUsedNotIn(vs ...time.Time) predicate.ApiKey { + return predicate.ApiKey(sql.FieldNotIn(FieldLastUsed, vs...)) +} + +// LastUsedGT applies the GT predicate on the "last_used" field. +func LastUsedGT(v time.Time) predicate.ApiKey { + return predicate.ApiKey(sql.FieldGT(FieldLastUsed, v)) +} + +// LastUsedGTE applies the GTE predicate on the "last_used" field. +func LastUsedGTE(v time.Time) predicate.ApiKey { + return predicate.ApiKey(sql.FieldGTE(FieldLastUsed, v)) +} + +// LastUsedLT applies the LT predicate on the "last_used" field. +func LastUsedLT(v time.Time) predicate.ApiKey { + return predicate.ApiKey(sql.FieldLT(FieldLastUsed, v)) +} + +// LastUsedLTE applies the LTE predicate on the "last_used" field. +func LastUsedLTE(v time.Time) predicate.ApiKey { + return predicate.ApiKey(sql.FieldLTE(FieldLastUsed, v)) +} + +// LastUsedIsNil applies the IsNil predicate on the "last_used" field. +func LastUsedIsNil() predicate.ApiKey { + return predicate.ApiKey(sql.FieldIsNull(FieldLastUsed)) +} + +// LastUsedNotNil applies the NotNil predicate on the "last_used" field. +func LastUsedNotNil() predicate.ApiKey { + return predicate.ApiKey(sql.FieldNotNull(FieldLastUsed)) +} + +// CreatedEQ applies the EQ predicate on the "created" field. +func CreatedEQ(v time.Time) predicate.ApiKey { + return predicate.ApiKey(sql.FieldEQ(FieldCreated, v)) +} + +// CreatedNEQ applies the NEQ predicate on the "created" field. +func CreatedNEQ(v time.Time) predicate.ApiKey { + return predicate.ApiKey(sql.FieldNEQ(FieldCreated, v)) +} + +// CreatedIn applies the In predicate on the "created" field. +func CreatedIn(vs ...time.Time) predicate.ApiKey { + return predicate.ApiKey(sql.FieldIn(FieldCreated, vs...)) +} + +// CreatedNotIn applies the NotIn predicate on the "created" field. +func CreatedNotIn(vs ...time.Time) predicate.ApiKey { + return predicate.ApiKey(sql.FieldNotIn(FieldCreated, vs...)) +} + +// CreatedGT applies the GT predicate on the "created" field. +func CreatedGT(v time.Time) predicate.ApiKey { + return predicate.ApiKey(sql.FieldGT(FieldCreated, v)) +} + +// CreatedGTE applies the GTE predicate on the "created" field. +func CreatedGTE(v time.Time) predicate.ApiKey { + return predicate.ApiKey(sql.FieldGTE(FieldCreated, v)) +} + +// CreatedLT applies the LT predicate on the "created" field. +func CreatedLT(v time.Time) predicate.ApiKey { + return predicate.ApiKey(sql.FieldLT(FieldCreated, v)) +} + +// CreatedLTE applies the LTE predicate on the "created" field. +func CreatedLTE(v time.Time) predicate.ApiKey { + return predicate.ApiKey(sql.FieldLTE(FieldCreated, v)) +} + +// And groups predicates with the AND operator between them. +func And(predicates ...predicate.ApiKey) predicate.ApiKey { + return predicate.ApiKey(sql.AndPredicates(predicates...)) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.ApiKey) predicate.ApiKey { + return predicate.ApiKey(sql.OrPredicates(predicates...)) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.ApiKey) predicate.ApiKey { + return predicate.ApiKey(sql.NotPredicates(p)) +} diff --git a/pkg/ent/apikey_create.go b/pkg/ent/apikey_create.go new file mode 100644 index 000000000..d1fb42022 --- /dev/null +++ b/pkg/ent/apikey_create.go @@ -0,0 +1,1053 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/apikey" + "github.com/google/uuid" +) + +// ApiKeyCreate is the builder for creating a ApiKey entity. +type ApiKeyCreate struct { + config + mutation *ApiKeyMutation + hooks []Hook + conflict []sql.ConflictOption +} + +// SetUserID sets the "user_id" field. +func (_c *ApiKeyCreate) SetUserID(v uuid.UUID) *ApiKeyCreate { + _c.mutation.SetUserID(v) + return _c +} + +// SetName sets the "name" field. +func (_c *ApiKeyCreate) SetName(v string) *ApiKeyCreate { + _c.mutation.SetName(v) + return _c +} + +// SetNillableName sets the "name" field if the given value is not nil. +func (_c *ApiKeyCreate) SetNillableName(v *string) *ApiKeyCreate { + if v != nil { + _c.SetName(*v) + } + return _c +} + +// SetPrefix sets the "prefix" field. +func (_c *ApiKeyCreate) SetPrefix(v string) *ApiKeyCreate { + _c.mutation.SetPrefix(v) + return _c +} + +// SetNillablePrefix sets the "prefix" field if the given value is not nil. +func (_c *ApiKeyCreate) SetNillablePrefix(v *string) *ApiKeyCreate { + if v != nil { + _c.SetPrefix(*v) + } + return _c +} + +// SetKeyHash sets the "key_hash" field. +func (_c *ApiKeyCreate) SetKeyHash(v string) *ApiKeyCreate { + _c.mutation.SetKeyHash(v) + return _c +} + +// SetScopes sets the "scopes" field. +func (_c *ApiKeyCreate) SetScopes(v string) *ApiKeyCreate { + _c.mutation.SetScopes(v) + return _c +} + +// SetNillableScopes sets the "scopes" field if the given value is not nil. +func (_c *ApiKeyCreate) SetNillableScopes(v *string) *ApiKeyCreate { + if v != nil { + _c.SetScopes(*v) + } + return _c +} + +// SetRevoked sets the "revoked" field. +func (_c *ApiKeyCreate) SetRevoked(v bool) *ApiKeyCreate { + _c.mutation.SetRevoked(v) + return _c +} + +// SetNillableRevoked sets the "revoked" field if the given value is not nil. +func (_c *ApiKeyCreate) SetNillableRevoked(v *bool) *ApiKeyCreate { + if v != nil { + _c.SetRevoked(*v) + } + return _c +} + +// SetExpiresAt sets the "expires_at" field. +func (_c *ApiKeyCreate) SetExpiresAt(v time.Time) *ApiKeyCreate { + _c.mutation.SetExpiresAt(v) + return _c +} + +// SetNillableExpiresAt sets the "expires_at" field if the given value is not nil. +func (_c *ApiKeyCreate) SetNillableExpiresAt(v *time.Time) *ApiKeyCreate { + if v != nil { + _c.SetExpiresAt(*v) + } + return _c +} + +// SetLastUsed sets the "last_used" field. +func (_c *ApiKeyCreate) SetLastUsed(v time.Time) *ApiKeyCreate { + _c.mutation.SetLastUsed(v) + return _c +} + +// SetNillableLastUsed sets the "last_used" field if the given value is not nil. +func (_c *ApiKeyCreate) SetNillableLastUsed(v *time.Time) *ApiKeyCreate { + if v != nil { + _c.SetLastUsed(*v) + } + return _c +} + +// SetCreated sets the "created" field. +func (_c *ApiKeyCreate) SetCreated(v time.Time) *ApiKeyCreate { + _c.mutation.SetCreated(v) + return _c +} + +// SetNillableCreated sets the "created" field if the given value is not nil. +func (_c *ApiKeyCreate) SetNillableCreated(v *time.Time) *ApiKeyCreate { + if v != nil { + _c.SetCreated(*v) + } + return _c +} + +// SetID sets the "id" field. +func (_c *ApiKeyCreate) SetID(v uuid.UUID) *ApiKeyCreate { + _c.mutation.SetID(v) + return _c +} + +// SetNillableID sets the "id" field if the given value is not nil. +func (_c *ApiKeyCreate) SetNillableID(v *uuid.UUID) *ApiKeyCreate { + if v != nil { + _c.SetID(*v) + } + return _c +} + +// Mutation returns the ApiKeyMutation object of the builder. +func (_c *ApiKeyCreate) Mutation() *ApiKeyMutation { + return _c.mutation +} + +// Save creates the ApiKey in the database. +func (_c *ApiKeyCreate) Save(ctx context.Context) (*ApiKey, error) { + _c.defaults() + return withHooks(ctx, _c.sqlSave, _c.mutation, _c.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (_c *ApiKeyCreate) SaveX(ctx context.Context) *ApiKey { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *ApiKeyCreate) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *ApiKeyCreate) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_c *ApiKeyCreate) defaults() { + if _, ok := _c.mutation.Revoked(); !ok { + v := apikey.DefaultRevoked + _c.mutation.SetRevoked(v) + } + if _, ok := _c.mutation.Created(); !ok { + v := apikey.DefaultCreated() + _c.mutation.SetCreated(v) + } + if _, ok := _c.mutation.ID(); !ok { + v := apikey.DefaultID() + _c.mutation.SetID(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_c *ApiKeyCreate) check() error { + if _, ok := _c.mutation.UserID(); !ok { + return &ValidationError{Name: "user_id", err: errors.New(`ent: missing required field "ApiKey.user_id"`)} + } + if _, ok := _c.mutation.KeyHash(); !ok { + return &ValidationError{Name: "key_hash", err: errors.New(`ent: missing required field "ApiKey.key_hash"`)} + } + if v, ok := _c.mutation.KeyHash(); ok { + if err := apikey.KeyHashValidator(v); err != nil { + return &ValidationError{Name: "key_hash", err: fmt.Errorf(`ent: validator failed for field "ApiKey.key_hash": %w`, err)} + } + } + if _, ok := _c.mutation.Revoked(); !ok { + return &ValidationError{Name: "revoked", err: errors.New(`ent: missing required field "ApiKey.revoked"`)} + } + if _, ok := _c.mutation.Created(); !ok { + return &ValidationError{Name: "created", err: errors.New(`ent: missing required field "ApiKey.created"`)} + } + return nil +} + +func (_c *ApiKeyCreate) sqlSave(ctx context.Context) (*ApiKey, error) { + if err := _c.check(); err != nil { + return nil, err + } + _node, _spec := _c.createSpec() + if err := sqlgraph.CreateNode(ctx, _c.driver, _spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + if _spec.ID.Value != nil { + if id, ok := _spec.ID.Value.(*uuid.UUID); ok { + _node.ID = *id + } else if err := _node.ID.Scan(_spec.ID.Value); err != nil { + return nil, err + } + } + _c.mutation.id = &_node.ID + _c.mutation.done = true + return _node, nil +} + +func (_c *ApiKeyCreate) createSpec() (*ApiKey, *sqlgraph.CreateSpec) { + var ( + _node = &ApiKey{config: _c.config} + _spec = sqlgraph.NewCreateSpec(apikey.Table, sqlgraph.NewFieldSpec(apikey.FieldID, field.TypeUUID)) + ) + _spec.OnConflict = _c.conflict + if id, ok := _c.mutation.ID(); ok { + _node.ID = id + _spec.ID.Value = &id + } + if value, ok := _c.mutation.UserID(); ok { + _spec.SetField(apikey.FieldUserID, field.TypeUUID, value) + _node.UserID = value + } + if value, ok := _c.mutation.Name(); ok { + _spec.SetField(apikey.FieldName, field.TypeString, value) + _node.Name = value + } + if value, ok := _c.mutation.Prefix(); ok { + _spec.SetField(apikey.FieldPrefix, field.TypeString, value) + _node.Prefix = value + } + if value, ok := _c.mutation.KeyHash(); ok { + _spec.SetField(apikey.FieldKeyHash, field.TypeString, value) + _node.KeyHash = value + } + if value, ok := _c.mutation.Scopes(); ok { + _spec.SetField(apikey.FieldScopes, field.TypeString, value) + _node.Scopes = value + } + if value, ok := _c.mutation.Revoked(); ok { + _spec.SetField(apikey.FieldRevoked, field.TypeBool, value) + _node.Revoked = value + } + if value, ok := _c.mutation.ExpiresAt(); ok { + _spec.SetField(apikey.FieldExpiresAt, field.TypeTime, value) + _node.ExpiresAt = &value + } + if value, ok := _c.mutation.LastUsed(); ok { + _spec.SetField(apikey.FieldLastUsed, field.TypeTime, value) + _node.LastUsed = &value + } + if value, ok := _c.mutation.Created(); ok { + _spec.SetField(apikey.FieldCreated, field.TypeTime, value) + _node.Created = value + } + return _node, _spec +} + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.ApiKey.Create(). +// SetUserID(v). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.ApiKeyUpsert) { +// SetUserID(v+v). +// }). +// Exec(ctx) +func (_c *ApiKeyCreate) OnConflict(opts ...sql.ConflictOption) *ApiKeyUpsertOne { + _c.conflict = opts + return &ApiKeyUpsertOne{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.ApiKey.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *ApiKeyCreate) OnConflictColumns(columns ...string) *ApiKeyUpsertOne { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &ApiKeyUpsertOne{ + create: _c, + } +} + +type ( + // ApiKeyUpsertOne is the builder for "upsert"-ing + // one ApiKey node. + ApiKeyUpsertOne struct { + create *ApiKeyCreate + } + + // ApiKeyUpsert is the "OnConflict" setter. + ApiKeyUpsert struct { + *sql.UpdateSet + } +) + +// SetUserID sets the "user_id" field. +func (u *ApiKeyUpsert) SetUserID(v uuid.UUID) *ApiKeyUpsert { + u.Set(apikey.FieldUserID, v) + return u +} + +// UpdateUserID sets the "user_id" field to the value that was provided on create. +func (u *ApiKeyUpsert) UpdateUserID() *ApiKeyUpsert { + u.SetExcluded(apikey.FieldUserID) + return u +} + +// SetName sets the "name" field. +func (u *ApiKeyUpsert) SetName(v string) *ApiKeyUpsert { + u.Set(apikey.FieldName, v) + return u +} + +// UpdateName sets the "name" field to the value that was provided on create. +func (u *ApiKeyUpsert) UpdateName() *ApiKeyUpsert { + u.SetExcluded(apikey.FieldName) + return u +} + +// ClearName clears the value of the "name" field. +func (u *ApiKeyUpsert) ClearName() *ApiKeyUpsert { + u.SetNull(apikey.FieldName) + return u +} + +// SetPrefix sets the "prefix" field. +func (u *ApiKeyUpsert) SetPrefix(v string) *ApiKeyUpsert { + u.Set(apikey.FieldPrefix, v) + return u +} + +// UpdatePrefix sets the "prefix" field to the value that was provided on create. +func (u *ApiKeyUpsert) UpdatePrefix() *ApiKeyUpsert { + u.SetExcluded(apikey.FieldPrefix) + return u +} + +// ClearPrefix clears the value of the "prefix" field. +func (u *ApiKeyUpsert) ClearPrefix() *ApiKeyUpsert { + u.SetNull(apikey.FieldPrefix) + return u +} + +// SetKeyHash sets the "key_hash" field. +func (u *ApiKeyUpsert) SetKeyHash(v string) *ApiKeyUpsert { + u.Set(apikey.FieldKeyHash, v) + return u +} + +// UpdateKeyHash sets the "key_hash" field to the value that was provided on create. +func (u *ApiKeyUpsert) UpdateKeyHash() *ApiKeyUpsert { + u.SetExcluded(apikey.FieldKeyHash) + return u +} + +// SetScopes sets the "scopes" field. +func (u *ApiKeyUpsert) SetScopes(v string) *ApiKeyUpsert { + u.Set(apikey.FieldScopes, v) + return u +} + +// UpdateScopes sets the "scopes" field to the value that was provided on create. +func (u *ApiKeyUpsert) UpdateScopes() *ApiKeyUpsert { + u.SetExcluded(apikey.FieldScopes) + return u +} + +// ClearScopes clears the value of the "scopes" field. +func (u *ApiKeyUpsert) ClearScopes() *ApiKeyUpsert { + u.SetNull(apikey.FieldScopes) + return u +} + +// SetRevoked sets the "revoked" field. +func (u *ApiKeyUpsert) SetRevoked(v bool) *ApiKeyUpsert { + u.Set(apikey.FieldRevoked, v) + return u +} + +// UpdateRevoked sets the "revoked" field to the value that was provided on create. +func (u *ApiKeyUpsert) UpdateRevoked() *ApiKeyUpsert { + u.SetExcluded(apikey.FieldRevoked) + return u +} + +// SetExpiresAt sets the "expires_at" field. +func (u *ApiKeyUpsert) SetExpiresAt(v time.Time) *ApiKeyUpsert { + u.Set(apikey.FieldExpiresAt, v) + return u +} + +// UpdateExpiresAt sets the "expires_at" field to the value that was provided on create. +func (u *ApiKeyUpsert) UpdateExpiresAt() *ApiKeyUpsert { + u.SetExcluded(apikey.FieldExpiresAt) + return u +} + +// ClearExpiresAt clears the value of the "expires_at" field. +func (u *ApiKeyUpsert) ClearExpiresAt() *ApiKeyUpsert { + u.SetNull(apikey.FieldExpiresAt) + return u +} + +// SetLastUsed sets the "last_used" field. +func (u *ApiKeyUpsert) SetLastUsed(v time.Time) *ApiKeyUpsert { + u.Set(apikey.FieldLastUsed, v) + return u +} + +// UpdateLastUsed sets the "last_used" field to the value that was provided on create. +func (u *ApiKeyUpsert) UpdateLastUsed() *ApiKeyUpsert { + u.SetExcluded(apikey.FieldLastUsed) + return u +} + +// ClearLastUsed clears the value of the "last_used" field. +func (u *ApiKeyUpsert) ClearLastUsed() *ApiKeyUpsert { + u.SetNull(apikey.FieldLastUsed) + return u +} + +// UpdateNewValues updates the mutable fields using the new values that were set on create except the ID field. +// Using this option is equivalent to using: +// +// client.ApiKey.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(apikey.FieldID) +// }), +// ). +// Exec(ctx) +func (u *ApiKeyUpsertOne) UpdateNewValues() *ApiKeyUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + if _, exists := u.create.mutation.ID(); exists { + s.SetIgnore(apikey.FieldID) + } + if _, exists := u.create.mutation.Created(); exists { + s.SetIgnore(apikey.FieldCreated) + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.ApiKey.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *ApiKeyUpsertOne) Ignore() *ApiKeyUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *ApiKeyUpsertOne) DoNothing() *ApiKeyUpsertOne { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the ApiKeyCreate.OnConflict +// documentation for more info. +func (u *ApiKeyUpsertOne) Update(set func(*ApiKeyUpsert)) *ApiKeyUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&ApiKeyUpsert{UpdateSet: update}) + })) + return u +} + +// SetUserID sets the "user_id" field. +func (u *ApiKeyUpsertOne) SetUserID(v uuid.UUID) *ApiKeyUpsertOne { + return u.Update(func(s *ApiKeyUpsert) { + s.SetUserID(v) + }) +} + +// UpdateUserID sets the "user_id" field to the value that was provided on create. +func (u *ApiKeyUpsertOne) UpdateUserID() *ApiKeyUpsertOne { + return u.Update(func(s *ApiKeyUpsert) { + s.UpdateUserID() + }) +} + +// SetName sets the "name" field. +func (u *ApiKeyUpsertOne) SetName(v string) *ApiKeyUpsertOne { + return u.Update(func(s *ApiKeyUpsert) { + s.SetName(v) + }) +} + +// UpdateName sets the "name" field to the value that was provided on create. +func (u *ApiKeyUpsertOne) UpdateName() *ApiKeyUpsertOne { + return u.Update(func(s *ApiKeyUpsert) { + s.UpdateName() + }) +} + +// ClearName clears the value of the "name" field. +func (u *ApiKeyUpsertOne) ClearName() *ApiKeyUpsertOne { + return u.Update(func(s *ApiKeyUpsert) { + s.ClearName() + }) +} + +// SetPrefix sets the "prefix" field. +func (u *ApiKeyUpsertOne) SetPrefix(v string) *ApiKeyUpsertOne { + return u.Update(func(s *ApiKeyUpsert) { + s.SetPrefix(v) + }) +} + +// UpdatePrefix sets the "prefix" field to the value that was provided on create. +func (u *ApiKeyUpsertOne) UpdatePrefix() *ApiKeyUpsertOne { + return u.Update(func(s *ApiKeyUpsert) { + s.UpdatePrefix() + }) +} + +// ClearPrefix clears the value of the "prefix" field. +func (u *ApiKeyUpsertOne) ClearPrefix() *ApiKeyUpsertOne { + return u.Update(func(s *ApiKeyUpsert) { + s.ClearPrefix() + }) +} + +// SetKeyHash sets the "key_hash" field. +func (u *ApiKeyUpsertOne) SetKeyHash(v string) *ApiKeyUpsertOne { + return u.Update(func(s *ApiKeyUpsert) { + s.SetKeyHash(v) + }) +} + +// UpdateKeyHash sets the "key_hash" field to the value that was provided on create. +func (u *ApiKeyUpsertOne) UpdateKeyHash() *ApiKeyUpsertOne { + return u.Update(func(s *ApiKeyUpsert) { + s.UpdateKeyHash() + }) +} + +// SetScopes sets the "scopes" field. +func (u *ApiKeyUpsertOne) SetScopes(v string) *ApiKeyUpsertOne { + return u.Update(func(s *ApiKeyUpsert) { + s.SetScopes(v) + }) +} + +// UpdateScopes sets the "scopes" field to the value that was provided on create. +func (u *ApiKeyUpsertOne) UpdateScopes() *ApiKeyUpsertOne { + return u.Update(func(s *ApiKeyUpsert) { + s.UpdateScopes() + }) +} + +// ClearScopes clears the value of the "scopes" field. +func (u *ApiKeyUpsertOne) ClearScopes() *ApiKeyUpsertOne { + return u.Update(func(s *ApiKeyUpsert) { + s.ClearScopes() + }) +} + +// SetRevoked sets the "revoked" field. +func (u *ApiKeyUpsertOne) SetRevoked(v bool) *ApiKeyUpsertOne { + return u.Update(func(s *ApiKeyUpsert) { + s.SetRevoked(v) + }) +} + +// UpdateRevoked sets the "revoked" field to the value that was provided on create. +func (u *ApiKeyUpsertOne) UpdateRevoked() *ApiKeyUpsertOne { + return u.Update(func(s *ApiKeyUpsert) { + s.UpdateRevoked() + }) +} + +// SetExpiresAt sets the "expires_at" field. +func (u *ApiKeyUpsertOne) SetExpiresAt(v time.Time) *ApiKeyUpsertOne { + return u.Update(func(s *ApiKeyUpsert) { + s.SetExpiresAt(v) + }) +} + +// UpdateExpiresAt sets the "expires_at" field to the value that was provided on create. +func (u *ApiKeyUpsertOne) UpdateExpiresAt() *ApiKeyUpsertOne { + return u.Update(func(s *ApiKeyUpsert) { + s.UpdateExpiresAt() + }) +} + +// ClearExpiresAt clears the value of the "expires_at" field. +func (u *ApiKeyUpsertOne) ClearExpiresAt() *ApiKeyUpsertOne { + return u.Update(func(s *ApiKeyUpsert) { + s.ClearExpiresAt() + }) +} + +// SetLastUsed sets the "last_used" field. +func (u *ApiKeyUpsertOne) SetLastUsed(v time.Time) *ApiKeyUpsertOne { + return u.Update(func(s *ApiKeyUpsert) { + s.SetLastUsed(v) + }) +} + +// UpdateLastUsed sets the "last_used" field to the value that was provided on create. +func (u *ApiKeyUpsertOne) UpdateLastUsed() *ApiKeyUpsertOne { + return u.Update(func(s *ApiKeyUpsert) { + s.UpdateLastUsed() + }) +} + +// ClearLastUsed clears the value of the "last_used" field. +func (u *ApiKeyUpsertOne) ClearLastUsed() *ApiKeyUpsertOne { + return u.Update(func(s *ApiKeyUpsert) { + s.ClearLastUsed() + }) +} + +// Exec executes the query. +func (u *ApiKeyUpsertOne) Exec(ctx context.Context) error { + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for ApiKeyCreate.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *ApiKeyUpsertOne) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} + +// Exec executes the UPSERT query and returns the inserted/updated ID. +func (u *ApiKeyUpsertOne) ID(ctx context.Context) (id uuid.UUID, err error) { + if u.create.driver.Dialect() == dialect.MySQL { + // In case of "ON CONFLICT", there is no way to get back non-numeric ID + // fields from the database since MySQL does not support the RETURNING clause. + return id, errors.New("ent: ApiKeyUpsertOne.ID is not supported by MySQL driver. Use ApiKeyUpsertOne.Exec instead") + } + node, err := u.create.Save(ctx) + if err != nil { + return id, err + } + return node.ID, nil +} + +// IDX is like ID, but panics if an error occurs. +func (u *ApiKeyUpsertOne) IDX(ctx context.Context) uuid.UUID { + id, err := u.ID(ctx) + if err != nil { + panic(err) + } + return id +} + +// ApiKeyCreateBulk is the builder for creating many ApiKey entities in bulk. +type ApiKeyCreateBulk struct { + config + err error + builders []*ApiKeyCreate + conflict []sql.ConflictOption +} + +// Save creates the ApiKey entities in the database. +func (_c *ApiKeyCreateBulk) Save(ctx context.Context) ([]*ApiKey, error) { + if _c.err != nil { + return nil, _c.err + } + specs := make([]*sqlgraph.CreateSpec, len(_c.builders)) + nodes := make([]*ApiKey, len(_c.builders)) + mutators := make([]Mutator, len(_c.builders)) + for i := range _c.builders { + func(i int, root context.Context) { + builder := _c.builders[i] + builder.defaults() + var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { + mutation, ok := m.(*ApiKeyMutation) + if !ok { + return nil, fmt.Errorf("unexpected mutation type %T", m) + } + if err := builder.check(); err != nil { + return nil, err + } + builder.mutation = mutation + var err error + nodes[i], specs[i] = builder.createSpec() + if i < len(mutators)-1 { + _, err = mutators[i+1].Mutate(root, _c.builders[i+1].mutation) + } else { + spec := &sqlgraph.BatchCreateSpec{Nodes: specs} + spec.OnConflict = _c.conflict + // Invoke the actual operation on the latest mutation in the chain. + if err = sqlgraph.BatchCreate(ctx, _c.driver, spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + } + } + if err != nil { + return nil, err + } + mutation.id = &nodes[i].ID + mutation.done = true + return nodes[i], nil + }) + for i := len(builder.hooks) - 1; i >= 0; i-- { + mut = builder.hooks[i](mut) + } + mutators[i] = mut + }(i, ctx) + } + if len(mutators) > 0 { + if _, err := mutators[0].Mutate(ctx, _c.builders[0].mutation); err != nil { + return nil, err + } + } + return nodes, nil +} + +// SaveX is like Save, but panics if an error occurs. +func (_c *ApiKeyCreateBulk) SaveX(ctx context.Context) []*ApiKey { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *ApiKeyCreateBulk) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *ApiKeyCreateBulk) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.ApiKey.CreateBulk(builders...). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.ApiKeyUpsert) { +// SetUserID(v+v). +// }). +// Exec(ctx) +func (_c *ApiKeyCreateBulk) OnConflict(opts ...sql.ConflictOption) *ApiKeyUpsertBulk { + _c.conflict = opts + return &ApiKeyUpsertBulk{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.ApiKey.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *ApiKeyCreateBulk) OnConflictColumns(columns ...string) *ApiKeyUpsertBulk { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &ApiKeyUpsertBulk{ + create: _c, + } +} + +// ApiKeyUpsertBulk is the builder for "upsert"-ing +// a bulk of ApiKey nodes. +type ApiKeyUpsertBulk struct { + create *ApiKeyCreateBulk +} + +// UpdateNewValues updates the mutable fields using the new values that +// were set on create. Using this option is equivalent to using: +// +// client.ApiKey.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(apikey.FieldID) +// }), +// ). +// Exec(ctx) +func (u *ApiKeyUpsertBulk) UpdateNewValues() *ApiKeyUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + for _, b := range u.create.builders { + if _, exists := b.mutation.ID(); exists { + s.SetIgnore(apikey.FieldID) + } + if _, exists := b.mutation.Created(); exists { + s.SetIgnore(apikey.FieldCreated) + } + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.ApiKey.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *ApiKeyUpsertBulk) Ignore() *ApiKeyUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *ApiKeyUpsertBulk) DoNothing() *ApiKeyUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the ApiKeyCreateBulk.OnConflict +// documentation for more info. +func (u *ApiKeyUpsertBulk) Update(set func(*ApiKeyUpsert)) *ApiKeyUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&ApiKeyUpsert{UpdateSet: update}) + })) + return u +} + +// SetUserID sets the "user_id" field. +func (u *ApiKeyUpsertBulk) SetUserID(v uuid.UUID) *ApiKeyUpsertBulk { + return u.Update(func(s *ApiKeyUpsert) { + s.SetUserID(v) + }) +} + +// UpdateUserID sets the "user_id" field to the value that was provided on create. +func (u *ApiKeyUpsertBulk) UpdateUserID() *ApiKeyUpsertBulk { + return u.Update(func(s *ApiKeyUpsert) { + s.UpdateUserID() + }) +} + +// SetName sets the "name" field. +func (u *ApiKeyUpsertBulk) SetName(v string) *ApiKeyUpsertBulk { + return u.Update(func(s *ApiKeyUpsert) { + s.SetName(v) + }) +} + +// UpdateName sets the "name" field to the value that was provided on create. +func (u *ApiKeyUpsertBulk) UpdateName() *ApiKeyUpsertBulk { + return u.Update(func(s *ApiKeyUpsert) { + s.UpdateName() + }) +} + +// ClearName clears the value of the "name" field. +func (u *ApiKeyUpsertBulk) ClearName() *ApiKeyUpsertBulk { + return u.Update(func(s *ApiKeyUpsert) { + s.ClearName() + }) +} + +// SetPrefix sets the "prefix" field. +func (u *ApiKeyUpsertBulk) SetPrefix(v string) *ApiKeyUpsertBulk { + return u.Update(func(s *ApiKeyUpsert) { + s.SetPrefix(v) + }) +} + +// UpdatePrefix sets the "prefix" field to the value that was provided on create. +func (u *ApiKeyUpsertBulk) UpdatePrefix() *ApiKeyUpsertBulk { + return u.Update(func(s *ApiKeyUpsert) { + s.UpdatePrefix() + }) +} + +// ClearPrefix clears the value of the "prefix" field. +func (u *ApiKeyUpsertBulk) ClearPrefix() *ApiKeyUpsertBulk { + return u.Update(func(s *ApiKeyUpsert) { + s.ClearPrefix() + }) +} + +// SetKeyHash sets the "key_hash" field. +func (u *ApiKeyUpsertBulk) SetKeyHash(v string) *ApiKeyUpsertBulk { + return u.Update(func(s *ApiKeyUpsert) { + s.SetKeyHash(v) + }) +} + +// UpdateKeyHash sets the "key_hash" field to the value that was provided on create. +func (u *ApiKeyUpsertBulk) UpdateKeyHash() *ApiKeyUpsertBulk { + return u.Update(func(s *ApiKeyUpsert) { + s.UpdateKeyHash() + }) +} + +// SetScopes sets the "scopes" field. +func (u *ApiKeyUpsertBulk) SetScopes(v string) *ApiKeyUpsertBulk { + return u.Update(func(s *ApiKeyUpsert) { + s.SetScopes(v) + }) +} + +// UpdateScopes sets the "scopes" field to the value that was provided on create. +func (u *ApiKeyUpsertBulk) UpdateScopes() *ApiKeyUpsertBulk { + return u.Update(func(s *ApiKeyUpsert) { + s.UpdateScopes() + }) +} + +// ClearScopes clears the value of the "scopes" field. +func (u *ApiKeyUpsertBulk) ClearScopes() *ApiKeyUpsertBulk { + return u.Update(func(s *ApiKeyUpsert) { + s.ClearScopes() + }) +} + +// SetRevoked sets the "revoked" field. +func (u *ApiKeyUpsertBulk) SetRevoked(v bool) *ApiKeyUpsertBulk { + return u.Update(func(s *ApiKeyUpsert) { + s.SetRevoked(v) + }) +} + +// UpdateRevoked sets the "revoked" field to the value that was provided on create. +func (u *ApiKeyUpsertBulk) UpdateRevoked() *ApiKeyUpsertBulk { + return u.Update(func(s *ApiKeyUpsert) { + s.UpdateRevoked() + }) +} + +// SetExpiresAt sets the "expires_at" field. +func (u *ApiKeyUpsertBulk) SetExpiresAt(v time.Time) *ApiKeyUpsertBulk { + return u.Update(func(s *ApiKeyUpsert) { + s.SetExpiresAt(v) + }) +} + +// UpdateExpiresAt sets the "expires_at" field to the value that was provided on create. +func (u *ApiKeyUpsertBulk) UpdateExpiresAt() *ApiKeyUpsertBulk { + return u.Update(func(s *ApiKeyUpsert) { + s.UpdateExpiresAt() + }) +} + +// ClearExpiresAt clears the value of the "expires_at" field. +func (u *ApiKeyUpsertBulk) ClearExpiresAt() *ApiKeyUpsertBulk { + return u.Update(func(s *ApiKeyUpsert) { + s.ClearExpiresAt() + }) +} + +// SetLastUsed sets the "last_used" field. +func (u *ApiKeyUpsertBulk) SetLastUsed(v time.Time) *ApiKeyUpsertBulk { + return u.Update(func(s *ApiKeyUpsert) { + s.SetLastUsed(v) + }) +} + +// UpdateLastUsed sets the "last_used" field to the value that was provided on create. +func (u *ApiKeyUpsertBulk) UpdateLastUsed() *ApiKeyUpsertBulk { + return u.Update(func(s *ApiKeyUpsert) { + s.UpdateLastUsed() + }) +} + +// ClearLastUsed clears the value of the "last_used" field. +func (u *ApiKeyUpsertBulk) ClearLastUsed() *ApiKeyUpsertBulk { + return u.Update(func(s *ApiKeyUpsert) { + s.ClearLastUsed() + }) +} + +// Exec executes the query. +func (u *ApiKeyUpsertBulk) Exec(ctx context.Context) error { + if u.create.err != nil { + return u.create.err + } + for i, b := range u.create.builders { + if len(b.conflict) != 0 { + return fmt.Errorf("ent: OnConflict was set for builder %d. Set it on the ApiKeyCreateBulk instead", i) + } + } + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for ApiKeyCreateBulk.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *ApiKeyUpsertBulk) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/pkg/ent/apikey_delete.go b/pkg/ent/apikey_delete.go new file mode 100644 index 000000000..78861601c --- /dev/null +++ b/pkg/ent/apikey_delete.go @@ -0,0 +1,88 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/apikey" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" +) + +// ApiKeyDelete is the builder for deleting a ApiKey entity. +type ApiKeyDelete struct { + config + hooks []Hook + mutation *ApiKeyMutation +} + +// Where appends a list predicates to the ApiKeyDelete builder. +func (_d *ApiKeyDelete) Where(ps ...predicate.ApiKey) *ApiKeyDelete { + _d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (_d *ApiKeyDelete) Exec(ctx context.Context) (int, error) { + return withHooks(ctx, _d.sqlExec, _d.mutation, _d.hooks) +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *ApiKeyDelete) ExecX(ctx context.Context) int { + n, err := _d.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (_d *ApiKeyDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(apikey.Table, sqlgraph.NewFieldSpec(apikey.FieldID, field.TypeUUID)) + if ps := _d.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + affected, err := sqlgraph.DeleteNodes(ctx, _d.driver, _spec) + if err != nil && sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + _d.mutation.done = true + return affected, err +} + +// ApiKeyDeleteOne is the builder for deleting a single ApiKey entity. +type ApiKeyDeleteOne struct { + _d *ApiKeyDelete +} + +// Where appends a list predicates to the ApiKeyDelete builder. +func (_d *ApiKeyDeleteOne) Where(ps ...predicate.ApiKey) *ApiKeyDeleteOne { + _d._d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query. +func (_d *ApiKeyDeleteOne) Exec(ctx context.Context) error { + n, err := _d._d.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{apikey.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *ApiKeyDeleteOne) ExecX(ctx context.Context) { + if err := _d.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/pkg/ent/apikey_query.go b/pkg/ent/apikey_query.go new file mode 100644 index 000000000..638c0d237 --- /dev/null +++ b/pkg/ent/apikey_query.go @@ -0,0 +1,565 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "fmt" + "math" + + "entgo.io/ent" + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/apikey" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/google/uuid" +) + +// ApiKeyQuery is the builder for querying ApiKey entities. +type ApiKeyQuery struct { + config + ctx *QueryContext + order []apikey.OrderOption + inters []Interceptor + predicates []predicate.ApiKey + modifiers []func(*sql.Selector) + // intermediate query (i.e. traversal path). + sql *sql.Selector + path func(context.Context) (*sql.Selector, error) +} + +// Where adds a new predicate for the ApiKeyQuery builder. +func (_q *ApiKeyQuery) Where(ps ...predicate.ApiKey) *ApiKeyQuery { + _q.predicates = append(_q.predicates, ps...) + return _q +} + +// Limit the number of records to be returned by this query. +func (_q *ApiKeyQuery) Limit(limit int) *ApiKeyQuery { + _q.ctx.Limit = &limit + return _q +} + +// Offset to start from. +func (_q *ApiKeyQuery) Offset(offset int) *ApiKeyQuery { + _q.ctx.Offset = &offset + return _q +} + +// Unique configures the query builder to filter duplicate records on query. +// By default, unique is set to true, and can be disabled using this method. +func (_q *ApiKeyQuery) Unique(unique bool) *ApiKeyQuery { + _q.ctx.Unique = &unique + return _q +} + +// Order specifies how the records should be ordered. +func (_q *ApiKeyQuery) Order(o ...apikey.OrderOption) *ApiKeyQuery { + _q.order = append(_q.order, o...) + return _q +} + +// First returns the first ApiKey entity from the query. +// Returns a *NotFoundError when no ApiKey was found. +func (_q *ApiKeyQuery) First(ctx context.Context) (*ApiKey, error) { + nodes, err := _q.Limit(1).All(setContextOp(ctx, _q.ctx, ent.OpQueryFirst)) + if err != nil { + return nil, err + } + if len(nodes) == 0 { + return nil, &NotFoundError{apikey.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (_q *ApiKeyQuery) FirstX(ctx context.Context) *ApiKey { + node, err := _q.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// FirstID returns the first ApiKey ID from the query. +// Returns a *NotFoundError when no ApiKey ID was found. +func (_q *ApiKeyQuery) FirstID(ctx context.Context) (id uuid.UUID, err error) { + var ids []uuid.UUID + if ids, err = _q.Limit(1).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryFirstID)); err != nil { + return + } + if len(ids) == 0 { + err = &NotFoundError{apikey.Label} + return + } + return ids[0], nil +} + +// FirstIDX is like FirstID, but panics if an error occurs. +func (_q *ApiKeyQuery) FirstIDX(ctx context.Context) uuid.UUID { + id, err := _q.FirstID(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return id +} + +// Only returns a single ApiKey entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one ApiKey entity is found. +// Returns a *NotFoundError when no ApiKey entities are found. +func (_q *ApiKeyQuery) Only(ctx context.Context) (*ApiKey, error) { + nodes, err := _q.Limit(2).All(setContextOp(ctx, _q.ctx, ent.OpQueryOnly)) + if err != nil { + return nil, err + } + switch len(nodes) { + case 1: + return nodes[0], nil + case 0: + return nil, &NotFoundError{apikey.Label} + default: + return nil, &NotSingularError{apikey.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (_q *ApiKeyQuery) OnlyX(ctx context.Context) *ApiKey { + node, err := _q.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// OnlyID is like Only, but returns the only ApiKey ID in the query. +// Returns a *NotSingularError when more than one ApiKey ID is found. +// Returns a *NotFoundError when no entities are found. +func (_q *ApiKeyQuery) OnlyID(ctx context.Context) (id uuid.UUID, err error) { + var ids []uuid.UUID + if ids, err = _q.Limit(2).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryOnlyID)); err != nil { + return + } + switch len(ids) { + case 1: + id = ids[0] + case 0: + err = &NotFoundError{apikey.Label} + default: + err = &NotSingularError{apikey.Label} + } + return +} + +// OnlyIDX is like OnlyID, but panics if an error occurs. +func (_q *ApiKeyQuery) OnlyIDX(ctx context.Context) uuid.UUID { + id, err := _q.OnlyID(ctx) + if err != nil { + panic(err) + } + return id +} + +// All executes the query and returns a list of ApiKeys. +func (_q *ApiKeyQuery) All(ctx context.Context) ([]*ApiKey, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryAll) + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*ApiKey, *ApiKeyQuery]() + return withInterceptors[[]*ApiKey](ctx, _q, qr, _q.inters) +} + +// AllX is like All, but panics if an error occurs. +func (_q *ApiKeyQuery) AllX(ctx context.Context) []*ApiKey { + nodes, err := _q.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// IDs executes the query and returns a list of ApiKey IDs. +func (_q *ApiKeyQuery) IDs(ctx context.Context) (ids []uuid.UUID, err error) { + if _q.ctx.Unique == nil && _q.path != nil { + _q.Unique(true) + } + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryIDs) + if err = _q.Select(apikey.FieldID).Scan(ctx, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// IDsX is like IDs, but panics if an error occurs. +func (_q *ApiKeyQuery) IDsX(ctx context.Context) []uuid.UUID { + ids, err := _q.IDs(ctx) + if err != nil { + panic(err) + } + return ids +} + +// Count returns the count of the given query. +func (_q *ApiKeyQuery) Count(ctx context.Context) (int, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryCount) + if err := _q.prepareQuery(ctx); err != nil { + return 0, err + } + return withInterceptors[int](ctx, _q, querierCount[*ApiKeyQuery](), _q.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (_q *ApiKeyQuery) CountX(ctx context.Context) int { + count, err := _q.Count(ctx) + if err != nil { + panic(err) + } + return count +} + +// Exist returns true if the query has elements in the graph. +func (_q *ApiKeyQuery) Exist(ctx context.Context) (bool, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryExist) + switch _, err := _q.FirstID(ctx); { + case IsNotFound(err): + return false, nil + case err != nil: + return false, fmt.Errorf("ent: check existence: %w", err) + default: + return true, nil + } +} + +// ExistX is like Exist, but panics if an error occurs. +func (_q *ApiKeyQuery) ExistX(ctx context.Context) bool { + exist, err := _q.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the ApiKeyQuery builder, including all associated steps. It can be +// used to prepare common query builders and use them differently after the clone is made. +func (_q *ApiKeyQuery) Clone() *ApiKeyQuery { + if _q == nil { + return nil + } + return &ApiKeyQuery{ + config: _q.config, + ctx: _q.ctx.Clone(), + order: append([]apikey.OrderOption{}, _q.order...), + inters: append([]Interceptor{}, _q.inters...), + predicates: append([]predicate.ApiKey{}, _q.predicates...), + // clone intermediate query. + sql: _q.sql.Clone(), + path: _q.path, + } +} + +// GroupBy is used to group vertices by one or more fields/columns. +// It is often used with aggregate functions, like: count, max, mean, min, sum. +// +// Example: +// +// var v []struct { +// UserID uuid.UUID `json:"user_id,omitempty"` +// Count int `json:"count,omitempty"` +// } +// +// client.ApiKey.Query(). +// GroupBy(apikey.FieldUserID). +// Aggregate(ent.Count()). +// Scan(ctx, &v) +func (_q *ApiKeyQuery) GroupBy(field string, fields ...string) *ApiKeyGroupBy { + _q.ctx.Fields = append([]string{field}, fields...) + grbuild := &ApiKeyGroupBy{build: _q} + grbuild.flds = &_q.ctx.Fields + grbuild.label = apikey.Label + grbuild.scan = grbuild.Scan + return grbuild +} + +// Select allows the selection one or more fields/columns for the given query, +// instead of selecting all fields in the entity. +// +// Example: +// +// var v []struct { +// UserID uuid.UUID `json:"user_id,omitempty"` +// } +// +// client.ApiKey.Query(). +// Select(apikey.FieldUserID). +// Scan(ctx, &v) +func (_q *ApiKeyQuery) Select(fields ...string) *ApiKeySelect { + _q.ctx.Fields = append(_q.ctx.Fields, fields...) + sbuild := &ApiKeySelect{ApiKeyQuery: _q} + sbuild.label = apikey.Label + sbuild.flds, sbuild.scan = &_q.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a ApiKeySelect configured with the given aggregations. +func (_q *ApiKeyQuery) Aggregate(fns ...AggregateFunc) *ApiKeySelect { + return _q.Select().Aggregate(fns...) +} + +func (_q *ApiKeyQuery) prepareQuery(ctx context.Context) error { + for _, inter := range _q.inters { + if inter == nil { + return fmt.Errorf("ent: uninitialized interceptor (forgotten import ent/runtime?)") + } + if trv, ok := inter.(Traverser); ok { + if err := trv.Traverse(ctx, _q); err != nil { + return err + } + } + } + for _, f := range _q.ctx.Fields { + if !apikey.ValidColumn(f) { + return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + } + if _q.path != nil { + prev, err := _q.path(ctx) + if err != nil { + return err + } + _q.sql = prev + } + return nil +} + +func (_q *ApiKeyQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*ApiKey, error) { + var ( + nodes = []*ApiKey{} + _spec = _q.querySpec() + ) + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*ApiKey).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &ApiKey{config: _q.config} + nodes = append(nodes, node) + return node.assignValues(columns, values) + } + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } + for i := range hooks { + hooks[i](ctx, _spec) + } + if err := sqlgraph.QueryNodes(ctx, _q.driver, _spec); err != nil { + return nil, err + } + if len(nodes) == 0 { + return nodes, nil + } + return nodes, nil +} + +func (_q *ApiKeyQuery) sqlCount(ctx context.Context) (int, error) { + _spec := _q.querySpec() + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } + _spec.Node.Columns = _q.ctx.Fields + if len(_q.ctx.Fields) > 0 { + _spec.Unique = _q.ctx.Unique != nil && *_q.ctx.Unique + } + return sqlgraph.CountNodes(ctx, _q.driver, _spec) +} + +func (_q *ApiKeyQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(apikey.Table, apikey.Columns, sqlgraph.NewFieldSpec(apikey.FieldID, field.TypeUUID)) + _spec.From = _q.sql + if unique := _q.ctx.Unique; unique != nil { + _spec.Unique = *unique + } else if _q.path != nil { + _spec.Unique = true + } + if fields := _q.ctx.Fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, apikey.FieldID) + for i := range fields { + if fields[i] != apikey.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, fields[i]) + } + } + } + if ps := _q.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if limit := _q.ctx.Limit; limit != nil { + _spec.Limit = *limit + } + if offset := _q.ctx.Offset; offset != nil { + _spec.Offset = *offset + } + if ps := _q.order; len(ps) > 0 { + _spec.Order = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + return _spec +} + +func (_q *ApiKeyQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(_q.driver.Dialect()) + t1 := builder.Table(apikey.Table) + columns := _q.ctx.Fields + if len(columns) == 0 { + columns = apikey.Columns + } + selector := builder.Select(t1.Columns(columns...)...).From(t1) + if _q.sql != nil { + selector = _q.sql + selector.Select(selector.Columns(columns...)...) + } + if _q.ctx.Unique != nil && *_q.ctx.Unique { + selector.Distinct() + } + for _, m := range _q.modifiers { + m(selector) + } + for _, p := range _q.predicates { + p(selector) + } + for _, p := range _q.order { + p(selector) + } + if offset := _q.ctx.Offset; offset != nil { + // limit is mandatory for offset clause. We start + // with default value, and override it below if needed. + selector.Offset(*offset).Limit(math.MaxInt32) + } + if limit := _q.ctx.Limit; limit != nil { + selector.Limit(*limit) + } + return selector +} + +// ForUpdate locks the selected rows against concurrent updates, and prevent them from being +// updated, deleted or "selected ... for update" by other sessions, until the transaction is +// either committed or rolled-back. +func (_q *ApiKeyQuery) ForUpdate(opts ...sql.LockOption) *ApiKeyQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForUpdate(opts...) + }) + return _q +} + +// ForShare behaves similarly to ForUpdate, except that it acquires a shared mode lock +// on any rows that are read. Other sessions can read the rows, but cannot modify them +// until your transaction commits. +func (_q *ApiKeyQuery) ForShare(opts ...sql.LockOption) *ApiKeyQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForShare(opts...) + }) + return _q +} + +// ApiKeyGroupBy is the group-by builder for ApiKey entities. +type ApiKeyGroupBy struct { + selector + build *ApiKeyQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (_g *ApiKeyGroupBy) Aggregate(fns ...AggregateFunc) *ApiKeyGroupBy { + _g.fns = append(_g.fns, fns...) + return _g +} + +// Scan applies the selector query and scans the result into the given value. +func (_g *ApiKeyGroupBy) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, _g.build.ctx, ent.OpQueryGroupBy) + if err := _g.build.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*ApiKeyQuery, *ApiKeyGroupBy](ctx, _g.build, _g, _g.build.inters, v) +} + +func (_g *ApiKeyGroupBy) sqlScan(ctx context.Context, root *ApiKeyQuery, v any) error { + selector := root.sqlQuery(ctx).Select() + aggregation := make([]string, 0, len(_g.fns)) + for _, fn := range _g.fns { + aggregation = append(aggregation, fn(selector)) + } + if len(selector.SelectedColumns()) == 0 { + columns := make([]string, 0, len(*_g.flds)+len(_g.fns)) + for _, f := range *_g.flds { + columns = append(columns, selector.C(f)) + } + columns = append(columns, aggregation...) + selector.Select(columns...) + } + selector.GroupBy(selector.Columns(*_g.flds...)...) + if err := selector.Err(); err != nil { + return err + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := _g.build.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} + +// ApiKeySelect is the builder for selecting fields of ApiKey entities. +type ApiKeySelect struct { + *ApiKeyQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (_s *ApiKeySelect) Aggregate(fns ...AggregateFunc) *ApiKeySelect { + _s.fns = append(_s.fns, fns...) + return _s +} + +// Scan applies the selector query and scans the result into the given value. +func (_s *ApiKeySelect) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, _s.ctx, ent.OpQuerySelect) + if err := _s.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*ApiKeyQuery, *ApiKeySelect](ctx, _s.ApiKeyQuery, _s, _s.inters, v) +} + +func (_s *ApiKeySelect) sqlScan(ctx context.Context, root *ApiKeyQuery, v any) error { + selector := root.sqlQuery(ctx) + aggregation := make([]string, 0, len(_s.fns)) + for _, fn := range _s.fns { + aggregation = append(aggregation, fn(selector)) + } + switch n := len(*_s.selector.flds); { + case n == 0 && len(aggregation) > 0: + selector.Select(aggregation...) + case n != 0 && len(aggregation) > 0: + selector.AppendSelect(aggregation...) + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := _s.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} diff --git a/pkg/ent/apikey_update.go b/pkg/ent/apikey_update.go new file mode 100644 index 000000000..549f99201 --- /dev/null +++ b/pkg/ent/apikey_update.go @@ -0,0 +1,565 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/apikey" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/google/uuid" +) + +// ApiKeyUpdate is the builder for updating ApiKey entities. +type ApiKeyUpdate struct { + config + hooks []Hook + mutation *ApiKeyMutation +} + +// Where appends a list predicates to the ApiKeyUpdate builder. +func (_u *ApiKeyUpdate) Where(ps ...predicate.ApiKey) *ApiKeyUpdate { + _u.mutation.Where(ps...) + return _u +} + +// SetUserID sets the "user_id" field. +func (_u *ApiKeyUpdate) SetUserID(v uuid.UUID) *ApiKeyUpdate { + _u.mutation.SetUserID(v) + return _u +} + +// SetNillableUserID sets the "user_id" field if the given value is not nil. +func (_u *ApiKeyUpdate) SetNillableUserID(v *uuid.UUID) *ApiKeyUpdate { + if v != nil { + _u.SetUserID(*v) + } + return _u +} + +// SetName sets the "name" field. +func (_u *ApiKeyUpdate) SetName(v string) *ApiKeyUpdate { + _u.mutation.SetName(v) + return _u +} + +// SetNillableName sets the "name" field if the given value is not nil. +func (_u *ApiKeyUpdate) SetNillableName(v *string) *ApiKeyUpdate { + if v != nil { + _u.SetName(*v) + } + return _u +} + +// ClearName clears the value of the "name" field. +func (_u *ApiKeyUpdate) ClearName() *ApiKeyUpdate { + _u.mutation.ClearName() + return _u +} + +// SetPrefix sets the "prefix" field. +func (_u *ApiKeyUpdate) SetPrefix(v string) *ApiKeyUpdate { + _u.mutation.SetPrefix(v) + return _u +} + +// SetNillablePrefix sets the "prefix" field if the given value is not nil. +func (_u *ApiKeyUpdate) SetNillablePrefix(v *string) *ApiKeyUpdate { + if v != nil { + _u.SetPrefix(*v) + } + return _u +} + +// ClearPrefix clears the value of the "prefix" field. +func (_u *ApiKeyUpdate) ClearPrefix() *ApiKeyUpdate { + _u.mutation.ClearPrefix() + return _u +} + +// SetKeyHash sets the "key_hash" field. +func (_u *ApiKeyUpdate) SetKeyHash(v string) *ApiKeyUpdate { + _u.mutation.SetKeyHash(v) + return _u +} + +// SetNillableKeyHash sets the "key_hash" field if the given value is not nil. +func (_u *ApiKeyUpdate) SetNillableKeyHash(v *string) *ApiKeyUpdate { + if v != nil { + _u.SetKeyHash(*v) + } + return _u +} + +// SetScopes sets the "scopes" field. +func (_u *ApiKeyUpdate) SetScopes(v string) *ApiKeyUpdate { + _u.mutation.SetScopes(v) + return _u +} + +// SetNillableScopes sets the "scopes" field if the given value is not nil. +func (_u *ApiKeyUpdate) SetNillableScopes(v *string) *ApiKeyUpdate { + if v != nil { + _u.SetScopes(*v) + } + return _u +} + +// ClearScopes clears the value of the "scopes" field. +func (_u *ApiKeyUpdate) ClearScopes() *ApiKeyUpdate { + _u.mutation.ClearScopes() + return _u +} + +// SetRevoked sets the "revoked" field. +func (_u *ApiKeyUpdate) SetRevoked(v bool) *ApiKeyUpdate { + _u.mutation.SetRevoked(v) + return _u +} + +// SetNillableRevoked sets the "revoked" field if the given value is not nil. +func (_u *ApiKeyUpdate) SetNillableRevoked(v *bool) *ApiKeyUpdate { + if v != nil { + _u.SetRevoked(*v) + } + return _u +} + +// SetExpiresAt sets the "expires_at" field. +func (_u *ApiKeyUpdate) SetExpiresAt(v time.Time) *ApiKeyUpdate { + _u.mutation.SetExpiresAt(v) + return _u +} + +// SetNillableExpiresAt sets the "expires_at" field if the given value is not nil. +func (_u *ApiKeyUpdate) SetNillableExpiresAt(v *time.Time) *ApiKeyUpdate { + if v != nil { + _u.SetExpiresAt(*v) + } + return _u +} + +// ClearExpiresAt clears the value of the "expires_at" field. +func (_u *ApiKeyUpdate) ClearExpiresAt() *ApiKeyUpdate { + _u.mutation.ClearExpiresAt() + return _u +} + +// SetLastUsed sets the "last_used" field. +func (_u *ApiKeyUpdate) SetLastUsed(v time.Time) *ApiKeyUpdate { + _u.mutation.SetLastUsed(v) + return _u +} + +// SetNillableLastUsed sets the "last_used" field if the given value is not nil. +func (_u *ApiKeyUpdate) SetNillableLastUsed(v *time.Time) *ApiKeyUpdate { + if v != nil { + _u.SetLastUsed(*v) + } + return _u +} + +// ClearLastUsed clears the value of the "last_used" field. +func (_u *ApiKeyUpdate) ClearLastUsed() *ApiKeyUpdate { + _u.mutation.ClearLastUsed() + return _u +} + +// Mutation returns the ApiKeyMutation object of the builder. +func (_u *ApiKeyUpdate) Mutation() *ApiKeyMutation { + return _u.mutation +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (_u *ApiKeyUpdate) Save(ctx context.Context) (int, error) { + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *ApiKeyUpdate) SaveX(ctx context.Context) int { + affected, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (_u *ApiKeyUpdate) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *ApiKeyUpdate) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_u *ApiKeyUpdate) check() error { + if v, ok := _u.mutation.KeyHash(); ok { + if err := apikey.KeyHashValidator(v); err != nil { + return &ValidationError{Name: "key_hash", err: fmt.Errorf(`ent: validator failed for field "ApiKey.key_hash": %w`, err)} + } + } + return nil +} + +func (_u *ApiKeyUpdate) sqlSave(ctx context.Context) (_node int, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(apikey.Table, apikey.Columns, sqlgraph.NewFieldSpec(apikey.FieldID, field.TypeUUID)) + if ps := _u.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := _u.mutation.UserID(); ok { + _spec.SetField(apikey.FieldUserID, field.TypeUUID, value) + } + if value, ok := _u.mutation.Name(); ok { + _spec.SetField(apikey.FieldName, field.TypeString, value) + } + if _u.mutation.NameCleared() { + _spec.ClearField(apikey.FieldName, field.TypeString) + } + if value, ok := _u.mutation.Prefix(); ok { + _spec.SetField(apikey.FieldPrefix, field.TypeString, value) + } + if _u.mutation.PrefixCleared() { + _spec.ClearField(apikey.FieldPrefix, field.TypeString) + } + if value, ok := _u.mutation.KeyHash(); ok { + _spec.SetField(apikey.FieldKeyHash, field.TypeString, value) + } + if value, ok := _u.mutation.Scopes(); ok { + _spec.SetField(apikey.FieldScopes, field.TypeString, value) + } + if _u.mutation.ScopesCleared() { + _spec.ClearField(apikey.FieldScopes, field.TypeString) + } + if value, ok := _u.mutation.Revoked(); ok { + _spec.SetField(apikey.FieldRevoked, field.TypeBool, value) + } + if value, ok := _u.mutation.ExpiresAt(); ok { + _spec.SetField(apikey.FieldExpiresAt, field.TypeTime, value) + } + if _u.mutation.ExpiresAtCleared() { + _spec.ClearField(apikey.FieldExpiresAt, field.TypeTime) + } + if value, ok := _u.mutation.LastUsed(); ok { + _spec.SetField(apikey.FieldLastUsed, field.TypeTime, value) + } + if _u.mutation.LastUsedCleared() { + _spec.ClearField(apikey.FieldLastUsed, field.TypeTime) + } + if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{apikey.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + _u.mutation.done = true + return _node, nil +} + +// ApiKeyUpdateOne is the builder for updating a single ApiKey entity. +type ApiKeyUpdateOne struct { + config + fields []string + hooks []Hook + mutation *ApiKeyMutation +} + +// SetUserID sets the "user_id" field. +func (_u *ApiKeyUpdateOne) SetUserID(v uuid.UUID) *ApiKeyUpdateOne { + _u.mutation.SetUserID(v) + return _u +} + +// SetNillableUserID sets the "user_id" field if the given value is not nil. +func (_u *ApiKeyUpdateOne) SetNillableUserID(v *uuid.UUID) *ApiKeyUpdateOne { + if v != nil { + _u.SetUserID(*v) + } + return _u +} + +// SetName sets the "name" field. +func (_u *ApiKeyUpdateOne) SetName(v string) *ApiKeyUpdateOne { + _u.mutation.SetName(v) + return _u +} + +// SetNillableName sets the "name" field if the given value is not nil. +func (_u *ApiKeyUpdateOne) SetNillableName(v *string) *ApiKeyUpdateOne { + if v != nil { + _u.SetName(*v) + } + return _u +} + +// ClearName clears the value of the "name" field. +func (_u *ApiKeyUpdateOne) ClearName() *ApiKeyUpdateOne { + _u.mutation.ClearName() + return _u +} + +// SetPrefix sets the "prefix" field. +func (_u *ApiKeyUpdateOne) SetPrefix(v string) *ApiKeyUpdateOne { + _u.mutation.SetPrefix(v) + return _u +} + +// SetNillablePrefix sets the "prefix" field if the given value is not nil. +func (_u *ApiKeyUpdateOne) SetNillablePrefix(v *string) *ApiKeyUpdateOne { + if v != nil { + _u.SetPrefix(*v) + } + return _u +} + +// ClearPrefix clears the value of the "prefix" field. +func (_u *ApiKeyUpdateOne) ClearPrefix() *ApiKeyUpdateOne { + _u.mutation.ClearPrefix() + return _u +} + +// SetKeyHash sets the "key_hash" field. +func (_u *ApiKeyUpdateOne) SetKeyHash(v string) *ApiKeyUpdateOne { + _u.mutation.SetKeyHash(v) + return _u +} + +// SetNillableKeyHash sets the "key_hash" field if the given value is not nil. +func (_u *ApiKeyUpdateOne) SetNillableKeyHash(v *string) *ApiKeyUpdateOne { + if v != nil { + _u.SetKeyHash(*v) + } + return _u +} + +// SetScopes sets the "scopes" field. +func (_u *ApiKeyUpdateOne) SetScopes(v string) *ApiKeyUpdateOne { + _u.mutation.SetScopes(v) + return _u +} + +// SetNillableScopes sets the "scopes" field if the given value is not nil. +func (_u *ApiKeyUpdateOne) SetNillableScopes(v *string) *ApiKeyUpdateOne { + if v != nil { + _u.SetScopes(*v) + } + return _u +} + +// ClearScopes clears the value of the "scopes" field. +func (_u *ApiKeyUpdateOne) ClearScopes() *ApiKeyUpdateOne { + _u.mutation.ClearScopes() + return _u +} + +// SetRevoked sets the "revoked" field. +func (_u *ApiKeyUpdateOne) SetRevoked(v bool) *ApiKeyUpdateOne { + _u.mutation.SetRevoked(v) + return _u +} + +// SetNillableRevoked sets the "revoked" field if the given value is not nil. +func (_u *ApiKeyUpdateOne) SetNillableRevoked(v *bool) *ApiKeyUpdateOne { + if v != nil { + _u.SetRevoked(*v) + } + return _u +} + +// SetExpiresAt sets the "expires_at" field. +func (_u *ApiKeyUpdateOne) SetExpiresAt(v time.Time) *ApiKeyUpdateOne { + _u.mutation.SetExpiresAt(v) + return _u +} + +// SetNillableExpiresAt sets the "expires_at" field if the given value is not nil. +func (_u *ApiKeyUpdateOne) SetNillableExpiresAt(v *time.Time) *ApiKeyUpdateOne { + if v != nil { + _u.SetExpiresAt(*v) + } + return _u +} + +// ClearExpiresAt clears the value of the "expires_at" field. +func (_u *ApiKeyUpdateOne) ClearExpiresAt() *ApiKeyUpdateOne { + _u.mutation.ClearExpiresAt() + return _u +} + +// SetLastUsed sets the "last_used" field. +func (_u *ApiKeyUpdateOne) SetLastUsed(v time.Time) *ApiKeyUpdateOne { + _u.mutation.SetLastUsed(v) + return _u +} + +// SetNillableLastUsed sets the "last_used" field if the given value is not nil. +func (_u *ApiKeyUpdateOne) SetNillableLastUsed(v *time.Time) *ApiKeyUpdateOne { + if v != nil { + _u.SetLastUsed(*v) + } + return _u +} + +// ClearLastUsed clears the value of the "last_used" field. +func (_u *ApiKeyUpdateOne) ClearLastUsed() *ApiKeyUpdateOne { + _u.mutation.ClearLastUsed() + return _u +} + +// Mutation returns the ApiKeyMutation object of the builder. +func (_u *ApiKeyUpdateOne) Mutation() *ApiKeyMutation { + return _u.mutation +} + +// Where appends a list predicates to the ApiKeyUpdate builder. +func (_u *ApiKeyUpdateOne) Where(ps ...predicate.ApiKey) *ApiKeyUpdateOne { + _u.mutation.Where(ps...) + return _u +} + +// Select allows selecting one or more fields (columns) of the returned entity. +// The default is selecting all fields defined in the entity schema. +func (_u *ApiKeyUpdateOne) Select(field string, fields ...string) *ApiKeyUpdateOne { + _u.fields = append([]string{field}, fields...) + return _u +} + +// Save executes the query and returns the updated ApiKey entity. +func (_u *ApiKeyUpdateOne) Save(ctx context.Context) (*ApiKey, error) { + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *ApiKeyUpdateOne) SaveX(ctx context.Context) *ApiKey { + node, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (_u *ApiKeyUpdateOne) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *ApiKeyUpdateOne) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_u *ApiKeyUpdateOne) check() error { + if v, ok := _u.mutation.KeyHash(); ok { + if err := apikey.KeyHashValidator(v); err != nil { + return &ValidationError{Name: "key_hash", err: fmt.Errorf(`ent: validator failed for field "ApiKey.key_hash": %w`, err)} + } + } + return nil +} + +func (_u *ApiKeyUpdateOne) sqlSave(ctx context.Context) (_node *ApiKey, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(apikey.Table, apikey.Columns, sqlgraph.NewFieldSpec(apikey.FieldID, field.TypeUUID)) + id, ok := _u.mutation.ID() + if !ok { + return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "ApiKey.id" for update`)} + } + _spec.Node.ID.Value = id + if fields := _u.fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, apikey.FieldID) + for _, f := range fields { + if !apikey.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + if f != apikey.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, f) + } + } + } + if ps := _u.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := _u.mutation.UserID(); ok { + _spec.SetField(apikey.FieldUserID, field.TypeUUID, value) + } + if value, ok := _u.mutation.Name(); ok { + _spec.SetField(apikey.FieldName, field.TypeString, value) + } + if _u.mutation.NameCleared() { + _spec.ClearField(apikey.FieldName, field.TypeString) + } + if value, ok := _u.mutation.Prefix(); ok { + _spec.SetField(apikey.FieldPrefix, field.TypeString, value) + } + if _u.mutation.PrefixCleared() { + _spec.ClearField(apikey.FieldPrefix, field.TypeString) + } + if value, ok := _u.mutation.KeyHash(); ok { + _spec.SetField(apikey.FieldKeyHash, field.TypeString, value) + } + if value, ok := _u.mutation.Scopes(); ok { + _spec.SetField(apikey.FieldScopes, field.TypeString, value) + } + if _u.mutation.ScopesCleared() { + _spec.ClearField(apikey.FieldScopes, field.TypeString) + } + if value, ok := _u.mutation.Revoked(); ok { + _spec.SetField(apikey.FieldRevoked, field.TypeBool, value) + } + if value, ok := _u.mutation.ExpiresAt(); ok { + _spec.SetField(apikey.FieldExpiresAt, field.TypeTime, value) + } + if _u.mutation.ExpiresAtCleared() { + _spec.ClearField(apikey.FieldExpiresAt, field.TypeTime) + } + if value, ok := _u.mutation.LastUsed(); ok { + _spec.SetField(apikey.FieldLastUsed, field.TypeTime, value) + } + if _u.mutation.LastUsedCleared() { + _spec.ClearField(apikey.FieldLastUsed, field.TypeTime) + } + _node = &ApiKey{config: _u.config} + _spec.Assign = _node.assignValues + _spec.ScanValues = _node.scanValues + if err = sqlgraph.UpdateNode(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{apikey.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + _u.mutation.done = true + return _node, nil +} diff --git a/pkg/ent/brokerjointoken.go b/pkg/ent/brokerjointoken.go new file mode 100644 index 000000000..a13f5c1c7 --- /dev/null +++ b/pkg/ent/brokerjointoken.go @@ -0,0 +1,140 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "fmt" + "strings" + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "github.com/GoogleCloudPlatform/scion/pkg/ent/brokerjointoken" + "github.com/google/uuid" +) + +// BrokerJoinToken is the model entity for the BrokerJoinToken schema. +type BrokerJoinToken struct { + config `json:"-"` + // ID of the ent. + ID uuid.UUID `json:"id,omitempty"` + // TokenHash holds the value of the "token_hash" field. + TokenHash string `json:"token_hash,omitempty"` + // ExpiresAt holds the value of the "expires_at" field. + ExpiresAt time.Time `json:"expires_at,omitempty"` + // CreatedBy holds the value of the "created_by" field. + CreatedBy string `json:"created_by,omitempty"` + // Created holds the value of the "created" field. + Created time.Time `json:"created,omitempty"` + selectValues sql.SelectValues +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*BrokerJoinToken) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case brokerjointoken.FieldTokenHash, brokerjointoken.FieldCreatedBy: + values[i] = new(sql.NullString) + case brokerjointoken.FieldExpiresAt, brokerjointoken.FieldCreated: + values[i] = new(sql.NullTime) + case brokerjointoken.FieldID: + values[i] = new(uuid.UUID) + default: + values[i] = new(sql.UnknownType) + } + } + return values, nil +} + +// assignValues assigns the values that were returned from sql.Rows (after scanning) +// to the BrokerJoinToken fields. +func (_m *BrokerJoinToken) assignValues(columns []string, values []any) error { + if m, n := len(values), len(columns); m < n { + return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) + } + for i := range columns { + switch columns[i] { + case brokerjointoken.FieldID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field id", values[i]) + } else if value != nil { + _m.ID = *value + } + case brokerjointoken.FieldTokenHash: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field token_hash", values[i]) + } else if value.Valid { + _m.TokenHash = value.String + } + case brokerjointoken.FieldExpiresAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field expires_at", values[i]) + } else if value.Valid { + _m.ExpiresAt = value.Time + } + case brokerjointoken.FieldCreatedBy: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field created_by", values[i]) + } else if value.Valid { + _m.CreatedBy = value.String + } + case brokerjointoken.FieldCreated: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field created", values[i]) + } else if value.Valid { + _m.Created = value.Time + } + default: + _m.selectValues.Set(columns[i], values[i]) + } + } + return nil +} + +// Value returns the ent.Value that was dynamically selected and assigned to the BrokerJoinToken. +// This includes values selected through modifiers, order, etc. +func (_m *BrokerJoinToken) Value(name string) (ent.Value, error) { + return _m.selectValues.Get(name) +} + +// Update returns a builder for updating this BrokerJoinToken. +// Note that you need to call BrokerJoinToken.Unwrap() before calling this method if this BrokerJoinToken +// was returned from a transaction, and the transaction was committed or rolled back. +func (_m *BrokerJoinToken) Update() *BrokerJoinTokenUpdateOne { + return NewBrokerJoinTokenClient(_m.config).UpdateOne(_m) +} + +// Unwrap unwraps the BrokerJoinToken entity that was returned from a transaction after it was closed, +// so that all future queries will be executed through the driver which created the transaction. +func (_m *BrokerJoinToken) Unwrap() *BrokerJoinToken { + _tx, ok := _m.config.driver.(*txDriver) + if !ok { + panic("ent: BrokerJoinToken is not a transactional entity") + } + _m.config.driver = _tx.drv + return _m +} + +// String implements the fmt.Stringer. +func (_m *BrokerJoinToken) String() string { + var builder strings.Builder + builder.WriteString("BrokerJoinToken(") + builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID)) + builder.WriteString("token_hash=") + builder.WriteString(_m.TokenHash) + builder.WriteString(", ") + builder.WriteString("expires_at=") + builder.WriteString(_m.ExpiresAt.Format(time.ANSIC)) + builder.WriteString(", ") + builder.WriteString("created_by=") + builder.WriteString(_m.CreatedBy) + builder.WriteString(", ") + builder.WriteString("created=") + builder.WriteString(_m.Created.Format(time.ANSIC)) + builder.WriteByte(')') + return builder.String() +} + +// BrokerJoinTokens is a parsable slice of BrokerJoinToken. +type BrokerJoinTokens []*BrokerJoinToken diff --git a/pkg/ent/brokerjointoken/brokerjointoken.go b/pkg/ent/brokerjointoken/brokerjointoken.go new file mode 100644 index 000000000..64e1a4b38 --- /dev/null +++ b/pkg/ent/brokerjointoken/brokerjointoken.go @@ -0,0 +1,82 @@ +// Code generated by ent, DO NOT EDIT. + +package brokerjointoken + +import ( + "time" + + "entgo.io/ent/dialect/sql" +) + +const ( + // Label holds the string label denoting the brokerjointoken type in the database. + Label = "broker_join_token" + // FieldID holds the string denoting the id field in the database. + FieldID = "broker_id" + // FieldTokenHash holds the string denoting the token_hash field in the database. + FieldTokenHash = "token_hash" + // FieldExpiresAt holds the string denoting the expires_at field in the database. + FieldExpiresAt = "expires_at" + // FieldCreatedBy holds the string denoting the created_by field in the database. + FieldCreatedBy = "created_by" + // FieldCreated holds the string denoting the created field in the database. + FieldCreated = "created" + // Table holds the table name of the brokerjointoken in the database. + Table = "broker_join_tokens" +) + +// Columns holds all SQL columns for brokerjointoken fields. +var Columns = []string{ + FieldID, + FieldTokenHash, + FieldExpiresAt, + FieldCreatedBy, + FieldCreated, +} + +// ValidColumn reports if the column name is valid (part of the table columns). +func ValidColumn(column string) bool { + for i := range Columns { + if column == Columns[i] { + return true + } + } + return false +} + +var ( + // TokenHashValidator is a validator for the "token_hash" field. It is called by the builders before save. + TokenHashValidator func(string) error + // CreatedByValidator is a validator for the "created_by" field. It is called by the builders before save. + CreatedByValidator func(string) error + // DefaultCreated holds the default value on creation for the "created" field. + DefaultCreated func() time.Time +) + +// OrderOption defines the ordering options for the BrokerJoinToken queries. +type OrderOption func(*sql.Selector) + +// ByID orders the results by the id field. +func ByID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldID, opts...).ToFunc() +} + +// ByTokenHash orders the results by the token_hash field. +func ByTokenHash(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldTokenHash, opts...).ToFunc() +} + +// ByExpiresAt orders the results by the expires_at field. +func ByExpiresAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldExpiresAt, opts...).ToFunc() +} + +// ByCreatedBy orders the results by the created_by field. +func ByCreatedBy(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCreatedBy, opts...).ToFunc() +} + +// ByCreated orders the results by the created field. +func ByCreated(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCreated, opts...).ToFunc() +} diff --git a/pkg/ent/brokerjointoken/where.go b/pkg/ent/brokerjointoken/where.go new file mode 100644 index 000000000..31747db81 --- /dev/null +++ b/pkg/ent/brokerjointoken/where.go @@ -0,0 +1,301 @@ +// Code generated by ent, DO NOT EDIT. + +package brokerjointoken + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/google/uuid" +) + +// ID filters vertices based on their ID field. +func ID(id uuid.UUID) predicate.BrokerJoinToken { + return predicate.BrokerJoinToken(sql.FieldEQ(FieldID, id)) +} + +// IDEQ applies the EQ predicate on the ID field. +func IDEQ(id uuid.UUID) predicate.BrokerJoinToken { + return predicate.BrokerJoinToken(sql.FieldEQ(FieldID, id)) +} + +// IDNEQ applies the NEQ predicate on the ID field. +func IDNEQ(id uuid.UUID) predicate.BrokerJoinToken { + return predicate.BrokerJoinToken(sql.FieldNEQ(FieldID, id)) +} + +// IDIn applies the In predicate on the ID field. +func IDIn(ids ...uuid.UUID) predicate.BrokerJoinToken { + return predicate.BrokerJoinToken(sql.FieldIn(FieldID, ids...)) +} + +// IDNotIn applies the NotIn predicate on the ID field. +func IDNotIn(ids ...uuid.UUID) predicate.BrokerJoinToken { + return predicate.BrokerJoinToken(sql.FieldNotIn(FieldID, ids...)) +} + +// IDGT applies the GT predicate on the ID field. +func IDGT(id uuid.UUID) predicate.BrokerJoinToken { + return predicate.BrokerJoinToken(sql.FieldGT(FieldID, id)) +} + +// IDGTE applies the GTE predicate on the ID field. +func IDGTE(id uuid.UUID) predicate.BrokerJoinToken { + return predicate.BrokerJoinToken(sql.FieldGTE(FieldID, id)) +} + +// IDLT applies the LT predicate on the ID field. +func IDLT(id uuid.UUID) predicate.BrokerJoinToken { + return predicate.BrokerJoinToken(sql.FieldLT(FieldID, id)) +} + +// IDLTE applies the LTE predicate on the ID field. +func IDLTE(id uuid.UUID) predicate.BrokerJoinToken { + return predicate.BrokerJoinToken(sql.FieldLTE(FieldID, id)) +} + +// TokenHash applies equality check predicate on the "token_hash" field. It's identical to TokenHashEQ. +func TokenHash(v string) predicate.BrokerJoinToken { + return predicate.BrokerJoinToken(sql.FieldEQ(FieldTokenHash, v)) +} + +// ExpiresAt applies equality check predicate on the "expires_at" field. It's identical to ExpiresAtEQ. +func ExpiresAt(v time.Time) predicate.BrokerJoinToken { + return predicate.BrokerJoinToken(sql.FieldEQ(FieldExpiresAt, v)) +} + +// CreatedBy applies equality check predicate on the "created_by" field. It's identical to CreatedByEQ. +func CreatedBy(v string) predicate.BrokerJoinToken { + return predicate.BrokerJoinToken(sql.FieldEQ(FieldCreatedBy, v)) +} + +// Created applies equality check predicate on the "created" field. It's identical to CreatedEQ. +func Created(v time.Time) predicate.BrokerJoinToken { + return predicate.BrokerJoinToken(sql.FieldEQ(FieldCreated, v)) +} + +// TokenHashEQ applies the EQ predicate on the "token_hash" field. +func TokenHashEQ(v string) predicate.BrokerJoinToken { + return predicate.BrokerJoinToken(sql.FieldEQ(FieldTokenHash, v)) +} + +// TokenHashNEQ applies the NEQ predicate on the "token_hash" field. +func TokenHashNEQ(v string) predicate.BrokerJoinToken { + return predicate.BrokerJoinToken(sql.FieldNEQ(FieldTokenHash, v)) +} + +// TokenHashIn applies the In predicate on the "token_hash" field. +func TokenHashIn(vs ...string) predicate.BrokerJoinToken { + return predicate.BrokerJoinToken(sql.FieldIn(FieldTokenHash, vs...)) +} + +// TokenHashNotIn applies the NotIn predicate on the "token_hash" field. +func TokenHashNotIn(vs ...string) predicate.BrokerJoinToken { + return predicate.BrokerJoinToken(sql.FieldNotIn(FieldTokenHash, vs...)) +} + +// TokenHashGT applies the GT predicate on the "token_hash" field. +func TokenHashGT(v string) predicate.BrokerJoinToken { + return predicate.BrokerJoinToken(sql.FieldGT(FieldTokenHash, v)) +} + +// TokenHashGTE applies the GTE predicate on the "token_hash" field. +func TokenHashGTE(v string) predicate.BrokerJoinToken { + return predicate.BrokerJoinToken(sql.FieldGTE(FieldTokenHash, v)) +} + +// TokenHashLT applies the LT predicate on the "token_hash" field. +func TokenHashLT(v string) predicate.BrokerJoinToken { + return predicate.BrokerJoinToken(sql.FieldLT(FieldTokenHash, v)) +} + +// TokenHashLTE applies the LTE predicate on the "token_hash" field. +func TokenHashLTE(v string) predicate.BrokerJoinToken { + return predicate.BrokerJoinToken(sql.FieldLTE(FieldTokenHash, v)) +} + +// TokenHashContains applies the Contains predicate on the "token_hash" field. +func TokenHashContains(v string) predicate.BrokerJoinToken { + return predicate.BrokerJoinToken(sql.FieldContains(FieldTokenHash, v)) +} + +// TokenHashHasPrefix applies the HasPrefix predicate on the "token_hash" field. +func TokenHashHasPrefix(v string) predicate.BrokerJoinToken { + return predicate.BrokerJoinToken(sql.FieldHasPrefix(FieldTokenHash, v)) +} + +// TokenHashHasSuffix applies the HasSuffix predicate on the "token_hash" field. +func TokenHashHasSuffix(v string) predicate.BrokerJoinToken { + return predicate.BrokerJoinToken(sql.FieldHasSuffix(FieldTokenHash, v)) +} + +// TokenHashEqualFold applies the EqualFold predicate on the "token_hash" field. +func TokenHashEqualFold(v string) predicate.BrokerJoinToken { + return predicate.BrokerJoinToken(sql.FieldEqualFold(FieldTokenHash, v)) +} + +// TokenHashContainsFold applies the ContainsFold predicate on the "token_hash" field. +func TokenHashContainsFold(v string) predicate.BrokerJoinToken { + return predicate.BrokerJoinToken(sql.FieldContainsFold(FieldTokenHash, v)) +} + +// ExpiresAtEQ applies the EQ predicate on the "expires_at" field. +func ExpiresAtEQ(v time.Time) predicate.BrokerJoinToken { + return predicate.BrokerJoinToken(sql.FieldEQ(FieldExpiresAt, v)) +} + +// ExpiresAtNEQ applies the NEQ predicate on the "expires_at" field. +func ExpiresAtNEQ(v time.Time) predicate.BrokerJoinToken { + return predicate.BrokerJoinToken(sql.FieldNEQ(FieldExpiresAt, v)) +} + +// ExpiresAtIn applies the In predicate on the "expires_at" field. +func ExpiresAtIn(vs ...time.Time) predicate.BrokerJoinToken { + return predicate.BrokerJoinToken(sql.FieldIn(FieldExpiresAt, vs...)) +} + +// ExpiresAtNotIn applies the NotIn predicate on the "expires_at" field. +func ExpiresAtNotIn(vs ...time.Time) predicate.BrokerJoinToken { + return predicate.BrokerJoinToken(sql.FieldNotIn(FieldExpiresAt, vs...)) +} + +// ExpiresAtGT applies the GT predicate on the "expires_at" field. +func ExpiresAtGT(v time.Time) predicate.BrokerJoinToken { + return predicate.BrokerJoinToken(sql.FieldGT(FieldExpiresAt, v)) +} + +// ExpiresAtGTE applies the GTE predicate on the "expires_at" field. +func ExpiresAtGTE(v time.Time) predicate.BrokerJoinToken { + return predicate.BrokerJoinToken(sql.FieldGTE(FieldExpiresAt, v)) +} + +// ExpiresAtLT applies the LT predicate on the "expires_at" field. +func ExpiresAtLT(v time.Time) predicate.BrokerJoinToken { + return predicate.BrokerJoinToken(sql.FieldLT(FieldExpiresAt, v)) +} + +// ExpiresAtLTE applies the LTE predicate on the "expires_at" field. +func ExpiresAtLTE(v time.Time) predicate.BrokerJoinToken { + return predicate.BrokerJoinToken(sql.FieldLTE(FieldExpiresAt, v)) +} + +// CreatedByEQ applies the EQ predicate on the "created_by" field. +func CreatedByEQ(v string) predicate.BrokerJoinToken { + return predicate.BrokerJoinToken(sql.FieldEQ(FieldCreatedBy, v)) +} + +// CreatedByNEQ applies the NEQ predicate on the "created_by" field. +func CreatedByNEQ(v string) predicate.BrokerJoinToken { + return predicate.BrokerJoinToken(sql.FieldNEQ(FieldCreatedBy, v)) +} + +// CreatedByIn applies the In predicate on the "created_by" field. +func CreatedByIn(vs ...string) predicate.BrokerJoinToken { + return predicate.BrokerJoinToken(sql.FieldIn(FieldCreatedBy, vs...)) +} + +// CreatedByNotIn applies the NotIn predicate on the "created_by" field. +func CreatedByNotIn(vs ...string) predicate.BrokerJoinToken { + return predicate.BrokerJoinToken(sql.FieldNotIn(FieldCreatedBy, vs...)) +} + +// CreatedByGT applies the GT predicate on the "created_by" field. +func CreatedByGT(v string) predicate.BrokerJoinToken { + return predicate.BrokerJoinToken(sql.FieldGT(FieldCreatedBy, v)) +} + +// CreatedByGTE applies the GTE predicate on the "created_by" field. +func CreatedByGTE(v string) predicate.BrokerJoinToken { + return predicate.BrokerJoinToken(sql.FieldGTE(FieldCreatedBy, v)) +} + +// CreatedByLT applies the LT predicate on the "created_by" field. +func CreatedByLT(v string) predicate.BrokerJoinToken { + return predicate.BrokerJoinToken(sql.FieldLT(FieldCreatedBy, v)) +} + +// CreatedByLTE applies the LTE predicate on the "created_by" field. +func CreatedByLTE(v string) predicate.BrokerJoinToken { + return predicate.BrokerJoinToken(sql.FieldLTE(FieldCreatedBy, v)) +} + +// CreatedByContains applies the Contains predicate on the "created_by" field. +func CreatedByContains(v string) predicate.BrokerJoinToken { + return predicate.BrokerJoinToken(sql.FieldContains(FieldCreatedBy, v)) +} + +// CreatedByHasPrefix applies the HasPrefix predicate on the "created_by" field. +func CreatedByHasPrefix(v string) predicate.BrokerJoinToken { + return predicate.BrokerJoinToken(sql.FieldHasPrefix(FieldCreatedBy, v)) +} + +// CreatedByHasSuffix applies the HasSuffix predicate on the "created_by" field. +func CreatedByHasSuffix(v string) predicate.BrokerJoinToken { + return predicate.BrokerJoinToken(sql.FieldHasSuffix(FieldCreatedBy, v)) +} + +// CreatedByEqualFold applies the EqualFold predicate on the "created_by" field. +func CreatedByEqualFold(v string) predicate.BrokerJoinToken { + return predicate.BrokerJoinToken(sql.FieldEqualFold(FieldCreatedBy, v)) +} + +// CreatedByContainsFold applies the ContainsFold predicate on the "created_by" field. +func CreatedByContainsFold(v string) predicate.BrokerJoinToken { + return predicate.BrokerJoinToken(sql.FieldContainsFold(FieldCreatedBy, v)) +} + +// CreatedEQ applies the EQ predicate on the "created" field. +func CreatedEQ(v time.Time) predicate.BrokerJoinToken { + return predicate.BrokerJoinToken(sql.FieldEQ(FieldCreated, v)) +} + +// CreatedNEQ applies the NEQ predicate on the "created" field. +func CreatedNEQ(v time.Time) predicate.BrokerJoinToken { + return predicate.BrokerJoinToken(sql.FieldNEQ(FieldCreated, v)) +} + +// CreatedIn applies the In predicate on the "created" field. +func CreatedIn(vs ...time.Time) predicate.BrokerJoinToken { + return predicate.BrokerJoinToken(sql.FieldIn(FieldCreated, vs...)) +} + +// CreatedNotIn applies the NotIn predicate on the "created" field. +func CreatedNotIn(vs ...time.Time) predicate.BrokerJoinToken { + return predicate.BrokerJoinToken(sql.FieldNotIn(FieldCreated, vs...)) +} + +// CreatedGT applies the GT predicate on the "created" field. +func CreatedGT(v time.Time) predicate.BrokerJoinToken { + return predicate.BrokerJoinToken(sql.FieldGT(FieldCreated, v)) +} + +// CreatedGTE applies the GTE predicate on the "created" field. +func CreatedGTE(v time.Time) predicate.BrokerJoinToken { + return predicate.BrokerJoinToken(sql.FieldGTE(FieldCreated, v)) +} + +// CreatedLT applies the LT predicate on the "created" field. +func CreatedLT(v time.Time) predicate.BrokerJoinToken { + return predicate.BrokerJoinToken(sql.FieldLT(FieldCreated, v)) +} + +// CreatedLTE applies the LTE predicate on the "created" field. +func CreatedLTE(v time.Time) predicate.BrokerJoinToken { + return predicate.BrokerJoinToken(sql.FieldLTE(FieldCreated, v)) +} + +// And groups predicates with the AND operator between them. +func And(predicates ...predicate.BrokerJoinToken) predicate.BrokerJoinToken { + return predicate.BrokerJoinToken(sql.AndPredicates(predicates...)) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.BrokerJoinToken) predicate.BrokerJoinToken { + return predicate.BrokerJoinToken(sql.OrPredicates(predicates...)) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.BrokerJoinToken) predicate.BrokerJoinToken { + return predicate.BrokerJoinToken(sql.NotPredicates(p)) +} diff --git a/pkg/ent/brokerjointoken_create.go b/pkg/ent/brokerjointoken_create.go new file mode 100644 index 000000000..c3f29f6df --- /dev/null +++ b/pkg/ent/brokerjointoken_create.go @@ -0,0 +1,644 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/brokerjointoken" + "github.com/google/uuid" +) + +// BrokerJoinTokenCreate is the builder for creating a BrokerJoinToken entity. +type BrokerJoinTokenCreate struct { + config + mutation *BrokerJoinTokenMutation + hooks []Hook + conflict []sql.ConflictOption +} + +// SetTokenHash sets the "token_hash" field. +func (_c *BrokerJoinTokenCreate) SetTokenHash(v string) *BrokerJoinTokenCreate { + _c.mutation.SetTokenHash(v) + return _c +} + +// SetExpiresAt sets the "expires_at" field. +func (_c *BrokerJoinTokenCreate) SetExpiresAt(v time.Time) *BrokerJoinTokenCreate { + _c.mutation.SetExpiresAt(v) + return _c +} + +// SetCreatedBy sets the "created_by" field. +func (_c *BrokerJoinTokenCreate) SetCreatedBy(v string) *BrokerJoinTokenCreate { + _c.mutation.SetCreatedBy(v) + return _c +} + +// SetCreated sets the "created" field. +func (_c *BrokerJoinTokenCreate) SetCreated(v time.Time) *BrokerJoinTokenCreate { + _c.mutation.SetCreated(v) + return _c +} + +// SetNillableCreated sets the "created" field if the given value is not nil. +func (_c *BrokerJoinTokenCreate) SetNillableCreated(v *time.Time) *BrokerJoinTokenCreate { + if v != nil { + _c.SetCreated(*v) + } + return _c +} + +// SetID sets the "id" field. +func (_c *BrokerJoinTokenCreate) SetID(v uuid.UUID) *BrokerJoinTokenCreate { + _c.mutation.SetID(v) + return _c +} + +// Mutation returns the BrokerJoinTokenMutation object of the builder. +func (_c *BrokerJoinTokenCreate) Mutation() *BrokerJoinTokenMutation { + return _c.mutation +} + +// Save creates the BrokerJoinToken in the database. +func (_c *BrokerJoinTokenCreate) Save(ctx context.Context) (*BrokerJoinToken, error) { + _c.defaults() + return withHooks(ctx, _c.sqlSave, _c.mutation, _c.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (_c *BrokerJoinTokenCreate) SaveX(ctx context.Context) *BrokerJoinToken { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *BrokerJoinTokenCreate) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *BrokerJoinTokenCreate) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_c *BrokerJoinTokenCreate) defaults() { + if _, ok := _c.mutation.Created(); !ok { + v := brokerjointoken.DefaultCreated() + _c.mutation.SetCreated(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_c *BrokerJoinTokenCreate) check() error { + if _, ok := _c.mutation.TokenHash(); !ok { + return &ValidationError{Name: "token_hash", err: errors.New(`ent: missing required field "BrokerJoinToken.token_hash"`)} + } + if v, ok := _c.mutation.TokenHash(); ok { + if err := brokerjointoken.TokenHashValidator(v); err != nil { + return &ValidationError{Name: "token_hash", err: fmt.Errorf(`ent: validator failed for field "BrokerJoinToken.token_hash": %w`, err)} + } + } + if _, ok := _c.mutation.ExpiresAt(); !ok { + return &ValidationError{Name: "expires_at", err: errors.New(`ent: missing required field "BrokerJoinToken.expires_at"`)} + } + if _, ok := _c.mutation.CreatedBy(); !ok { + return &ValidationError{Name: "created_by", err: errors.New(`ent: missing required field "BrokerJoinToken.created_by"`)} + } + if v, ok := _c.mutation.CreatedBy(); ok { + if err := brokerjointoken.CreatedByValidator(v); err != nil { + return &ValidationError{Name: "created_by", err: fmt.Errorf(`ent: validator failed for field "BrokerJoinToken.created_by": %w`, err)} + } + } + if _, ok := _c.mutation.Created(); !ok { + return &ValidationError{Name: "created", err: errors.New(`ent: missing required field "BrokerJoinToken.created"`)} + } + return nil +} + +func (_c *BrokerJoinTokenCreate) sqlSave(ctx context.Context) (*BrokerJoinToken, error) { + if err := _c.check(); err != nil { + return nil, err + } + _node, _spec := _c.createSpec() + if err := sqlgraph.CreateNode(ctx, _c.driver, _spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + if _spec.ID.Value != nil { + if id, ok := _spec.ID.Value.(*uuid.UUID); ok { + _node.ID = *id + } else if err := _node.ID.Scan(_spec.ID.Value); err != nil { + return nil, err + } + } + _c.mutation.id = &_node.ID + _c.mutation.done = true + return _node, nil +} + +func (_c *BrokerJoinTokenCreate) createSpec() (*BrokerJoinToken, *sqlgraph.CreateSpec) { + var ( + _node = &BrokerJoinToken{config: _c.config} + _spec = sqlgraph.NewCreateSpec(brokerjointoken.Table, sqlgraph.NewFieldSpec(brokerjointoken.FieldID, field.TypeUUID)) + ) + _spec.OnConflict = _c.conflict + if id, ok := _c.mutation.ID(); ok { + _node.ID = id + _spec.ID.Value = &id + } + if value, ok := _c.mutation.TokenHash(); ok { + _spec.SetField(brokerjointoken.FieldTokenHash, field.TypeString, value) + _node.TokenHash = value + } + if value, ok := _c.mutation.ExpiresAt(); ok { + _spec.SetField(brokerjointoken.FieldExpiresAt, field.TypeTime, value) + _node.ExpiresAt = value + } + if value, ok := _c.mutation.CreatedBy(); ok { + _spec.SetField(brokerjointoken.FieldCreatedBy, field.TypeString, value) + _node.CreatedBy = value + } + if value, ok := _c.mutation.Created(); ok { + _spec.SetField(brokerjointoken.FieldCreated, field.TypeTime, value) + _node.Created = value + } + return _node, _spec +} + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.BrokerJoinToken.Create(). +// SetTokenHash(v). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.BrokerJoinTokenUpsert) { +// SetTokenHash(v+v). +// }). +// Exec(ctx) +func (_c *BrokerJoinTokenCreate) OnConflict(opts ...sql.ConflictOption) *BrokerJoinTokenUpsertOne { + _c.conflict = opts + return &BrokerJoinTokenUpsertOne{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.BrokerJoinToken.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *BrokerJoinTokenCreate) OnConflictColumns(columns ...string) *BrokerJoinTokenUpsertOne { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &BrokerJoinTokenUpsertOne{ + create: _c, + } +} + +type ( + // BrokerJoinTokenUpsertOne is the builder for "upsert"-ing + // one BrokerJoinToken node. + BrokerJoinTokenUpsertOne struct { + create *BrokerJoinTokenCreate + } + + // BrokerJoinTokenUpsert is the "OnConflict" setter. + BrokerJoinTokenUpsert struct { + *sql.UpdateSet + } +) + +// SetTokenHash sets the "token_hash" field. +func (u *BrokerJoinTokenUpsert) SetTokenHash(v string) *BrokerJoinTokenUpsert { + u.Set(brokerjointoken.FieldTokenHash, v) + return u +} + +// UpdateTokenHash sets the "token_hash" field to the value that was provided on create. +func (u *BrokerJoinTokenUpsert) UpdateTokenHash() *BrokerJoinTokenUpsert { + u.SetExcluded(brokerjointoken.FieldTokenHash) + return u +} + +// SetExpiresAt sets the "expires_at" field. +func (u *BrokerJoinTokenUpsert) SetExpiresAt(v time.Time) *BrokerJoinTokenUpsert { + u.Set(brokerjointoken.FieldExpiresAt, v) + return u +} + +// UpdateExpiresAt sets the "expires_at" field to the value that was provided on create. +func (u *BrokerJoinTokenUpsert) UpdateExpiresAt() *BrokerJoinTokenUpsert { + u.SetExcluded(brokerjointoken.FieldExpiresAt) + return u +} + +// SetCreatedBy sets the "created_by" field. +func (u *BrokerJoinTokenUpsert) SetCreatedBy(v string) *BrokerJoinTokenUpsert { + u.Set(brokerjointoken.FieldCreatedBy, v) + return u +} + +// UpdateCreatedBy sets the "created_by" field to the value that was provided on create. +func (u *BrokerJoinTokenUpsert) UpdateCreatedBy() *BrokerJoinTokenUpsert { + u.SetExcluded(brokerjointoken.FieldCreatedBy) + return u +} + +// UpdateNewValues updates the mutable fields using the new values that were set on create except the ID field. +// Using this option is equivalent to using: +// +// client.BrokerJoinToken.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(brokerjointoken.FieldID) +// }), +// ). +// Exec(ctx) +func (u *BrokerJoinTokenUpsertOne) UpdateNewValues() *BrokerJoinTokenUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + if _, exists := u.create.mutation.ID(); exists { + s.SetIgnore(brokerjointoken.FieldID) + } + if _, exists := u.create.mutation.Created(); exists { + s.SetIgnore(brokerjointoken.FieldCreated) + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.BrokerJoinToken.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *BrokerJoinTokenUpsertOne) Ignore() *BrokerJoinTokenUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *BrokerJoinTokenUpsertOne) DoNothing() *BrokerJoinTokenUpsertOne { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the BrokerJoinTokenCreate.OnConflict +// documentation for more info. +func (u *BrokerJoinTokenUpsertOne) Update(set func(*BrokerJoinTokenUpsert)) *BrokerJoinTokenUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&BrokerJoinTokenUpsert{UpdateSet: update}) + })) + return u +} + +// SetTokenHash sets the "token_hash" field. +func (u *BrokerJoinTokenUpsertOne) SetTokenHash(v string) *BrokerJoinTokenUpsertOne { + return u.Update(func(s *BrokerJoinTokenUpsert) { + s.SetTokenHash(v) + }) +} + +// UpdateTokenHash sets the "token_hash" field to the value that was provided on create. +func (u *BrokerJoinTokenUpsertOne) UpdateTokenHash() *BrokerJoinTokenUpsertOne { + return u.Update(func(s *BrokerJoinTokenUpsert) { + s.UpdateTokenHash() + }) +} + +// SetExpiresAt sets the "expires_at" field. +func (u *BrokerJoinTokenUpsertOne) SetExpiresAt(v time.Time) *BrokerJoinTokenUpsertOne { + return u.Update(func(s *BrokerJoinTokenUpsert) { + s.SetExpiresAt(v) + }) +} + +// UpdateExpiresAt sets the "expires_at" field to the value that was provided on create. +func (u *BrokerJoinTokenUpsertOne) UpdateExpiresAt() *BrokerJoinTokenUpsertOne { + return u.Update(func(s *BrokerJoinTokenUpsert) { + s.UpdateExpiresAt() + }) +} + +// SetCreatedBy sets the "created_by" field. +func (u *BrokerJoinTokenUpsertOne) SetCreatedBy(v string) *BrokerJoinTokenUpsertOne { + return u.Update(func(s *BrokerJoinTokenUpsert) { + s.SetCreatedBy(v) + }) +} + +// UpdateCreatedBy sets the "created_by" field to the value that was provided on create. +func (u *BrokerJoinTokenUpsertOne) UpdateCreatedBy() *BrokerJoinTokenUpsertOne { + return u.Update(func(s *BrokerJoinTokenUpsert) { + s.UpdateCreatedBy() + }) +} + +// Exec executes the query. +func (u *BrokerJoinTokenUpsertOne) Exec(ctx context.Context) error { + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for BrokerJoinTokenCreate.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *BrokerJoinTokenUpsertOne) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} + +// Exec executes the UPSERT query and returns the inserted/updated ID. +func (u *BrokerJoinTokenUpsertOne) ID(ctx context.Context) (id uuid.UUID, err error) { + if u.create.driver.Dialect() == dialect.MySQL { + // In case of "ON CONFLICT", there is no way to get back non-numeric ID + // fields from the database since MySQL does not support the RETURNING clause. + return id, errors.New("ent: BrokerJoinTokenUpsertOne.ID is not supported by MySQL driver. Use BrokerJoinTokenUpsertOne.Exec instead") + } + node, err := u.create.Save(ctx) + if err != nil { + return id, err + } + return node.ID, nil +} + +// IDX is like ID, but panics if an error occurs. +func (u *BrokerJoinTokenUpsertOne) IDX(ctx context.Context) uuid.UUID { + id, err := u.ID(ctx) + if err != nil { + panic(err) + } + return id +} + +// BrokerJoinTokenCreateBulk is the builder for creating many BrokerJoinToken entities in bulk. +type BrokerJoinTokenCreateBulk struct { + config + err error + builders []*BrokerJoinTokenCreate + conflict []sql.ConflictOption +} + +// Save creates the BrokerJoinToken entities in the database. +func (_c *BrokerJoinTokenCreateBulk) Save(ctx context.Context) ([]*BrokerJoinToken, error) { + if _c.err != nil { + return nil, _c.err + } + specs := make([]*sqlgraph.CreateSpec, len(_c.builders)) + nodes := make([]*BrokerJoinToken, len(_c.builders)) + mutators := make([]Mutator, len(_c.builders)) + for i := range _c.builders { + func(i int, root context.Context) { + builder := _c.builders[i] + builder.defaults() + var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { + mutation, ok := m.(*BrokerJoinTokenMutation) + if !ok { + return nil, fmt.Errorf("unexpected mutation type %T", m) + } + if err := builder.check(); err != nil { + return nil, err + } + builder.mutation = mutation + var err error + nodes[i], specs[i] = builder.createSpec() + if i < len(mutators)-1 { + _, err = mutators[i+1].Mutate(root, _c.builders[i+1].mutation) + } else { + spec := &sqlgraph.BatchCreateSpec{Nodes: specs} + spec.OnConflict = _c.conflict + // Invoke the actual operation on the latest mutation in the chain. + if err = sqlgraph.BatchCreate(ctx, _c.driver, spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + } + } + if err != nil { + return nil, err + } + mutation.id = &nodes[i].ID + mutation.done = true + return nodes[i], nil + }) + for i := len(builder.hooks) - 1; i >= 0; i-- { + mut = builder.hooks[i](mut) + } + mutators[i] = mut + }(i, ctx) + } + if len(mutators) > 0 { + if _, err := mutators[0].Mutate(ctx, _c.builders[0].mutation); err != nil { + return nil, err + } + } + return nodes, nil +} + +// SaveX is like Save, but panics if an error occurs. +func (_c *BrokerJoinTokenCreateBulk) SaveX(ctx context.Context) []*BrokerJoinToken { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *BrokerJoinTokenCreateBulk) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *BrokerJoinTokenCreateBulk) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.BrokerJoinToken.CreateBulk(builders...). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.BrokerJoinTokenUpsert) { +// SetTokenHash(v+v). +// }). +// Exec(ctx) +func (_c *BrokerJoinTokenCreateBulk) OnConflict(opts ...sql.ConflictOption) *BrokerJoinTokenUpsertBulk { + _c.conflict = opts + return &BrokerJoinTokenUpsertBulk{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.BrokerJoinToken.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *BrokerJoinTokenCreateBulk) OnConflictColumns(columns ...string) *BrokerJoinTokenUpsertBulk { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &BrokerJoinTokenUpsertBulk{ + create: _c, + } +} + +// BrokerJoinTokenUpsertBulk is the builder for "upsert"-ing +// a bulk of BrokerJoinToken nodes. +type BrokerJoinTokenUpsertBulk struct { + create *BrokerJoinTokenCreateBulk +} + +// UpdateNewValues updates the mutable fields using the new values that +// were set on create. Using this option is equivalent to using: +// +// client.BrokerJoinToken.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(brokerjointoken.FieldID) +// }), +// ). +// Exec(ctx) +func (u *BrokerJoinTokenUpsertBulk) UpdateNewValues() *BrokerJoinTokenUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + for _, b := range u.create.builders { + if _, exists := b.mutation.ID(); exists { + s.SetIgnore(brokerjointoken.FieldID) + } + if _, exists := b.mutation.Created(); exists { + s.SetIgnore(brokerjointoken.FieldCreated) + } + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.BrokerJoinToken.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *BrokerJoinTokenUpsertBulk) Ignore() *BrokerJoinTokenUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *BrokerJoinTokenUpsertBulk) DoNothing() *BrokerJoinTokenUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the BrokerJoinTokenCreateBulk.OnConflict +// documentation for more info. +func (u *BrokerJoinTokenUpsertBulk) Update(set func(*BrokerJoinTokenUpsert)) *BrokerJoinTokenUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&BrokerJoinTokenUpsert{UpdateSet: update}) + })) + return u +} + +// SetTokenHash sets the "token_hash" field. +func (u *BrokerJoinTokenUpsertBulk) SetTokenHash(v string) *BrokerJoinTokenUpsertBulk { + return u.Update(func(s *BrokerJoinTokenUpsert) { + s.SetTokenHash(v) + }) +} + +// UpdateTokenHash sets the "token_hash" field to the value that was provided on create. +func (u *BrokerJoinTokenUpsertBulk) UpdateTokenHash() *BrokerJoinTokenUpsertBulk { + return u.Update(func(s *BrokerJoinTokenUpsert) { + s.UpdateTokenHash() + }) +} + +// SetExpiresAt sets the "expires_at" field. +func (u *BrokerJoinTokenUpsertBulk) SetExpiresAt(v time.Time) *BrokerJoinTokenUpsertBulk { + return u.Update(func(s *BrokerJoinTokenUpsert) { + s.SetExpiresAt(v) + }) +} + +// UpdateExpiresAt sets the "expires_at" field to the value that was provided on create. +func (u *BrokerJoinTokenUpsertBulk) UpdateExpiresAt() *BrokerJoinTokenUpsertBulk { + return u.Update(func(s *BrokerJoinTokenUpsert) { + s.UpdateExpiresAt() + }) +} + +// SetCreatedBy sets the "created_by" field. +func (u *BrokerJoinTokenUpsertBulk) SetCreatedBy(v string) *BrokerJoinTokenUpsertBulk { + return u.Update(func(s *BrokerJoinTokenUpsert) { + s.SetCreatedBy(v) + }) +} + +// UpdateCreatedBy sets the "created_by" field to the value that was provided on create. +func (u *BrokerJoinTokenUpsertBulk) UpdateCreatedBy() *BrokerJoinTokenUpsertBulk { + return u.Update(func(s *BrokerJoinTokenUpsert) { + s.UpdateCreatedBy() + }) +} + +// Exec executes the query. +func (u *BrokerJoinTokenUpsertBulk) Exec(ctx context.Context) error { + if u.create.err != nil { + return u.create.err + } + for i, b := range u.create.builders { + if len(b.conflict) != 0 { + return fmt.Errorf("ent: OnConflict was set for builder %d. Set it on the BrokerJoinTokenCreateBulk instead", i) + } + } + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for BrokerJoinTokenCreateBulk.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *BrokerJoinTokenUpsertBulk) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/pkg/ent/brokerjointoken_delete.go b/pkg/ent/brokerjointoken_delete.go new file mode 100644 index 000000000..5aa3ea0ef --- /dev/null +++ b/pkg/ent/brokerjointoken_delete.go @@ -0,0 +1,88 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/brokerjointoken" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" +) + +// BrokerJoinTokenDelete is the builder for deleting a BrokerJoinToken entity. +type BrokerJoinTokenDelete struct { + config + hooks []Hook + mutation *BrokerJoinTokenMutation +} + +// Where appends a list predicates to the BrokerJoinTokenDelete builder. +func (_d *BrokerJoinTokenDelete) Where(ps ...predicate.BrokerJoinToken) *BrokerJoinTokenDelete { + _d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (_d *BrokerJoinTokenDelete) Exec(ctx context.Context) (int, error) { + return withHooks(ctx, _d.sqlExec, _d.mutation, _d.hooks) +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *BrokerJoinTokenDelete) ExecX(ctx context.Context) int { + n, err := _d.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (_d *BrokerJoinTokenDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(brokerjointoken.Table, sqlgraph.NewFieldSpec(brokerjointoken.FieldID, field.TypeUUID)) + if ps := _d.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + affected, err := sqlgraph.DeleteNodes(ctx, _d.driver, _spec) + if err != nil && sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + _d.mutation.done = true + return affected, err +} + +// BrokerJoinTokenDeleteOne is the builder for deleting a single BrokerJoinToken entity. +type BrokerJoinTokenDeleteOne struct { + _d *BrokerJoinTokenDelete +} + +// Where appends a list predicates to the BrokerJoinTokenDelete builder. +func (_d *BrokerJoinTokenDeleteOne) Where(ps ...predicate.BrokerJoinToken) *BrokerJoinTokenDeleteOne { + _d._d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query. +func (_d *BrokerJoinTokenDeleteOne) Exec(ctx context.Context) error { + n, err := _d._d.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{brokerjointoken.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *BrokerJoinTokenDeleteOne) ExecX(ctx context.Context) { + if err := _d.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/pkg/ent/brokerjointoken_query.go b/pkg/ent/brokerjointoken_query.go new file mode 100644 index 000000000..92a86cdec --- /dev/null +++ b/pkg/ent/brokerjointoken_query.go @@ -0,0 +1,565 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "fmt" + "math" + + "entgo.io/ent" + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/brokerjointoken" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/google/uuid" +) + +// BrokerJoinTokenQuery is the builder for querying BrokerJoinToken entities. +type BrokerJoinTokenQuery struct { + config + ctx *QueryContext + order []brokerjointoken.OrderOption + inters []Interceptor + predicates []predicate.BrokerJoinToken + modifiers []func(*sql.Selector) + // intermediate query (i.e. traversal path). + sql *sql.Selector + path func(context.Context) (*sql.Selector, error) +} + +// Where adds a new predicate for the BrokerJoinTokenQuery builder. +func (_q *BrokerJoinTokenQuery) Where(ps ...predicate.BrokerJoinToken) *BrokerJoinTokenQuery { + _q.predicates = append(_q.predicates, ps...) + return _q +} + +// Limit the number of records to be returned by this query. +func (_q *BrokerJoinTokenQuery) Limit(limit int) *BrokerJoinTokenQuery { + _q.ctx.Limit = &limit + return _q +} + +// Offset to start from. +func (_q *BrokerJoinTokenQuery) Offset(offset int) *BrokerJoinTokenQuery { + _q.ctx.Offset = &offset + return _q +} + +// Unique configures the query builder to filter duplicate records on query. +// By default, unique is set to true, and can be disabled using this method. +func (_q *BrokerJoinTokenQuery) Unique(unique bool) *BrokerJoinTokenQuery { + _q.ctx.Unique = &unique + return _q +} + +// Order specifies how the records should be ordered. +func (_q *BrokerJoinTokenQuery) Order(o ...brokerjointoken.OrderOption) *BrokerJoinTokenQuery { + _q.order = append(_q.order, o...) + return _q +} + +// First returns the first BrokerJoinToken entity from the query. +// Returns a *NotFoundError when no BrokerJoinToken was found. +func (_q *BrokerJoinTokenQuery) First(ctx context.Context) (*BrokerJoinToken, error) { + nodes, err := _q.Limit(1).All(setContextOp(ctx, _q.ctx, ent.OpQueryFirst)) + if err != nil { + return nil, err + } + if len(nodes) == 0 { + return nil, &NotFoundError{brokerjointoken.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (_q *BrokerJoinTokenQuery) FirstX(ctx context.Context) *BrokerJoinToken { + node, err := _q.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// FirstID returns the first BrokerJoinToken ID from the query. +// Returns a *NotFoundError when no BrokerJoinToken ID was found. +func (_q *BrokerJoinTokenQuery) FirstID(ctx context.Context) (id uuid.UUID, err error) { + var ids []uuid.UUID + if ids, err = _q.Limit(1).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryFirstID)); err != nil { + return + } + if len(ids) == 0 { + err = &NotFoundError{brokerjointoken.Label} + return + } + return ids[0], nil +} + +// FirstIDX is like FirstID, but panics if an error occurs. +func (_q *BrokerJoinTokenQuery) FirstIDX(ctx context.Context) uuid.UUID { + id, err := _q.FirstID(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return id +} + +// Only returns a single BrokerJoinToken entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one BrokerJoinToken entity is found. +// Returns a *NotFoundError when no BrokerJoinToken entities are found. +func (_q *BrokerJoinTokenQuery) Only(ctx context.Context) (*BrokerJoinToken, error) { + nodes, err := _q.Limit(2).All(setContextOp(ctx, _q.ctx, ent.OpQueryOnly)) + if err != nil { + return nil, err + } + switch len(nodes) { + case 1: + return nodes[0], nil + case 0: + return nil, &NotFoundError{brokerjointoken.Label} + default: + return nil, &NotSingularError{brokerjointoken.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (_q *BrokerJoinTokenQuery) OnlyX(ctx context.Context) *BrokerJoinToken { + node, err := _q.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// OnlyID is like Only, but returns the only BrokerJoinToken ID in the query. +// Returns a *NotSingularError when more than one BrokerJoinToken ID is found. +// Returns a *NotFoundError when no entities are found. +func (_q *BrokerJoinTokenQuery) OnlyID(ctx context.Context) (id uuid.UUID, err error) { + var ids []uuid.UUID + if ids, err = _q.Limit(2).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryOnlyID)); err != nil { + return + } + switch len(ids) { + case 1: + id = ids[0] + case 0: + err = &NotFoundError{brokerjointoken.Label} + default: + err = &NotSingularError{brokerjointoken.Label} + } + return +} + +// OnlyIDX is like OnlyID, but panics if an error occurs. +func (_q *BrokerJoinTokenQuery) OnlyIDX(ctx context.Context) uuid.UUID { + id, err := _q.OnlyID(ctx) + if err != nil { + panic(err) + } + return id +} + +// All executes the query and returns a list of BrokerJoinTokens. +func (_q *BrokerJoinTokenQuery) All(ctx context.Context) ([]*BrokerJoinToken, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryAll) + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*BrokerJoinToken, *BrokerJoinTokenQuery]() + return withInterceptors[[]*BrokerJoinToken](ctx, _q, qr, _q.inters) +} + +// AllX is like All, but panics if an error occurs. +func (_q *BrokerJoinTokenQuery) AllX(ctx context.Context) []*BrokerJoinToken { + nodes, err := _q.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// IDs executes the query and returns a list of BrokerJoinToken IDs. +func (_q *BrokerJoinTokenQuery) IDs(ctx context.Context) (ids []uuid.UUID, err error) { + if _q.ctx.Unique == nil && _q.path != nil { + _q.Unique(true) + } + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryIDs) + if err = _q.Select(brokerjointoken.FieldID).Scan(ctx, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// IDsX is like IDs, but panics if an error occurs. +func (_q *BrokerJoinTokenQuery) IDsX(ctx context.Context) []uuid.UUID { + ids, err := _q.IDs(ctx) + if err != nil { + panic(err) + } + return ids +} + +// Count returns the count of the given query. +func (_q *BrokerJoinTokenQuery) Count(ctx context.Context) (int, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryCount) + if err := _q.prepareQuery(ctx); err != nil { + return 0, err + } + return withInterceptors[int](ctx, _q, querierCount[*BrokerJoinTokenQuery](), _q.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (_q *BrokerJoinTokenQuery) CountX(ctx context.Context) int { + count, err := _q.Count(ctx) + if err != nil { + panic(err) + } + return count +} + +// Exist returns true if the query has elements in the graph. +func (_q *BrokerJoinTokenQuery) Exist(ctx context.Context) (bool, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryExist) + switch _, err := _q.FirstID(ctx); { + case IsNotFound(err): + return false, nil + case err != nil: + return false, fmt.Errorf("ent: check existence: %w", err) + default: + return true, nil + } +} + +// ExistX is like Exist, but panics if an error occurs. +func (_q *BrokerJoinTokenQuery) ExistX(ctx context.Context) bool { + exist, err := _q.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the BrokerJoinTokenQuery builder, including all associated steps. It can be +// used to prepare common query builders and use them differently after the clone is made. +func (_q *BrokerJoinTokenQuery) Clone() *BrokerJoinTokenQuery { + if _q == nil { + return nil + } + return &BrokerJoinTokenQuery{ + config: _q.config, + ctx: _q.ctx.Clone(), + order: append([]brokerjointoken.OrderOption{}, _q.order...), + inters: append([]Interceptor{}, _q.inters...), + predicates: append([]predicate.BrokerJoinToken{}, _q.predicates...), + // clone intermediate query. + sql: _q.sql.Clone(), + path: _q.path, + } +} + +// GroupBy is used to group vertices by one or more fields/columns. +// It is often used with aggregate functions, like: count, max, mean, min, sum. +// +// Example: +// +// var v []struct { +// TokenHash string `json:"token_hash,omitempty"` +// Count int `json:"count,omitempty"` +// } +// +// client.BrokerJoinToken.Query(). +// GroupBy(brokerjointoken.FieldTokenHash). +// Aggregate(ent.Count()). +// Scan(ctx, &v) +func (_q *BrokerJoinTokenQuery) GroupBy(field string, fields ...string) *BrokerJoinTokenGroupBy { + _q.ctx.Fields = append([]string{field}, fields...) + grbuild := &BrokerJoinTokenGroupBy{build: _q} + grbuild.flds = &_q.ctx.Fields + grbuild.label = brokerjointoken.Label + grbuild.scan = grbuild.Scan + return grbuild +} + +// Select allows the selection one or more fields/columns for the given query, +// instead of selecting all fields in the entity. +// +// Example: +// +// var v []struct { +// TokenHash string `json:"token_hash,omitempty"` +// } +// +// client.BrokerJoinToken.Query(). +// Select(brokerjointoken.FieldTokenHash). +// Scan(ctx, &v) +func (_q *BrokerJoinTokenQuery) Select(fields ...string) *BrokerJoinTokenSelect { + _q.ctx.Fields = append(_q.ctx.Fields, fields...) + sbuild := &BrokerJoinTokenSelect{BrokerJoinTokenQuery: _q} + sbuild.label = brokerjointoken.Label + sbuild.flds, sbuild.scan = &_q.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a BrokerJoinTokenSelect configured with the given aggregations. +func (_q *BrokerJoinTokenQuery) Aggregate(fns ...AggregateFunc) *BrokerJoinTokenSelect { + return _q.Select().Aggregate(fns...) +} + +func (_q *BrokerJoinTokenQuery) prepareQuery(ctx context.Context) error { + for _, inter := range _q.inters { + if inter == nil { + return fmt.Errorf("ent: uninitialized interceptor (forgotten import ent/runtime?)") + } + if trv, ok := inter.(Traverser); ok { + if err := trv.Traverse(ctx, _q); err != nil { + return err + } + } + } + for _, f := range _q.ctx.Fields { + if !brokerjointoken.ValidColumn(f) { + return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + } + if _q.path != nil { + prev, err := _q.path(ctx) + if err != nil { + return err + } + _q.sql = prev + } + return nil +} + +func (_q *BrokerJoinTokenQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*BrokerJoinToken, error) { + var ( + nodes = []*BrokerJoinToken{} + _spec = _q.querySpec() + ) + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*BrokerJoinToken).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &BrokerJoinToken{config: _q.config} + nodes = append(nodes, node) + return node.assignValues(columns, values) + } + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } + for i := range hooks { + hooks[i](ctx, _spec) + } + if err := sqlgraph.QueryNodes(ctx, _q.driver, _spec); err != nil { + return nil, err + } + if len(nodes) == 0 { + return nodes, nil + } + return nodes, nil +} + +func (_q *BrokerJoinTokenQuery) sqlCount(ctx context.Context) (int, error) { + _spec := _q.querySpec() + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } + _spec.Node.Columns = _q.ctx.Fields + if len(_q.ctx.Fields) > 0 { + _spec.Unique = _q.ctx.Unique != nil && *_q.ctx.Unique + } + return sqlgraph.CountNodes(ctx, _q.driver, _spec) +} + +func (_q *BrokerJoinTokenQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(brokerjointoken.Table, brokerjointoken.Columns, sqlgraph.NewFieldSpec(brokerjointoken.FieldID, field.TypeUUID)) + _spec.From = _q.sql + if unique := _q.ctx.Unique; unique != nil { + _spec.Unique = *unique + } else if _q.path != nil { + _spec.Unique = true + } + if fields := _q.ctx.Fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, brokerjointoken.FieldID) + for i := range fields { + if fields[i] != brokerjointoken.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, fields[i]) + } + } + } + if ps := _q.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if limit := _q.ctx.Limit; limit != nil { + _spec.Limit = *limit + } + if offset := _q.ctx.Offset; offset != nil { + _spec.Offset = *offset + } + if ps := _q.order; len(ps) > 0 { + _spec.Order = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + return _spec +} + +func (_q *BrokerJoinTokenQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(_q.driver.Dialect()) + t1 := builder.Table(brokerjointoken.Table) + columns := _q.ctx.Fields + if len(columns) == 0 { + columns = brokerjointoken.Columns + } + selector := builder.Select(t1.Columns(columns...)...).From(t1) + if _q.sql != nil { + selector = _q.sql + selector.Select(selector.Columns(columns...)...) + } + if _q.ctx.Unique != nil && *_q.ctx.Unique { + selector.Distinct() + } + for _, m := range _q.modifiers { + m(selector) + } + for _, p := range _q.predicates { + p(selector) + } + for _, p := range _q.order { + p(selector) + } + if offset := _q.ctx.Offset; offset != nil { + // limit is mandatory for offset clause. We start + // with default value, and override it below if needed. + selector.Offset(*offset).Limit(math.MaxInt32) + } + if limit := _q.ctx.Limit; limit != nil { + selector.Limit(*limit) + } + return selector +} + +// ForUpdate locks the selected rows against concurrent updates, and prevent them from being +// updated, deleted or "selected ... for update" by other sessions, until the transaction is +// either committed or rolled-back. +func (_q *BrokerJoinTokenQuery) ForUpdate(opts ...sql.LockOption) *BrokerJoinTokenQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForUpdate(opts...) + }) + return _q +} + +// ForShare behaves similarly to ForUpdate, except that it acquires a shared mode lock +// on any rows that are read. Other sessions can read the rows, but cannot modify them +// until your transaction commits. +func (_q *BrokerJoinTokenQuery) ForShare(opts ...sql.LockOption) *BrokerJoinTokenQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForShare(opts...) + }) + return _q +} + +// BrokerJoinTokenGroupBy is the group-by builder for BrokerJoinToken entities. +type BrokerJoinTokenGroupBy struct { + selector + build *BrokerJoinTokenQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (_g *BrokerJoinTokenGroupBy) Aggregate(fns ...AggregateFunc) *BrokerJoinTokenGroupBy { + _g.fns = append(_g.fns, fns...) + return _g +} + +// Scan applies the selector query and scans the result into the given value. +func (_g *BrokerJoinTokenGroupBy) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, _g.build.ctx, ent.OpQueryGroupBy) + if err := _g.build.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*BrokerJoinTokenQuery, *BrokerJoinTokenGroupBy](ctx, _g.build, _g, _g.build.inters, v) +} + +func (_g *BrokerJoinTokenGroupBy) sqlScan(ctx context.Context, root *BrokerJoinTokenQuery, v any) error { + selector := root.sqlQuery(ctx).Select() + aggregation := make([]string, 0, len(_g.fns)) + for _, fn := range _g.fns { + aggregation = append(aggregation, fn(selector)) + } + if len(selector.SelectedColumns()) == 0 { + columns := make([]string, 0, len(*_g.flds)+len(_g.fns)) + for _, f := range *_g.flds { + columns = append(columns, selector.C(f)) + } + columns = append(columns, aggregation...) + selector.Select(columns...) + } + selector.GroupBy(selector.Columns(*_g.flds...)...) + if err := selector.Err(); err != nil { + return err + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := _g.build.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} + +// BrokerJoinTokenSelect is the builder for selecting fields of BrokerJoinToken entities. +type BrokerJoinTokenSelect struct { + *BrokerJoinTokenQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (_s *BrokerJoinTokenSelect) Aggregate(fns ...AggregateFunc) *BrokerJoinTokenSelect { + _s.fns = append(_s.fns, fns...) + return _s +} + +// Scan applies the selector query and scans the result into the given value. +func (_s *BrokerJoinTokenSelect) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, _s.ctx, ent.OpQuerySelect) + if err := _s.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*BrokerJoinTokenQuery, *BrokerJoinTokenSelect](ctx, _s.BrokerJoinTokenQuery, _s, _s.inters, v) +} + +func (_s *BrokerJoinTokenSelect) sqlScan(ctx context.Context, root *BrokerJoinTokenQuery, v any) error { + selector := root.sqlQuery(ctx) + aggregation := make([]string, 0, len(_s.fns)) + for _, fn := range _s.fns { + aggregation = append(aggregation, fn(selector)) + } + switch n := len(*_s.selector.flds); { + case n == 0 && len(aggregation) > 0: + selector.Select(aggregation...) + case n != 0 && len(aggregation) > 0: + selector.AppendSelect(aggregation...) + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := _s.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} diff --git a/pkg/ent/brokerjointoken_update.go b/pkg/ent/brokerjointoken_update.go new file mode 100644 index 000000000..2a91bcc82 --- /dev/null +++ b/pkg/ent/brokerjointoken_update.go @@ -0,0 +1,314 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/brokerjointoken" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" +) + +// BrokerJoinTokenUpdate is the builder for updating BrokerJoinToken entities. +type BrokerJoinTokenUpdate struct { + config + hooks []Hook + mutation *BrokerJoinTokenMutation +} + +// Where appends a list predicates to the BrokerJoinTokenUpdate builder. +func (_u *BrokerJoinTokenUpdate) Where(ps ...predicate.BrokerJoinToken) *BrokerJoinTokenUpdate { + _u.mutation.Where(ps...) + return _u +} + +// SetTokenHash sets the "token_hash" field. +func (_u *BrokerJoinTokenUpdate) SetTokenHash(v string) *BrokerJoinTokenUpdate { + _u.mutation.SetTokenHash(v) + return _u +} + +// SetNillableTokenHash sets the "token_hash" field if the given value is not nil. +func (_u *BrokerJoinTokenUpdate) SetNillableTokenHash(v *string) *BrokerJoinTokenUpdate { + if v != nil { + _u.SetTokenHash(*v) + } + return _u +} + +// SetExpiresAt sets the "expires_at" field. +func (_u *BrokerJoinTokenUpdate) SetExpiresAt(v time.Time) *BrokerJoinTokenUpdate { + _u.mutation.SetExpiresAt(v) + return _u +} + +// SetNillableExpiresAt sets the "expires_at" field if the given value is not nil. +func (_u *BrokerJoinTokenUpdate) SetNillableExpiresAt(v *time.Time) *BrokerJoinTokenUpdate { + if v != nil { + _u.SetExpiresAt(*v) + } + return _u +} + +// SetCreatedBy sets the "created_by" field. +func (_u *BrokerJoinTokenUpdate) SetCreatedBy(v string) *BrokerJoinTokenUpdate { + _u.mutation.SetCreatedBy(v) + return _u +} + +// SetNillableCreatedBy sets the "created_by" field if the given value is not nil. +func (_u *BrokerJoinTokenUpdate) SetNillableCreatedBy(v *string) *BrokerJoinTokenUpdate { + if v != nil { + _u.SetCreatedBy(*v) + } + return _u +} + +// Mutation returns the BrokerJoinTokenMutation object of the builder. +func (_u *BrokerJoinTokenUpdate) Mutation() *BrokerJoinTokenMutation { + return _u.mutation +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (_u *BrokerJoinTokenUpdate) Save(ctx context.Context) (int, error) { + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *BrokerJoinTokenUpdate) SaveX(ctx context.Context) int { + affected, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (_u *BrokerJoinTokenUpdate) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *BrokerJoinTokenUpdate) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_u *BrokerJoinTokenUpdate) check() error { + if v, ok := _u.mutation.TokenHash(); ok { + if err := brokerjointoken.TokenHashValidator(v); err != nil { + return &ValidationError{Name: "token_hash", err: fmt.Errorf(`ent: validator failed for field "BrokerJoinToken.token_hash": %w`, err)} + } + } + if v, ok := _u.mutation.CreatedBy(); ok { + if err := brokerjointoken.CreatedByValidator(v); err != nil { + return &ValidationError{Name: "created_by", err: fmt.Errorf(`ent: validator failed for field "BrokerJoinToken.created_by": %w`, err)} + } + } + return nil +} + +func (_u *BrokerJoinTokenUpdate) sqlSave(ctx context.Context) (_node int, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(brokerjointoken.Table, brokerjointoken.Columns, sqlgraph.NewFieldSpec(brokerjointoken.FieldID, field.TypeUUID)) + if ps := _u.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := _u.mutation.TokenHash(); ok { + _spec.SetField(brokerjointoken.FieldTokenHash, field.TypeString, value) + } + if value, ok := _u.mutation.ExpiresAt(); ok { + _spec.SetField(brokerjointoken.FieldExpiresAt, field.TypeTime, value) + } + if value, ok := _u.mutation.CreatedBy(); ok { + _spec.SetField(brokerjointoken.FieldCreatedBy, field.TypeString, value) + } + if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{brokerjointoken.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + _u.mutation.done = true + return _node, nil +} + +// BrokerJoinTokenUpdateOne is the builder for updating a single BrokerJoinToken entity. +type BrokerJoinTokenUpdateOne struct { + config + fields []string + hooks []Hook + mutation *BrokerJoinTokenMutation +} + +// SetTokenHash sets the "token_hash" field. +func (_u *BrokerJoinTokenUpdateOne) SetTokenHash(v string) *BrokerJoinTokenUpdateOne { + _u.mutation.SetTokenHash(v) + return _u +} + +// SetNillableTokenHash sets the "token_hash" field if the given value is not nil. +func (_u *BrokerJoinTokenUpdateOne) SetNillableTokenHash(v *string) *BrokerJoinTokenUpdateOne { + if v != nil { + _u.SetTokenHash(*v) + } + return _u +} + +// SetExpiresAt sets the "expires_at" field. +func (_u *BrokerJoinTokenUpdateOne) SetExpiresAt(v time.Time) *BrokerJoinTokenUpdateOne { + _u.mutation.SetExpiresAt(v) + return _u +} + +// SetNillableExpiresAt sets the "expires_at" field if the given value is not nil. +func (_u *BrokerJoinTokenUpdateOne) SetNillableExpiresAt(v *time.Time) *BrokerJoinTokenUpdateOne { + if v != nil { + _u.SetExpiresAt(*v) + } + return _u +} + +// SetCreatedBy sets the "created_by" field. +func (_u *BrokerJoinTokenUpdateOne) SetCreatedBy(v string) *BrokerJoinTokenUpdateOne { + _u.mutation.SetCreatedBy(v) + return _u +} + +// SetNillableCreatedBy sets the "created_by" field if the given value is not nil. +func (_u *BrokerJoinTokenUpdateOne) SetNillableCreatedBy(v *string) *BrokerJoinTokenUpdateOne { + if v != nil { + _u.SetCreatedBy(*v) + } + return _u +} + +// Mutation returns the BrokerJoinTokenMutation object of the builder. +func (_u *BrokerJoinTokenUpdateOne) Mutation() *BrokerJoinTokenMutation { + return _u.mutation +} + +// Where appends a list predicates to the BrokerJoinTokenUpdate builder. +func (_u *BrokerJoinTokenUpdateOne) Where(ps ...predicate.BrokerJoinToken) *BrokerJoinTokenUpdateOne { + _u.mutation.Where(ps...) + return _u +} + +// Select allows selecting one or more fields (columns) of the returned entity. +// The default is selecting all fields defined in the entity schema. +func (_u *BrokerJoinTokenUpdateOne) Select(field string, fields ...string) *BrokerJoinTokenUpdateOne { + _u.fields = append([]string{field}, fields...) + return _u +} + +// Save executes the query and returns the updated BrokerJoinToken entity. +func (_u *BrokerJoinTokenUpdateOne) Save(ctx context.Context) (*BrokerJoinToken, error) { + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *BrokerJoinTokenUpdateOne) SaveX(ctx context.Context) *BrokerJoinToken { + node, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (_u *BrokerJoinTokenUpdateOne) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *BrokerJoinTokenUpdateOne) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_u *BrokerJoinTokenUpdateOne) check() error { + if v, ok := _u.mutation.TokenHash(); ok { + if err := brokerjointoken.TokenHashValidator(v); err != nil { + return &ValidationError{Name: "token_hash", err: fmt.Errorf(`ent: validator failed for field "BrokerJoinToken.token_hash": %w`, err)} + } + } + if v, ok := _u.mutation.CreatedBy(); ok { + if err := brokerjointoken.CreatedByValidator(v); err != nil { + return &ValidationError{Name: "created_by", err: fmt.Errorf(`ent: validator failed for field "BrokerJoinToken.created_by": %w`, err)} + } + } + return nil +} + +func (_u *BrokerJoinTokenUpdateOne) sqlSave(ctx context.Context) (_node *BrokerJoinToken, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(brokerjointoken.Table, brokerjointoken.Columns, sqlgraph.NewFieldSpec(brokerjointoken.FieldID, field.TypeUUID)) + id, ok := _u.mutation.ID() + if !ok { + return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "BrokerJoinToken.id" for update`)} + } + _spec.Node.ID.Value = id + if fields := _u.fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, brokerjointoken.FieldID) + for _, f := range fields { + if !brokerjointoken.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + if f != brokerjointoken.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, f) + } + } + } + if ps := _u.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := _u.mutation.TokenHash(); ok { + _spec.SetField(brokerjointoken.FieldTokenHash, field.TypeString, value) + } + if value, ok := _u.mutation.ExpiresAt(); ok { + _spec.SetField(brokerjointoken.FieldExpiresAt, field.TypeTime, value) + } + if value, ok := _u.mutation.CreatedBy(); ok { + _spec.SetField(brokerjointoken.FieldCreatedBy, field.TypeString, value) + } + _node = &BrokerJoinToken{config: _u.config} + _spec.Assign = _node.assignValues + _spec.ScanValues = _node.scanValues + if err = sqlgraph.UpdateNode(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{brokerjointoken.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + _u.mutation.done = true + return _node, nil +} diff --git a/pkg/ent/brokersecret.go b/pkg/ent/brokersecret.go new file mode 100644 index 000000000..7547a5729 --- /dev/null +++ b/pkg/ent/brokersecret.go @@ -0,0 +1,169 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "fmt" + "strings" + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "github.com/GoogleCloudPlatform/scion/pkg/ent/brokersecret" + "github.com/google/uuid" +) + +// BrokerSecret is the model entity for the BrokerSecret schema. +type BrokerSecret struct { + config `json:"-"` + // ID of the ent. + ID uuid.UUID `json:"id,omitempty"` + // SecretKey holds the value of the "secret_key" field. + SecretKey []byte `json:"-"` + // Algorithm holds the value of the "algorithm" field. + Algorithm string `json:"algorithm,omitempty"` + // RotatedAt holds the value of the "rotated_at" field. + RotatedAt *time.Time `json:"rotated_at,omitempty"` + // ExpiresAt holds the value of the "expires_at" field. + ExpiresAt *time.Time `json:"expires_at,omitempty"` + // Status holds the value of the "status" field. + Status string `json:"status,omitempty"` + // Created holds the value of the "created" field. + Created time.Time `json:"created,omitempty"` + selectValues sql.SelectValues +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*BrokerSecret) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case brokersecret.FieldSecretKey: + values[i] = new([]byte) + case brokersecret.FieldAlgorithm, brokersecret.FieldStatus: + values[i] = new(sql.NullString) + case brokersecret.FieldRotatedAt, brokersecret.FieldExpiresAt, brokersecret.FieldCreated: + values[i] = new(sql.NullTime) + case brokersecret.FieldID: + values[i] = new(uuid.UUID) + default: + values[i] = new(sql.UnknownType) + } + } + return values, nil +} + +// assignValues assigns the values that were returned from sql.Rows (after scanning) +// to the BrokerSecret fields. +func (_m *BrokerSecret) assignValues(columns []string, values []any) error { + if m, n := len(values), len(columns); m < n { + return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) + } + for i := range columns { + switch columns[i] { + case brokersecret.FieldID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field id", values[i]) + } else if value != nil { + _m.ID = *value + } + case brokersecret.FieldSecretKey: + if value, ok := values[i].(*[]byte); !ok { + return fmt.Errorf("unexpected type %T for field secret_key", values[i]) + } else if value != nil { + _m.SecretKey = *value + } + case brokersecret.FieldAlgorithm: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field algorithm", values[i]) + } else if value.Valid { + _m.Algorithm = value.String + } + case brokersecret.FieldRotatedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field rotated_at", values[i]) + } else if value.Valid { + _m.RotatedAt = new(time.Time) + *_m.RotatedAt = value.Time + } + case brokersecret.FieldExpiresAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field expires_at", values[i]) + } else if value.Valid { + _m.ExpiresAt = new(time.Time) + *_m.ExpiresAt = value.Time + } + case brokersecret.FieldStatus: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field status", values[i]) + } else if value.Valid { + _m.Status = value.String + } + case brokersecret.FieldCreated: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field created", values[i]) + } else if value.Valid { + _m.Created = value.Time + } + default: + _m.selectValues.Set(columns[i], values[i]) + } + } + return nil +} + +// Value returns the ent.Value that was dynamically selected and assigned to the BrokerSecret. +// This includes values selected through modifiers, order, etc. +func (_m *BrokerSecret) Value(name string) (ent.Value, error) { + return _m.selectValues.Get(name) +} + +// Update returns a builder for updating this BrokerSecret. +// Note that you need to call BrokerSecret.Unwrap() before calling this method if this BrokerSecret +// was returned from a transaction, and the transaction was committed or rolled back. +func (_m *BrokerSecret) Update() *BrokerSecretUpdateOne { + return NewBrokerSecretClient(_m.config).UpdateOne(_m) +} + +// Unwrap unwraps the BrokerSecret entity that was returned from a transaction after it was closed, +// so that all future queries will be executed through the driver which created the transaction. +func (_m *BrokerSecret) Unwrap() *BrokerSecret { + _tx, ok := _m.config.driver.(*txDriver) + if !ok { + panic("ent: BrokerSecret is not a transactional entity") + } + _m.config.driver = _tx.drv + return _m +} + +// String implements the fmt.Stringer. +func (_m *BrokerSecret) String() string { + var builder strings.Builder + builder.WriteString("BrokerSecret(") + builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID)) + builder.WriteString("secret_key=") + builder.WriteString(", ") + builder.WriteString("algorithm=") + builder.WriteString(_m.Algorithm) + builder.WriteString(", ") + if v := _m.RotatedAt; v != nil { + builder.WriteString("rotated_at=") + builder.WriteString(v.Format(time.ANSIC)) + } + builder.WriteString(", ") + if v := _m.ExpiresAt; v != nil { + builder.WriteString("expires_at=") + builder.WriteString(v.Format(time.ANSIC)) + } + builder.WriteString(", ") + builder.WriteString("status=") + builder.WriteString(_m.Status) + builder.WriteString(", ") + builder.WriteString("created=") + builder.WriteString(_m.Created.Format(time.ANSIC)) + builder.WriteByte(')') + return builder.String() +} + +// BrokerSecrets is a parsable slice of BrokerSecret. +type BrokerSecrets []*BrokerSecret diff --git a/pkg/ent/brokersecret/brokersecret.go b/pkg/ent/brokersecret/brokersecret.go new file mode 100644 index 000000000..f0a09b2c8 --- /dev/null +++ b/pkg/ent/brokersecret/brokersecret.go @@ -0,0 +1,95 @@ +// Code generated by ent, DO NOT EDIT. + +package brokersecret + +import ( + "time" + + "entgo.io/ent/dialect/sql" +) + +const ( + // Label holds the string label denoting the brokersecret type in the database. + Label = "broker_secret" + // FieldID holds the string denoting the id field in the database. + FieldID = "broker_id" + // FieldSecretKey holds the string denoting the secret_key field in the database. + FieldSecretKey = "secret_key" + // FieldAlgorithm holds the string denoting the algorithm field in the database. + FieldAlgorithm = "algorithm" + // FieldRotatedAt holds the string denoting the rotated_at field in the database. + FieldRotatedAt = "rotated_at" + // FieldExpiresAt holds the string denoting the expires_at field in the database. + FieldExpiresAt = "expires_at" + // FieldStatus holds the string denoting the status field in the database. + FieldStatus = "status" + // FieldCreated holds the string denoting the created field in the database. + FieldCreated = "created" + // Table holds the table name of the brokersecret in the database. + Table = "broker_secrets" +) + +// Columns holds all SQL columns for brokersecret fields. +var Columns = []string{ + FieldID, + FieldSecretKey, + FieldAlgorithm, + FieldRotatedAt, + FieldExpiresAt, + FieldStatus, + FieldCreated, +} + +// ValidColumn reports if the column name is valid (part of the table columns). +func ValidColumn(column string) bool { + for i := range Columns { + if column == Columns[i] { + return true + } + } + return false +} + +var ( + // SecretKeyValidator is a validator for the "secret_key" field. It is called by the builders before save. + SecretKeyValidator func([]byte) error + // DefaultAlgorithm holds the default value on creation for the "algorithm" field. + DefaultAlgorithm string + // DefaultStatus holds the default value on creation for the "status" field. + DefaultStatus string + // DefaultCreated holds the default value on creation for the "created" field. + DefaultCreated func() time.Time +) + +// OrderOption defines the ordering options for the BrokerSecret queries. +type OrderOption func(*sql.Selector) + +// ByID orders the results by the id field. +func ByID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldID, opts...).ToFunc() +} + +// ByAlgorithm orders the results by the algorithm field. +func ByAlgorithm(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldAlgorithm, opts...).ToFunc() +} + +// ByRotatedAt orders the results by the rotated_at field. +func ByRotatedAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldRotatedAt, opts...).ToFunc() +} + +// ByExpiresAt orders the results by the expires_at field. +func ByExpiresAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldExpiresAt, opts...).ToFunc() +} + +// ByStatus orders the results by the status field. +func ByStatus(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldStatus, opts...).ToFunc() +} + +// ByCreated orders the results by the created field. +func ByCreated(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCreated, opts...).ToFunc() +} diff --git a/pkg/ent/brokersecret/where.go b/pkg/ent/brokersecret/where.go new file mode 100644 index 000000000..c5e7edd74 --- /dev/null +++ b/pkg/ent/brokersecret/where.go @@ -0,0 +1,411 @@ +// Code generated by ent, DO NOT EDIT. + +package brokersecret + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/google/uuid" +) + +// ID filters vertices based on their ID field. +func ID(id uuid.UUID) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldEQ(FieldID, id)) +} + +// IDEQ applies the EQ predicate on the ID field. +func IDEQ(id uuid.UUID) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldEQ(FieldID, id)) +} + +// IDNEQ applies the NEQ predicate on the ID field. +func IDNEQ(id uuid.UUID) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldNEQ(FieldID, id)) +} + +// IDIn applies the In predicate on the ID field. +func IDIn(ids ...uuid.UUID) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldIn(FieldID, ids...)) +} + +// IDNotIn applies the NotIn predicate on the ID field. +func IDNotIn(ids ...uuid.UUID) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldNotIn(FieldID, ids...)) +} + +// IDGT applies the GT predicate on the ID field. +func IDGT(id uuid.UUID) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldGT(FieldID, id)) +} + +// IDGTE applies the GTE predicate on the ID field. +func IDGTE(id uuid.UUID) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldGTE(FieldID, id)) +} + +// IDLT applies the LT predicate on the ID field. +func IDLT(id uuid.UUID) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldLT(FieldID, id)) +} + +// IDLTE applies the LTE predicate on the ID field. +func IDLTE(id uuid.UUID) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldLTE(FieldID, id)) +} + +// SecretKey applies equality check predicate on the "secret_key" field. It's identical to SecretKeyEQ. +func SecretKey(v []byte) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldEQ(FieldSecretKey, v)) +} + +// Algorithm applies equality check predicate on the "algorithm" field. It's identical to AlgorithmEQ. +func Algorithm(v string) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldEQ(FieldAlgorithm, v)) +} + +// RotatedAt applies equality check predicate on the "rotated_at" field. It's identical to RotatedAtEQ. +func RotatedAt(v time.Time) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldEQ(FieldRotatedAt, v)) +} + +// ExpiresAt applies equality check predicate on the "expires_at" field. It's identical to ExpiresAtEQ. +func ExpiresAt(v time.Time) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldEQ(FieldExpiresAt, v)) +} + +// Status applies equality check predicate on the "status" field. It's identical to StatusEQ. +func Status(v string) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldEQ(FieldStatus, v)) +} + +// Created applies equality check predicate on the "created" field. It's identical to CreatedEQ. +func Created(v time.Time) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldEQ(FieldCreated, v)) +} + +// SecretKeyEQ applies the EQ predicate on the "secret_key" field. +func SecretKeyEQ(v []byte) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldEQ(FieldSecretKey, v)) +} + +// SecretKeyNEQ applies the NEQ predicate on the "secret_key" field. +func SecretKeyNEQ(v []byte) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldNEQ(FieldSecretKey, v)) +} + +// SecretKeyIn applies the In predicate on the "secret_key" field. +func SecretKeyIn(vs ...[]byte) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldIn(FieldSecretKey, vs...)) +} + +// SecretKeyNotIn applies the NotIn predicate on the "secret_key" field. +func SecretKeyNotIn(vs ...[]byte) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldNotIn(FieldSecretKey, vs...)) +} + +// SecretKeyGT applies the GT predicate on the "secret_key" field. +func SecretKeyGT(v []byte) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldGT(FieldSecretKey, v)) +} + +// SecretKeyGTE applies the GTE predicate on the "secret_key" field. +func SecretKeyGTE(v []byte) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldGTE(FieldSecretKey, v)) +} + +// SecretKeyLT applies the LT predicate on the "secret_key" field. +func SecretKeyLT(v []byte) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldLT(FieldSecretKey, v)) +} + +// SecretKeyLTE applies the LTE predicate on the "secret_key" field. +func SecretKeyLTE(v []byte) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldLTE(FieldSecretKey, v)) +} + +// AlgorithmEQ applies the EQ predicate on the "algorithm" field. +func AlgorithmEQ(v string) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldEQ(FieldAlgorithm, v)) +} + +// AlgorithmNEQ applies the NEQ predicate on the "algorithm" field. +func AlgorithmNEQ(v string) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldNEQ(FieldAlgorithm, v)) +} + +// AlgorithmIn applies the In predicate on the "algorithm" field. +func AlgorithmIn(vs ...string) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldIn(FieldAlgorithm, vs...)) +} + +// AlgorithmNotIn applies the NotIn predicate on the "algorithm" field. +func AlgorithmNotIn(vs ...string) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldNotIn(FieldAlgorithm, vs...)) +} + +// AlgorithmGT applies the GT predicate on the "algorithm" field. +func AlgorithmGT(v string) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldGT(FieldAlgorithm, v)) +} + +// AlgorithmGTE applies the GTE predicate on the "algorithm" field. +func AlgorithmGTE(v string) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldGTE(FieldAlgorithm, v)) +} + +// AlgorithmLT applies the LT predicate on the "algorithm" field. +func AlgorithmLT(v string) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldLT(FieldAlgorithm, v)) +} + +// AlgorithmLTE applies the LTE predicate on the "algorithm" field. +func AlgorithmLTE(v string) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldLTE(FieldAlgorithm, v)) +} + +// AlgorithmContains applies the Contains predicate on the "algorithm" field. +func AlgorithmContains(v string) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldContains(FieldAlgorithm, v)) +} + +// AlgorithmHasPrefix applies the HasPrefix predicate on the "algorithm" field. +func AlgorithmHasPrefix(v string) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldHasPrefix(FieldAlgorithm, v)) +} + +// AlgorithmHasSuffix applies the HasSuffix predicate on the "algorithm" field. +func AlgorithmHasSuffix(v string) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldHasSuffix(FieldAlgorithm, v)) +} + +// AlgorithmEqualFold applies the EqualFold predicate on the "algorithm" field. +func AlgorithmEqualFold(v string) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldEqualFold(FieldAlgorithm, v)) +} + +// AlgorithmContainsFold applies the ContainsFold predicate on the "algorithm" field. +func AlgorithmContainsFold(v string) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldContainsFold(FieldAlgorithm, v)) +} + +// RotatedAtEQ applies the EQ predicate on the "rotated_at" field. +func RotatedAtEQ(v time.Time) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldEQ(FieldRotatedAt, v)) +} + +// RotatedAtNEQ applies the NEQ predicate on the "rotated_at" field. +func RotatedAtNEQ(v time.Time) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldNEQ(FieldRotatedAt, v)) +} + +// RotatedAtIn applies the In predicate on the "rotated_at" field. +func RotatedAtIn(vs ...time.Time) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldIn(FieldRotatedAt, vs...)) +} + +// RotatedAtNotIn applies the NotIn predicate on the "rotated_at" field. +func RotatedAtNotIn(vs ...time.Time) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldNotIn(FieldRotatedAt, vs...)) +} + +// RotatedAtGT applies the GT predicate on the "rotated_at" field. +func RotatedAtGT(v time.Time) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldGT(FieldRotatedAt, v)) +} + +// RotatedAtGTE applies the GTE predicate on the "rotated_at" field. +func RotatedAtGTE(v time.Time) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldGTE(FieldRotatedAt, v)) +} + +// RotatedAtLT applies the LT predicate on the "rotated_at" field. +func RotatedAtLT(v time.Time) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldLT(FieldRotatedAt, v)) +} + +// RotatedAtLTE applies the LTE predicate on the "rotated_at" field. +func RotatedAtLTE(v time.Time) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldLTE(FieldRotatedAt, v)) +} + +// RotatedAtIsNil applies the IsNil predicate on the "rotated_at" field. +func RotatedAtIsNil() predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldIsNull(FieldRotatedAt)) +} + +// RotatedAtNotNil applies the NotNil predicate on the "rotated_at" field. +func RotatedAtNotNil() predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldNotNull(FieldRotatedAt)) +} + +// ExpiresAtEQ applies the EQ predicate on the "expires_at" field. +func ExpiresAtEQ(v time.Time) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldEQ(FieldExpiresAt, v)) +} + +// ExpiresAtNEQ applies the NEQ predicate on the "expires_at" field. +func ExpiresAtNEQ(v time.Time) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldNEQ(FieldExpiresAt, v)) +} + +// ExpiresAtIn applies the In predicate on the "expires_at" field. +func ExpiresAtIn(vs ...time.Time) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldIn(FieldExpiresAt, vs...)) +} + +// ExpiresAtNotIn applies the NotIn predicate on the "expires_at" field. +func ExpiresAtNotIn(vs ...time.Time) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldNotIn(FieldExpiresAt, vs...)) +} + +// ExpiresAtGT applies the GT predicate on the "expires_at" field. +func ExpiresAtGT(v time.Time) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldGT(FieldExpiresAt, v)) +} + +// ExpiresAtGTE applies the GTE predicate on the "expires_at" field. +func ExpiresAtGTE(v time.Time) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldGTE(FieldExpiresAt, v)) +} + +// ExpiresAtLT applies the LT predicate on the "expires_at" field. +func ExpiresAtLT(v time.Time) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldLT(FieldExpiresAt, v)) +} + +// ExpiresAtLTE applies the LTE predicate on the "expires_at" field. +func ExpiresAtLTE(v time.Time) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldLTE(FieldExpiresAt, v)) +} + +// ExpiresAtIsNil applies the IsNil predicate on the "expires_at" field. +func ExpiresAtIsNil() predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldIsNull(FieldExpiresAt)) +} + +// ExpiresAtNotNil applies the NotNil predicate on the "expires_at" field. +func ExpiresAtNotNil() predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldNotNull(FieldExpiresAt)) +} + +// StatusEQ applies the EQ predicate on the "status" field. +func StatusEQ(v string) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldEQ(FieldStatus, v)) +} + +// StatusNEQ applies the NEQ predicate on the "status" field. +func StatusNEQ(v string) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldNEQ(FieldStatus, v)) +} + +// StatusIn applies the In predicate on the "status" field. +func StatusIn(vs ...string) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldIn(FieldStatus, vs...)) +} + +// StatusNotIn applies the NotIn predicate on the "status" field. +func StatusNotIn(vs ...string) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldNotIn(FieldStatus, vs...)) +} + +// StatusGT applies the GT predicate on the "status" field. +func StatusGT(v string) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldGT(FieldStatus, v)) +} + +// StatusGTE applies the GTE predicate on the "status" field. +func StatusGTE(v string) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldGTE(FieldStatus, v)) +} + +// StatusLT applies the LT predicate on the "status" field. +func StatusLT(v string) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldLT(FieldStatus, v)) +} + +// StatusLTE applies the LTE predicate on the "status" field. +func StatusLTE(v string) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldLTE(FieldStatus, v)) +} + +// StatusContains applies the Contains predicate on the "status" field. +func StatusContains(v string) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldContains(FieldStatus, v)) +} + +// StatusHasPrefix applies the HasPrefix predicate on the "status" field. +func StatusHasPrefix(v string) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldHasPrefix(FieldStatus, v)) +} + +// StatusHasSuffix applies the HasSuffix predicate on the "status" field. +func StatusHasSuffix(v string) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldHasSuffix(FieldStatus, v)) +} + +// StatusEqualFold applies the EqualFold predicate on the "status" field. +func StatusEqualFold(v string) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldEqualFold(FieldStatus, v)) +} + +// StatusContainsFold applies the ContainsFold predicate on the "status" field. +func StatusContainsFold(v string) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldContainsFold(FieldStatus, v)) +} + +// CreatedEQ applies the EQ predicate on the "created" field. +func CreatedEQ(v time.Time) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldEQ(FieldCreated, v)) +} + +// CreatedNEQ applies the NEQ predicate on the "created" field. +func CreatedNEQ(v time.Time) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldNEQ(FieldCreated, v)) +} + +// CreatedIn applies the In predicate on the "created" field. +func CreatedIn(vs ...time.Time) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldIn(FieldCreated, vs...)) +} + +// CreatedNotIn applies the NotIn predicate on the "created" field. +func CreatedNotIn(vs ...time.Time) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldNotIn(FieldCreated, vs...)) +} + +// CreatedGT applies the GT predicate on the "created" field. +func CreatedGT(v time.Time) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldGT(FieldCreated, v)) +} + +// CreatedGTE applies the GTE predicate on the "created" field. +func CreatedGTE(v time.Time) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldGTE(FieldCreated, v)) +} + +// CreatedLT applies the LT predicate on the "created" field. +func CreatedLT(v time.Time) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldLT(FieldCreated, v)) +} + +// CreatedLTE applies the LTE predicate on the "created" field. +func CreatedLTE(v time.Time) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.FieldLTE(FieldCreated, v)) +} + +// And groups predicates with the AND operator between them. +func And(predicates ...predicate.BrokerSecret) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.AndPredicates(predicates...)) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.BrokerSecret) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.OrPredicates(predicates...)) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.BrokerSecret) predicate.BrokerSecret { + return predicate.BrokerSecret(sql.NotPredicates(p)) +} diff --git a/pkg/ent/brokersecret_create.go b/pkg/ent/brokersecret_create.go new file mode 100644 index 000000000..cd390921a --- /dev/null +++ b/pkg/ent/brokersecret_create.go @@ -0,0 +1,819 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/brokersecret" + "github.com/google/uuid" +) + +// BrokerSecretCreate is the builder for creating a BrokerSecret entity. +type BrokerSecretCreate struct { + config + mutation *BrokerSecretMutation + hooks []Hook + conflict []sql.ConflictOption +} + +// SetSecretKey sets the "secret_key" field. +func (_c *BrokerSecretCreate) SetSecretKey(v []byte) *BrokerSecretCreate { + _c.mutation.SetSecretKey(v) + return _c +} + +// SetAlgorithm sets the "algorithm" field. +func (_c *BrokerSecretCreate) SetAlgorithm(v string) *BrokerSecretCreate { + _c.mutation.SetAlgorithm(v) + return _c +} + +// SetNillableAlgorithm sets the "algorithm" field if the given value is not nil. +func (_c *BrokerSecretCreate) SetNillableAlgorithm(v *string) *BrokerSecretCreate { + if v != nil { + _c.SetAlgorithm(*v) + } + return _c +} + +// SetRotatedAt sets the "rotated_at" field. +func (_c *BrokerSecretCreate) SetRotatedAt(v time.Time) *BrokerSecretCreate { + _c.mutation.SetRotatedAt(v) + return _c +} + +// SetNillableRotatedAt sets the "rotated_at" field if the given value is not nil. +func (_c *BrokerSecretCreate) SetNillableRotatedAt(v *time.Time) *BrokerSecretCreate { + if v != nil { + _c.SetRotatedAt(*v) + } + return _c +} + +// SetExpiresAt sets the "expires_at" field. +func (_c *BrokerSecretCreate) SetExpiresAt(v time.Time) *BrokerSecretCreate { + _c.mutation.SetExpiresAt(v) + return _c +} + +// SetNillableExpiresAt sets the "expires_at" field if the given value is not nil. +func (_c *BrokerSecretCreate) SetNillableExpiresAt(v *time.Time) *BrokerSecretCreate { + if v != nil { + _c.SetExpiresAt(*v) + } + return _c +} + +// SetStatus sets the "status" field. +func (_c *BrokerSecretCreate) SetStatus(v string) *BrokerSecretCreate { + _c.mutation.SetStatus(v) + return _c +} + +// SetNillableStatus sets the "status" field if the given value is not nil. +func (_c *BrokerSecretCreate) SetNillableStatus(v *string) *BrokerSecretCreate { + if v != nil { + _c.SetStatus(*v) + } + return _c +} + +// SetCreated sets the "created" field. +func (_c *BrokerSecretCreate) SetCreated(v time.Time) *BrokerSecretCreate { + _c.mutation.SetCreated(v) + return _c +} + +// SetNillableCreated sets the "created" field if the given value is not nil. +func (_c *BrokerSecretCreate) SetNillableCreated(v *time.Time) *BrokerSecretCreate { + if v != nil { + _c.SetCreated(*v) + } + return _c +} + +// SetID sets the "id" field. +func (_c *BrokerSecretCreate) SetID(v uuid.UUID) *BrokerSecretCreate { + _c.mutation.SetID(v) + return _c +} + +// Mutation returns the BrokerSecretMutation object of the builder. +func (_c *BrokerSecretCreate) Mutation() *BrokerSecretMutation { + return _c.mutation +} + +// Save creates the BrokerSecret in the database. +func (_c *BrokerSecretCreate) Save(ctx context.Context) (*BrokerSecret, error) { + _c.defaults() + return withHooks(ctx, _c.sqlSave, _c.mutation, _c.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (_c *BrokerSecretCreate) SaveX(ctx context.Context) *BrokerSecret { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *BrokerSecretCreate) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *BrokerSecretCreate) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_c *BrokerSecretCreate) defaults() { + if _, ok := _c.mutation.Algorithm(); !ok { + v := brokersecret.DefaultAlgorithm + _c.mutation.SetAlgorithm(v) + } + if _, ok := _c.mutation.Status(); !ok { + v := brokersecret.DefaultStatus + _c.mutation.SetStatus(v) + } + if _, ok := _c.mutation.Created(); !ok { + v := brokersecret.DefaultCreated() + _c.mutation.SetCreated(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_c *BrokerSecretCreate) check() error { + if _, ok := _c.mutation.SecretKey(); !ok { + return &ValidationError{Name: "secret_key", err: errors.New(`ent: missing required field "BrokerSecret.secret_key"`)} + } + if v, ok := _c.mutation.SecretKey(); ok { + if err := brokersecret.SecretKeyValidator(v); err != nil { + return &ValidationError{Name: "secret_key", err: fmt.Errorf(`ent: validator failed for field "BrokerSecret.secret_key": %w`, err)} + } + } + if _, ok := _c.mutation.Algorithm(); !ok { + return &ValidationError{Name: "algorithm", err: errors.New(`ent: missing required field "BrokerSecret.algorithm"`)} + } + if _, ok := _c.mutation.Status(); !ok { + return &ValidationError{Name: "status", err: errors.New(`ent: missing required field "BrokerSecret.status"`)} + } + if _, ok := _c.mutation.Created(); !ok { + return &ValidationError{Name: "created", err: errors.New(`ent: missing required field "BrokerSecret.created"`)} + } + return nil +} + +func (_c *BrokerSecretCreate) sqlSave(ctx context.Context) (*BrokerSecret, error) { + if err := _c.check(); err != nil { + return nil, err + } + _node, _spec := _c.createSpec() + if err := sqlgraph.CreateNode(ctx, _c.driver, _spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + if _spec.ID.Value != nil { + if id, ok := _spec.ID.Value.(*uuid.UUID); ok { + _node.ID = *id + } else if err := _node.ID.Scan(_spec.ID.Value); err != nil { + return nil, err + } + } + _c.mutation.id = &_node.ID + _c.mutation.done = true + return _node, nil +} + +func (_c *BrokerSecretCreate) createSpec() (*BrokerSecret, *sqlgraph.CreateSpec) { + var ( + _node = &BrokerSecret{config: _c.config} + _spec = sqlgraph.NewCreateSpec(brokersecret.Table, sqlgraph.NewFieldSpec(brokersecret.FieldID, field.TypeUUID)) + ) + _spec.OnConflict = _c.conflict + if id, ok := _c.mutation.ID(); ok { + _node.ID = id + _spec.ID.Value = &id + } + if value, ok := _c.mutation.SecretKey(); ok { + _spec.SetField(brokersecret.FieldSecretKey, field.TypeBytes, value) + _node.SecretKey = value + } + if value, ok := _c.mutation.Algorithm(); ok { + _spec.SetField(brokersecret.FieldAlgorithm, field.TypeString, value) + _node.Algorithm = value + } + if value, ok := _c.mutation.RotatedAt(); ok { + _spec.SetField(brokersecret.FieldRotatedAt, field.TypeTime, value) + _node.RotatedAt = &value + } + if value, ok := _c.mutation.ExpiresAt(); ok { + _spec.SetField(brokersecret.FieldExpiresAt, field.TypeTime, value) + _node.ExpiresAt = &value + } + if value, ok := _c.mutation.Status(); ok { + _spec.SetField(brokersecret.FieldStatus, field.TypeString, value) + _node.Status = value + } + if value, ok := _c.mutation.Created(); ok { + _spec.SetField(brokersecret.FieldCreated, field.TypeTime, value) + _node.Created = value + } + return _node, _spec +} + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.BrokerSecret.Create(). +// SetSecretKey(v). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.BrokerSecretUpsert) { +// SetSecretKey(v+v). +// }). +// Exec(ctx) +func (_c *BrokerSecretCreate) OnConflict(opts ...sql.ConflictOption) *BrokerSecretUpsertOne { + _c.conflict = opts + return &BrokerSecretUpsertOne{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.BrokerSecret.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *BrokerSecretCreate) OnConflictColumns(columns ...string) *BrokerSecretUpsertOne { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &BrokerSecretUpsertOne{ + create: _c, + } +} + +type ( + // BrokerSecretUpsertOne is the builder for "upsert"-ing + // one BrokerSecret node. + BrokerSecretUpsertOne struct { + create *BrokerSecretCreate + } + + // BrokerSecretUpsert is the "OnConflict" setter. + BrokerSecretUpsert struct { + *sql.UpdateSet + } +) + +// SetSecretKey sets the "secret_key" field. +func (u *BrokerSecretUpsert) SetSecretKey(v []byte) *BrokerSecretUpsert { + u.Set(brokersecret.FieldSecretKey, v) + return u +} + +// UpdateSecretKey sets the "secret_key" field to the value that was provided on create. +func (u *BrokerSecretUpsert) UpdateSecretKey() *BrokerSecretUpsert { + u.SetExcluded(brokersecret.FieldSecretKey) + return u +} + +// SetAlgorithm sets the "algorithm" field. +func (u *BrokerSecretUpsert) SetAlgorithm(v string) *BrokerSecretUpsert { + u.Set(brokersecret.FieldAlgorithm, v) + return u +} + +// UpdateAlgorithm sets the "algorithm" field to the value that was provided on create. +func (u *BrokerSecretUpsert) UpdateAlgorithm() *BrokerSecretUpsert { + u.SetExcluded(brokersecret.FieldAlgorithm) + return u +} + +// SetRotatedAt sets the "rotated_at" field. +func (u *BrokerSecretUpsert) SetRotatedAt(v time.Time) *BrokerSecretUpsert { + u.Set(brokersecret.FieldRotatedAt, v) + return u +} + +// UpdateRotatedAt sets the "rotated_at" field to the value that was provided on create. +func (u *BrokerSecretUpsert) UpdateRotatedAt() *BrokerSecretUpsert { + u.SetExcluded(brokersecret.FieldRotatedAt) + return u +} + +// ClearRotatedAt clears the value of the "rotated_at" field. +func (u *BrokerSecretUpsert) ClearRotatedAt() *BrokerSecretUpsert { + u.SetNull(brokersecret.FieldRotatedAt) + return u +} + +// SetExpiresAt sets the "expires_at" field. +func (u *BrokerSecretUpsert) SetExpiresAt(v time.Time) *BrokerSecretUpsert { + u.Set(brokersecret.FieldExpiresAt, v) + return u +} + +// UpdateExpiresAt sets the "expires_at" field to the value that was provided on create. +func (u *BrokerSecretUpsert) UpdateExpiresAt() *BrokerSecretUpsert { + u.SetExcluded(brokersecret.FieldExpiresAt) + return u +} + +// ClearExpiresAt clears the value of the "expires_at" field. +func (u *BrokerSecretUpsert) ClearExpiresAt() *BrokerSecretUpsert { + u.SetNull(brokersecret.FieldExpiresAt) + return u +} + +// SetStatus sets the "status" field. +func (u *BrokerSecretUpsert) SetStatus(v string) *BrokerSecretUpsert { + u.Set(brokersecret.FieldStatus, v) + return u +} + +// UpdateStatus sets the "status" field to the value that was provided on create. +func (u *BrokerSecretUpsert) UpdateStatus() *BrokerSecretUpsert { + u.SetExcluded(brokersecret.FieldStatus) + return u +} + +// UpdateNewValues updates the mutable fields using the new values that were set on create except the ID field. +// Using this option is equivalent to using: +// +// client.BrokerSecret.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(brokersecret.FieldID) +// }), +// ). +// Exec(ctx) +func (u *BrokerSecretUpsertOne) UpdateNewValues() *BrokerSecretUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + if _, exists := u.create.mutation.ID(); exists { + s.SetIgnore(brokersecret.FieldID) + } + if _, exists := u.create.mutation.Created(); exists { + s.SetIgnore(brokersecret.FieldCreated) + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.BrokerSecret.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *BrokerSecretUpsertOne) Ignore() *BrokerSecretUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *BrokerSecretUpsertOne) DoNothing() *BrokerSecretUpsertOne { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the BrokerSecretCreate.OnConflict +// documentation for more info. +func (u *BrokerSecretUpsertOne) Update(set func(*BrokerSecretUpsert)) *BrokerSecretUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&BrokerSecretUpsert{UpdateSet: update}) + })) + return u +} + +// SetSecretKey sets the "secret_key" field. +func (u *BrokerSecretUpsertOne) SetSecretKey(v []byte) *BrokerSecretUpsertOne { + return u.Update(func(s *BrokerSecretUpsert) { + s.SetSecretKey(v) + }) +} + +// UpdateSecretKey sets the "secret_key" field to the value that was provided on create. +func (u *BrokerSecretUpsertOne) UpdateSecretKey() *BrokerSecretUpsertOne { + return u.Update(func(s *BrokerSecretUpsert) { + s.UpdateSecretKey() + }) +} + +// SetAlgorithm sets the "algorithm" field. +func (u *BrokerSecretUpsertOne) SetAlgorithm(v string) *BrokerSecretUpsertOne { + return u.Update(func(s *BrokerSecretUpsert) { + s.SetAlgorithm(v) + }) +} + +// UpdateAlgorithm sets the "algorithm" field to the value that was provided on create. +func (u *BrokerSecretUpsertOne) UpdateAlgorithm() *BrokerSecretUpsertOne { + return u.Update(func(s *BrokerSecretUpsert) { + s.UpdateAlgorithm() + }) +} + +// SetRotatedAt sets the "rotated_at" field. +func (u *BrokerSecretUpsertOne) SetRotatedAt(v time.Time) *BrokerSecretUpsertOne { + return u.Update(func(s *BrokerSecretUpsert) { + s.SetRotatedAt(v) + }) +} + +// UpdateRotatedAt sets the "rotated_at" field to the value that was provided on create. +func (u *BrokerSecretUpsertOne) UpdateRotatedAt() *BrokerSecretUpsertOne { + return u.Update(func(s *BrokerSecretUpsert) { + s.UpdateRotatedAt() + }) +} + +// ClearRotatedAt clears the value of the "rotated_at" field. +func (u *BrokerSecretUpsertOne) ClearRotatedAt() *BrokerSecretUpsertOne { + return u.Update(func(s *BrokerSecretUpsert) { + s.ClearRotatedAt() + }) +} + +// SetExpiresAt sets the "expires_at" field. +func (u *BrokerSecretUpsertOne) SetExpiresAt(v time.Time) *BrokerSecretUpsertOne { + return u.Update(func(s *BrokerSecretUpsert) { + s.SetExpiresAt(v) + }) +} + +// UpdateExpiresAt sets the "expires_at" field to the value that was provided on create. +func (u *BrokerSecretUpsertOne) UpdateExpiresAt() *BrokerSecretUpsertOne { + return u.Update(func(s *BrokerSecretUpsert) { + s.UpdateExpiresAt() + }) +} + +// ClearExpiresAt clears the value of the "expires_at" field. +func (u *BrokerSecretUpsertOne) ClearExpiresAt() *BrokerSecretUpsertOne { + return u.Update(func(s *BrokerSecretUpsert) { + s.ClearExpiresAt() + }) +} + +// SetStatus sets the "status" field. +func (u *BrokerSecretUpsertOne) SetStatus(v string) *BrokerSecretUpsertOne { + return u.Update(func(s *BrokerSecretUpsert) { + s.SetStatus(v) + }) +} + +// UpdateStatus sets the "status" field to the value that was provided on create. +func (u *BrokerSecretUpsertOne) UpdateStatus() *BrokerSecretUpsertOne { + return u.Update(func(s *BrokerSecretUpsert) { + s.UpdateStatus() + }) +} + +// Exec executes the query. +func (u *BrokerSecretUpsertOne) Exec(ctx context.Context) error { + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for BrokerSecretCreate.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *BrokerSecretUpsertOne) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} + +// Exec executes the UPSERT query and returns the inserted/updated ID. +func (u *BrokerSecretUpsertOne) ID(ctx context.Context) (id uuid.UUID, err error) { + if u.create.driver.Dialect() == dialect.MySQL { + // In case of "ON CONFLICT", there is no way to get back non-numeric ID + // fields from the database since MySQL does not support the RETURNING clause. + return id, errors.New("ent: BrokerSecretUpsertOne.ID is not supported by MySQL driver. Use BrokerSecretUpsertOne.Exec instead") + } + node, err := u.create.Save(ctx) + if err != nil { + return id, err + } + return node.ID, nil +} + +// IDX is like ID, but panics if an error occurs. +func (u *BrokerSecretUpsertOne) IDX(ctx context.Context) uuid.UUID { + id, err := u.ID(ctx) + if err != nil { + panic(err) + } + return id +} + +// BrokerSecretCreateBulk is the builder for creating many BrokerSecret entities in bulk. +type BrokerSecretCreateBulk struct { + config + err error + builders []*BrokerSecretCreate + conflict []sql.ConflictOption +} + +// Save creates the BrokerSecret entities in the database. +func (_c *BrokerSecretCreateBulk) Save(ctx context.Context) ([]*BrokerSecret, error) { + if _c.err != nil { + return nil, _c.err + } + specs := make([]*sqlgraph.CreateSpec, len(_c.builders)) + nodes := make([]*BrokerSecret, len(_c.builders)) + mutators := make([]Mutator, len(_c.builders)) + for i := range _c.builders { + func(i int, root context.Context) { + builder := _c.builders[i] + builder.defaults() + var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { + mutation, ok := m.(*BrokerSecretMutation) + if !ok { + return nil, fmt.Errorf("unexpected mutation type %T", m) + } + if err := builder.check(); err != nil { + return nil, err + } + builder.mutation = mutation + var err error + nodes[i], specs[i] = builder.createSpec() + if i < len(mutators)-1 { + _, err = mutators[i+1].Mutate(root, _c.builders[i+1].mutation) + } else { + spec := &sqlgraph.BatchCreateSpec{Nodes: specs} + spec.OnConflict = _c.conflict + // Invoke the actual operation on the latest mutation in the chain. + if err = sqlgraph.BatchCreate(ctx, _c.driver, spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + } + } + if err != nil { + return nil, err + } + mutation.id = &nodes[i].ID + mutation.done = true + return nodes[i], nil + }) + for i := len(builder.hooks) - 1; i >= 0; i-- { + mut = builder.hooks[i](mut) + } + mutators[i] = mut + }(i, ctx) + } + if len(mutators) > 0 { + if _, err := mutators[0].Mutate(ctx, _c.builders[0].mutation); err != nil { + return nil, err + } + } + return nodes, nil +} + +// SaveX is like Save, but panics if an error occurs. +func (_c *BrokerSecretCreateBulk) SaveX(ctx context.Context) []*BrokerSecret { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *BrokerSecretCreateBulk) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *BrokerSecretCreateBulk) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.BrokerSecret.CreateBulk(builders...). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.BrokerSecretUpsert) { +// SetSecretKey(v+v). +// }). +// Exec(ctx) +func (_c *BrokerSecretCreateBulk) OnConflict(opts ...sql.ConflictOption) *BrokerSecretUpsertBulk { + _c.conflict = opts + return &BrokerSecretUpsertBulk{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.BrokerSecret.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *BrokerSecretCreateBulk) OnConflictColumns(columns ...string) *BrokerSecretUpsertBulk { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &BrokerSecretUpsertBulk{ + create: _c, + } +} + +// BrokerSecretUpsertBulk is the builder for "upsert"-ing +// a bulk of BrokerSecret nodes. +type BrokerSecretUpsertBulk struct { + create *BrokerSecretCreateBulk +} + +// UpdateNewValues updates the mutable fields using the new values that +// were set on create. Using this option is equivalent to using: +// +// client.BrokerSecret.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(brokersecret.FieldID) +// }), +// ). +// Exec(ctx) +func (u *BrokerSecretUpsertBulk) UpdateNewValues() *BrokerSecretUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + for _, b := range u.create.builders { + if _, exists := b.mutation.ID(); exists { + s.SetIgnore(brokersecret.FieldID) + } + if _, exists := b.mutation.Created(); exists { + s.SetIgnore(brokersecret.FieldCreated) + } + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.BrokerSecret.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *BrokerSecretUpsertBulk) Ignore() *BrokerSecretUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *BrokerSecretUpsertBulk) DoNothing() *BrokerSecretUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the BrokerSecretCreateBulk.OnConflict +// documentation for more info. +func (u *BrokerSecretUpsertBulk) Update(set func(*BrokerSecretUpsert)) *BrokerSecretUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&BrokerSecretUpsert{UpdateSet: update}) + })) + return u +} + +// SetSecretKey sets the "secret_key" field. +func (u *BrokerSecretUpsertBulk) SetSecretKey(v []byte) *BrokerSecretUpsertBulk { + return u.Update(func(s *BrokerSecretUpsert) { + s.SetSecretKey(v) + }) +} + +// UpdateSecretKey sets the "secret_key" field to the value that was provided on create. +func (u *BrokerSecretUpsertBulk) UpdateSecretKey() *BrokerSecretUpsertBulk { + return u.Update(func(s *BrokerSecretUpsert) { + s.UpdateSecretKey() + }) +} + +// SetAlgorithm sets the "algorithm" field. +func (u *BrokerSecretUpsertBulk) SetAlgorithm(v string) *BrokerSecretUpsertBulk { + return u.Update(func(s *BrokerSecretUpsert) { + s.SetAlgorithm(v) + }) +} + +// UpdateAlgorithm sets the "algorithm" field to the value that was provided on create. +func (u *BrokerSecretUpsertBulk) UpdateAlgorithm() *BrokerSecretUpsertBulk { + return u.Update(func(s *BrokerSecretUpsert) { + s.UpdateAlgorithm() + }) +} + +// SetRotatedAt sets the "rotated_at" field. +func (u *BrokerSecretUpsertBulk) SetRotatedAt(v time.Time) *BrokerSecretUpsertBulk { + return u.Update(func(s *BrokerSecretUpsert) { + s.SetRotatedAt(v) + }) +} + +// UpdateRotatedAt sets the "rotated_at" field to the value that was provided on create. +func (u *BrokerSecretUpsertBulk) UpdateRotatedAt() *BrokerSecretUpsertBulk { + return u.Update(func(s *BrokerSecretUpsert) { + s.UpdateRotatedAt() + }) +} + +// ClearRotatedAt clears the value of the "rotated_at" field. +func (u *BrokerSecretUpsertBulk) ClearRotatedAt() *BrokerSecretUpsertBulk { + return u.Update(func(s *BrokerSecretUpsert) { + s.ClearRotatedAt() + }) +} + +// SetExpiresAt sets the "expires_at" field. +func (u *BrokerSecretUpsertBulk) SetExpiresAt(v time.Time) *BrokerSecretUpsertBulk { + return u.Update(func(s *BrokerSecretUpsert) { + s.SetExpiresAt(v) + }) +} + +// UpdateExpiresAt sets the "expires_at" field to the value that was provided on create. +func (u *BrokerSecretUpsertBulk) UpdateExpiresAt() *BrokerSecretUpsertBulk { + return u.Update(func(s *BrokerSecretUpsert) { + s.UpdateExpiresAt() + }) +} + +// ClearExpiresAt clears the value of the "expires_at" field. +func (u *BrokerSecretUpsertBulk) ClearExpiresAt() *BrokerSecretUpsertBulk { + return u.Update(func(s *BrokerSecretUpsert) { + s.ClearExpiresAt() + }) +} + +// SetStatus sets the "status" field. +func (u *BrokerSecretUpsertBulk) SetStatus(v string) *BrokerSecretUpsertBulk { + return u.Update(func(s *BrokerSecretUpsert) { + s.SetStatus(v) + }) +} + +// UpdateStatus sets the "status" field to the value that was provided on create. +func (u *BrokerSecretUpsertBulk) UpdateStatus() *BrokerSecretUpsertBulk { + return u.Update(func(s *BrokerSecretUpsert) { + s.UpdateStatus() + }) +} + +// Exec executes the query. +func (u *BrokerSecretUpsertBulk) Exec(ctx context.Context) error { + if u.create.err != nil { + return u.create.err + } + for i, b := range u.create.builders { + if len(b.conflict) != 0 { + return fmt.Errorf("ent: OnConflict was set for builder %d. Set it on the BrokerSecretCreateBulk instead", i) + } + } + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for BrokerSecretCreateBulk.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *BrokerSecretUpsertBulk) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/pkg/ent/brokersecret_delete.go b/pkg/ent/brokersecret_delete.go new file mode 100644 index 000000000..853f2dad1 --- /dev/null +++ b/pkg/ent/brokersecret_delete.go @@ -0,0 +1,88 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/brokersecret" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" +) + +// BrokerSecretDelete is the builder for deleting a BrokerSecret entity. +type BrokerSecretDelete struct { + config + hooks []Hook + mutation *BrokerSecretMutation +} + +// Where appends a list predicates to the BrokerSecretDelete builder. +func (_d *BrokerSecretDelete) Where(ps ...predicate.BrokerSecret) *BrokerSecretDelete { + _d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (_d *BrokerSecretDelete) Exec(ctx context.Context) (int, error) { + return withHooks(ctx, _d.sqlExec, _d.mutation, _d.hooks) +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *BrokerSecretDelete) ExecX(ctx context.Context) int { + n, err := _d.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (_d *BrokerSecretDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(brokersecret.Table, sqlgraph.NewFieldSpec(brokersecret.FieldID, field.TypeUUID)) + if ps := _d.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + affected, err := sqlgraph.DeleteNodes(ctx, _d.driver, _spec) + if err != nil && sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + _d.mutation.done = true + return affected, err +} + +// BrokerSecretDeleteOne is the builder for deleting a single BrokerSecret entity. +type BrokerSecretDeleteOne struct { + _d *BrokerSecretDelete +} + +// Where appends a list predicates to the BrokerSecretDelete builder. +func (_d *BrokerSecretDeleteOne) Where(ps ...predicate.BrokerSecret) *BrokerSecretDeleteOne { + _d._d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query. +func (_d *BrokerSecretDeleteOne) Exec(ctx context.Context) error { + n, err := _d._d.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{brokersecret.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *BrokerSecretDeleteOne) ExecX(ctx context.Context) { + if err := _d.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/pkg/ent/brokersecret_query.go b/pkg/ent/brokersecret_query.go new file mode 100644 index 000000000..430228400 --- /dev/null +++ b/pkg/ent/brokersecret_query.go @@ -0,0 +1,565 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "fmt" + "math" + + "entgo.io/ent" + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/brokersecret" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/google/uuid" +) + +// BrokerSecretQuery is the builder for querying BrokerSecret entities. +type BrokerSecretQuery struct { + config + ctx *QueryContext + order []brokersecret.OrderOption + inters []Interceptor + predicates []predicate.BrokerSecret + modifiers []func(*sql.Selector) + // intermediate query (i.e. traversal path). + sql *sql.Selector + path func(context.Context) (*sql.Selector, error) +} + +// Where adds a new predicate for the BrokerSecretQuery builder. +func (_q *BrokerSecretQuery) Where(ps ...predicate.BrokerSecret) *BrokerSecretQuery { + _q.predicates = append(_q.predicates, ps...) + return _q +} + +// Limit the number of records to be returned by this query. +func (_q *BrokerSecretQuery) Limit(limit int) *BrokerSecretQuery { + _q.ctx.Limit = &limit + return _q +} + +// Offset to start from. +func (_q *BrokerSecretQuery) Offset(offset int) *BrokerSecretQuery { + _q.ctx.Offset = &offset + return _q +} + +// Unique configures the query builder to filter duplicate records on query. +// By default, unique is set to true, and can be disabled using this method. +func (_q *BrokerSecretQuery) Unique(unique bool) *BrokerSecretQuery { + _q.ctx.Unique = &unique + return _q +} + +// Order specifies how the records should be ordered. +func (_q *BrokerSecretQuery) Order(o ...brokersecret.OrderOption) *BrokerSecretQuery { + _q.order = append(_q.order, o...) + return _q +} + +// First returns the first BrokerSecret entity from the query. +// Returns a *NotFoundError when no BrokerSecret was found. +func (_q *BrokerSecretQuery) First(ctx context.Context) (*BrokerSecret, error) { + nodes, err := _q.Limit(1).All(setContextOp(ctx, _q.ctx, ent.OpQueryFirst)) + if err != nil { + return nil, err + } + if len(nodes) == 0 { + return nil, &NotFoundError{brokersecret.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (_q *BrokerSecretQuery) FirstX(ctx context.Context) *BrokerSecret { + node, err := _q.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// FirstID returns the first BrokerSecret ID from the query. +// Returns a *NotFoundError when no BrokerSecret ID was found. +func (_q *BrokerSecretQuery) FirstID(ctx context.Context) (id uuid.UUID, err error) { + var ids []uuid.UUID + if ids, err = _q.Limit(1).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryFirstID)); err != nil { + return + } + if len(ids) == 0 { + err = &NotFoundError{brokersecret.Label} + return + } + return ids[0], nil +} + +// FirstIDX is like FirstID, but panics if an error occurs. +func (_q *BrokerSecretQuery) FirstIDX(ctx context.Context) uuid.UUID { + id, err := _q.FirstID(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return id +} + +// Only returns a single BrokerSecret entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one BrokerSecret entity is found. +// Returns a *NotFoundError when no BrokerSecret entities are found. +func (_q *BrokerSecretQuery) Only(ctx context.Context) (*BrokerSecret, error) { + nodes, err := _q.Limit(2).All(setContextOp(ctx, _q.ctx, ent.OpQueryOnly)) + if err != nil { + return nil, err + } + switch len(nodes) { + case 1: + return nodes[0], nil + case 0: + return nil, &NotFoundError{brokersecret.Label} + default: + return nil, &NotSingularError{brokersecret.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (_q *BrokerSecretQuery) OnlyX(ctx context.Context) *BrokerSecret { + node, err := _q.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// OnlyID is like Only, but returns the only BrokerSecret ID in the query. +// Returns a *NotSingularError when more than one BrokerSecret ID is found. +// Returns a *NotFoundError when no entities are found. +func (_q *BrokerSecretQuery) OnlyID(ctx context.Context) (id uuid.UUID, err error) { + var ids []uuid.UUID + if ids, err = _q.Limit(2).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryOnlyID)); err != nil { + return + } + switch len(ids) { + case 1: + id = ids[0] + case 0: + err = &NotFoundError{brokersecret.Label} + default: + err = &NotSingularError{brokersecret.Label} + } + return +} + +// OnlyIDX is like OnlyID, but panics if an error occurs. +func (_q *BrokerSecretQuery) OnlyIDX(ctx context.Context) uuid.UUID { + id, err := _q.OnlyID(ctx) + if err != nil { + panic(err) + } + return id +} + +// All executes the query and returns a list of BrokerSecrets. +func (_q *BrokerSecretQuery) All(ctx context.Context) ([]*BrokerSecret, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryAll) + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*BrokerSecret, *BrokerSecretQuery]() + return withInterceptors[[]*BrokerSecret](ctx, _q, qr, _q.inters) +} + +// AllX is like All, but panics if an error occurs. +func (_q *BrokerSecretQuery) AllX(ctx context.Context) []*BrokerSecret { + nodes, err := _q.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// IDs executes the query and returns a list of BrokerSecret IDs. +func (_q *BrokerSecretQuery) IDs(ctx context.Context) (ids []uuid.UUID, err error) { + if _q.ctx.Unique == nil && _q.path != nil { + _q.Unique(true) + } + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryIDs) + if err = _q.Select(brokersecret.FieldID).Scan(ctx, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// IDsX is like IDs, but panics if an error occurs. +func (_q *BrokerSecretQuery) IDsX(ctx context.Context) []uuid.UUID { + ids, err := _q.IDs(ctx) + if err != nil { + panic(err) + } + return ids +} + +// Count returns the count of the given query. +func (_q *BrokerSecretQuery) Count(ctx context.Context) (int, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryCount) + if err := _q.prepareQuery(ctx); err != nil { + return 0, err + } + return withInterceptors[int](ctx, _q, querierCount[*BrokerSecretQuery](), _q.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (_q *BrokerSecretQuery) CountX(ctx context.Context) int { + count, err := _q.Count(ctx) + if err != nil { + panic(err) + } + return count +} + +// Exist returns true if the query has elements in the graph. +func (_q *BrokerSecretQuery) Exist(ctx context.Context) (bool, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryExist) + switch _, err := _q.FirstID(ctx); { + case IsNotFound(err): + return false, nil + case err != nil: + return false, fmt.Errorf("ent: check existence: %w", err) + default: + return true, nil + } +} + +// ExistX is like Exist, but panics if an error occurs. +func (_q *BrokerSecretQuery) ExistX(ctx context.Context) bool { + exist, err := _q.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the BrokerSecretQuery builder, including all associated steps. It can be +// used to prepare common query builders and use them differently after the clone is made. +func (_q *BrokerSecretQuery) Clone() *BrokerSecretQuery { + if _q == nil { + return nil + } + return &BrokerSecretQuery{ + config: _q.config, + ctx: _q.ctx.Clone(), + order: append([]brokersecret.OrderOption{}, _q.order...), + inters: append([]Interceptor{}, _q.inters...), + predicates: append([]predicate.BrokerSecret{}, _q.predicates...), + // clone intermediate query. + sql: _q.sql.Clone(), + path: _q.path, + } +} + +// GroupBy is used to group vertices by one or more fields/columns. +// It is often used with aggregate functions, like: count, max, mean, min, sum. +// +// Example: +// +// var v []struct { +// SecretKey []byte `json:"secret_key,omitempty"` +// Count int `json:"count,omitempty"` +// } +// +// client.BrokerSecret.Query(). +// GroupBy(brokersecret.FieldSecretKey). +// Aggregate(ent.Count()). +// Scan(ctx, &v) +func (_q *BrokerSecretQuery) GroupBy(field string, fields ...string) *BrokerSecretGroupBy { + _q.ctx.Fields = append([]string{field}, fields...) + grbuild := &BrokerSecretGroupBy{build: _q} + grbuild.flds = &_q.ctx.Fields + grbuild.label = brokersecret.Label + grbuild.scan = grbuild.Scan + return grbuild +} + +// Select allows the selection one or more fields/columns for the given query, +// instead of selecting all fields in the entity. +// +// Example: +// +// var v []struct { +// SecretKey []byte `json:"secret_key,omitempty"` +// } +// +// client.BrokerSecret.Query(). +// Select(brokersecret.FieldSecretKey). +// Scan(ctx, &v) +func (_q *BrokerSecretQuery) Select(fields ...string) *BrokerSecretSelect { + _q.ctx.Fields = append(_q.ctx.Fields, fields...) + sbuild := &BrokerSecretSelect{BrokerSecretQuery: _q} + sbuild.label = brokersecret.Label + sbuild.flds, sbuild.scan = &_q.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a BrokerSecretSelect configured with the given aggregations. +func (_q *BrokerSecretQuery) Aggregate(fns ...AggregateFunc) *BrokerSecretSelect { + return _q.Select().Aggregate(fns...) +} + +func (_q *BrokerSecretQuery) prepareQuery(ctx context.Context) error { + for _, inter := range _q.inters { + if inter == nil { + return fmt.Errorf("ent: uninitialized interceptor (forgotten import ent/runtime?)") + } + if trv, ok := inter.(Traverser); ok { + if err := trv.Traverse(ctx, _q); err != nil { + return err + } + } + } + for _, f := range _q.ctx.Fields { + if !brokersecret.ValidColumn(f) { + return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + } + if _q.path != nil { + prev, err := _q.path(ctx) + if err != nil { + return err + } + _q.sql = prev + } + return nil +} + +func (_q *BrokerSecretQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*BrokerSecret, error) { + var ( + nodes = []*BrokerSecret{} + _spec = _q.querySpec() + ) + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*BrokerSecret).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &BrokerSecret{config: _q.config} + nodes = append(nodes, node) + return node.assignValues(columns, values) + } + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } + for i := range hooks { + hooks[i](ctx, _spec) + } + if err := sqlgraph.QueryNodes(ctx, _q.driver, _spec); err != nil { + return nil, err + } + if len(nodes) == 0 { + return nodes, nil + } + return nodes, nil +} + +func (_q *BrokerSecretQuery) sqlCount(ctx context.Context) (int, error) { + _spec := _q.querySpec() + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } + _spec.Node.Columns = _q.ctx.Fields + if len(_q.ctx.Fields) > 0 { + _spec.Unique = _q.ctx.Unique != nil && *_q.ctx.Unique + } + return sqlgraph.CountNodes(ctx, _q.driver, _spec) +} + +func (_q *BrokerSecretQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(brokersecret.Table, brokersecret.Columns, sqlgraph.NewFieldSpec(brokersecret.FieldID, field.TypeUUID)) + _spec.From = _q.sql + if unique := _q.ctx.Unique; unique != nil { + _spec.Unique = *unique + } else if _q.path != nil { + _spec.Unique = true + } + if fields := _q.ctx.Fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, brokersecret.FieldID) + for i := range fields { + if fields[i] != brokersecret.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, fields[i]) + } + } + } + if ps := _q.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if limit := _q.ctx.Limit; limit != nil { + _spec.Limit = *limit + } + if offset := _q.ctx.Offset; offset != nil { + _spec.Offset = *offset + } + if ps := _q.order; len(ps) > 0 { + _spec.Order = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + return _spec +} + +func (_q *BrokerSecretQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(_q.driver.Dialect()) + t1 := builder.Table(brokersecret.Table) + columns := _q.ctx.Fields + if len(columns) == 0 { + columns = brokersecret.Columns + } + selector := builder.Select(t1.Columns(columns...)...).From(t1) + if _q.sql != nil { + selector = _q.sql + selector.Select(selector.Columns(columns...)...) + } + if _q.ctx.Unique != nil && *_q.ctx.Unique { + selector.Distinct() + } + for _, m := range _q.modifiers { + m(selector) + } + for _, p := range _q.predicates { + p(selector) + } + for _, p := range _q.order { + p(selector) + } + if offset := _q.ctx.Offset; offset != nil { + // limit is mandatory for offset clause. We start + // with default value, and override it below if needed. + selector.Offset(*offset).Limit(math.MaxInt32) + } + if limit := _q.ctx.Limit; limit != nil { + selector.Limit(*limit) + } + return selector +} + +// ForUpdate locks the selected rows against concurrent updates, and prevent them from being +// updated, deleted or "selected ... for update" by other sessions, until the transaction is +// either committed or rolled-back. +func (_q *BrokerSecretQuery) ForUpdate(opts ...sql.LockOption) *BrokerSecretQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForUpdate(opts...) + }) + return _q +} + +// ForShare behaves similarly to ForUpdate, except that it acquires a shared mode lock +// on any rows that are read. Other sessions can read the rows, but cannot modify them +// until your transaction commits. +func (_q *BrokerSecretQuery) ForShare(opts ...sql.LockOption) *BrokerSecretQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForShare(opts...) + }) + return _q +} + +// BrokerSecretGroupBy is the group-by builder for BrokerSecret entities. +type BrokerSecretGroupBy struct { + selector + build *BrokerSecretQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (_g *BrokerSecretGroupBy) Aggregate(fns ...AggregateFunc) *BrokerSecretGroupBy { + _g.fns = append(_g.fns, fns...) + return _g +} + +// Scan applies the selector query and scans the result into the given value. +func (_g *BrokerSecretGroupBy) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, _g.build.ctx, ent.OpQueryGroupBy) + if err := _g.build.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*BrokerSecretQuery, *BrokerSecretGroupBy](ctx, _g.build, _g, _g.build.inters, v) +} + +func (_g *BrokerSecretGroupBy) sqlScan(ctx context.Context, root *BrokerSecretQuery, v any) error { + selector := root.sqlQuery(ctx).Select() + aggregation := make([]string, 0, len(_g.fns)) + for _, fn := range _g.fns { + aggregation = append(aggregation, fn(selector)) + } + if len(selector.SelectedColumns()) == 0 { + columns := make([]string, 0, len(*_g.flds)+len(_g.fns)) + for _, f := range *_g.flds { + columns = append(columns, selector.C(f)) + } + columns = append(columns, aggregation...) + selector.Select(columns...) + } + selector.GroupBy(selector.Columns(*_g.flds...)...) + if err := selector.Err(); err != nil { + return err + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := _g.build.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} + +// BrokerSecretSelect is the builder for selecting fields of BrokerSecret entities. +type BrokerSecretSelect struct { + *BrokerSecretQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (_s *BrokerSecretSelect) Aggregate(fns ...AggregateFunc) *BrokerSecretSelect { + _s.fns = append(_s.fns, fns...) + return _s +} + +// Scan applies the selector query and scans the result into the given value. +func (_s *BrokerSecretSelect) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, _s.ctx, ent.OpQuerySelect) + if err := _s.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*BrokerSecretQuery, *BrokerSecretSelect](ctx, _s.BrokerSecretQuery, _s, _s.inters, v) +} + +func (_s *BrokerSecretSelect) sqlScan(ctx context.Context, root *BrokerSecretQuery, v any) error { + selector := root.sqlQuery(ctx) + aggregation := make([]string, 0, len(_s.fns)) + for _, fn := range _s.fns { + aggregation = append(aggregation, fn(selector)) + } + switch n := len(*_s.selector.flds); { + case n == 0 && len(aggregation) > 0: + selector.Select(aggregation...) + case n != 0 && len(aggregation) > 0: + selector.AppendSelect(aggregation...) + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := _s.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} diff --git a/pkg/ent/brokersecret_update.go b/pkg/ent/brokersecret_update.go new file mode 100644 index 000000000..86a2c4359 --- /dev/null +++ b/pkg/ent/brokersecret_update.go @@ -0,0 +1,392 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/brokersecret" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" +) + +// BrokerSecretUpdate is the builder for updating BrokerSecret entities. +type BrokerSecretUpdate struct { + config + hooks []Hook + mutation *BrokerSecretMutation +} + +// Where appends a list predicates to the BrokerSecretUpdate builder. +func (_u *BrokerSecretUpdate) Where(ps ...predicate.BrokerSecret) *BrokerSecretUpdate { + _u.mutation.Where(ps...) + return _u +} + +// SetSecretKey sets the "secret_key" field. +func (_u *BrokerSecretUpdate) SetSecretKey(v []byte) *BrokerSecretUpdate { + _u.mutation.SetSecretKey(v) + return _u +} + +// SetAlgorithm sets the "algorithm" field. +func (_u *BrokerSecretUpdate) SetAlgorithm(v string) *BrokerSecretUpdate { + _u.mutation.SetAlgorithm(v) + return _u +} + +// SetNillableAlgorithm sets the "algorithm" field if the given value is not nil. +func (_u *BrokerSecretUpdate) SetNillableAlgorithm(v *string) *BrokerSecretUpdate { + if v != nil { + _u.SetAlgorithm(*v) + } + return _u +} + +// SetRotatedAt sets the "rotated_at" field. +func (_u *BrokerSecretUpdate) SetRotatedAt(v time.Time) *BrokerSecretUpdate { + _u.mutation.SetRotatedAt(v) + return _u +} + +// SetNillableRotatedAt sets the "rotated_at" field if the given value is not nil. +func (_u *BrokerSecretUpdate) SetNillableRotatedAt(v *time.Time) *BrokerSecretUpdate { + if v != nil { + _u.SetRotatedAt(*v) + } + return _u +} + +// ClearRotatedAt clears the value of the "rotated_at" field. +func (_u *BrokerSecretUpdate) ClearRotatedAt() *BrokerSecretUpdate { + _u.mutation.ClearRotatedAt() + return _u +} + +// SetExpiresAt sets the "expires_at" field. +func (_u *BrokerSecretUpdate) SetExpiresAt(v time.Time) *BrokerSecretUpdate { + _u.mutation.SetExpiresAt(v) + return _u +} + +// SetNillableExpiresAt sets the "expires_at" field if the given value is not nil. +func (_u *BrokerSecretUpdate) SetNillableExpiresAt(v *time.Time) *BrokerSecretUpdate { + if v != nil { + _u.SetExpiresAt(*v) + } + return _u +} + +// ClearExpiresAt clears the value of the "expires_at" field. +func (_u *BrokerSecretUpdate) ClearExpiresAt() *BrokerSecretUpdate { + _u.mutation.ClearExpiresAt() + return _u +} + +// SetStatus sets the "status" field. +func (_u *BrokerSecretUpdate) SetStatus(v string) *BrokerSecretUpdate { + _u.mutation.SetStatus(v) + return _u +} + +// SetNillableStatus sets the "status" field if the given value is not nil. +func (_u *BrokerSecretUpdate) SetNillableStatus(v *string) *BrokerSecretUpdate { + if v != nil { + _u.SetStatus(*v) + } + return _u +} + +// Mutation returns the BrokerSecretMutation object of the builder. +func (_u *BrokerSecretUpdate) Mutation() *BrokerSecretMutation { + return _u.mutation +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (_u *BrokerSecretUpdate) Save(ctx context.Context) (int, error) { + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *BrokerSecretUpdate) SaveX(ctx context.Context) int { + affected, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (_u *BrokerSecretUpdate) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *BrokerSecretUpdate) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_u *BrokerSecretUpdate) check() error { + if v, ok := _u.mutation.SecretKey(); ok { + if err := brokersecret.SecretKeyValidator(v); err != nil { + return &ValidationError{Name: "secret_key", err: fmt.Errorf(`ent: validator failed for field "BrokerSecret.secret_key": %w`, err)} + } + } + return nil +} + +func (_u *BrokerSecretUpdate) sqlSave(ctx context.Context) (_node int, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(brokersecret.Table, brokersecret.Columns, sqlgraph.NewFieldSpec(brokersecret.FieldID, field.TypeUUID)) + if ps := _u.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := _u.mutation.SecretKey(); ok { + _spec.SetField(brokersecret.FieldSecretKey, field.TypeBytes, value) + } + if value, ok := _u.mutation.Algorithm(); ok { + _spec.SetField(brokersecret.FieldAlgorithm, field.TypeString, value) + } + if value, ok := _u.mutation.RotatedAt(); ok { + _spec.SetField(brokersecret.FieldRotatedAt, field.TypeTime, value) + } + if _u.mutation.RotatedAtCleared() { + _spec.ClearField(brokersecret.FieldRotatedAt, field.TypeTime) + } + if value, ok := _u.mutation.ExpiresAt(); ok { + _spec.SetField(brokersecret.FieldExpiresAt, field.TypeTime, value) + } + if _u.mutation.ExpiresAtCleared() { + _spec.ClearField(brokersecret.FieldExpiresAt, field.TypeTime) + } + if value, ok := _u.mutation.Status(); ok { + _spec.SetField(brokersecret.FieldStatus, field.TypeString, value) + } + if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{brokersecret.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + _u.mutation.done = true + return _node, nil +} + +// BrokerSecretUpdateOne is the builder for updating a single BrokerSecret entity. +type BrokerSecretUpdateOne struct { + config + fields []string + hooks []Hook + mutation *BrokerSecretMutation +} + +// SetSecretKey sets the "secret_key" field. +func (_u *BrokerSecretUpdateOne) SetSecretKey(v []byte) *BrokerSecretUpdateOne { + _u.mutation.SetSecretKey(v) + return _u +} + +// SetAlgorithm sets the "algorithm" field. +func (_u *BrokerSecretUpdateOne) SetAlgorithm(v string) *BrokerSecretUpdateOne { + _u.mutation.SetAlgorithm(v) + return _u +} + +// SetNillableAlgorithm sets the "algorithm" field if the given value is not nil. +func (_u *BrokerSecretUpdateOne) SetNillableAlgorithm(v *string) *BrokerSecretUpdateOne { + if v != nil { + _u.SetAlgorithm(*v) + } + return _u +} + +// SetRotatedAt sets the "rotated_at" field. +func (_u *BrokerSecretUpdateOne) SetRotatedAt(v time.Time) *BrokerSecretUpdateOne { + _u.mutation.SetRotatedAt(v) + return _u +} + +// SetNillableRotatedAt sets the "rotated_at" field if the given value is not nil. +func (_u *BrokerSecretUpdateOne) SetNillableRotatedAt(v *time.Time) *BrokerSecretUpdateOne { + if v != nil { + _u.SetRotatedAt(*v) + } + return _u +} + +// ClearRotatedAt clears the value of the "rotated_at" field. +func (_u *BrokerSecretUpdateOne) ClearRotatedAt() *BrokerSecretUpdateOne { + _u.mutation.ClearRotatedAt() + return _u +} + +// SetExpiresAt sets the "expires_at" field. +func (_u *BrokerSecretUpdateOne) SetExpiresAt(v time.Time) *BrokerSecretUpdateOne { + _u.mutation.SetExpiresAt(v) + return _u +} + +// SetNillableExpiresAt sets the "expires_at" field if the given value is not nil. +func (_u *BrokerSecretUpdateOne) SetNillableExpiresAt(v *time.Time) *BrokerSecretUpdateOne { + if v != nil { + _u.SetExpiresAt(*v) + } + return _u +} + +// ClearExpiresAt clears the value of the "expires_at" field. +func (_u *BrokerSecretUpdateOne) ClearExpiresAt() *BrokerSecretUpdateOne { + _u.mutation.ClearExpiresAt() + return _u +} + +// SetStatus sets the "status" field. +func (_u *BrokerSecretUpdateOne) SetStatus(v string) *BrokerSecretUpdateOne { + _u.mutation.SetStatus(v) + return _u +} + +// SetNillableStatus sets the "status" field if the given value is not nil. +func (_u *BrokerSecretUpdateOne) SetNillableStatus(v *string) *BrokerSecretUpdateOne { + if v != nil { + _u.SetStatus(*v) + } + return _u +} + +// Mutation returns the BrokerSecretMutation object of the builder. +func (_u *BrokerSecretUpdateOne) Mutation() *BrokerSecretMutation { + return _u.mutation +} + +// Where appends a list predicates to the BrokerSecretUpdate builder. +func (_u *BrokerSecretUpdateOne) Where(ps ...predicate.BrokerSecret) *BrokerSecretUpdateOne { + _u.mutation.Where(ps...) + return _u +} + +// Select allows selecting one or more fields (columns) of the returned entity. +// The default is selecting all fields defined in the entity schema. +func (_u *BrokerSecretUpdateOne) Select(field string, fields ...string) *BrokerSecretUpdateOne { + _u.fields = append([]string{field}, fields...) + return _u +} + +// Save executes the query and returns the updated BrokerSecret entity. +func (_u *BrokerSecretUpdateOne) Save(ctx context.Context) (*BrokerSecret, error) { + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *BrokerSecretUpdateOne) SaveX(ctx context.Context) *BrokerSecret { + node, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (_u *BrokerSecretUpdateOne) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *BrokerSecretUpdateOne) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_u *BrokerSecretUpdateOne) check() error { + if v, ok := _u.mutation.SecretKey(); ok { + if err := brokersecret.SecretKeyValidator(v); err != nil { + return &ValidationError{Name: "secret_key", err: fmt.Errorf(`ent: validator failed for field "BrokerSecret.secret_key": %w`, err)} + } + } + return nil +} + +func (_u *BrokerSecretUpdateOne) sqlSave(ctx context.Context) (_node *BrokerSecret, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(brokersecret.Table, brokersecret.Columns, sqlgraph.NewFieldSpec(brokersecret.FieldID, field.TypeUUID)) + id, ok := _u.mutation.ID() + if !ok { + return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "BrokerSecret.id" for update`)} + } + _spec.Node.ID.Value = id + if fields := _u.fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, brokersecret.FieldID) + for _, f := range fields { + if !brokersecret.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + if f != brokersecret.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, f) + } + } + } + if ps := _u.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := _u.mutation.SecretKey(); ok { + _spec.SetField(brokersecret.FieldSecretKey, field.TypeBytes, value) + } + if value, ok := _u.mutation.Algorithm(); ok { + _spec.SetField(brokersecret.FieldAlgorithm, field.TypeString, value) + } + if value, ok := _u.mutation.RotatedAt(); ok { + _spec.SetField(brokersecret.FieldRotatedAt, field.TypeTime, value) + } + if _u.mutation.RotatedAtCleared() { + _spec.ClearField(brokersecret.FieldRotatedAt, field.TypeTime) + } + if value, ok := _u.mutation.ExpiresAt(); ok { + _spec.SetField(brokersecret.FieldExpiresAt, field.TypeTime, value) + } + if _u.mutation.ExpiresAtCleared() { + _spec.ClearField(brokersecret.FieldExpiresAt, field.TypeTime) + } + if value, ok := _u.mutation.Status(); ok { + _spec.SetField(brokersecret.FieldStatus, field.TypeString, value) + } + _node = &BrokerSecret{config: _u.config} + _spec.Assign = _node.assignValues + _spec.ScanValues = _node.scanValues + if err = sqlgraph.UpdateNode(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{brokersecret.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + _u.mutation.done = true + return _node, nil +} diff --git a/pkg/ent/client.go b/pkg/ent/client.go index ccb3f1a7d..8b87bf6a9 100644 --- a/pkg/ent/client.go +++ b/pkg/ent/client.go @@ -18,11 +18,34 @@ import ( "entgo.io/ent/dialect/sql/sqlgraph" "github.com/GoogleCloudPlatform/scion/pkg/ent/accesspolicy" "github.com/GoogleCloudPlatform/scion/pkg/ent/agent" + "github.com/GoogleCloudPlatform/scion/pkg/ent/allowlistentry" + "github.com/GoogleCloudPlatform/scion/pkg/ent/apikey" + "github.com/GoogleCloudPlatform/scion/pkg/ent/brokerjointoken" + "github.com/GoogleCloudPlatform/scion/pkg/ent/brokersecret" + "github.com/GoogleCloudPlatform/scion/pkg/ent/envvar" + "github.com/GoogleCloudPlatform/scion/pkg/ent/gcpserviceaccount" + "github.com/GoogleCloudPlatform/scion/pkg/ent/githubinstallation" "github.com/GoogleCloudPlatform/scion/pkg/ent/group" "github.com/GoogleCloudPlatform/scion/pkg/ent/groupmembership" + "github.com/GoogleCloudPlatform/scion/pkg/ent/harnessconfig" + "github.com/GoogleCloudPlatform/scion/pkg/ent/invitecode" + "github.com/GoogleCloudPlatform/scion/pkg/ent/maintenanceoperation" + "github.com/GoogleCloudPlatform/scion/pkg/ent/maintenanceoperationrun" + "github.com/GoogleCloudPlatform/scion/pkg/ent/message" + "github.com/GoogleCloudPlatform/scion/pkg/ent/notification" + "github.com/GoogleCloudPlatform/scion/pkg/ent/notificationsubscription" "github.com/GoogleCloudPlatform/scion/pkg/ent/policybinding" "github.com/GoogleCloudPlatform/scion/pkg/ent/project" + "github.com/GoogleCloudPlatform/scion/pkg/ent/projectcontributor" + "github.com/GoogleCloudPlatform/scion/pkg/ent/projectsyncstate" + "github.com/GoogleCloudPlatform/scion/pkg/ent/runtimebroker" + "github.com/GoogleCloudPlatform/scion/pkg/ent/schedule" + "github.com/GoogleCloudPlatform/scion/pkg/ent/scheduledevent" + "github.com/GoogleCloudPlatform/scion/pkg/ent/secret" + "github.com/GoogleCloudPlatform/scion/pkg/ent/subscriptiontemplate" + "github.com/GoogleCloudPlatform/scion/pkg/ent/template" "github.com/GoogleCloudPlatform/scion/pkg/ent/user" + "github.com/GoogleCloudPlatform/scion/pkg/ent/useraccesstoken" ) // Client is the client that holds all ent builders. @@ -34,16 +57,62 @@ type Client struct { AccessPolicy *AccessPolicyClient // Agent is the client for interacting with the Agent builders. Agent *AgentClient + // AllowListEntry is the client for interacting with the AllowListEntry builders. + AllowListEntry *AllowListEntryClient + // ApiKey is the client for interacting with the ApiKey builders. + ApiKey *ApiKeyClient + // BrokerJoinToken is the client for interacting with the BrokerJoinToken builders. + BrokerJoinToken *BrokerJoinTokenClient + // BrokerSecret is the client for interacting with the BrokerSecret builders. + BrokerSecret *BrokerSecretClient + // EnvVar is the client for interacting with the EnvVar builders. + EnvVar *EnvVarClient + // GCPServiceAccount is the client for interacting with the GCPServiceAccount builders. + GCPServiceAccount *GCPServiceAccountClient + // GithubInstallation is the client for interacting with the GithubInstallation builders. + GithubInstallation *GithubInstallationClient // Group is the client for interacting with the Group builders. Group *GroupClient // GroupMembership is the client for interacting with the GroupMembership builders. GroupMembership *GroupMembershipClient + // HarnessConfig is the client for interacting with the HarnessConfig builders. + HarnessConfig *HarnessConfigClient + // InviteCode is the client for interacting with the InviteCode builders. + InviteCode *InviteCodeClient + // MaintenanceOperation is the client for interacting with the MaintenanceOperation builders. + MaintenanceOperation *MaintenanceOperationClient + // MaintenanceOperationRun is the client for interacting with the MaintenanceOperationRun builders. + MaintenanceOperationRun *MaintenanceOperationRunClient + // Message is the client for interacting with the Message builders. + Message *MessageClient + // Notification is the client for interacting with the Notification builders. + Notification *NotificationClient + // NotificationSubscription is the client for interacting with the NotificationSubscription builders. + NotificationSubscription *NotificationSubscriptionClient // PolicyBinding is the client for interacting with the PolicyBinding builders. PolicyBinding *PolicyBindingClient // Project is the client for interacting with the Project builders. Project *ProjectClient + // ProjectContributor is the client for interacting with the ProjectContributor builders. + ProjectContributor *ProjectContributorClient + // ProjectSyncState is the client for interacting with the ProjectSyncState builders. + ProjectSyncState *ProjectSyncStateClient + // RuntimeBroker is the client for interacting with the RuntimeBroker builders. + RuntimeBroker *RuntimeBrokerClient + // Schedule is the client for interacting with the Schedule builders. + Schedule *ScheduleClient + // ScheduledEvent is the client for interacting with the ScheduledEvent builders. + ScheduledEvent *ScheduledEventClient + // Secret is the client for interacting with the Secret builders. + Secret *SecretClient + // SubscriptionTemplate is the client for interacting with the SubscriptionTemplate builders. + SubscriptionTemplate *SubscriptionTemplateClient + // Template is the client for interacting with the Template builders. + Template *TemplateClient // User is the client for interacting with the User builders. User *UserClient + // UserAccessToken is the client for interacting with the UserAccessToken builders. + UserAccessToken *UserAccessTokenClient } // NewClient creates a new client configured with the given options. @@ -57,11 +126,34 @@ func (c *Client) init() { c.Schema = migrate.NewSchema(c.driver) c.AccessPolicy = NewAccessPolicyClient(c.config) c.Agent = NewAgentClient(c.config) + c.AllowListEntry = NewAllowListEntryClient(c.config) + c.ApiKey = NewApiKeyClient(c.config) + c.BrokerJoinToken = NewBrokerJoinTokenClient(c.config) + c.BrokerSecret = NewBrokerSecretClient(c.config) + c.EnvVar = NewEnvVarClient(c.config) + c.GCPServiceAccount = NewGCPServiceAccountClient(c.config) + c.GithubInstallation = NewGithubInstallationClient(c.config) c.Group = NewGroupClient(c.config) c.GroupMembership = NewGroupMembershipClient(c.config) + c.HarnessConfig = NewHarnessConfigClient(c.config) + c.InviteCode = NewInviteCodeClient(c.config) + c.MaintenanceOperation = NewMaintenanceOperationClient(c.config) + c.MaintenanceOperationRun = NewMaintenanceOperationRunClient(c.config) + c.Message = NewMessageClient(c.config) + c.Notification = NewNotificationClient(c.config) + c.NotificationSubscription = NewNotificationSubscriptionClient(c.config) c.PolicyBinding = NewPolicyBindingClient(c.config) c.Project = NewProjectClient(c.config) + c.ProjectContributor = NewProjectContributorClient(c.config) + c.ProjectSyncState = NewProjectSyncStateClient(c.config) + c.RuntimeBroker = NewRuntimeBrokerClient(c.config) + c.Schedule = NewScheduleClient(c.config) + c.ScheduledEvent = NewScheduledEventClient(c.config) + c.Secret = NewSecretClient(c.config) + c.SubscriptionTemplate = NewSubscriptionTemplateClient(c.config) + c.Template = NewTemplateClient(c.config) c.User = NewUserClient(c.config) + c.UserAccessToken = NewUserAccessTokenClient(c.config) } type ( @@ -152,15 +244,38 @@ func (c *Client) Tx(ctx context.Context) (*Tx, error) { cfg := c.config cfg.driver = tx return &Tx{ - ctx: ctx, - config: cfg, - AccessPolicy: NewAccessPolicyClient(cfg), - Agent: NewAgentClient(cfg), - Group: NewGroupClient(cfg), - GroupMembership: NewGroupMembershipClient(cfg), - PolicyBinding: NewPolicyBindingClient(cfg), - Project: NewProjectClient(cfg), - User: NewUserClient(cfg), + ctx: ctx, + config: cfg, + AccessPolicy: NewAccessPolicyClient(cfg), + Agent: NewAgentClient(cfg), + AllowListEntry: NewAllowListEntryClient(cfg), + ApiKey: NewApiKeyClient(cfg), + BrokerJoinToken: NewBrokerJoinTokenClient(cfg), + BrokerSecret: NewBrokerSecretClient(cfg), + EnvVar: NewEnvVarClient(cfg), + GCPServiceAccount: NewGCPServiceAccountClient(cfg), + GithubInstallation: NewGithubInstallationClient(cfg), + Group: NewGroupClient(cfg), + GroupMembership: NewGroupMembershipClient(cfg), + HarnessConfig: NewHarnessConfigClient(cfg), + InviteCode: NewInviteCodeClient(cfg), + MaintenanceOperation: NewMaintenanceOperationClient(cfg), + MaintenanceOperationRun: NewMaintenanceOperationRunClient(cfg), + Message: NewMessageClient(cfg), + Notification: NewNotificationClient(cfg), + NotificationSubscription: NewNotificationSubscriptionClient(cfg), + PolicyBinding: NewPolicyBindingClient(cfg), + Project: NewProjectClient(cfg), + ProjectContributor: NewProjectContributorClient(cfg), + ProjectSyncState: NewProjectSyncStateClient(cfg), + RuntimeBroker: NewRuntimeBrokerClient(cfg), + Schedule: NewScheduleClient(cfg), + ScheduledEvent: NewScheduledEventClient(cfg), + Secret: NewSecretClient(cfg), + SubscriptionTemplate: NewSubscriptionTemplateClient(cfg), + Template: NewTemplateClient(cfg), + User: NewUserClient(cfg), + UserAccessToken: NewUserAccessTokenClient(cfg), }, nil } @@ -178,15 +293,38 @@ func (c *Client) BeginTx(ctx context.Context, opts *sql.TxOptions) (*Tx, error) cfg := c.config cfg.driver = &txDriver{tx: tx, drv: c.driver} return &Tx{ - ctx: ctx, - config: cfg, - AccessPolicy: NewAccessPolicyClient(cfg), - Agent: NewAgentClient(cfg), - Group: NewGroupClient(cfg), - GroupMembership: NewGroupMembershipClient(cfg), - PolicyBinding: NewPolicyBindingClient(cfg), - Project: NewProjectClient(cfg), - User: NewUserClient(cfg), + ctx: ctx, + config: cfg, + AccessPolicy: NewAccessPolicyClient(cfg), + Agent: NewAgentClient(cfg), + AllowListEntry: NewAllowListEntryClient(cfg), + ApiKey: NewApiKeyClient(cfg), + BrokerJoinToken: NewBrokerJoinTokenClient(cfg), + BrokerSecret: NewBrokerSecretClient(cfg), + EnvVar: NewEnvVarClient(cfg), + GCPServiceAccount: NewGCPServiceAccountClient(cfg), + GithubInstallation: NewGithubInstallationClient(cfg), + Group: NewGroupClient(cfg), + GroupMembership: NewGroupMembershipClient(cfg), + HarnessConfig: NewHarnessConfigClient(cfg), + InviteCode: NewInviteCodeClient(cfg), + MaintenanceOperation: NewMaintenanceOperationClient(cfg), + MaintenanceOperationRun: NewMaintenanceOperationRunClient(cfg), + Message: NewMessageClient(cfg), + Notification: NewNotificationClient(cfg), + NotificationSubscription: NewNotificationSubscriptionClient(cfg), + PolicyBinding: NewPolicyBindingClient(cfg), + Project: NewProjectClient(cfg), + ProjectContributor: NewProjectContributorClient(cfg), + ProjectSyncState: NewProjectSyncStateClient(cfg), + RuntimeBroker: NewRuntimeBrokerClient(cfg), + Schedule: NewScheduleClient(cfg), + ScheduledEvent: NewScheduledEventClient(cfg), + Secret: NewSecretClient(cfg), + SubscriptionTemplate: NewSubscriptionTemplateClient(cfg), + Template: NewTemplateClient(cfg), + User: NewUserClient(cfg), + UserAccessToken: NewUserAccessTokenClient(cfg), }, nil } @@ -216,8 +354,13 @@ func (c *Client) Close() error { // In order to add hooks to a specific client, call: `client.Node.Use(...)`. func (c *Client) Use(hooks ...Hook) { for _, n := range []interface{ Use(...Hook) }{ - c.AccessPolicy, c.Agent, c.Group, c.GroupMembership, c.PolicyBinding, c.Project, - c.User, + c.AccessPolicy, c.Agent, c.AllowListEntry, c.ApiKey, c.BrokerJoinToken, + c.BrokerSecret, c.EnvVar, c.GCPServiceAccount, c.GithubInstallation, c.Group, + c.GroupMembership, c.HarnessConfig, c.InviteCode, c.MaintenanceOperation, + c.MaintenanceOperationRun, c.Message, c.Notification, + c.NotificationSubscription, c.PolicyBinding, c.Project, c.ProjectContributor, + c.ProjectSyncState, c.RuntimeBroker, c.Schedule, c.ScheduledEvent, c.Secret, + c.SubscriptionTemplate, c.Template, c.User, c.UserAccessToken, } { n.Use(hooks...) } @@ -227,8 +370,13 @@ func (c *Client) Use(hooks ...Hook) { // In order to add interceptors to a specific client, call: `client.Node.Intercept(...)`. func (c *Client) Intercept(interceptors ...Interceptor) { for _, n := range []interface{ Intercept(...Interceptor) }{ - c.AccessPolicy, c.Agent, c.Group, c.GroupMembership, c.PolicyBinding, c.Project, - c.User, + c.AccessPolicy, c.Agent, c.AllowListEntry, c.ApiKey, c.BrokerJoinToken, + c.BrokerSecret, c.EnvVar, c.GCPServiceAccount, c.GithubInstallation, c.Group, + c.GroupMembership, c.HarnessConfig, c.InviteCode, c.MaintenanceOperation, + c.MaintenanceOperationRun, c.Message, c.Notification, + c.NotificationSubscription, c.PolicyBinding, c.Project, c.ProjectContributor, + c.ProjectSyncState, c.RuntimeBroker, c.Schedule, c.ScheduledEvent, c.Secret, + c.SubscriptionTemplate, c.Template, c.User, c.UserAccessToken, } { n.Intercept(interceptors...) } @@ -241,16 +389,62 @@ func (c *Client) Mutate(ctx context.Context, m Mutation) (Value, error) { return c.AccessPolicy.mutate(ctx, m) case *AgentMutation: return c.Agent.mutate(ctx, m) + case *AllowListEntryMutation: + return c.AllowListEntry.mutate(ctx, m) + case *ApiKeyMutation: + return c.ApiKey.mutate(ctx, m) + case *BrokerJoinTokenMutation: + return c.BrokerJoinToken.mutate(ctx, m) + case *BrokerSecretMutation: + return c.BrokerSecret.mutate(ctx, m) + case *EnvVarMutation: + return c.EnvVar.mutate(ctx, m) + case *GCPServiceAccountMutation: + return c.GCPServiceAccount.mutate(ctx, m) + case *GithubInstallationMutation: + return c.GithubInstallation.mutate(ctx, m) case *GroupMutation: return c.Group.mutate(ctx, m) case *GroupMembershipMutation: return c.GroupMembership.mutate(ctx, m) + case *HarnessConfigMutation: + return c.HarnessConfig.mutate(ctx, m) + case *InviteCodeMutation: + return c.InviteCode.mutate(ctx, m) + case *MaintenanceOperationMutation: + return c.MaintenanceOperation.mutate(ctx, m) + case *MaintenanceOperationRunMutation: + return c.MaintenanceOperationRun.mutate(ctx, m) + case *MessageMutation: + return c.Message.mutate(ctx, m) + case *NotificationMutation: + return c.Notification.mutate(ctx, m) + case *NotificationSubscriptionMutation: + return c.NotificationSubscription.mutate(ctx, m) case *PolicyBindingMutation: return c.PolicyBinding.mutate(ctx, m) case *ProjectMutation: return c.Project.mutate(ctx, m) + case *ProjectContributorMutation: + return c.ProjectContributor.mutate(ctx, m) + case *ProjectSyncStateMutation: + return c.ProjectSyncState.mutate(ctx, m) + case *RuntimeBrokerMutation: + return c.RuntimeBroker.mutate(ctx, m) + case *ScheduleMutation: + return c.Schedule.mutate(ctx, m) + case *ScheduledEventMutation: + return c.ScheduledEvent.mutate(ctx, m) + case *SecretMutation: + return c.Secret.mutate(ctx, m) + case *SubscriptionTemplateMutation: + return c.SubscriptionTemplate.mutate(ctx, m) + case *TemplateMutation: + return c.Template.mutate(ctx, m) case *UserMutation: return c.User.mutate(ctx, m) + case *UserAccessTokenMutation: + return c.UserAccessToken.mutate(ctx, m) default: return nil, fmt.Errorf("ent: unknown mutation type %T", m) } @@ -618,107 +812,107 @@ func (c *AgentClient) mutate(ctx context.Context, m *AgentMutation) (Value, erro } } -// GroupClient is a client for the Group schema. -type GroupClient struct { +// AllowListEntryClient is a client for the AllowListEntry schema. +type AllowListEntryClient struct { config } -// NewGroupClient returns a client for the Group from the given config. -func NewGroupClient(c config) *GroupClient { - return &GroupClient{config: c} +// NewAllowListEntryClient returns a client for the AllowListEntry from the given config. +func NewAllowListEntryClient(c config) *AllowListEntryClient { + return &AllowListEntryClient{config: c} } // Use adds a list of mutation hooks to the hooks stack. -// A call to `Use(f, g, h)` equals to `group.Hooks(f(g(h())))`. -func (c *GroupClient) Use(hooks ...Hook) { - c.hooks.Group = append(c.hooks.Group, hooks...) +// A call to `Use(f, g, h)` equals to `allowlistentry.Hooks(f(g(h())))`. +func (c *AllowListEntryClient) Use(hooks ...Hook) { + c.hooks.AllowListEntry = append(c.hooks.AllowListEntry, hooks...) } // Intercept adds a list of query interceptors to the interceptors stack. -// A call to `Intercept(f, g, h)` equals to `group.Intercept(f(g(h())))`. -func (c *GroupClient) Intercept(interceptors ...Interceptor) { - c.inters.Group = append(c.inters.Group, interceptors...) +// A call to `Intercept(f, g, h)` equals to `allowlistentry.Intercept(f(g(h())))`. +func (c *AllowListEntryClient) Intercept(interceptors ...Interceptor) { + c.inters.AllowListEntry = append(c.inters.AllowListEntry, interceptors...) } -// Create returns a builder for creating a Group entity. -func (c *GroupClient) Create() *GroupCreate { - mutation := newGroupMutation(c.config, OpCreate) - return &GroupCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +// Create returns a builder for creating a AllowListEntry entity. +func (c *AllowListEntryClient) Create() *AllowListEntryCreate { + mutation := newAllowListEntryMutation(c.config, OpCreate) + return &AllowListEntryCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} } -// CreateBulk returns a builder for creating a bulk of Group entities. -func (c *GroupClient) CreateBulk(builders ...*GroupCreate) *GroupCreateBulk { - return &GroupCreateBulk{config: c.config, builders: builders} +// CreateBulk returns a builder for creating a bulk of AllowListEntry entities. +func (c *AllowListEntryClient) CreateBulk(builders ...*AllowListEntryCreate) *AllowListEntryCreateBulk { + return &AllowListEntryCreateBulk{config: c.config, builders: builders} } // MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates // a builder and applies setFunc on it. -func (c *GroupClient) MapCreateBulk(slice any, setFunc func(*GroupCreate, int)) *GroupCreateBulk { +func (c *AllowListEntryClient) MapCreateBulk(slice any, setFunc func(*AllowListEntryCreate, int)) *AllowListEntryCreateBulk { rv := reflect.ValueOf(slice) if rv.Kind() != reflect.Slice { - return &GroupCreateBulk{err: fmt.Errorf("calling to GroupClient.MapCreateBulk with wrong type %T, need slice", slice)} + return &AllowListEntryCreateBulk{err: fmt.Errorf("calling to AllowListEntryClient.MapCreateBulk with wrong type %T, need slice", slice)} } - builders := make([]*GroupCreate, rv.Len()) + builders := make([]*AllowListEntryCreate, rv.Len()) for i := 0; i < rv.Len(); i++ { builders[i] = c.Create() setFunc(builders[i], i) } - return &GroupCreateBulk{config: c.config, builders: builders} + return &AllowListEntryCreateBulk{config: c.config, builders: builders} } -// Update returns an update builder for Group. -func (c *GroupClient) Update() *GroupUpdate { - mutation := newGroupMutation(c.config, OpUpdate) - return &GroupUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +// Update returns an update builder for AllowListEntry. +func (c *AllowListEntryClient) Update() *AllowListEntryUpdate { + mutation := newAllowListEntryMutation(c.config, OpUpdate) + return &AllowListEntryUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} } // UpdateOne returns an update builder for the given entity. -func (c *GroupClient) UpdateOne(_m *Group) *GroupUpdateOne { - mutation := newGroupMutation(c.config, OpUpdateOne, withGroup(_m)) - return &GroupUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +func (c *AllowListEntryClient) UpdateOne(_m *AllowListEntry) *AllowListEntryUpdateOne { + mutation := newAllowListEntryMutation(c.config, OpUpdateOne, withAllowListEntry(_m)) + return &AllowListEntryUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} } // UpdateOneID returns an update builder for the given id. -func (c *GroupClient) UpdateOneID(id uuid.UUID) *GroupUpdateOne { - mutation := newGroupMutation(c.config, OpUpdateOne, withGroupID(id)) - return &GroupUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +func (c *AllowListEntryClient) UpdateOneID(id uuid.UUID) *AllowListEntryUpdateOne { + mutation := newAllowListEntryMutation(c.config, OpUpdateOne, withAllowListEntryID(id)) + return &AllowListEntryUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} } -// Delete returns a delete builder for Group. -func (c *GroupClient) Delete() *GroupDelete { - mutation := newGroupMutation(c.config, OpDelete) - return &GroupDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +// Delete returns a delete builder for AllowListEntry. +func (c *AllowListEntryClient) Delete() *AllowListEntryDelete { + mutation := newAllowListEntryMutation(c.config, OpDelete) + return &AllowListEntryDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} } // DeleteOne returns a builder for deleting the given entity. -func (c *GroupClient) DeleteOne(_m *Group) *GroupDeleteOne { +func (c *AllowListEntryClient) DeleteOne(_m *AllowListEntry) *AllowListEntryDeleteOne { return c.DeleteOneID(_m.ID) } // DeleteOneID returns a builder for deleting the given entity by its id. -func (c *GroupClient) DeleteOneID(id uuid.UUID) *GroupDeleteOne { - builder := c.Delete().Where(group.ID(id)) +func (c *AllowListEntryClient) DeleteOneID(id uuid.UUID) *AllowListEntryDeleteOne { + builder := c.Delete().Where(allowlistentry.ID(id)) builder.mutation.id = &id builder.mutation.op = OpDeleteOne - return &GroupDeleteOne{builder} + return &AllowListEntryDeleteOne{builder} } -// Query returns a query builder for Group. -func (c *GroupClient) Query() *GroupQuery { - return &GroupQuery{ +// Query returns a query builder for AllowListEntry. +func (c *AllowListEntryClient) Query() *AllowListEntryQuery { + return &AllowListEntryQuery{ config: c.config, - ctx: &QueryContext{Type: TypeGroup}, + ctx: &QueryContext{Type: TypeAllowListEntry}, inters: c.Interceptors(), } } -// Get returns a Group entity by its id. -func (c *GroupClient) Get(ctx context.Context, id uuid.UUID) (*Group, error) { - return c.Query().Where(group.ID(id)).Only(ctx) +// Get returns a AllowListEntry entity by its id. +func (c *AllowListEntryClient) Get(ctx context.Context, id uuid.UUID) (*AllowListEntry, error) { + return c.Query().Where(allowlistentry.ID(id)).Only(ctx) } // GetX is like Get, but panics if an error occurs. -func (c *GroupClient) GetX(ctx context.Context, id uuid.UUID) *Group { +func (c *AllowListEntryClient) GetX(ctx context.Context, id uuid.UUID) *AllowListEntry { obj, err := c.Get(ctx, id) if err != nil { panic(err) @@ -726,212 +920,132 @@ func (c *GroupClient) GetX(ctx context.Context, id uuid.UUID) *Group { return obj } -// QueryMemberships queries the memberships edge of a Group. -func (c *GroupClient) QueryMemberships(_m *Group) *GroupMembershipQuery { - query := (&GroupMembershipClient{config: c.config}).Query() - query.path = func(context.Context) (fromV *sql.Selector, _ error) { - id := _m.ID - step := sqlgraph.NewStep( - sqlgraph.From(group.Table, group.FieldID, id), - sqlgraph.To(groupmembership.Table, groupmembership.FieldID), - sqlgraph.Edge(sqlgraph.O2M, false, group.MembershipsTable, group.MembershipsColumn), - ) - fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step) - return fromV, nil - } - return query -} - -// QueryParentGroups queries the parent_groups edge of a Group. -func (c *GroupClient) QueryParentGroups(_m *Group) *GroupQuery { - query := (&GroupClient{config: c.config}).Query() - query.path = func(context.Context) (fromV *sql.Selector, _ error) { - id := _m.ID - step := sqlgraph.NewStep( - sqlgraph.From(group.Table, group.FieldID, id), - sqlgraph.To(group.Table, group.FieldID), - sqlgraph.Edge(sqlgraph.M2M, true, group.ParentGroupsTable, group.ParentGroupsPrimaryKey...), - ) - fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step) - return fromV, nil - } - return query -} - -// QueryChildGroups queries the child_groups edge of a Group. -func (c *GroupClient) QueryChildGroups(_m *Group) *GroupQuery { - query := (&GroupClient{config: c.config}).Query() - query.path = func(context.Context) (fromV *sql.Selector, _ error) { - id := _m.ID - step := sqlgraph.NewStep( - sqlgraph.From(group.Table, group.FieldID, id), - sqlgraph.To(group.Table, group.FieldID), - sqlgraph.Edge(sqlgraph.M2M, false, group.ChildGroupsTable, group.ChildGroupsPrimaryKey...), - ) - fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step) - return fromV, nil - } - return query -} - -// QueryOwner queries the owner edge of a Group. -func (c *GroupClient) QueryOwner(_m *Group) *UserQuery { - query := (&UserClient{config: c.config}).Query() - query.path = func(context.Context) (fromV *sql.Selector, _ error) { - id := _m.ID - step := sqlgraph.NewStep( - sqlgraph.From(group.Table, group.FieldID, id), - sqlgraph.To(user.Table, user.FieldID), - sqlgraph.Edge(sqlgraph.M2O, true, group.OwnerTable, group.OwnerColumn), - ) - fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step) - return fromV, nil - } - return query -} - -// QueryPolicyBindings queries the policy_bindings edge of a Group. -func (c *GroupClient) QueryPolicyBindings(_m *Group) *PolicyBindingQuery { - query := (&PolicyBindingClient{config: c.config}).Query() - query.path = func(context.Context) (fromV *sql.Selector, _ error) { - id := _m.ID - step := sqlgraph.NewStep( - sqlgraph.From(group.Table, group.FieldID, id), - sqlgraph.To(policybinding.Table, policybinding.FieldID), - sqlgraph.Edge(sqlgraph.O2M, true, group.PolicyBindingsTable, group.PolicyBindingsColumn), - ) - fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step) - return fromV, nil - } - return query -} - // Hooks returns the client hooks. -func (c *GroupClient) Hooks() []Hook { - return c.hooks.Group +func (c *AllowListEntryClient) Hooks() []Hook { + return c.hooks.AllowListEntry } // Interceptors returns the client interceptors. -func (c *GroupClient) Interceptors() []Interceptor { - return c.inters.Group +func (c *AllowListEntryClient) Interceptors() []Interceptor { + return c.inters.AllowListEntry } -func (c *GroupClient) mutate(ctx context.Context, m *GroupMutation) (Value, error) { +func (c *AllowListEntryClient) mutate(ctx context.Context, m *AllowListEntryMutation) (Value, error) { switch m.Op() { case OpCreate: - return (&GroupCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + return (&AllowListEntryCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) case OpUpdate: - return (&GroupUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + return (&AllowListEntryUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) case OpUpdateOne: - return (&GroupUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + return (&AllowListEntryUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) case OpDelete, OpDeleteOne: - return (&GroupDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + return (&AllowListEntryDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) default: - return nil, fmt.Errorf("ent: unknown Group mutation op: %q", m.Op()) + return nil, fmt.Errorf("ent: unknown AllowListEntry mutation op: %q", m.Op()) } } -// GroupMembershipClient is a client for the GroupMembership schema. -type GroupMembershipClient struct { +// ApiKeyClient is a client for the ApiKey schema. +type ApiKeyClient struct { config } -// NewGroupMembershipClient returns a client for the GroupMembership from the given config. -func NewGroupMembershipClient(c config) *GroupMembershipClient { - return &GroupMembershipClient{config: c} +// NewApiKeyClient returns a client for the ApiKey from the given config. +func NewApiKeyClient(c config) *ApiKeyClient { + return &ApiKeyClient{config: c} } // Use adds a list of mutation hooks to the hooks stack. -// A call to `Use(f, g, h)` equals to `groupmembership.Hooks(f(g(h())))`. -func (c *GroupMembershipClient) Use(hooks ...Hook) { - c.hooks.GroupMembership = append(c.hooks.GroupMembership, hooks...) +// A call to `Use(f, g, h)` equals to `apikey.Hooks(f(g(h())))`. +func (c *ApiKeyClient) Use(hooks ...Hook) { + c.hooks.ApiKey = append(c.hooks.ApiKey, hooks...) } // Intercept adds a list of query interceptors to the interceptors stack. -// A call to `Intercept(f, g, h)` equals to `groupmembership.Intercept(f(g(h())))`. -func (c *GroupMembershipClient) Intercept(interceptors ...Interceptor) { - c.inters.GroupMembership = append(c.inters.GroupMembership, interceptors...) +// A call to `Intercept(f, g, h)` equals to `apikey.Intercept(f(g(h())))`. +func (c *ApiKeyClient) Intercept(interceptors ...Interceptor) { + c.inters.ApiKey = append(c.inters.ApiKey, interceptors...) } -// Create returns a builder for creating a GroupMembership entity. -func (c *GroupMembershipClient) Create() *GroupMembershipCreate { - mutation := newGroupMembershipMutation(c.config, OpCreate) - return &GroupMembershipCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +// Create returns a builder for creating a ApiKey entity. +func (c *ApiKeyClient) Create() *ApiKeyCreate { + mutation := newApiKeyMutation(c.config, OpCreate) + return &ApiKeyCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} } -// CreateBulk returns a builder for creating a bulk of GroupMembership entities. -func (c *GroupMembershipClient) CreateBulk(builders ...*GroupMembershipCreate) *GroupMembershipCreateBulk { - return &GroupMembershipCreateBulk{config: c.config, builders: builders} +// CreateBulk returns a builder for creating a bulk of ApiKey entities. +func (c *ApiKeyClient) CreateBulk(builders ...*ApiKeyCreate) *ApiKeyCreateBulk { + return &ApiKeyCreateBulk{config: c.config, builders: builders} } // MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates // a builder and applies setFunc on it. -func (c *GroupMembershipClient) MapCreateBulk(slice any, setFunc func(*GroupMembershipCreate, int)) *GroupMembershipCreateBulk { +func (c *ApiKeyClient) MapCreateBulk(slice any, setFunc func(*ApiKeyCreate, int)) *ApiKeyCreateBulk { rv := reflect.ValueOf(slice) if rv.Kind() != reflect.Slice { - return &GroupMembershipCreateBulk{err: fmt.Errorf("calling to GroupMembershipClient.MapCreateBulk with wrong type %T, need slice", slice)} + return &ApiKeyCreateBulk{err: fmt.Errorf("calling to ApiKeyClient.MapCreateBulk with wrong type %T, need slice", slice)} } - builders := make([]*GroupMembershipCreate, rv.Len()) + builders := make([]*ApiKeyCreate, rv.Len()) for i := 0; i < rv.Len(); i++ { builders[i] = c.Create() setFunc(builders[i], i) } - return &GroupMembershipCreateBulk{config: c.config, builders: builders} + return &ApiKeyCreateBulk{config: c.config, builders: builders} } -// Update returns an update builder for GroupMembership. -func (c *GroupMembershipClient) Update() *GroupMembershipUpdate { - mutation := newGroupMembershipMutation(c.config, OpUpdate) - return &GroupMembershipUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +// Update returns an update builder for ApiKey. +func (c *ApiKeyClient) Update() *ApiKeyUpdate { + mutation := newApiKeyMutation(c.config, OpUpdate) + return &ApiKeyUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} } // UpdateOne returns an update builder for the given entity. -func (c *GroupMembershipClient) UpdateOne(_m *GroupMembership) *GroupMembershipUpdateOne { - mutation := newGroupMembershipMutation(c.config, OpUpdateOne, withGroupMembership(_m)) - return &GroupMembershipUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +func (c *ApiKeyClient) UpdateOne(_m *ApiKey) *ApiKeyUpdateOne { + mutation := newApiKeyMutation(c.config, OpUpdateOne, withApiKey(_m)) + return &ApiKeyUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} } // UpdateOneID returns an update builder for the given id. -func (c *GroupMembershipClient) UpdateOneID(id uuid.UUID) *GroupMembershipUpdateOne { - mutation := newGroupMembershipMutation(c.config, OpUpdateOne, withGroupMembershipID(id)) - return &GroupMembershipUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +func (c *ApiKeyClient) UpdateOneID(id uuid.UUID) *ApiKeyUpdateOne { + mutation := newApiKeyMutation(c.config, OpUpdateOne, withApiKeyID(id)) + return &ApiKeyUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} } -// Delete returns a delete builder for GroupMembership. -func (c *GroupMembershipClient) Delete() *GroupMembershipDelete { - mutation := newGroupMembershipMutation(c.config, OpDelete) - return &GroupMembershipDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +// Delete returns a delete builder for ApiKey. +func (c *ApiKeyClient) Delete() *ApiKeyDelete { + mutation := newApiKeyMutation(c.config, OpDelete) + return &ApiKeyDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} } // DeleteOne returns a builder for deleting the given entity. -func (c *GroupMembershipClient) DeleteOne(_m *GroupMembership) *GroupMembershipDeleteOne { +func (c *ApiKeyClient) DeleteOne(_m *ApiKey) *ApiKeyDeleteOne { return c.DeleteOneID(_m.ID) } // DeleteOneID returns a builder for deleting the given entity by its id. -func (c *GroupMembershipClient) DeleteOneID(id uuid.UUID) *GroupMembershipDeleteOne { - builder := c.Delete().Where(groupmembership.ID(id)) +func (c *ApiKeyClient) DeleteOneID(id uuid.UUID) *ApiKeyDeleteOne { + builder := c.Delete().Where(apikey.ID(id)) builder.mutation.id = &id builder.mutation.op = OpDeleteOne - return &GroupMembershipDeleteOne{builder} + return &ApiKeyDeleteOne{builder} } -// Query returns a query builder for GroupMembership. -func (c *GroupMembershipClient) Query() *GroupMembershipQuery { - return &GroupMembershipQuery{ +// Query returns a query builder for ApiKey. +func (c *ApiKeyClient) Query() *ApiKeyQuery { + return &ApiKeyQuery{ config: c.config, - ctx: &QueryContext{Type: TypeGroupMembership}, + ctx: &QueryContext{Type: TypeApiKey}, inters: c.Interceptors(), } } -// Get returns a GroupMembership entity by its id. -func (c *GroupMembershipClient) Get(ctx context.Context, id uuid.UUID) (*GroupMembership, error) { - return c.Query().Where(groupmembership.ID(id)).Only(ctx) +// Get returns a ApiKey entity by its id. +func (c *ApiKeyClient) Get(ctx context.Context, id uuid.UUID) (*ApiKey, error) { + return c.Query().Where(apikey.ID(id)).Only(ctx) } // GetX is like Get, but panics if an error occurs. -func (c *GroupMembershipClient) GetX(ctx context.Context, id uuid.UUID) *GroupMembership { +func (c *ApiKeyClient) GetX(ctx context.Context, id uuid.UUID) *ApiKey { obj, err := c.Get(ctx, id) if err != nil { panic(err) @@ -939,180 +1053,3266 @@ func (c *GroupMembershipClient) GetX(ctx context.Context, id uuid.UUID) *GroupMe return obj } -// QueryGroup queries the group edge of a GroupMembership. -func (c *GroupMembershipClient) QueryGroup(_m *GroupMembership) *GroupQuery { - query := (&GroupClient{config: c.config}).Query() - query.path = func(context.Context) (fromV *sql.Selector, _ error) { - id := _m.ID - step := sqlgraph.NewStep( - sqlgraph.From(groupmembership.Table, groupmembership.FieldID, id), - sqlgraph.To(group.Table, group.FieldID), - sqlgraph.Edge(sqlgraph.M2O, true, groupmembership.GroupTable, groupmembership.GroupColumn), - ) - fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step) - return fromV, nil - } - return query -} - -// QueryUser queries the user edge of a GroupMembership. -func (c *GroupMembershipClient) QueryUser(_m *GroupMembership) *UserQuery { - query := (&UserClient{config: c.config}).Query() - query.path = func(context.Context) (fromV *sql.Selector, _ error) { - id := _m.ID - step := sqlgraph.NewStep( - sqlgraph.From(groupmembership.Table, groupmembership.FieldID, id), - sqlgraph.To(user.Table, user.FieldID), - sqlgraph.Edge(sqlgraph.M2O, false, groupmembership.UserTable, groupmembership.UserColumn), - ) - fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step) - return fromV, nil - } - return query -} - -// QueryAgent queries the agent edge of a GroupMembership. -func (c *GroupMembershipClient) QueryAgent(_m *GroupMembership) *AgentQuery { - query := (&AgentClient{config: c.config}).Query() - query.path = func(context.Context) (fromV *sql.Selector, _ error) { - id := _m.ID - step := sqlgraph.NewStep( - sqlgraph.From(groupmembership.Table, groupmembership.FieldID, id), - sqlgraph.To(agent.Table, agent.FieldID), - sqlgraph.Edge(sqlgraph.M2O, false, groupmembership.AgentTable, groupmembership.AgentColumn), - ) - fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step) - return fromV, nil - } - return query -} - // Hooks returns the client hooks. -func (c *GroupMembershipClient) Hooks() []Hook { - return c.hooks.GroupMembership +func (c *ApiKeyClient) Hooks() []Hook { + return c.hooks.ApiKey } // Interceptors returns the client interceptors. -func (c *GroupMembershipClient) Interceptors() []Interceptor { - return c.inters.GroupMembership +func (c *ApiKeyClient) Interceptors() []Interceptor { + return c.inters.ApiKey } -func (c *GroupMembershipClient) mutate(ctx context.Context, m *GroupMembershipMutation) (Value, error) { +func (c *ApiKeyClient) mutate(ctx context.Context, m *ApiKeyMutation) (Value, error) { switch m.Op() { case OpCreate: - return (&GroupMembershipCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + return (&ApiKeyCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) case OpUpdate: - return (&GroupMembershipUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + return (&ApiKeyUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) case OpUpdateOne: - return (&GroupMembershipUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + return (&ApiKeyUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) case OpDelete, OpDeleteOne: - return (&GroupMembershipDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + return (&ApiKeyDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) default: - return nil, fmt.Errorf("ent: unknown GroupMembership mutation op: %q", m.Op()) + return nil, fmt.Errorf("ent: unknown ApiKey mutation op: %q", m.Op()) } } -// PolicyBindingClient is a client for the PolicyBinding schema. -type PolicyBindingClient struct { +// BrokerJoinTokenClient is a client for the BrokerJoinToken schema. +type BrokerJoinTokenClient struct { config } -// NewPolicyBindingClient returns a client for the PolicyBinding from the given config. -func NewPolicyBindingClient(c config) *PolicyBindingClient { - return &PolicyBindingClient{config: c} +// NewBrokerJoinTokenClient returns a client for the BrokerJoinToken from the given config. +func NewBrokerJoinTokenClient(c config) *BrokerJoinTokenClient { + return &BrokerJoinTokenClient{config: c} } // Use adds a list of mutation hooks to the hooks stack. -// A call to `Use(f, g, h)` equals to `policybinding.Hooks(f(g(h())))`. -func (c *PolicyBindingClient) Use(hooks ...Hook) { - c.hooks.PolicyBinding = append(c.hooks.PolicyBinding, hooks...) +// A call to `Use(f, g, h)` equals to `brokerjointoken.Hooks(f(g(h())))`. +func (c *BrokerJoinTokenClient) Use(hooks ...Hook) { + c.hooks.BrokerJoinToken = append(c.hooks.BrokerJoinToken, hooks...) } // Intercept adds a list of query interceptors to the interceptors stack. -// A call to `Intercept(f, g, h)` equals to `policybinding.Intercept(f(g(h())))`. -func (c *PolicyBindingClient) Intercept(interceptors ...Interceptor) { - c.inters.PolicyBinding = append(c.inters.PolicyBinding, interceptors...) +// A call to `Intercept(f, g, h)` equals to `brokerjointoken.Intercept(f(g(h())))`. +func (c *BrokerJoinTokenClient) Intercept(interceptors ...Interceptor) { + c.inters.BrokerJoinToken = append(c.inters.BrokerJoinToken, interceptors...) } -// Create returns a builder for creating a PolicyBinding entity. +// Create returns a builder for creating a BrokerJoinToken entity. +func (c *BrokerJoinTokenClient) Create() *BrokerJoinTokenCreate { + mutation := newBrokerJoinTokenMutation(c.config, OpCreate) + return &BrokerJoinTokenCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of BrokerJoinToken entities. +func (c *BrokerJoinTokenClient) CreateBulk(builders ...*BrokerJoinTokenCreate) *BrokerJoinTokenCreateBulk { + return &BrokerJoinTokenCreateBulk{config: c.config, builders: builders} +} + +// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates +// a builder and applies setFunc on it. +func (c *BrokerJoinTokenClient) MapCreateBulk(slice any, setFunc func(*BrokerJoinTokenCreate, int)) *BrokerJoinTokenCreateBulk { + rv := reflect.ValueOf(slice) + if rv.Kind() != reflect.Slice { + return &BrokerJoinTokenCreateBulk{err: fmt.Errorf("calling to BrokerJoinTokenClient.MapCreateBulk with wrong type %T, need slice", slice)} + } + builders := make([]*BrokerJoinTokenCreate, rv.Len()) + for i := 0; i < rv.Len(); i++ { + builders[i] = c.Create() + setFunc(builders[i], i) + } + return &BrokerJoinTokenCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for BrokerJoinToken. +func (c *BrokerJoinTokenClient) Update() *BrokerJoinTokenUpdate { + mutation := newBrokerJoinTokenMutation(c.config, OpUpdate) + return &BrokerJoinTokenUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *BrokerJoinTokenClient) UpdateOne(_m *BrokerJoinToken) *BrokerJoinTokenUpdateOne { + mutation := newBrokerJoinTokenMutation(c.config, OpUpdateOne, withBrokerJoinToken(_m)) + return &BrokerJoinTokenUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOneID returns an update builder for the given id. +func (c *BrokerJoinTokenClient) UpdateOneID(id uuid.UUID) *BrokerJoinTokenUpdateOne { + mutation := newBrokerJoinTokenMutation(c.config, OpUpdateOne, withBrokerJoinTokenID(id)) + return &BrokerJoinTokenUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for BrokerJoinToken. +func (c *BrokerJoinTokenClient) Delete() *BrokerJoinTokenDelete { + mutation := newBrokerJoinTokenMutation(c.config, OpDelete) + return &BrokerJoinTokenDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// DeleteOne returns a builder for deleting the given entity. +func (c *BrokerJoinTokenClient) DeleteOne(_m *BrokerJoinToken) *BrokerJoinTokenDeleteOne { + return c.DeleteOneID(_m.ID) +} + +// DeleteOneID returns a builder for deleting the given entity by its id. +func (c *BrokerJoinTokenClient) DeleteOneID(id uuid.UUID) *BrokerJoinTokenDeleteOne { + builder := c.Delete().Where(brokerjointoken.ID(id)) + builder.mutation.id = &id + builder.mutation.op = OpDeleteOne + return &BrokerJoinTokenDeleteOne{builder} +} + +// Query returns a query builder for BrokerJoinToken. +func (c *BrokerJoinTokenClient) Query() *BrokerJoinTokenQuery { + return &BrokerJoinTokenQuery{ + config: c.config, + ctx: &QueryContext{Type: TypeBrokerJoinToken}, + inters: c.Interceptors(), + } +} + +// Get returns a BrokerJoinToken entity by its id. +func (c *BrokerJoinTokenClient) Get(ctx context.Context, id uuid.UUID) (*BrokerJoinToken, error) { + return c.Query().Where(brokerjointoken.ID(id)).Only(ctx) +} + +// GetX is like Get, but panics if an error occurs. +func (c *BrokerJoinTokenClient) GetX(ctx context.Context, id uuid.UUID) *BrokerJoinToken { + obj, err := c.Get(ctx, id) + if err != nil { + panic(err) + } + return obj +} + +// Hooks returns the client hooks. +func (c *BrokerJoinTokenClient) Hooks() []Hook { + return c.hooks.BrokerJoinToken +} + +// Interceptors returns the client interceptors. +func (c *BrokerJoinTokenClient) Interceptors() []Interceptor { + return c.inters.BrokerJoinToken +} + +func (c *BrokerJoinTokenClient) mutate(ctx context.Context, m *BrokerJoinTokenMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&BrokerJoinTokenCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&BrokerJoinTokenUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&BrokerJoinTokenUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&BrokerJoinTokenDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("ent: unknown BrokerJoinToken mutation op: %q", m.Op()) + } +} + +// BrokerSecretClient is a client for the BrokerSecret schema. +type BrokerSecretClient struct { + config +} + +// NewBrokerSecretClient returns a client for the BrokerSecret from the given config. +func NewBrokerSecretClient(c config) *BrokerSecretClient { + return &BrokerSecretClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `brokersecret.Hooks(f(g(h())))`. +func (c *BrokerSecretClient) Use(hooks ...Hook) { + c.hooks.BrokerSecret = append(c.hooks.BrokerSecret, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `brokersecret.Intercept(f(g(h())))`. +func (c *BrokerSecretClient) Intercept(interceptors ...Interceptor) { + c.inters.BrokerSecret = append(c.inters.BrokerSecret, interceptors...) +} + +// Create returns a builder for creating a BrokerSecret entity. +func (c *BrokerSecretClient) Create() *BrokerSecretCreate { + mutation := newBrokerSecretMutation(c.config, OpCreate) + return &BrokerSecretCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of BrokerSecret entities. +func (c *BrokerSecretClient) CreateBulk(builders ...*BrokerSecretCreate) *BrokerSecretCreateBulk { + return &BrokerSecretCreateBulk{config: c.config, builders: builders} +} + +// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates +// a builder and applies setFunc on it. +func (c *BrokerSecretClient) MapCreateBulk(slice any, setFunc func(*BrokerSecretCreate, int)) *BrokerSecretCreateBulk { + rv := reflect.ValueOf(slice) + if rv.Kind() != reflect.Slice { + return &BrokerSecretCreateBulk{err: fmt.Errorf("calling to BrokerSecretClient.MapCreateBulk with wrong type %T, need slice", slice)} + } + builders := make([]*BrokerSecretCreate, rv.Len()) + for i := 0; i < rv.Len(); i++ { + builders[i] = c.Create() + setFunc(builders[i], i) + } + return &BrokerSecretCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for BrokerSecret. +func (c *BrokerSecretClient) Update() *BrokerSecretUpdate { + mutation := newBrokerSecretMutation(c.config, OpUpdate) + return &BrokerSecretUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *BrokerSecretClient) UpdateOne(_m *BrokerSecret) *BrokerSecretUpdateOne { + mutation := newBrokerSecretMutation(c.config, OpUpdateOne, withBrokerSecret(_m)) + return &BrokerSecretUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOneID returns an update builder for the given id. +func (c *BrokerSecretClient) UpdateOneID(id uuid.UUID) *BrokerSecretUpdateOne { + mutation := newBrokerSecretMutation(c.config, OpUpdateOne, withBrokerSecretID(id)) + return &BrokerSecretUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for BrokerSecret. +func (c *BrokerSecretClient) Delete() *BrokerSecretDelete { + mutation := newBrokerSecretMutation(c.config, OpDelete) + return &BrokerSecretDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// DeleteOne returns a builder for deleting the given entity. +func (c *BrokerSecretClient) DeleteOne(_m *BrokerSecret) *BrokerSecretDeleteOne { + return c.DeleteOneID(_m.ID) +} + +// DeleteOneID returns a builder for deleting the given entity by its id. +func (c *BrokerSecretClient) DeleteOneID(id uuid.UUID) *BrokerSecretDeleteOne { + builder := c.Delete().Where(brokersecret.ID(id)) + builder.mutation.id = &id + builder.mutation.op = OpDeleteOne + return &BrokerSecretDeleteOne{builder} +} + +// Query returns a query builder for BrokerSecret. +func (c *BrokerSecretClient) Query() *BrokerSecretQuery { + return &BrokerSecretQuery{ + config: c.config, + ctx: &QueryContext{Type: TypeBrokerSecret}, + inters: c.Interceptors(), + } +} + +// Get returns a BrokerSecret entity by its id. +func (c *BrokerSecretClient) Get(ctx context.Context, id uuid.UUID) (*BrokerSecret, error) { + return c.Query().Where(brokersecret.ID(id)).Only(ctx) +} + +// GetX is like Get, but panics if an error occurs. +func (c *BrokerSecretClient) GetX(ctx context.Context, id uuid.UUID) *BrokerSecret { + obj, err := c.Get(ctx, id) + if err != nil { + panic(err) + } + return obj +} + +// Hooks returns the client hooks. +func (c *BrokerSecretClient) Hooks() []Hook { + return c.hooks.BrokerSecret +} + +// Interceptors returns the client interceptors. +func (c *BrokerSecretClient) Interceptors() []Interceptor { + return c.inters.BrokerSecret +} + +func (c *BrokerSecretClient) mutate(ctx context.Context, m *BrokerSecretMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&BrokerSecretCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&BrokerSecretUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&BrokerSecretUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&BrokerSecretDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("ent: unknown BrokerSecret mutation op: %q", m.Op()) + } +} + +// EnvVarClient is a client for the EnvVar schema. +type EnvVarClient struct { + config +} + +// NewEnvVarClient returns a client for the EnvVar from the given config. +func NewEnvVarClient(c config) *EnvVarClient { + return &EnvVarClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `envvar.Hooks(f(g(h())))`. +func (c *EnvVarClient) Use(hooks ...Hook) { + c.hooks.EnvVar = append(c.hooks.EnvVar, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `envvar.Intercept(f(g(h())))`. +func (c *EnvVarClient) Intercept(interceptors ...Interceptor) { + c.inters.EnvVar = append(c.inters.EnvVar, interceptors...) +} + +// Create returns a builder for creating a EnvVar entity. +func (c *EnvVarClient) Create() *EnvVarCreate { + mutation := newEnvVarMutation(c.config, OpCreate) + return &EnvVarCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of EnvVar entities. +func (c *EnvVarClient) CreateBulk(builders ...*EnvVarCreate) *EnvVarCreateBulk { + return &EnvVarCreateBulk{config: c.config, builders: builders} +} + +// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates +// a builder and applies setFunc on it. +func (c *EnvVarClient) MapCreateBulk(slice any, setFunc func(*EnvVarCreate, int)) *EnvVarCreateBulk { + rv := reflect.ValueOf(slice) + if rv.Kind() != reflect.Slice { + return &EnvVarCreateBulk{err: fmt.Errorf("calling to EnvVarClient.MapCreateBulk with wrong type %T, need slice", slice)} + } + builders := make([]*EnvVarCreate, rv.Len()) + for i := 0; i < rv.Len(); i++ { + builders[i] = c.Create() + setFunc(builders[i], i) + } + return &EnvVarCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for EnvVar. +func (c *EnvVarClient) Update() *EnvVarUpdate { + mutation := newEnvVarMutation(c.config, OpUpdate) + return &EnvVarUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *EnvVarClient) UpdateOne(_m *EnvVar) *EnvVarUpdateOne { + mutation := newEnvVarMutation(c.config, OpUpdateOne, withEnvVar(_m)) + return &EnvVarUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOneID returns an update builder for the given id. +func (c *EnvVarClient) UpdateOneID(id uuid.UUID) *EnvVarUpdateOne { + mutation := newEnvVarMutation(c.config, OpUpdateOne, withEnvVarID(id)) + return &EnvVarUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for EnvVar. +func (c *EnvVarClient) Delete() *EnvVarDelete { + mutation := newEnvVarMutation(c.config, OpDelete) + return &EnvVarDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// DeleteOne returns a builder for deleting the given entity. +func (c *EnvVarClient) DeleteOne(_m *EnvVar) *EnvVarDeleteOne { + return c.DeleteOneID(_m.ID) +} + +// DeleteOneID returns a builder for deleting the given entity by its id. +func (c *EnvVarClient) DeleteOneID(id uuid.UUID) *EnvVarDeleteOne { + builder := c.Delete().Where(envvar.ID(id)) + builder.mutation.id = &id + builder.mutation.op = OpDeleteOne + return &EnvVarDeleteOne{builder} +} + +// Query returns a query builder for EnvVar. +func (c *EnvVarClient) Query() *EnvVarQuery { + return &EnvVarQuery{ + config: c.config, + ctx: &QueryContext{Type: TypeEnvVar}, + inters: c.Interceptors(), + } +} + +// Get returns a EnvVar entity by its id. +func (c *EnvVarClient) Get(ctx context.Context, id uuid.UUID) (*EnvVar, error) { + return c.Query().Where(envvar.ID(id)).Only(ctx) +} + +// GetX is like Get, but panics if an error occurs. +func (c *EnvVarClient) GetX(ctx context.Context, id uuid.UUID) *EnvVar { + obj, err := c.Get(ctx, id) + if err != nil { + panic(err) + } + return obj +} + +// Hooks returns the client hooks. +func (c *EnvVarClient) Hooks() []Hook { + return c.hooks.EnvVar +} + +// Interceptors returns the client interceptors. +func (c *EnvVarClient) Interceptors() []Interceptor { + return c.inters.EnvVar +} + +func (c *EnvVarClient) mutate(ctx context.Context, m *EnvVarMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&EnvVarCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&EnvVarUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&EnvVarUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&EnvVarDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("ent: unknown EnvVar mutation op: %q", m.Op()) + } +} + +// GCPServiceAccountClient is a client for the GCPServiceAccount schema. +type GCPServiceAccountClient struct { + config +} + +// NewGCPServiceAccountClient returns a client for the GCPServiceAccount from the given config. +func NewGCPServiceAccountClient(c config) *GCPServiceAccountClient { + return &GCPServiceAccountClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `gcpserviceaccount.Hooks(f(g(h())))`. +func (c *GCPServiceAccountClient) Use(hooks ...Hook) { + c.hooks.GCPServiceAccount = append(c.hooks.GCPServiceAccount, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `gcpserviceaccount.Intercept(f(g(h())))`. +func (c *GCPServiceAccountClient) Intercept(interceptors ...Interceptor) { + c.inters.GCPServiceAccount = append(c.inters.GCPServiceAccount, interceptors...) +} + +// Create returns a builder for creating a GCPServiceAccount entity. +func (c *GCPServiceAccountClient) Create() *GCPServiceAccountCreate { + mutation := newGCPServiceAccountMutation(c.config, OpCreate) + return &GCPServiceAccountCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of GCPServiceAccount entities. +func (c *GCPServiceAccountClient) CreateBulk(builders ...*GCPServiceAccountCreate) *GCPServiceAccountCreateBulk { + return &GCPServiceAccountCreateBulk{config: c.config, builders: builders} +} + +// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates +// a builder and applies setFunc on it. +func (c *GCPServiceAccountClient) MapCreateBulk(slice any, setFunc func(*GCPServiceAccountCreate, int)) *GCPServiceAccountCreateBulk { + rv := reflect.ValueOf(slice) + if rv.Kind() != reflect.Slice { + return &GCPServiceAccountCreateBulk{err: fmt.Errorf("calling to GCPServiceAccountClient.MapCreateBulk with wrong type %T, need slice", slice)} + } + builders := make([]*GCPServiceAccountCreate, rv.Len()) + for i := 0; i < rv.Len(); i++ { + builders[i] = c.Create() + setFunc(builders[i], i) + } + return &GCPServiceAccountCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for GCPServiceAccount. +func (c *GCPServiceAccountClient) Update() *GCPServiceAccountUpdate { + mutation := newGCPServiceAccountMutation(c.config, OpUpdate) + return &GCPServiceAccountUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *GCPServiceAccountClient) UpdateOne(_m *GCPServiceAccount) *GCPServiceAccountUpdateOne { + mutation := newGCPServiceAccountMutation(c.config, OpUpdateOne, withGCPServiceAccount(_m)) + return &GCPServiceAccountUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOneID returns an update builder for the given id. +func (c *GCPServiceAccountClient) UpdateOneID(id uuid.UUID) *GCPServiceAccountUpdateOne { + mutation := newGCPServiceAccountMutation(c.config, OpUpdateOne, withGCPServiceAccountID(id)) + return &GCPServiceAccountUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for GCPServiceAccount. +func (c *GCPServiceAccountClient) Delete() *GCPServiceAccountDelete { + mutation := newGCPServiceAccountMutation(c.config, OpDelete) + return &GCPServiceAccountDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// DeleteOne returns a builder for deleting the given entity. +func (c *GCPServiceAccountClient) DeleteOne(_m *GCPServiceAccount) *GCPServiceAccountDeleteOne { + return c.DeleteOneID(_m.ID) +} + +// DeleteOneID returns a builder for deleting the given entity by its id. +func (c *GCPServiceAccountClient) DeleteOneID(id uuid.UUID) *GCPServiceAccountDeleteOne { + builder := c.Delete().Where(gcpserviceaccount.ID(id)) + builder.mutation.id = &id + builder.mutation.op = OpDeleteOne + return &GCPServiceAccountDeleteOne{builder} +} + +// Query returns a query builder for GCPServiceAccount. +func (c *GCPServiceAccountClient) Query() *GCPServiceAccountQuery { + return &GCPServiceAccountQuery{ + config: c.config, + ctx: &QueryContext{Type: TypeGCPServiceAccount}, + inters: c.Interceptors(), + } +} + +// Get returns a GCPServiceAccount entity by its id. +func (c *GCPServiceAccountClient) Get(ctx context.Context, id uuid.UUID) (*GCPServiceAccount, error) { + return c.Query().Where(gcpserviceaccount.ID(id)).Only(ctx) +} + +// GetX is like Get, but panics if an error occurs. +func (c *GCPServiceAccountClient) GetX(ctx context.Context, id uuid.UUID) *GCPServiceAccount { + obj, err := c.Get(ctx, id) + if err != nil { + panic(err) + } + return obj +} + +// Hooks returns the client hooks. +func (c *GCPServiceAccountClient) Hooks() []Hook { + return c.hooks.GCPServiceAccount +} + +// Interceptors returns the client interceptors. +func (c *GCPServiceAccountClient) Interceptors() []Interceptor { + return c.inters.GCPServiceAccount +} + +func (c *GCPServiceAccountClient) mutate(ctx context.Context, m *GCPServiceAccountMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&GCPServiceAccountCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&GCPServiceAccountUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&GCPServiceAccountUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&GCPServiceAccountDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("ent: unknown GCPServiceAccount mutation op: %q", m.Op()) + } +} + +// GithubInstallationClient is a client for the GithubInstallation schema. +type GithubInstallationClient struct { + config +} + +// NewGithubInstallationClient returns a client for the GithubInstallation from the given config. +func NewGithubInstallationClient(c config) *GithubInstallationClient { + return &GithubInstallationClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `githubinstallation.Hooks(f(g(h())))`. +func (c *GithubInstallationClient) Use(hooks ...Hook) { + c.hooks.GithubInstallation = append(c.hooks.GithubInstallation, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `githubinstallation.Intercept(f(g(h())))`. +func (c *GithubInstallationClient) Intercept(interceptors ...Interceptor) { + c.inters.GithubInstallation = append(c.inters.GithubInstallation, interceptors...) +} + +// Create returns a builder for creating a GithubInstallation entity. +func (c *GithubInstallationClient) Create() *GithubInstallationCreate { + mutation := newGithubInstallationMutation(c.config, OpCreate) + return &GithubInstallationCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of GithubInstallation entities. +func (c *GithubInstallationClient) CreateBulk(builders ...*GithubInstallationCreate) *GithubInstallationCreateBulk { + return &GithubInstallationCreateBulk{config: c.config, builders: builders} +} + +// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates +// a builder and applies setFunc on it. +func (c *GithubInstallationClient) MapCreateBulk(slice any, setFunc func(*GithubInstallationCreate, int)) *GithubInstallationCreateBulk { + rv := reflect.ValueOf(slice) + if rv.Kind() != reflect.Slice { + return &GithubInstallationCreateBulk{err: fmt.Errorf("calling to GithubInstallationClient.MapCreateBulk with wrong type %T, need slice", slice)} + } + builders := make([]*GithubInstallationCreate, rv.Len()) + for i := 0; i < rv.Len(); i++ { + builders[i] = c.Create() + setFunc(builders[i], i) + } + return &GithubInstallationCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for GithubInstallation. +func (c *GithubInstallationClient) Update() *GithubInstallationUpdate { + mutation := newGithubInstallationMutation(c.config, OpUpdate) + return &GithubInstallationUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *GithubInstallationClient) UpdateOne(_m *GithubInstallation) *GithubInstallationUpdateOne { + mutation := newGithubInstallationMutation(c.config, OpUpdateOne, withGithubInstallation(_m)) + return &GithubInstallationUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOneID returns an update builder for the given id. +func (c *GithubInstallationClient) UpdateOneID(id int64) *GithubInstallationUpdateOne { + mutation := newGithubInstallationMutation(c.config, OpUpdateOne, withGithubInstallationID(id)) + return &GithubInstallationUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for GithubInstallation. +func (c *GithubInstallationClient) Delete() *GithubInstallationDelete { + mutation := newGithubInstallationMutation(c.config, OpDelete) + return &GithubInstallationDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// DeleteOne returns a builder for deleting the given entity. +func (c *GithubInstallationClient) DeleteOne(_m *GithubInstallation) *GithubInstallationDeleteOne { + return c.DeleteOneID(_m.ID) +} + +// DeleteOneID returns a builder for deleting the given entity by its id. +func (c *GithubInstallationClient) DeleteOneID(id int64) *GithubInstallationDeleteOne { + builder := c.Delete().Where(githubinstallation.ID(id)) + builder.mutation.id = &id + builder.mutation.op = OpDeleteOne + return &GithubInstallationDeleteOne{builder} +} + +// Query returns a query builder for GithubInstallation. +func (c *GithubInstallationClient) Query() *GithubInstallationQuery { + return &GithubInstallationQuery{ + config: c.config, + ctx: &QueryContext{Type: TypeGithubInstallation}, + inters: c.Interceptors(), + } +} + +// Get returns a GithubInstallation entity by its id. +func (c *GithubInstallationClient) Get(ctx context.Context, id int64) (*GithubInstallation, error) { + return c.Query().Where(githubinstallation.ID(id)).Only(ctx) +} + +// GetX is like Get, but panics if an error occurs. +func (c *GithubInstallationClient) GetX(ctx context.Context, id int64) *GithubInstallation { + obj, err := c.Get(ctx, id) + if err != nil { + panic(err) + } + return obj +} + +// Hooks returns the client hooks. +func (c *GithubInstallationClient) Hooks() []Hook { + return c.hooks.GithubInstallation +} + +// Interceptors returns the client interceptors. +func (c *GithubInstallationClient) Interceptors() []Interceptor { + return c.inters.GithubInstallation +} + +func (c *GithubInstallationClient) mutate(ctx context.Context, m *GithubInstallationMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&GithubInstallationCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&GithubInstallationUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&GithubInstallationUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&GithubInstallationDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("ent: unknown GithubInstallation mutation op: %q", m.Op()) + } +} + +// GroupClient is a client for the Group schema. +type GroupClient struct { + config +} + +// NewGroupClient returns a client for the Group from the given config. +func NewGroupClient(c config) *GroupClient { + return &GroupClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `group.Hooks(f(g(h())))`. +func (c *GroupClient) Use(hooks ...Hook) { + c.hooks.Group = append(c.hooks.Group, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `group.Intercept(f(g(h())))`. +func (c *GroupClient) Intercept(interceptors ...Interceptor) { + c.inters.Group = append(c.inters.Group, interceptors...) +} + +// Create returns a builder for creating a Group entity. +func (c *GroupClient) Create() *GroupCreate { + mutation := newGroupMutation(c.config, OpCreate) + return &GroupCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of Group entities. +func (c *GroupClient) CreateBulk(builders ...*GroupCreate) *GroupCreateBulk { + return &GroupCreateBulk{config: c.config, builders: builders} +} + +// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates +// a builder and applies setFunc on it. +func (c *GroupClient) MapCreateBulk(slice any, setFunc func(*GroupCreate, int)) *GroupCreateBulk { + rv := reflect.ValueOf(slice) + if rv.Kind() != reflect.Slice { + return &GroupCreateBulk{err: fmt.Errorf("calling to GroupClient.MapCreateBulk with wrong type %T, need slice", slice)} + } + builders := make([]*GroupCreate, rv.Len()) + for i := 0; i < rv.Len(); i++ { + builders[i] = c.Create() + setFunc(builders[i], i) + } + return &GroupCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for Group. +func (c *GroupClient) Update() *GroupUpdate { + mutation := newGroupMutation(c.config, OpUpdate) + return &GroupUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *GroupClient) UpdateOne(_m *Group) *GroupUpdateOne { + mutation := newGroupMutation(c.config, OpUpdateOne, withGroup(_m)) + return &GroupUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOneID returns an update builder for the given id. +func (c *GroupClient) UpdateOneID(id uuid.UUID) *GroupUpdateOne { + mutation := newGroupMutation(c.config, OpUpdateOne, withGroupID(id)) + return &GroupUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for Group. +func (c *GroupClient) Delete() *GroupDelete { + mutation := newGroupMutation(c.config, OpDelete) + return &GroupDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// DeleteOne returns a builder for deleting the given entity. +func (c *GroupClient) DeleteOne(_m *Group) *GroupDeleteOne { + return c.DeleteOneID(_m.ID) +} + +// DeleteOneID returns a builder for deleting the given entity by its id. +func (c *GroupClient) DeleteOneID(id uuid.UUID) *GroupDeleteOne { + builder := c.Delete().Where(group.ID(id)) + builder.mutation.id = &id + builder.mutation.op = OpDeleteOne + return &GroupDeleteOne{builder} +} + +// Query returns a query builder for Group. +func (c *GroupClient) Query() *GroupQuery { + return &GroupQuery{ + config: c.config, + ctx: &QueryContext{Type: TypeGroup}, + inters: c.Interceptors(), + } +} + +// Get returns a Group entity by its id. +func (c *GroupClient) Get(ctx context.Context, id uuid.UUID) (*Group, error) { + return c.Query().Where(group.ID(id)).Only(ctx) +} + +// GetX is like Get, but panics if an error occurs. +func (c *GroupClient) GetX(ctx context.Context, id uuid.UUID) *Group { + obj, err := c.Get(ctx, id) + if err != nil { + panic(err) + } + return obj +} + +// QueryMemberships queries the memberships edge of a Group. +func (c *GroupClient) QueryMemberships(_m *Group) *GroupMembershipQuery { + query := (&GroupMembershipClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := _m.ID + step := sqlgraph.NewStep( + sqlgraph.From(group.Table, group.FieldID, id), + sqlgraph.To(groupmembership.Table, groupmembership.FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, group.MembershipsTable, group.MembershipsColumn), + ) + fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// QueryParentGroups queries the parent_groups edge of a Group. +func (c *GroupClient) QueryParentGroups(_m *Group) *GroupQuery { + query := (&GroupClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := _m.ID + step := sqlgraph.NewStep( + sqlgraph.From(group.Table, group.FieldID, id), + sqlgraph.To(group.Table, group.FieldID), + sqlgraph.Edge(sqlgraph.M2M, true, group.ParentGroupsTable, group.ParentGroupsPrimaryKey...), + ) + fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// QueryChildGroups queries the child_groups edge of a Group. +func (c *GroupClient) QueryChildGroups(_m *Group) *GroupQuery { + query := (&GroupClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := _m.ID + step := sqlgraph.NewStep( + sqlgraph.From(group.Table, group.FieldID, id), + sqlgraph.To(group.Table, group.FieldID), + sqlgraph.Edge(sqlgraph.M2M, false, group.ChildGroupsTable, group.ChildGroupsPrimaryKey...), + ) + fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// QueryOwner queries the owner edge of a Group. +func (c *GroupClient) QueryOwner(_m *Group) *UserQuery { + query := (&UserClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := _m.ID + step := sqlgraph.NewStep( + sqlgraph.From(group.Table, group.FieldID, id), + sqlgraph.To(user.Table, user.FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, group.OwnerTable, group.OwnerColumn), + ) + fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// QueryPolicyBindings queries the policy_bindings edge of a Group. +func (c *GroupClient) QueryPolicyBindings(_m *Group) *PolicyBindingQuery { + query := (&PolicyBindingClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := _m.ID + step := sqlgraph.NewStep( + sqlgraph.From(group.Table, group.FieldID, id), + sqlgraph.To(policybinding.Table, policybinding.FieldID), + sqlgraph.Edge(sqlgraph.O2M, true, group.PolicyBindingsTable, group.PolicyBindingsColumn), + ) + fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// Hooks returns the client hooks. +func (c *GroupClient) Hooks() []Hook { + return c.hooks.Group +} + +// Interceptors returns the client interceptors. +func (c *GroupClient) Interceptors() []Interceptor { + return c.inters.Group +} + +func (c *GroupClient) mutate(ctx context.Context, m *GroupMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&GroupCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&GroupUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&GroupUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&GroupDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("ent: unknown Group mutation op: %q", m.Op()) + } +} + +// GroupMembershipClient is a client for the GroupMembership schema. +type GroupMembershipClient struct { + config +} + +// NewGroupMembershipClient returns a client for the GroupMembership from the given config. +func NewGroupMembershipClient(c config) *GroupMembershipClient { + return &GroupMembershipClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `groupmembership.Hooks(f(g(h())))`. +func (c *GroupMembershipClient) Use(hooks ...Hook) { + c.hooks.GroupMembership = append(c.hooks.GroupMembership, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `groupmembership.Intercept(f(g(h())))`. +func (c *GroupMembershipClient) Intercept(interceptors ...Interceptor) { + c.inters.GroupMembership = append(c.inters.GroupMembership, interceptors...) +} + +// Create returns a builder for creating a GroupMembership entity. +func (c *GroupMembershipClient) Create() *GroupMembershipCreate { + mutation := newGroupMembershipMutation(c.config, OpCreate) + return &GroupMembershipCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of GroupMembership entities. +func (c *GroupMembershipClient) CreateBulk(builders ...*GroupMembershipCreate) *GroupMembershipCreateBulk { + return &GroupMembershipCreateBulk{config: c.config, builders: builders} +} + +// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates +// a builder and applies setFunc on it. +func (c *GroupMembershipClient) MapCreateBulk(slice any, setFunc func(*GroupMembershipCreate, int)) *GroupMembershipCreateBulk { + rv := reflect.ValueOf(slice) + if rv.Kind() != reflect.Slice { + return &GroupMembershipCreateBulk{err: fmt.Errorf("calling to GroupMembershipClient.MapCreateBulk with wrong type %T, need slice", slice)} + } + builders := make([]*GroupMembershipCreate, rv.Len()) + for i := 0; i < rv.Len(); i++ { + builders[i] = c.Create() + setFunc(builders[i], i) + } + return &GroupMembershipCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for GroupMembership. +func (c *GroupMembershipClient) Update() *GroupMembershipUpdate { + mutation := newGroupMembershipMutation(c.config, OpUpdate) + return &GroupMembershipUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *GroupMembershipClient) UpdateOne(_m *GroupMembership) *GroupMembershipUpdateOne { + mutation := newGroupMembershipMutation(c.config, OpUpdateOne, withGroupMembership(_m)) + return &GroupMembershipUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOneID returns an update builder for the given id. +func (c *GroupMembershipClient) UpdateOneID(id uuid.UUID) *GroupMembershipUpdateOne { + mutation := newGroupMembershipMutation(c.config, OpUpdateOne, withGroupMembershipID(id)) + return &GroupMembershipUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for GroupMembership. +func (c *GroupMembershipClient) Delete() *GroupMembershipDelete { + mutation := newGroupMembershipMutation(c.config, OpDelete) + return &GroupMembershipDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// DeleteOne returns a builder for deleting the given entity. +func (c *GroupMembershipClient) DeleteOne(_m *GroupMembership) *GroupMembershipDeleteOne { + return c.DeleteOneID(_m.ID) +} + +// DeleteOneID returns a builder for deleting the given entity by its id. +func (c *GroupMembershipClient) DeleteOneID(id uuid.UUID) *GroupMembershipDeleteOne { + builder := c.Delete().Where(groupmembership.ID(id)) + builder.mutation.id = &id + builder.mutation.op = OpDeleteOne + return &GroupMembershipDeleteOne{builder} +} + +// Query returns a query builder for GroupMembership. +func (c *GroupMembershipClient) Query() *GroupMembershipQuery { + return &GroupMembershipQuery{ + config: c.config, + ctx: &QueryContext{Type: TypeGroupMembership}, + inters: c.Interceptors(), + } +} + +// Get returns a GroupMembership entity by its id. +func (c *GroupMembershipClient) Get(ctx context.Context, id uuid.UUID) (*GroupMembership, error) { + return c.Query().Where(groupmembership.ID(id)).Only(ctx) +} + +// GetX is like Get, but panics if an error occurs. +func (c *GroupMembershipClient) GetX(ctx context.Context, id uuid.UUID) *GroupMembership { + obj, err := c.Get(ctx, id) + if err != nil { + panic(err) + } + return obj +} + +// QueryGroup queries the group edge of a GroupMembership. +func (c *GroupMembershipClient) QueryGroup(_m *GroupMembership) *GroupQuery { + query := (&GroupClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := _m.ID + step := sqlgraph.NewStep( + sqlgraph.From(groupmembership.Table, groupmembership.FieldID, id), + sqlgraph.To(group.Table, group.FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, groupmembership.GroupTable, groupmembership.GroupColumn), + ) + fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// QueryUser queries the user edge of a GroupMembership. +func (c *GroupMembershipClient) QueryUser(_m *GroupMembership) *UserQuery { + query := (&UserClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := _m.ID + step := sqlgraph.NewStep( + sqlgraph.From(groupmembership.Table, groupmembership.FieldID, id), + sqlgraph.To(user.Table, user.FieldID), + sqlgraph.Edge(sqlgraph.M2O, false, groupmembership.UserTable, groupmembership.UserColumn), + ) + fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// QueryAgent queries the agent edge of a GroupMembership. +func (c *GroupMembershipClient) QueryAgent(_m *GroupMembership) *AgentQuery { + query := (&AgentClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := _m.ID + step := sqlgraph.NewStep( + sqlgraph.From(groupmembership.Table, groupmembership.FieldID, id), + sqlgraph.To(agent.Table, agent.FieldID), + sqlgraph.Edge(sqlgraph.M2O, false, groupmembership.AgentTable, groupmembership.AgentColumn), + ) + fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// Hooks returns the client hooks. +func (c *GroupMembershipClient) Hooks() []Hook { + return c.hooks.GroupMembership +} + +// Interceptors returns the client interceptors. +func (c *GroupMembershipClient) Interceptors() []Interceptor { + return c.inters.GroupMembership +} + +func (c *GroupMembershipClient) mutate(ctx context.Context, m *GroupMembershipMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&GroupMembershipCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&GroupMembershipUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&GroupMembershipUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&GroupMembershipDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("ent: unknown GroupMembership mutation op: %q", m.Op()) + } +} + +// HarnessConfigClient is a client for the HarnessConfig schema. +type HarnessConfigClient struct { + config +} + +// NewHarnessConfigClient returns a client for the HarnessConfig from the given config. +func NewHarnessConfigClient(c config) *HarnessConfigClient { + return &HarnessConfigClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `harnessconfig.Hooks(f(g(h())))`. +func (c *HarnessConfigClient) Use(hooks ...Hook) { + c.hooks.HarnessConfig = append(c.hooks.HarnessConfig, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `harnessconfig.Intercept(f(g(h())))`. +func (c *HarnessConfigClient) Intercept(interceptors ...Interceptor) { + c.inters.HarnessConfig = append(c.inters.HarnessConfig, interceptors...) +} + +// Create returns a builder for creating a HarnessConfig entity. +func (c *HarnessConfigClient) Create() *HarnessConfigCreate { + mutation := newHarnessConfigMutation(c.config, OpCreate) + return &HarnessConfigCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of HarnessConfig entities. +func (c *HarnessConfigClient) CreateBulk(builders ...*HarnessConfigCreate) *HarnessConfigCreateBulk { + return &HarnessConfigCreateBulk{config: c.config, builders: builders} +} + +// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates +// a builder and applies setFunc on it. +func (c *HarnessConfigClient) MapCreateBulk(slice any, setFunc func(*HarnessConfigCreate, int)) *HarnessConfigCreateBulk { + rv := reflect.ValueOf(slice) + if rv.Kind() != reflect.Slice { + return &HarnessConfigCreateBulk{err: fmt.Errorf("calling to HarnessConfigClient.MapCreateBulk with wrong type %T, need slice", slice)} + } + builders := make([]*HarnessConfigCreate, rv.Len()) + for i := 0; i < rv.Len(); i++ { + builders[i] = c.Create() + setFunc(builders[i], i) + } + return &HarnessConfigCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for HarnessConfig. +func (c *HarnessConfigClient) Update() *HarnessConfigUpdate { + mutation := newHarnessConfigMutation(c.config, OpUpdate) + return &HarnessConfigUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *HarnessConfigClient) UpdateOne(_m *HarnessConfig) *HarnessConfigUpdateOne { + mutation := newHarnessConfigMutation(c.config, OpUpdateOne, withHarnessConfig(_m)) + return &HarnessConfigUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOneID returns an update builder for the given id. +func (c *HarnessConfigClient) UpdateOneID(id uuid.UUID) *HarnessConfigUpdateOne { + mutation := newHarnessConfigMutation(c.config, OpUpdateOne, withHarnessConfigID(id)) + return &HarnessConfigUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for HarnessConfig. +func (c *HarnessConfigClient) Delete() *HarnessConfigDelete { + mutation := newHarnessConfigMutation(c.config, OpDelete) + return &HarnessConfigDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// DeleteOne returns a builder for deleting the given entity. +func (c *HarnessConfigClient) DeleteOne(_m *HarnessConfig) *HarnessConfigDeleteOne { + return c.DeleteOneID(_m.ID) +} + +// DeleteOneID returns a builder for deleting the given entity by its id. +func (c *HarnessConfigClient) DeleteOneID(id uuid.UUID) *HarnessConfigDeleteOne { + builder := c.Delete().Where(harnessconfig.ID(id)) + builder.mutation.id = &id + builder.mutation.op = OpDeleteOne + return &HarnessConfigDeleteOne{builder} +} + +// Query returns a query builder for HarnessConfig. +func (c *HarnessConfigClient) Query() *HarnessConfigQuery { + return &HarnessConfigQuery{ + config: c.config, + ctx: &QueryContext{Type: TypeHarnessConfig}, + inters: c.Interceptors(), + } +} + +// Get returns a HarnessConfig entity by its id. +func (c *HarnessConfigClient) Get(ctx context.Context, id uuid.UUID) (*HarnessConfig, error) { + return c.Query().Where(harnessconfig.ID(id)).Only(ctx) +} + +// GetX is like Get, but panics if an error occurs. +func (c *HarnessConfigClient) GetX(ctx context.Context, id uuid.UUID) *HarnessConfig { + obj, err := c.Get(ctx, id) + if err != nil { + panic(err) + } + return obj +} + +// Hooks returns the client hooks. +func (c *HarnessConfigClient) Hooks() []Hook { + return c.hooks.HarnessConfig +} + +// Interceptors returns the client interceptors. +func (c *HarnessConfigClient) Interceptors() []Interceptor { + return c.inters.HarnessConfig +} + +func (c *HarnessConfigClient) mutate(ctx context.Context, m *HarnessConfigMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&HarnessConfigCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&HarnessConfigUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&HarnessConfigUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&HarnessConfigDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("ent: unknown HarnessConfig mutation op: %q", m.Op()) + } +} + +// InviteCodeClient is a client for the InviteCode schema. +type InviteCodeClient struct { + config +} + +// NewInviteCodeClient returns a client for the InviteCode from the given config. +func NewInviteCodeClient(c config) *InviteCodeClient { + return &InviteCodeClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `invitecode.Hooks(f(g(h())))`. +func (c *InviteCodeClient) Use(hooks ...Hook) { + c.hooks.InviteCode = append(c.hooks.InviteCode, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `invitecode.Intercept(f(g(h())))`. +func (c *InviteCodeClient) Intercept(interceptors ...Interceptor) { + c.inters.InviteCode = append(c.inters.InviteCode, interceptors...) +} + +// Create returns a builder for creating a InviteCode entity. +func (c *InviteCodeClient) Create() *InviteCodeCreate { + mutation := newInviteCodeMutation(c.config, OpCreate) + return &InviteCodeCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of InviteCode entities. +func (c *InviteCodeClient) CreateBulk(builders ...*InviteCodeCreate) *InviteCodeCreateBulk { + return &InviteCodeCreateBulk{config: c.config, builders: builders} +} + +// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates +// a builder and applies setFunc on it. +func (c *InviteCodeClient) MapCreateBulk(slice any, setFunc func(*InviteCodeCreate, int)) *InviteCodeCreateBulk { + rv := reflect.ValueOf(slice) + if rv.Kind() != reflect.Slice { + return &InviteCodeCreateBulk{err: fmt.Errorf("calling to InviteCodeClient.MapCreateBulk with wrong type %T, need slice", slice)} + } + builders := make([]*InviteCodeCreate, rv.Len()) + for i := 0; i < rv.Len(); i++ { + builders[i] = c.Create() + setFunc(builders[i], i) + } + return &InviteCodeCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for InviteCode. +func (c *InviteCodeClient) Update() *InviteCodeUpdate { + mutation := newInviteCodeMutation(c.config, OpUpdate) + return &InviteCodeUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *InviteCodeClient) UpdateOne(_m *InviteCode) *InviteCodeUpdateOne { + mutation := newInviteCodeMutation(c.config, OpUpdateOne, withInviteCode(_m)) + return &InviteCodeUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOneID returns an update builder for the given id. +func (c *InviteCodeClient) UpdateOneID(id uuid.UUID) *InviteCodeUpdateOne { + mutation := newInviteCodeMutation(c.config, OpUpdateOne, withInviteCodeID(id)) + return &InviteCodeUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for InviteCode. +func (c *InviteCodeClient) Delete() *InviteCodeDelete { + mutation := newInviteCodeMutation(c.config, OpDelete) + return &InviteCodeDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// DeleteOne returns a builder for deleting the given entity. +func (c *InviteCodeClient) DeleteOne(_m *InviteCode) *InviteCodeDeleteOne { + return c.DeleteOneID(_m.ID) +} + +// DeleteOneID returns a builder for deleting the given entity by its id. +func (c *InviteCodeClient) DeleteOneID(id uuid.UUID) *InviteCodeDeleteOne { + builder := c.Delete().Where(invitecode.ID(id)) + builder.mutation.id = &id + builder.mutation.op = OpDeleteOne + return &InviteCodeDeleteOne{builder} +} + +// Query returns a query builder for InviteCode. +func (c *InviteCodeClient) Query() *InviteCodeQuery { + return &InviteCodeQuery{ + config: c.config, + ctx: &QueryContext{Type: TypeInviteCode}, + inters: c.Interceptors(), + } +} + +// Get returns a InviteCode entity by its id. +func (c *InviteCodeClient) Get(ctx context.Context, id uuid.UUID) (*InviteCode, error) { + return c.Query().Where(invitecode.ID(id)).Only(ctx) +} + +// GetX is like Get, but panics if an error occurs. +func (c *InviteCodeClient) GetX(ctx context.Context, id uuid.UUID) *InviteCode { + obj, err := c.Get(ctx, id) + if err != nil { + panic(err) + } + return obj +} + +// Hooks returns the client hooks. +func (c *InviteCodeClient) Hooks() []Hook { + return c.hooks.InviteCode +} + +// Interceptors returns the client interceptors. +func (c *InviteCodeClient) Interceptors() []Interceptor { + return c.inters.InviteCode +} + +func (c *InviteCodeClient) mutate(ctx context.Context, m *InviteCodeMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&InviteCodeCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&InviteCodeUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&InviteCodeUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&InviteCodeDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("ent: unknown InviteCode mutation op: %q", m.Op()) + } +} + +// MaintenanceOperationClient is a client for the MaintenanceOperation schema. +type MaintenanceOperationClient struct { + config +} + +// NewMaintenanceOperationClient returns a client for the MaintenanceOperation from the given config. +func NewMaintenanceOperationClient(c config) *MaintenanceOperationClient { + return &MaintenanceOperationClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `maintenanceoperation.Hooks(f(g(h())))`. +func (c *MaintenanceOperationClient) Use(hooks ...Hook) { + c.hooks.MaintenanceOperation = append(c.hooks.MaintenanceOperation, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `maintenanceoperation.Intercept(f(g(h())))`. +func (c *MaintenanceOperationClient) Intercept(interceptors ...Interceptor) { + c.inters.MaintenanceOperation = append(c.inters.MaintenanceOperation, interceptors...) +} + +// Create returns a builder for creating a MaintenanceOperation entity. +func (c *MaintenanceOperationClient) Create() *MaintenanceOperationCreate { + mutation := newMaintenanceOperationMutation(c.config, OpCreate) + return &MaintenanceOperationCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of MaintenanceOperation entities. +func (c *MaintenanceOperationClient) CreateBulk(builders ...*MaintenanceOperationCreate) *MaintenanceOperationCreateBulk { + return &MaintenanceOperationCreateBulk{config: c.config, builders: builders} +} + +// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates +// a builder and applies setFunc on it. +func (c *MaintenanceOperationClient) MapCreateBulk(slice any, setFunc func(*MaintenanceOperationCreate, int)) *MaintenanceOperationCreateBulk { + rv := reflect.ValueOf(slice) + if rv.Kind() != reflect.Slice { + return &MaintenanceOperationCreateBulk{err: fmt.Errorf("calling to MaintenanceOperationClient.MapCreateBulk with wrong type %T, need slice", slice)} + } + builders := make([]*MaintenanceOperationCreate, rv.Len()) + for i := 0; i < rv.Len(); i++ { + builders[i] = c.Create() + setFunc(builders[i], i) + } + return &MaintenanceOperationCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for MaintenanceOperation. +func (c *MaintenanceOperationClient) Update() *MaintenanceOperationUpdate { + mutation := newMaintenanceOperationMutation(c.config, OpUpdate) + return &MaintenanceOperationUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *MaintenanceOperationClient) UpdateOne(_m *MaintenanceOperation) *MaintenanceOperationUpdateOne { + mutation := newMaintenanceOperationMutation(c.config, OpUpdateOne, withMaintenanceOperation(_m)) + return &MaintenanceOperationUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOneID returns an update builder for the given id. +func (c *MaintenanceOperationClient) UpdateOneID(id uuid.UUID) *MaintenanceOperationUpdateOne { + mutation := newMaintenanceOperationMutation(c.config, OpUpdateOne, withMaintenanceOperationID(id)) + return &MaintenanceOperationUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for MaintenanceOperation. +func (c *MaintenanceOperationClient) Delete() *MaintenanceOperationDelete { + mutation := newMaintenanceOperationMutation(c.config, OpDelete) + return &MaintenanceOperationDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// DeleteOne returns a builder for deleting the given entity. +func (c *MaintenanceOperationClient) DeleteOne(_m *MaintenanceOperation) *MaintenanceOperationDeleteOne { + return c.DeleteOneID(_m.ID) +} + +// DeleteOneID returns a builder for deleting the given entity by its id. +func (c *MaintenanceOperationClient) DeleteOneID(id uuid.UUID) *MaintenanceOperationDeleteOne { + builder := c.Delete().Where(maintenanceoperation.ID(id)) + builder.mutation.id = &id + builder.mutation.op = OpDeleteOne + return &MaintenanceOperationDeleteOne{builder} +} + +// Query returns a query builder for MaintenanceOperation. +func (c *MaintenanceOperationClient) Query() *MaintenanceOperationQuery { + return &MaintenanceOperationQuery{ + config: c.config, + ctx: &QueryContext{Type: TypeMaintenanceOperation}, + inters: c.Interceptors(), + } +} + +// Get returns a MaintenanceOperation entity by its id. +func (c *MaintenanceOperationClient) Get(ctx context.Context, id uuid.UUID) (*MaintenanceOperation, error) { + return c.Query().Where(maintenanceoperation.ID(id)).Only(ctx) +} + +// GetX is like Get, but panics if an error occurs. +func (c *MaintenanceOperationClient) GetX(ctx context.Context, id uuid.UUID) *MaintenanceOperation { + obj, err := c.Get(ctx, id) + if err != nil { + panic(err) + } + return obj +} + +// Hooks returns the client hooks. +func (c *MaintenanceOperationClient) Hooks() []Hook { + return c.hooks.MaintenanceOperation +} + +// Interceptors returns the client interceptors. +func (c *MaintenanceOperationClient) Interceptors() []Interceptor { + return c.inters.MaintenanceOperation +} + +func (c *MaintenanceOperationClient) mutate(ctx context.Context, m *MaintenanceOperationMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&MaintenanceOperationCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&MaintenanceOperationUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&MaintenanceOperationUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&MaintenanceOperationDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("ent: unknown MaintenanceOperation mutation op: %q", m.Op()) + } +} + +// MaintenanceOperationRunClient is a client for the MaintenanceOperationRun schema. +type MaintenanceOperationRunClient struct { + config +} + +// NewMaintenanceOperationRunClient returns a client for the MaintenanceOperationRun from the given config. +func NewMaintenanceOperationRunClient(c config) *MaintenanceOperationRunClient { + return &MaintenanceOperationRunClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `maintenanceoperationrun.Hooks(f(g(h())))`. +func (c *MaintenanceOperationRunClient) Use(hooks ...Hook) { + c.hooks.MaintenanceOperationRun = append(c.hooks.MaintenanceOperationRun, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `maintenanceoperationrun.Intercept(f(g(h())))`. +func (c *MaintenanceOperationRunClient) Intercept(interceptors ...Interceptor) { + c.inters.MaintenanceOperationRun = append(c.inters.MaintenanceOperationRun, interceptors...) +} + +// Create returns a builder for creating a MaintenanceOperationRun entity. +func (c *MaintenanceOperationRunClient) Create() *MaintenanceOperationRunCreate { + mutation := newMaintenanceOperationRunMutation(c.config, OpCreate) + return &MaintenanceOperationRunCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of MaintenanceOperationRun entities. +func (c *MaintenanceOperationRunClient) CreateBulk(builders ...*MaintenanceOperationRunCreate) *MaintenanceOperationRunCreateBulk { + return &MaintenanceOperationRunCreateBulk{config: c.config, builders: builders} +} + +// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates +// a builder and applies setFunc on it. +func (c *MaintenanceOperationRunClient) MapCreateBulk(slice any, setFunc func(*MaintenanceOperationRunCreate, int)) *MaintenanceOperationRunCreateBulk { + rv := reflect.ValueOf(slice) + if rv.Kind() != reflect.Slice { + return &MaintenanceOperationRunCreateBulk{err: fmt.Errorf("calling to MaintenanceOperationRunClient.MapCreateBulk with wrong type %T, need slice", slice)} + } + builders := make([]*MaintenanceOperationRunCreate, rv.Len()) + for i := 0; i < rv.Len(); i++ { + builders[i] = c.Create() + setFunc(builders[i], i) + } + return &MaintenanceOperationRunCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for MaintenanceOperationRun. +func (c *MaintenanceOperationRunClient) Update() *MaintenanceOperationRunUpdate { + mutation := newMaintenanceOperationRunMutation(c.config, OpUpdate) + return &MaintenanceOperationRunUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *MaintenanceOperationRunClient) UpdateOne(_m *MaintenanceOperationRun) *MaintenanceOperationRunUpdateOne { + mutation := newMaintenanceOperationRunMutation(c.config, OpUpdateOne, withMaintenanceOperationRun(_m)) + return &MaintenanceOperationRunUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOneID returns an update builder for the given id. +func (c *MaintenanceOperationRunClient) UpdateOneID(id uuid.UUID) *MaintenanceOperationRunUpdateOne { + mutation := newMaintenanceOperationRunMutation(c.config, OpUpdateOne, withMaintenanceOperationRunID(id)) + return &MaintenanceOperationRunUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for MaintenanceOperationRun. +func (c *MaintenanceOperationRunClient) Delete() *MaintenanceOperationRunDelete { + mutation := newMaintenanceOperationRunMutation(c.config, OpDelete) + return &MaintenanceOperationRunDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// DeleteOne returns a builder for deleting the given entity. +func (c *MaintenanceOperationRunClient) DeleteOne(_m *MaintenanceOperationRun) *MaintenanceOperationRunDeleteOne { + return c.DeleteOneID(_m.ID) +} + +// DeleteOneID returns a builder for deleting the given entity by its id. +func (c *MaintenanceOperationRunClient) DeleteOneID(id uuid.UUID) *MaintenanceOperationRunDeleteOne { + builder := c.Delete().Where(maintenanceoperationrun.ID(id)) + builder.mutation.id = &id + builder.mutation.op = OpDeleteOne + return &MaintenanceOperationRunDeleteOne{builder} +} + +// Query returns a query builder for MaintenanceOperationRun. +func (c *MaintenanceOperationRunClient) Query() *MaintenanceOperationRunQuery { + return &MaintenanceOperationRunQuery{ + config: c.config, + ctx: &QueryContext{Type: TypeMaintenanceOperationRun}, + inters: c.Interceptors(), + } +} + +// Get returns a MaintenanceOperationRun entity by its id. +func (c *MaintenanceOperationRunClient) Get(ctx context.Context, id uuid.UUID) (*MaintenanceOperationRun, error) { + return c.Query().Where(maintenanceoperationrun.ID(id)).Only(ctx) +} + +// GetX is like Get, but panics if an error occurs. +func (c *MaintenanceOperationRunClient) GetX(ctx context.Context, id uuid.UUID) *MaintenanceOperationRun { + obj, err := c.Get(ctx, id) + if err != nil { + panic(err) + } + return obj +} + +// Hooks returns the client hooks. +func (c *MaintenanceOperationRunClient) Hooks() []Hook { + return c.hooks.MaintenanceOperationRun +} + +// Interceptors returns the client interceptors. +func (c *MaintenanceOperationRunClient) Interceptors() []Interceptor { + return c.inters.MaintenanceOperationRun +} + +func (c *MaintenanceOperationRunClient) mutate(ctx context.Context, m *MaintenanceOperationRunMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&MaintenanceOperationRunCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&MaintenanceOperationRunUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&MaintenanceOperationRunUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&MaintenanceOperationRunDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("ent: unknown MaintenanceOperationRun mutation op: %q", m.Op()) + } +} + +// MessageClient is a client for the Message schema. +type MessageClient struct { + config +} + +// NewMessageClient returns a client for the Message from the given config. +func NewMessageClient(c config) *MessageClient { + return &MessageClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `message.Hooks(f(g(h())))`. +func (c *MessageClient) Use(hooks ...Hook) { + c.hooks.Message = append(c.hooks.Message, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `message.Intercept(f(g(h())))`. +func (c *MessageClient) Intercept(interceptors ...Interceptor) { + c.inters.Message = append(c.inters.Message, interceptors...) +} + +// Create returns a builder for creating a Message entity. +func (c *MessageClient) Create() *MessageCreate { + mutation := newMessageMutation(c.config, OpCreate) + return &MessageCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of Message entities. +func (c *MessageClient) CreateBulk(builders ...*MessageCreate) *MessageCreateBulk { + return &MessageCreateBulk{config: c.config, builders: builders} +} + +// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates +// a builder and applies setFunc on it. +func (c *MessageClient) MapCreateBulk(slice any, setFunc func(*MessageCreate, int)) *MessageCreateBulk { + rv := reflect.ValueOf(slice) + if rv.Kind() != reflect.Slice { + return &MessageCreateBulk{err: fmt.Errorf("calling to MessageClient.MapCreateBulk with wrong type %T, need slice", slice)} + } + builders := make([]*MessageCreate, rv.Len()) + for i := 0; i < rv.Len(); i++ { + builders[i] = c.Create() + setFunc(builders[i], i) + } + return &MessageCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for Message. +func (c *MessageClient) Update() *MessageUpdate { + mutation := newMessageMutation(c.config, OpUpdate) + return &MessageUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *MessageClient) UpdateOne(_m *Message) *MessageUpdateOne { + mutation := newMessageMutation(c.config, OpUpdateOne, withMessage(_m)) + return &MessageUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOneID returns an update builder for the given id. +func (c *MessageClient) UpdateOneID(id uuid.UUID) *MessageUpdateOne { + mutation := newMessageMutation(c.config, OpUpdateOne, withMessageID(id)) + return &MessageUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for Message. +func (c *MessageClient) Delete() *MessageDelete { + mutation := newMessageMutation(c.config, OpDelete) + return &MessageDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// DeleteOne returns a builder for deleting the given entity. +func (c *MessageClient) DeleteOne(_m *Message) *MessageDeleteOne { + return c.DeleteOneID(_m.ID) +} + +// DeleteOneID returns a builder for deleting the given entity by its id. +func (c *MessageClient) DeleteOneID(id uuid.UUID) *MessageDeleteOne { + builder := c.Delete().Where(message.ID(id)) + builder.mutation.id = &id + builder.mutation.op = OpDeleteOne + return &MessageDeleteOne{builder} +} + +// Query returns a query builder for Message. +func (c *MessageClient) Query() *MessageQuery { + return &MessageQuery{ + config: c.config, + ctx: &QueryContext{Type: TypeMessage}, + inters: c.Interceptors(), + } +} + +// Get returns a Message entity by its id. +func (c *MessageClient) Get(ctx context.Context, id uuid.UUID) (*Message, error) { + return c.Query().Where(message.ID(id)).Only(ctx) +} + +// GetX is like Get, but panics if an error occurs. +func (c *MessageClient) GetX(ctx context.Context, id uuid.UUID) *Message { + obj, err := c.Get(ctx, id) + if err != nil { + panic(err) + } + return obj +} + +// Hooks returns the client hooks. +func (c *MessageClient) Hooks() []Hook { + return c.hooks.Message +} + +// Interceptors returns the client interceptors. +func (c *MessageClient) Interceptors() []Interceptor { + return c.inters.Message +} + +func (c *MessageClient) mutate(ctx context.Context, m *MessageMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&MessageCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&MessageUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&MessageUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&MessageDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("ent: unknown Message mutation op: %q", m.Op()) + } +} + +// NotificationClient is a client for the Notification schema. +type NotificationClient struct { + config +} + +// NewNotificationClient returns a client for the Notification from the given config. +func NewNotificationClient(c config) *NotificationClient { + return &NotificationClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `notification.Hooks(f(g(h())))`. +func (c *NotificationClient) Use(hooks ...Hook) { + c.hooks.Notification = append(c.hooks.Notification, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `notification.Intercept(f(g(h())))`. +func (c *NotificationClient) Intercept(interceptors ...Interceptor) { + c.inters.Notification = append(c.inters.Notification, interceptors...) +} + +// Create returns a builder for creating a Notification entity. +func (c *NotificationClient) Create() *NotificationCreate { + mutation := newNotificationMutation(c.config, OpCreate) + return &NotificationCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of Notification entities. +func (c *NotificationClient) CreateBulk(builders ...*NotificationCreate) *NotificationCreateBulk { + return &NotificationCreateBulk{config: c.config, builders: builders} +} + +// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates +// a builder and applies setFunc on it. +func (c *NotificationClient) MapCreateBulk(slice any, setFunc func(*NotificationCreate, int)) *NotificationCreateBulk { + rv := reflect.ValueOf(slice) + if rv.Kind() != reflect.Slice { + return &NotificationCreateBulk{err: fmt.Errorf("calling to NotificationClient.MapCreateBulk with wrong type %T, need slice", slice)} + } + builders := make([]*NotificationCreate, rv.Len()) + for i := 0; i < rv.Len(); i++ { + builders[i] = c.Create() + setFunc(builders[i], i) + } + return &NotificationCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for Notification. +func (c *NotificationClient) Update() *NotificationUpdate { + mutation := newNotificationMutation(c.config, OpUpdate) + return &NotificationUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *NotificationClient) UpdateOne(_m *Notification) *NotificationUpdateOne { + mutation := newNotificationMutation(c.config, OpUpdateOne, withNotification(_m)) + return &NotificationUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOneID returns an update builder for the given id. +func (c *NotificationClient) UpdateOneID(id uuid.UUID) *NotificationUpdateOne { + mutation := newNotificationMutation(c.config, OpUpdateOne, withNotificationID(id)) + return &NotificationUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for Notification. +func (c *NotificationClient) Delete() *NotificationDelete { + mutation := newNotificationMutation(c.config, OpDelete) + return &NotificationDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// DeleteOne returns a builder for deleting the given entity. +func (c *NotificationClient) DeleteOne(_m *Notification) *NotificationDeleteOne { + return c.DeleteOneID(_m.ID) +} + +// DeleteOneID returns a builder for deleting the given entity by its id. +func (c *NotificationClient) DeleteOneID(id uuid.UUID) *NotificationDeleteOne { + builder := c.Delete().Where(notification.ID(id)) + builder.mutation.id = &id + builder.mutation.op = OpDeleteOne + return &NotificationDeleteOne{builder} +} + +// Query returns a query builder for Notification. +func (c *NotificationClient) Query() *NotificationQuery { + return &NotificationQuery{ + config: c.config, + ctx: &QueryContext{Type: TypeNotification}, + inters: c.Interceptors(), + } +} + +// Get returns a Notification entity by its id. +func (c *NotificationClient) Get(ctx context.Context, id uuid.UUID) (*Notification, error) { + return c.Query().Where(notification.ID(id)).Only(ctx) +} + +// GetX is like Get, but panics if an error occurs. +func (c *NotificationClient) GetX(ctx context.Context, id uuid.UUID) *Notification { + obj, err := c.Get(ctx, id) + if err != nil { + panic(err) + } + return obj +} + +// Hooks returns the client hooks. +func (c *NotificationClient) Hooks() []Hook { + return c.hooks.Notification +} + +// Interceptors returns the client interceptors. +func (c *NotificationClient) Interceptors() []Interceptor { + return c.inters.Notification +} + +func (c *NotificationClient) mutate(ctx context.Context, m *NotificationMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&NotificationCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&NotificationUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&NotificationUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&NotificationDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("ent: unknown Notification mutation op: %q", m.Op()) + } +} + +// NotificationSubscriptionClient is a client for the NotificationSubscription schema. +type NotificationSubscriptionClient struct { + config +} + +// NewNotificationSubscriptionClient returns a client for the NotificationSubscription from the given config. +func NewNotificationSubscriptionClient(c config) *NotificationSubscriptionClient { + return &NotificationSubscriptionClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `notificationsubscription.Hooks(f(g(h())))`. +func (c *NotificationSubscriptionClient) Use(hooks ...Hook) { + c.hooks.NotificationSubscription = append(c.hooks.NotificationSubscription, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `notificationsubscription.Intercept(f(g(h())))`. +func (c *NotificationSubscriptionClient) Intercept(interceptors ...Interceptor) { + c.inters.NotificationSubscription = append(c.inters.NotificationSubscription, interceptors...) +} + +// Create returns a builder for creating a NotificationSubscription entity. +func (c *NotificationSubscriptionClient) Create() *NotificationSubscriptionCreate { + mutation := newNotificationSubscriptionMutation(c.config, OpCreate) + return &NotificationSubscriptionCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of NotificationSubscription entities. +func (c *NotificationSubscriptionClient) CreateBulk(builders ...*NotificationSubscriptionCreate) *NotificationSubscriptionCreateBulk { + return &NotificationSubscriptionCreateBulk{config: c.config, builders: builders} +} + +// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates +// a builder and applies setFunc on it. +func (c *NotificationSubscriptionClient) MapCreateBulk(slice any, setFunc func(*NotificationSubscriptionCreate, int)) *NotificationSubscriptionCreateBulk { + rv := reflect.ValueOf(slice) + if rv.Kind() != reflect.Slice { + return &NotificationSubscriptionCreateBulk{err: fmt.Errorf("calling to NotificationSubscriptionClient.MapCreateBulk with wrong type %T, need slice", slice)} + } + builders := make([]*NotificationSubscriptionCreate, rv.Len()) + for i := 0; i < rv.Len(); i++ { + builders[i] = c.Create() + setFunc(builders[i], i) + } + return &NotificationSubscriptionCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for NotificationSubscription. +func (c *NotificationSubscriptionClient) Update() *NotificationSubscriptionUpdate { + mutation := newNotificationSubscriptionMutation(c.config, OpUpdate) + return &NotificationSubscriptionUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *NotificationSubscriptionClient) UpdateOne(_m *NotificationSubscription) *NotificationSubscriptionUpdateOne { + mutation := newNotificationSubscriptionMutation(c.config, OpUpdateOne, withNotificationSubscription(_m)) + return &NotificationSubscriptionUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOneID returns an update builder for the given id. +func (c *NotificationSubscriptionClient) UpdateOneID(id uuid.UUID) *NotificationSubscriptionUpdateOne { + mutation := newNotificationSubscriptionMutation(c.config, OpUpdateOne, withNotificationSubscriptionID(id)) + return &NotificationSubscriptionUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for NotificationSubscription. +func (c *NotificationSubscriptionClient) Delete() *NotificationSubscriptionDelete { + mutation := newNotificationSubscriptionMutation(c.config, OpDelete) + return &NotificationSubscriptionDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// DeleteOne returns a builder for deleting the given entity. +func (c *NotificationSubscriptionClient) DeleteOne(_m *NotificationSubscription) *NotificationSubscriptionDeleteOne { + return c.DeleteOneID(_m.ID) +} + +// DeleteOneID returns a builder for deleting the given entity by its id. +func (c *NotificationSubscriptionClient) DeleteOneID(id uuid.UUID) *NotificationSubscriptionDeleteOne { + builder := c.Delete().Where(notificationsubscription.ID(id)) + builder.mutation.id = &id + builder.mutation.op = OpDeleteOne + return &NotificationSubscriptionDeleteOne{builder} +} + +// Query returns a query builder for NotificationSubscription. +func (c *NotificationSubscriptionClient) Query() *NotificationSubscriptionQuery { + return &NotificationSubscriptionQuery{ + config: c.config, + ctx: &QueryContext{Type: TypeNotificationSubscription}, + inters: c.Interceptors(), + } +} + +// Get returns a NotificationSubscription entity by its id. +func (c *NotificationSubscriptionClient) Get(ctx context.Context, id uuid.UUID) (*NotificationSubscription, error) { + return c.Query().Where(notificationsubscription.ID(id)).Only(ctx) +} + +// GetX is like Get, but panics if an error occurs. +func (c *NotificationSubscriptionClient) GetX(ctx context.Context, id uuid.UUID) *NotificationSubscription { + obj, err := c.Get(ctx, id) + if err != nil { + panic(err) + } + return obj +} + +// Hooks returns the client hooks. +func (c *NotificationSubscriptionClient) Hooks() []Hook { + return c.hooks.NotificationSubscription +} + +// Interceptors returns the client interceptors. +func (c *NotificationSubscriptionClient) Interceptors() []Interceptor { + return c.inters.NotificationSubscription +} + +func (c *NotificationSubscriptionClient) mutate(ctx context.Context, m *NotificationSubscriptionMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&NotificationSubscriptionCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&NotificationSubscriptionUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&NotificationSubscriptionUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&NotificationSubscriptionDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("ent: unknown NotificationSubscription mutation op: %q", m.Op()) + } +} + +// PolicyBindingClient is a client for the PolicyBinding schema. +type PolicyBindingClient struct { + config +} + +// NewPolicyBindingClient returns a client for the PolicyBinding from the given config. +func NewPolicyBindingClient(c config) *PolicyBindingClient { + return &PolicyBindingClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `policybinding.Hooks(f(g(h())))`. +func (c *PolicyBindingClient) Use(hooks ...Hook) { + c.hooks.PolicyBinding = append(c.hooks.PolicyBinding, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `policybinding.Intercept(f(g(h())))`. +func (c *PolicyBindingClient) Intercept(interceptors ...Interceptor) { + c.inters.PolicyBinding = append(c.inters.PolicyBinding, interceptors...) +} + +// Create returns a builder for creating a PolicyBinding entity. func (c *PolicyBindingClient) Create() *PolicyBindingCreate { mutation := newPolicyBindingMutation(c.config, OpCreate) return &PolicyBindingCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} } -// CreateBulk returns a builder for creating a bulk of PolicyBinding entities. -func (c *PolicyBindingClient) CreateBulk(builders ...*PolicyBindingCreate) *PolicyBindingCreateBulk { - return &PolicyBindingCreateBulk{config: c.config, builders: builders} +// CreateBulk returns a builder for creating a bulk of PolicyBinding entities. +func (c *PolicyBindingClient) CreateBulk(builders ...*PolicyBindingCreate) *PolicyBindingCreateBulk { + return &PolicyBindingCreateBulk{config: c.config, builders: builders} +} + +// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates +// a builder and applies setFunc on it. +func (c *PolicyBindingClient) MapCreateBulk(slice any, setFunc func(*PolicyBindingCreate, int)) *PolicyBindingCreateBulk { + rv := reflect.ValueOf(slice) + if rv.Kind() != reflect.Slice { + return &PolicyBindingCreateBulk{err: fmt.Errorf("calling to PolicyBindingClient.MapCreateBulk with wrong type %T, need slice", slice)} + } + builders := make([]*PolicyBindingCreate, rv.Len()) + for i := 0; i < rv.Len(); i++ { + builders[i] = c.Create() + setFunc(builders[i], i) + } + return &PolicyBindingCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for PolicyBinding. +func (c *PolicyBindingClient) Update() *PolicyBindingUpdate { + mutation := newPolicyBindingMutation(c.config, OpUpdate) + return &PolicyBindingUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *PolicyBindingClient) UpdateOne(_m *PolicyBinding) *PolicyBindingUpdateOne { + mutation := newPolicyBindingMutation(c.config, OpUpdateOne, withPolicyBinding(_m)) + return &PolicyBindingUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOneID returns an update builder for the given id. +func (c *PolicyBindingClient) UpdateOneID(id uuid.UUID) *PolicyBindingUpdateOne { + mutation := newPolicyBindingMutation(c.config, OpUpdateOne, withPolicyBindingID(id)) + return &PolicyBindingUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for PolicyBinding. +func (c *PolicyBindingClient) Delete() *PolicyBindingDelete { + mutation := newPolicyBindingMutation(c.config, OpDelete) + return &PolicyBindingDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// DeleteOne returns a builder for deleting the given entity. +func (c *PolicyBindingClient) DeleteOne(_m *PolicyBinding) *PolicyBindingDeleteOne { + return c.DeleteOneID(_m.ID) +} + +// DeleteOneID returns a builder for deleting the given entity by its id. +func (c *PolicyBindingClient) DeleteOneID(id uuid.UUID) *PolicyBindingDeleteOne { + builder := c.Delete().Where(policybinding.ID(id)) + builder.mutation.id = &id + builder.mutation.op = OpDeleteOne + return &PolicyBindingDeleteOne{builder} +} + +// Query returns a query builder for PolicyBinding. +func (c *PolicyBindingClient) Query() *PolicyBindingQuery { + return &PolicyBindingQuery{ + config: c.config, + ctx: &QueryContext{Type: TypePolicyBinding}, + inters: c.Interceptors(), + } +} + +// Get returns a PolicyBinding entity by its id. +func (c *PolicyBindingClient) Get(ctx context.Context, id uuid.UUID) (*PolicyBinding, error) { + return c.Query().Where(policybinding.ID(id)).Only(ctx) +} + +// GetX is like Get, but panics if an error occurs. +func (c *PolicyBindingClient) GetX(ctx context.Context, id uuid.UUID) *PolicyBinding { + obj, err := c.Get(ctx, id) + if err != nil { + panic(err) + } + return obj +} + +// QueryPolicy queries the policy edge of a PolicyBinding. +func (c *PolicyBindingClient) QueryPolicy(_m *PolicyBinding) *AccessPolicyQuery { + query := (&AccessPolicyClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := _m.ID + step := sqlgraph.NewStep( + sqlgraph.From(policybinding.Table, policybinding.FieldID, id), + sqlgraph.To(accesspolicy.Table, accesspolicy.FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, policybinding.PolicyTable, policybinding.PolicyColumn), + ) + fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// QueryUser queries the user edge of a PolicyBinding. +func (c *PolicyBindingClient) QueryUser(_m *PolicyBinding) *UserQuery { + query := (&UserClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := _m.ID + step := sqlgraph.NewStep( + sqlgraph.From(policybinding.Table, policybinding.FieldID, id), + sqlgraph.To(user.Table, user.FieldID), + sqlgraph.Edge(sqlgraph.M2O, false, policybinding.UserTable, policybinding.UserColumn), + ) + fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// QueryGroup queries the group edge of a PolicyBinding. +func (c *PolicyBindingClient) QueryGroup(_m *PolicyBinding) *GroupQuery { + query := (&GroupClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := _m.ID + step := sqlgraph.NewStep( + sqlgraph.From(policybinding.Table, policybinding.FieldID, id), + sqlgraph.To(group.Table, group.FieldID), + sqlgraph.Edge(sqlgraph.M2O, false, policybinding.GroupTable, policybinding.GroupColumn), + ) + fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// QueryAgent queries the agent edge of a PolicyBinding. +func (c *PolicyBindingClient) QueryAgent(_m *PolicyBinding) *AgentQuery { + query := (&AgentClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := _m.ID + step := sqlgraph.NewStep( + sqlgraph.From(policybinding.Table, policybinding.FieldID, id), + sqlgraph.To(agent.Table, agent.FieldID), + sqlgraph.Edge(sqlgraph.M2O, false, policybinding.AgentTable, policybinding.AgentColumn), + ) + fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// Hooks returns the client hooks. +func (c *PolicyBindingClient) Hooks() []Hook { + return c.hooks.PolicyBinding +} + +// Interceptors returns the client interceptors. +func (c *PolicyBindingClient) Interceptors() []Interceptor { + return c.inters.PolicyBinding +} + +func (c *PolicyBindingClient) mutate(ctx context.Context, m *PolicyBindingMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&PolicyBindingCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&PolicyBindingUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&PolicyBindingUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&PolicyBindingDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("ent: unknown PolicyBinding mutation op: %q", m.Op()) + } +} + +// ProjectClient is a client for the Project schema. +type ProjectClient struct { + config +} + +// NewProjectClient returns a client for the Project from the given config. +func NewProjectClient(c config) *ProjectClient { + return &ProjectClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `project.Hooks(f(g(h())))`. +func (c *ProjectClient) Use(hooks ...Hook) { + c.hooks.Project = append(c.hooks.Project, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `project.Intercept(f(g(h())))`. +func (c *ProjectClient) Intercept(interceptors ...Interceptor) { + c.inters.Project = append(c.inters.Project, interceptors...) +} + +// Create returns a builder for creating a Project entity. +func (c *ProjectClient) Create() *ProjectCreate { + mutation := newProjectMutation(c.config, OpCreate) + return &ProjectCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of Project entities. +func (c *ProjectClient) CreateBulk(builders ...*ProjectCreate) *ProjectCreateBulk { + return &ProjectCreateBulk{config: c.config, builders: builders} +} + +// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates +// a builder and applies setFunc on it. +func (c *ProjectClient) MapCreateBulk(slice any, setFunc func(*ProjectCreate, int)) *ProjectCreateBulk { + rv := reflect.ValueOf(slice) + if rv.Kind() != reflect.Slice { + return &ProjectCreateBulk{err: fmt.Errorf("calling to ProjectClient.MapCreateBulk with wrong type %T, need slice", slice)} + } + builders := make([]*ProjectCreate, rv.Len()) + for i := 0; i < rv.Len(); i++ { + builders[i] = c.Create() + setFunc(builders[i], i) + } + return &ProjectCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for Project. +func (c *ProjectClient) Update() *ProjectUpdate { + mutation := newProjectMutation(c.config, OpUpdate) + return &ProjectUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *ProjectClient) UpdateOne(_m *Project) *ProjectUpdateOne { + mutation := newProjectMutation(c.config, OpUpdateOne, withProject(_m)) + return &ProjectUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOneID returns an update builder for the given id. +func (c *ProjectClient) UpdateOneID(id uuid.UUID) *ProjectUpdateOne { + mutation := newProjectMutation(c.config, OpUpdateOne, withProjectID(id)) + return &ProjectUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for Project. +func (c *ProjectClient) Delete() *ProjectDelete { + mutation := newProjectMutation(c.config, OpDelete) + return &ProjectDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// DeleteOne returns a builder for deleting the given entity. +func (c *ProjectClient) DeleteOne(_m *Project) *ProjectDeleteOne { + return c.DeleteOneID(_m.ID) +} + +// DeleteOneID returns a builder for deleting the given entity by its id. +func (c *ProjectClient) DeleteOneID(id uuid.UUID) *ProjectDeleteOne { + builder := c.Delete().Where(project.ID(id)) + builder.mutation.id = &id + builder.mutation.op = OpDeleteOne + return &ProjectDeleteOne{builder} +} + +// Query returns a query builder for Project. +func (c *ProjectClient) Query() *ProjectQuery { + return &ProjectQuery{ + config: c.config, + ctx: &QueryContext{Type: TypeProject}, + inters: c.Interceptors(), + } +} + +// Get returns a Project entity by its id. +func (c *ProjectClient) Get(ctx context.Context, id uuid.UUID) (*Project, error) { + return c.Query().Where(project.ID(id)).Only(ctx) +} + +// GetX is like Get, but panics if an error occurs. +func (c *ProjectClient) GetX(ctx context.Context, id uuid.UUID) *Project { + obj, err := c.Get(ctx, id) + if err != nil { + panic(err) + } + return obj +} + +// QueryAgents queries the agents edge of a Project. +func (c *ProjectClient) QueryAgents(_m *Project) *AgentQuery { + query := (&AgentClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := _m.ID + step := sqlgraph.NewStep( + sqlgraph.From(project.Table, project.FieldID, id), + sqlgraph.To(agent.Table, agent.FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, project.AgentsTable, project.AgentsColumn), + ) + fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// Hooks returns the client hooks. +func (c *ProjectClient) Hooks() []Hook { + return c.hooks.Project +} + +// Interceptors returns the client interceptors. +func (c *ProjectClient) Interceptors() []Interceptor { + return c.inters.Project +} + +func (c *ProjectClient) mutate(ctx context.Context, m *ProjectMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&ProjectCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&ProjectUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&ProjectUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&ProjectDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("ent: unknown Project mutation op: %q", m.Op()) + } +} + +// ProjectContributorClient is a client for the ProjectContributor schema. +type ProjectContributorClient struct { + config +} + +// NewProjectContributorClient returns a client for the ProjectContributor from the given config. +func NewProjectContributorClient(c config) *ProjectContributorClient { + return &ProjectContributorClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `projectcontributor.Hooks(f(g(h())))`. +func (c *ProjectContributorClient) Use(hooks ...Hook) { + c.hooks.ProjectContributor = append(c.hooks.ProjectContributor, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `projectcontributor.Intercept(f(g(h())))`. +func (c *ProjectContributorClient) Intercept(interceptors ...Interceptor) { + c.inters.ProjectContributor = append(c.inters.ProjectContributor, interceptors...) +} + +// Create returns a builder for creating a ProjectContributor entity. +func (c *ProjectContributorClient) Create() *ProjectContributorCreate { + mutation := newProjectContributorMutation(c.config, OpCreate) + return &ProjectContributorCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of ProjectContributor entities. +func (c *ProjectContributorClient) CreateBulk(builders ...*ProjectContributorCreate) *ProjectContributorCreateBulk { + return &ProjectContributorCreateBulk{config: c.config, builders: builders} +} + +// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates +// a builder and applies setFunc on it. +func (c *ProjectContributorClient) MapCreateBulk(slice any, setFunc func(*ProjectContributorCreate, int)) *ProjectContributorCreateBulk { + rv := reflect.ValueOf(slice) + if rv.Kind() != reflect.Slice { + return &ProjectContributorCreateBulk{err: fmt.Errorf("calling to ProjectContributorClient.MapCreateBulk with wrong type %T, need slice", slice)} + } + builders := make([]*ProjectContributorCreate, rv.Len()) + for i := 0; i < rv.Len(); i++ { + builders[i] = c.Create() + setFunc(builders[i], i) + } + return &ProjectContributorCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for ProjectContributor. +func (c *ProjectContributorClient) Update() *ProjectContributorUpdate { + mutation := newProjectContributorMutation(c.config, OpUpdate) + return &ProjectContributorUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *ProjectContributorClient) UpdateOne(_m *ProjectContributor) *ProjectContributorUpdateOne { + mutation := newProjectContributorMutation(c.config, OpUpdateOne, withProjectContributor(_m)) + return &ProjectContributorUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOneID returns an update builder for the given id. +func (c *ProjectContributorClient) UpdateOneID(id uuid.UUID) *ProjectContributorUpdateOne { + mutation := newProjectContributorMutation(c.config, OpUpdateOne, withProjectContributorID(id)) + return &ProjectContributorUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for ProjectContributor. +func (c *ProjectContributorClient) Delete() *ProjectContributorDelete { + mutation := newProjectContributorMutation(c.config, OpDelete) + return &ProjectContributorDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// DeleteOne returns a builder for deleting the given entity. +func (c *ProjectContributorClient) DeleteOne(_m *ProjectContributor) *ProjectContributorDeleteOne { + return c.DeleteOneID(_m.ID) +} + +// DeleteOneID returns a builder for deleting the given entity by its id. +func (c *ProjectContributorClient) DeleteOneID(id uuid.UUID) *ProjectContributorDeleteOne { + builder := c.Delete().Where(projectcontributor.ID(id)) + builder.mutation.id = &id + builder.mutation.op = OpDeleteOne + return &ProjectContributorDeleteOne{builder} +} + +// Query returns a query builder for ProjectContributor. +func (c *ProjectContributorClient) Query() *ProjectContributorQuery { + return &ProjectContributorQuery{ + config: c.config, + ctx: &QueryContext{Type: TypeProjectContributor}, + inters: c.Interceptors(), + } +} + +// Get returns a ProjectContributor entity by its id. +func (c *ProjectContributorClient) Get(ctx context.Context, id uuid.UUID) (*ProjectContributor, error) { + return c.Query().Where(projectcontributor.ID(id)).Only(ctx) +} + +// GetX is like Get, but panics if an error occurs. +func (c *ProjectContributorClient) GetX(ctx context.Context, id uuid.UUID) *ProjectContributor { + obj, err := c.Get(ctx, id) + if err != nil { + panic(err) + } + return obj +} + +// Hooks returns the client hooks. +func (c *ProjectContributorClient) Hooks() []Hook { + return c.hooks.ProjectContributor +} + +// Interceptors returns the client interceptors. +func (c *ProjectContributorClient) Interceptors() []Interceptor { + return c.inters.ProjectContributor +} + +func (c *ProjectContributorClient) mutate(ctx context.Context, m *ProjectContributorMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&ProjectContributorCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&ProjectContributorUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&ProjectContributorUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&ProjectContributorDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("ent: unknown ProjectContributor mutation op: %q", m.Op()) + } +} + +// ProjectSyncStateClient is a client for the ProjectSyncState schema. +type ProjectSyncStateClient struct { + config +} + +// NewProjectSyncStateClient returns a client for the ProjectSyncState from the given config. +func NewProjectSyncStateClient(c config) *ProjectSyncStateClient { + return &ProjectSyncStateClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `projectsyncstate.Hooks(f(g(h())))`. +func (c *ProjectSyncStateClient) Use(hooks ...Hook) { + c.hooks.ProjectSyncState = append(c.hooks.ProjectSyncState, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `projectsyncstate.Intercept(f(g(h())))`. +func (c *ProjectSyncStateClient) Intercept(interceptors ...Interceptor) { + c.inters.ProjectSyncState = append(c.inters.ProjectSyncState, interceptors...) +} + +// Create returns a builder for creating a ProjectSyncState entity. +func (c *ProjectSyncStateClient) Create() *ProjectSyncStateCreate { + mutation := newProjectSyncStateMutation(c.config, OpCreate) + return &ProjectSyncStateCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of ProjectSyncState entities. +func (c *ProjectSyncStateClient) CreateBulk(builders ...*ProjectSyncStateCreate) *ProjectSyncStateCreateBulk { + return &ProjectSyncStateCreateBulk{config: c.config, builders: builders} +} + +// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates +// a builder and applies setFunc on it. +func (c *ProjectSyncStateClient) MapCreateBulk(slice any, setFunc func(*ProjectSyncStateCreate, int)) *ProjectSyncStateCreateBulk { + rv := reflect.ValueOf(slice) + if rv.Kind() != reflect.Slice { + return &ProjectSyncStateCreateBulk{err: fmt.Errorf("calling to ProjectSyncStateClient.MapCreateBulk with wrong type %T, need slice", slice)} + } + builders := make([]*ProjectSyncStateCreate, rv.Len()) + for i := 0; i < rv.Len(); i++ { + builders[i] = c.Create() + setFunc(builders[i], i) + } + return &ProjectSyncStateCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for ProjectSyncState. +func (c *ProjectSyncStateClient) Update() *ProjectSyncStateUpdate { + mutation := newProjectSyncStateMutation(c.config, OpUpdate) + return &ProjectSyncStateUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *ProjectSyncStateClient) UpdateOne(_m *ProjectSyncState) *ProjectSyncStateUpdateOne { + mutation := newProjectSyncStateMutation(c.config, OpUpdateOne, withProjectSyncState(_m)) + return &ProjectSyncStateUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOneID returns an update builder for the given id. +func (c *ProjectSyncStateClient) UpdateOneID(id uuid.UUID) *ProjectSyncStateUpdateOne { + mutation := newProjectSyncStateMutation(c.config, OpUpdateOne, withProjectSyncStateID(id)) + return &ProjectSyncStateUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for ProjectSyncState. +func (c *ProjectSyncStateClient) Delete() *ProjectSyncStateDelete { + mutation := newProjectSyncStateMutation(c.config, OpDelete) + return &ProjectSyncStateDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// DeleteOne returns a builder for deleting the given entity. +func (c *ProjectSyncStateClient) DeleteOne(_m *ProjectSyncState) *ProjectSyncStateDeleteOne { + return c.DeleteOneID(_m.ID) +} + +// DeleteOneID returns a builder for deleting the given entity by its id. +func (c *ProjectSyncStateClient) DeleteOneID(id uuid.UUID) *ProjectSyncStateDeleteOne { + builder := c.Delete().Where(projectsyncstate.ID(id)) + builder.mutation.id = &id + builder.mutation.op = OpDeleteOne + return &ProjectSyncStateDeleteOne{builder} +} + +// Query returns a query builder for ProjectSyncState. +func (c *ProjectSyncStateClient) Query() *ProjectSyncStateQuery { + return &ProjectSyncStateQuery{ + config: c.config, + ctx: &QueryContext{Type: TypeProjectSyncState}, + inters: c.Interceptors(), + } +} + +// Get returns a ProjectSyncState entity by its id. +func (c *ProjectSyncStateClient) Get(ctx context.Context, id uuid.UUID) (*ProjectSyncState, error) { + return c.Query().Where(projectsyncstate.ID(id)).Only(ctx) +} + +// GetX is like Get, but panics if an error occurs. +func (c *ProjectSyncStateClient) GetX(ctx context.Context, id uuid.UUID) *ProjectSyncState { + obj, err := c.Get(ctx, id) + if err != nil { + panic(err) + } + return obj +} + +// Hooks returns the client hooks. +func (c *ProjectSyncStateClient) Hooks() []Hook { + return c.hooks.ProjectSyncState +} + +// Interceptors returns the client interceptors. +func (c *ProjectSyncStateClient) Interceptors() []Interceptor { + return c.inters.ProjectSyncState +} + +func (c *ProjectSyncStateClient) mutate(ctx context.Context, m *ProjectSyncStateMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&ProjectSyncStateCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&ProjectSyncStateUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&ProjectSyncStateUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&ProjectSyncStateDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("ent: unknown ProjectSyncState mutation op: %q", m.Op()) + } +} + +// RuntimeBrokerClient is a client for the RuntimeBroker schema. +type RuntimeBrokerClient struct { + config +} + +// NewRuntimeBrokerClient returns a client for the RuntimeBroker from the given config. +func NewRuntimeBrokerClient(c config) *RuntimeBrokerClient { + return &RuntimeBrokerClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `runtimebroker.Hooks(f(g(h())))`. +func (c *RuntimeBrokerClient) Use(hooks ...Hook) { + c.hooks.RuntimeBroker = append(c.hooks.RuntimeBroker, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `runtimebroker.Intercept(f(g(h())))`. +func (c *RuntimeBrokerClient) Intercept(interceptors ...Interceptor) { + c.inters.RuntimeBroker = append(c.inters.RuntimeBroker, interceptors...) +} + +// Create returns a builder for creating a RuntimeBroker entity. +func (c *RuntimeBrokerClient) Create() *RuntimeBrokerCreate { + mutation := newRuntimeBrokerMutation(c.config, OpCreate) + return &RuntimeBrokerCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of RuntimeBroker entities. +func (c *RuntimeBrokerClient) CreateBulk(builders ...*RuntimeBrokerCreate) *RuntimeBrokerCreateBulk { + return &RuntimeBrokerCreateBulk{config: c.config, builders: builders} +} + +// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates +// a builder and applies setFunc on it. +func (c *RuntimeBrokerClient) MapCreateBulk(slice any, setFunc func(*RuntimeBrokerCreate, int)) *RuntimeBrokerCreateBulk { + rv := reflect.ValueOf(slice) + if rv.Kind() != reflect.Slice { + return &RuntimeBrokerCreateBulk{err: fmt.Errorf("calling to RuntimeBrokerClient.MapCreateBulk with wrong type %T, need slice", slice)} + } + builders := make([]*RuntimeBrokerCreate, rv.Len()) + for i := 0; i < rv.Len(); i++ { + builders[i] = c.Create() + setFunc(builders[i], i) + } + return &RuntimeBrokerCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for RuntimeBroker. +func (c *RuntimeBrokerClient) Update() *RuntimeBrokerUpdate { + mutation := newRuntimeBrokerMutation(c.config, OpUpdate) + return &RuntimeBrokerUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *RuntimeBrokerClient) UpdateOne(_m *RuntimeBroker) *RuntimeBrokerUpdateOne { + mutation := newRuntimeBrokerMutation(c.config, OpUpdateOne, withRuntimeBroker(_m)) + return &RuntimeBrokerUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOneID returns an update builder for the given id. +func (c *RuntimeBrokerClient) UpdateOneID(id uuid.UUID) *RuntimeBrokerUpdateOne { + mutation := newRuntimeBrokerMutation(c.config, OpUpdateOne, withRuntimeBrokerID(id)) + return &RuntimeBrokerUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for RuntimeBroker. +func (c *RuntimeBrokerClient) Delete() *RuntimeBrokerDelete { + mutation := newRuntimeBrokerMutation(c.config, OpDelete) + return &RuntimeBrokerDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// DeleteOne returns a builder for deleting the given entity. +func (c *RuntimeBrokerClient) DeleteOne(_m *RuntimeBroker) *RuntimeBrokerDeleteOne { + return c.DeleteOneID(_m.ID) +} + +// DeleteOneID returns a builder for deleting the given entity by its id. +func (c *RuntimeBrokerClient) DeleteOneID(id uuid.UUID) *RuntimeBrokerDeleteOne { + builder := c.Delete().Where(runtimebroker.ID(id)) + builder.mutation.id = &id + builder.mutation.op = OpDeleteOne + return &RuntimeBrokerDeleteOne{builder} +} + +// Query returns a query builder for RuntimeBroker. +func (c *RuntimeBrokerClient) Query() *RuntimeBrokerQuery { + return &RuntimeBrokerQuery{ + config: c.config, + ctx: &QueryContext{Type: TypeRuntimeBroker}, + inters: c.Interceptors(), + } +} + +// Get returns a RuntimeBroker entity by its id. +func (c *RuntimeBrokerClient) Get(ctx context.Context, id uuid.UUID) (*RuntimeBroker, error) { + return c.Query().Where(runtimebroker.ID(id)).Only(ctx) +} + +// GetX is like Get, but panics if an error occurs. +func (c *RuntimeBrokerClient) GetX(ctx context.Context, id uuid.UUID) *RuntimeBroker { + obj, err := c.Get(ctx, id) + if err != nil { + panic(err) + } + return obj +} + +// Hooks returns the client hooks. +func (c *RuntimeBrokerClient) Hooks() []Hook { + return c.hooks.RuntimeBroker +} + +// Interceptors returns the client interceptors. +func (c *RuntimeBrokerClient) Interceptors() []Interceptor { + return c.inters.RuntimeBroker +} + +func (c *RuntimeBrokerClient) mutate(ctx context.Context, m *RuntimeBrokerMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&RuntimeBrokerCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&RuntimeBrokerUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&RuntimeBrokerUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&RuntimeBrokerDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("ent: unknown RuntimeBroker mutation op: %q", m.Op()) + } +} + +// ScheduleClient is a client for the Schedule schema. +type ScheduleClient struct { + config +} + +// NewScheduleClient returns a client for the Schedule from the given config. +func NewScheduleClient(c config) *ScheduleClient { + return &ScheduleClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `schedule.Hooks(f(g(h())))`. +func (c *ScheduleClient) Use(hooks ...Hook) { + c.hooks.Schedule = append(c.hooks.Schedule, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `schedule.Intercept(f(g(h())))`. +func (c *ScheduleClient) Intercept(interceptors ...Interceptor) { + c.inters.Schedule = append(c.inters.Schedule, interceptors...) +} + +// Create returns a builder for creating a Schedule entity. +func (c *ScheduleClient) Create() *ScheduleCreate { + mutation := newScheduleMutation(c.config, OpCreate) + return &ScheduleCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of Schedule entities. +func (c *ScheduleClient) CreateBulk(builders ...*ScheduleCreate) *ScheduleCreateBulk { + return &ScheduleCreateBulk{config: c.config, builders: builders} +} + +// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates +// a builder and applies setFunc on it. +func (c *ScheduleClient) MapCreateBulk(slice any, setFunc func(*ScheduleCreate, int)) *ScheduleCreateBulk { + rv := reflect.ValueOf(slice) + if rv.Kind() != reflect.Slice { + return &ScheduleCreateBulk{err: fmt.Errorf("calling to ScheduleClient.MapCreateBulk with wrong type %T, need slice", slice)} + } + builders := make([]*ScheduleCreate, rv.Len()) + for i := 0; i < rv.Len(); i++ { + builders[i] = c.Create() + setFunc(builders[i], i) + } + return &ScheduleCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for Schedule. +func (c *ScheduleClient) Update() *ScheduleUpdate { + mutation := newScheduleMutation(c.config, OpUpdate) + return &ScheduleUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *ScheduleClient) UpdateOne(_m *Schedule) *ScheduleUpdateOne { + mutation := newScheduleMutation(c.config, OpUpdateOne, withSchedule(_m)) + return &ScheduleUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOneID returns an update builder for the given id. +func (c *ScheduleClient) UpdateOneID(id uuid.UUID) *ScheduleUpdateOne { + mutation := newScheduleMutation(c.config, OpUpdateOne, withScheduleID(id)) + return &ScheduleUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for Schedule. +func (c *ScheduleClient) Delete() *ScheduleDelete { + mutation := newScheduleMutation(c.config, OpDelete) + return &ScheduleDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// DeleteOne returns a builder for deleting the given entity. +func (c *ScheduleClient) DeleteOne(_m *Schedule) *ScheduleDeleteOne { + return c.DeleteOneID(_m.ID) +} + +// DeleteOneID returns a builder for deleting the given entity by its id. +func (c *ScheduleClient) DeleteOneID(id uuid.UUID) *ScheduleDeleteOne { + builder := c.Delete().Where(schedule.ID(id)) + builder.mutation.id = &id + builder.mutation.op = OpDeleteOne + return &ScheduleDeleteOne{builder} +} + +// Query returns a query builder for Schedule. +func (c *ScheduleClient) Query() *ScheduleQuery { + return &ScheduleQuery{ + config: c.config, + ctx: &QueryContext{Type: TypeSchedule}, + inters: c.Interceptors(), + } +} + +// Get returns a Schedule entity by its id. +func (c *ScheduleClient) Get(ctx context.Context, id uuid.UUID) (*Schedule, error) { + return c.Query().Where(schedule.ID(id)).Only(ctx) +} + +// GetX is like Get, but panics if an error occurs. +func (c *ScheduleClient) GetX(ctx context.Context, id uuid.UUID) *Schedule { + obj, err := c.Get(ctx, id) + if err != nil { + panic(err) + } + return obj +} + +// Hooks returns the client hooks. +func (c *ScheduleClient) Hooks() []Hook { + return c.hooks.Schedule +} + +// Interceptors returns the client interceptors. +func (c *ScheduleClient) Interceptors() []Interceptor { + return c.inters.Schedule +} + +func (c *ScheduleClient) mutate(ctx context.Context, m *ScheduleMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&ScheduleCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&ScheduleUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&ScheduleUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&ScheduleDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("ent: unknown Schedule mutation op: %q", m.Op()) + } +} + +// ScheduledEventClient is a client for the ScheduledEvent schema. +type ScheduledEventClient struct { + config +} + +// NewScheduledEventClient returns a client for the ScheduledEvent from the given config. +func NewScheduledEventClient(c config) *ScheduledEventClient { + return &ScheduledEventClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `scheduledevent.Hooks(f(g(h())))`. +func (c *ScheduledEventClient) Use(hooks ...Hook) { + c.hooks.ScheduledEvent = append(c.hooks.ScheduledEvent, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `scheduledevent.Intercept(f(g(h())))`. +func (c *ScheduledEventClient) Intercept(interceptors ...Interceptor) { + c.inters.ScheduledEvent = append(c.inters.ScheduledEvent, interceptors...) +} + +// Create returns a builder for creating a ScheduledEvent entity. +func (c *ScheduledEventClient) Create() *ScheduledEventCreate { + mutation := newScheduledEventMutation(c.config, OpCreate) + return &ScheduledEventCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of ScheduledEvent entities. +func (c *ScheduledEventClient) CreateBulk(builders ...*ScheduledEventCreate) *ScheduledEventCreateBulk { + return &ScheduledEventCreateBulk{config: c.config, builders: builders} +} + +// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates +// a builder and applies setFunc on it. +func (c *ScheduledEventClient) MapCreateBulk(slice any, setFunc func(*ScheduledEventCreate, int)) *ScheduledEventCreateBulk { + rv := reflect.ValueOf(slice) + if rv.Kind() != reflect.Slice { + return &ScheduledEventCreateBulk{err: fmt.Errorf("calling to ScheduledEventClient.MapCreateBulk with wrong type %T, need slice", slice)} + } + builders := make([]*ScheduledEventCreate, rv.Len()) + for i := 0; i < rv.Len(); i++ { + builders[i] = c.Create() + setFunc(builders[i], i) + } + return &ScheduledEventCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for ScheduledEvent. +func (c *ScheduledEventClient) Update() *ScheduledEventUpdate { + mutation := newScheduledEventMutation(c.config, OpUpdate) + return &ScheduledEventUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *ScheduledEventClient) UpdateOne(_m *ScheduledEvent) *ScheduledEventUpdateOne { + mutation := newScheduledEventMutation(c.config, OpUpdateOne, withScheduledEvent(_m)) + return &ScheduledEventUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOneID returns an update builder for the given id. +func (c *ScheduledEventClient) UpdateOneID(id uuid.UUID) *ScheduledEventUpdateOne { + mutation := newScheduledEventMutation(c.config, OpUpdateOne, withScheduledEventID(id)) + return &ScheduledEventUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for ScheduledEvent. +func (c *ScheduledEventClient) Delete() *ScheduledEventDelete { + mutation := newScheduledEventMutation(c.config, OpDelete) + return &ScheduledEventDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// DeleteOne returns a builder for deleting the given entity. +func (c *ScheduledEventClient) DeleteOne(_m *ScheduledEvent) *ScheduledEventDeleteOne { + return c.DeleteOneID(_m.ID) +} + +// DeleteOneID returns a builder for deleting the given entity by its id. +func (c *ScheduledEventClient) DeleteOneID(id uuid.UUID) *ScheduledEventDeleteOne { + builder := c.Delete().Where(scheduledevent.ID(id)) + builder.mutation.id = &id + builder.mutation.op = OpDeleteOne + return &ScheduledEventDeleteOne{builder} +} + +// Query returns a query builder for ScheduledEvent. +func (c *ScheduledEventClient) Query() *ScheduledEventQuery { + return &ScheduledEventQuery{ + config: c.config, + ctx: &QueryContext{Type: TypeScheduledEvent}, + inters: c.Interceptors(), + } +} + +// Get returns a ScheduledEvent entity by its id. +func (c *ScheduledEventClient) Get(ctx context.Context, id uuid.UUID) (*ScheduledEvent, error) { + return c.Query().Where(scheduledevent.ID(id)).Only(ctx) +} + +// GetX is like Get, but panics if an error occurs. +func (c *ScheduledEventClient) GetX(ctx context.Context, id uuid.UUID) *ScheduledEvent { + obj, err := c.Get(ctx, id) + if err != nil { + panic(err) + } + return obj +} + +// Hooks returns the client hooks. +func (c *ScheduledEventClient) Hooks() []Hook { + return c.hooks.ScheduledEvent +} + +// Interceptors returns the client interceptors. +func (c *ScheduledEventClient) Interceptors() []Interceptor { + return c.inters.ScheduledEvent +} + +func (c *ScheduledEventClient) mutate(ctx context.Context, m *ScheduledEventMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&ScheduledEventCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&ScheduledEventUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&ScheduledEventUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&ScheduledEventDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("ent: unknown ScheduledEvent mutation op: %q", m.Op()) + } +} + +// SecretClient is a client for the Secret schema. +type SecretClient struct { + config +} + +// NewSecretClient returns a client for the Secret from the given config. +func NewSecretClient(c config) *SecretClient { + return &SecretClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `secret.Hooks(f(g(h())))`. +func (c *SecretClient) Use(hooks ...Hook) { + c.hooks.Secret = append(c.hooks.Secret, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `secret.Intercept(f(g(h())))`. +func (c *SecretClient) Intercept(interceptors ...Interceptor) { + c.inters.Secret = append(c.inters.Secret, interceptors...) +} + +// Create returns a builder for creating a Secret entity. +func (c *SecretClient) Create() *SecretCreate { + mutation := newSecretMutation(c.config, OpCreate) + return &SecretCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of Secret entities. +func (c *SecretClient) CreateBulk(builders ...*SecretCreate) *SecretCreateBulk { + return &SecretCreateBulk{config: c.config, builders: builders} +} + +// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates +// a builder and applies setFunc on it. +func (c *SecretClient) MapCreateBulk(slice any, setFunc func(*SecretCreate, int)) *SecretCreateBulk { + rv := reflect.ValueOf(slice) + if rv.Kind() != reflect.Slice { + return &SecretCreateBulk{err: fmt.Errorf("calling to SecretClient.MapCreateBulk with wrong type %T, need slice", slice)} + } + builders := make([]*SecretCreate, rv.Len()) + for i := 0; i < rv.Len(); i++ { + builders[i] = c.Create() + setFunc(builders[i], i) + } + return &SecretCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for Secret. +func (c *SecretClient) Update() *SecretUpdate { + mutation := newSecretMutation(c.config, OpUpdate) + return &SecretUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *SecretClient) UpdateOne(_m *Secret) *SecretUpdateOne { + mutation := newSecretMutation(c.config, OpUpdateOne, withSecret(_m)) + return &SecretUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOneID returns an update builder for the given id. +func (c *SecretClient) UpdateOneID(id uuid.UUID) *SecretUpdateOne { + mutation := newSecretMutation(c.config, OpUpdateOne, withSecretID(id)) + return &SecretUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for Secret. +func (c *SecretClient) Delete() *SecretDelete { + mutation := newSecretMutation(c.config, OpDelete) + return &SecretDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// DeleteOne returns a builder for deleting the given entity. +func (c *SecretClient) DeleteOne(_m *Secret) *SecretDeleteOne { + return c.DeleteOneID(_m.ID) +} + +// DeleteOneID returns a builder for deleting the given entity by its id. +func (c *SecretClient) DeleteOneID(id uuid.UUID) *SecretDeleteOne { + builder := c.Delete().Where(secret.ID(id)) + builder.mutation.id = &id + builder.mutation.op = OpDeleteOne + return &SecretDeleteOne{builder} +} + +// Query returns a query builder for Secret. +func (c *SecretClient) Query() *SecretQuery { + return &SecretQuery{ + config: c.config, + ctx: &QueryContext{Type: TypeSecret}, + inters: c.Interceptors(), + } +} + +// Get returns a Secret entity by its id. +func (c *SecretClient) Get(ctx context.Context, id uuid.UUID) (*Secret, error) { + return c.Query().Where(secret.ID(id)).Only(ctx) +} + +// GetX is like Get, but panics if an error occurs. +func (c *SecretClient) GetX(ctx context.Context, id uuid.UUID) *Secret { + obj, err := c.Get(ctx, id) + if err != nil { + panic(err) + } + return obj +} + +// Hooks returns the client hooks. +func (c *SecretClient) Hooks() []Hook { + return c.hooks.Secret +} + +// Interceptors returns the client interceptors. +func (c *SecretClient) Interceptors() []Interceptor { + return c.inters.Secret +} + +func (c *SecretClient) mutate(ctx context.Context, m *SecretMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&SecretCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&SecretUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&SecretUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&SecretDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("ent: unknown Secret mutation op: %q", m.Op()) + } +} + +// SubscriptionTemplateClient is a client for the SubscriptionTemplate schema. +type SubscriptionTemplateClient struct { + config +} + +// NewSubscriptionTemplateClient returns a client for the SubscriptionTemplate from the given config. +func NewSubscriptionTemplateClient(c config) *SubscriptionTemplateClient { + return &SubscriptionTemplateClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `subscriptiontemplate.Hooks(f(g(h())))`. +func (c *SubscriptionTemplateClient) Use(hooks ...Hook) { + c.hooks.SubscriptionTemplate = append(c.hooks.SubscriptionTemplate, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `subscriptiontemplate.Intercept(f(g(h())))`. +func (c *SubscriptionTemplateClient) Intercept(interceptors ...Interceptor) { + c.inters.SubscriptionTemplate = append(c.inters.SubscriptionTemplate, interceptors...) +} + +// Create returns a builder for creating a SubscriptionTemplate entity. +func (c *SubscriptionTemplateClient) Create() *SubscriptionTemplateCreate { + mutation := newSubscriptionTemplateMutation(c.config, OpCreate) + return &SubscriptionTemplateCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of SubscriptionTemplate entities. +func (c *SubscriptionTemplateClient) CreateBulk(builders ...*SubscriptionTemplateCreate) *SubscriptionTemplateCreateBulk { + return &SubscriptionTemplateCreateBulk{config: c.config, builders: builders} } // MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates // a builder and applies setFunc on it. -func (c *PolicyBindingClient) MapCreateBulk(slice any, setFunc func(*PolicyBindingCreate, int)) *PolicyBindingCreateBulk { +func (c *SubscriptionTemplateClient) MapCreateBulk(slice any, setFunc func(*SubscriptionTemplateCreate, int)) *SubscriptionTemplateCreateBulk { rv := reflect.ValueOf(slice) if rv.Kind() != reflect.Slice { - return &PolicyBindingCreateBulk{err: fmt.Errorf("calling to PolicyBindingClient.MapCreateBulk with wrong type %T, need slice", slice)} + return &SubscriptionTemplateCreateBulk{err: fmt.Errorf("calling to SubscriptionTemplateClient.MapCreateBulk with wrong type %T, need slice", slice)} } - builders := make([]*PolicyBindingCreate, rv.Len()) + builders := make([]*SubscriptionTemplateCreate, rv.Len()) for i := 0; i < rv.Len(); i++ { builders[i] = c.Create() setFunc(builders[i], i) } - return &PolicyBindingCreateBulk{config: c.config, builders: builders} + return &SubscriptionTemplateCreateBulk{config: c.config, builders: builders} } -// Update returns an update builder for PolicyBinding. -func (c *PolicyBindingClient) Update() *PolicyBindingUpdate { - mutation := newPolicyBindingMutation(c.config, OpUpdate) - return &PolicyBindingUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +// Update returns an update builder for SubscriptionTemplate. +func (c *SubscriptionTemplateClient) Update() *SubscriptionTemplateUpdate { + mutation := newSubscriptionTemplateMutation(c.config, OpUpdate) + return &SubscriptionTemplateUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} } // UpdateOne returns an update builder for the given entity. -func (c *PolicyBindingClient) UpdateOne(_m *PolicyBinding) *PolicyBindingUpdateOne { - mutation := newPolicyBindingMutation(c.config, OpUpdateOne, withPolicyBinding(_m)) - return &PolicyBindingUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +func (c *SubscriptionTemplateClient) UpdateOne(_m *SubscriptionTemplate) *SubscriptionTemplateUpdateOne { + mutation := newSubscriptionTemplateMutation(c.config, OpUpdateOne, withSubscriptionTemplate(_m)) + return &SubscriptionTemplateUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} } // UpdateOneID returns an update builder for the given id. -func (c *PolicyBindingClient) UpdateOneID(id uuid.UUID) *PolicyBindingUpdateOne { - mutation := newPolicyBindingMutation(c.config, OpUpdateOne, withPolicyBindingID(id)) - return &PolicyBindingUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +func (c *SubscriptionTemplateClient) UpdateOneID(id uuid.UUID) *SubscriptionTemplateUpdateOne { + mutation := newSubscriptionTemplateMutation(c.config, OpUpdateOne, withSubscriptionTemplateID(id)) + return &SubscriptionTemplateUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} } -// Delete returns a delete builder for PolicyBinding. -func (c *PolicyBindingClient) Delete() *PolicyBindingDelete { - mutation := newPolicyBindingMutation(c.config, OpDelete) - return &PolicyBindingDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +// Delete returns a delete builder for SubscriptionTemplate. +func (c *SubscriptionTemplateClient) Delete() *SubscriptionTemplateDelete { + mutation := newSubscriptionTemplateMutation(c.config, OpDelete) + return &SubscriptionTemplateDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} } // DeleteOne returns a builder for deleting the given entity. -func (c *PolicyBindingClient) DeleteOne(_m *PolicyBinding) *PolicyBindingDeleteOne { +func (c *SubscriptionTemplateClient) DeleteOne(_m *SubscriptionTemplate) *SubscriptionTemplateDeleteOne { return c.DeleteOneID(_m.ID) } // DeleteOneID returns a builder for deleting the given entity by its id. -func (c *PolicyBindingClient) DeleteOneID(id uuid.UUID) *PolicyBindingDeleteOne { - builder := c.Delete().Where(policybinding.ID(id)) +func (c *SubscriptionTemplateClient) DeleteOneID(id uuid.UUID) *SubscriptionTemplateDeleteOne { + builder := c.Delete().Where(subscriptiontemplate.ID(id)) builder.mutation.id = &id builder.mutation.op = OpDeleteOne - return &PolicyBindingDeleteOne{builder} + return &SubscriptionTemplateDeleteOne{builder} } -// Query returns a query builder for PolicyBinding. -func (c *PolicyBindingClient) Query() *PolicyBindingQuery { - return &PolicyBindingQuery{ +// Query returns a query builder for SubscriptionTemplate. +func (c *SubscriptionTemplateClient) Query() *SubscriptionTemplateQuery { + return &SubscriptionTemplateQuery{ config: c.config, - ctx: &QueryContext{Type: TypePolicyBinding}, + ctx: &QueryContext{Type: TypeSubscriptionTemplate}, inters: c.Interceptors(), } } -// Get returns a PolicyBinding entity by its id. -func (c *PolicyBindingClient) Get(ctx context.Context, id uuid.UUID) (*PolicyBinding, error) { - return c.Query().Where(policybinding.ID(id)).Only(ctx) +// Get returns a SubscriptionTemplate entity by its id. +func (c *SubscriptionTemplateClient) Get(ctx context.Context, id uuid.UUID) (*SubscriptionTemplate, error) { + return c.Query().Where(subscriptiontemplate.ID(id)).Only(ctx) } // GetX is like Get, but panics if an error occurs. -func (c *PolicyBindingClient) GetX(ctx context.Context, id uuid.UUID) *PolicyBinding { +func (c *SubscriptionTemplateClient) GetX(ctx context.Context, id uuid.UUID) *SubscriptionTemplate { obj, err := c.Get(ctx, id) if err != nil { panic(err) @@ -1120,196 +4320,132 @@ func (c *PolicyBindingClient) GetX(ctx context.Context, id uuid.UUID) *PolicyBin return obj } -// QueryPolicy queries the policy edge of a PolicyBinding. -func (c *PolicyBindingClient) QueryPolicy(_m *PolicyBinding) *AccessPolicyQuery { - query := (&AccessPolicyClient{config: c.config}).Query() - query.path = func(context.Context) (fromV *sql.Selector, _ error) { - id := _m.ID - step := sqlgraph.NewStep( - sqlgraph.From(policybinding.Table, policybinding.FieldID, id), - sqlgraph.To(accesspolicy.Table, accesspolicy.FieldID), - sqlgraph.Edge(sqlgraph.M2O, true, policybinding.PolicyTable, policybinding.PolicyColumn), - ) - fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step) - return fromV, nil - } - return query -} - -// QueryUser queries the user edge of a PolicyBinding. -func (c *PolicyBindingClient) QueryUser(_m *PolicyBinding) *UserQuery { - query := (&UserClient{config: c.config}).Query() - query.path = func(context.Context) (fromV *sql.Selector, _ error) { - id := _m.ID - step := sqlgraph.NewStep( - sqlgraph.From(policybinding.Table, policybinding.FieldID, id), - sqlgraph.To(user.Table, user.FieldID), - sqlgraph.Edge(sqlgraph.M2O, false, policybinding.UserTable, policybinding.UserColumn), - ) - fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step) - return fromV, nil - } - return query -} - -// QueryGroup queries the group edge of a PolicyBinding. -func (c *PolicyBindingClient) QueryGroup(_m *PolicyBinding) *GroupQuery { - query := (&GroupClient{config: c.config}).Query() - query.path = func(context.Context) (fromV *sql.Selector, _ error) { - id := _m.ID - step := sqlgraph.NewStep( - sqlgraph.From(policybinding.Table, policybinding.FieldID, id), - sqlgraph.To(group.Table, group.FieldID), - sqlgraph.Edge(sqlgraph.M2O, false, policybinding.GroupTable, policybinding.GroupColumn), - ) - fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step) - return fromV, nil - } - return query -} - -// QueryAgent queries the agent edge of a PolicyBinding. -func (c *PolicyBindingClient) QueryAgent(_m *PolicyBinding) *AgentQuery { - query := (&AgentClient{config: c.config}).Query() - query.path = func(context.Context) (fromV *sql.Selector, _ error) { - id := _m.ID - step := sqlgraph.NewStep( - sqlgraph.From(policybinding.Table, policybinding.FieldID, id), - sqlgraph.To(agent.Table, agent.FieldID), - sqlgraph.Edge(sqlgraph.M2O, false, policybinding.AgentTable, policybinding.AgentColumn), - ) - fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step) - return fromV, nil - } - return query -} - // Hooks returns the client hooks. -func (c *PolicyBindingClient) Hooks() []Hook { - return c.hooks.PolicyBinding +func (c *SubscriptionTemplateClient) Hooks() []Hook { + return c.hooks.SubscriptionTemplate } // Interceptors returns the client interceptors. -func (c *PolicyBindingClient) Interceptors() []Interceptor { - return c.inters.PolicyBinding +func (c *SubscriptionTemplateClient) Interceptors() []Interceptor { + return c.inters.SubscriptionTemplate } -func (c *PolicyBindingClient) mutate(ctx context.Context, m *PolicyBindingMutation) (Value, error) { +func (c *SubscriptionTemplateClient) mutate(ctx context.Context, m *SubscriptionTemplateMutation) (Value, error) { switch m.Op() { case OpCreate: - return (&PolicyBindingCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + return (&SubscriptionTemplateCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) case OpUpdate: - return (&PolicyBindingUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + return (&SubscriptionTemplateUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) case OpUpdateOne: - return (&PolicyBindingUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + return (&SubscriptionTemplateUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) case OpDelete, OpDeleteOne: - return (&PolicyBindingDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + return (&SubscriptionTemplateDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) default: - return nil, fmt.Errorf("ent: unknown PolicyBinding mutation op: %q", m.Op()) + return nil, fmt.Errorf("ent: unknown SubscriptionTemplate mutation op: %q", m.Op()) } } -// ProjectClient is a client for the Project schema. -type ProjectClient struct { +// TemplateClient is a client for the Template schema. +type TemplateClient struct { config } -// NewProjectClient returns a client for the Project from the given config. -func NewProjectClient(c config) *ProjectClient { - return &ProjectClient{config: c} +// NewTemplateClient returns a client for the Template from the given config. +func NewTemplateClient(c config) *TemplateClient { + return &TemplateClient{config: c} } // Use adds a list of mutation hooks to the hooks stack. -// A call to `Use(f, g, h)` equals to `project.Hooks(f(g(h())))`. -func (c *ProjectClient) Use(hooks ...Hook) { - c.hooks.Project = append(c.hooks.Project, hooks...) +// A call to `Use(f, g, h)` equals to `template.Hooks(f(g(h())))`. +func (c *TemplateClient) Use(hooks ...Hook) { + c.hooks.Template = append(c.hooks.Template, hooks...) } // Intercept adds a list of query interceptors to the interceptors stack. -// A call to `Intercept(f, g, h)` equals to `project.Intercept(f(g(h())))`. -func (c *ProjectClient) Intercept(interceptors ...Interceptor) { - c.inters.Project = append(c.inters.Project, interceptors...) +// A call to `Intercept(f, g, h)` equals to `template.Intercept(f(g(h())))`. +func (c *TemplateClient) Intercept(interceptors ...Interceptor) { + c.inters.Template = append(c.inters.Template, interceptors...) } -// Create returns a builder for creating a Project entity. -func (c *ProjectClient) Create() *ProjectCreate { - mutation := newProjectMutation(c.config, OpCreate) - return &ProjectCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +// Create returns a builder for creating a Template entity. +func (c *TemplateClient) Create() *TemplateCreate { + mutation := newTemplateMutation(c.config, OpCreate) + return &TemplateCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} } -// CreateBulk returns a builder for creating a bulk of Project entities. -func (c *ProjectClient) CreateBulk(builders ...*ProjectCreate) *ProjectCreateBulk { - return &ProjectCreateBulk{config: c.config, builders: builders} +// CreateBulk returns a builder for creating a bulk of Template entities. +func (c *TemplateClient) CreateBulk(builders ...*TemplateCreate) *TemplateCreateBulk { + return &TemplateCreateBulk{config: c.config, builders: builders} } // MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates // a builder and applies setFunc on it. -func (c *ProjectClient) MapCreateBulk(slice any, setFunc func(*ProjectCreate, int)) *ProjectCreateBulk { +func (c *TemplateClient) MapCreateBulk(slice any, setFunc func(*TemplateCreate, int)) *TemplateCreateBulk { rv := reflect.ValueOf(slice) if rv.Kind() != reflect.Slice { - return &ProjectCreateBulk{err: fmt.Errorf("calling to ProjectClient.MapCreateBulk with wrong type %T, need slice", slice)} + return &TemplateCreateBulk{err: fmt.Errorf("calling to TemplateClient.MapCreateBulk with wrong type %T, need slice", slice)} } - builders := make([]*ProjectCreate, rv.Len()) + builders := make([]*TemplateCreate, rv.Len()) for i := 0; i < rv.Len(); i++ { builders[i] = c.Create() setFunc(builders[i], i) } - return &ProjectCreateBulk{config: c.config, builders: builders} + return &TemplateCreateBulk{config: c.config, builders: builders} } -// Update returns an update builder for Project. -func (c *ProjectClient) Update() *ProjectUpdate { - mutation := newProjectMutation(c.config, OpUpdate) - return &ProjectUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +// Update returns an update builder for Template. +func (c *TemplateClient) Update() *TemplateUpdate { + mutation := newTemplateMutation(c.config, OpUpdate) + return &TemplateUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} } // UpdateOne returns an update builder for the given entity. -func (c *ProjectClient) UpdateOne(_m *Project) *ProjectUpdateOne { - mutation := newProjectMutation(c.config, OpUpdateOne, withProject(_m)) - return &ProjectUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +func (c *TemplateClient) UpdateOne(_m *Template) *TemplateUpdateOne { + mutation := newTemplateMutation(c.config, OpUpdateOne, withTemplate(_m)) + return &TemplateUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} } // UpdateOneID returns an update builder for the given id. -func (c *ProjectClient) UpdateOneID(id uuid.UUID) *ProjectUpdateOne { - mutation := newProjectMutation(c.config, OpUpdateOne, withProjectID(id)) - return &ProjectUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +func (c *TemplateClient) UpdateOneID(id uuid.UUID) *TemplateUpdateOne { + mutation := newTemplateMutation(c.config, OpUpdateOne, withTemplateID(id)) + return &TemplateUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} } -// Delete returns a delete builder for Project. -func (c *ProjectClient) Delete() *ProjectDelete { - mutation := newProjectMutation(c.config, OpDelete) - return &ProjectDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +// Delete returns a delete builder for Template. +func (c *TemplateClient) Delete() *TemplateDelete { + mutation := newTemplateMutation(c.config, OpDelete) + return &TemplateDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} } // DeleteOne returns a builder for deleting the given entity. -func (c *ProjectClient) DeleteOne(_m *Project) *ProjectDeleteOne { +func (c *TemplateClient) DeleteOne(_m *Template) *TemplateDeleteOne { return c.DeleteOneID(_m.ID) } // DeleteOneID returns a builder for deleting the given entity by its id. -func (c *ProjectClient) DeleteOneID(id uuid.UUID) *ProjectDeleteOne { - builder := c.Delete().Where(project.ID(id)) +func (c *TemplateClient) DeleteOneID(id uuid.UUID) *TemplateDeleteOne { + builder := c.Delete().Where(template.ID(id)) builder.mutation.id = &id builder.mutation.op = OpDeleteOne - return &ProjectDeleteOne{builder} + return &TemplateDeleteOne{builder} } -// Query returns a query builder for Project. -func (c *ProjectClient) Query() *ProjectQuery { - return &ProjectQuery{ +// Query returns a query builder for Template. +func (c *TemplateClient) Query() *TemplateQuery { + return &TemplateQuery{ config: c.config, - ctx: &QueryContext{Type: TypeProject}, + ctx: &QueryContext{Type: TypeTemplate}, inters: c.Interceptors(), } } -// Get returns a Project entity by its id. -func (c *ProjectClient) Get(ctx context.Context, id uuid.UUID) (*Project, error) { - return c.Query().Where(project.ID(id)).Only(ctx) +// Get returns a Template entity by its id. +func (c *TemplateClient) Get(ctx context.Context, id uuid.UUID) (*Template, error) { + return c.Query().Where(template.ID(id)).Only(ctx) } // GetX is like Get, but panics if an error occurs. -func (c *ProjectClient) GetX(ctx context.Context, id uuid.UUID) *Project { +func (c *TemplateClient) GetX(ctx context.Context, id uuid.UUID) *Template { obj, err := c.Get(ctx, id) if err != nil { panic(err) @@ -1317,44 +4453,28 @@ func (c *ProjectClient) GetX(ctx context.Context, id uuid.UUID) *Project { return obj } -// QueryAgents queries the agents edge of a Project. -func (c *ProjectClient) QueryAgents(_m *Project) *AgentQuery { - query := (&AgentClient{config: c.config}).Query() - query.path = func(context.Context) (fromV *sql.Selector, _ error) { - id := _m.ID - step := sqlgraph.NewStep( - sqlgraph.From(project.Table, project.FieldID, id), - sqlgraph.To(agent.Table, agent.FieldID), - sqlgraph.Edge(sqlgraph.O2M, false, project.AgentsTable, project.AgentsColumn), - ) - fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step) - return fromV, nil - } - return query -} - // Hooks returns the client hooks. -func (c *ProjectClient) Hooks() []Hook { - return c.hooks.Project +func (c *TemplateClient) Hooks() []Hook { + return c.hooks.Template } // Interceptors returns the client interceptors. -func (c *ProjectClient) Interceptors() []Interceptor { - return c.inters.Project +func (c *TemplateClient) Interceptors() []Interceptor { + return c.inters.Template } -func (c *ProjectClient) mutate(ctx context.Context, m *ProjectMutation) (Value, error) { +func (c *TemplateClient) mutate(ctx context.Context, m *TemplateMutation) (Value, error) { switch m.Op() { case OpCreate: - return (&ProjectCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + return (&TemplateCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) case OpUpdate: - return (&ProjectUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + return (&TemplateUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) case OpUpdateOne: - return (&ProjectUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + return (&TemplateUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) case OpDelete, OpDeleteOne: - return (&ProjectDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + return (&TemplateDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) default: - return nil, fmt.Errorf("ent: unknown Project mutation op: %q", m.Op()) + return nil, fmt.Errorf("ent: unknown Template mutation op: %q", m.Op()) } } @@ -1571,14 +4691,155 @@ func (c *UserClient) mutate(ctx context.Context, m *UserMutation) (Value, error) } } +// UserAccessTokenClient is a client for the UserAccessToken schema. +type UserAccessTokenClient struct { + config +} + +// NewUserAccessTokenClient returns a client for the UserAccessToken from the given config. +func NewUserAccessTokenClient(c config) *UserAccessTokenClient { + return &UserAccessTokenClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `useraccesstoken.Hooks(f(g(h())))`. +func (c *UserAccessTokenClient) Use(hooks ...Hook) { + c.hooks.UserAccessToken = append(c.hooks.UserAccessToken, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `useraccesstoken.Intercept(f(g(h())))`. +func (c *UserAccessTokenClient) Intercept(interceptors ...Interceptor) { + c.inters.UserAccessToken = append(c.inters.UserAccessToken, interceptors...) +} + +// Create returns a builder for creating a UserAccessToken entity. +func (c *UserAccessTokenClient) Create() *UserAccessTokenCreate { + mutation := newUserAccessTokenMutation(c.config, OpCreate) + return &UserAccessTokenCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of UserAccessToken entities. +func (c *UserAccessTokenClient) CreateBulk(builders ...*UserAccessTokenCreate) *UserAccessTokenCreateBulk { + return &UserAccessTokenCreateBulk{config: c.config, builders: builders} +} + +// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates +// a builder and applies setFunc on it. +func (c *UserAccessTokenClient) MapCreateBulk(slice any, setFunc func(*UserAccessTokenCreate, int)) *UserAccessTokenCreateBulk { + rv := reflect.ValueOf(slice) + if rv.Kind() != reflect.Slice { + return &UserAccessTokenCreateBulk{err: fmt.Errorf("calling to UserAccessTokenClient.MapCreateBulk with wrong type %T, need slice", slice)} + } + builders := make([]*UserAccessTokenCreate, rv.Len()) + for i := 0; i < rv.Len(); i++ { + builders[i] = c.Create() + setFunc(builders[i], i) + } + return &UserAccessTokenCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for UserAccessToken. +func (c *UserAccessTokenClient) Update() *UserAccessTokenUpdate { + mutation := newUserAccessTokenMutation(c.config, OpUpdate) + return &UserAccessTokenUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *UserAccessTokenClient) UpdateOne(_m *UserAccessToken) *UserAccessTokenUpdateOne { + mutation := newUserAccessTokenMutation(c.config, OpUpdateOne, withUserAccessToken(_m)) + return &UserAccessTokenUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOneID returns an update builder for the given id. +func (c *UserAccessTokenClient) UpdateOneID(id uuid.UUID) *UserAccessTokenUpdateOne { + mutation := newUserAccessTokenMutation(c.config, OpUpdateOne, withUserAccessTokenID(id)) + return &UserAccessTokenUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for UserAccessToken. +func (c *UserAccessTokenClient) Delete() *UserAccessTokenDelete { + mutation := newUserAccessTokenMutation(c.config, OpDelete) + return &UserAccessTokenDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// DeleteOne returns a builder for deleting the given entity. +func (c *UserAccessTokenClient) DeleteOne(_m *UserAccessToken) *UserAccessTokenDeleteOne { + return c.DeleteOneID(_m.ID) +} + +// DeleteOneID returns a builder for deleting the given entity by its id. +func (c *UserAccessTokenClient) DeleteOneID(id uuid.UUID) *UserAccessTokenDeleteOne { + builder := c.Delete().Where(useraccesstoken.ID(id)) + builder.mutation.id = &id + builder.mutation.op = OpDeleteOne + return &UserAccessTokenDeleteOne{builder} +} + +// Query returns a query builder for UserAccessToken. +func (c *UserAccessTokenClient) Query() *UserAccessTokenQuery { + return &UserAccessTokenQuery{ + config: c.config, + ctx: &QueryContext{Type: TypeUserAccessToken}, + inters: c.Interceptors(), + } +} + +// Get returns a UserAccessToken entity by its id. +func (c *UserAccessTokenClient) Get(ctx context.Context, id uuid.UUID) (*UserAccessToken, error) { + return c.Query().Where(useraccesstoken.ID(id)).Only(ctx) +} + +// GetX is like Get, but panics if an error occurs. +func (c *UserAccessTokenClient) GetX(ctx context.Context, id uuid.UUID) *UserAccessToken { + obj, err := c.Get(ctx, id) + if err != nil { + panic(err) + } + return obj +} + +// Hooks returns the client hooks. +func (c *UserAccessTokenClient) Hooks() []Hook { + return c.hooks.UserAccessToken +} + +// Interceptors returns the client interceptors. +func (c *UserAccessTokenClient) Interceptors() []Interceptor { + return c.inters.UserAccessToken +} + +func (c *UserAccessTokenClient) mutate(ctx context.Context, m *UserAccessTokenMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&UserAccessTokenCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&UserAccessTokenUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&UserAccessTokenUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&UserAccessTokenDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("ent: unknown UserAccessToken mutation op: %q", m.Op()) + } +} + // hooks and interceptors per client, for fast access. type ( hooks struct { - AccessPolicy, Agent, Group, GroupMembership, PolicyBinding, Project, - User []ent.Hook + AccessPolicy, Agent, AllowListEntry, ApiKey, BrokerJoinToken, BrokerSecret, + EnvVar, GCPServiceAccount, GithubInstallation, Group, GroupMembership, + HarnessConfig, InviteCode, MaintenanceOperation, MaintenanceOperationRun, + Message, Notification, NotificationSubscription, PolicyBinding, Project, + ProjectContributor, ProjectSyncState, RuntimeBroker, Schedule, ScheduledEvent, + Secret, SubscriptionTemplate, Template, User, UserAccessToken []ent.Hook } inters struct { - AccessPolicy, Agent, Group, GroupMembership, PolicyBinding, Project, - User []ent.Interceptor + AccessPolicy, Agent, AllowListEntry, ApiKey, BrokerJoinToken, BrokerSecret, + EnvVar, GCPServiceAccount, GithubInstallation, Group, GroupMembership, + HarnessConfig, InviteCode, MaintenanceOperation, MaintenanceOperationRun, + Message, Notification, NotificationSubscription, PolicyBinding, Project, + ProjectContributor, ProjectSyncState, RuntimeBroker, Schedule, ScheduledEvent, + Secret, SubscriptionTemplate, Template, User, UserAccessToken []ent.Interceptor } ) diff --git a/pkg/ent/client_driver.go b/pkg/ent/client_driver.go new file mode 100644 index 000000000..602cc1b53 --- /dev/null +++ b/pkg/ent/client_driver.go @@ -0,0 +1,29 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ent + +import "entgo.io/ent/dialect" + +// Driver returns the underlying dialect.Driver backing the client. +// +// This is a hand-written extension (not part of the generated code) that gives +// adapters access to the driver so they can (a) branch on the active SQL +// dialect and (b) execute dialect-specific raw statements that the generated +// query builders cannot express — notably `SELECT ... FOR UPDATE SKIP LOCKED` +// used by the job-claim paths under Postgres. See +// pkg/store/entadapter/schedule_store.go. +func (c *Client) Driver() dialect.Driver { + return c.driver +} diff --git a/pkg/ent/ent.go b/pkg/ent/ent.go index 62ea96d21..2b9315510 100644 --- a/pkg/ent/ent.go +++ b/pkg/ent/ent.go @@ -14,11 +14,34 @@ import ( "entgo.io/ent/dialect/sql/sqlgraph" "github.com/GoogleCloudPlatform/scion/pkg/ent/accesspolicy" "github.com/GoogleCloudPlatform/scion/pkg/ent/agent" + "github.com/GoogleCloudPlatform/scion/pkg/ent/allowlistentry" + "github.com/GoogleCloudPlatform/scion/pkg/ent/apikey" + "github.com/GoogleCloudPlatform/scion/pkg/ent/brokerjointoken" + "github.com/GoogleCloudPlatform/scion/pkg/ent/brokersecret" + "github.com/GoogleCloudPlatform/scion/pkg/ent/envvar" + "github.com/GoogleCloudPlatform/scion/pkg/ent/gcpserviceaccount" + "github.com/GoogleCloudPlatform/scion/pkg/ent/githubinstallation" "github.com/GoogleCloudPlatform/scion/pkg/ent/group" "github.com/GoogleCloudPlatform/scion/pkg/ent/groupmembership" + "github.com/GoogleCloudPlatform/scion/pkg/ent/harnessconfig" + "github.com/GoogleCloudPlatform/scion/pkg/ent/invitecode" + "github.com/GoogleCloudPlatform/scion/pkg/ent/maintenanceoperation" + "github.com/GoogleCloudPlatform/scion/pkg/ent/maintenanceoperationrun" + "github.com/GoogleCloudPlatform/scion/pkg/ent/message" + "github.com/GoogleCloudPlatform/scion/pkg/ent/notification" + "github.com/GoogleCloudPlatform/scion/pkg/ent/notificationsubscription" "github.com/GoogleCloudPlatform/scion/pkg/ent/policybinding" "github.com/GoogleCloudPlatform/scion/pkg/ent/project" + "github.com/GoogleCloudPlatform/scion/pkg/ent/projectcontributor" + "github.com/GoogleCloudPlatform/scion/pkg/ent/projectsyncstate" + "github.com/GoogleCloudPlatform/scion/pkg/ent/runtimebroker" + "github.com/GoogleCloudPlatform/scion/pkg/ent/schedule" + "github.com/GoogleCloudPlatform/scion/pkg/ent/scheduledevent" + "github.com/GoogleCloudPlatform/scion/pkg/ent/secret" + "github.com/GoogleCloudPlatform/scion/pkg/ent/subscriptiontemplate" + "github.com/GoogleCloudPlatform/scion/pkg/ent/template" "github.com/GoogleCloudPlatform/scion/pkg/ent/user" + "github.com/GoogleCloudPlatform/scion/pkg/ent/useraccesstoken" ) // ent aliases to avoid import conflicts in user's code. @@ -79,13 +102,36 @@ var ( func checkColumn(t, c string) error { initCheck.Do(func() { columnCheck = sql.NewColumnCheck(map[string]func(string) bool{ - accesspolicy.Table: accesspolicy.ValidColumn, - agent.Table: agent.ValidColumn, - group.Table: group.ValidColumn, - groupmembership.Table: groupmembership.ValidColumn, - policybinding.Table: policybinding.ValidColumn, - project.Table: project.ValidColumn, - user.Table: user.ValidColumn, + accesspolicy.Table: accesspolicy.ValidColumn, + agent.Table: agent.ValidColumn, + allowlistentry.Table: allowlistentry.ValidColumn, + apikey.Table: apikey.ValidColumn, + brokerjointoken.Table: brokerjointoken.ValidColumn, + brokersecret.Table: brokersecret.ValidColumn, + envvar.Table: envvar.ValidColumn, + gcpserviceaccount.Table: gcpserviceaccount.ValidColumn, + githubinstallation.Table: githubinstallation.ValidColumn, + group.Table: group.ValidColumn, + groupmembership.Table: groupmembership.ValidColumn, + harnessconfig.Table: harnessconfig.ValidColumn, + invitecode.Table: invitecode.ValidColumn, + maintenanceoperation.Table: maintenanceoperation.ValidColumn, + maintenanceoperationrun.Table: maintenanceoperationrun.ValidColumn, + message.Table: message.ValidColumn, + notification.Table: notification.ValidColumn, + notificationsubscription.Table: notificationsubscription.ValidColumn, + policybinding.Table: policybinding.ValidColumn, + project.Table: project.ValidColumn, + projectcontributor.Table: projectcontributor.ValidColumn, + projectsyncstate.Table: projectsyncstate.ValidColumn, + runtimebroker.Table: runtimebroker.ValidColumn, + schedule.Table: schedule.ValidColumn, + scheduledevent.Table: scheduledevent.ValidColumn, + secret.Table: secret.ValidColumn, + subscriptiontemplate.Table: subscriptiontemplate.ValidColumn, + template.Table: template.ValidColumn, + user.Table: user.ValidColumn, + useraccesstoken.Table: useraccesstoken.ValidColumn, }) }) return columnCheck(t, c) diff --git a/pkg/ent/entc/client.go b/pkg/ent/entc/client.go index 9504ea5b7..4b91c07a6 100644 --- a/pkg/ent/entc/client.go +++ b/pkg/ent/entc/client.go @@ -20,18 +20,58 @@ import ( "context" "database/sql" "fmt" + "time" "entgo.io/ent/dialect" entsql "entgo.io/ent/dialect/sql" + "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/stdlib" + "github.com/GoogleCloudPlatform/scion/pkg/ent" "github.com/GoogleCloudPlatform/scion/pkg/ent/migrate" ) +// PoolConfig holds connection pool settings applied to the underlying +// *sql.DB after it is opened. A zero value leaves the corresponding pool +// setting at the database/sql default (i.e. the field is only applied when +// it is greater than zero). +// +// NOTE: for SQLite, MaxOpenConns must be 1 to serialize writes and avoid +// "database is locked" errors; callers are responsible for supplying that. +type PoolConfig struct { + MaxOpenConns int + MaxIdleConns int + ConnMaxLifetime time.Duration + // ConnMaxIdleTime bounds how long a connection may sit idle in the pool + // before being closed. Set it shorter than the server/proxy idle timeout + // (CloudSQL drops idle connections after ~10m) so the pool recycles a + // connection before the remote silently closes it; otherwise the first + // request after an idle period stalls waiting for a dead connection to time + // out. A zero value leaves the database/sql default (no idle limit). + ConnMaxIdleTime time.Duration +} + +// apply sets the pool parameters on db, skipping any unset (non-positive) field. +func (p PoolConfig) apply(db *sql.DB) { + if p.MaxOpenConns > 0 { + db.SetMaxOpenConns(p.MaxOpenConns) + } + if p.MaxIdleConns > 0 { + db.SetMaxIdleConns(p.MaxIdleConns) + } + if p.ConnMaxLifetime > 0 { + db.SetConnMaxLifetime(p.ConnMaxLifetime) + } + if p.ConnMaxIdleTime > 0 { + db.SetConnMaxIdleTime(p.ConnMaxIdleTime) + } +} + // OpenSQLite creates an Ent client backed by SQLite. // The dsn should be a SQLite connection string (e.g. "file:ent?mode=memory&cache=shared"). // Foreign keys and WAL journal mode are enabled automatically. // This uses the modernc.org/sqlite pure-Go driver which registers as "sqlite". -func OpenSQLite(dsn string, opts ...ent.Option) (*ent.Client, error) { +func OpenSQLite(dsn string, pool PoolConfig, opts ...ent.Option) (*ent.Client, error) { db, err := sql.Open("sqlite", dsn) if err != nil { return nil, fmt.Errorf("opening sqlite connection: %w", err) @@ -45,6 +85,39 @@ func OpenSQLite(dsn string, opts ...ent.Option) (*ent.Client, error) { db.Close() return nil, fmt.Errorf("enabling WAL mode: %w", err) } + pool.apply(db) + drv := entsql.OpenDB(dialect.SQLite, db) + client := ent.NewClient(append(opts, ent.Driver(drv))...) + return client, nil +} + +// OpenSQLiteReadOnly creates an Ent client backed by a read-only SQLite +// database. It is used by the migration tool to read from a source SQLite file +// without mutating it: the connection is opened with `PRAGMA query_only = ON` +// so any accidental write fails loudly, and—unlike OpenSQLite—it does NOT +// switch the journal to WAL mode (doing so would write to the database header +// and fail on a query-only connection). +// +// MaxOpenConns is forced to 1 because the query_only and foreign_keys pragmas +// are connection-scoped; with a larger pool, unprimed connections would not +// inherit them. +func OpenSQLiteReadOnly(dsn string, opts ...ent.Option) (*ent.Client, error) { + db, err := sql.Open("sqlite", dsn) + if err != nil { + return nil, fmt.Errorf("opening sqlite connection: %w", err) + } + // Pin to a single connection so the pragmas below apply to every query. + db.SetMaxOpenConns(1) + // Foreign keys on for read consistency; query_only to guarantee the source + // is never modified during migration. + if _, err := db.Exec("PRAGMA foreign_keys = ON"); err != nil { + db.Close() + return nil, fmt.Errorf("enabling foreign keys: %w", err) + } + if _, err := db.Exec("PRAGMA query_only = ON"); err != nil { + db.Close() + return nil, fmt.Errorf("enabling query_only mode: %w", err) + } drv := entsql.OpenDB(dialect.SQLite, db) client := ent.NewClient(append(opts, ent.Driver(drv))...) return client, nil @@ -53,14 +126,49 @@ func OpenSQLite(dsn string, opts ...ent.Option) (*ent.Client, error) { // OpenPostgres creates an Ent client backed by PostgreSQL. // The dsn should be a PostgreSQL connection string // (e.g. "host=localhost port=5432 user=scion dbname=scion sslmode=disable"). -func OpenPostgres(dsn string, opts ...ent.Option) (*ent.Client, error) { - client, err := ent.Open(dialect.Postgres, dsn, opts...) +func OpenPostgres(dsn string, pool PoolConfig, opts ...ent.Option) (*ent.Client, error) { + // Parse the DSN with pgx (accepts both keyword/value DSNs "host=... port=..." + // and URL-style "postgres://..." connection strings) so we can attach TCP + // keepalive settings to the connection before handing it to database/sql via + // stdlib.OpenDB. Keepalives let the OS detect a connection silently dropped by + // a peer (e.g. CloudSQL recycling idle backends or a NAT timeout) instead of + // the first query after idle hanging on a dead socket. + connConfig, err := pgx.ParseConfig(dsn) if err != nil { - return nil, fmt.Errorf("opening postgres connection: %w", err) + return nil, fmt.Errorf("parsing postgres dsn: %w", err) } + applyKeepalives(connConfig.RuntimeParams) + if connConfig.ConnectTimeout == 0 { + connConfig.ConnectTimeout = connectTimeout + } + + db := stdlib.OpenDB(*connConfig) + pool.apply(db) + drv := entsql.OpenDB(dialect.Postgres, db) + client := ent.NewClient(append(opts, ent.Driver(drv))...) return client, nil } +const connectTimeout = 10 * time.Second + +// applyKeepalives sets server-side TCP keepalive GUCs as pgx RuntimeParams so the +// kernel probes idle connections and tears down dead ones promptly. Values mirror +// the pgx event pool (events_postgres.go): probe after 60s idle, every 15s, give +// up after 4 missed probes (~2 min to detect a dead peer). Existing keys are not +// overwritten so an explicit DSN setting wins. +func applyKeepalives(params map[string]string) { + defaults := map[string]string{ + "tcp_keepalives_idle": "60", + "tcp_keepalives_interval": "15", + "tcp_keepalives_count": "4", + } + for k, v := range defaults { + if _, ok := params[k]; !ok { + params[k] = v + } + } +} + // AutoMigrate runs automatic schema migration on the given client. func AutoMigrate(ctx context.Context, client *ent.Client) error { if err := client.Schema.Create(ctx, migrate.WithDropIndex(true), migrate.WithDropColumn(true)); err != nil { diff --git a/pkg/ent/entc/client_test.go b/pkg/ent/entc/client_test.go index 7eaf361ae..30bedf312 100644 --- a/pkg/ent/entc/client_test.go +++ b/pkg/ent/entc/client_test.go @@ -31,7 +31,7 @@ import ( // newTestClient creates an in-memory SQLite Ent client with auto-migration. func newTestClient(t *testing.T) *ent.Client { t.Helper() - client, err := OpenSQLite("file:" + t.Name() + "?mode=memory&cache=shared") + client, err := OpenSQLite("file:"+t.Name()+"?mode=memory&cache=shared", PoolConfig{}) require.NoError(t, err) t.Cleanup(func() { client.Close() }) require.NoError(t, AutoMigrate(context.Background(), client)) @@ -39,7 +39,7 @@ func newTestClient(t *testing.T) *ent.Client { } func TestOpenSQLite(t *testing.T) { - client, err := OpenSQLite("file:TestOpenSQLite?mode=memory&cache=shared") + client, err := OpenSQLite("file:TestOpenSQLite?mode=memory&cache=shared", PoolConfig{}) require.NoError(t, err) defer client.Close() require.NoError(t, AutoMigrate(context.Background(), client)) diff --git a/pkg/ent/entc/driver_postgres.go b/pkg/ent/entc/driver_postgres.go index 87b239e62..319ce12c2 100644 --- a/pkg/ent/entc/driver_postgres.go +++ b/pkg/ent/entc/driver_postgres.go @@ -14,4 +14,4 @@ package entc -import _ "github.com/lib/pq" // PostgreSQL driver +import _ "github.com/jackc/pgx/v5/stdlib" // PostgreSQL driver (pgx stdlib, registers as "pgx") diff --git a/pkg/ent/entc/migrate_alpha.go b/pkg/ent/entc/migrate_alpha.go new file mode 100644 index 000000000..39ceb1e2b --- /dev/null +++ b/pkg/ent/entc/migrate_alpha.go @@ -0,0 +1,728 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build !no_sqlite + +// Package entc — migration alpha (α). +// +// Migration α upgrades a legacy raw-SQL Hub database (the ~53-migration, +// 30-table schema produced by the now-removed pkg/store/sqlite store) to the +// consolidated Ent-backed SQLite schema. It runs in-process on first boot when +// the hub detects a legacy schema, behind an automatic backup. +// +// Strategy (validated against four real-world production hub.db files): +// +// 1. Detect the legacy schema by the presence of the `schema_migrations` +// bookkeeping table plus the legacy-only `agents.agent_id` column. +// 2. Back up the original file (checkpoint WAL, then copy to +// hub.db.bak.). +// 3. AutoMigrate a fresh Ent schema into a temporary file. +// 4. ATTACH the legacy file and copy every table with `INSERT … SELECT`, +// applying the mechanical schema differences (column renames such as +// created_at→created / agent_id→slug, the policies→access_policies and +// group_members→group_memberships table renames, surrogate-id synthesis for +// the formerly composite-keyed tables, and the polymorphic +// member_type/principal_type split). SQLite's dynamic typing carries +// bool-as-int, JSON-as-TEXT and timestamp text across unchanged, and Ent +// reads them back natively (verified end-to-end). +// 5. Verify per-table row counts match. +// 6. Atomically swap the migrated file into place. +// +// Foreign keys are disabled on the loader connection so the copy is insensitive +// to insertion order and to any dangling references the legacy data already +// contained; the live store re-enables them for all subsequent writes. +package entc + +import ( + "context" + "database/sql" + "fmt" + "io" + "os" + "strings" + "time" + + entschema "entgo.io/ent/dialect/sql/schema" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/migrate" + "github.com/google/uuid" + _ "modernc.org/sqlite" // pure-Go SQLite driver +) + +// migrationNamespace is the fixed UUIDv5 namespace used to deterministically +// rewrite legacy non-UUID primary keys (e.g. the internal "hub-…-signing_key" +// secret ids and "plugin-broker-…" runtime-broker ids) into the UUIDs the Ent +// schema requires. Deterministic so that a key and every foreign-key reference +// to it map to the same UUID, and so re-deriving the value is stable. +var migrationNamespace = uuid.MustParse("5c104390-a1d0-5e9a-9b1e-5c104390a1d0") + +// remapSource is a legacy table whose primary key may hold a non-UUID string. +type remapSource struct { + table string + pk string +} + +// remapSources are the legacy tables observed (and known possible) to carry +// non-UUID primary keys. Their keys are rewritten to deterministic UUIDs. +var remapSources = []remapSource{ + {table: "secrets", pk: "id"}, + {table: "runtime_brokers", pk: "id"}, +} + +// remapRefColumns maps an Ent table to the columns that reference a remappable +// id and must be rewritten with the same mapping. Includes the primary keys of +// the remapped entities themselves plus every foreign key that points at a +// runtime broker (whether typed UUID or TEXT in the Ent schema). +var remapRefColumns = map[string][]string{ + "secrets": {"id"}, + "runtime_brokers": {"id"}, + "agents": {"runtime_broker_id"}, + "broker_secrets": {"broker_id"}, + "broker_join_tokens": {"broker_id"}, + "project_contributors": {"broker_id"}, + "project_sync_state": {"broker_id"}, + "projects": {"default_runtime_broker_id"}, +} + +// uuidGenExpr is a SQLite expression that mints a syntactically valid v4-style +// UUID string (8-4-4-4-12 hex). Used to synthesize primary keys for legacy +// tables that had no `id` column (composite-keyed project_contributors / +// project_sync_state, and the restructured group_memberships / policy_bindings). +const uuidGenExpr = `lower(hex(randomblob(4))||'-'||hex(randomblob(2))||'-'||hex(randomblob(2))||'-'||hex(randomblob(2))||'-'||hex(randomblob(6)))` + +// nowExpr is a SQLite expression yielding the current UTC time in the RFC3339 +// form Ent stores. Used for required timestamp columns the legacy schema lacked +// (e.g. policy_bindings.created). +const nowExpr = `strftime('%Y-%m-%dT%H:%M:%fZ','now')` + +// AlphaTableResult records the outcome of migrating one legacy table. +type AlphaTableResult struct { + EntTable string + LegacyTable string + Source int // rows in the legacy table + Dest int // rows in the destination table after the copy +} + +// AlphaReport is the aggregate outcome of a migration α run. +type AlphaReport struct { + BackupPath string + SourcePath string + Tables []AlphaTableResult + ChildEdges int // group_child_groups edges copied + Skipped bool // true when the source was not a legacy schema (no-op) + SkipReason string // populated when Skipped is true +} + +// TotalRows returns the total number of destination rows written across all +// tables (excluding M2M child-group edges). +func (r *AlphaReport) TotalRows() int { + n := 0 + for _, t := range r.Tables { + n += t.Dest + } + return n +} + +// AlphaOptions tunes a migration α run. +type AlphaOptions struct { + // Logf, if non-nil, receives one human-readable progress line per step. + Logf func(format string, args ...any) + // BackupSuffix overrides the timestamp suffix appended to the backup file + // name. Primarily for deterministic tests; defaults to time.Now(). + BackupSuffix string +} + +func (o AlphaOptions) logf(format string, args ...any) { + if o.Logf != nil { + o.Logf(format, args...) + } +} + +// IsLegacyRawSQLSchema reports whether the SQLite file at path holds a legacy +// raw-SQL Hub schema (as opposed to the consolidated Ent schema, an empty file, +// or a non-existent file). Detection is conservative: it requires both the +// `schema_migrations` bookkeeping table — which the Ent store never creates — +// and the legacy-only `agents.agent_id` column. +func IsLegacyRawSQLSchema(path string) (bool, error) { + if _, err := os.Stat(path); err != nil { + if os.IsNotExist(err) { + return false, nil + } + return false, err + } + db, err := sql.Open("sqlite", "file:"+path+"?mode=ro") + if err != nil { + return false, fmt.Errorf("opening %s: %w", path, err) + } + defer db.Close() + + hasMigrations, err := tableExists(db, "schema_migrations") + if err != nil { + return false, err + } + if !hasMigrations { + return false, nil + } + hasAgents, err := tableExists(db, "agents") + if err != nil { + return false, err + } + if !hasAgents { + return false, nil + } + cols, err := tableColumns(db, "agents") + if err != nil { + return false, err + } + return cols["agent_id"], nil +} + +// MigrateAlphaSQLite upgrades the legacy raw-SQL Hub database at path in place to +// the consolidated Ent schema. It is a no-op (Skipped=true) when the file is not +// a legacy schema, which makes re-running on an already-migrated database safe. +// +// On success the original file has been replaced by the Ent-schema database and a +// backup of the original remains at .bak.. On any failure the +// original file is left untouched and the temporary working file is removed. +func MigrateAlphaSQLite(ctx context.Context, path string, opts AlphaOptions) (*AlphaReport, error) { + report := &AlphaReport{SourcePath: path} + + legacy, err := IsLegacyRawSQLSchema(path) + if err != nil { + return nil, fmt.Errorf("detecting schema: %w", err) + } + if !legacy { + report.Skipped = true + report.SkipReason = "not a legacy raw-SQL schema (already Ent, empty, or absent)" + opts.logf("migration α: %s", report.SkipReason) + return report, nil + } + + // 1) Back up the original (fold WAL in first so the copy is complete). + backupPath, err := backupLegacy(path, opts) + if err != nil { + return nil, fmt.Errorf("backing up legacy database: %w", err) + } + report.BackupPath = backupPath + opts.logf("migration α: backed up %s -> %s", path, backupPath) + + // 2) Build the Ent schema into a fresh temporary file. + tmpPath := path + ".migrating" + removeSQLiteFiles(tmpPath) + defer removeSQLiteFiles(tmpPath) // cleaned up unless we successfully swap it in + + if err := buildEntSchema(ctx, tmpPath); err != nil { + return nil, fmt.Errorf("creating Ent schema: %w", err) + } + opts.logf("migration α: created Ent schema in %s", tmpPath) + + // 3) Copy all data from the legacy file into the new Ent file. + if err := copyLegacyData(ctx, tmpPath, path, report, opts); err != nil { + return nil, fmt.Errorf("copying data: %w", err) + } + + // 4) Atomically replace the original with the migrated file. The original is + // already safely backed up. + removeSQLiteFiles(path) + if err := os.Rename(tmpPath, path); err != nil { + return nil, fmt.Errorf("swapping migrated database into place: %w", err) + } + opts.logf("migration α: complete — %d tables, %d rows migrated", len(report.Tables), report.TotalRows()) + return report, nil +} + +// backupLegacy checkpoints the legacy WAL and copies the database to a +// timestamped backup file, returning the backup path. +func backupLegacy(path string, opts AlphaOptions) (string, error) { + // Fold any WAL frames into the main file so a plain copy is complete. + db, err := sql.Open("sqlite", "file:"+path) + if err != nil { + return "", err + } + if _, err := db.Exec("PRAGMA wal_checkpoint(TRUNCATE)"); err != nil { + // Non-fatal: a DB in rollback-journal mode has no WAL to checkpoint. + opts.logf("migration α: wal_checkpoint skipped: %v", err) + } + db.Close() + + suffix := opts.BackupSuffix + if suffix == "" { + suffix = time.Now().UTC().Format("20060102-150405") + } + backupPath := path + ".bak." + suffix + if err := copyFile(path, backupPath); err != nil { + return "", err + } + return backupPath, nil +} + +// buildEntSchema creates the destination Ent schema in a new file at tmpPath. +func buildEntSchema(ctx context.Context, tmpPath string) error { + client, err := OpenSQLite(tmpPath, PoolConfig{}) + if err != nil { + return err + } + defer client.Close() + return AutoMigrate(ctx, client) +} + +// tableMap describes how one Ent table is populated from a legacy table via the +// generic INSERT…SELECT path. The three structurally-restructured tables +// (group_memberships, policy_bindings) are handled by bespoke SQL instead. +type tableMap struct { + entTable string + legacyTable string + // overrides maps an Ent column name to a raw SQLite select expression, + // bypassing the automatic same-name / created_at→created mapping. Used for + // the agents agent_id→slug rename and for synthesizing surrogate ids. + overrides map[string]string +} + +// genericTables lists every table copied by the column-name-driven engine, in +// parent-before-child order (cosmetic only — foreign keys are off during load). +var genericTables = []tableMap{ + {entTable: "users", legacyTable: "users"}, + {entTable: "projects", legacyTable: "projects"}, + {entTable: "runtime_brokers", legacyTable: "runtime_brokers"}, + {entTable: "agents", legacyTable: "agents", overrides: map[string]string{"slug": `"agent_id"`}}, + {entTable: "groups", legacyTable: "groups"}, + {entTable: "access_policies", legacyTable: "policies"}, + {entTable: "templates", legacyTable: "templates"}, + {entTable: "harness_configs", legacyTable: "harness_configs"}, + {entTable: "secrets", legacyTable: "secrets"}, + {entTable: "env_vars", legacyTable: "env_vars"}, + {entTable: "project_contributors", legacyTable: "project_contributors", overrides: map[string]string{"id": uuidGenExpr}}, + {entTable: "project_sync_state", legacyTable: "project_sync_state", overrides: map[string]string{"id": uuidGenExpr}}, + {entTable: "broker_secrets", legacyTable: "broker_secrets"}, + {entTable: "broker_join_tokens", legacyTable: "broker_join_tokens"}, + {entTable: "notification_subscriptions", legacyTable: "notification_subscriptions"}, + {entTable: "notifications", legacyTable: "notifications"}, + {entTable: "subscription_templates", legacyTable: "subscription_templates"}, + {entTable: "scheduled_events", legacyTable: "scheduled_events"}, + {entTable: "schedules", legacyTable: "schedules"}, + {entTable: "messages", legacyTable: "messages"}, + {entTable: "gcp_service_accounts", legacyTable: "gcp_service_accounts"}, + {entTable: "github_installations", legacyTable: "github_installations"}, + {entTable: "maintenance_operations", legacyTable: "maintenance_operations"}, + {entTable: "maintenance_operation_runs", legacyTable: "maintenance_operation_runs"}, + {entTable: "api_keys", legacyTable: "api_keys"}, + {entTable: "user_access_tokens", legacyTable: "user_access_tokens"}, + {entTable: "allow_list", legacyTable: "allow_list"}, + {entTable: "invite_codes", legacyTable: "invite_codes"}, +} + +// copyLegacyData opens the new Ent database, attaches the legacy database, and +// copies every table with foreign keys disabled. +func copyLegacyData(ctx context.Context, dstPath, legacyPath string, report *AlphaReport, opts AlphaOptions) error { + db, err := sql.Open("sqlite", "file:"+dstPath) + if err != nil { + return err + } + defer db.Close() + // Pin to one connection so PRAGMAs and the ATTACH apply to every statement. + db.SetMaxOpenConns(1) + + if _, err := db.ExecContext(ctx, "PRAGMA foreign_keys = OFF"); err != nil { + return fmt.Errorf("disabling foreign keys: %w", err) + } + if _, err := db.ExecContext(ctx, `ATTACH DATABASE ? AS legacy`, "file:"+legacyPath+"?mode=ro"); err != nil { + return fmt.Errorf("attaching legacy database: %w", err) + } + defer db.ExecContext(ctx, "DETACH DATABASE legacy") //nolint:errcheck + + // Build the id-remap table (legacy non-UUID primary keys -> deterministic + // UUIDs) before copying, so every reference resolves consistently. The table + // is TEMP, so it never lands in the migrated file. + if err := buildIDRemap(ctx, db, opts); err != nil { + return fmt.Errorf("building id remap: %w", err) + } + + entCols := entColumnsByTable() + + for _, tm := range genericTables { + cols, ok := entCols[tm.entTable] + if !ok { + return fmt.Errorf("unknown Ent table %q (generated schema drift?)", tm.entTable) + } + res, err := copyGenericTable(ctx, db, tm, cols) + if err != nil { + return fmt.Errorf("copying %s: %w", tm.entTable, err) + } + report.Tables = append(report.Tables, res) + opts.logf("migration α: %-26s source=%d dest=%d", tm.entTable, res.Source, res.Dest) + } + + // Restructured tables: polymorphic membership and policy-binding splits. + memberships, err := copyGroupMemberships(ctx, db) + if err != nil { + return fmt.Errorf("copying group_memberships: %w", err) + } + report.Tables = append(report.Tables, memberships) + opts.logf("migration α: %-26s source=%d dest=%d", "group_memberships", memberships.Source, memberships.Dest) + + bindings, err := copyPolicyBindings(ctx, db) + if err != nil { + return fmt.Errorf("copying policy_bindings: %w", err) + } + report.Tables = append(report.Tables, bindings) + opts.logf("migration α: %-26s source=%d dest=%d", "policy_bindings", bindings.Source, bindings.Dest) + + // The Group.child_groups M2M edge, derived from legacy groups.parent_id. + edges, err := copyGroupChildEdgesSQL(ctx, db) + if err != nil { + return fmt.Errorf("copying group child edges: %w", err) + } + report.ChildEdges = edges + + return nil +} + +// copyGenericTable performs the column-name-driven INSERT…SELECT for one table +// and verifies the row counts match. +func copyGenericTable(ctx context.Context, db *sql.DB, tm tableMap, entCols []*entschema.Column) (AlphaTableResult, error) { + res := AlphaTableResult{EntTable: tm.entTable, LegacyTable: tm.legacyTable} + + legacyCols, err := attachedTableColumns(ctx, db, tm.legacyTable) + if err != nil { + return res, err + } + if len(legacyCols) == 0 { + // The legacy database does not have this table (e.g. an older schema + // version). Nothing to copy. + return res, nil + } + + var destNames, selectExprs []string + for _, c := range entCols { + if ov, ok := tm.overrides[c.Name]; ok { + destNames = append(destNames, quoteIdent(c.Name)) + selectExprs = append(selectExprs, ov) + continue + } + src := legacySourceColumn(c.Name, legacyCols) + if src == "" { + continue // no legacy source; rely on the Ent column default + } + expr := coerceExpr(src, c) + if isRemapColumn(tm.entTable, c.Name) { + expr = remapWrap(expr) + } + destNames = append(destNames, quoteIdent(c.Name)) + selectExprs = append(selectExprs, expr) + } + if len(destNames) == 0 { + return res, fmt.Errorf("no column mapping for %s", tm.entTable) + } + + stmt := fmt.Sprintf("INSERT INTO main.%s (%s) SELECT %s FROM legacy.%s", + quoteIdent(tm.entTable), strings.Join(destNames, ", "), + strings.Join(selectExprs, ", "), quoteIdent(tm.legacyTable)) + if _, err := db.ExecContext(ctx, stmt); err != nil { + return res, fmt.Errorf("insert: %w", err) + } + + res.Source, err = countRows(ctx, db, "legacy."+quoteIdent(tm.legacyTable)) + if err != nil { + return res, err + } + res.Dest, err = countRows(ctx, db, "main."+quoteIdent(tm.entTable)) + if err != nil { + return res, err + } + if res.Source != res.Dest { + return res, fmt.Errorf("row count mismatch: legacy=%d dest=%d", res.Source, res.Dest) + } + return res, nil +} + +// legacySourceColumn resolves the legacy column that feeds an Ent column, +// applying the systematic created_at→created / updated_at→updated renames. +// Returns "" when the legacy table has no corresponding column. +func legacySourceColumn(entCol string, legacyCols map[string]bool) string { + if legacyCols[entCol] { + return entCol + } + switch entCol { + case "created": + if legacyCols["created_at"] { + return "created_at" + } + case "updated": + if legacyCols["updated_at"] { + return "updated_at" + } + } + return "" +} + +// coerceExpr wraps a legacy column reference with any conversion needed for the +// destination Ent column type. SQLite's dynamic typing handles bool-as-int and +// timestamp text transparently, so only two cases need care: +// - nullable JSON columns: an empty string is not valid JSON, so map ”→NULL. +// - nullable UUID columns: legacy free-text values (or ”) that are not a +// 36-char UUID are mapped to NULL so Ent's uuid.Scan never fails on read. +func coerceExpr(legacyCol string, c *entschema.Column) string { + ref := quoteIdent(legacyCol) + switch { + case c.Type == field.TypeJSON && c.Nullable: + return "NULLIF(" + ref + ", '')" + case c.Type == field.TypeUUID && c.Nullable: + return fmt.Sprintf("CASE WHEN %s LIKE '________-____-____-____-____________' THEN %s ELSE NULL END", ref, ref) + default: + return ref + } +} + +// isRemapColumn reports whether the given Ent table/column holds a reference to +// a remappable (formerly non-UUID) id and must be rewritten via _id_remap. +func isRemapColumn(entTable, entCol string) bool { + for _, c := range remapRefColumns[entTable] { + if c == entCol { + return true + } + } + return false +} + +// remapWrap rewrites a column reference through the _id_remap table: a legacy id +// present in the map yields its deterministic UUID; anything else (already a +// UUID, or NULL) passes through unchanged. +func remapWrap(expr string) string { + return "COALESCE((SELECT new FROM _id_remap WHERE old = " + expr + "), " + expr + ")" +} + +// buildIDRemap creates and populates the TEMP _id_remap table mapping each +// legacy non-UUID primary key to a deterministic UUID. Already-UUID keys are not +// added (they pass through remapWrap unchanged). +func buildIDRemap(ctx context.Context, db *sql.DB, opts AlphaOptions) error { + if _, err := db.ExecContext(ctx, `CREATE TEMP TABLE _id_remap (old TEXT PRIMARY KEY, new TEXT NOT NULL)`); err != nil { + return err + } + total := 0 + for _, rs := range remapSources { + rows, err := db.QueryContext(ctx, fmt.Sprintf("SELECT DISTINCT %s FROM legacy.%s", quoteIdent(rs.pk), quoteIdent(rs.table))) + if err != nil { + return err + } + var legacyIDs []string + for rows.Next() { + var id sql.NullString + if err := rows.Scan(&id); err != nil { + rows.Close() + return err + } + if !id.Valid || id.String == "" { + continue + } + if _, err := uuid.Parse(id.String); err == nil { + continue // already a valid UUID + } + legacyIDs = append(legacyIDs, id.String) + } + if err := rows.Err(); err != nil { + rows.Close() + return err + } + rows.Close() + for _, old := range legacyIDs { + newID := uuid.NewSHA1(migrationNamespace, []byte(old)).String() + if _, err := db.ExecContext(ctx, `INSERT OR IGNORE INTO _id_remap (old, new) VALUES (?, ?)`, old, newID); err != nil { + return err + } + total++ + opts.logf("migration α: remap id %-40s -> %s (%s)", old, newID, rs.table) + } + } + if total > 0 { + opts.logf("migration α: rewrote %d non-UUID id(s) to deterministic UUIDs", total) + } + return nil +} + +// copyGroupMemberships migrates the legacy `group_members` table (composite key +// group_id/member_type/member_id) into Ent `group_memberships`, splitting the +// polymorphic member into user_id / agent_id and minting a surrogate id. +func copyGroupMemberships(ctx context.Context, db *sql.DB) (AlphaTableResult, error) { + res := AlphaTableResult{EntTable: "group_memberships", LegacyTable: "group_members"} + if ok, err := attachedTableExists(ctx, db, "group_members"); err != nil || !ok { + return res, err + } + stmt := fmt.Sprintf(`INSERT INTO main.group_memberships (id, group_id, user_id, agent_id, role, added_at, added_by) + SELECT %s, group_id, + CASE WHEN member_type='user' THEN member_id END, + CASE WHEN member_type='agent' THEN member_id END, + role, added_at, added_by + FROM legacy.group_members`, uuidGenExpr) + if _, err := db.ExecContext(ctx, stmt); err != nil { + return res, err + } + return countSourceDest(ctx, db, res, "legacy.group_members", "main.group_memberships") +} + +// copyPolicyBindings migrates the legacy composite-keyed `policy_bindings` into +// the Ent table, splitting the polymorphic principal into user_id / group_id / +// agent_id, minting a surrogate id, and stamping a `created` time the legacy +// schema did not record. +func copyPolicyBindings(ctx context.Context, db *sql.DB) (AlphaTableResult, error) { + res := AlphaTableResult{EntTable: "policy_bindings", LegacyTable: "policy_bindings"} + if ok, err := attachedTableExists(ctx, db, "policy_bindings"); err != nil || !ok { + return res, err + } + stmt := fmt.Sprintf(`INSERT INTO main.policy_bindings (id, policy_id, principal_type, user_id, group_id, agent_id, created) + SELECT %s, policy_id, principal_type, + CASE WHEN principal_type='user' THEN principal_id END, + CASE WHEN principal_type='group' THEN principal_id END, + CASE WHEN principal_type='agent' THEN principal_id END, + %s + FROM legacy.policy_bindings`, uuidGenExpr, nowExpr) + if _, err := db.ExecContext(ctx, stmt); err != nil { + return res, err + } + return countSourceDest(ctx, db, res, "legacy.policy_bindings", "main.policy_bindings") +} + +// copyGroupChildEdgesSQL populates the group_child_groups M2M join table from +// legacy groups that carried a parent_id. Idempotent within a fresh run. +func copyGroupChildEdgesSQL(ctx context.Context, db *sql.DB) (int, error) { + cols, err := attachedTableColumns(ctx, db, "groups") + if err != nil { + return 0, err + } + if !cols["parent_id"] { + // No legacy groups table, or a schema without parent hierarchy. + return 0, nil + } + // Only legacy groups with a non-empty parent_id contribute an edge. + stmt := `INSERT INTO main.group_child_groups (group_id, parent_group_id) + SELECT parent_id, id FROM legacy.groups + WHERE parent_id IS NOT NULL AND parent_id <> ''` + r, err := db.ExecContext(ctx, stmt) + if err != nil { + return 0, err + } + n, _ := r.RowsAffected() + return int(n), nil +} + +// --- small helpers --- + +func countSourceDest(ctx context.Context, db *sql.DB, res AlphaTableResult, srcQ, dstQ string) (AlphaTableResult, error) { + var err error + if res.Source, err = countRows(ctx, db, srcQ); err != nil { + return res, err + } + if res.Dest, err = countRows(ctx, db, dstQ); err != nil { + return res, err + } + if res.Source != res.Dest { + return res, fmt.Errorf("row count mismatch: source=%d dest=%d", res.Source, res.Dest) + } + return res, nil +} + +func countRows(ctx context.Context, db *sql.DB, qualifiedTable string) (int, error) { + var n int + err := db.QueryRowContext(ctx, "SELECT count(*) FROM "+qualifiedTable).Scan(&n) + return n, err +} + +// entColumnsByTable indexes the generated Ent schema columns by table name. +func entColumnsByTable() map[string][]*entschema.Column { + out := make(map[string][]*entschema.Column, len(migrate.Tables)) + for _, t := range migrate.Tables { + out[t.Name] = t.Columns + } + return out +} + +func tableExists(db *sql.DB, name string) (bool, error) { + var n int + err := db.QueryRow(`SELECT count(*) FROM sqlite_master WHERE type='table' AND name=?`, name).Scan(&n) + return n > 0, err +} + +func tableColumns(db *sql.DB, table string) (map[string]bool, error) { + rows, err := db.Query(fmt.Sprintf("PRAGMA table_info(%s)", quoteIdent(table))) + if err != nil { + return nil, err + } + defer rows.Close() + cols := map[string]bool{} + for rows.Next() { + var cid, notnull, pk int + var name, ctype string + var dflt sql.NullString + if err := rows.Scan(&cid, &name, &ctype, ¬null, &dflt, &pk); err != nil { + return nil, err + } + cols[name] = true + } + return cols, rows.Err() +} + +// attachedTableExists reports whether a table exists in the ATTACHed legacy DB. +func attachedTableExists(ctx context.Context, db *sql.DB, name string) (bool, error) { + var n int + err := db.QueryRowContext(ctx, `SELECT count(*) FROM legacy.sqlite_master WHERE type='table' AND name=?`, name).Scan(&n) + return n > 0, err +} + +// attachedTableColumns reads the columns of a table in the ATTACHed legacy DB. +func attachedTableColumns(ctx context.Context, db *sql.DB, table string) (map[string]bool, error) { + rows, err := db.QueryContext(ctx, fmt.Sprintf("PRAGMA legacy.table_info(%s)", quoteIdent(table))) + if err != nil { + return nil, err + } + defer rows.Close() + cols := map[string]bool{} + for rows.Next() { + var cid, notnull, pk int + var name, ctype string + var dflt sql.NullString + if err := rows.Scan(&cid, &name, &ctype, ¬null, &dflt, &pk); err != nil { + return nil, err + } + cols[name] = true + } + return cols, rows.Err() +} + +// quoteIdent double-quotes a SQLite identifier. +func quoteIdent(s string) string { + return `"` + strings.ReplaceAll(s, `"`, `""`) + `"` +} + +func copyFile(src, dst string) error { + in, err := os.Open(src) + if err != nil { + return err + } + defer in.Close() + out, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o600) + if err != nil { + return err + } + if _, err := io.Copy(out, in); err != nil { + out.Close() + return err + } + return out.Close() +} + +// removeSQLiteFiles removes a SQLite database file and its WAL/SHM sidecars. +func removeSQLiteFiles(path string) { + for _, suffix := range []string{"", "-wal", "-shm"} { + _ = os.Remove(path + suffix) + } +} diff --git a/pkg/ent/entc/migrate_alpha_nosqlite.go b/pkg/ent/entc/migrate_alpha_nosqlite.go new file mode 100644 index 000000000..9c710894d --- /dev/null +++ b/pkg/ent/entc/migrate_alpha_nosqlite.go @@ -0,0 +1,60 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build no_sqlite + +package entc + +import "context" + +// AlphaTableResult records the outcome of migrating one legacy table. +type AlphaTableResult struct { + EntTable string + LegacyTable string + Source int + Dest int +} + +// AlphaReport is the aggregate outcome of a migration α run. +type AlphaReport struct { + BackupPath string + SourcePath string + Tables []AlphaTableResult + ChildEdges int + Skipped bool + SkipReason string +} + +// TotalRows returns the total number of destination rows written. +func (r *AlphaReport) TotalRows() int { + n := 0 + for _, t := range r.Tables { + n += t.Dest + } + return n +} + +// AlphaOptions tunes a migration α run. +type AlphaOptions struct { + Logf func(format string, args ...any) + BackupSuffix string +} + +// IsLegacyRawSQLSchema always reports false when built without SQLite support. +func IsLegacyRawSQLSchema(_ string) (bool, error) { return false, nil } + +// MigrateAlphaSQLite is a no-op when built without SQLite support. +func MigrateAlphaSQLite(_ context.Context, path string, _ AlphaOptions) (*AlphaReport, error) { + return &AlphaReport{SourcePath: path, Skipped: true, SkipReason: "built without sqlite support"}, nil +} diff --git a/pkg/ent/entc/migrate_alpha_test.go b/pkg/ent/entc/migrate_alpha_test.go new file mode 100644 index 000000000..cd085b2ea --- /dev/null +++ b/pkg/ent/entc/migrate_alpha_test.go @@ -0,0 +1,296 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build !no_sqlite + +package entc + +import ( + "context" + "database/sql" + "os" + "path/filepath" + "testing" + + "github.com/GoogleCloudPlatform/scion/pkg/ent" + "github.com/google/uuid" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// writeLegacyDB creates a representative legacy raw-SQL hub.db at path. It +// intentionally exercises every mechanical difference migration α handles: +// the schema_migrations sentinel, created_at/updated_at and agent_id renames, +// the policies/group_members table renames, composite-keyed tables, the +// polymorphic member/principal split, bool-as-int, JSON-as-TEXT, BLOBs, a +// dangling agents.created_by reference, and non-UUID primary keys for both +// secrets and runtime_brokers (with a foreign-key reference to the latter). +func writeLegacyDB(t *testing.T, path string) legacyFixture { + t.Helper() + db, err := sql.Open("sqlite", "file:"+path) + require.NoError(t, err) + defer db.Close() + + ddl := []string{ + `CREATE TABLE schema_migrations (version INTEGER PRIMARY KEY, applied_at TIMESTAMP)`, + `CREATE TABLE users (id TEXT PRIMARY KEY, email TEXT, display_name TEXT, role TEXT, status TEXT, preferences TEXT, created_at TIMESTAMP, last_login TIMESTAMP, last_seen TIMESTAMP)`, + `CREATE TABLE projects (id TEXT PRIMARY KEY, name TEXT, slug TEXT, labels TEXT, annotations TEXT, visibility TEXT, created_at TIMESTAMP, updated_at TIMESTAMP, created_by TEXT, owner_id TEXT, default_runtime_broker_id TEXT)`, + `CREATE TABLE runtime_brokers (id TEXT PRIMARY KEY, name TEXT, slug TEXT, type TEXT, mode TEXT, status TEXT, connection_state TEXT, auto_provide INTEGER, capabilities TEXT, created_at TIMESTAMP, updated_at TIMESTAMP)`, + `CREATE TABLE agents (id TEXT PRIMARY KEY, agent_id TEXT, name TEXT, project_id TEXT, labels TEXT, detached INTEGER, web_pty_enabled INTEGER, visibility TEXT, runtime_broker_id TEXT, created_by TEXT, owner_id TEXT, created_at TIMESTAMP, updated_at TIMESTAMP)`, + `CREATE TABLE broker_secrets (broker_id TEXT PRIMARY KEY, secret_key BLOB, algorithm TEXT, status TEXT, created_at TIMESTAMP)`, + `CREATE TABLE secrets (id TEXT PRIMARY KEY, key TEXT, encrypted_value TEXT, scope TEXT, scope_id TEXT, version INTEGER, secret_type TEXT, injection_mode TEXT, allow_progeny INTEGER, created_at TIMESTAMP, updated_at TIMESTAMP)`, + `CREATE TABLE groups (id TEXT PRIMARY KEY, name TEXT, slug TEXT, group_type TEXT, parent_id TEXT, created_at TIMESTAMP, updated_at TIMESTAMP)`, + `CREATE TABLE group_members (group_id TEXT, member_type TEXT, member_id TEXT, role TEXT, added_at TIMESTAMP, added_by TEXT, PRIMARY KEY (group_id, member_type, member_id))`, + `CREATE TABLE policies (id TEXT PRIMARY KEY, name TEXT, scope_type TEXT, resource_type TEXT, effect TEXT, priority INTEGER, created_at TIMESTAMP, updated_at TIMESTAMP)`, + `CREATE TABLE policy_bindings (policy_id TEXT, principal_type TEXT, principal_id TEXT, PRIMARY KEY (policy_id, principal_type, principal_id))`, + `CREATE TABLE allow_list (id TEXT PRIMARY KEY, email TEXT, note TEXT, added_by TEXT, created DATETIME)`, + `CREATE TABLE messages (id TEXT PRIMARY KEY, project_id TEXT, sender TEXT, recipient TEXT, msg TEXT, type TEXT, urgent INTEGER, read INTEGER, created_at TIMESTAMP)`, + } + for _, s := range ddl { + _, err := db.Exec(s) + require.NoError(t, err, s) + } + + const ts = "2026-05-01 12:30:00" + fx := legacyFixture{ + userID: uuid.NewString(), + projectID: uuid.NewString(), + agentID: uuid.NewString(), + groupID: uuid.NewString(), + policyID: uuid.NewString(), + // Non-UUID ids that must be remapped to deterministic UUIDs. + brokerLegacyID: "plugin-broker-telegram", + secretLegacyID: "hub-abc123-agent_signing_key", + // A created_by that references no user row (dangling, must survive). + danglingPrincipal: uuid.NewString(), + } + + exec := func(q string, args ...any) { + _, err := db.Exec(q, args...) + require.NoError(t, err, q) + } + + exec(`INSERT INTO users (id,email,display_name,role,status,preferences,created_at) VALUES (?,?,?,?,?,?,?)`, + fx.userID, "a@example.com", "Alice", "admin", "active", `{"theme":"dark"}`, ts) + exec(`INSERT INTO projects (id,name,slug,labels,annotations,visibility,created_at,updated_at,owner_id,default_runtime_broker_id) VALUES (?,?,?,?,?,?,?,?,?,?)`, + fx.projectID, "Proj", "proj", `{"team":"x"}`, "", "private", ts, ts, fx.userID, fx.brokerLegacyID) + exec(`INSERT INTO runtime_brokers (id,name,slug,type,mode,status,connection_state,auto_provide,capabilities,created_at,updated_at) VALUES (?,?,?,?,?,?,?,?,?,?,?)`, + fx.brokerLegacyID, "telegram", "telegram", "plugin", "connected", "online", "connected", 1, `{"x":true}`, ts, ts) + // Agent with agent_id (->slug), int bools, JSON labels, dangling created_by, + // and a runtime_broker_id pointing at the non-UUID broker. + exec(`INSERT INTO agents (id,agent_id,name,project_id,labels,detached,web_pty_enabled,visibility,runtime_broker_id,created_by,owner_id,created_at,updated_at) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)`, + fx.agentID, "deploy-bot", "Deploy Bot", fx.projectID, `{"k":"v"}`, 1, 0, "private", fx.brokerLegacyID, fx.danglingPrincipal, fx.danglingPrincipal, ts, ts) + exec(`INSERT INTO broker_secrets (broker_id,secret_key,algorithm,status,created_at) VALUES (?,?,?,?,?)`, + fx.brokerLegacyID, []byte{0x01, 0x02, 0x03, 0xff}, "hmac-sha256", "active", ts) + exec(`INSERT INTO secrets (id,key,encrypted_value,scope,scope_id,version,secret_type,injection_mode,allow_progeny,created_at,updated_at) VALUES (?,?,?,?,?,?,?,?,?,?,?)`, + fx.secretLegacyID, "agent_signing_key", "ZW5j", "hub", "abc123", 1, "internal", "as_needed", 0, ts, ts) + exec(`INSERT INTO groups (id,name,slug,group_type,created_at,updated_at) VALUES (?,?,?,?,?,?)`, + fx.groupID, "G", "g", "custom", ts, ts) + exec(`INSERT INTO group_members (group_id,member_type,member_id,role,added_at,added_by) VALUES (?,?,?,?,?,?)`, + fx.groupID, "user", fx.userID, "member", ts, fx.userID) + exec(`INSERT INTO policies (id,name,scope_type,resource_type,effect,priority,created_at,updated_at) VALUES (?,?,?,?,?,?,?,?)`, + fx.policyID, "P", "hub", "project", "allow", 0, ts, ts) + exec(`INSERT INTO policy_bindings (policy_id,principal_type,principal_id) VALUES (?,?,?)`, + fx.policyID, "user", fx.userID) + exec(`INSERT INTO allow_list (id,email,note,added_by,created) VALUES (?,?,?,?,?)`, + uuid.NewString(), "b@example.com", "", fx.userID, ts) + exec(`INSERT INTO messages (id,project_id,sender,recipient,msg,type,urgent,read,created_at) VALUES (?,?,?,?,?,?,?,?,?)`, + uuid.NewString(), fx.projectID, "alice", "deploy-bot", "hi", "instruction", 0, 1, ts) + // Empty-string JSON / nullable cases on a second project (must not break read-back). + exec(`INSERT INTO projects (id,name,slug,labels,annotations,visibility,created_at,updated_at) VALUES (?,?,?,?,?,?,?,?)`, + uuid.NewString(), "Proj2", "proj2", "", "", "private", ts, ts) + + return fx +} + +type legacyFixture struct { + userID, projectID, agentID, groupID, policyID string + brokerLegacyID, secretLegacyID string + danglingPrincipal string +} + +// openMigrated opens the migrated file through the Ent client. +func openMigrated(t *testing.T, path string) *ent.Client { + t.Helper() + client, err := OpenSQLite("file:"+path+"?cache=shared", PoolConfig{MaxOpenConns: 1}) + require.NoError(t, err) + t.Cleanup(func() { client.Close() }) + return client +} + +func TestIsLegacyRawSQLSchema(t *testing.T) { + dir := t.TempDir() + + // Non-existent file. + ok, err := IsLegacyRawSQLSchema(filepath.Join(dir, "nope.db")) + require.NoError(t, err) + assert.False(t, ok) + + // Legacy file. + legacyPath := filepath.Join(dir, "legacy.db") + writeLegacyDB(t, legacyPath) + ok, err = IsLegacyRawSQLSchema(legacyPath) + require.NoError(t, err) + assert.True(t, ok, "legacy raw-SQL schema should be detected") + + // Fresh Ent file. + entPath := filepath.Join(dir, "ent.db") + require.NoError(t, buildEntSchema(context.Background(), entPath)) + ok, err = IsLegacyRawSQLSchema(entPath) + require.NoError(t, err) + assert.False(t, ok, "Ent schema must not be flagged as legacy") +} + +func TestMigrateAlphaSQLite_EndToEnd(t *testing.T) { + ctx := context.Background() + dir := t.TempDir() + path := filepath.Join(dir, "hub.db") + fx := writeLegacyDB(t, path) + + report, err := MigrateAlphaSQLite(ctx, path, AlphaOptions{BackupSuffix: "unit"}) + require.NoError(t, err) + require.False(t, report.Skipped) + + // Backup exists and is itself the legacy schema. + assert.Equal(t, path+".bak.unit", report.BackupPath) + bakLegacy, err := IsLegacyRawSQLSchema(report.BackupPath) + require.NoError(t, err) + assert.True(t, bakLegacy, "backup should retain the legacy schema") + + // The migrated file is no longer legacy. + nowLegacy, err := IsLegacyRawSQLSchema(path) + require.NoError(t, err) + assert.False(t, nowLegacy) + + // Every table reported equal source/dest counts. + for _, tr := range report.Tables { + assert.Equalf(t, tr.Source, tr.Dest, "row count mismatch for %s", tr.EntTable) + } + + // Read everything back through the Ent client. + client := openMigrated(t, path) + + users, err := client.User.Query().All(ctx) + require.NoError(t, err) + require.Len(t, users, 1) + assert.Equal(t, "a@example.com", users[0].Email) + require.NotNil(t, users[0].Preferences) + assert.Equal(t, "dark", users[0].Preferences.Theme, "typed JSON preferences must deserialize") + + projects, err := client.Project.Query().All(ctx) + require.NoError(t, err) + assert.Len(t, projects, 2) + + // Agent: agent_id -> slug, int bools -> real bools, JSON labels. + agents, err := client.Agent.Query().All(ctx) + require.NoError(t, err) + require.Len(t, agents, 1) + assert.Equal(t, "deploy-bot", agents[0].Slug) + assert.True(t, agents[0].Detached) + assert.False(t, agents[0].WebPtyEnabled) + assert.Equal(t, map[string]string{"k": "v"}, agents[0].Labels) + + // Non-UUID broker id remapped deterministically, and the broker_secret + + // agent + project references resolve to the SAME new id. + wantBroker := uuid.NewSHA1(migrationNamespace, []byte(fx.brokerLegacyID)) + brokers, err := client.RuntimeBroker.Query().All(ctx) + require.NoError(t, err) + require.Len(t, brokers, 1) + assert.Equal(t, wantBroker, brokers[0].ID) + assert.True(t, brokers[0].AutoProvide) + + bs, err := client.BrokerSecret.Query().All(ctx) + require.NoError(t, err) + require.Len(t, bs, 1) + assert.Equal(t, wantBroker, bs[0].ID, "broker_secret PK (broker_id) must follow the remap") + assert.Equal(t, []byte{0x01, 0x02, 0x03, 0xff}, bs[0].SecretKey, "BLOB must survive") + assert.Equal(t, wantBroker.String(), agents[0].RuntimeBrokerID, "agent broker ref must follow the remap") + var brokerProj *ent.Project + for _, p := range projects { + if p.DefaultRuntimeBrokerID != nil && *p.DefaultRuntimeBrokerID != "" { + brokerProj = p + } + } + require.NotNil(t, brokerProj, "expected a project with a default runtime broker") + assert.Equal(t, wantBroker.String(), *brokerProj.DefaultRuntimeBrokerID, "project broker ref must follow the remap") + + // Non-UUID secret id remapped (no inbound FK, just the PK). + wantSecret := uuid.NewSHA1(migrationNamespace, []byte(fx.secretLegacyID)) + secrets, err := client.Secret.Query().All(ctx) + require.NoError(t, err) + require.Len(t, secrets, 1) + assert.Equal(t, wantSecret, secrets[0].ID) + assert.Equal(t, "agent_signing_key", secrets[0].Key) + + // Restructured tables: polymorphic split. + gms, err := client.GroupMembership.Query().All(ctx) + require.NoError(t, err) + require.Len(t, gms, 1) + require.NotNil(t, gms[0].UserID) + assert.Equal(t, fx.userID, gms[0].UserID.String()) + + pbs, err := client.PolicyBinding.Query().All(ctx) + require.NoError(t, err) + require.Len(t, pbs, 1) + require.NotNil(t, pbs[0].UserID) + assert.Equal(t, fx.userID, pbs[0].UserID.String()) + assert.False(t, pbs[0].Created.IsZero(), "policy binding created must be stamped") + + // Dangling created_by survives as a (parseable) UUID even though no user row exists. + require.NotNil(t, agents[0].CreatedBy) + assert.Equal(t, fx.danglingPrincipal, agents[0].CreatedBy.String()) +} + +func TestMigrateAlphaSQLite_Idempotent(t *testing.T) { + ctx := context.Background() + dir := t.TempDir() + path := filepath.Join(dir, "hub.db") + writeLegacyDB(t, path) + + r1, err := MigrateAlphaSQLite(ctx, path, AlphaOptions{BackupSuffix: "one"}) + require.NoError(t, err) + require.False(t, r1.Skipped) + + // Second run is a no-op: already migrated. + r2, err := MigrateAlphaSQLite(ctx, path, AlphaOptions{BackupSuffix: "two"}) + require.NoError(t, err) + assert.True(t, r2.Skipped) + + // No duplicate backup from the skipped run. + _, err = os.Stat(path + ".bak.two") + assert.True(t, os.IsNotExist(err), "skipped run must not create a backup") + + // Data is unchanged: still exactly one agent. + client := openMigrated(t, path) + n, err := client.Agent.Query().Count(ctx) + require.NoError(t, err) + assert.Equal(t, 1, n) +} + +func TestMigrateAlphaSQLite_SkipsNonLegacy(t *testing.T) { + ctx := context.Background() + dir := t.TempDir() + + // A fresh Ent file. + entPath := filepath.Join(dir, "ent.db") + require.NoError(t, buildEntSchema(ctx, entPath)) + r, err := MigrateAlphaSQLite(ctx, entPath, AlphaOptions{}) + require.NoError(t, err) + assert.True(t, r.Skipped) + + // A missing file. + r, err = MigrateAlphaSQLite(ctx, filepath.Join(dir, "absent.db"), AlphaOptions{}) + require.NoError(t, err) + assert.True(t, r.Skipped) +} diff --git a/pkg/ent/entc/migrate_beta.go b/pkg/ent/entc/migrate_beta.go new file mode 100644 index 000000000..6a62017b3 --- /dev/null +++ b/pkg/ent/entc/migrate_beta.go @@ -0,0 +1,402 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package entc + +import ( + "context" + "fmt" + "reflect" + + "github.com/GoogleCloudPlatform/scion/pkg/ent" +) + +// migrationEntities lists every Ent node by its field name on the generated +// *ent.Client, ordered so that an entity always appears after the entities its +// foreign keys reference. Ent models its M2O/O2M relationships as plain FK +// columns on the child node, so copying nodes in this order satisfies every +// constraint at insert time. +// +// The first seven entities have FK edges: +// - Group -> User (owner) +// - Agent -> Project (required), User (creator, owner) +// - GroupMembership -> Group (required), User, Agent +// - PolicyBinding -> AccessPolicy, User, Group, Agent +// +// The remainder declare no Ent edges (no DB-level FK constraints), so their +// relative order is irrelevant; they are listed alphabetically for readability. +var migrationEntities = []string{ + // FK-ordered core. + "User", + "Project", + "AccessPolicy", + "Group", + "Agent", + "GroupMembership", + "PolicyBinding", + // Independent entities (no Ent edges). + "AllowListEntry", + "ApiKey", + "BrokerJoinToken", + "BrokerSecret", + "EnvVar", + "GCPServiceAccount", + "GithubInstallation", + "HarnessConfig", + "InviteCode", + "MaintenanceOperation", + "MaintenanceOperationRun", + "Message", + "Notification", + "NotificationSubscription", + "ProjectContributor", + "ProjectSyncState", + "RuntimeBroker", + "Schedule", + "ScheduledEvent", + "Secret", + "SubscriptionTemplate", + "Template", + "UserAccessToken", +} + +// defaultBatchSize bounds how many rows a single CreateBulk statement inserts. +// Postgres caps a statement at 65535 bind parameters; the widest entity (Agent) +// has ~36 columns, so 500 rows stays comfortably under the limit while keeping +// the number of round-trips low. +const defaultBatchSize = 500 + +// MigrateOptions tunes a MigrateData run. +type MigrateOptions struct { + // BatchSize is the maximum number of rows per CreateBulk statement. + // Defaults to defaultBatchSize when <= 0. + BatchSize int + // Logf, if non-nil, receives one human-readable progress line per entity. + Logf func(format string, args ...any) +} + +// EntityResult records the outcome of migrating a single entity. +type EntityResult struct { + Entity string + Source int // rows present in the source + Inserted int // rows newly written to the destination this run + Skipped int // rows already present in the destination (idempotent skips) + Dest int // rows in the destination after migration +} + +// MigrateReport is the aggregate outcome of a migration run. +type MigrateReport struct { + Entities []EntityResult + ChildGroupEdgs int // group->child_group M2M edges copied +} + +// MigrateData copies all data from src into dst, entity by entity, in foreign +// key dependency order. The destination schema must already exist (call +// AutoMigrate first). +// +// Properties: +// - Idempotent: rows whose primary key already exists in dst are skipped, so +// a partially completed run can be safely restarted. +// - Atomic per entity: each entity's inserts run inside a single transaction. +// - Verified: after each entity the source and destination row counts are +// compared and a mismatch aborts the migration. +// +// MigrateData never writes to src. +func MigrateData(ctx context.Context, src, dst *ent.Client, opts MigrateOptions) (*MigrateReport, error) { + batchSize := opts.BatchSize + if batchSize <= 0 { + batchSize = defaultBatchSize + } + logf := opts.Logf + if logf == nil { + logf = func(string, ...any) {} + } + + report := &MigrateReport{} + srcStruct := reflect.ValueOf(src).Elem() + dstStruct := reflect.ValueOf(dst).Elem() + + for _, name := range migrationEntities { + srcClient := srcStruct.FieldByName(name) + dstClient := dstStruct.FieldByName(name) + if !srcClient.IsValid() || !dstClient.IsValid() { + return report, fmt.Errorf("entity %q not found on ent.Client (generated code drift?)", name) + } + + res, err := migrateEntity(ctx, name, srcClient, dst, dstClient, batchSize) + if err != nil { + return report, fmt.Errorf("migrating %s: %w", name, err) + } + report.Entities = append(report.Entities, res) + logf("migrated %-26s source=%d inserted=%d skipped=%d dest=%d", + res.Entity, res.Source, res.Inserted, res.Skipped, res.Dest) + } + + // Copy the one many-to-many edge (Group.child_groups) that lives in a join + // table rather than as an FK column on a node. + edges, err := copyGroupChildEdges(ctx, src, dst) + if err != nil { + return report, fmt.Errorf("copying group child edges: %w", err) + } + report.ChildGroupEdgs = edges + logf("migrated %-26s edges=%d", "group_child_groups", edges) + + return report, nil +} + +// migrateEntity copies a single entity. Reads happen against srcClient (a +// *XClient on the source); writes happen inside a transaction on dst so the +// entity is migrated atomically even when split across multiple CreateBulk +// batches. +func migrateEntity(ctx context.Context, name string, srcClient reflect.Value, dst *ent.Client, dstClient reflect.Value, batchSize int) (EntityResult, error) { + res := EntityResult{Entity: name} + + rows, err := queryAll(ctx, srcClient) + if err != nil { + return res, fmt.Errorf("querying source: %w", err) + } + res.Source = len(rows) + + existing, err := queryIDSet(ctx, dstClient) + if err != nil { + return res, fmt.Errorf("reading destination ids: %w", err) + } + + tx, err := dst.Tx(ctx) + if err != nil { + return res, fmt.Errorf("starting transaction: %w", err) + } + txClient := reflect.ValueOf(tx).Elem().FieldByName(name) + + batch := make([]reflect.Value, 0, batchSize) + flush := func() error { + if len(batch) == 0 { + return nil + } + n, err := createBulk(ctx, txClient, batch) + if err != nil { + return err + } + res.Inserted += n + batch = batch[:0] + return nil + } + + for _, row := range rows { + id := row.Elem().FieldByName("ID").Interface() + if _, ok := existing[id]; ok { + res.Skipped++ + continue + } + builder := txClient.MethodByName("Create").Call(nil)[0] + if err := applyFields(builder, row.Elem()); err != nil { + _ = tx.Rollback() + return res, fmt.Errorf("mapping fields for id %v: %w", id, err) + } + batch = append(batch, builder) + if len(batch) >= batchSize { + if err := flush(); err != nil { + _ = tx.Rollback() + return res, fmt.Errorf("bulk insert: %w", err) + } + } + } + if err := flush(); err != nil { + _ = tx.Rollback() + return res, fmt.Errorf("bulk insert: %w", err) + } + if err := tx.Commit(); err != nil { + return res, fmt.Errorf("committing: %w", err) + } + + // Verify: the destination must now hold exactly as many rows as the source. + dstCount, err := queryCount(ctx, dstClient) + if err != nil { + return res, fmt.Errorf("counting destination: %w", err) + } + res.Dest = dstCount + if dstCount != res.Source { + return res, fmt.Errorf("row count mismatch: source=%d dest=%d", res.Source, dstCount) + } + return res, nil +} + +// applyFields copies every persisted field from a source entity struct (rowElem, +// the dereferenced *X) onto a create builder by calling the matching generated +// setter via reflection. Pointer fields use SetNillable when available +// (scalar optionals) and fall back to Set (JSON pointer fields whose +// setter already takes a pointer). The relationship FK columns (e.g. ProjectID, +// OwnerID) are ordinary fields and are copied the same way, preserving edges. +func applyFields(builder, rowElem reflect.Value) error { + t := rowElem.Type() + for i := 0; i < t.NumField(); i++ { + f := t.Field(i) + switch f.Name { + case "config", "Edges", "selectValues": + continue + } + if f.PkgPath != "" { // unexported field + continue + } + fv := rowElem.Field(i) + + var candidates []string + if f.Type.Kind() == reflect.Ptr { + candidates = []string{"SetNillable" + f.Name, "Set" + f.Name} + } else { + candidates = []string{"Set" + f.Name} + } + + applied := false + for _, mName := range candidates { + m := builder.MethodByName(mName) + if !m.IsValid() { + continue + } + mt := m.Type() + if mt.NumIn() != 1 || mt.IsVariadic() { + continue + } + if !fv.Type().AssignableTo(mt.In(0)) { + continue + } + m.Call([]reflect.Value{fv}) + applied = true + break + } + if !applied && f.Name == "ID" { + // Every Ent entity in this schema uses a settable UUID primary key. + // A missing SetID would silently re-generate IDs and break FKs, so + // fail loud instead. + return fmt.Errorf("no SetID setter found for %s", t.Name()) + } + } + return nil +} + +// queryAll returns every row of an entity as a slice of *X reflect.Values via +// client.Query().All(ctx). +func queryAll(ctx context.Context, client reflect.Value) ([]reflect.Value, error) { + query := client.MethodByName("Query").Call(nil)[0] + out := query.MethodByName("All").Call([]reflect.Value{reflect.ValueOf(ctx)}) + if err := asError(out[1]); err != nil { + return nil, err + } + slice := out[0] + rows := make([]reflect.Value, slice.Len()) + for i := 0; i < slice.Len(); i++ { + rows[i] = slice.Index(i) + } + return rows, nil +} + +// queryIDSet returns the set of primary keys present for an entity via +// client.Query().IDs(ctx). Ent ID types (uuid.UUID, string, int) are all +// comparable and usable as map keys. +func queryIDSet(ctx context.Context, client reflect.Value) (map[any]struct{}, error) { + query := client.MethodByName("Query").Call(nil)[0] + out := query.MethodByName("IDs").Call([]reflect.Value{reflect.ValueOf(ctx)}) + if err := asError(out[1]); err != nil { + return nil, err + } + ids := out[0] + set := make(map[any]struct{}, ids.Len()) + for i := 0; i < ids.Len(); i++ { + set[ids.Index(i).Interface()] = struct{}{} + } + return set, nil +} + +// queryCount returns the row count for an entity via client.Query().Count(ctx). +func queryCount(ctx context.Context, client reflect.Value) (int, error) { + query := client.MethodByName("Query").Call(nil)[0] + out := query.MethodByName("Count").Call([]reflect.Value{reflect.ValueOf(ctx)}) + if err := asError(out[1]); err != nil { + return 0, err + } + return int(out[0].Int()), nil +} + +// createBulk runs client.CreateBulk(builders...).Save(ctx) and returns the +// number of rows written. +func createBulk(ctx context.Context, client reflect.Value, builders []reflect.Value) (int, error) { + if len(builders) == 0 { + return 0, nil + } + builderType := builders[0].Type() // *XCreate + slice := reflect.MakeSlice(reflect.SliceOf(builderType), len(builders), len(builders)) + for i, b := range builders { + slice.Index(i).Set(b) + } + bulk := client.MethodByName("CreateBulk").CallSlice([]reflect.Value{slice})[0] + out := bulk.MethodByName("Save").Call([]reflect.Value{reflect.ValueOf(ctx)}) + if err := asError(out[1]); err != nil { + return 0, err + } + return out[0].Len(), nil +} + +// copyGroupChildEdges copies the Group.child_groups many-to-many relationship, +// the only edge in the schema backed by a join table rather than an FK column. +// It is idempotent: only edges missing on the destination are added. +func copyGroupChildEdges(ctx context.Context, src, dst *ent.Client) (int, error) { + groups, err := src.Group.Query().All(ctx) + if err != nil { + return 0, err + } + added := 0 + for _, g := range groups { + srcChildIDs, err := g.QueryChildGroups().IDs(ctx) + if err != nil { + return added, err + } + if len(srcChildIDs) == 0 { + continue + } + dstGroup, err := dst.Group.Get(ctx, g.ID) + if err != nil { + return added, err + } + dstChildIDs, err := dstGroup.QueryChildGroups().IDs(ctx) + if err != nil { + return added, err + } + have := make(map[any]struct{}, len(dstChildIDs)) + for _, id := range dstChildIDs { + have[id] = struct{}{} + } + missing := srcChildIDs[:0:0] + for _, id := range srcChildIDs { + if _, ok := have[id]; !ok { + missing = append(missing, id) + } + } + if len(missing) == 0 { + continue + } + if err := dst.Group.UpdateOneID(g.ID).AddChildGroupIDs(missing...).Exec(ctx); err != nil { + return added, err + } + added += len(missing) + } + return added, nil +} + +// asError converts a reflect.Value holding an error interface to a Go error. +func asError(v reflect.Value) error { + if v.IsNil() { + return nil + } + return v.Interface().(error) +} diff --git a/pkg/ent/entc/migrate_beta_integration_test.go b/pkg/ent/entc/migrate_beta_integration_test.go new file mode 100644 index 000000000..4ee2e8362 --- /dev/null +++ b/pkg/ent/entc/migrate_beta_integration_test.go @@ -0,0 +1,296 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build integration + +package entc_test + +import ( + "context" + "database/sql" + "os" + "path/filepath" + "testing" + "time" + + "github.com/google/uuid" + + "github.com/GoogleCloudPlatform/scion/pkg/ent" + "github.com/GoogleCloudPlatform/scion/pkg/ent/accesspolicy" + "github.com/GoogleCloudPlatform/scion/pkg/ent/agent" + "github.com/GoogleCloudPlatform/scion/pkg/ent/entc" + "github.com/GoogleCloudPlatform/scion/pkg/ent/group" + "github.com/GoogleCloudPlatform/scion/pkg/ent/groupmembership" + "github.com/GoogleCloudPlatform/scion/pkg/ent/policybinding" + "github.com/GoogleCloudPlatform/scion/pkg/ent/user" +) + +// TestMigrateBeta_SQLiteToPostgres exercises the full Migration β path against a +// real PostgreSQL instance. It seeds an Ent-on-SQLite database, copies it to +// Postgres with MigrateData, then asserts: +// - every entity's source and destination counts match, +// - the run is idempotent (a second run inserts nothing), +// - FK relationships and a M2M edge survive the copy, +// - representative field values round-trip intact. +// +// The destination DSN comes from SCION_PG_TEST_DSN; the test skips when it is +// unset. Run with: +// +// SCION_PG_TEST_DSN='postgres://user:pass@host:5432/db?sslmode=require' \ +// go test -tags integration -run TestMigrateBeta ./pkg/ent/entc/... +func TestMigrateBeta_SQLiteToPostgres(t *testing.T) { + dstDSN := os.Getenv("SCION_PG_TEST_DSN") + if dstDSN == "" { + t.Skip("SCION_PG_TEST_DSN not set; skipping Postgres integration test") + } + ctx := context.Background() + + // Start from a clean destination schema so row counts are deterministic. + resetPostgresSchema(t, dstDSN) + + // --- Seed an Ent-on-SQLite source. --- + dir := t.TempDir() + srcPath := filepath.Join(dir, "hub.db") + seed := seedSQLiteSource(t, ctx, srcPath) + + // --- Open source read-only + destination, ensure schema, migrate. --- + src, err := entc.OpenSQLiteReadOnly("file:" + srcPath + "?cache=shared") + if err != nil { + t.Fatalf("open source read-only: %v", err) + } + defer src.Close() + + dst, err := entc.OpenPostgres(dstDSN, entc.PoolConfig{MaxOpenConns: 10, MaxIdleConns: 5}) + if err != nil { + t.Fatalf("open postgres: %v", err) + } + defer dst.Close() + + if err := entc.AutoMigrate(ctx, dst); err != nil { + t.Fatalf("auto-migrate destination: %v", err) + } + + report, err := entc.MigrateData(ctx, src, dst, entc.MigrateOptions{ + Logf: func(format string, args ...any) { t.Logf(format, args...) }, + }) + if err != nil { + t.Fatalf("first migration: %v", err) + } + + // Every entity must have matching counts; the seeded entities must have + // actually inserted rows with nothing skipped on the first pass. + seenInserts := 0 + for _, e := range report.Entities { + if e.Source != e.Dest { + t.Errorf("%s: source=%d dest=%d (mismatch)", e.Entity, e.Source, e.Dest) + } + if e.Skipped != 0 { + t.Errorf("%s: expected 0 skipped on first run, got %d", e.Entity, e.Skipped) + } + seenInserts += e.Inserted + } + if seenInserts == 0 { + t.Fatal("first migration inserted nothing; seed did not take") + } + if report.ChildGroupEdgs != 1 { + t.Errorf("expected 1 child-group edge, got %d", report.ChildGroupEdgs) + } + + // --- Idempotency: a second run inserts nothing and skips everything. --- + report2, err := entc.MigrateData(ctx, src, dst, entc.MigrateOptions{}) + if err != nil { + t.Fatalf("second (idempotent) migration: %v", err) + } + for _, e := range report2.Entities { + if e.Inserted != 0 { + t.Errorf("%s: idempotent run inserted %d rows", e.Entity, e.Inserted) + } + if e.Source != e.Dest { + t.Errorf("%s: idempotent run count mismatch source=%d dest=%d", e.Entity, e.Source, e.Dest) + } + } + if report2.ChildGroupEdgs != 0 { + t.Errorf("idempotent run added %d child-group edges, want 0", report2.ChildGroupEdgs) + } + + // --- Value round-trip + relationship checks on the destination. --- + gotUser, err := dst.User.Get(ctx, seed.userID) + if err != nil { + t.Fatalf("fetch migrated user: %v", err) + } + if gotUser.Email != "alice@example.com" { + t.Errorf("user email = %q, want alice@example.com", gotUser.Email) + } + if gotUser.Role != user.RoleAdmin { + t.Errorf("user role = %q, want admin", gotUser.Role) + } + + gotAgent, err := dst.Agent.Get(ctx, seed.agentID) + if err != nil { + t.Fatalf("fetch migrated agent: %v", err) + } + if gotAgent.ProjectID != seed.projectID { + t.Errorf("agent project_id = %v, want %v", gotAgent.ProjectID, seed.projectID) + } + if gotAgent.OwnerID == nil || *gotAgent.OwnerID != seed.user2ID { + t.Errorf("agent owner_id = %v, want %v", gotAgent.OwnerID, seed.user2ID) + } + + // The parent group must still point at the child group. + parent, err := dst.Group.Get(ctx, seed.parentGroupID) + if err != nil { + t.Fatalf("fetch parent group: %v", err) + } + childIDs, err := parent.QueryChildGroups().IDs(ctx) + if err != nil { + t.Fatalf("query child groups: %v", err) + } + if len(childIDs) != 1 || childIDs[0] != seed.childGroupID { + t.Errorf("child group edges = %v, want [%v]", childIDs, seed.childGroupID) + } +} + +// seededIDs records the primary keys created by seedSQLiteSource for later +// assertions against the destination. +type seededIDs struct { + userID uuid.UUID + user2ID uuid.UUID + projectID uuid.UUID + agentID uuid.UUID + parentGroupID uuid.UUID + childGroupID uuid.UUID +} + +// seedSQLiteSource creates an Ent-on-SQLite database at path and populates it +// with a representative graph: two users, a project, a policy, two groups (in a +// parent/child relationship), an agent, a group membership, a policy binding, +// and an API key (an independent entity with a plain FK-style column). +func seedSQLiteSource(t *testing.T, ctx context.Context, path string) seededIDs { + t.Helper() + c, err := entc.OpenSQLite("file:"+path+"?cache=shared", entc.PoolConfig{MaxOpenConns: 1}) + if err != nil { + t.Fatalf("open sqlite for seeding: %v", err) + } + defer c.Close() + if err := entc.AutoMigrate(ctx, c); err != nil { + t.Fatalf("auto-migrate source: %v", err) + } + + now := time.Now().UTC().Truncate(time.Second) + ids := seededIDs{ + userID: uuid.New(), + user2ID: uuid.New(), + projectID: uuid.New(), + agentID: uuid.New(), + parentGroupID: uuid.New(), + childGroupID: uuid.New(), + } + + if err := c.User.Create(). + SetID(ids.userID).SetEmail("alice@example.com").SetDisplayName("Alice"). + SetRole(user.RoleAdmin).SetStatus(user.StatusActive).SetCreated(now). + Exec(ctx); err != nil { + t.Fatalf("seed user1: %v", err) + } + if err := c.User.Create(). + SetID(ids.user2ID).SetEmail("bob@example.com").SetDisplayName("Bob"). + SetRole(user.RoleMember).SetStatus(user.StatusActive).SetCreated(now). + Exec(ctx); err != nil { + t.Fatalf("seed user2: %v", err) + } + + if err := c.Project.Create(). + SetID(ids.projectID).SetName("Demo").SetSlug("demo").SetVisibility("private"). + SetOwnerID(ids.userID.String()).SetCreated(now).SetUpdated(now). + Exec(ctx); err != nil { + t.Fatalf("seed project: %v", err) + } + + policyID := uuid.New() + if err := c.AccessPolicy.Create(). + SetID(policyID).SetName("allow-all").SetScopeType(accesspolicy.ScopeTypeHub). + SetResourceType("agent").SetEffect(accesspolicy.EffectAllow).SetActions([]string{"read"}). + SetPriority(0).SetCreated(now).SetUpdated(now). + Exec(ctx); err != nil { + t.Fatalf("seed policy: %v", err) + } + + if err := c.Group.Create(). + SetID(ids.parentGroupID).SetName("Parent").SetSlug("parent"). + SetGroupType(group.GroupTypeExplicit).SetOwnerID(ids.userID).SetCreated(now).SetUpdated(now). + Exec(ctx); err != nil { + t.Fatalf("seed parent group: %v", err) + } + if err := c.Group.Create(). + SetID(ids.childGroupID).SetName("Child").SetSlug("child"). + SetGroupType(group.GroupTypeExplicit).SetOwnerID(ids.userID).SetCreated(now).SetUpdated(now). + Exec(ctx); err != nil { + t.Fatalf("seed child group: %v", err) + } + if err := c.Group.UpdateOneID(ids.parentGroupID).AddChildGroupIDs(ids.childGroupID).Exec(ctx); err != nil { + t.Fatalf("link child group: %v", err) + } + + if err := c.Agent.Create(). + SetID(ids.agentID).SetSlug("agent-1").SetName("Agent One"). + SetProjectID(ids.projectID).SetStatus(agent.StatusRunning).SetVisibility("private"). + SetCreatedBy(ids.userID).SetOwnerID(ids.user2ID).SetCreated(now).SetUpdated(now). + Exec(ctx); err != nil { + t.Fatalf("seed agent: %v", err) + } + + if err := c.GroupMembership.Create(). + SetID(uuid.New()).SetRole(groupmembership.RoleMember).SetAddedAt(now). + SetGroupID(ids.parentGroupID).SetUserID(ids.user2ID). + Exec(ctx); err != nil { + t.Fatalf("seed group membership: %v", err) + } + + if err := c.PolicyBinding.Create(). + SetID(uuid.New()).SetPrincipalType(policybinding.PrincipalTypeUser). + SetPolicyID(policyID).SetUserID(ids.userID).SetCreated(now). + Exec(ctx); err != nil { + t.Fatalf("seed policy binding: %v", err) + } + + if err := c.ApiKey.Create(). + SetID(uuid.New()).SetUserID(ids.userID).SetKeyHash("hash-abc").SetCreated(now). + Exec(ctx); err != nil { + t.Fatalf("seed api key: %v", err) + } + + return ids +} + +// resetPostgresSchema drops and recreates the public schema so the test starts +// from an empty database, making row-count assertions deterministic. +func resetPostgresSchema(t *testing.T, dsn string) { + t.Helper() + db, err := sql.Open("pgx", dsn) + if err != nil { + t.Fatalf("open postgres for reset: %v", err) + } + defer db.Close() + for _, stmt := range []string{ + "DROP SCHEMA public CASCADE", + "CREATE SCHEMA public", + } { + if _, err := db.Exec(stmt); err != nil { + t.Fatalf("reset schema (%s): %v", stmt, err) + } + } +} + +// ensure ent is referenced even if future edits drop direct uses. +var _ = ent.Client{} diff --git a/pkg/ent/entc/migrate_grove_to_project_test.go b/pkg/ent/entc/migrate_grove_to_project_test.go index 54ec5b53d..f43115513 100644 --- a/pkg/ent/entc/migrate_grove_to_project_test.go +++ b/pkg/ent/entc/migrate_grove_to_project_test.go @@ -49,7 +49,7 @@ func setupEntDB(t *testing.T) string { t.Helper() dbName := t.Name() dsn := "file:" + dbName + "?mode=memory&cache=shared" - client, err := OpenSQLite(dsn) + client, err := OpenSQLite(dsn, PoolConfig{}) require.NoError(t, err) t.Cleanup(func() { client.Close() }) require.NoError(t, AutoMigrate(context.Background(), client)) diff --git a/pkg/ent/envvar.go b/pkg/ent/envvar.go new file mode 100644 index 000000000..7262c6a09 --- /dev/null +++ b/pkg/ent/envvar.go @@ -0,0 +1,219 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "fmt" + "strings" + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "github.com/GoogleCloudPlatform/scion/pkg/ent/envvar" + "github.com/google/uuid" +) + +// EnvVar is the model entity for the EnvVar schema. +type EnvVar struct { + config `json:"-"` + // ID of the ent. + ID uuid.UUID `json:"id,omitempty"` + // Key holds the value of the "key" field. + Key string `json:"key,omitempty"` + // Value holds the value of the "value" field. + Value string `json:"value,omitempty"` + // Scope holds the value of the "scope" field. + Scope string `json:"scope,omitempty"` + // ScopeID holds the value of the "scope_id" field. + ScopeID string `json:"scope_id,omitempty"` + // Description holds the value of the "description" field. + Description string `json:"description,omitempty"` + // Sensitive holds the value of the "sensitive" field. + Sensitive bool `json:"sensitive,omitempty"` + // InjectionMode holds the value of the "injection_mode" field. + InjectionMode envvar.InjectionMode `json:"injection_mode,omitempty"` + // Secret holds the value of the "secret" field. + Secret bool `json:"secret,omitempty"` + // CreatedBy holds the value of the "created_by" field. + CreatedBy string `json:"created_by,omitempty"` + // Created holds the value of the "created" field. + Created time.Time `json:"created,omitempty"` + // Updated holds the value of the "updated" field. + Updated time.Time `json:"updated,omitempty"` + selectValues sql.SelectValues +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*EnvVar) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case envvar.FieldSensitive, envvar.FieldSecret: + values[i] = new(sql.NullBool) + case envvar.FieldKey, envvar.FieldValue, envvar.FieldScope, envvar.FieldScopeID, envvar.FieldDescription, envvar.FieldInjectionMode, envvar.FieldCreatedBy: + values[i] = new(sql.NullString) + case envvar.FieldCreated, envvar.FieldUpdated: + values[i] = new(sql.NullTime) + case envvar.FieldID: + values[i] = new(uuid.UUID) + default: + values[i] = new(sql.UnknownType) + } + } + return values, nil +} + +// assignValues assigns the values that were returned from sql.Rows (after scanning) +// to the EnvVar fields. +func (_m *EnvVar) assignValues(columns []string, values []any) error { + if m, n := len(values), len(columns); m < n { + return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) + } + for i := range columns { + switch columns[i] { + case envvar.FieldID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field id", values[i]) + } else if value != nil { + _m.ID = *value + } + case envvar.FieldKey: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field key", values[i]) + } else if value.Valid { + _m.Key = value.String + } + case envvar.FieldValue: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field value", values[i]) + } else if value.Valid { + _m.Value = value.String + } + case envvar.FieldScope: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field scope", values[i]) + } else if value.Valid { + _m.Scope = value.String + } + case envvar.FieldScopeID: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field scope_id", values[i]) + } else if value.Valid { + _m.ScopeID = value.String + } + case envvar.FieldDescription: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field description", values[i]) + } else if value.Valid { + _m.Description = value.String + } + case envvar.FieldSensitive: + if value, ok := values[i].(*sql.NullBool); !ok { + return fmt.Errorf("unexpected type %T for field sensitive", values[i]) + } else if value.Valid { + _m.Sensitive = value.Bool + } + case envvar.FieldInjectionMode: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field injection_mode", values[i]) + } else if value.Valid { + _m.InjectionMode = envvar.InjectionMode(value.String) + } + case envvar.FieldSecret: + if value, ok := values[i].(*sql.NullBool); !ok { + return fmt.Errorf("unexpected type %T for field secret", values[i]) + } else if value.Valid { + _m.Secret = value.Bool + } + case envvar.FieldCreatedBy: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field created_by", values[i]) + } else if value.Valid { + _m.CreatedBy = value.String + } + case envvar.FieldCreated: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field created", values[i]) + } else if value.Valid { + _m.Created = value.Time + } + case envvar.FieldUpdated: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field updated", values[i]) + } else if value.Valid { + _m.Updated = value.Time + } + default: + _m.selectValues.Set(columns[i], values[i]) + } + } + return nil +} + +// GetValue returns the ent.Value that was dynamically selected and assigned to the EnvVar. +// This includes values selected through modifiers, order, etc. +func (_m *EnvVar) GetValue(name string) (ent.Value, error) { + return _m.selectValues.Get(name) +} + +// Update returns a builder for updating this EnvVar. +// Note that you need to call EnvVar.Unwrap() before calling this method if this EnvVar +// was returned from a transaction, and the transaction was committed or rolled back. +func (_m *EnvVar) Update() *EnvVarUpdateOne { + return NewEnvVarClient(_m.config).UpdateOne(_m) +} + +// Unwrap unwraps the EnvVar entity that was returned from a transaction after it was closed, +// so that all future queries will be executed through the driver which created the transaction. +func (_m *EnvVar) Unwrap() *EnvVar { + _tx, ok := _m.config.driver.(*txDriver) + if !ok { + panic("ent: EnvVar is not a transactional entity") + } + _m.config.driver = _tx.drv + return _m +} + +// String implements the fmt.Stringer. +func (_m *EnvVar) String() string { + var builder strings.Builder + builder.WriteString("EnvVar(") + builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID)) + builder.WriteString("key=") + builder.WriteString(_m.Key) + builder.WriteString(", ") + builder.WriteString("value=") + builder.WriteString(_m.Value) + builder.WriteString(", ") + builder.WriteString("scope=") + builder.WriteString(_m.Scope) + builder.WriteString(", ") + builder.WriteString("scope_id=") + builder.WriteString(_m.ScopeID) + builder.WriteString(", ") + builder.WriteString("description=") + builder.WriteString(_m.Description) + builder.WriteString(", ") + builder.WriteString("sensitive=") + builder.WriteString(fmt.Sprintf("%v", _m.Sensitive)) + builder.WriteString(", ") + builder.WriteString("injection_mode=") + builder.WriteString(fmt.Sprintf("%v", _m.InjectionMode)) + builder.WriteString(", ") + builder.WriteString("secret=") + builder.WriteString(fmt.Sprintf("%v", _m.Secret)) + builder.WriteString(", ") + builder.WriteString("created_by=") + builder.WriteString(_m.CreatedBy) + builder.WriteString(", ") + builder.WriteString("created=") + builder.WriteString(_m.Created.Format(time.ANSIC)) + builder.WriteString(", ") + builder.WriteString("updated=") + builder.WriteString(_m.Updated.Format(time.ANSIC)) + builder.WriteByte(')') + return builder.String() +} + +// EnvVars is a parsable slice of EnvVar. +type EnvVars []*EnvVar diff --git a/pkg/ent/envvar/envvar.go b/pkg/ent/envvar/envvar.go new file mode 100644 index 000000000..8af7fe804 --- /dev/null +++ b/pkg/ent/envvar/envvar.go @@ -0,0 +1,176 @@ +// Code generated by ent, DO NOT EDIT. + +package envvar + +import ( + "fmt" + "time" + + "entgo.io/ent/dialect/sql" + "github.com/google/uuid" +) + +const ( + // Label holds the string label denoting the envvar type in the database. + Label = "env_var" + // FieldID holds the string denoting the id field in the database. + FieldID = "id" + // FieldKey holds the string denoting the key field in the database. + FieldKey = "key" + // FieldValue holds the string denoting the value field in the database. + FieldValue = "value" + // FieldScope holds the string denoting the scope field in the database. + FieldScope = "scope" + // FieldScopeID holds the string denoting the scope_id field in the database. + FieldScopeID = "scope_id" + // FieldDescription holds the string denoting the description field in the database. + FieldDescription = "description" + // FieldSensitive holds the string denoting the sensitive field in the database. + FieldSensitive = "sensitive" + // FieldInjectionMode holds the string denoting the injection_mode field in the database. + FieldInjectionMode = "injection_mode" + // FieldSecret holds the string denoting the secret field in the database. + FieldSecret = "secret" + // FieldCreatedBy holds the string denoting the created_by field in the database. + FieldCreatedBy = "created_by" + // FieldCreated holds the string denoting the created field in the database. + FieldCreated = "created" + // FieldUpdated holds the string denoting the updated field in the database. + FieldUpdated = "updated" + // Table holds the table name of the envvar in the database. + Table = "env_vars" +) + +// Columns holds all SQL columns for envvar fields. +var Columns = []string{ + FieldID, + FieldKey, + FieldValue, + FieldScope, + FieldScopeID, + FieldDescription, + FieldSensitive, + FieldInjectionMode, + FieldSecret, + FieldCreatedBy, + FieldCreated, + FieldUpdated, +} + +// ValidColumn reports if the column name is valid (part of the table columns). +func ValidColumn(column string) bool { + for i := range Columns { + if column == Columns[i] { + return true + } + } + return false +} + +var ( + // KeyValidator is a validator for the "key" field. It is called by the builders before save. + KeyValidator func(string) error + // ScopeValidator is a validator for the "scope" field. It is called by the builders before save. + ScopeValidator func(string) error + // DefaultSensitive holds the default value on creation for the "sensitive" field. + DefaultSensitive bool + // DefaultSecret holds the default value on creation for the "secret" field. + DefaultSecret bool + // DefaultCreated holds the default value on creation for the "created" field. + DefaultCreated func() time.Time + // DefaultUpdated holds the default value on creation for the "updated" field. + DefaultUpdated func() time.Time + // UpdateDefaultUpdated holds the default value on update for the "updated" field. + UpdateDefaultUpdated func() time.Time + // DefaultID holds the default value on creation for the "id" field. + DefaultID func() uuid.UUID +) + +// InjectionMode defines the type for the "injection_mode" enum field. +type InjectionMode string + +// InjectionModeAsNeeded is the default value of the InjectionMode enum. +const DefaultInjectionMode = InjectionModeAsNeeded + +// InjectionMode values. +const ( + InjectionModeAlways InjectionMode = "always" + InjectionModeAsNeeded InjectionMode = "as_needed" +) + +func (im InjectionMode) String() string { + return string(im) +} + +// InjectionModeValidator is a validator for the "injection_mode" field enum values. It is called by the builders before save. +func InjectionModeValidator(im InjectionMode) error { + switch im { + case InjectionModeAlways, InjectionModeAsNeeded: + return nil + default: + return fmt.Errorf("envvar: invalid enum value for injection_mode field: %q", im) + } +} + +// OrderOption defines the ordering options for the EnvVar queries. +type OrderOption func(*sql.Selector) + +// ByID orders the results by the id field. +func ByID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldID, opts...).ToFunc() +} + +// ByKey orders the results by the key field. +func ByKey(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldKey, opts...).ToFunc() +} + +// ByValue orders the results by the value field. +func ByValue(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldValue, opts...).ToFunc() +} + +// ByScope orders the results by the scope field. +func ByScope(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldScope, opts...).ToFunc() +} + +// ByScopeID orders the results by the scope_id field. +func ByScopeID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldScopeID, opts...).ToFunc() +} + +// ByDescription orders the results by the description field. +func ByDescription(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldDescription, opts...).ToFunc() +} + +// BySensitive orders the results by the sensitive field. +func BySensitive(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldSensitive, opts...).ToFunc() +} + +// ByInjectionMode orders the results by the injection_mode field. +func ByInjectionMode(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldInjectionMode, opts...).ToFunc() +} + +// BySecret orders the results by the secret field. +func BySecret(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldSecret, opts...).ToFunc() +} + +// ByCreatedBy orders the results by the created_by field. +func ByCreatedBy(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCreatedBy, opts...).ToFunc() +} + +// ByCreated orders the results by the created field. +func ByCreated(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCreated, opts...).ToFunc() +} + +// ByUpdated orders the results by the updated field. +func ByUpdated(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldUpdated, opts...).ToFunc() +} diff --git a/pkg/ent/envvar/where.go b/pkg/ent/envvar/where.go new file mode 100644 index 000000000..ebc748c5a --- /dev/null +++ b/pkg/ent/envvar/where.go @@ -0,0 +1,651 @@ +// Code generated by ent, DO NOT EDIT. + +package envvar + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/google/uuid" +) + +// ID filters vertices based on their ID field. +func ID(id uuid.UUID) predicate.EnvVar { + return predicate.EnvVar(sql.FieldEQ(FieldID, id)) +} + +// IDEQ applies the EQ predicate on the ID field. +func IDEQ(id uuid.UUID) predicate.EnvVar { + return predicate.EnvVar(sql.FieldEQ(FieldID, id)) +} + +// IDNEQ applies the NEQ predicate on the ID field. +func IDNEQ(id uuid.UUID) predicate.EnvVar { + return predicate.EnvVar(sql.FieldNEQ(FieldID, id)) +} + +// IDIn applies the In predicate on the ID field. +func IDIn(ids ...uuid.UUID) predicate.EnvVar { + return predicate.EnvVar(sql.FieldIn(FieldID, ids...)) +} + +// IDNotIn applies the NotIn predicate on the ID field. +func IDNotIn(ids ...uuid.UUID) predicate.EnvVar { + return predicate.EnvVar(sql.FieldNotIn(FieldID, ids...)) +} + +// IDGT applies the GT predicate on the ID field. +func IDGT(id uuid.UUID) predicate.EnvVar { + return predicate.EnvVar(sql.FieldGT(FieldID, id)) +} + +// IDGTE applies the GTE predicate on the ID field. +func IDGTE(id uuid.UUID) predicate.EnvVar { + return predicate.EnvVar(sql.FieldGTE(FieldID, id)) +} + +// IDLT applies the LT predicate on the ID field. +func IDLT(id uuid.UUID) predicate.EnvVar { + return predicate.EnvVar(sql.FieldLT(FieldID, id)) +} + +// IDLTE applies the LTE predicate on the ID field. +func IDLTE(id uuid.UUID) predicate.EnvVar { + return predicate.EnvVar(sql.FieldLTE(FieldID, id)) +} + +// Key applies equality check predicate on the "key" field. It's identical to KeyEQ. +func Key(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldEQ(FieldKey, v)) +} + +// Value applies equality check predicate on the "value" field. It's identical to ValueEQ. +func Value(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldEQ(FieldValue, v)) +} + +// Scope applies equality check predicate on the "scope" field. It's identical to ScopeEQ. +func Scope(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldEQ(FieldScope, v)) +} + +// ScopeID applies equality check predicate on the "scope_id" field. It's identical to ScopeIDEQ. +func ScopeID(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldEQ(FieldScopeID, v)) +} + +// Description applies equality check predicate on the "description" field. It's identical to DescriptionEQ. +func Description(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldEQ(FieldDescription, v)) +} + +// Sensitive applies equality check predicate on the "sensitive" field. It's identical to SensitiveEQ. +func Sensitive(v bool) predicate.EnvVar { + return predicate.EnvVar(sql.FieldEQ(FieldSensitive, v)) +} + +// Secret applies equality check predicate on the "secret" field. It's identical to SecretEQ. +func Secret(v bool) predicate.EnvVar { + return predicate.EnvVar(sql.FieldEQ(FieldSecret, v)) +} + +// CreatedBy applies equality check predicate on the "created_by" field. It's identical to CreatedByEQ. +func CreatedBy(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldEQ(FieldCreatedBy, v)) +} + +// Created applies equality check predicate on the "created" field. It's identical to CreatedEQ. +func Created(v time.Time) predicate.EnvVar { + return predicate.EnvVar(sql.FieldEQ(FieldCreated, v)) +} + +// Updated applies equality check predicate on the "updated" field. It's identical to UpdatedEQ. +func Updated(v time.Time) predicate.EnvVar { + return predicate.EnvVar(sql.FieldEQ(FieldUpdated, v)) +} + +// KeyEQ applies the EQ predicate on the "key" field. +func KeyEQ(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldEQ(FieldKey, v)) +} + +// KeyNEQ applies the NEQ predicate on the "key" field. +func KeyNEQ(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldNEQ(FieldKey, v)) +} + +// KeyIn applies the In predicate on the "key" field. +func KeyIn(vs ...string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldIn(FieldKey, vs...)) +} + +// KeyNotIn applies the NotIn predicate on the "key" field. +func KeyNotIn(vs ...string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldNotIn(FieldKey, vs...)) +} + +// KeyGT applies the GT predicate on the "key" field. +func KeyGT(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldGT(FieldKey, v)) +} + +// KeyGTE applies the GTE predicate on the "key" field. +func KeyGTE(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldGTE(FieldKey, v)) +} + +// KeyLT applies the LT predicate on the "key" field. +func KeyLT(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldLT(FieldKey, v)) +} + +// KeyLTE applies the LTE predicate on the "key" field. +func KeyLTE(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldLTE(FieldKey, v)) +} + +// KeyContains applies the Contains predicate on the "key" field. +func KeyContains(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldContains(FieldKey, v)) +} + +// KeyHasPrefix applies the HasPrefix predicate on the "key" field. +func KeyHasPrefix(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldHasPrefix(FieldKey, v)) +} + +// KeyHasSuffix applies the HasSuffix predicate on the "key" field. +func KeyHasSuffix(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldHasSuffix(FieldKey, v)) +} + +// KeyEqualFold applies the EqualFold predicate on the "key" field. +func KeyEqualFold(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldEqualFold(FieldKey, v)) +} + +// KeyContainsFold applies the ContainsFold predicate on the "key" field. +func KeyContainsFold(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldContainsFold(FieldKey, v)) +} + +// ValueEQ applies the EQ predicate on the "value" field. +func ValueEQ(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldEQ(FieldValue, v)) +} + +// ValueNEQ applies the NEQ predicate on the "value" field. +func ValueNEQ(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldNEQ(FieldValue, v)) +} + +// ValueIn applies the In predicate on the "value" field. +func ValueIn(vs ...string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldIn(FieldValue, vs...)) +} + +// ValueNotIn applies the NotIn predicate on the "value" field. +func ValueNotIn(vs ...string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldNotIn(FieldValue, vs...)) +} + +// ValueGT applies the GT predicate on the "value" field. +func ValueGT(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldGT(FieldValue, v)) +} + +// ValueGTE applies the GTE predicate on the "value" field. +func ValueGTE(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldGTE(FieldValue, v)) +} + +// ValueLT applies the LT predicate on the "value" field. +func ValueLT(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldLT(FieldValue, v)) +} + +// ValueLTE applies the LTE predicate on the "value" field. +func ValueLTE(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldLTE(FieldValue, v)) +} + +// ValueContains applies the Contains predicate on the "value" field. +func ValueContains(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldContains(FieldValue, v)) +} + +// ValueHasPrefix applies the HasPrefix predicate on the "value" field. +func ValueHasPrefix(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldHasPrefix(FieldValue, v)) +} + +// ValueHasSuffix applies the HasSuffix predicate on the "value" field. +func ValueHasSuffix(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldHasSuffix(FieldValue, v)) +} + +// ValueEqualFold applies the EqualFold predicate on the "value" field. +func ValueEqualFold(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldEqualFold(FieldValue, v)) +} + +// ValueContainsFold applies the ContainsFold predicate on the "value" field. +func ValueContainsFold(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldContainsFold(FieldValue, v)) +} + +// ScopeEQ applies the EQ predicate on the "scope" field. +func ScopeEQ(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldEQ(FieldScope, v)) +} + +// ScopeNEQ applies the NEQ predicate on the "scope" field. +func ScopeNEQ(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldNEQ(FieldScope, v)) +} + +// ScopeIn applies the In predicate on the "scope" field. +func ScopeIn(vs ...string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldIn(FieldScope, vs...)) +} + +// ScopeNotIn applies the NotIn predicate on the "scope" field. +func ScopeNotIn(vs ...string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldNotIn(FieldScope, vs...)) +} + +// ScopeGT applies the GT predicate on the "scope" field. +func ScopeGT(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldGT(FieldScope, v)) +} + +// ScopeGTE applies the GTE predicate on the "scope" field. +func ScopeGTE(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldGTE(FieldScope, v)) +} + +// ScopeLT applies the LT predicate on the "scope" field. +func ScopeLT(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldLT(FieldScope, v)) +} + +// ScopeLTE applies the LTE predicate on the "scope" field. +func ScopeLTE(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldLTE(FieldScope, v)) +} + +// ScopeContains applies the Contains predicate on the "scope" field. +func ScopeContains(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldContains(FieldScope, v)) +} + +// ScopeHasPrefix applies the HasPrefix predicate on the "scope" field. +func ScopeHasPrefix(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldHasPrefix(FieldScope, v)) +} + +// ScopeHasSuffix applies the HasSuffix predicate on the "scope" field. +func ScopeHasSuffix(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldHasSuffix(FieldScope, v)) +} + +// ScopeEqualFold applies the EqualFold predicate on the "scope" field. +func ScopeEqualFold(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldEqualFold(FieldScope, v)) +} + +// ScopeContainsFold applies the ContainsFold predicate on the "scope" field. +func ScopeContainsFold(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldContainsFold(FieldScope, v)) +} + +// ScopeIDEQ applies the EQ predicate on the "scope_id" field. +func ScopeIDEQ(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldEQ(FieldScopeID, v)) +} + +// ScopeIDNEQ applies the NEQ predicate on the "scope_id" field. +func ScopeIDNEQ(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldNEQ(FieldScopeID, v)) +} + +// ScopeIDIn applies the In predicate on the "scope_id" field. +func ScopeIDIn(vs ...string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldIn(FieldScopeID, vs...)) +} + +// ScopeIDNotIn applies the NotIn predicate on the "scope_id" field. +func ScopeIDNotIn(vs ...string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldNotIn(FieldScopeID, vs...)) +} + +// ScopeIDGT applies the GT predicate on the "scope_id" field. +func ScopeIDGT(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldGT(FieldScopeID, v)) +} + +// ScopeIDGTE applies the GTE predicate on the "scope_id" field. +func ScopeIDGTE(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldGTE(FieldScopeID, v)) +} + +// ScopeIDLT applies the LT predicate on the "scope_id" field. +func ScopeIDLT(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldLT(FieldScopeID, v)) +} + +// ScopeIDLTE applies the LTE predicate on the "scope_id" field. +func ScopeIDLTE(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldLTE(FieldScopeID, v)) +} + +// ScopeIDContains applies the Contains predicate on the "scope_id" field. +func ScopeIDContains(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldContains(FieldScopeID, v)) +} + +// ScopeIDHasPrefix applies the HasPrefix predicate on the "scope_id" field. +func ScopeIDHasPrefix(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldHasPrefix(FieldScopeID, v)) +} + +// ScopeIDHasSuffix applies the HasSuffix predicate on the "scope_id" field. +func ScopeIDHasSuffix(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldHasSuffix(FieldScopeID, v)) +} + +// ScopeIDEqualFold applies the EqualFold predicate on the "scope_id" field. +func ScopeIDEqualFold(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldEqualFold(FieldScopeID, v)) +} + +// ScopeIDContainsFold applies the ContainsFold predicate on the "scope_id" field. +func ScopeIDContainsFold(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldContainsFold(FieldScopeID, v)) +} + +// DescriptionEQ applies the EQ predicate on the "description" field. +func DescriptionEQ(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldEQ(FieldDescription, v)) +} + +// DescriptionNEQ applies the NEQ predicate on the "description" field. +func DescriptionNEQ(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldNEQ(FieldDescription, v)) +} + +// DescriptionIn applies the In predicate on the "description" field. +func DescriptionIn(vs ...string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldIn(FieldDescription, vs...)) +} + +// DescriptionNotIn applies the NotIn predicate on the "description" field. +func DescriptionNotIn(vs ...string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldNotIn(FieldDescription, vs...)) +} + +// DescriptionGT applies the GT predicate on the "description" field. +func DescriptionGT(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldGT(FieldDescription, v)) +} + +// DescriptionGTE applies the GTE predicate on the "description" field. +func DescriptionGTE(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldGTE(FieldDescription, v)) +} + +// DescriptionLT applies the LT predicate on the "description" field. +func DescriptionLT(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldLT(FieldDescription, v)) +} + +// DescriptionLTE applies the LTE predicate on the "description" field. +func DescriptionLTE(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldLTE(FieldDescription, v)) +} + +// DescriptionContains applies the Contains predicate on the "description" field. +func DescriptionContains(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldContains(FieldDescription, v)) +} + +// DescriptionHasPrefix applies the HasPrefix predicate on the "description" field. +func DescriptionHasPrefix(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldHasPrefix(FieldDescription, v)) +} + +// DescriptionHasSuffix applies the HasSuffix predicate on the "description" field. +func DescriptionHasSuffix(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldHasSuffix(FieldDescription, v)) +} + +// DescriptionIsNil applies the IsNil predicate on the "description" field. +func DescriptionIsNil() predicate.EnvVar { + return predicate.EnvVar(sql.FieldIsNull(FieldDescription)) +} + +// DescriptionNotNil applies the NotNil predicate on the "description" field. +func DescriptionNotNil() predicate.EnvVar { + return predicate.EnvVar(sql.FieldNotNull(FieldDescription)) +} + +// DescriptionEqualFold applies the EqualFold predicate on the "description" field. +func DescriptionEqualFold(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldEqualFold(FieldDescription, v)) +} + +// DescriptionContainsFold applies the ContainsFold predicate on the "description" field. +func DescriptionContainsFold(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldContainsFold(FieldDescription, v)) +} + +// SensitiveEQ applies the EQ predicate on the "sensitive" field. +func SensitiveEQ(v bool) predicate.EnvVar { + return predicate.EnvVar(sql.FieldEQ(FieldSensitive, v)) +} + +// SensitiveNEQ applies the NEQ predicate on the "sensitive" field. +func SensitiveNEQ(v bool) predicate.EnvVar { + return predicate.EnvVar(sql.FieldNEQ(FieldSensitive, v)) +} + +// InjectionModeEQ applies the EQ predicate on the "injection_mode" field. +func InjectionModeEQ(v InjectionMode) predicate.EnvVar { + return predicate.EnvVar(sql.FieldEQ(FieldInjectionMode, v)) +} + +// InjectionModeNEQ applies the NEQ predicate on the "injection_mode" field. +func InjectionModeNEQ(v InjectionMode) predicate.EnvVar { + return predicate.EnvVar(sql.FieldNEQ(FieldInjectionMode, v)) +} + +// InjectionModeIn applies the In predicate on the "injection_mode" field. +func InjectionModeIn(vs ...InjectionMode) predicate.EnvVar { + return predicate.EnvVar(sql.FieldIn(FieldInjectionMode, vs...)) +} + +// InjectionModeNotIn applies the NotIn predicate on the "injection_mode" field. +func InjectionModeNotIn(vs ...InjectionMode) predicate.EnvVar { + return predicate.EnvVar(sql.FieldNotIn(FieldInjectionMode, vs...)) +} + +// SecretEQ applies the EQ predicate on the "secret" field. +func SecretEQ(v bool) predicate.EnvVar { + return predicate.EnvVar(sql.FieldEQ(FieldSecret, v)) +} + +// SecretNEQ applies the NEQ predicate on the "secret" field. +func SecretNEQ(v bool) predicate.EnvVar { + return predicate.EnvVar(sql.FieldNEQ(FieldSecret, v)) +} + +// CreatedByEQ applies the EQ predicate on the "created_by" field. +func CreatedByEQ(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldEQ(FieldCreatedBy, v)) +} + +// CreatedByNEQ applies the NEQ predicate on the "created_by" field. +func CreatedByNEQ(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldNEQ(FieldCreatedBy, v)) +} + +// CreatedByIn applies the In predicate on the "created_by" field. +func CreatedByIn(vs ...string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldIn(FieldCreatedBy, vs...)) +} + +// CreatedByNotIn applies the NotIn predicate on the "created_by" field. +func CreatedByNotIn(vs ...string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldNotIn(FieldCreatedBy, vs...)) +} + +// CreatedByGT applies the GT predicate on the "created_by" field. +func CreatedByGT(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldGT(FieldCreatedBy, v)) +} + +// CreatedByGTE applies the GTE predicate on the "created_by" field. +func CreatedByGTE(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldGTE(FieldCreatedBy, v)) +} + +// CreatedByLT applies the LT predicate on the "created_by" field. +func CreatedByLT(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldLT(FieldCreatedBy, v)) +} + +// CreatedByLTE applies the LTE predicate on the "created_by" field. +func CreatedByLTE(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldLTE(FieldCreatedBy, v)) +} + +// CreatedByContains applies the Contains predicate on the "created_by" field. +func CreatedByContains(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldContains(FieldCreatedBy, v)) +} + +// CreatedByHasPrefix applies the HasPrefix predicate on the "created_by" field. +func CreatedByHasPrefix(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldHasPrefix(FieldCreatedBy, v)) +} + +// CreatedByHasSuffix applies the HasSuffix predicate on the "created_by" field. +func CreatedByHasSuffix(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldHasSuffix(FieldCreatedBy, v)) +} + +// CreatedByIsNil applies the IsNil predicate on the "created_by" field. +func CreatedByIsNil() predicate.EnvVar { + return predicate.EnvVar(sql.FieldIsNull(FieldCreatedBy)) +} + +// CreatedByNotNil applies the NotNil predicate on the "created_by" field. +func CreatedByNotNil() predicate.EnvVar { + return predicate.EnvVar(sql.FieldNotNull(FieldCreatedBy)) +} + +// CreatedByEqualFold applies the EqualFold predicate on the "created_by" field. +func CreatedByEqualFold(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldEqualFold(FieldCreatedBy, v)) +} + +// CreatedByContainsFold applies the ContainsFold predicate on the "created_by" field. +func CreatedByContainsFold(v string) predicate.EnvVar { + return predicate.EnvVar(sql.FieldContainsFold(FieldCreatedBy, v)) +} + +// CreatedEQ applies the EQ predicate on the "created" field. +func CreatedEQ(v time.Time) predicate.EnvVar { + return predicate.EnvVar(sql.FieldEQ(FieldCreated, v)) +} + +// CreatedNEQ applies the NEQ predicate on the "created" field. +func CreatedNEQ(v time.Time) predicate.EnvVar { + return predicate.EnvVar(sql.FieldNEQ(FieldCreated, v)) +} + +// CreatedIn applies the In predicate on the "created" field. +func CreatedIn(vs ...time.Time) predicate.EnvVar { + return predicate.EnvVar(sql.FieldIn(FieldCreated, vs...)) +} + +// CreatedNotIn applies the NotIn predicate on the "created" field. +func CreatedNotIn(vs ...time.Time) predicate.EnvVar { + return predicate.EnvVar(sql.FieldNotIn(FieldCreated, vs...)) +} + +// CreatedGT applies the GT predicate on the "created" field. +func CreatedGT(v time.Time) predicate.EnvVar { + return predicate.EnvVar(sql.FieldGT(FieldCreated, v)) +} + +// CreatedGTE applies the GTE predicate on the "created" field. +func CreatedGTE(v time.Time) predicate.EnvVar { + return predicate.EnvVar(sql.FieldGTE(FieldCreated, v)) +} + +// CreatedLT applies the LT predicate on the "created" field. +func CreatedLT(v time.Time) predicate.EnvVar { + return predicate.EnvVar(sql.FieldLT(FieldCreated, v)) +} + +// CreatedLTE applies the LTE predicate on the "created" field. +func CreatedLTE(v time.Time) predicate.EnvVar { + return predicate.EnvVar(sql.FieldLTE(FieldCreated, v)) +} + +// UpdatedEQ applies the EQ predicate on the "updated" field. +func UpdatedEQ(v time.Time) predicate.EnvVar { + return predicate.EnvVar(sql.FieldEQ(FieldUpdated, v)) +} + +// UpdatedNEQ applies the NEQ predicate on the "updated" field. +func UpdatedNEQ(v time.Time) predicate.EnvVar { + return predicate.EnvVar(sql.FieldNEQ(FieldUpdated, v)) +} + +// UpdatedIn applies the In predicate on the "updated" field. +func UpdatedIn(vs ...time.Time) predicate.EnvVar { + return predicate.EnvVar(sql.FieldIn(FieldUpdated, vs...)) +} + +// UpdatedNotIn applies the NotIn predicate on the "updated" field. +func UpdatedNotIn(vs ...time.Time) predicate.EnvVar { + return predicate.EnvVar(sql.FieldNotIn(FieldUpdated, vs...)) +} + +// UpdatedGT applies the GT predicate on the "updated" field. +func UpdatedGT(v time.Time) predicate.EnvVar { + return predicate.EnvVar(sql.FieldGT(FieldUpdated, v)) +} + +// UpdatedGTE applies the GTE predicate on the "updated" field. +func UpdatedGTE(v time.Time) predicate.EnvVar { + return predicate.EnvVar(sql.FieldGTE(FieldUpdated, v)) +} + +// UpdatedLT applies the LT predicate on the "updated" field. +func UpdatedLT(v time.Time) predicate.EnvVar { + return predicate.EnvVar(sql.FieldLT(FieldUpdated, v)) +} + +// UpdatedLTE applies the LTE predicate on the "updated" field. +func UpdatedLTE(v time.Time) predicate.EnvVar { + return predicate.EnvVar(sql.FieldLTE(FieldUpdated, v)) +} + +// And groups predicates with the AND operator between them. +func And(predicates ...predicate.EnvVar) predicate.EnvVar { + return predicate.EnvVar(sql.AndPredicates(predicates...)) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.EnvVar) predicate.EnvVar { + return predicate.EnvVar(sql.OrPredicates(predicates...)) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.EnvVar) predicate.EnvVar { + return predicate.EnvVar(sql.NotPredicates(p)) +} diff --git a/pkg/ent/envvar_create.go b/pkg/ent/envvar_create.go new file mode 100644 index 000000000..af444f06e --- /dev/null +++ b/pkg/ent/envvar_create.go @@ -0,0 +1,1130 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/envvar" + "github.com/google/uuid" +) + +// EnvVarCreate is the builder for creating a EnvVar entity. +type EnvVarCreate struct { + config + mutation *EnvVarMutation + hooks []Hook + conflict []sql.ConflictOption +} + +// SetKey sets the "key" field. +func (_c *EnvVarCreate) SetKey(v string) *EnvVarCreate { + _c.mutation.SetKey(v) + return _c +} + +// SetValue sets the "value" field. +func (_c *EnvVarCreate) SetValue(v string) *EnvVarCreate { + _c.mutation.SetValue(v) + return _c +} + +// SetScope sets the "scope" field. +func (_c *EnvVarCreate) SetScope(v string) *EnvVarCreate { + _c.mutation.SetScope(v) + return _c +} + +// SetScopeID sets the "scope_id" field. +func (_c *EnvVarCreate) SetScopeID(v string) *EnvVarCreate { + _c.mutation.SetScopeID(v) + return _c +} + +// SetDescription sets the "description" field. +func (_c *EnvVarCreate) SetDescription(v string) *EnvVarCreate { + _c.mutation.SetDescription(v) + return _c +} + +// SetNillableDescription sets the "description" field if the given value is not nil. +func (_c *EnvVarCreate) SetNillableDescription(v *string) *EnvVarCreate { + if v != nil { + _c.SetDescription(*v) + } + return _c +} + +// SetSensitive sets the "sensitive" field. +func (_c *EnvVarCreate) SetSensitive(v bool) *EnvVarCreate { + _c.mutation.SetSensitive(v) + return _c +} + +// SetNillableSensitive sets the "sensitive" field if the given value is not nil. +func (_c *EnvVarCreate) SetNillableSensitive(v *bool) *EnvVarCreate { + if v != nil { + _c.SetSensitive(*v) + } + return _c +} + +// SetInjectionMode sets the "injection_mode" field. +func (_c *EnvVarCreate) SetInjectionMode(v envvar.InjectionMode) *EnvVarCreate { + _c.mutation.SetInjectionMode(v) + return _c +} + +// SetNillableInjectionMode sets the "injection_mode" field if the given value is not nil. +func (_c *EnvVarCreate) SetNillableInjectionMode(v *envvar.InjectionMode) *EnvVarCreate { + if v != nil { + _c.SetInjectionMode(*v) + } + return _c +} + +// SetSecret sets the "secret" field. +func (_c *EnvVarCreate) SetSecret(v bool) *EnvVarCreate { + _c.mutation.SetSecret(v) + return _c +} + +// SetNillableSecret sets the "secret" field if the given value is not nil. +func (_c *EnvVarCreate) SetNillableSecret(v *bool) *EnvVarCreate { + if v != nil { + _c.SetSecret(*v) + } + return _c +} + +// SetCreatedBy sets the "created_by" field. +func (_c *EnvVarCreate) SetCreatedBy(v string) *EnvVarCreate { + _c.mutation.SetCreatedBy(v) + return _c +} + +// SetNillableCreatedBy sets the "created_by" field if the given value is not nil. +func (_c *EnvVarCreate) SetNillableCreatedBy(v *string) *EnvVarCreate { + if v != nil { + _c.SetCreatedBy(*v) + } + return _c +} + +// SetCreated sets the "created" field. +func (_c *EnvVarCreate) SetCreated(v time.Time) *EnvVarCreate { + _c.mutation.SetCreated(v) + return _c +} + +// SetNillableCreated sets the "created" field if the given value is not nil. +func (_c *EnvVarCreate) SetNillableCreated(v *time.Time) *EnvVarCreate { + if v != nil { + _c.SetCreated(*v) + } + return _c +} + +// SetUpdated sets the "updated" field. +func (_c *EnvVarCreate) SetUpdated(v time.Time) *EnvVarCreate { + _c.mutation.SetUpdated(v) + return _c +} + +// SetNillableUpdated sets the "updated" field if the given value is not nil. +func (_c *EnvVarCreate) SetNillableUpdated(v *time.Time) *EnvVarCreate { + if v != nil { + _c.SetUpdated(*v) + } + return _c +} + +// SetID sets the "id" field. +func (_c *EnvVarCreate) SetID(v uuid.UUID) *EnvVarCreate { + _c.mutation.SetID(v) + return _c +} + +// SetNillableID sets the "id" field if the given value is not nil. +func (_c *EnvVarCreate) SetNillableID(v *uuid.UUID) *EnvVarCreate { + if v != nil { + _c.SetID(*v) + } + return _c +} + +// Mutation returns the EnvVarMutation object of the builder. +func (_c *EnvVarCreate) Mutation() *EnvVarMutation { + return _c.mutation +} + +// Save creates the EnvVar in the database. +func (_c *EnvVarCreate) Save(ctx context.Context) (*EnvVar, error) { + _c.defaults() + return withHooks(ctx, _c.sqlSave, _c.mutation, _c.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (_c *EnvVarCreate) SaveX(ctx context.Context) *EnvVar { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *EnvVarCreate) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *EnvVarCreate) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_c *EnvVarCreate) defaults() { + if _, ok := _c.mutation.Sensitive(); !ok { + v := envvar.DefaultSensitive + _c.mutation.SetSensitive(v) + } + if _, ok := _c.mutation.InjectionMode(); !ok { + v := envvar.DefaultInjectionMode + _c.mutation.SetInjectionMode(v) + } + if _, ok := _c.mutation.Secret(); !ok { + v := envvar.DefaultSecret + _c.mutation.SetSecret(v) + } + if _, ok := _c.mutation.Created(); !ok { + v := envvar.DefaultCreated() + _c.mutation.SetCreated(v) + } + if _, ok := _c.mutation.Updated(); !ok { + v := envvar.DefaultUpdated() + _c.mutation.SetUpdated(v) + } + if _, ok := _c.mutation.ID(); !ok { + v := envvar.DefaultID() + _c.mutation.SetID(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_c *EnvVarCreate) check() error { + if _, ok := _c.mutation.Key(); !ok { + return &ValidationError{Name: "key", err: errors.New(`ent: missing required field "EnvVar.key"`)} + } + if v, ok := _c.mutation.Key(); ok { + if err := envvar.KeyValidator(v); err != nil { + return &ValidationError{Name: "key", err: fmt.Errorf(`ent: validator failed for field "EnvVar.key": %w`, err)} + } + } + if _, ok := _c.mutation.Value(); !ok { + return &ValidationError{Name: "value", err: errors.New(`ent: missing required field "EnvVar.value"`)} + } + if _, ok := _c.mutation.Scope(); !ok { + return &ValidationError{Name: "scope", err: errors.New(`ent: missing required field "EnvVar.scope"`)} + } + if v, ok := _c.mutation.Scope(); ok { + if err := envvar.ScopeValidator(v); err != nil { + return &ValidationError{Name: "scope", err: fmt.Errorf(`ent: validator failed for field "EnvVar.scope": %w`, err)} + } + } + if _, ok := _c.mutation.ScopeID(); !ok { + return &ValidationError{Name: "scope_id", err: errors.New(`ent: missing required field "EnvVar.scope_id"`)} + } + if _, ok := _c.mutation.Sensitive(); !ok { + return &ValidationError{Name: "sensitive", err: errors.New(`ent: missing required field "EnvVar.sensitive"`)} + } + if _, ok := _c.mutation.InjectionMode(); !ok { + return &ValidationError{Name: "injection_mode", err: errors.New(`ent: missing required field "EnvVar.injection_mode"`)} + } + if v, ok := _c.mutation.InjectionMode(); ok { + if err := envvar.InjectionModeValidator(v); err != nil { + return &ValidationError{Name: "injection_mode", err: fmt.Errorf(`ent: validator failed for field "EnvVar.injection_mode": %w`, err)} + } + } + if _, ok := _c.mutation.Secret(); !ok { + return &ValidationError{Name: "secret", err: errors.New(`ent: missing required field "EnvVar.secret"`)} + } + if _, ok := _c.mutation.Created(); !ok { + return &ValidationError{Name: "created", err: errors.New(`ent: missing required field "EnvVar.created"`)} + } + if _, ok := _c.mutation.Updated(); !ok { + return &ValidationError{Name: "updated", err: errors.New(`ent: missing required field "EnvVar.updated"`)} + } + return nil +} + +func (_c *EnvVarCreate) sqlSave(ctx context.Context) (*EnvVar, error) { + if err := _c.check(); err != nil { + return nil, err + } + _node, _spec := _c.createSpec() + if err := sqlgraph.CreateNode(ctx, _c.driver, _spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + if _spec.ID.Value != nil { + if id, ok := _spec.ID.Value.(*uuid.UUID); ok { + _node.ID = *id + } else if err := _node.ID.Scan(_spec.ID.Value); err != nil { + return nil, err + } + } + _c.mutation.id = &_node.ID + _c.mutation.done = true + return _node, nil +} + +func (_c *EnvVarCreate) createSpec() (*EnvVar, *sqlgraph.CreateSpec) { + var ( + _node = &EnvVar{config: _c.config} + _spec = sqlgraph.NewCreateSpec(envvar.Table, sqlgraph.NewFieldSpec(envvar.FieldID, field.TypeUUID)) + ) + _spec.OnConflict = _c.conflict + if id, ok := _c.mutation.ID(); ok { + _node.ID = id + _spec.ID.Value = &id + } + if value, ok := _c.mutation.Key(); ok { + _spec.SetField(envvar.FieldKey, field.TypeString, value) + _node.Key = value + } + if value, ok := _c.mutation.Value(); ok { + _spec.SetField(envvar.FieldValue, field.TypeString, value) + _node.Value = value + } + if value, ok := _c.mutation.Scope(); ok { + _spec.SetField(envvar.FieldScope, field.TypeString, value) + _node.Scope = value + } + if value, ok := _c.mutation.ScopeID(); ok { + _spec.SetField(envvar.FieldScopeID, field.TypeString, value) + _node.ScopeID = value + } + if value, ok := _c.mutation.Description(); ok { + _spec.SetField(envvar.FieldDescription, field.TypeString, value) + _node.Description = value + } + if value, ok := _c.mutation.Sensitive(); ok { + _spec.SetField(envvar.FieldSensitive, field.TypeBool, value) + _node.Sensitive = value + } + if value, ok := _c.mutation.InjectionMode(); ok { + _spec.SetField(envvar.FieldInjectionMode, field.TypeEnum, value) + _node.InjectionMode = value + } + if value, ok := _c.mutation.Secret(); ok { + _spec.SetField(envvar.FieldSecret, field.TypeBool, value) + _node.Secret = value + } + if value, ok := _c.mutation.CreatedBy(); ok { + _spec.SetField(envvar.FieldCreatedBy, field.TypeString, value) + _node.CreatedBy = value + } + if value, ok := _c.mutation.Created(); ok { + _spec.SetField(envvar.FieldCreated, field.TypeTime, value) + _node.Created = value + } + if value, ok := _c.mutation.Updated(); ok { + _spec.SetField(envvar.FieldUpdated, field.TypeTime, value) + _node.Updated = value + } + return _node, _spec +} + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.EnvVar.Create(). +// SetKey(v). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.EnvVarUpsert) { +// SetKey(v+v). +// }). +// Exec(ctx) +func (_c *EnvVarCreate) OnConflict(opts ...sql.ConflictOption) *EnvVarUpsertOne { + _c.conflict = opts + return &EnvVarUpsertOne{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.EnvVar.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *EnvVarCreate) OnConflictColumns(columns ...string) *EnvVarUpsertOne { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &EnvVarUpsertOne{ + create: _c, + } +} + +type ( + // EnvVarUpsertOne is the builder for "upsert"-ing + // one EnvVar node. + EnvVarUpsertOne struct { + create *EnvVarCreate + } + + // EnvVarUpsert is the "OnConflict" setter. + EnvVarUpsert struct { + *sql.UpdateSet + } +) + +// SetKey sets the "key" field. +func (u *EnvVarUpsert) SetKey(v string) *EnvVarUpsert { + u.Set(envvar.FieldKey, v) + return u +} + +// UpdateKey sets the "key" field to the value that was provided on create. +func (u *EnvVarUpsert) UpdateKey() *EnvVarUpsert { + u.SetExcluded(envvar.FieldKey) + return u +} + +// SetValue sets the "value" field. +func (u *EnvVarUpsert) SetValue(v string) *EnvVarUpsert { + u.Set(envvar.FieldValue, v) + return u +} + +// UpdateValue sets the "value" field to the value that was provided on create. +func (u *EnvVarUpsert) UpdateValue() *EnvVarUpsert { + u.SetExcluded(envvar.FieldValue) + return u +} + +// SetScope sets the "scope" field. +func (u *EnvVarUpsert) SetScope(v string) *EnvVarUpsert { + u.Set(envvar.FieldScope, v) + return u +} + +// UpdateScope sets the "scope" field to the value that was provided on create. +func (u *EnvVarUpsert) UpdateScope() *EnvVarUpsert { + u.SetExcluded(envvar.FieldScope) + return u +} + +// SetScopeID sets the "scope_id" field. +func (u *EnvVarUpsert) SetScopeID(v string) *EnvVarUpsert { + u.Set(envvar.FieldScopeID, v) + return u +} + +// UpdateScopeID sets the "scope_id" field to the value that was provided on create. +func (u *EnvVarUpsert) UpdateScopeID() *EnvVarUpsert { + u.SetExcluded(envvar.FieldScopeID) + return u +} + +// SetDescription sets the "description" field. +func (u *EnvVarUpsert) SetDescription(v string) *EnvVarUpsert { + u.Set(envvar.FieldDescription, v) + return u +} + +// UpdateDescription sets the "description" field to the value that was provided on create. +func (u *EnvVarUpsert) UpdateDescription() *EnvVarUpsert { + u.SetExcluded(envvar.FieldDescription) + return u +} + +// ClearDescription clears the value of the "description" field. +func (u *EnvVarUpsert) ClearDescription() *EnvVarUpsert { + u.SetNull(envvar.FieldDescription) + return u +} + +// SetSensitive sets the "sensitive" field. +func (u *EnvVarUpsert) SetSensitive(v bool) *EnvVarUpsert { + u.Set(envvar.FieldSensitive, v) + return u +} + +// UpdateSensitive sets the "sensitive" field to the value that was provided on create. +func (u *EnvVarUpsert) UpdateSensitive() *EnvVarUpsert { + u.SetExcluded(envvar.FieldSensitive) + return u +} + +// SetInjectionMode sets the "injection_mode" field. +func (u *EnvVarUpsert) SetInjectionMode(v envvar.InjectionMode) *EnvVarUpsert { + u.Set(envvar.FieldInjectionMode, v) + return u +} + +// UpdateInjectionMode sets the "injection_mode" field to the value that was provided on create. +func (u *EnvVarUpsert) UpdateInjectionMode() *EnvVarUpsert { + u.SetExcluded(envvar.FieldInjectionMode) + return u +} + +// SetSecret sets the "secret" field. +func (u *EnvVarUpsert) SetSecret(v bool) *EnvVarUpsert { + u.Set(envvar.FieldSecret, v) + return u +} + +// UpdateSecret sets the "secret" field to the value that was provided on create. +func (u *EnvVarUpsert) UpdateSecret() *EnvVarUpsert { + u.SetExcluded(envvar.FieldSecret) + return u +} + +// SetCreatedBy sets the "created_by" field. +func (u *EnvVarUpsert) SetCreatedBy(v string) *EnvVarUpsert { + u.Set(envvar.FieldCreatedBy, v) + return u +} + +// UpdateCreatedBy sets the "created_by" field to the value that was provided on create. +func (u *EnvVarUpsert) UpdateCreatedBy() *EnvVarUpsert { + u.SetExcluded(envvar.FieldCreatedBy) + return u +} + +// ClearCreatedBy clears the value of the "created_by" field. +func (u *EnvVarUpsert) ClearCreatedBy() *EnvVarUpsert { + u.SetNull(envvar.FieldCreatedBy) + return u +} + +// SetUpdated sets the "updated" field. +func (u *EnvVarUpsert) SetUpdated(v time.Time) *EnvVarUpsert { + u.Set(envvar.FieldUpdated, v) + return u +} + +// UpdateUpdated sets the "updated" field to the value that was provided on create. +func (u *EnvVarUpsert) UpdateUpdated() *EnvVarUpsert { + u.SetExcluded(envvar.FieldUpdated) + return u +} + +// UpdateNewValues updates the mutable fields using the new values that were set on create except the ID field. +// Using this option is equivalent to using: +// +// client.EnvVar.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(envvar.FieldID) +// }), +// ). +// Exec(ctx) +func (u *EnvVarUpsertOne) UpdateNewValues() *EnvVarUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + if _, exists := u.create.mutation.ID(); exists { + s.SetIgnore(envvar.FieldID) + } + if _, exists := u.create.mutation.Created(); exists { + s.SetIgnore(envvar.FieldCreated) + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.EnvVar.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *EnvVarUpsertOne) Ignore() *EnvVarUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *EnvVarUpsertOne) DoNothing() *EnvVarUpsertOne { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the EnvVarCreate.OnConflict +// documentation for more info. +func (u *EnvVarUpsertOne) Update(set func(*EnvVarUpsert)) *EnvVarUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&EnvVarUpsert{UpdateSet: update}) + })) + return u +} + +// SetKey sets the "key" field. +func (u *EnvVarUpsertOne) SetKey(v string) *EnvVarUpsertOne { + return u.Update(func(s *EnvVarUpsert) { + s.SetKey(v) + }) +} + +// UpdateKey sets the "key" field to the value that was provided on create. +func (u *EnvVarUpsertOne) UpdateKey() *EnvVarUpsertOne { + return u.Update(func(s *EnvVarUpsert) { + s.UpdateKey() + }) +} + +// SetValue sets the "value" field. +func (u *EnvVarUpsertOne) SetValue(v string) *EnvVarUpsertOne { + return u.Update(func(s *EnvVarUpsert) { + s.SetValue(v) + }) +} + +// UpdateValue sets the "value" field to the value that was provided on create. +func (u *EnvVarUpsertOne) UpdateValue() *EnvVarUpsertOne { + return u.Update(func(s *EnvVarUpsert) { + s.UpdateValue() + }) +} + +// SetScope sets the "scope" field. +func (u *EnvVarUpsertOne) SetScope(v string) *EnvVarUpsertOne { + return u.Update(func(s *EnvVarUpsert) { + s.SetScope(v) + }) +} + +// UpdateScope sets the "scope" field to the value that was provided on create. +func (u *EnvVarUpsertOne) UpdateScope() *EnvVarUpsertOne { + return u.Update(func(s *EnvVarUpsert) { + s.UpdateScope() + }) +} + +// SetScopeID sets the "scope_id" field. +func (u *EnvVarUpsertOne) SetScopeID(v string) *EnvVarUpsertOne { + return u.Update(func(s *EnvVarUpsert) { + s.SetScopeID(v) + }) +} + +// UpdateScopeID sets the "scope_id" field to the value that was provided on create. +func (u *EnvVarUpsertOne) UpdateScopeID() *EnvVarUpsertOne { + return u.Update(func(s *EnvVarUpsert) { + s.UpdateScopeID() + }) +} + +// SetDescription sets the "description" field. +func (u *EnvVarUpsertOne) SetDescription(v string) *EnvVarUpsertOne { + return u.Update(func(s *EnvVarUpsert) { + s.SetDescription(v) + }) +} + +// UpdateDescription sets the "description" field to the value that was provided on create. +func (u *EnvVarUpsertOne) UpdateDescription() *EnvVarUpsertOne { + return u.Update(func(s *EnvVarUpsert) { + s.UpdateDescription() + }) +} + +// ClearDescription clears the value of the "description" field. +func (u *EnvVarUpsertOne) ClearDescription() *EnvVarUpsertOne { + return u.Update(func(s *EnvVarUpsert) { + s.ClearDescription() + }) +} + +// SetSensitive sets the "sensitive" field. +func (u *EnvVarUpsertOne) SetSensitive(v bool) *EnvVarUpsertOne { + return u.Update(func(s *EnvVarUpsert) { + s.SetSensitive(v) + }) +} + +// UpdateSensitive sets the "sensitive" field to the value that was provided on create. +func (u *EnvVarUpsertOne) UpdateSensitive() *EnvVarUpsertOne { + return u.Update(func(s *EnvVarUpsert) { + s.UpdateSensitive() + }) +} + +// SetInjectionMode sets the "injection_mode" field. +func (u *EnvVarUpsertOne) SetInjectionMode(v envvar.InjectionMode) *EnvVarUpsertOne { + return u.Update(func(s *EnvVarUpsert) { + s.SetInjectionMode(v) + }) +} + +// UpdateInjectionMode sets the "injection_mode" field to the value that was provided on create. +func (u *EnvVarUpsertOne) UpdateInjectionMode() *EnvVarUpsertOne { + return u.Update(func(s *EnvVarUpsert) { + s.UpdateInjectionMode() + }) +} + +// SetSecret sets the "secret" field. +func (u *EnvVarUpsertOne) SetSecret(v bool) *EnvVarUpsertOne { + return u.Update(func(s *EnvVarUpsert) { + s.SetSecret(v) + }) +} + +// UpdateSecret sets the "secret" field to the value that was provided on create. +func (u *EnvVarUpsertOne) UpdateSecret() *EnvVarUpsertOne { + return u.Update(func(s *EnvVarUpsert) { + s.UpdateSecret() + }) +} + +// SetCreatedBy sets the "created_by" field. +func (u *EnvVarUpsertOne) SetCreatedBy(v string) *EnvVarUpsertOne { + return u.Update(func(s *EnvVarUpsert) { + s.SetCreatedBy(v) + }) +} + +// UpdateCreatedBy sets the "created_by" field to the value that was provided on create. +func (u *EnvVarUpsertOne) UpdateCreatedBy() *EnvVarUpsertOne { + return u.Update(func(s *EnvVarUpsert) { + s.UpdateCreatedBy() + }) +} + +// ClearCreatedBy clears the value of the "created_by" field. +func (u *EnvVarUpsertOne) ClearCreatedBy() *EnvVarUpsertOne { + return u.Update(func(s *EnvVarUpsert) { + s.ClearCreatedBy() + }) +} + +// SetUpdated sets the "updated" field. +func (u *EnvVarUpsertOne) SetUpdated(v time.Time) *EnvVarUpsertOne { + return u.Update(func(s *EnvVarUpsert) { + s.SetUpdated(v) + }) +} + +// UpdateUpdated sets the "updated" field to the value that was provided on create. +func (u *EnvVarUpsertOne) UpdateUpdated() *EnvVarUpsertOne { + return u.Update(func(s *EnvVarUpsert) { + s.UpdateUpdated() + }) +} + +// Exec executes the query. +func (u *EnvVarUpsertOne) Exec(ctx context.Context) error { + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for EnvVarCreate.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *EnvVarUpsertOne) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} + +// Exec executes the UPSERT query and returns the inserted/updated ID. +func (u *EnvVarUpsertOne) ID(ctx context.Context) (id uuid.UUID, err error) { + if u.create.driver.Dialect() == dialect.MySQL { + // In case of "ON CONFLICT", there is no way to get back non-numeric ID + // fields from the database since MySQL does not support the RETURNING clause. + return id, errors.New("ent: EnvVarUpsertOne.ID is not supported by MySQL driver. Use EnvVarUpsertOne.Exec instead") + } + node, err := u.create.Save(ctx) + if err != nil { + return id, err + } + return node.ID, nil +} + +// IDX is like ID, but panics if an error occurs. +func (u *EnvVarUpsertOne) IDX(ctx context.Context) uuid.UUID { + id, err := u.ID(ctx) + if err != nil { + panic(err) + } + return id +} + +// EnvVarCreateBulk is the builder for creating many EnvVar entities in bulk. +type EnvVarCreateBulk struct { + config + err error + builders []*EnvVarCreate + conflict []sql.ConflictOption +} + +// Save creates the EnvVar entities in the database. +func (_c *EnvVarCreateBulk) Save(ctx context.Context) ([]*EnvVar, error) { + if _c.err != nil { + return nil, _c.err + } + specs := make([]*sqlgraph.CreateSpec, len(_c.builders)) + nodes := make([]*EnvVar, len(_c.builders)) + mutators := make([]Mutator, len(_c.builders)) + for i := range _c.builders { + func(i int, root context.Context) { + builder := _c.builders[i] + builder.defaults() + var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { + mutation, ok := m.(*EnvVarMutation) + if !ok { + return nil, fmt.Errorf("unexpected mutation type %T", m) + } + if err := builder.check(); err != nil { + return nil, err + } + builder.mutation = mutation + var err error + nodes[i], specs[i] = builder.createSpec() + if i < len(mutators)-1 { + _, err = mutators[i+1].Mutate(root, _c.builders[i+1].mutation) + } else { + spec := &sqlgraph.BatchCreateSpec{Nodes: specs} + spec.OnConflict = _c.conflict + // Invoke the actual operation on the latest mutation in the chain. + if err = sqlgraph.BatchCreate(ctx, _c.driver, spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + } + } + if err != nil { + return nil, err + } + mutation.id = &nodes[i].ID + mutation.done = true + return nodes[i], nil + }) + for i := len(builder.hooks) - 1; i >= 0; i-- { + mut = builder.hooks[i](mut) + } + mutators[i] = mut + }(i, ctx) + } + if len(mutators) > 0 { + if _, err := mutators[0].Mutate(ctx, _c.builders[0].mutation); err != nil { + return nil, err + } + } + return nodes, nil +} + +// SaveX is like Save, but panics if an error occurs. +func (_c *EnvVarCreateBulk) SaveX(ctx context.Context) []*EnvVar { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *EnvVarCreateBulk) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *EnvVarCreateBulk) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.EnvVar.CreateBulk(builders...). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.EnvVarUpsert) { +// SetKey(v+v). +// }). +// Exec(ctx) +func (_c *EnvVarCreateBulk) OnConflict(opts ...sql.ConflictOption) *EnvVarUpsertBulk { + _c.conflict = opts + return &EnvVarUpsertBulk{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.EnvVar.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *EnvVarCreateBulk) OnConflictColumns(columns ...string) *EnvVarUpsertBulk { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &EnvVarUpsertBulk{ + create: _c, + } +} + +// EnvVarUpsertBulk is the builder for "upsert"-ing +// a bulk of EnvVar nodes. +type EnvVarUpsertBulk struct { + create *EnvVarCreateBulk +} + +// UpdateNewValues updates the mutable fields using the new values that +// were set on create. Using this option is equivalent to using: +// +// client.EnvVar.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(envvar.FieldID) +// }), +// ). +// Exec(ctx) +func (u *EnvVarUpsertBulk) UpdateNewValues() *EnvVarUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + for _, b := range u.create.builders { + if _, exists := b.mutation.ID(); exists { + s.SetIgnore(envvar.FieldID) + } + if _, exists := b.mutation.Created(); exists { + s.SetIgnore(envvar.FieldCreated) + } + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.EnvVar.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *EnvVarUpsertBulk) Ignore() *EnvVarUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *EnvVarUpsertBulk) DoNothing() *EnvVarUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the EnvVarCreateBulk.OnConflict +// documentation for more info. +func (u *EnvVarUpsertBulk) Update(set func(*EnvVarUpsert)) *EnvVarUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&EnvVarUpsert{UpdateSet: update}) + })) + return u +} + +// SetKey sets the "key" field. +func (u *EnvVarUpsertBulk) SetKey(v string) *EnvVarUpsertBulk { + return u.Update(func(s *EnvVarUpsert) { + s.SetKey(v) + }) +} + +// UpdateKey sets the "key" field to the value that was provided on create. +func (u *EnvVarUpsertBulk) UpdateKey() *EnvVarUpsertBulk { + return u.Update(func(s *EnvVarUpsert) { + s.UpdateKey() + }) +} + +// SetValue sets the "value" field. +func (u *EnvVarUpsertBulk) SetValue(v string) *EnvVarUpsertBulk { + return u.Update(func(s *EnvVarUpsert) { + s.SetValue(v) + }) +} + +// UpdateValue sets the "value" field to the value that was provided on create. +func (u *EnvVarUpsertBulk) UpdateValue() *EnvVarUpsertBulk { + return u.Update(func(s *EnvVarUpsert) { + s.UpdateValue() + }) +} + +// SetScope sets the "scope" field. +func (u *EnvVarUpsertBulk) SetScope(v string) *EnvVarUpsertBulk { + return u.Update(func(s *EnvVarUpsert) { + s.SetScope(v) + }) +} + +// UpdateScope sets the "scope" field to the value that was provided on create. +func (u *EnvVarUpsertBulk) UpdateScope() *EnvVarUpsertBulk { + return u.Update(func(s *EnvVarUpsert) { + s.UpdateScope() + }) +} + +// SetScopeID sets the "scope_id" field. +func (u *EnvVarUpsertBulk) SetScopeID(v string) *EnvVarUpsertBulk { + return u.Update(func(s *EnvVarUpsert) { + s.SetScopeID(v) + }) +} + +// UpdateScopeID sets the "scope_id" field to the value that was provided on create. +func (u *EnvVarUpsertBulk) UpdateScopeID() *EnvVarUpsertBulk { + return u.Update(func(s *EnvVarUpsert) { + s.UpdateScopeID() + }) +} + +// SetDescription sets the "description" field. +func (u *EnvVarUpsertBulk) SetDescription(v string) *EnvVarUpsertBulk { + return u.Update(func(s *EnvVarUpsert) { + s.SetDescription(v) + }) +} + +// UpdateDescription sets the "description" field to the value that was provided on create. +func (u *EnvVarUpsertBulk) UpdateDescription() *EnvVarUpsertBulk { + return u.Update(func(s *EnvVarUpsert) { + s.UpdateDescription() + }) +} + +// ClearDescription clears the value of the "description" field. +func (u *EnvVarUpsertBulk) ClearDescription() *EnvVarUpsertBulk { + return u.Update(func(s *EnvVarUpsert) { + s.ClearDescription() + }) +} + +// SetSensitive sets the "sensitive" field. +func (u *EnvVarUpsertBulk) SetSensitive(v bool) *EnvVarUpsertBulk { + return u.Update(func(s *EnvVarUpsert) { + s.SetSensitive(v) + }) +} + +// UpdateSensitive sets the "sensitive" field to the value that was provided on create. +func (u *EnvVarUpsertBulk) UpdateSensitive() *EnvVarUpsertBulk { + return u.Update(func(s *EnvVarUpsert) { + s.UpdateSensitive() + }) +} + +// SetInjectionMode sets the "injection_mode" field. +func (u *EnvVarUpsertBulk) SetInjectionMode(v envvar.InjectionMode) *EnvVarUpsertBulk { + return u.Update(func(s *EnvVarUpsert) { + s.SetInjectionMode(v) + }) +} + +// UpdateInjectionMode sets the "injection_mode" field to the value that was provided on create. +func (u *EnvVarUpsertBulk) UpdateInjectionMode() *EnvVarUpsertBulk { + return u.Update(func(s *EnvVarUpsert) { + s.UpdateInjectionMode() + }) +} + +// SetSecret sets the "secret" field. +func (u *EnvVarUpsertBulk) SetSecret(v bool) *EnvVarUpsertBulk { + return u.Update(func(s *EnvVarUpsert) { + s.SetSecret(v) + }) +} + +// UpdateSecret sets the "secret" field to the value that was provided on create. +func (u *EnvVarUpsertBulk) UpdateSecret() *EnvVarUpsertBulk { + return u.Update(func(s *EnvVarUpsert) { + s.UpdateSecret() + }) +} + +// SetCreatedBy sets the "created_by" field. +func (u *EnvVarUpsertBulk) SetCreatedBy(v string) *EnvVarUpsertBulk { + return u.Update(func(s *EnvVarUpsert) { + s.SetCreatedBy(v) + }) +} + +// UpdateCreatedBy sets the "created_by" field to the value that was provided on create. +func (u *EnvVarUpsertBulk) UpdateCreatedBy() *EnvVarUpsertBulk { + return u.Update(func(s *EnvVarUpsert) { + s.UpdateCreatedBy() + }) +} + +// ClearCreatedBy clears the value of the "created_by" field. +func (u *EnvVarUpsertBulk) ClearCreatedBy() *EnvVarUpsertBulk { + return u.Update(func(s *EnvVarUpsert) { + s.ClearCreatedBy() + }) +} + +// SetUpdated sets the "updated" field. +func (u *EnvVarUpsertBulk) SetUpdated(v time.Time) *EnvVarUpsertBulk { + return u.Update(func(s *EnvVarUpsert) { + s.SetUpdated(v) + }) +} + +// UpdateUpdated sets the "updated" field to the value that was provided on create. +func (u *EnvVarUpsertBulk) UpdateUpdated() *EnvVarUpsertBulk { + return u.Update(func(s *EnvVarUpsert) { + s.UpdateUpdated() + }) +} + +// Exec executes the query. +func (u *EnvVarUpsertBulk) Exec(ctx context.Context) error { + if u.create.err != nil { + return u.create.err + } + for i, b := range u.create.builders { + if len(b.conflict) != 0 { + return fmt.Errorf("ent: OnConflict was set for builder %d. Set it on the EnvVarCreateBulk instead", i) + } + } + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for EnvVarCreateBulk.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *EnvVarUpsertBulk) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/pkg/ent/envvar_delete.go b/pkg/ent/envvar_delete.go new file mode 100644 index 000000000..5e5c80afa --- /dev/null +++ b/pkg/ent/envvar_delete.go @@ -0,0 +1,88 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/envvar" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" +) + +// EnvVarDelete is the builder for deleting a EnvVar entity. +type EnvVarDelete struct { + config + hooks []Hook + mutation *EnvVarMutation +} + +// Where appends a list predicates to the EnvVarDelete builder. +func (_d *EnvVarDelete) Where(ps ...predicate.EnvVar) *EnvVarDelete { + _d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (_d *EnvVarDelete) Exec(ctx context.Context) (int, error) { + return withHooks(ctx, _d.sqlExec, _d.mutation, _d.hooks) +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *EnvVarDelete) ExecX(ctx context.Context) int { + n, err := _d.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (_d *EnvVarDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(envvar.Table, sqlgraph.NewFieldSpec(envvar.FieldID, field.TypeUUID)) + if ps := _d.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + affected, err := sqlgraph.DeleteNodes(ctx, _d.driver, _spec) + if err != nil && sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + _d.mutation.done = true + return affected, err +} + +// EnvVarDeleteOne is the builder for deleting a single EnvVar entity. +type EnvVarDeleteOne struct { + _d *EnvVarDelete +} + +// Where appends a list predicates to the EnvVarDelete builder. +func (_d *EnvVarDeleteOne) Where(ps ...predicate.EnvVar) *EnvVarDeleteOne { + _d._d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query. +func (_d *EnvVarDeleteOne) Exec(ctx context.Context) error { + n, err := _d._d.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{envvar.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *EnvVarDeleteOne) ExecX(ctx context.Context) { + if err := _d.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/pkg/ent/envvar_query.go b/pkg/ent/envvar_query.go new file mode 100644 index 000000000..d155d76b3 --- /dev/null +++ b/pkg/ent/envvar_query.go @@ -0,0 +1,565 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "fmt" + "math" + + "entgo.io/ent" + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/envvar" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/google/uuid" +) + +// EnvVarQuery is the builder for querying EnvVar entities. +type EnvVarQuery struct { + config + ctx *QueryContext + order []envvar.OrderOption + inters []Interceptor + predicates []predicate.EnvVar + modifiers []func(*sql.Selector) + // intermediate query (i.e. traversal path). + sql *sql.Selector + path func(context.Context) (*sql.Selector, error) +} + +// Where adds a new predicate for the EnvVarQuery builder. +func (_q *EnvVarQuery) Where(ps ...predicate.EnvVar) *EnvVarQuery { + _q.predicates = append(_q.predicates, ps...) + return _q +} + +// Limit the number of records to be returned by this query. +func (_q *EnvVarQuery) Limit(limit int) *EnvVarQuery { + _q.ctx.Limit = &limit + return _q +} + +// Offset to start from. +func (_q *EnvVarQuery) Offset(offset int) *EnvVarQuery { + _q.ctx.Offset = &offset + return _q +} + +// Unique configures the query builder to filter duplicate records on query. +// By default, unique is set to true, and can be disabled using this method. +func (_q *EnvVarQuery) Unique(unique bool) *EnvVarQuery { + _q.ctx.Unique = &unique + return _q +} + +// Order specifies how the records should be ordered. +func (_q *EnvVarQuery) Order(o ...envvar.OrderOption) *EnvVarQuery { + _q.order = append(_q.order, o...) + return _q +} + +// First returns the first EnvVar entity from the query. +// Returns a *NotFoundError when no EnvVar was found. +func (_q *EnvVarQuery) First(ctx context.Context) (*EnvVar, error) { + nodes, err := _q.Limit(1).All(setContextOp(ctx, _q.ctx, ent.OpQueryFirst)) + if err != nil { + return nil, err + } + if len(nodes) == 0 { + return nil, &NotFoundError{envvar.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (_q *EnvVarQuery) FirstX(ctx context.Context) *EnvVar { + node, err := _q.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// FirstID returns the first EnvVar ID from the query. +// Returns a *NotFoundError when no EnvVar ID was found. +func (_q *EnvVarQuery) FirstID(ctx context.Context) (id uuid.UUID, err error) { + var ids []uuid.UUID + if ids, err = _q.Limit(1).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryFirstID)); err != nil { + return + } + if len(ids) == 0 { + err = &NotFoundError{envvar.Label} + return + } + return ids[0], nil +} + +// FirstIDX is like FirstID, but panics if an error occurs. +func (_q *EnvVarQuery) FirstIDX(ctx context.Context) uuid.UUID { + id, err := _q.FirstID(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return id +} + +// Only returns a single EnvVar entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one EnvVar entity is found. +// Returns a *NotFoundError when no EnvVar entities are found. +func (_q *EnvVarQuery) Only(ctx context.Context) (*EnvVar, error) { + nodes, err := _q.Limit(2).All(setContextOp(ctx, _q.ctx, ent.OpQueryOnly)) + if err != nil { + return nil, err + } + switch len(nodes) { + case 1: + return nodes[0], nil + case 0: + return nil, &NotFoundError{envvar.Label} + default: + return nil, &NotSingularError{envvar.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (_q *EnvVarQuery) OnlyX(ctx context.Context) *EnvVar { + node, err := _q.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// OnlyID is like Only, but returns the only EnvVar ID in the query. +// Returns a *NotSingularError when more than one EnvVar ID is found. +// Returns a *NotFoundError when no entities are found. +func (_q *EnvVarQuery) OnlyID(ctx context.Context) (id uuid.UUID, err error) { + var ids []uuid.UUID + if ids, err = _q.Limit(2).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryOnlyID)); err != nil { + return + } + switch len(ids) { + case 1: + id = ids[0] + case 0: + err = &NotFoundError{envvar.Label} + default: + err = &NotSingularError{envvar.Label} + } + return +} + +// OnlyIDX is like OnlyID, but panics if an error occurs. +func (_q *EnvVarQuery) OnlyIDX(ctx context.Context) uuid.UUID { + id, err := _q.OnlyID(ctx) + if err != nil { + panic(err) + } + return id +} + +// All executes the query and returns a list of EnvVars. +func (_q *EnvVarQuery) All(ctx context.Context) ([]*EnvVar, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryAll) + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*EnvVar, *EnvVarQuery]() + return withInterceptors[[]*EnvVar](ctx, _q, qr, _q.inters) +} + +// AllX is like All, but panics if an error occurs. +func (_q *EnvVarQuery) AllX(ctx context.Context) []*EnvVar { + nodes, err := _q.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// IDs executes the query and returns a list of EnvVar IDs. +func (_q *EnvVarQuery) IDs(ctx context.Context) (ids []uuid.UUID, err error) { + if _q.ctx.Unique == nil && _q.path != nil { + _q.Unique(true) + } + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryIDs) + if err = _q.Select(envvar.FieldID).Scan(ctx, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// IDsX is like IDs, but panics if an error occurs. +func (_q *EnvVarQuery) IDsX(ctx context.Context) []uuid.UUID { + ids, err := _q.IDs(ctx) + if err != nil { + panic(err) + } + return ids +} + +// Count returns the count of the given query. +func (_q *EnvVarQuery) Count(ctx context.Context) (int, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryCount) + if err := _q.prepareQuery(ctx); err != nil { + return 0, err + } + return withInterceptors[int](ctx, _q, querierCount[*EnvVarQuery](), _q.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (_q *EnvVarQuery) CountX(ctx context.Context) int { + count, err := _q.Count(ctx) + if err != nil { + panic(err) + } + return count +} + +// Exist returns true if the query has elements in the graph. +func (_q *EnvVarQuery) Exist(ctx context.Context) (bool, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryExist) + switch _, err := _q.FirstID(ctx); { + case IsNotFound(err): + return false, nil + case err != nil: + return false, fmt.Errorf("ent: check existence: %w", err) + default: + return true, nil + } +} + +// ExistX is like Exist, but panics if an error occurs. +func (_q *EnvVarQuery) ExistX(ctx context.Context) bool { + exist, err := _q.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the EnvVarQuery builder, including all associated steps. It can be +// used to prepare common query builders and use them differently after the clone is made. +func (_q *EnvVarQuery) Clone() *EnvVarQuery { + if _q == nil { + return nil + } + return &EnvVarQuery{ + config: _q.config, + ctx: _q.ctx.Clone(), + order: append([]envvar.OrderOption{}, _q.order...), + inters: append([]Interceptor{}, _q.inters...), + predicates: append([]predicate.EnvVar{}, _q.predicates...), + // clone intermediate query. + sql: _q.sql.Clone(), + path: _q.path, + } +} + +// GroupBy is used to group vertices by one or more fields/columns. +// It is often used with aggregate functions, like: count, max, mean, min, sum. +// +// Example: +// +// var v []struct { +// Key string `json:"key,omitempty"` +// Count int `json:"count,omitempty"` +// } +// +// client.EnvVar.Query(). +// GroupBy(envvar.FieldKey). +// Aggregate(ent.Count()). +// Scan(ctx, &v) +func (_q *EnvVarQuery) GroupBy(field string, fields ...string) *EnvVarGroupBy { + _q.ctx.Fields = append([]string{field}, fields...) + grbuild := &EnvVarGroupBy{build: _q} + grbuild.flds = &_q.ctx.Fields + grbuild.label = envvar.Label + grbuild.scan = grbuild.Scan + return grbuild +} + +// Select allows the selection one or more fields/columns for the given query, +// instead of selecting all fields in the entity. +// +// Example: +// +// var v []struct { +// Key string `json:"key,omitempty"` +// } +// +// client.EnvVar.Query(). +// Select(envvar.FieldKey). +// Scan(ctx, &v) +func (_q *EnvVarQuery) Select(fields ...string) *EnvVarSelect { + _q.ctx.Fields = append(_q.ctx.Fields, fields...) + sbuild := &EnvVarSelect{EnvVarQuery: _q} + sbuild.label = envvar.Label + sbuild.flds, sbuild.scan = &_q.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a EnvVarSelect configured with the given aggregations. +func (_q *EnvVarQuery) Aggregate(fns ...AggregateFunc) *EnvVarSelect { + return _q.Select().Aggregate(fns...) +} + +func (_q *EnvVarQuery) prepareQuery(ctx context.Context) error { + for _, inter := range _q.inters { + if inter == nil { + return fmt.Errorf("ent: uninitialized interceptor (forgotten import ent/runtime?)") + } + if trv, ok := inter.(Traverser); ok { + if err := trv.Traverse(ctx, _q); err != nil { + return err + } + } + } + for _, f := range _q.ctx.Fields { + if !envvar.ValidColumn(f) { + return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + } + if _q.path != nil { + prev, err := _q.path(ctx) + if err != nil { + return err + } + _q.sql = prev + } + return nil +} + +func (_q *EnvVarQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*EnvVar, error) { + var ( + nodes = []*EnvVar{} + _spec = _q.querySpec() + ) + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*EnvVar).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &EnvVar{config: _q.config} + nodes = append(nodes, node) + return node.assignValues(columns, values) + } + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } + for i := range hooks { + hooks[i](ctx, _spec) + } + if err := sqlgraph.QueryNodes(ctx, _q.driver, _spec); err != nil { + return nil, err + } + if len(nodes) == 0 { + return nodes, nil + } + return nodes, nil +} + +func (_q *EnvVarQuery) sqlCount(ctx context.Context) (int, error) { + _spec := _q.querySpec() + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } + _spec.Node.Columns = _q.ctx.Fields + if len(_q.ctx.Fields) > 0 { + _spec.Unique = _q.ctx.Unique != nil && *_q.ctx.Unique + } + return sqlgraph.CountNodes(ctx, _q.driver, _spec) +} + +func (_q *EnvVarQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(envvar.Table, envvar.Columns, sqlgraph.NewFieldSpec(envvar.FieldID, field.TypeUUID)) + _spec.From = _q.sql + if unique := _q.ctx.Unique; unique != nil { + _spec.Unique = *unique + } else if _q.path != nil { + _spec.Unique = true + } + if fields := _q.ctx.Fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, envvar.FieldID) + for i := range fields { + if fields[i] != envvar.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, fields[i]) + } + } + } + if ps := _q.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if limit := _q.ctx.Limit; limit != nil { + _spec.Limit = *limit + } + if offset := _q.ctx.Offset; offset != nil { + _spec.Offset = *offset + } + if ps := _q.order; len(ps) > 0 { + _spec.Order = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + return _spec +} + +func (_q *EnvVarQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(_q.driver.Dialect()) + t1 := builder.Table(envvar.Table) + columns := _q.ctx.Fields + if len(columns) == 0 { + columns = envvar.Columns + } + selector := builder.Select(t1.Columns(columns...)...).From(t1) + if _q.sql != nil { + selector = _q.sql + selector.Select(selector.Columns(columns...)...) + } + if _q.ctx.Unique != nil && *_q.ctx.Unique { + selector.Distinct() + } + for _, m := range _q.modifiers { + m(selector) + } + for _, p := range _q.predicates { + p(selector) + } + for _, p := range _q.order { + p(selector) + } + if offset := _q.ctx.Offset; offset != nil { + // limit is mandatory for offset clause. We start + // with default value, and override it below if needed. + selector.Offset(*offset).Limit(math.MaxInt32) + } + if limit := _q.ctx.Limit; limit != nil { + selector.Limit(*limit) + } + return selector +} + +// ForUpdate locks the selected rows against concurrent updates, and prevent them from being +// updated, deleted or "selected ... for update" by other sessions, until the transaction is +// either committed or rolled-back. +func (_q *EnvVarQuery) ForUpdate(opts ...sql.LockOption) *EnvVarQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForUpdate(opts...) + }) + return _q +} + +// ForShare behaves similarly to ForUpdate, except that it acquires a shared mode lock +// on any rows that are read. Other sessions can read the rows, but cannot modify them +// until your transaction commits. +func (_q *EnvVarQuery) ForShare(opts ...sql.LockOption) *EnvVarQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForShare(opts...) + }) + return _q +} + +// EnvVarGroupBy is the group-by builder for EnvVar entities. +type EnvVarGroupBy struct { + selector + build *EnvVarQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (_g *EnvVarGroupBy) Aggregate(fns ...AggregateFunc) *EnvVarGroupBy { + _g.fns = append(_g.fns, fns...) + return _g +} + +// Scan applies the selector query and scans the result into the given value. +func (_g *EnvVarGroupBy) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, _g.build.ctx, ent.OpQueryGroupBy) + if err := _g.build.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*EnvVarQuery, *EnvVarGroupBy](ctx, _g.build, _g, _g.build.inters, v) +} + +func (_g *EnvVarGroupBy) sqlScan(ctx context.Context, root *EnvVarQuery, v any) error { + selector := root.sqlQuery(ctx).Select() + aggregation := make([]string, 0, len(_g.fns)) + for _, fn := range _g.fns { + aggregation = append(aggregation, fn(selector)) + } + if len(selector.SelectedColumns()) == 0 { + columns := make([]string, 0, len(*_g.flds)+len(_g.fns)) + for _, f := range *_g.flds { + columns = append(columns, selector.C(f)) + } + columns = append(columns, aggregation...) + selector.Select(columns...) + } + selector.GroupBy(selector.Columns(*_g.flds...)...) + if err := selector.Err(); err != nil { + return err + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := _g.build.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} + +// EnvVarSelect is the builder for selecting fields of EnvVar entities. +type EnvVarSelect struct { + *EnvVarQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (_s *EnvVarSelect) Aggregate(fns ...AggregateFunc) *EnvVarSelect { + _s.fns = append(_s.fns, fns...) + return _s +} + +// Scan applies the selector query and scans the result into the given value. +func (_s *EnvVarSelect) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, _s.ctx, ent.OpQuerySelect) + if err := _s.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*EnvVarQuery, *EnvVarSelect](ctx, _s.EnvVarQuery, _s, _s.inters, v) +} + +func (_s *EnvVarSelect) sqlScan(ctx context.Context, root *EnvVarQuery, v any) error { + selector := root.sqlQuery(ctx) + aggregation := make([]string, 0, len(_s.fns)) + for _, fn := range _s.fns { + aggregation = append(aggregation, fn(selector)) + } + switch n := len(*_s.selector.flds); { + case n == 0 && len(aggregation) > 0: + selector.Select(aggregation...) + case n != 0 && len(aggregation) > 0: + selector.AppendSelect(aggregation...) + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := _s.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} diff --git a/pkg/ent/envvar_update.go b/pkg/ent/envvar_update.go new file mode 100644 index 000000000..200e65f66 --- /dev/null +++ b/pkg/ent/envvar_update.go @@ -0,0 +1,600 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/envvar" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" +) + +// EnvVarUpdate is the builder for updating EnvVar entities. +type EnvVarUpdate struct { + config + hooks []Hook + mutation *EnvVarMutation +} + +// Where appends a list predicates to the EnvVarUpdate builder. +func (_u *EnvVarUpdate) Where(ps ...predicate.EnvVar) *EnvVarUpdate { + _u.mutation.Where(ps...) + return _u +} + +// SetKey sets the "key" field. +func (_u *EnvVarUpdate) SetKey(v string) *EnvVarUpdate { + _u.mutation.SetKey(v) + return _u +} + +// SetNillableKey sets the "key" field if the given value is not nil. +func (_u *EnvVarUpdate) SetNillableKey(v *string) *EnvVarUpdate { + if v != nil { + _u.SetKey(*v) + } + return _u +} + +// SetValue sets the "value" field. +func (_u *EnvVarUpdate) SetValue(v string) *EnvVarUpdate { + _u.mutation.SetValue(v) + return _u +} + +// SetNillableValue sets the "value" field if the given value is not nil. +func (_u *EnvVarUpdate) SetNillableValue(v *string) *EnvVarUpdate { + if v != nil { + _u.SetValue(*v) + } + return _u +} + +// SetScope sets the "scope" field. +func (_u *EnvVarUpdate) SetScope(v string) *EnvVarUpdate { + _u.mutation.SetScope(v) + return _u +} + +// SetNillableScope sets the "scope" field if the given value is not nil. +func (_u *EnvVarUpdate) SetNillableScope(v *string) *EnvVarUpdate { + if v != nil { + _u.SetScope(*v) + } + return _u +} + +// SetScopeID sets the "scope_id" field. +func (_u *EnvVarUpdate) SetScopeID(v string) *EnvVarUpdate { + _u.mutation.SetScopeID(v) + return _u +} + +// SetNillableScopeID sets the "scope_id" field if the given value is not nil. +func (_u *EnvVarUpdate) SetNillableScopeID(v *string) *EnvVarUpdate { + if v != nil { + _u.SetScopeID(*v) + } + return _u +} + +// SetDescription sets the "description" field. +func (_u *EnvVarUpdate) SetDescription(v string) *EnvVarUpdate { + _u.mutation.SetDescription(v) + return _u +} + +// SetNillableDescription sets the "description" field if the given value is not nil. +func (_u *EnvVarUpdate) SetNillableDescription(v *string) *EnvVarUpdate { + if v != nil { + _u.SetDescription(*v) + } + return _u +} + +// ClearDescription clears the value of the "description" field. +func (_u *EnvVarUpdate) ClearDescription() *EnvVarUpdate { + _u.mutation.ClearDescription() + return _u +} + +// SetSensitive sets the "sensitive" field. +func (_u *EnvVarUpdate) SetSensitive(v bool) *EnvVarUpdate { + _u.mutation.SetSensitive(v) + return _u +} + +// SetNillableSensitive sets the "sensitive" field if the given value is not nil. +func (_u *EnvVarUpdate) SetNillableSensitive(v *bool) *EnvVarUpdate { + if v != nil { + _u.SetSensitive(*v) + } + return _u +} + +// SetInjectionMode sets the "injection_mode" field. +func (_u *EnvVarUpdate) SetInjectionMode(v envvar.InjectionMode) *EnvVarUpdate { + _u.mutation.SetInjectionMode(v) + return _u +} + +// SetNillableInjectionMode sets the "injection_mode" field if the given value is not nil. +func (_u *EnvVarUpdate) SetNillableInjectionMode(v *envvar.InjectionMode) *EnvVarUpdate { + if v != nil { + _u.SetInjectionMode(*v) + } + return _u +} + +// SetSecret sets the "secret" field. +func (_u *EnvVarUpdate) SetSecret(v bool) *EnvVarUpdate { + _u.mutation.SetSecret(v) + return _u +} + +// SetNillableSecret sets the "secret" field if the given value is not nil. +func (_u *EnvVarUpdate) SetNillableSecret(v *bool) *EnvVarUpdate { + if v != nil { + _u.SetSecret(*v) + } + return _u +} + +// SetCreatedBy sets the "created_by" field. +func (_u *EnvVarUpdate) SetCreatedBy(v string) *EnvVarUpdate { + _u.mutation.SetCreatedBy(v) + return _u +} + +// SetNillableCreatedBy sets the "created_by" field if the given value is not nil. +func (_u *EnvVarUpdate) SetNillableCreatedBy(v *string) *EnvVarUpdate { + if v != nil { + _u.SetCreatedBy(*v) + } + return _u +} + +// ClearCreatedBy clears the value of the "created_by" field. +func (_u *EnvVarUpdate) ClearCreatedBy() *EnvVarUpdate { + _u.mutation.ClearCreatedBy() + return _u +} + +// SetUpdated sets the "updated" field. +func (_u *EnvVarUpdate) SetUpdated(v time.Time) *EnvVarUpdate { + _u.mutation.SetUpdated(v) + return _u +} + +// Mutation returns the EnvVarMutation object of the builder. +func (_u *EnvVarUpdate) Mutation() *EnvVarMutation { + return _u.mutation +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (_u *EnvVarUpdate) Save(ctx context.Context) (int, error) { + _u.defaults() + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *EnvVarUpdate) SaveX(ctx context.Context) int { + affected, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (_u *EnvVarUpdate) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *EnvVarUpdate) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_u *EnvVarUpdate) defaults() { + if _, ok := _u.mutation.Updated(); !ok { + v := envvar.UpdateDefaultUpdated() + _u.mutation.SetUpdated(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_u *EnvVarUpdate) check() error { + if v, ok := _u.mutation.Key(); ok { + if err := envvar.KeyValidator(v); err != nil { + return &ValidationError{Name: "key", err: fmt.Errorf(`ent: validator failed for field "EnvVar.key": %w`, err)} + } + } + if v, ok := _u.mutation.Scope(); ok { + if err := envvar.ScopeValidator(v); err != nil { + return &ValidationError{Name: "scope", err: fmt.Errorf(`ent: validator failed for field "EnvVar.scope": %w`, err)} + } + } + if v, ok := _u.mutation.InjectionMode(); ok { + if err := envvar.InjectionModeValidator(v); err != nil { + return &ValidationError{Name: "injection_mode", err: fmt.Errorf(`ent: validator failed for field "EnvVar.injection_mode": %w`, err)} + } + } + return nil +} + +func (_u *EnvVarUpdate) sqlSave(ctx context.Context) (_node int, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(envvar.Table, envvar.Columns, sqlgraph.NewFieldSpec(envvar.FieldID, field.TypeUUID)) + if ps := _u.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := _u.mutation.Key(); ok { + _spec.SetField(envvar.FieldKey, field.TypeString, value) + } + if value, ok := _u.mutation.Value(); ok { + _spec.SetField(envvar.FieldValue, field.TypeString, value) + } + if value, ok := _u.mutation.Scope(); ok { + _spec.SetField(envvar.FieldScope, field.TypeString, value) + } + if value, ok := _u.mutation.ScopeID(); ok { + _spec.SetField(envvar.FieldScopeID, field.TypeString, value) + } + if value, ok := _u.mutation.Description(); ok { + _spec.SetField(envvar.FieldDescription, field.TypeString, value) + } + if _u.mutation.DescriptionCleared() { + _spec.ClearField(envvar.FieldDescription, field.TypeString) + } + if value, ok := _u.mutation.Sensitive(); ok { + _spec.SetField(envvar.FieldSensitive, field.TypeBool, value) + } + if value, ok := _u.mutation.InjectionMode(); ok { + _spec.SetField(envvar.FieldInjectionMode, field.TypeEnum, value) + } + if value, ok := _u.mutation.Secret(); ok { + _spec.SetField(envvar.FieldSecret, field.TypeBool, value) + } + if value, ok := _u.mutation.CreatedBy(); ok { + _spec.SetField(envvar.FieldCreatedBy, field.TypeString, value) + } + if _u.mutation.CreatedByCleared() { + _spec.ClearField(envvar.FieldCreatedBy, field.TypeString) + } + if value, ok := _u.mutation.Updated(); ok { + _spec.SetField(envvar.FieldUpdated, field.TypeTime, value) + } + if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{envvar.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + _u.mutation.done = true + return _node, nil +} + +// EnvVarUpdateOne is the builder for updating a single EnvVar entity. +type EnvVarUpdateOne struct { + config + fields []string + hooks []Hook + mutation *EnvVarMutation +} + +// SetKey sets the "key" field. +func (_u *EnvVarUpdateOne) SetKey(v string) *EnvVarUpdateOne { + _u.mutation.SetKey(v) + return _u +} + +// SetNillableKey sets the "key" field if the given value is not nil. +func (_u *EnvVarUpdateOne) SetNillableKey(v *string) *EnvVarUpdateOne { + if v != nil { + _u.SetKey(*v) + } + return _u +} + +// SetValue sets the "value" field. +func (_u *EnvVarUpdateOne) SetValue(v string) *EnvVarUpdateOne { + _u.mutation.SetValue(v) + return _u +} + +// SetNillableValue sets the "value" field if the given value is not nil. +func (_u *EnvVarUpdateOne) SetNillableValue(v *string) *EnvVarUpdateOne { + if v != nil { + _u.SetValue(*v) + } + return _u +} + +// SetScope sets the "scope" field. +func (_u *EnvVarUpdateOne) SetScope(v string) *EnvVarUpdateOne { + _u.mutation.SetScope(v) + return _u +} + +// SetNillableScope sets the "scope" field if the given value is not nil. +func (_u *EnvVarUpdateOne) SetNillableScope(v *string) *EnvVarUpdateOne { + if v != nil { + _u.SetScope(*v) + } + return _u +} + +// SetScopeID sets the "scope_id" field. +func (_u *EnvVarUpdateOne) SetScopeID(v string) *EnvVarUpdateOne { + _u.mutation.SetScopeID(v) + return _u +} + +// SetNillableScopeID sets the "scope_id" field if the given value is not nil. +func (_u *EnvVarUpdateOne) SetNillableScopeID(v *string) *EnvVarUpdateOne { + if v != nil { + _u.SetScopeID(*v) + } + return _u +} + +// SetDescription sets the "description" field. +func (_u *EnvVarUpdateOne) SetDescription(v string) *EnvVarUpdateOne { + _u.mutation.SetDescription(v) + return _u +} + +// SetNillableDescription sets the "description" field if the given value is not nil. +func (_u *EnvVarUpdateOne) SetNillableDescription(v *string) *EnvVarUpdateOne { + if v != nil { + _u.SetDescription(*v) + } + return _u +} + +// ClearDescription clears the value of the "description" field. +func (_u *EnvVarUpdateOne) ClearDescription() *EnvVarUpdateOne { + _u.mutation.ClearDescription() + return _u +} + +// SetSensitive sets the "sensitive" field. +func (_u *EnvVarUpdateOne) SetSensitive(v bool) *EnvVarUpdateOne { + _u.mutation.SetSensitive(v) + return _u +} + +// SetNillableSensitive sets the "sensitive" field if the given value is not nil. +func (_u *EnvVarUpdateOne) SetNillableSensitive(v *bool) *EnvVarUpdateOne { + if v != nil { + _u.SetSensitive(*v) + } + return _u +} + +// SetInjectionMode sets the "injection_mode" field. +func (_u *EnvVarUpdateOne) SetInjectionMode(v envvar.InjectionMode) *EnvVarUpdateOne { + _u.mutation.SetInjectionMode(v) + return _u +} + +// SetNillableInjectionMode sets the "injection_mode" field if the given value is not nil. +func (_u *EnvVarUpdateOne) SetNillableInjectionMode(v *envvar.InjectionMode) *EnvVarUpdateOne { + if v != nil { + _u.SetInjectionMode(*v) + } + return _u +} + +// SetSecret sets the "secret" field. +func (_u *EnvVarUpdateOne) SetSecret(v bool) *EnvVarUpdateOne { + _u.mutation.SetSecret(v) + return _u +} + +// SetNillableSecret sets the "secret" field if the given value is not nil. +func (_u *EnvVarUpdateOne) SetNillableSecret(v *bool) *EnvVarUpdateOne { + if v != nil { + _u.SetSecret(*v) + } + return _u +} + +// SetCreatedBy sets the "created_by" field. +func (_u *EnvVarUpdateOne) SetCreatedBy(v string) *EnvVarUpdateOne { + _u.mutation.SetCreatedBy(v) + return _u +} + +// SetNillableCreatedBy sets the "created_by" field if the given value is not nil. +func (_u *EnvVarUpdateOne) SetNillableCreatedBy(v *string) *EnvVarUpdateOne { + if v != nil { + _u.SetCreatedBy(*v) + } + return _u +} + +// ClearCreatedBy clears the value of the "created_by" field. +func (_u *EnvVarUpdateOne) ClearCreatedBy() *EnvVarUpdateOne { + _u.mutation.ClearCreatedBy() + return _u +} + +// SetUpdated sets the "updated" field. +func (_u *EnvVarUpdateOne) SetUpdated(v time.Time) *EnvVarUpdateOne { + _u.mutation.SetUpdated(v) + return _u +} + +// Mutation returns the EnvVarMutation object of the builder. +func (_u *EnvVarUpdateOne) Mutation() *EnvVarMutation { + return _u.mutation +} + +// Where appends a list predicates to the EnvVarUpdate builder. +func (_u *EnvVarUpdateOne) Where(ps ...predicate.EnvVar) *EnvVarUpdateOne { + _u.mutation.Where(ps...) + return _u +} + +// Select allows selecting one or more fields (columns) of the returned entity. +// The default is selecting all fields defined in the entity schema. +func (_u *EnvVarUpdateOne) Select(field string, fields ...string) *EnvVarUpdateOne { + _u.fields = append([]string{field}, fields...) + return _u +} + +// Save executes the query and returns the updated EnvVar entity. +func (_u *EnvVarUpdateOne) Save(ctx context.Context) (*EnvVar, error) { + _u.defaults() + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *EnvVarUpdateOne) SaveX(ctx context.Context) *EnvVar { + node, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (_u *EnvVarUpdateOne) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *EnvVarUpdateOne) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_u *EnvVarUpdateOne) defaults() { + if _, ok := _u.mutation.Updated(); !ok { + v := envvar.UpdateDefaultUpdated() + _u.mutation.SetUpdated(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_u *EnvVarUpdateOne) check() error { + if v, ok := _u.mutation.Key(); ok { + if err := envvar.KeyValidator(v); err != nil { + return &ValidationError{Name: "key", err: fmt.Errorf(`ent: validator failed for field "EnvVar.key": %w`, err)} + } + } + if v, ok := _u.mutation.Scope(); ok { + if err := envvar.ScopeValidator(v); err != nil { + return &ValidationError{Name: "scope", err: fmt.Errorf(`ent: validator failed for field "EnvVar.scope": %w`, err)} + } + } + if v, ok := _u.mutation.InjectionMode(); ok { + if err := envvar.InjectionModeValidator(v); err != nil { + return &ValidationError{Name: "injection_mode", err: fmt.Errorf(`ent: validator failed for field "EnvVar.injection_mode": %w`, err)} + } + } + return nil +} + +func (_u *EnvVarUpdateOne) sqlSave(ctx context.Context) (_node *EnvVar, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(envvar.Table, envvar.Columns, sqlgraph.NewFieldSpec(envvar.FieldID, field.TypeUUID)) + id, ok := _u.mutation.ID() + if !ok { + return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "EnvVar.id" for update`)} + } + _spec.Node.ID.Value = id + if fields := _u.fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, envvar.FieldID) + for _, f := range fields { + if !envvar.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + if f != envvar.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, f) + } + } + } + if ps := _u.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := _u.mutation.Key(); ok { + _spec.SetField(envvar.FieldKey, field.TypeString, value) + } + if value, ok := _u.mutation.Value(); ok { + _spec.SetField(envvar.FieldValue, field.TypeString, value) + } + if value, ok := _u.mutation.Scope(); ok { + _spec.SetField(envvar.FieldScope, field.TypeString, value) + } + if value, ok := _u.mutation.ScopeID(); ok { + _spec.SetField(envvar.FieldScopeID, field.TypeString, value) + } + if value, ok := _u.mutation.Description(); ok { + _spec.SetField(envvar.FieldDescription, field.TypeString, value) + } + if _u.mutation.DescriptionCleared() { + _spec.ClearField(envvar.FieldDescription, field.TypeString) + } + if value, ok := _u.mutation.Sensitive(); ok { + _spec.SetField(envvar.FieldSensitive, field.TypeBool, value) + } + if value, ok := _u.mutation.InjectionMode(); ok { + _spec.SetField(envvar.FieldInjectionMode, field.TypeEnum, value) + } + if value, ok := _u.mutation.Secret(); ok { + _spec.SetField(envvar.FieldSecret, field.TypeBool, value) + } + if value, ok := _u.mutation.CreatedBy(); ok { + _spec.SetField(envvar.FieldCreatedBy, field.TypeString, value) + } + if _u.mutation.CreatedByCleared() { + _spec.ClearField(envvar.FieldCreatedBy, field.TypeString) + } + if value, ok := _u.mutation.Updated(); ok { + _spec.SetField(envvar.FieldUpdated, field.TypeTime, value) + } + _node = &EnvVar{config: _u.config} + _spec.Assign = _node.assignValues + _spec.ScanValues = _node.scanValues + if err = sqlgraph.UpdateNode(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{envvar.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + _u.mutation.done = true + return _node, nil +} diff --git a/pkg/ent/gcpserviceaccount.go b/pkg/ent/gcpserviceaccount.go new file mode 100644 index 000000000..eb8a16cec --- /dev/null +++ b/pkg/ent/gcpserviceaccount.go @@ -0,0 +1,233 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "fmt" + "strings" + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "github.com/GoogleCloudPlatform/scion/pkg/ent/gcpserviceaccount" + "github.com/google/uuid" +) + +// GCPServiceAccount is the model entity for the GCPServiceAccount schema. +type GCPServiceAccount struct { + config `json:"-"` + // ID of the ent. + ID uuid.UUID `json:"id,omitempty"` + // Scope holds the value of the "scope" field. + Scope string `json:"scope,omitempty"` + // ScopeID holds the value of the "scope_id" field. + ScopeID string `json:"scope_id,omitempty"` + // Email holds the value of the "email" field. + Email string `json:"email,omitempty"` + // ProjectID holds the value of the "project_id" field. + ProjectID string `json:"project_id,omitempty"` + // DisplayName holds the value of the "display_name" field. + DisplayName string `json:"display_name,omitempty"` + // DefaultScopes holds the value of the "default_scopes" field. + DefaultScopes string `json:"default_scopes,omitempty"` + // Verified holds the value of the "verified" field. + Verified bool `json:"verified,omitempty"` + // VerifiedAt holds the value of the "verified_at" field. + VerifiedAt *time.Time `json:"verified_at,omitempty"` + // CreatedBy holds the value of the "created_by" field. + CreatedBy string `json:"created_by,omitempty"` + // Managed holds the value of the "managed" field. + Managed bool `json:"managed,omitempty"` + // ManagedBy holds the value of the "managed_by" field. + ManagedBy string `json:"managed_by,omitempty"` + // Created holds the value of the "created" field. + Created time.Time `json:"created,omitempty"` + selectValues sql.SelectValues +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*GCPServiceAccount) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case gcpserviceaccount.FieldVerified, gcpserviceaccount.FieldManaged: + values[i] = new(sql.NullBool) + case gcpserviceaccount.FieldScope, gcpserviceaccount.FieldScopeID, gcpserviceaccount.FieldEmail, gcpserviceaccount.FieldProjectID, gcpserviceaccount.FieldDisplayName, gcpserviceaccount.FieldDefaultScopes, gcpserviceaccount.FieldCreatedBy, gcpserviceaccount.FieldManagedBy: + values[i] = new(sql.NullString) + case gcpserviceaccount.FieldVerifiedAt, gcpserviceaccount.FieldCreated: + values[i] = new(sql.NullTime) + case gcpserviceaccount.FieldID: + values[i] = new(uuid.UUID) + default: + values[i] = new(sql.UnknownType) + } + } + return values, nil +} + +// assignValues assigns the values that were returned from sql.Rows (after scanning) +// to the GCPServiceAccount fields. +func (_m *GCPServiceAccount) assignValues(columns []string, values []any) error { + if m, n := len(values), len(columns); m < n { + return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) + } + for i := range columns { + switch columns[i] { + case gcpserviceaccount.FieldID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field id", values[i]) + } else if value != nil { + _m.ID = *value + } + case gcpserviceaccount.FieldScope: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field scope", values[i]) + } else if value.Valid { + _m.Scope = value.String + } + case gcpserviceaccount.FieldScopeID: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field scope_id", values[i]) + } else if value.Valid { + _m.ScopeID = value.String + } + case gcpserviceaccount.FieldEmail: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field email", values[i]) + } else if value.Valid { + _m.Email = value.String + } + case gcpserviceaccount.FieldProjectID: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field project_id", values[i]) + } else if value.Valid { + _m.ProjectID = value.String + } + case gcpserviceaccount.FieldDisplayName: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field display_name", values[i]) + } else if value.Valid { + _m.DisplayName = value.String + } + case gcpserviceaccount.FieldDefaultScopes: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field default_scopes", values[i]) + } else if value.Valid { + _m.DefaultScopes = value.String + } + case gcpserviceaccount.FieldVerified: + if value, ok := values[i].(*sql.NullBool); !ok { + return fmt.Errorf("unexpected type %T for field verified", values[i]) + } else if value.Valid { + _m.Verified = value.Bool + } + case gcpserviceaccount.FieldVerifiedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field verified_at", values[i]) + } else if value.Valid { + _m.VerifiedAt = new(time.Time) + *_m.VerifiedAt = value.Time + } + case gcpserviceaccount.FieldCreatedBy: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field created_by", values[i]) + } else if value.Valid { + _m.CreatedBy = value.String + } + case gcpserviceaccount.FieldManaged: + if value, ok := values[i].(*sql.NullBool); !ok { + return fmt.Errorf("unexpected type %T for field managed", values[i]) + } else if value.Valid { + _m.Managed = value.Bool + } + case gcpserviceaccount.FieldManagedBy: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field managed_by", values[i]) + } else if value.Valid { + _m.ManagedBy = value.String + } + case gcpserviceaccount.FieldCreated: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field created", values[i]) + } else if value.Valid { + _m.Created = value.Time + } + default: + _m.selectValues.Set(columns[i], values[i]) + } + } + return nil +} + +// Value returns the ent.Value that was dynamically selected and assigned to the GCPServiceAccount. +// This includes values selected through modifiers, order, etc. +func (_m *GCPServiceAccount) Value(name string) (ent.Value, error) { + return _m.selectValues.Get(name) +} + +// Update returns a builder for updating this GCPServiceAccount. +// Note that you need to call GCPServiceAccount.Unwrap() before calling this method if this GCPServiceAccount +// was returned from a transaction, and the transaction was committed or rolled back. +func (_m *GCPServiceAccount) Update() *GCPServiceAccountUpdateOne { + return NewGCPServiceAccountClient(_m.config).UpdateOne(_m) +} + +// Unwrap unwraps the GCPServiceAccount entity that was returned from a transaction after it was closed, +// so that all future queries will be executed through the driver which created the transaction. +func (_m *GCPServiceAccount) Unwrap() *GCPServiceAccount { + _tx, ok := _m.config.driver.(*txDriver) + if !ok { + panic("ent: GCPServiceAccount is not a transactional entity") + } + _m.config.driver = _tx.drv + return _m +} + +// String implements the fmt.Stringer. +func (_m *GCPServiceAccount) String() string { + var builder strings.Builder + builder.WriteString("GCPServiceAccount(") + builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID)) + builder.WriteString("scope=") + builder.WriteString(_m.Scope) + builder.WriteString(", ") + builder.WriteString("scope_id=") + builder.WriteString(_m.ScopeID) + builder.WriteString(", ") + builder.WriteString("email=") + builder.WriteString(_m.Email) + builder.WriteString(", ") + builder.WriteString("project_id=") + builder.WriteString(_m.ProjectID) + builder.WriteString(", ") + builder.WriteString("display_name=") + builder.WriteString(_m.DisplayName) + builder.WriteString(", ") + builder.WriteString("default_scopes=") + builder.WriteString(_m.DefaultScopes) + builder.WriteString(", ") + builder.WriteString("verified=") + builder.WriteString(fmt.Sprintf("%v", _m.Verified)) + builder.WriteString(", ") + if v := _m.VerifiedAt; v != nil { + builder.WriteString("verified_at=") + builder.WriteString(v.Format(time.ANSIC)) + } + builder.WriteString(", ") + builder.WriteString("created_by=") + builder.WriteString(_m.CreatedBy) + builder.WriteString(", ") + builder.WriteString("managed=") + builder.WriteString(fmt.Sprintf("%v", _m.Managed)) + builder.WriteString(", ") + builder.WriteString("managed_by=") + builder.WriteString(_m.ManagedBy) + builder.WriteString(", ") + builder.WriteString("created=") + builder.WriteString(_m.Created.Format(time.ANSIC)) + builder.WriteByte(')') + return builder.String() +} + +// GCPServiceAccounts is a parsable slice of GCPServiceAccount. +type GCPServiceAccounts []*GCPServiceAccount diff --git a/pkg/ent/gcpserviceaccount/gcpserviceaccount.go b/pkg/ent/gcpserviceaccount/gcpserviceaccount.go new file mode 100644 index 000000000..c20f0b2be --- /dev/null +++ b/pkg/ent/gcpserviceaccount/gcpserviceaccount.go @@ -0,0 +1,165 @@ +// Code generated by ent, DO NOT EDIT. + +package gcpserviceaccount + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "github.com/google/uuid" +) + +const ( + // Label holds the string label denoting the gcpserviceaccount type in the database. + Label = "gcp_service_account" + // FieldID holds the string denoting the id field in the database. + FieldID = "id" + // FieldScope holds the string denoting the scope field in the database. + FieldScope = "scope" + // FieldScopeID holds the string denoting the scope_id field in the database. + FieldScopeID = "scope_id" + // FieldEmail holds the string denoting the email field in the database. + FieldEmail = "email" + // FieldProjectID holds the string denoting the project_id field in the database. + FieldProjectID = "project_id" + // FieldDisplayName holds the string denoting the display_name field in the database. + FieldDisplayName = "display_name" + // FieldDefaultScopes holds the string denoting the default_scopes field in the database. + FieldDefaultScopes = "default_scopes" + // FieldVerified holds the string denoting the verified field in the database. + FieldVerified = "verified" + // FieldVerifiedAt holds the string denoting the verified_at field in the database. + FieldVerifiedAt = "verified_at" + // FieldCreatedBy holds the string denoting the created_by field in the database. + FieldCreatedBy = "created_by" + // FieldManaged holds the string denoting the managed field in the database. + FieldManaged = "managed" + // FieldManagedBy holds the string denoting the managed_by field in the database. + FieldManagedBy = "managed_by" + // FieldCreated holds the string denoting the created field in the database. + FieldCreated = "created" + // Table holds the table name of the gcpserviceaccount in the database. + Table = "gcp_service_accounts" +) + +// Columns holds all SQL columns for gcpserviceaccount fields. +var Columns = []string{ + FieldID, + FieldScope, + FieldScopeID, + FieldEmail, + FieldProjectID, + FieldDisplayName, + FieldDefaultScopes, + FieldVerified, + FieldVerifiedAt, + FieldCreatedBy, + FieldManaged, + FieldManagedBy, + FieldCreated, +} + +// ValidColumn reports if the column name is valid (part of the table columns). +func ValidColumn(column string) bool { + for i := range Columns { + if column == Columns[i] { + return true + } + } + return false +} + +var ( + // ScopeValidator is a validator for the "scope" field. It is called by the builders before save. + ScopeValidator func(string) error + // ScopeIDValidator is a validator for the "scope_id" field. It is called by the builders before save. + ScopeIDValidator func(string) error + // EmailValidator is a validator for the "email" field. It is called by the builders before save. + EmailValidator func(string) error + // ProjectIDValidator is a validator for the "project_id" field. It is called by the builders before save. + ProjectIDValidator func(string) error + // DefaultDisplayName holds the default value on creation for the "display_name" field. + DefaultDisplayName string + // DefaultDefaultScopes holds the default value on creation for the "default_scopes" field. + DefaultDefaultScopes string + // DefaultVerified holds the default value on creation for the "verified" field. + DefaultVerified bool + // DefaultCreatedBy holds the default value on creation for the "created_by" field. + DefaultCreatedBy string + // DefaultManaged holds the default value on creation for the "managed" field. + DefaultManaged bool + // DefaultManagedBy holds the default value on creation for the "managed_by" field. + DefaultManagedBy string + // DefaultCreated holds the default value on creation for the "created" field. + DefaultCreated func() time.Time + // DefaultID holds the default value on creation for the "id" field. + DefaultID func() uuid.UUID +) + +// OrderOption defines the ordering options for the GCPServiceAccount queries. +type OrderOption func(*sql.Selector) + +// ByID orders the results by the id field. +func ByID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldID, opts...).ToFunc() +} + +// ByScope orders the results by the scope field. +func ByScope(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldScope, opts...).ToFunc() +} + +// ByScopeID orders the results by the scope_id field. +func ByScopeID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldScopeID, opts...).ToFunc() +} + +// ByEmail orders the results by the email field. +func ByEmail(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldEmail, opts...).ToFunc() +} + +// ByProjectID orders the results by the project_id field. +func ByProjectID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldProjectID, opts...).ToFunc() +} + +// ByDisplayName orders the results by the display_name field. +func ByDisplayName(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldDisplayName, opts...).ToFunc() +} + +// ByDefaultScopes orders the results by the default_scopes field. +func ByDefaultScopes(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldDefaultScopes, opts...).ToFunc() +} + +// ByVerified orders the results by the verified field. +func ByVerified(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldVerified, opts...).ToFunc() +} + +// ByVerifiedAt orders the results by the verified_at field. +func ByVerifiedAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldVerifiedAt, opts...).ToFunc() +} + +// ByCreatedBy orders the results by the created_by field. +func ByCreatedBy(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCreatedBy, opts...).ToFunc() +} + +// ByManaged orders the results by the managed field. +func ByManaged(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldManaged, opts...).ToFunc() +} + +// ByManagedBy orders the results by the managed_by field. +func ByManagedBy(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldManagedBy, opts...).ToFunc() +} + +// ByCreated orders the results by the created field. +func ByCreated(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCreated, opts...).ToFunc() +} diff --git a/pkg/ent/gcpserviceaccount/where.go b/pkg/ent/gcpserviceaccount/where.go new file mode 100644 index 000000000..7123a14af --- /dev/null +++ b/pkg/ent/gcpserviceaccount/where.go @@ -0,0 +1,761 @@ +// Code generated by ent, DO NOT EDIT. + +package gcpserviceaccount + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/google/uuid" +) + +// ID filters vertices based on their ID field. +func ID(id uuid.UUID) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldEQ(FieldID, id)) +} + +// IDEQ applies the EQ predicate on the ID field. +func IDEQ(id uuid.UUID) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldEQ(FieldID, id)) +} + +// IDNEQ applies the NEQ predicate on the ID field. +func IDNEQ(id uuid.UUID) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldNEQ(FieldID, id)) +} + +// IDIn applies the In predicate on the ID field. +func IDIn(ids ...uuid.UUID) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldIn(FieldID, ids...)) +} + +// IDNotIn applies the NotIn predicate on the ID field. +func IDNotIn(ids ...uuid.UUID) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldNotIn(FieldID, ids...)) +} + +// IDGT applies the GT predicate on the ID field. +func IDGT(id uuid.UUID) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldGT(FieldID, id)) +} + +// IDGTE applies the GTE predicate on the ID field. +func IDGTE(id uuid.UUID) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldGTE(FieldID, id)) +} + +// IDLT applies the LT predicate on the ID field. +func IDLT(id uuid.UUID) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldLT(FieldID, id)) +} + +// IDLTE applies the LTE predicate on the ID field. +func IDLTE(id uuid.UUID) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldLTE(FieldID, id)) +} + +// Scope applies equality check predicate on the "scope" field. It's identical to ScopeEQ. +func Scope(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldEQ(FieldScope, v)) +} + +// ScopeID applies equality check predicate on the "scope_id" field. It's identical to ScopeIDEQ. +func ScopeID(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldEQ(FieldScopeID, v)) +} + +// Email applies equality check predicate on the "email" field. It's identical to EmailEQ. +func Email(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldEQ(FieldEmail, v)) +} + +// ProjectID applies equality check predicate on the "project_id" field. It's identical to ProjectIDEQ. +func ProjectID(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldEQ(FieldProjectID, v)) +} + +// DisplayName applies equality check predicate on the "display_name" field. It's identical to DisplayNameEQ. +func DisplayName(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldEQ(FieldDisplayName, v)) +} + +// DefaultScopes applies equality check predicate on the "default_scopes" field. It's identical to DefaultScopesEQ. +func DefaultScopes(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldEQ(FieldDefaultScopes, v)) +} + +// Verified applies equality check predicate on the "verified" field. It's identical to VerifiedEQ. +func Verified(v bool) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldEQ(FieldVerified, v)) +} + +// VerifiedAt applies equality check predicate on the "verified_at" field. It's identical to VerifiedAtEQ. +func VerifiedAt(v time.Time) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldEQ(FieldVerifiedAt, v)) +} + +// CreatedBy applies equality check predicate on the "created_by" field. It's identical to CreatedByEQ. +func CreatedBy(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldEQ(FieldCreatedBy, v)) +} + +// Managed applies equality check predicate on the "managed" field. It's identical to ManagedEQ. +func Managed(v bool) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldEQ(FieldManaged, v)) +} + +// ManagedBy applies equality check predicate on the "managed_by" field. It's identical to ManagedByEQ. +func ManagedBy(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldEQ(FieldManagedBy, v)) +} + +// Created applies equality check predicate on the "created" field. It's identical to CreatedEQ. +func Created(v time.Time) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldEQ(FieldCreated, v)) +} + +// ScopeEQ applies the EQ predicate on the "scope" field. +func ScopeEQ(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldEQ(FieldScope, v)) +} + +// ScopeNEQ applies the NEQ predicate on the "scope" field. +func ScopeNEQ(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldNEQ(FieldScope, v)) +} + +// ScopeIn applies the In predicate on the "scope" field. +func ScopeIn(vs ...string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldIn(FieldScope, vs...)) +} + +// ScopeNotIn applies the NotIn predicate on the "scope" field. +func ScopeNotIn(vs ...string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldNotIn(FieldScope, vs...)) +} + +// ScopeGT applies the GT predicate on the "scope" field. +func ScopeGT(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldGT(FieldScope, v)) +} + +// ScopeGTE applies the GTE predicate on the "scope" field. +func ScopeGTE(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldGTE(FieldScope, v)) +} + +// ScopeLT applies the LT predicate on the "scope" field. +func ScopeLT(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldLT(FieldScope, v)) +} + +// ScopeLTE applies the LTE predicate on the "scope" field. +func ScopeLTE(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldLTE(FieldScope, v)) +} + +// ScopeContains applies the Contains predicate on the "scope" field. +func ScopeContains(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldContains(FieldScope, v)) +} + +// ScopeHasPrefix applies the HasPrefix predicate on the "scope" field. +func ScopeHasPrefix(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldHasPrefix(FieldScope, v)) +} + +// ScopeHasSuffix applies the HasSuffix predicate on the "scope" field. +func ScopeHasSuffix(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldHasSuffix(FieldScope, v)) +} + +// ScopeEqualFold applies the EqualFold predicate on the "scope" field. +func ScopeEqualFold(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldEqualFold(FieldScope, v)) +} + +// ScopeContainsFold applies the ContainsFold predicate on the "scope" field. +func ScopeContainsFold(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldContainsFold(FieldScope, v)) +} + +// ScopeIDEQ applies the EQ predicate on the "scope_id" field. +func ScopeIDEQ(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldEQ(FieldScopeID, v)) +} + +// ScopeIDNEQ applies the NEQ predicate on the "scope_id" field. +func ScopeIDNEQ(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldNEQ(FieldScopeID, v)) +} + +// ScopeIDIn applies the In predicate on the "scope_id" field. +func ScopeIDIn(vs ...string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldIn(FieldScopeID, vs...)) +} + +// ScopeIDNotIn applies the NotIn predicate on the "scope_id" field. +func ScopeIDNotIn(vs ...string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldNotIn(FieldScopeID, vs...)) +} + +// ScopeIDGT applies the GT predicate on the "scope_id" field. +func ScopeIDGT(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldGT(FieldScopeID, v)) +} + +// ScopeIDGTE applies the GTE predicate on the "scope_id" field. +func ScopeIDGTE(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldGTE(FieldScopeID, v)) +} + +// ScopeIDLT applies the LT predicate on the "scope_id" field. +func ScopeIDLT(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldLT(FieldScopeID, v)) +} + +// ScopeIDLTE applies the LTE predicate on the "scope_id" field. +func ScopeIDLTE(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldLTE(FieldScopeID, v)) +} + +// ScopeIDContains applies the Contains predicate on the "scope_id" field. +func ScopeIDContains(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldContains(FieldScopeID, v)) +} + +// ScopeIDHasPrefix applies the HasPrefix predicate on the "scope_id" field. +func ScopeIDHasPrefix(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldHasPrefix(FieldScopeID, v)) +} + +// ScopeIDHasSuffix applies the HasSuffix predicate on the "scope_id" field. +func ScopeIDHasSuffix(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldHasSuffix(FieldScopeID, v)) +} + +// ScopeIDEqualFold applies the EqualFold predicate on the "scope_id" field. +func ScopeIDEqualFold(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldEqualFold(FieldScopeID, v)) +} + +// ScopeIDContainsFold applies the ContainsFold predicate on the "scope_id" field. +func ScopeIDContainsFold(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldContainsFold(FieldScopeID, v)) +} + +// EmailEQ applies the EQ predicate on the "email" field. +func EmailEQ(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldEQ(FieldEmail, v)) +} + +// EmailNEQ applies the NEQ predicate on the "email" field. +func EmailNEQ(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldNEQ(FieldEmail, v)) +} + +// EmailIn applies the In predicate on the "email" field. +func EmailIn(vs ...string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldIn(FieldEmail, vs...)) +} + +// EmailNotIn applies the NotIn predicate on the "email" field. +func EmailNotIn(vs ...string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldNotIn(FieldEmail, vs...)) +} + +// EmailGT applies the GT predicate on the "email" field. +func EmailGT(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldGT(FieldEmail, v)) +} + +// EmailGTE applies the GTE predicate on the "email" field. +func EmailGTE(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldGTE(FieldEmail, v)) +} + +// EmailLT applies the LT predicate on the "email" field. +func EmailLT(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldLT(FieldEmail, v)) +} + +// EmailLTE applies the LTE predicate on the "email" field. +func EmailLTE(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldLTE(FieldEmail, v)) +} + +// EmailContains applies the Contains predicate on the "email" field. +func EmailContains(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldContains(FieldEmail, v)) +} + +// EmailHasPrefix applies the HasPrefix predicate on the "email" field. +func EmailHasPrefix(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldHasPrefix(FieldEmail, v)) +} + +// EmailHasSuffix applies the HasSuffix predicate on the "email" field. +func EmailHasSuffix(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldHasSuffix(FieldEmail, v)) +} + +// EmailEqualFold applies the EqualFold predicate on the "email" field. +func EmailEqualFold(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldEqualFold(FieldEmail, v)) +} + +// EmailContainsFold applies the ContainsFold predicate on the "email" field. +func EmailContainsFold(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldContainsFold(FieldEmail, v)) +} + +// ProjectIDEQ applies the EQ predicate on the "project_id" field. +func ProjectIDEQ(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldEQ(FieldProjectID, v)) +} + +// ProjectIDNEQ applies the NEQ predicate on the "project_id" field. +func ProjectIDNEQ(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldNEQ(FieldProjectID, v)) +} + +// ProjectIDIn applies the In predicate on the "project_id" field. +func ProjectIDIn(vs ...string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldIn(FieldProjectID, vs...)) +} + +// ProjectIDNotIn applies the NotIn predicate on the "project_id" field. +func ProjectIDNotIn(vs ...string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldNotIn(FieldProjectID, vs...)) +} + +// ProjectIDGT applies the GT predicate on the "project_id" field. +func ProjectIDGT(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldGT(FieldProjectID, v)) +} + +// ProjectIDGTE applies the GTE predicate on the "project_id" field. +func ProjectIDGTE(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldGTE(FieldProjectID, v)) +} + +// ProjectIDLT applies the LT predicate on the "project_id" field. +func ProjectIDLT(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldLT(FieldProjectID, v)) +} + +// ProjectIDLTE applies the LTE predicate on the "project_id" field. +func ProjectIDLTE(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldLTE(FieldProjectID, v)) +} + +// ProjectIDContains applies the Contains predicate on the "project_id" field. +func ProjectIDContains(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldContains(FieldProjectID, v)) +} + +// ProjectIDHasPrefix applies the HasPrefix predicate on the "project_id" field. +func ProjectIDHasPrefix(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldHasPrefix(FieldProjectID, v)) +} + +// ProjectIDHasSuffix applies the HasSuffix predicate on the "project_id" field. +func ProjectIDHasSuffix(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldHasSuffix(FieldProjectID, v)) +} + +// ProjectIDEqualFold applies the EqualFold predicate on the "project_id" field. +func ProjectIDEqualFold(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldEqualFold(FieldProjectID, v)) +} + +// ProjectIDContainsFold applies the ContainsFold predicate on the "project_id" field. +func ProjectIDContainsFold(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldContainsFold(FieldProjectID, v)) +} + +// DisplayNameEQ applies the EQ predicate on the "display_name" field. +func DisplayNameEQ(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldEQ(FieldDisplayName, v)) +} + +// DisplayNameNEQ applies the NEQ predicate on the "display_name" field. +func DisplayNameNEQ(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldNEQ(FieldDisplayName, v)) +} + +// DisplayNameIn applies the In predicate on the "display_name" field. +func DisplayNameIn(vs ...string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldIn(FieldDisplayName, vs...)) +} + +// DisplayNameNotIn applies the NotIn predicate on the "display_name" field. +func DisplayNameNotIn(vs ...string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldNotIn(FieldDisplayName, vs...)) +} + +// DisplayNameGT applies the GT predicate on the "display_name" field. +func DisplayNameGT(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldGT(FieldDisplayName, v)) +} + +// DisplayNameGTE applies the GTE predicate on the "display_name" field. +func DisplayNameGTE(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldGTE(FieldDisplayName, v)) +} + +// DisplayNameLT applies the LT predicate on the "display_name" field. +func DisplayNameLT(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldLT(FieldDisplayName, v)) +} + +// DisplayNameLTE applies the LTE predicate on the "display_name" field. +func DisplayNameLTE(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldLTE(FieldDisplayName, v)) +} + +// DisplayNameContains applies the Contains predicate on the "display_name" field. +func DisplayNameContains(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldContains(FieldDisplayName, v)) +} + +// DisplayNameHasPrefix applies the HasPrefix predicate on the "display_name" field. +func DisplayNameHasPrefix(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldHasPrefix(FieldDisplayName, v)) +} + +// DisplayNameHasSuffix applies the HasSuffix predicate on the "display_name" field. +func DisplayNameHasSuffix(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldHasSuffix(FieldDisplayName, v)) +} + +// DisplayNameEqualFold applies the EqualFold predicate on the "display_name" field. +func DisplayNameEqualFold(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldEqualFold(FieldDisplayName, v)) +} + +// DisplayNameContainsFold applies the ContainsFold predicate on the "display_name" field. +func DisplayNameContainsFold(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldContainsFold(FieldDisplayName, v)) +} + +// DefaultScopesEQ applies the EQ predicate on the "default_scopes" field. +func DefaultScopesEQ(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldEQ(FieldDefaultScopes, v)) +} + +// DefaultScopesNEQ applies the NEQ predicate on the "default_scopes" field. +func DefaultScopesNEQ(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldNEQ(FieldDefaultScopes, v)) +} + +// DefaultScopesIn applies the In predicate on the "default_scopes" field. +func DefaultScopesIn(vs ...string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldIn(FieldDefaultScopes, vs...)) +} + +// DefaultScopesNotIn applies the NotIn predicate on the "default_scopes" field. +func DefaultScopesNotIn(vs ...string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldNotIn(FieldDefaultScopes, vs...)) +} + +// DefaultScopesGT applies the GT predicate on the "default_scopes" field. +func DefaultScopesGT(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldGT(FieldDefaultScopes, v)) +} + +// DefaultScopesGTE applies the GTE predicate on the "default_scopes" field. +func DefaultScopesGTE(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldGTE(FieldDefaultScopes, v)) +} + +// DefaultScopesLT applies the LT predicate on the "default_scopes" field. +func DefaultScopesLT(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldLT(FieldDefaultScopes, v)) +} + +// DefaultScopesLTE applies the LTE predicate on the "default_scopes" field. +func DefaultScopesLTE(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldLTE(FieldDefaultScopes, v)) +} + +// DefaultScopesContains applies the Contains predicate on the "default_scopes" field. +func DefaultScopesContains(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldContains(FieldDefaultScopes, v)) +} + +// DefaultScopesHasPrefix applies the HasPrefix predicate on the "default_scopes" field. +func DefaultScopesHasPrefix(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldHasPrefix(FieldDefaultScopes, v)) +} + +// DefaultScopesHasSuffix applies the HasSuffix predicate on the "default_scopes" field. +func DefaultScopesHasSuffix(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldHasSuffix(FieldDefaultScopes, v)) +} + +// DefaultScopesEqualFold applies the EqualFold predicate on the "default_scopes" field. +func DefaultScopesEqualFold(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldEqualFold(FieldDefaultScopes, v)) +} + +// DefaultScopesContainsFold applies the ContainsFold predicate on the "default_scopes" field. +func DefaultScopesContainsFold(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldContainsFold(FieldDefaultScopes, v)) +} + +// VerifiedEQ applies the EQ predicate on the "verified" field. +func VerifiedEQ(v bool) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldEQ(FieldVerified, v)) +} + +// VerifiedNEQ applies the NEQ predicate on the "verified" field. +func VerifiedNEQ(v bool) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldNEQ(FieldVerified, v)) +} + +// VerifiedAtEQ applies the EQ predicate on the "verified_at" field. +func VerifiedAtEQ(v time.Time) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldEQ(FieldVerifiedAt, v)) +} + +// VerifiedAtNEQ applies the NEQ predicate on the "verified_at" field. +func VerifiedAtNEQ(v time.Time) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldNEQ(FieldVerifiedAt, v)) +} + +// VerifiedAtIn applies the In predicate on the "verified_at" field. +func VerifiedAtIn(vs ...time.Time) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldIn(FieldVerifiedAt, vs...)) +} + +// VerifiedAtNotIn applies the NotIn predicate on the "verified_at" field. +func VerifiedAtNotIn(vs ...time.Time) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldNotIn(FieldVerifiedAt, vs...)) +} + +// VerifiedAtGT applies the GT predicate on the "verified_at" field. +func VerifiedAtGT(v time.Time) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldGT(FieldVerifiedAt, v)) +} + +// VerifiedAtGTE applies the GTE predicate on the "verified_at" field. +func VerifiedAtGTE(v time.Time) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldGTE(FieldVerifiedAt, v)) +} + +// VerifiedAtLT applies the LT predicate on the "verified_at" field. +func VerifiedAtLT(v time.Time) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldLT(FieldVerifiedAt, v)) +} + +// VerifiedAtLTE applies the LTE predicate on the "verified_at" field. +func VerifiedAtLTE(v time.Time) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldLTE(FieldVerifiedAt, v)) +} + +// VerifiedAtIsNil applies the IsNil predicate on the "verified_at" field. +func VerifiedAtIsNil() predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldIsNull(FieldVerifiedAt)) +} + +// VerifiedAtNotNil applies the NotNil predicate on the "verified_at" field. +func VerifiedAtNotNil() predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldNotNull(FieldVerifiedAt)) +} + +// CreatedByEQ applies the EQ predicate on the "created_by" field. +func CreatedByEQ(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldEQ(FieldCreatedBy, v)) +} + +// CreatedByNEQ applies the NEQ predicate on the "created_by" field. +func CreatedByNEQ(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldNEQ(FieldCreatedBy, v)) +} + +// CreatedByIn applies the In predicate on the "created_by" field. +func CreatedByIn(vs ...string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldIn(FieldCreatedBy, vs...)) +} + +// CreatedByNotIn applies the NotIn predicate on the "created_by" field. +func CreatedByNotIn(vs ...string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldNotIn(FieldCreatedBy, vs...)) +} + +// CreatedByGT applies the GT predicate on the "created_by" field. +func CreatedByGT(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldGT(FieldCreatedBy, v)) +} + +// CreatedByGTE applies the GTE predicate on the "created_by" field. +func CreatedByGTE(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldGTE(FieldCreatedBy, v)) +} + +// CreatedByLT applies the LT predicate on the "created_by" field. +func CreatedByLT(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldLT(FieldCreatedBy, v)) +} + +// CreatedByLTE applies the LTE predicate on the "created_by" field. +func CreatedByLTE(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldLTE(FieldCreatedBy, v)) +} + +// CreatedByContains applies the Contains predicate on the "created_by" field. +func CreatedByContains(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldContains(FieldCreatedBy, v)) +} + +// CreatedByHasPrefix applies the HasPrefix predicate on the "created_by" field. +func CreatedByHasPrefix(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldHasPrefix(FieldCreatedBy, v)) +} + +// CreatedByHasSuffix applies the HasSuffix predicate on the "created_by" field. +func CreatedByHasSuffix(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldHasSuffix(FieldCreatedBy, v)) +} + +// CreatedByEqualFold applies the EqualFold predicate on the "created_by" field. +func CreatedByEqualFold(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldEqualFold(FieldCreatedBy, v)) +} + +// CreatedByContainsFold applies the ContainsFold predicate on the "created_by" field. +func CreatedByContainsFold(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldContainsFold(FieldCreatedBy, v)) +} + +// ManagedEQ applies the EQ predicate on the "managed" field. +func ManagedEQ(v bool) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldEQ(FieldManaged, v)) +} + +// ManagedNEQ applies the NEQ predicate on the "managed" field. +func ManagedNEQ(v bool) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldNEQ(FieldManaged, v)) +} + +// ManagedByEQ applies the EQ predicate on the "managed_by" field. +func ManagedByEQ(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldEQ(FieldManagedBy, v)) +} + +// ManagedByNEQ applies the NEQ predicate on the "managed_by" field. +func ManagedByNEQ(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldNEQ(FieldManagedBy, v)) +} + +// ManagedByIn applies the In predicate on the "managed_by" field. +func ManagedByIn(vs ...string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldIn(FieldManagedBy, vs...)) +} + +// ManagedByNotIn applies the NotIn predicate on the "managed_by" field. +func ManagedByNotIn(vs ...string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldNotIn(FieldManagedBy, vs...)) +} + +// ManagedByGT applies the GT predicate on the "managed_by" field. +func ManagedByGT(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldGT(FieldManagedBy, v)) +} + +// ManagedByGTE applies the GTE predicate on the "managed_by" field. +func ManagedByGTE(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldGTE(FieldManagedBy, v)) +} + +// ManagedByLT applies the LT predicate on the "managed_by" field. +func ManagedByLT(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldLT(FieldManagedBy, v)) +} + +// ManagedByLTE applies the LTE predicate on the "managed_by" field. +func ManagedByLTE(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldLTE(FieldManagedBy, v)) +} + +// ManagedByContains applies the Contains predicate on the "managed_by" field. +func ManagedByContains(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldContains(FieldManagedBy, v)) +} + +// ManagedByHasPrefix applies the HasPrefix predicate on the "managed_by" field. +func ManagedByHasPrefix(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldHasPrefix(FieldManagedBy, v)) +} + +// ManagedByHasSuffix applies the HasSuffix predicate on the "managed_by" field. +func ManagedByHasSuffix(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldHasSuffix(FieldManagedBy, v)) +} + +// ManagedByEqualFold applies the EqualFold predicate on the "managed_by" field. +func ManagedByEqualFold(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldEqualFold(FieldManagedBy, v)) +} + +// ManagedByContainsFold applies the ContainsFold predicate on the "managed_by" field. +func ManagedByContainsFold(v string) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldContainsFold(FieldManagedBy, v)) +} + +// CreatedEQ applies the EQ predicate on the "created" field. +func CreatedEQ(v time.Time) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldEQ(FieldCreated, v)) +} + +// CreatedNEQ applies the NEQ predicate on the "created" field. +func CreatedNEQ(v time.Time) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldNEQ(FieldCreated, v)) +} + +// CreatedIn applies the In predicate on the "created" field. +func CreatedIn(vs ...time.Time) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldIn(FieldCreated, vs...)) +} + +// CreatedNotIn applies the NotIn predicate on the "created" field. +func CreatedNotIn(vs ...time.Time) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldNotIn(FieldCreated, vs...)) +} + +// CreatedGT applies the GT predicate on the "created" field. +func CreatedGT(v time.Time) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldGT(FieldCreated, v)) +} + +// CreatedGTE applies the GTE predicate on the "created" field. +func CreatedGTE(v time.Time) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldGTE(FieldCreated, v)) +} + +// CreatedLT applies the LT predicate on the "created" field. +func CreatedLT(v time.Time) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldLT(FieldCreated, v)) +} + +// CreatedLTE applies the LTE predicate on the "created" field. +func CreatedLTE(v time.Time) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.FieldLTE(FieldCreated, v)) +} + +// And groups predicates with the AND operator between them. +func And(predicates ...predicate.GCPServiceAccount) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.AndPredicates(predicates...)) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.GCPServiceAccount) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.OrPredicates(predicates...)) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.GCPServiceAccount) predicate.GCPServiceAccount { + return predicate.GCPServiceAccount(sql.NotPredicates(p)) +} diff --git a/pkg/ent/gcpserviceaccount_create.go b/pkg/ent/gcpserviceaccount_create.go new file mode 100644 index 000000000..509861cbf --- /dev/null +++ b/pkg/ent/gcpserviceaccount_create.go @@ -0,0 +1,1187 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/gcpserviceaccount" + "github.com/google/uuid" +) + +// GCPServiceAccountCreate is the builder for creating a GCPServiceAccount entity. +type GCPServiceAccountCreate struct { + config + mutation *GCPServiceAccountMutation + hooks []Hook + conflict []sql.ConflictOption +} + +// SetScope sets the "scope" field. +func (_c *GCPServiceAccountCreate) SetScope(v string) *GCPServiceAccountCreate { + _c.mutation.SetScope(v) + return _c +} + +// SetScopeID sets the "scope_id" field. +func (_c *GCPServiceAccountCreate) SetScopeID(v string) *GCPServiceAccountCreate { + _c.mutation.SetScopeID(v) + return _c +} + +// SetEmail sets the "email" field. +func (_c *GCPServiceAccountCreate) SetEmail(v string) *GCPServiceAccountCreate { + _c.mutation.SetEmail(v) + return _c +} + +// SetProjectID sets the "project_id" field. +func (_c *GCPServiceAccountCreate) SetProjectID(v string) *GCPServiceAccountCreate { + _c.mutation.SetProjectID(v) + return _c +} + +// SetDisplayName sets the "display_name" field. +func (_c *GCPServiceAccountCreate) SetDisplayName(v string) *GCPServiceAccountCreate { + _c.mutation.SetDisplayName(v) + return _c +} + +// SetNillableDisplayName sets the "display_name" field if the given value is not nil. +func (_c *GCPServiceAccountCreate) SetNillableDisplayName(v *string) *GCPServiceAccountCreate { + if v != nil { + _c.SetDisplayName(*v) + } + return _c +} + +// SetDefaultScopes sets the "default_scopes" field. +func (_c *GCPServiceAccountCreate) SetDefaultScopes(v string) *GCPServiceAccountCreate { + _c.mutation.SetDefaultScopes(v) + return _c +} + +// SetNillableDefaultScopes sets the "default_scopes" field if the given value is not nil. +func (_c *GCPServiceAccountCreate) SetNillableDefaultScopes(v *string) *GCPServiceAccountCreate { + if v != nil { + _c.SetDefaultScopes(*v) + } + return _c +} + +// SetVerified sets the "verified" field. +func (_c *GCPServiceAccountCreate) SetVerified(v bool) *GCPServiceAccountCreate { + _c.mutation.SetVerified(v) + return _c +} + +// SetNillableVerified sets the "verified" field if the given value is not nil. +func (_c *GCPServiceAccountCreate) SetNillableVerified(v *bool) *GCPServiceAccountCreate { + if v != nil { + _c.SetVerified(*v) + } + return _c +} + +// SetVerifiedAt sets the "verified_at" field. +func (_c *GCPServiceAccountCreate) SetVerifiedAt(v time.Time) *GCPServiceAccountCreate { + _c.mutation.SetVerifiedAt(v) + return _c +} + +// SetNillableVerifiedAt sets the "verified_at" field if the given value is not nil. +func (_c *GCPServiceAccountCreate) SetNillableVerifiedAt(v *time.Time) *GCPServiceAccountCreate { + if v != nil { + _c.SetVerifiedAt(*v) + } + return _c +} + +// SetCreatedBy sets the "created_by" field. +func (_c *GCPServiceAccountCreate) SetCreatedBy(v string) *GCPServiceAccountCreate { + _c.mutation.SetCreatedBy(v) + return _c +} + +// SetNillableCreatedBy sets the "created_by" field if the given value is not nil. +func (_c *GCPServiceAccountCreate) SetNillableCreatedBy(v *string) *GCPServiceAccountCreate { + if v != nil { + _c.SetCreatedBy(*v) + } + return _c +} + +// SetManaged sets the "managed" field. +func (_c *GCPServiceAccountCreate) SetManaged(v bool) *GCPServiceAccountCreate { + _c.mutation.SetManaged(v) + return _c +} + +// SetNillableManaged sets the "managed" field if the given value is not nil. +func (_c *GCPServiceAccountCreate) SetNillableManaged(v *bool) *GCPServiceAccountCreate { + if v != nil { + _c.SetManaged(*v) + } + return _c +} + +// SetManagedBy sets the "managed_by" field. +func (_c *GCPServiceAccountCreate) SetManagedBy(v string) *GCPServiceAccountCreate { + _c.mutation.SetManagedBy(v) + return _c +} + +// SetNillableManagedBy sets the "managed_by" field if the given value is not nil. +func (_c *GCPServiceAccountCreate) SetNillableManagedBy(v *string) *GCPServiceAccountCreate { + if v != nil { + _c.SetManagedBy(*v) + } + return _c +} + +// SetCreated sets the "created" field. +func (_c *GCPServiceAccountCreate) SetCreated(v time.Time) *GCPServiceAccountCreate { + _c.mutation.SetCreated(v) + return _c +} + +// SetNillableCreated sets the "created" field if the given value is not nil. +func (_c *GCPServiceAccountCreate) SetNillableCreated(v *time.Time) *GCPServiceAccountCreate { + if v != nil { + _c.SetCreated(*v) + } + return _c +} + +// SetID sets the "id" field. +func (_c *GCPServiceAccountCreate) SetID(v uuid.UUID) *GCPServiceAccountCreate { + _c.mutation.SetID(v) + return _c +} + +// SetNillableID sets the "id" field if the given value is not nil. +func (_c *GCPServiceAccountCreate) SetNillableID(v *uuid.UUID) *GCPServiceAccountCreate { + if v != nil { + _c.SetID(*v) + } + return _c +} + +// Mutation returns the GCPServiceAccountMutation object of the builder. +func (_c *GCPServiceAccountCreate) Mutation() *GCPServiceAccountMutation { + return _c.mutation +} + +// Save creates the GCPServiceAccount in the database. +func (_c *GCPServiceAccountCreate) Save(ctx context.Context) (*GCPServiceAccount, error) { + _c.defaults() + return withHooks(ctx, _c.sqlSave, _c.mutation, _c.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (_c *GCPServiceAccountCreate) SaveX(ctx context.Context) *GCPServiceAccount { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *GCPServiceAccountCreate) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *GCPServiceAccountCreate) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_c *GCPServiceAccountCreate) defaults() { + if _, ok := _c.mutation.DisplayName(); !ok { + v := gcpserviceaccount.DefaultDisplayName + _c.mutation.SetDisplayName(v) + } + if _, ok := _c.mutation.DefaultScopes(); !ok { + v := gcpserviceaccount.DefaultDefaultScopes + _c.mutation.SetDefaultScopes(v) + } + if _, ok := _c.mutation.Verified(); !ok { + v := gcpserviceaccount.DefaultVerified + _c.mutation.SetVerified(v) + } + if _, ok := _c.mutation.CreatedBy(); !ok { + v := gcpserviceaccount.DefaultCreatedBy + _c.mutation.SetCreatedBy(v) + } + if _, ok := _c.mutation.Managed(); !ok { + v := gcpserviceaccount.DefaultManaged + _c.mutation.SetManaged(v) + } + if _, ok := _c.mutation.ManagedBy(); !ok { + v := gcpserviceaccount.DefaultManagedBy + _c.mutation.SetManagedBy(v) + } + if _, ok := _c.mutation.Created(); !ok { + v := gcpserviceaccount.DefaultCreated() + _c.mutation.SetCreated(v) + } + if _, ok := _c.mutation.ID(); !ok { + v := gcpserviceaccount.DefaultID() + _c.mutation.SetID(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_c *GCPServiceAccountCreate) check() error { + if _, ok := _c.mutation.Scope(); !ok { + return &ValidationError{Name: "scope", err: errors.New(`ent: missing required field "GCPServiceAccount.scope"`)} + } + if v, ok := _c.mutation.Scope(); ok { + if err := gcpserviceaccount.ScopeValidator(v); err != nil { + return &ValidationError{Name: "scope", err: fmt.Errorf(`ent: validator failed for field "GCPServiceAccount.scope": %w`, err)} + } + } + if _, ok := _c.mutation.ScopeID(); !ok { + return &ValidationError{Name: "scope_id", err: errors.New(`ent: missing required field "GCPServiceAccount.scope_id"`)} + } + if v, ok := _c.mutation.ScopeID(); ok { + if err := gcpserviceaccount.ScopeIDValidator(v); err != nil { + return &ValidationError{Name: "scope_id", err: fmt.Errorf(`ent: validator failed for field "GCPServiceAccount.scope_id": %w`, err)} + } + } + if _, ok := _c.mutation.Email(); !ok { + return &ValidationError{Name: "email", err: errors.New(`ent: missing required field "GCPServiceAccount.email"`)} + } + if v, ok := _c.mutation.Email(); ok { + if err := gcpserviceaccount.EmailValidator(v); err != nil { + return &ValidationError{Name: "email", err: fmt.Errorf(`ent: validator failed for field "GCPServiceAccount.email": %w`, err)} + } + } + if _, ok := _c.mutation.ProjectID(); !ok { + return &ValidationError{Name: "project_id", err: errors.New(`ent: missing required field "GCPServiceAccount.project_id"`)} + } + if v, ok := _c.mutation.ProjectID(); ok { + if err := gcpserviceaccount.ProjectIDValidator(v); err != nil { + return &ValidationError{Name: "project_id", err: fmt.Errorf(`ent: validator failed for field "GCPServiceAccount.project_id": %w`, err)} + } + } + if _, ok := _c.mutation.DisplayName(); !ok { + return &ValidationError{Name: "display_name", err: errors.New(`ent: missing required field "GCPServiceAccount.display_name"`)} + } + if _, ok := _c.mutation.DefaultScopes(); !ok { + return &ValidationError{Name: "default_scopes", err: errors.New(`ent: missing required field "GCPServiceAccount.default_scopes"`)} + } + if _, ok := _c.mutation.Verified(); !ok { + return &ValidationError{Name: "verified", err: errors.New(`ent: missing required field "GCPServiceAccount.verified"`)} + } + if _, ok := _c.mutation.CreatedBy(); !ok { + return &ValidationError{Name: "created_by", err: errors.New(`ent: missing required field "GCPServiceAccount.created_by"`)} + } + if _, ok := _c.mutation.Managed(); !ok { + return &ValidationError{Name: "managed", err: errors.New(`ent: missing required field "GCPServiceAccount.managed"`)} + } + if _, ok := _c.mutation.ManagedBy(); !ok { + return &ValidationError{Name: "managed_by", err: errors.New(`ent: missing required field "GCPServiceAccount.managed_by"`)} + } + if _, ok := _c.mutation.Created(); !ok { + return &ValidationError{Name: "created", err: errors.New(`ent: missing required field "GCPServiceAccount.created"`)} + } + return nil +} + +func (_c *GCPServiceAccountCreate) sqlSave(ctx context.Context) (*GCPServiceAccount, error) { + if err := _c.check(); err != nil { + return nil, err + } + _node, _spec := _c.createSpec() + if err := sqlgraph.CreateNode(ctx, _c.driver, _spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + if _spec.ID.Value != nil { + if id, ok := _spec.ID.Value.(*uuid.UUID); ok { + _node.ID = *id + } else if err := _node.ID.Scan(_spec.ID.Value); err != nil { + return nil, err + } + } + _c.mutation.id = &_node.ID + _c.mutation.done = true + return _node, nil +} + +func (_c *GCPServiceAccountCreate) createSpec() (*GCPServiceAccount, *sqlgraph.CreateSpec) { + var ( + _node = &GCPServiceAccount{config: _c.config} + _spec = sqlgraph.NewCreateSpec(gcpserviceaccount.Table, sqlgraph.NewFieldSpec(gcpserviceaccount.FieldID, field.TypeUUID)) + ) + _spec.OnConflict = _c.conflict + if id, ok := _c.mutation.ID(); ok { + _node.ID = id + _spec.ID.Value = &id + } + if value, ok := _c.mutation.Scope(); ok { + _spec.SetField(gcpserviceaccount.FieldScope, field.TypeString, value) + _node.Scope = value + } + if value, ok := _c.mutation.ScopeID(); ok { + _spec.SetField(gcpserviceaccount.FieldScopeID, field.TypeString, value) + _node.ScopeID = value + } + if value, ok := _c.mutation.Email(); ok { + _spec.SetField(gcpserviceaccount.FieldEmail, field.TypeString, value) + _node.Email = value + } + if value, ok := _c.mutation.ProjectID(); ok { + _spec.SetField(gcpserviceaccount.FieldProjectID, field.TypeString, value) + _node.ProjectID = value + } + if value, ok := _c.mutation.DisplayName(); ok { + _spec.SetField(gcpserviceaccount.FieldDisplayName, field.TypeString, value) + _node.DisplayName = value + } + if value, ok := _c.mutation.DefaultScopes(); ok { + _spec.SetField(gcpserviceaccount.FieldDefaultScopes, field.TypeString, value) + _node.DefaultScopes = value + } + if value, ok := _c.mutation.Verified(); ok { + _spec.SetField(gcpserviceaccount.FieldVerified, field.TypeBool, value) + _node.Verified = value + } + if value, ok := _c.mutation.VerifiedAt(); ok { + _spec.SetField(gcpserviceaccount.FieldVerifiedAt, field.TypeTime, value) + _node.VerifiedAt = &value + } + if value, ok := _c.mutation.CreatedBy(); ok { + _spec.SetField(gcpserviceaccount.FieldCreatedBy, field.TypeString, value) + _node.CreatedBy = value + } + if value, ok := _c.mutation.Managed(); ok { + _spec.SetField(gcpserviceaccount.FieldManaged, field.TypeBool, value) + _node.Managed = value + } + if value, ok := _c.mutation.ManagedBy(); ok { + _spec.SetField(gcpserviceaccount.FieldManagedBy, field.TypeString, value) + _node.ManagedBy = value + } + if value, ok := _c.mutation.Created(); ok { + _spec.SetField(gcpserviceaccount.FieldCreated, field.TypeTime, value) + _node.Created = value + } + return _node, _spec +} + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.GCPServiceAccount.Create(). +// SetScope(v). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.GCPServiceAccountUpsert) { +// SetScope(v+v). +// }). +// Exec(ctx) +func (_c *GCPServiceAccountCreate) OnConflict(opts ...sql.ConflictOption) *GCPServiceAccountUpsertOne { + _c.conflict = opts + return &GCPServiceAccountUpsertOne{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.GCPServiceAccount.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *GCPServiceAccountCreate) OnConflictColumns(columns ...string) *GCPServiceAccountUpsertOne { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &GCPServiceAccountUpsertOne{ + create: _c, + } +} + +type ( + // GCPServiceAccountUpsertOne is the builder for "upsert"-ing + // one GCPServiceAccount node. + GCPServiceAccountUpsertOne struct { + create *GCPServiceAccountCreate + } + + // GCPServiceAccountUpsert is the "OnConflict" setter. + GCPServiceAccountUpsert struct { + *sql.UpdateSet + } +) + +// SetScope sets the "scope" field. +func (u *GCPServiceAccountUpsert) SetScope(v string) *GCPServiceAccountUpsert { + u.Set(gcpserviceaccount.FieldScope, v) + return u +} + +// UpdateScope sets the "scope" field to the value that was provided on create. +func (u *GCPServiceAccountUpsert) UpdateScope() *GCPServiceAccountUpsert { + u.SetExcluded(gcpserviceaccount.FieldScope) + return u +} + +// SetScopeID sets the "scope_id" field. +func (u *GCPServiceAccountUpsert) SetScopeID(v string) *GCPServiceAccountUpsert { + u.Set(gcpserviceaccount.FieldScopeID, v) + return u +} + +// UpdateScopeID sets the "scope_id" field to the value that was provided on create. +func (u *GCPServiceAccountUpsert) UpdateScopeID() *GCPServiceAccountUpsert { + u.SetExcluded(gcpserviceaccount.FieldScopeID) + return u +} + +// SetEmail sets the "email" field. +func (u *GCPServiceAccountUpsert) SetEmail(v string) *GCPServiceAccountUpsert { + u.Set(gcpserviceaccount.FieldEmail, v) + return u +} + +// UpdateEmail sets the "email" field to the value that was provided on create. +func (u *GCPServiceAccountUpsert) UpdateEmail() *GCPServiceAccountUpsert { + u.SetExcluded(gcpserviceaccount.FieldEmail) + return u +} + +// SetProjectID sets the "project_id" field. +func (u *GCPServiceAccountUpsert) SetProjectID(v string) *GCPServiceAccountUpsert { + u.Set(gcpserviceaccount.FieldProjectID, v) + return u +} + +// UpdateProjectID sets the "project_id" field to the value that was provided on create. +func (u *GCPServiceAccountUpsert) UpdateProjectID() *GCPServiceAccountUpsert { + u.SetExcluded(gcpserviceaccount.FieldProjectID) + return u +} + +// SetDisplayName sets the "display_name" field. +func (u *GCPServiceAccountUpsert) SetDisplayName(v string) *GCPServiceAccountUpsert { + u.Set(gcpserviceaccount.FieldDisplayName, v) + return u +} + +// UpdateDisplayName sets the "display_name" field to the value that was provided on create. +func (u *GCPServiceAccountUpsert) UpdateDisplayName() *GCPServiceAccountUpsert { + u.SetExcluded(gcpserviceaccount.FieldDisplayName) + return u +} + +// SetDefaultScopes sets the "default_scopes" field. +func (u *GCPServiceAccountUpsert) SetDefaultScopes(v string) *GCPServiceAccountUpsert { + u.Set(gcpserviceaccount.FieldDefaultScopes, v) + return u +} + +// UpdateDefaultScopes sets the "default_scopes" field to the value that was provided on create. +func (u *GCPServiceAccountUpsert) UpdateDefaultScopes() *GCPServiceAccountUpsert { + u.SetExcluded(gcpserviceaccount.FieldDefaultScopes) + return u +} + +// SetVerified sets the "verified" field. +func (u *GCPServiceAccountUpsert) SetVerified(v bool) *GCPServiceAccountUpsert { + u.Set(gcpserviceaccount.FieldVerified, v) + return u +} + +// UpdateVerified sets the "verified" field to the value that was provided on create. +func (u *GCPServiceAccountUpsert) UpdateVerified() *GCPServiceAccountUpsert { + u.SetExcluded(gcpserviceaccount.FieldVerified) + return u +} + +// SetVerifiedAt sets the "verified_at" field. +func (u *GCPServiceAccountUpsert) SetVerifiedAt(v time.Time) *GCPServiceAccountUpsert { + u.Set(gcpserviceaccount.FieldVerifiedAt, v) + return u +} + +// UpdateVerifiedAt sets the "verified_at" field to the value that was provided on create. +func (u *GCPServiceAccountUpsert) UpdateVerifiedAt() *GCPServiceAccountUpsert { + u.SetExcluded(gcpserviceaccount.FieldVerifiedAt) + return u +} + +// ClearVerifiedAt clears the value of the "verified_at" field. +func (u *GCPServiceAccountUpsert) ClearVerifiedAt() *GCPServiceAccountUpsert { + u.SetNull(gcpserviceaccount.FieldVerifiedAt) + return u +} + +// SetCreatedBy sets the "created_by" field. +func (u *GCPServiceAccountUpsert) SetCreatedBy(v string) *GCPServiceAccountUpsert { + u.Set(gcpserviceaccount.FieldCreatedBy, v) + return u +} + +// UpdateCreatedBy sets the "created_by" field to the value that was provided on create. +func (u *GCPServiceAccountUpsert) UpdateCreatedBy() *GCPServiceAccountUpsert { + u.SetExcluded(gcpserviceaccount.FieldCreatedBy) + return u +} + +// SetManaged sets the "managed" field. +func (u *GCPServiceAccountUpsert) SetManaged(v bool) *GCPServiceAccountUpsert { + u.Set(gcpserviceaccount.FieldManaged, v) + return u +} + +// UpdateManaged sets the "managed" field to the value that was provided on create. +func (u *GCPServiceAccountUpsert) UpdateManaged() *GCPServiceAccountUpsert { + u.SetExcluded(gcpserviceaccount.FieldManaged) + return u +} + +// SetManagedBy sets the "managed_by" field. +func (u *GCPServiceAccountUpsert) SetManagedBy(v string) *GCPServiceAccountUpsert { + u.Set(gcpserviceaccount.FieldManagedBy, v) + return u +} + +// UpdateManagedBy sets the "managed_by" field to the value that was provided on create. +func (u *GCPServiceAccountUpsert) UpdateManagedBy() *GCPServiceAccountUpsert { + u.SetExcluded(gcpserviceaccount.FieldManagedBy) + return u +} + +// UpdateNewValues updates the mutable fields using the new values that were set on create except the ID field. +// Using this option is equivalent to using: +// +// client.GCPServiceAccount.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(gcpserviceaccount.FieldID) +// }), +// ). +// Exec(ctx) +func (u *GCPServiceAccountUpsertOne) UpdateNewValues() *GCPServiceAccountUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + if _, exists := u.create.mutation.ID(); exists { + s.SetIgnore(gcpserviceaccount.FieldID) + } + if _, exists := u.create.mutation.Created(); exists { + s.SetIgnore(gcpserviceaccount.FieldCreated) + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.GCPServiceAccount.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *GCPServiceAccountUpsertOne) Ignore() *GCPServiceAccountUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *GCPServiceAccountUpsertOne) DoNothing() *GCPServiceAccountUpsertOne { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the GCPServiceAccountCreate.OnConflict +// documentation for more info. +func (u *GCPServiceAccountUpsertOne) Update(set func(*GCPServiceAccountUpsert)) *GCPServiceAccountUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&GCPServiceAccountUpsert{UpdateSet: update}) + })) + return u +} + +// SetScope sets the "scope" field. +func (u *GCPServiceAccountUpsertOne) SetScope(v string) *GCPServiceAccountUpsertOne { + return u.Update(func(s *GCPServiceAccountUpsert) { + s.SetScope(v) + }) +} + +// UpdateScope sets the "scope" field to the value that was provided on create. +func (u *GCPServiceAccountUpsertOne) UpdateScope() *GCPServiceAccountUpsertOne { + return u.Update(func(s *GCPServiceAccountUpsert) { + s.UpdateScope() + }) +} + +// SetScopeID sets the "scope_id" field. +func (u *GCPServiceAccountUpsertOne) SetScopeID(v string) *GCPServiceAccountUpsertOne { + return u.Update(func(s *GCPServiceAccountUpsert) { + s.SetScopeID(v) + }) +} + +// UpdateScopeID sets the "scope_id" field to the value that was provided on create. +func (u *GCPServiceAccountUpsertOne) UpdateScopeID() *GCPServiceAccountUpsertOne { + return u.Update(func(s *GCPServiceAccountUpsert) { + s.UpdateScopeID() + }) +} + +// SetEmail sets the "email" field. +func (u *GCPServiceAccountUpsertOne) SetEmail(v string) *GCPServiceAccountUpsertOne { + return u.Update(func(s *GCPServiceAccountUpsert) { + s.SetEmail(v) + }) +} + +// UpdateEmail sets the "email" field to the value that was provided on create. +func (u *GCPServiceAccountUpsertOne) UpdateEmail() *GCPServiceAccountUpsertOne { + return u.Update(func(s *GCPServiceAccountUpsert) { + s.UpdateEmail() + }) +} + +// SetProjectID sets the "project_id" field. +func (u *GCPServiceAccountUpsertOne) SetProjectID(v string) *GCPServiceAccountUpsertOne { + return u.Update(func(s *GCPServiceAccountUpsert) { + s.SetProjectID(v) + }) +} + +// UpdateProjectID sets the "project_id" field to the value that was provided on create. +func (u *GCPServiceAccountUpsertOne) UpdateProjectID() *GCPServiceAccountUpsertOne { + return u.Update(func(s *GCPServiceAccountUpsert) { + s.UpdateProjectID() + }) +} + +// SetDisplayName sets the "display_name" field. +func (u *GCPServiceAccountUpsertOne) SetDisplayName(v string) *GCPServiceAccountUpsertOne { + return u.Update(func(s *GCPServiceAccountUpsert) { + s.SetDisplayName(v) + }) +} + +// UpdateDisplayName sets the "display_name" field to the value that was provided on create. +func (u *GCPServiceAccountUpsertOne) UpdateDisplayName() *GCPServiceAccountUpsertOne { + return u.Update(func(s *GCPServiceAccountUpsert) { + s.UpdateDisplayName() + }) +} + +// SetDefaultScopes sets the "default_scopes" field. +func (u *GCPServiceAccountUpsertOne) SetDefaultScopes(v string) *GCPServiceAccountUpsertOne { + return u.Update(func(s *GCPServiceAccountUpsert) { + s.SetDefaultScopes(v) + }) +} + +// UpdateDefaultScopes sets the "default_scopes" field to the value that was provided on create. +func (u *GCPServiceAccountUpsertOne) UpdateDefaultScopes() *GCPServiceAccountUpsertOne { + return u.Update(func(s *GCPServiceAccountUpsert) { + s.UpdateDefaultScopes() + }) +} + +// SetVerified sets the "verified" field. +func (u *GCPServiceAccountUpsertOne) SetVerified(v bool) *GCPServiceAccountUpsertOne { + return u.Update(func(s *GCPServiceAccountUpsert) { + s.SetVerified(v) + }) +} + +// UpdateVerified sets the "verified" field to the value that was provided on create. +func (u *GCPServiceAccountUpsertOne) UpdateVerified() *GCPServiceAccountUpsertOne { + return u.Update(func(s *GCPServiceAccountUpsert) { + s.UpdateVerified() + }) +} + +// SetVerifiedAt sets the "verified_at" field. +func (u *GCPServiceAccountUpsertOne) SetVerifiedAt(v time.Time) *GCPServiceAccountUpsertOne { + return u.Update(func(s *GCPServiceAccountUpsert) { + s.SetVerifiedAt(v) + }) +} + +// UpdateVerifiedAt sets the "verified_at" field to the value that was provided on create. +func (u *GCPServiceAccountUpsertOne) UpdateVerifiedAt() *GCPServiceAccountUpsertOne { + return u.Update(func(s *GCPServiceAccountUpsert) { + s.UpdateVerifiedAt() + }) +} + +// ClearVerifiedAt clears the value of the "verified_at" field. +func (u *GCPServiceAccountUpsertOne) ClearVerifiedAt() *GCPServiceAccountUpsertOne { + return u.Update(func(s *GCPServiceAccountUpsert) { + s.ClearVerifiedAt() + }) +} + +// SetCreatedBy sets the "created_by" field. +func (u *GCPServiceAccountUpsertOne) SetCreatedBy(v string) *GCPServiceAccountUpsertOne { + return u.Update(func(s *GCPServiceAccountUpsert) { + s.SetCreatedBy(v) + }) +} + +// UpdateCreatedBy sets the "created_by" field to the value that was provided on create. +func (u *GCPServiceAccountUpsertOne) UpdateCreatedBy() *GCPServiceAccountUpsertOne { + return u.Update(func(s *GCPServiceAccountUpsert) { + s.UpdateCreatedBy() + }) +} + +// SetManaged sets the "managed" field. +func (u *GCPServiceAccountUpsertOne) SetManaged(v bool) *GCPServiceAccountUpsertOne { + return u.Update(func(s *GCPServiceAccountUpsert) { + s.SetManaged(v) + }) +} + +// UpdateManaged sets the "managed" field to the value that was provided on create. +func (u *GCPServiceAccountUpsertOne) UpdateManaged() *GCPServiceAccountUpsertOne { + return u.Update(func(s *GCPServiceAccountUpsert) { + s.UpdateManaged() + }) +} + +// SetManagedBy sets the "managed_by" field. +func (u *GCPServiceAccountUpsertOne) SetManagedBy(v string) *GCPServiceAccountUpsertOne { + return u.Update(func(s *GCPServiceAccountUpsert) { + s.SetManagedBy(v) + }) +} + +// UpdateManagedBy sets the "managed_by" field to the value that was provided on create. +func (u *GCPServiceAccountUpsertOne) UpdateManagedBy() *GCPServiceAccountUpsertOne { + return u.Update(func(s *GCPServiceAccountUpsert) { + s.UpdateManagedBy() + }) +} + +// Exec executes the query. +func (u *GCPServiceAccountUpsertOne) Exec(ctx context.Context) error { + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for GCPServiceAccountCreate.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *GCPServiceAccountUpsertOne) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} + +// Exec executes the UPSERT query and returns the inserted/updated ID. +func (u *GCPServiceAccountUpsertOne) ID(ctx context.Context) (id uuid.UUID, err error) { + if u.create.driver.Dialect() == dialect.MySQL { + // In case of "ON CONFLICT", there is no way to get back non-numeric ID + // fields from the database since MySQL does not support the RETURNING clause. + return id, errors.New("ent: GCPServiceAccountUpsertOne.ID is not supported by MySQL driver. Use GCPServiceAccountUpsertOne.Exec instead") + } + node, err := u.create.Save(ctx) + if err != nil { + return id, err + } + return node.ID, nil +} + +// IDX is like ID, but panics if an error occurs. +func (u *GCPServiceAccountUpsertOne) IDX(ctx context.Context) uuid.UUID { + id, err := u.ID(ctx) + if err != nil { + panic(err) + } + return id +} + +// GCPServiceAccountCreateBulk is the builder for creating many GCPServiceAccount entities in bulk. +type GCPServiceAccountCreateBulk struct { + config + err error + builders []*GCPServiceAccountCreate + conflict []sql.ConflictOption +} + +// Save creates the GCPServiceAccount entities in the database. +func (_c *GCPServiceAccountCreateBulk) Save(ctx context.Context) ([]*GCPServiceAccount, error) { + if _c.err != nil { + return nil, _c.err + } + specs := make([]*sqlgraph.CreateSpec, len(_c.builders)) + nodes := make([]*GCPServiceAccount, len(_c.builders)) + mutators := make([]Mutator, len(_c.builders)) + for i := range _c.builders { + func(i int, root context.Context) { + builder := _c.builders[i] + builder.defaults() + var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { + mutation, ok := m.(*GCPServiceAccountMutation) + if !ok { + return nil, fmt.Errorf("unexpected mutation type %T", m) + } + if err := builder.check(); err != nil { + return nil, err + } + builder.mutation = mutation + var err error + nodes[i], specs[i] = builder.createSpec() + if i < len(mutators)-1 { + _, err = mutators[i+1].Mutate(root, _c.builders[i+1].mutation) + } else { + spec := &sqlgraph.BatchCreateSpec{Nodes: specs} + spec.OnConflict = _c.conflict + // Invoke the actual operation on the latest mutation in the chain. + if err = sqlgraph.BatchCreate(ctx, _c.driver, spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + } + } + if err != nil { + return nil, err + } + mutation.id = &nodes[i].ID + mutation.done = true + return nodes[i], nil + }) + for i := len(builder.hooks) - 1; i >= 0; i-- { + mut = builder.hooks[i](mut) + } + mutators[i] = mut + }(i, ctx) + } + if len(mutators) > 0 { + if _, err := mutators[0].Mutate(ctx, _c.builders[0].mutation); err != nil { + return nil, err + } + } + return nodes, nil +} + +// SaveX is like Save, but panics if an error occurs. +func (_c *GCPServiceAccountCreateBulk) SaveX(ctx context.Context) []*GCPServiceAccount { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *GCPServiceAccountCreateBulk) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *GCPServiceAccountCreateBulk) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.GCPServiceAccount.CreateBulk(builders...). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.GCPServiceAccountUpsert) { +// SetScope(v+v). +// }). +// Exec(ctx) +func (_c *GCPServiceAccountCreateBulk) OnConflict(opts ...sql.ConflictOption) *GCPServiceAccountUpsertBulk { + _c.conflict = opts + return &GCPServiceAccountUpsertBulk{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.GCPServiceAccount.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *GCPServiceAccountCreateBulk) OnConflictColumns(columns ...string) *GCPServiceAccountUpsertBulk { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &GCPServiceAccountUpsertBulk{ + create: _c, + } +} + +// GCPServiceAccountUpsertBulk is the builder for "upsert"-ing +// a bulk of GCPServiceAccount nodes. +type GCPServiceAccountUpsertBulk struct { + create *GCPServiceAccountCreateBulk +} + +// UpdateNewValues updates the mutable fields using the new values that +// were set on create. Using this option is equivalent to using: +// +// client.GCPServiceAccount.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(gcpserviceaccount.FieldID) +// }), +// ). +// Exec(ctx) +func (u *GCPServiceAccountUpsertBulk) UpdateNewValues() *GCPServiceAccountUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + for _, b := range u.create.builders { + if _, exists := b.mutation.ID(); exists { + s.SetIgnore(gcpserviceaccount.FieldID) + } + if _, exists := b.mutation.Created(); exists { + s.SetIgnore(gcpserviceaccount.FieldCreated) + } + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.GCPServiceAccount.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *GCPServiceAccountUpsertBulk) Ignore() *GCPServiceAccountUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *GCPServiceAccountUpsertBulk) DoNothing() *GCPServiceAccountUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the GCPServiceAccountCreateBulk.OnConflict +// documentation for more info. +func (u *GCPServiceAccountUpsertBulk) Update(set func(*GCPServiceAccountUpsert)) *GCPServiceAccountUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&GCPServiceAccountUpsert{UpdateSet: update}) + })) + return u +} + +// SetScope sets the "scope" field. +func (u *GCPServiceAccountUpsertBulk) SetScope(v string) *GCPServiceAccountUpsertBulk { + return u.Update(func(s *GCPServiceAccountUpsert) { + s.SetScope(v) + }) +} + +// UpdateScope sets the "scope" field to the value that was provided on create. +func (u *GCPServiceAccountUpsertBulk) UpdateScope() *GCPServiceAccountUpsertBulk { + return u.Update(func(s *GCPServiceAccountUpsert) { + s.UpdateScope() + }) +} + +// SetScopeID sets the "scope_id" field. +func (u *GCPServiceAccountUpsertBulk) SetScopeID(v string) *GCPServiceAccountUpsertBulk { + return u.Update(func(s *GCPServiceAccountUpsert) { + s.SetScopeID(v) + }) +} + +// UpdateScopeID sets the "scope_id" field to the value that was provided on create. +func (u *GCPServiceAccountUpsertBulk) UpdateScopeID() *GCPServiceAccountUpsertBulk { + return u.Update(func(s *GCPServiceAccountUpsert) { + s.UpdateScopeID() + }) +} + +// SetEmail sets the "email" field. +func (u *GCPServiceAccountUpsertBulk) SetEmail(v string) *GCPServiceAccountUpsertBulk { + return u.Update(func(s *GCPServiceAccountUpsert) { + s.SetEmail(v) + }) +} + +// UpdateEmail sets the "email" field to the value that was provided on create. +func (u *GCPServiceAccountUpsertBulk) UpdateEmail() *GCPServiceAccountUpsertBulk { + return u.Update(func(s *GCPServiceAccountUpsert) { + s.UpdateEmail() + }) +} + +// SetProjectID sets the "project_id" field. +func (u *GCPServiceAccountUpsertBulk) SetProjectID(v string) *GCPServiceAccountUpsertBulk { + return u.Update(func(s *GCPServiceAccountUpsert) { + s.SetProjectID(v) + }) +} + +// UpdateProjectID sets the "project_id" field to the value that was provided on create. +func (u *GCPServiceAccountUpsertBulk) UpdateProjectID() *GCPServiceAccountUpsertBulk { + return u.Update(func(s *GCPServiceAccountUpsert) { + s.UpdateProjectID() + }) +} + +// SetDisplayName sets the "display_name" field. +func (u *GCPServiceAccountUpsertBulk) SetDisplayName(v string) *GCPServiceAccountUpsertBulk { + return u.Update(func(s *GCPServiceAccountUpsert) { + s.SetDisplayName(v) + }) +} + +// UpdateDisplayName sets the "display_name" field to the value that was provided on create. +func (u *GCPServiceAccountUpsertBulk) UpdateDisplayName() *GCPServiceAccountUpsertBulk { + return u.Update(func(s *GCPServiceAccountUpsert) { + s.UpdateDisplayName() + }) +} + +// SetDefaultScopes sets the "default_scopes" field. +func (u *GCPServiceAccountUpsertBulk) SetDefaultScopes(v string) *GCPServiceAccountUpsertBulk { + return u.Update(func(s *GCPServiceAccountUpsert) { + s.SetDefaultScopes(v) + }) +} + +// UpdateDefaultScopes sets the "default_scopes" field to the value that was provided on create. +func (u *GCPServiceAccountUpsertBulk) UpdateDefaultScopes() *GCPServiceAccountUpsertBulk { + return u.Update(func(s *GCPServiceAccountUpsert) { + s.UpdateDefaultScopes() + }) +} + +// SetVerified sets the "verified" field. +func (u *GCPServiceAccountUpsertBulk) SetVerified(v bool) *GCPServiceAccountUpsertBulk { + return u.Update(func(s *GCPServiceAccountUpsert) { + s.SetVerified(v) + }) +} + +// UpdateVerified sets the "verified" field to the value that was provided on create. +func (u *GCPServiceAccountUpsertBulk) UpdateVerified() *GCPServiceAccountUpsertBulk { + return u.Update(func(s *GCPServiceAccountUpsert) { + s.UpdateVerified() + }) +} + +// SetVerifiedAt sets the "verified_at" field. +func (u *GCPServiceAccountUpsertBulk) SetVerifiedAt(v time.Time) *GCPServiceAccountUpsertBulk { + return u.Update(func(s *GCPServiceAccountUpsert) { + s.SetVerifiedAt(v) + }) +} + +// UpdateVerifiedAt sets the "verified_at" field to the value that was provided on create. +func (u *GCPServiceAccountUpsertBulk) UpdateVerifiedAt() *GCPServiceAccountUpsertBulk { + return u.Update(func(s *GCPServiceAccountUpsert) { + s.UpdateVerifiedAt() + }) +} + +// ClearVerifiedAt clears the value of the "verified_at" field. +func (u *GCPServiceAccountUpsertBulk) ClearVerifiedAt() *GCPServiceAccountUpsertBulk { + return u.Update(func(s *GCPServiceAccountUpsert) { + s.ClearVerifiedAt() + }) +} + +// SetCreatedBy sets the "created_by" field. +func (u *GCPServiceAccountUpsertBulk) SetCreatedBy(v string) *GCPServiceAccountUpsertBulk { + return u.Update(func(s *GCPServiceAccountUpsert) { + s.SetCreatedBy(v) + }) +} + +// UpdateCreatedBy sets the "created_by" field to the value that was provided on create. +func (u *GCPServiceAccountUpsertBulk) UpdateCreatedBy() *GCPServiceAccountUpsertBulk { + return u.Update(func(s *GCPServiceAccountUpsert) { + s.UpdateCreatedBy() + }) +} + +// SetManaged sets the "managed" field. +func (u *GCPServiceAccountUpsertBulk) SetManaged(v bool) *GCPServiceAccountUpsertBulk { + return u.Update(func(s *GCPServiceAccountUpsert) { + s.SetManaged(v) + }) +} + +// UpdateManaged sets the "managed" field to the value that was provided on create. +func (u *GCPServiceAccountUpsertBulk) UpdateManaged() *GCPServiceAccountUpsertBulk { + return u.Update(func(s *GCPServiceAccountUpsert) { + s.UpdateManaged() + }) +} + +// SetManagedBy sets the "managed_by" field. +func (u *GCPServiceAccountUpsertBulk) SetManagedBy(v string) *GCPServiceAccountUpsertBulk { + return u.Update(func(s *GCPServiceAccountUpsert) { + s.SetManagedBy(v) + }) +} + +// UpdateManagedBy sets the "managed_by" field to the value that was provided on create. +func (u *GCPServiceAccountUpsertBulk) UpdateManagedBy() *GCPServiceAccountUpsertBulk { + return u.Update(func(s *GCPServiceAccountUpsert) { + s.UpdateManagedBy() + }) +} + +// Exec executes the query. +func (u *GCPServiceAccountUpsertBulk) Exec(ctx context.Context) error { + if u.create.err != nil { + return u.create.err + } + for i, b := range u.create.builders { + if len(b.conflict) != 0 { + return fmt.Errorf("ent: OnConflict was set for builder %d. Set it on the GCPServiceAccountCreateBulk instead", i) + } + } + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for GCPServiceAccountCreateBulk.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *GCPServiceAccountUpsertBulk) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/pkg/ent/gcpserviceaccount_delete.go b/pkg/ent/gcpserviceaccount_delete.go new file mode 100644 index 000000000..5fbc7e0cc --- /dev/null +++ b/pkg/ent/gcpserviceaccount_delete.go @@ -0,0 +1,88 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/gcpserviceaccount" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" +) + +// GCPServiceAccountDelete is the builder for deleting a GCPServiceAccount entity. +type GCPServiceAccountDelete struct { + config + hooks []Hook + mutation *GCPServiceAccountMutation +} + +// Where appends a list predicates to the GCPServiceAccountDelete builder. +func (_d *GCPServiceAccountDelete) Where(ps ...predicate.GCPServiceAccount) *GCPServiceAccountDelete { + _d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (_d *GCPServiceAccountDelete) Exec(ctx context.Context) (int, error) { + return withHooks(ctx, _d.sqlExec, _d.mutation, _d.hooks) +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *GCPServiceAccountDelete) ExecX(ctx context.Context) int { + n, err := _d.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (_d *GCPServiceAccountDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(gcpserviceaccount.Table, sqlgraph.NewFieldSpec(gcpserviceaccount.FieldID, field.TypeUUID)) + if ps := _d.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + affected, err := sqlgraph.DeleteNodes(ctx, _d.driver, _spec) + if err != nil && sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + _d.mutation.done = true + return affected, err +} + +// GCPServiceAccountDeleteOne is the builder for deleting a single GCPServiceAccount entity. +type GCPServiceAccountDeleteOne struct { + _d *GCPServiceAccountDelete +} + +// Where appends a list predicates to the GCPServiceAccountDelete builder. +func (_d *GCPServiceAccountDeleteOne) Where(ps ...predicate.GCPServiceAccount) *GCPServiceAccountDeleteOne { + _d._d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query. +func (_d *GCPServiceAccountDeleteOne) Exec(ctx context.Context) error { + n, err := _d._d.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{gcpserviceaccount.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *GCPServiceAccountDeleteOne) ExecX(ctx context.Context) { + if err := _d.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/pkg/ent/gcpserviceaccount_query.go b/pkg/ent/gcpserviceaccount_query.go new file mode 100644 index 000000000..fe5657d10 --- /dev/null +++ b/pkg/ent/gcpserviceaccount_query.go @@ -0,0 +1,565 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "fmt" + "math" + + "entgo.io/ent" + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/gcpserviceaccount" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/google/uuid" +) + +// GCPServiceAccountQuery is the builder for querying GCPServiceAccount entities. +type GCPServiceAccountQuery struct { + config + ctx *QueryContext + order []gcpserviceaccount.OrderOption + inters []Interceptor + predicates []predicate.GCPServiceAccount + modifiers []func(*sql.Selector) + // intermediate query (i.e. traversal path). + sql *sql.Selector + path func(context.Context) (*sql.Selector, error) +} + +// Where adds a new predicate for the GCPServiceAccountQuery builder. +func (_q *GCPServiceAccountQuery) Where(ps ...predicate.GCPServiceAccount) *GCPServiceAccountQuery { + _q.predicates = append(_q.predicates, ps...) + return _q +} + +// Limit the number of records to be returned by this query. +func (_q *GCPServiceAccountQuery) Limit(limit int) *GCPServiceAccountQuery { + _q.ctx.Limit = &limit + return _q +} + +// Offset to start from. +func (_q *GCPServiceAccountQuery) Offset(offset int) *GCPServiceAccountQuery { + _q.ctx.Offset = &offset + return _q +} + +// Unique configures the query builder to filter duplicate records on query. +// By default, unique is set to true, and can be disabled using this method. +func (_q *GCPServiceAccountQuery) Unique(unique bool) *GCPServiceAccountQuery { + _q.ctx.Unique = &unique + return _q +} + +// Order specifies how the records should be ordered. +func (_q *GCPServiceAccountQuery) Order(o ...gcpserviceaccount.OrderOption) *GCPServiceAccountQuery { + _q.order = append(_q.order, o...) + return _q +} + +// First returns the first GCPServiceAccount entity from the query. +// Returns a *NotFoundError when no GCPServiceAccount was found. +func (_q *GCPServiceAccountQuery) First(ctx context.Context) (*GCPServiceAccount, error) { + nodes, err := _q.Limit(1).All(setContextOp(ctx, _q.ctx, ent.OpQueryFirst)) + if err != nil { + return nil, err + } + if len(nodes) == 0 { + return nil, &NotFoundError{gcpserviceaccount.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (_q *GCPServiceAccountQuery) FirstX(ctx context.Context) *GCPServiceAccount { + node, err := _q.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// FirstID returns the first GCPServiceAccount ID from the query. +// Returns a *NotFoundError when no GCPServiceAccount ID was found. +func (_q *GCPServiceAccountQuery) FirstID(ctx context.Context) (id uuid.UUID, err error) { + var ids []uuid.UUID + if ids, err = _q.Limit(1).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryFirstID)); err != nil { + return + } + if len(ids) == 0 { + err = &NotFoundError{gcpserviceaccount.Label} + return + } + return ids[0], nil +} + +// FirstIDX is like FirstID, but panics if an error occurs. +func (_q *GCPServiceAccountQuery) FirstIDX(ctx context.Context) uuid.UUID { + id, err := _q.FirstID(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return id +} + +// Only returns a single GCPServiceAccount entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one GCPServiceAccount entity is found. +// Returns a *NotFoundError when no GCPServiceAccount entities are found. +func (_q *GCPServiceAccountQuery) Only(ctx context.Context) (*GCPServiceAccount, error) { + nodes, err := _q.Limit(2).All(setContextOp(ctx, _q.ctx, ent.OpQueryOnly)) + if err != nil { + return nil, err + } + switch len(nodes) { + case 1: + return nodes[0], nil + case 0: + return nil, &NotFoundError{gcpserviceaccount.Label} + default: + return nil, &NotSingularError{gcpserviceaccount.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (_q *GCPServiceAccountQuery) OnlyX(ctx context.Context) *GCPServiceAccount { + node, err := _q.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// OnlyID is like Only, but returns the only GCPServiceAccount ID in the query. +// Returns a *NotSingularError when more than one GCPServiceAccount ID is found. +// Returns a *NotFoundError when no entities are found. +func (_q *GCPServiceAccountQuery) OnlyID(ctx context.Context) (id uuid.UUID, err error) { + var ids []uuid.UUID + if ids, err = _q.Limit(2).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryOnlyID)); err != nil { + return + } + switch len(ids) { + case 1: + id = ids[0] + case 0: + err = &NotFoundError{gcpserviceaccount.Label} + default: + err = &NotSingularError{gcpserviceaccount.Label} + } + return +} + +// OnlyIDX is like OnlyID, but panics if an error occurs. +func (_q *GCPServiceAccountQuery) OnlyIDX(ctx context.Context) uuid.UUID { + id, err := _q.OnlyID(ctx) + if err != nil { + panic(err) + } + return id +} + +// All executes the query and returns a list of GCPServiceAccounts. +func (_q *GCPServiceAccountQuery) All(ctx context.Context) ([]*GCPServiceAccount, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryAll) + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*GCPServiceAccount, *GCPServiceAccountQuery]() + return withInterceptors[[]*GCPServiceAccount](ctx, _q, qr, _q.inters) +} + +// AllX is like All, but panics if an error occurs. +func (_q *GCPServiceAccountQuery) AllX(ctx context.Context) []*GCPServiceAccount { + nodes, err := _q.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// IDs executes the query and returns a list of GCPServiceAccount IDs. +func (_q *GCPServiceAccountQuery) IDs(ctx context.Context) (ids []uuid.UUID, err error) { + if _q.ctx.Unique == nil && _q.path != nil { + _q.Unique(true) + } + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryIDs) + if err = _q.Select(gcpserviceaccount.FieldID).Scan(ctx, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// IDsX is like IDs, but panics if an error occurs. +func (_q *GCPServiceAccountQuery) IDsX(ctx context.Context) []uuid.UUID { + ids, err := _q.IDs(ctx) + if err != nil { + panic(err) + } + return ids +} + +// Count returns the count of the given query. +func (_q *GCPServiceAccountQuery) Count(ctx context.Context) (int, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryCount) + if err := _q.prepareQuery(ctx); err != nil { + return 0, err + } + return withInterceptors[int](ctx, _q, querierCount[*GCPServiceAccountQuery](), _q.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (_q *GCPServiceAccountQuery) CountX(ctx context.Context) int { + count, err := _q.Count(ctx) + if err != nil { + panic(err) + } + return count +} + +// Exist returns true if the query has elements in the graph. +func (_q *GCPServiceAccountQuery) Exist(ctx context.Context) (bool, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryExist) + switch _, err := _q.FirstID(ctx); { + case IsNotFound(err): + return false, nil + case err != nil: + return false, fmt.Errorf("ent: check existence: %w", err) + default: + return true, nil + } +} + +// ExistX is like Exist, but panics if an error occurs. +func (_q *GCPServiceAccountQuery) ExistX(ctx context.Context) bool { + exist, err := _q.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the GCPServiceAccountQuery builder, including all associated steps. It can be +// used to prepare common query builders and use them differently after the clone is made. +func (_q *GCPServiceAccountQuery) Clone() *GCPServiceAccountQuery { + if _q == nil { + return nil + } + return &GCPServiceAccountQuery{ + config: _q.config, + ctx: _q.ctx.Clone(), + order: append([]gcpserviceaccount.OrderOption{}, _q.order...), + inters: append([]Interceptor{}, _q.inters...), + predicates: append([]predicate.GCPServiceAccount{}, _q.predicates...), + // clone intermediate query. + sql: _q.sql.Clone(), + path: _q.path, + } +} + +// GroupBy is used to group vertices by one or more fields/columns. +// It is often used with aggregate functions, like: count, max, mean, min, sum. +// +// Example: +// +// var v []struct { +// Scope string `json:"scope,omitempty"` +// Count int `json:"count,omitempty"` +// } +// +// client.GCPServiceAccount.Query(). +// GroupBy(gcpserviceaccount.FieldScope). +// Aggregate(ent.Count()). +// Scan(ctx, &v) +func (_q *GCPServiceAccountQuery) GroupBy(field string, fields ...string) *GCPServiceAccountGroupBy { + _q.ctx.Fields = append([]string{field}, fields...) + grbuild := &GCPServiceAccountGroupBy{build: _q} + grbuild.flds = &_q.ctx.Fields + grbuild.label = gcpserviceaccount.Label + grbuild.scan = grbuild.Scan + return grbuild +} + +// Select allows the selection one or more fields/columns for the given query, +// instead of selecting all fields in the entity. +// +// Example: +// +// var v []struct { +// Scope string `json:"scope,omitempty"` +// } +// +// client.GCPServiceAccount.Query(). +// Select(gcpserviceaccount.FieldScope). +// Scan(ctx, &v) +func (_q *GCPServiceAccountQuery) Select(fields ...string) *GCPServiceAccountSelect { + _q.ctx.Fields = append(_q.ctx.Fields, fields...) + sbuild := &GCPServiceAccountSelect{GCPServiceAccountQuery: _q} + sbuild.label = gcpserviceaccount.Label + sbuild.flds, sbuild.scan = &_q.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a GCPServiceAccountSelect configured with the given aggregations. +func (_q *GCPServiceAccountQuery) Aggregate(fns ...AggregateFunc) *GCPServiceAccountSelect { + return _q.Select().Aggregate(fns...) +} + +func (_q *GCPServiceAccountQuery) prepareQuery(ctx context.Context) error { + for _, inter := range _q.inters { + if inter == nil { + return fmt.Errorf("ent: uninitialized interceptor (forgotten import ent/runtime?)") + } + if trv, ok := inter.(Traverser); ok { + if err := trv.Traverse(ctx, _q); err != nil { + return err + } + } + } + for _, f := range _q.ctx.Fields { + if !gcpserviceaccount.ValidColumn(f) { + return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + } + if _q.path != nil { + prev, err := _q.path(ctx) + if err != nil { + return err + } + _q.sql = prev + } + return nil +} + +func (_q *GCPServiceAccountQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*GCPServiceAccount, error) { + var ( + nodes = []*GCPServiceAccount{} + _spec = _q.querySpec() + ) + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*GCPServiceAccount).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &GCPServiceAccount{config: _q.config} + nodes = append(nodes, node) + return node.assignValues(columns, values) + } + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } + for i := range hooks { + hooks[i](ctx, _spec) + } + if err := sqlgraph.QueryNodes(ctx, _q.driver, _spec); err != nil { + return nil, err + } + if len(nodes) == 0 { + return nodes, nil + } + return nodes, nil +} + +func (_q *GCPServiceAccountQuery) sqlCount(ctx context.Context) (int, error) { + _spec := _q.querySpec() + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } + _spec.Node.Columns = _q.ctx.Fields + if len(_q.ctx.Fields) > 0 { + _spec.Unique = _q.ctx.Unique != nil && *_q.ctx.Unique + } + return sqlgraph.CountNodes(ctx, _q.driver, _spec) +} + +func (_q *GCPServiceAccountQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(gcpserviceaccount.Table, gcpserviceaccount.Columns, sqlgraph.NewFieldSpec(gcpserviceaccount.FieldID, field.TypeUUID)) + _spec.From = _q.sql + if unique := _q.ctx.Unique; unique != nil { + _spec.Unique = *unique + } else if _q.path != nil { + _spec.Unique = true + } + if fields := _q.ctx.Fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, gcpserviceaccount.FieldID) + for i := range fields { + if fields[i] != gcpserviceaccount.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, fields[i]) + } + } + } + if ps := _q.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if limit := _q.ctx.Limit; limit != nil { + _spec.Limit = *limit + } + if offset := _q.ctx.Offset; offset != nil { + _spec.Offset = *offset + } + if ps := _q.order; len(ps) > 0 { + _spec.Order = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + return _spec +} + +func (_q *GCPServiceAccountQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(_q.driver.Dialect()) + t1 := builder.Table(gcpserviceaccount.Table) + columns := _q.ctx.Fields + if len(columns) == 0 { + columns = gcpserviceaccount.Columns + } + selector := builder.Select(t1.Columns(columns...)...).From(t1) + if _q.sql != nil { + selector = _q.sql + selector.Select(selector.Columns(columns...)...) + } + if _q.ctx.Unique != nil && *_q.ctx.Unique { + selector.Distinct() + } + for _, m := range _q.modifiers { + m(selector) + } + for _, p := range _q.predicates { + p(selector) + } + for _, p := range _q.order { + p(selector) + } + if offset := _q.ctx.Offset; offset != nil { + // limit is mandatory for offset clause. We start + // with default value, and override it below if needed. + selector.Offset(*offset).Limit(math.MaxInt32) + } + if limit := _q.ctx.Limit; limit != nil { + selector.Limit(*limit) + } + return selector +} + +// ForUpdate locks the selected rows against concurrent updates, and prevent them from being +// updated, deleted or "selected ... for update" by other sessions, until the transaction is +// either committed or rolled-back. +func (_q *GCPServiceAccountQuery) ForUpdate(opts ...sql.LockOption) *GCPServiceAccountQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForUpdate(opts...) + }) + return _q +} + +// ForShare behaves similarly to ForUpdate, except that it acquires a shared mode lock +// on any rows that are read. Other sessions can read the rows, but cannot modify them +// until your transaction commits. +func (_q *GCPServiceAccountQuery) ForShare(opts ...sql.LockOption) *GCPServiceAccountQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForShare(opts...) + }) + return _q +} + +// GCPServiceAccountGroupBy is the group-by builder for GCPServiceAccount entities. +type GCPServiceAccountGroupBy struct { + selector + build *GCPServiceAccountQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (_g *GCPServiceAccountGroupBy) Aggregate(fns ...AggregateFunc) *GCPServiceAccountGroupBy { + _g.fns = append(_g.fns, fns...) + return _g +} + +// Scan applies the selector query and scans the result into the given value. +func (_g *GCPServiceAccountGroupBy) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, _g.build.ctx, ent.OpQueryGroupBy) + if err := _g.build.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*GCPServiceAccountQuery, *GCPServiceAccountGroupBy](ctx, _g.build, _g, _g.build.inters, v) +} + +func (_g *GCPServiceAccountGroupBy) sqlScan(ctx context.Context, root *GCPServiceAccountQuery, v any) error { + selector := root.sqlQuery(ctx).Select() + aggregation := make([]string, 0, len(_g.fns)) + for _, fn := range _g.fns { + aggregation = append(aggregation, fn(selector)) + } + if len(selector.SelectedColumns()) == 0 { + columns := make([]string, 0, len(*_g.flds)+len(_g.fns)) + for _, f := range *_g.flds { + columns = append(columns, selector.C(f)) + } + columns = append(columns, aggregation...) + selector.Select(columns...) + } + selector.GroupBy(selector.Columns(*_g.flds...)...) + if err := selector.Err(); err != nil { + return err + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := _g.build.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} + +// GCPServiceAccountSelect is the builder for selecting fields of GCPServiceAccount entities. +type GCPServiceAccountSelect struct { + *GCPServiceAccountQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (_s *GCPServiceAccountSelect) Aggregate(fns ...AggregateFunc) *GCPServiceAccountSelect { + _s.fns = append(_s.fns, fns...) + return _s +} + +// Scan applies the selector query and scans the result into the given value. +func (_s *GCPServiceAccountSelect) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, _s.ctx, ent.OpQuerySelect) + if err := _s.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*GCPServiceAccountQuery, *GCPServiceAccountSelect](ctx, _s.GCPServiceAccountQuery, _s, _s.inters, v) +} + +func (_s *GCPServiceAccountSelect) sqlScan(ctx context.Context, root *GCPServiceAccountQuery, v any) error { + selector := root.sqlQuery(ctx) + aggregation := make([]string, 0, len(_s.fns)) + for _, fn := range _s.fns { + aggregation = append(aggregation, fn(selector)) + } + switch n := len(*_s.selector.flds); { + case n == 0 && len(aggregation) > 0: + selector.Select(aggregation...) + case n != 0 && len(aggregation) > 0: + selector.AppendSelect(aggregation...) + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := _s.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} diff --git a/pkg/ent/gcpserviceaccount_update.go b/pkg/ent/gcpserviceaccount_update.go new file mode 100644 index 000000000..fe9bcd234 --- /dev/null +++ b/pkg/ent/gcpserviceaccount_update.go @@ -0,0 +1,624 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/gcpserviceaccount" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" +) + +// GCPServiceAccountUpdate is the builder for updating GCPServiceAccount entities. +type GCPServiceAccountUpdate struct { + config + hooks []Hook + mutation *GCPServiceAccountMutation +} + +// Where appends a list predicates to the GCPServiceAccountUpdate builder. +func (_u *GCPServiceAccountUpdate) Where(ps ...predicate.GCPServiceAccount) *GCPServiceAccountUpdate { + _u.mutation.Where(ps...) + return _u +} + +// SetScope sets the "scope" field. +func (_u *GCPServiceAccountUpdate) SetScope(v string) *GCPServiceAccountUpdate { + _u.mutation.SetScope(v) + return _u +} + +// SetNillableScope sets the "scope" field if the given value is not nil. +func (_u *GCPServiceAccountUpdate) SetNillableScope(v *string) *GCPServiceAccountUpdate { + if v != nil { + _u.SetScope(*v) + } + return _u +} + +// SetScopeID sets the "scope_id" field. +func (_u *GCPServiceAccountUpdate) SetScopeID(v string) *GCPServiceAccountUpdate { + _u.mutation.SetScopeID(v) + return _u +} + +// SetNillableScopeID sets the "scope_id" field if the given value is not nil. +func (_u *GCPServiceAccountUpdate) SetNillableScopeID(v *string) *GCPServiceAccountUpdate { + if v != nil { + _u.SetScopeID(*v) + } + return _u +} + +// SetEmail sets the "email" field. +func (_u *GCPServiceAccountUpdate) SetEmail(v string) *GCPServiceAccountUpdate { + _u.mutation.SetEmail(v) + return _u +} + +// SetNillableEmail sets the "email" field if the given value is not nil. +func (_u *GCPServiceAccountUpdate) SetNillableEmail(v *string) *GCPServiceAccountUpdate { + if v != nil { + _u.SetEmail(*v) + } + return _u +} + +// SetProjectID sets the "project_id" field. +func (_u *GCPServiceAccountUpdate) SetProjectID(v string) *GCPServiceAccountUpdate { + _u.mutation.SetProjectID(v) + return _u +} + +// SetNillableProjectID sets the "project_id" field if the given value is not nil. +func (_u *GCPServiceAccountUpdate) SetNillableProjectID(v *string) *GCPServiceAccountUpdate { + if v != nil { + _u.SetProjectID(*v) + } + return _u +} + +// SetDisplayName sets the "display_name" field. +func (_u *GCPServiceAccountUpdate) SetDisplayName(v string) *GCPServiceAccountUpdate { + _u.mutation.SetDisplayName(v) + return _u +} + +// SetNillableDisplayName sets the "display_name" field if the given value is not nil. +func (_u *GCPServiceAccountUpdate) SetNillableDisplayName(v *string) *GCPServiceAccountUpdate { + if v != nil { + _u.SetDisplayName(*v) + } + return _u +} + +// SetDefaultScopes sets the "default_scopes" field. +func (_u *GCPServiceAccountUpdate) SetDefaultScopes(v string) *GCPServiceAccountUpdate { + _u.mutation.SetDefaultScopes(v) + return _u +} + +// SetNillableDefaultScopes sets the "default_scopes" field if the given value is not nil. +func (_u *GCPServiceAccountUpdate) SetNillableDefaultScopes(v *string) *GCPServiceAccountUpdate { + if v != nil { + _u.SetDefaultScopes(*v) + } + return _u +} + +// SetVerified sets the "verified" field. +func (_u *GCPServiceAccountUpdate) SetVerified(v bool) *GCPServiceAccountUpdate { + _u.mutation.SetVerified(v) + return _u +} + +// SetNillableVerified sets the "verified" field if the given value is not nil. +func (_u *GCPServiceAccountUpdate) SetNillableVerified(v *bool) *GCPServiceAccountUpdate { + if v != nil { + _u.SetVerified(*v) + } + return _u +} + +// SetVerifiedAt sets the "verified_at" field. +func (_u *GCPServiceAccountUpdate) SetVerifiedAt(v time.Time) *GCPServiceAccountUpdate { + _u.mutation.SetVerifiedAt(v) + return _u +} + +// SetNillableVerifiedAt sets the "verified_at" field if the given value is not nil. +func (_u *GCPServiceAccountUpdate) SetNillableVerifiedAt(v *time.Time) *GCPServiceAccountUpdate { + if v != nil { + _u.SetVerifiedAt(*v) + } + return _u +} + +// ClearVerifiedAt clears the value of the "verified_at" field. +func (_u *GCPServiceAccountUpdate) ClearVerifiedAt() *GCPServiceAccountUpdate { + _u.mutation.ClearVerifiedAt() + return _u +} + +// SetCreatedBy sets the "created_by" field. +func (_u *GCPServiceAccountUpdate) SetCreatedBy(v string) *GCPServiceAccountUpdate { + _u.mutation.SetCreatedBy(v) + return _u +} + +// SetNillableCreatedBy sets the "created_by" field if the given value is not nil. +func (_u *GCPServiceAccountUpdate) SetNillableCreatedBy(v *string) *GCPServiceAccountUpdate { + if v != nil { + _u.SetCreatedBy(*v) + } + return _u +} + +// SetManaged sets the "managed" field. +func (_u *GCPServiceAccountUpdate) SetManaged(v bool) *GCPServiceAccountUpdate { + _u.mutation.SetManaged(v) + return _u +} + +// SetNillableManaged sets the "managed" field if the given value is not nil. +func (_u *GCPServiceAccountUpdate) SetNillableManaged(v *bool) *GCPServiceAccountUpdate { + if v != nil { + _u.SetManaged(*v) + } + return _u +} + +// SetManagedBy sets the "managed_by" field. +func (_u *GCPServiceAccountUpdate) SetManagedBy(v string) *GCPServiceAccountUpdate { + _u.mutation.SetManagedBy(v) + return _u +} + +// SetNillableManagedBy sets the "managed_by" field if the given value is not nil. +func (_u *GCPServiceAccountUpdate) SetNillableManagedBy(v *string) *GCPServiceAccountUpdate { + if v != nil { + _u.SetManagedBy(*v) + } + return _u +} + +// Mutation returns the GCPServiceAccountMutation object of the builder. +func (_u *GCPServiceAccountUpdate) Mutation() *GCPServiceAccountMutation { + return _u.mutation +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (_u *GCPServiceAccountUpdate) Save(ctx context.Context) (int, error) { + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *GCPServiceAccountUpdate) SaveX(ctx context.Context) int { + affected, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (_u *GCPServiceAccountUpdate) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *GCPServiceAccountUpdate) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_u *GCPServiceAccountUpdate) check() error { + if v, ok := _u.mutation.Scope(); ok { + if err := gcpserviceaccount.ScopeValidator(v); err != nil { + return &ValidationError{Name: "scope", err: fmt.Errorf(`ent: validator failed for field "GCPServiceAccount.scope": %w`, err)} + } + } + if v, ok := _u.mutation.ScopeID(); ok { + if err := gcpserviceaccount.ScopeIDValidator(v); err != nil { + return &ValidationError{Name: "scope_id", err: fmt.Errorf(`ent: validator failed for field "GCPServiceAccount.scope_id": %w`, err)} + } + } + if v, ok := _u.mutation.Email(); ok { + if err := gcpserviceaccount.EmailValidator(v); err != nil { + return &ValidationError{Name: "email", err: fmt.Errorf(`ent: validator failed for field "GCPServiceAccount.email": %w`, err)} + } + } + if v, ok := _u.mutation.ProjectID(); ok { + if err := gcpserviceaccount.ProjectIDValidator(v); err != nil { + return &ValidationError{Name: "project_id", err: fmt.Errorf(`ent: validator failed for field "GCPServiceAccount.project_id": %w`, err)} + } + } + return nil +} + +func (_u *GCPServiceAccountUpdate) sqlSave(ctx context.Context) (_node int, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(gcpserviceaccount.Table, gcpserviceaccount.Columns, sqlgraph.NewFieldSpec(gcpserviceaccount.FieldID, field.TypeUUID)) + if ps := _u.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := _u.mutation.Scope(); ok { + _spec.SetField(gcpserviceaccount.FieldScope, field.TypeString, value) + } + if value, ok := _u.mutation.ScopeID(); ok { + _spec.SetField(gcpserviceaccount.FieldScopeID, field.TypeString, value) + } + if value, ok := _u.mutation.Email(); ok { + _spec.SetField(gcpserviceaccount.FieldEmail, field.TypeString, value) + } + if value, ok := _u.mutation.ProjectID(); ok { + _spec.SetField(gcpserviceaccount.FieldProjectID, field.TypeString, value) + } + if value, ok := _u.mutation.DisplayName(); ok { + _spec.SetField(gcpserviceaccount.FieldDisplayName, field.TypeString, value) + } + if value, ok := _u.mutation.DefaultScopes(); ok { + _spec.SetField(gcpserviceaccount.FieldDefaultScopes, field.TypeString, value) + } + if value, ok := _u.mutation.Verified(); ok { + _spec.SetField(gcpserviceaccount.FieldVerified, field.TypeBool, value) + } + if value, ok := _u.mutation.VerifiedAt(); ok { + _spec.SetField(gcpserviceaccount.FieldVerifiedAt, field.TypeTime, value) + } + if _u.mutation.VerifiedAtCleared() { + _spec.ClearField(gcpserviceaccount.FieldVerifiedAt, field.TypeTime) + } + if value, ok := _u.mutation.CreatedBy(); ok { + _spec.SetField(gcpserviceaccount.FieldCreatedBy, field.TypeString, value) + } + if value, ok := _u.mutation.Managed(); ok { + _spec.SetField(gcpserviceaccount.FieldManaged, field.TypeBool, value) + } + if value, ok := _u.mutation.ManagedBy(); ok { + _spec.SetField(gcpserviceaccount.FieldManagedBy, field.TypeString, value) + } + if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{gcpserviceaccount.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + _u.mutation.done = true + return _node, nil +} + +// GCPServiceAccountUpdateOne is the builder for updating a single GCPServiceAccount entity. +type GCPServiceAccountUpdateOne struct { + config + fields []string + hooks []Hook + mutation *GCPServiceAccountMutation +} + +// SetScope sets the "scope" field. +func (_u *GCPServiceAccountUpdateOne) SetScope(v string) *GCPServiceAccountUpdateOne { + _u.mutation.SetScope(v) + return _u +} + +// SetNillableScope sets the "scope" field if the given value is not nil. +func (_u *GCPServiceAccountUpdateOne) SetNillableScope(v *string) *GCPServiceAccountUpdateOne { + if v != nil { + _u.SetScope(*v) + } + return _u +} + +// SetScopeID sets the "scope_id" field. +func (_u *GCPServiceAccountUpdateOne) SetScopeID(v string) *GCPServiceAccountUpdateOne { + _u.mutation.SetScopeID(v) + return _u +} + +// SetNillableScopeID sets the "scope_id" field if the given value is not nil. +func (_u *GCPServiceAccountUpdateOne) SetNillableScopeID(v *string) *GCPServiceAccountUpdateOne { + if v != nil { + _u.SetScopeID(*v) + } + return _u +} + +// SetEmail sets the "email" field. +func (_u *GCPServiceAccountUpdateOne) SetEmail(v string) *GCPServiceAccountUpdateOne { + _u.mutation.SetEmail(v) + return _u +} + +// SetNillableEmail sets the "email" field if the given value is not nil. +func (_u *GCPServiceAccountUpdateOne) SetNillableEmail(v *string) *GCPServiceAccountUpdateOne { + if v != nil { + _u.SetEmail(*v) + } + return _u +} + +// SetProjectID sets the "project_id" field. +func (_u *GCPServiceAccountUpdateOne) SetProjectID(v string) *GCPServiceAccountUpdateOne { + _u.mutation.SetProjectID(v) + return _u +} + +// SetNillableProjectID sets the "project_id" field if the given value is not nil. +func (_u *GCPServiceAccountUpdateOne) SetNillableProjectID(v *string) *GCPServiceAccountUpdateOne { + if v != nil { + _u.SetProjectID(*v) + } + return _u +} + +// SetDisplayName sets the "display_name" field. +func (_u *GCPServiceAccountUpdateOne) SetDisplayName(v string) *GCPServiceAccountUpdateOne { + _u.mutation.SetDisplayName(v) + return _u +} + +// SetNillableDisplayName sets the "display_name" field if the given value is not nil. +func (_u *GCPServiceAccountUpdateOne) SetNillableDisplayName(v *string) *GCPServiceAccountUpdateOne { + if v != nil { + _u.SetDisplayName(*v) + } + return _u +} + +// SetDefaultScopes sets the "default_scopes" field. +func (_u *GCPServiceAccountUpdateOne) SetDefaultScopes(v string) *GCPServiceAccountUpdateOne { + _u.mutation.SetDefaultScopes(v) + return _u +} + +// SetNillableDefaultScopes sets the "default_scopes" field if the given value is not nil. +func (_u *GCPServiceAccountUpdateOne) SetNillableDefaultScopes(v *string) *GCPServiceAccountUpdateOne { + if v != nil { + _u.SetDefaultScopes(*v) + } + return _u +} + +// SetVerified sets the "verified" field. +func (_u *GCPServiceAccountUpdateOne) SetVerified(v bool) *GCPServiceAccountUpdateOne { + _u.mutation.SetVerified(v) + return _u +} + +// SetNillableVerified sets the "verified" field if the given value is not nil. +func (_u *GCPServiceAccountUpdateOne) SetNillableVerified(v *bool) *GCPServiceAccountUpdateOne { + if v != nil { + _u.SetVerified(*v) + } + return _u +} + +// SetVerifiedAt sets the "verified_at" field. +func (_u *GCPServiceAccountUpdateOne) SetVerifiedAt(v time.Time) *GCPServiceAccountUpdateOne { + _u.mutation.SetVerifiedAt(v) + return _u +} + +// SetNillableVerifiedAt sets the "verified_at" field if the given value is not nil. +func (_u *GCPServiceAccountUpdateOne) SetNillableVerifiedAt(v *time.Time) *GCPServiceAccountUpdateOne { + if v != nil { + _u.SetVerifiedAt(*v) + } + return _u +} + +// ClearVerifiedAt clears the value of the "verified_at" field. +func (_u *GCPServiceAccountUpdateOne) ClearVerifiedAt() *GCPServiceAccountUpdateOne { + _u.mutation.ClearVerifiedAt() + return _u +} + +// SetCreatedBy sets the "created_by" field. +func (_u *GCPServiceAccountUpdateOne) SetCreatedBy(v string) *GCPServiceAccountUpdateOne { + _u.mutation.SetCreatedBy(v) + return _u +} + +// SetNillableCreatedBy sets the "created_by" field if the given value is not nil. +func (_u *GCPServiceAccountUpdateOne) SetNillableCreatedBy(v *string) *GCPServiceAccountUpdateOne { + if v != nil { + _u.SetCreatedBy(*v) + } + return _u +} + +// SetManaged sets the "managed" field. +func (_u *GCPServiceAccountUpdateOne) SetManaged(v bool) *GCPServiceAccountUpdateOne { + _u.mutation.SetManaged(v) + return _u +} + +// SetNillableManaged sets the "managed" field if the given value is not nil. +func (_u *GCPServiceAccountUpdateOne) SetNillableManaged(v *bool) *GCPServiceAccountUpdateOne { + if v != nil { + _u.SetManaged(*v) + } + return _u +} + +// SetManagedBy sets the "managed_by" field. +func (_u *GCPServiceAccountUpdateOne) SetManagedBy(v string) *GCPServiceAccountUpdateOne { + _u.mutation.SetManagedBy(v) + return _u +} + +// SetNillableManagedBy sets the "managed_by" field if the given value is not nil. +func (_u *GCPServiceAccountUpdateOne) SetNillableManagedBy(v *string) *GCPServiceAccountUpdateOne { + if v != nil { + _u.SetManagedBy(*v) + } + return _u +} + +// Mutation returns the GCPServiceAccountMutation object of the builder. +func (_u *GCPServiceAccountUpdateOne) Mutation() *GCPServiceAccountMutation { + return _u.mutation +} + +// Where appends a list predicates to the GCPServiceAccountUpdate builder. +func (_u *GCPServiceAccountUpdateOne) Where(ps ...predicate.GCPServiceAccount) *GCPServiceAccountUpdateOne { + _u.mutation.Where(ps...) + return _u +} + +// Select allows selecting one or more fields (columns) of the returned entity. +// The default is selecting all fields defined in the entity schema. +func (_u *GCPServiceAccountUpdateOne) Select(field string, fields ...string) *GCPServiceAccountUpdateOne { + _u.fields = append([]string{field}, fields...) + return _u +} + +// Save executes the query and returns the updated GCPServiceAccount entity. +func (_u *GCPServiceAccountUpdateOne) Save(ctx context.Context) (*GCPServiceAccount, error) { + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *GCPServiceAccountUpdateOne) SaveX(ctx context.Context) *GCPServiceAccount { + node, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (_u *GCPServiceAccountUpdateOne) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *GCPServiceAccountUpdateOne) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_u *GCPServiceAccountUpdateOne) check() error { + if v, ok := _u.mutation.Scope(); ok { + if err := gcpserviceaccount.ScopeValidator(v); err != nil { + return &ValidationError{Name: "scope", err: fmt.Errorf(`ent: validator failed for field "GCPServiceAccount.scope": %w`, err)} + } + } + if v, ok := _u.mutation.ScopeID(); ok { + if err := gcpserviceaccount.ScopeIDValidator(v); err != nil { + return &ValidationError{Name: "scope_id", err: fmt.Errorf(`ent: validator failed for field "GCPServiceAccount.scope_id": %w`, err)} + } + } + if v, ok := _u.mutation.Email(); ok { + if err := gcpserviceaccount.EmailValidator(v); err != nil { + return &ValidationError{Name: "email", err: fmt.Errorf(`ent: validator failed for field "GCPServiceAccount.email": %w`, err)} + } + } + if v, ok := _u.mutation.ProjectID(); ok { + if err := gcpserviceaccount.ProjectIDValidator(v); err != nil { + return &ValidationError{Name: "project_id", err: fmt.Errorf(`ent: validator failed for field "GCPServiceAccount.project_id": %w`, err)} + } + } + return nil +} + +func (_u *GCPServiceAccountUpdateOne) sqlSave(ctx context.Context) (_node *GCPServiceAccount, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(gcpserviceaccount.Table, gcpserviceaccount.Columns, sqlgraph.NewFieldSpec(gcpserviceaccount.FieldID, field.TypeUUID)) + id, ok := _u.mutation.ID() + if !ok { + return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "GCPServiceAccount.id" for update`)} + } + _spec.Node.ID.Value = id + if fields := _u.fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, gcpserviceaccount.FieldID) + for _, f := range fields { + if !gcpserviceaccount.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + if f != gcpserviceaccount.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, f) + } + } + } + if ps := _u.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := _u.mutation.Scope(); ok { + _spec.SetField(gcpserviceaccount.FieldScope, field.TypeString, value) + } + if value, ok := _u.mutation.ScopeID(); ok { + _spec.SetField(gcpserviceaccount.FieldScopeID, field.TypeString, value) + } + if value, ok := _u.mutation.Email(); ok { + _spec.SetField(gcpserviceaccount.FieldEmail, field.TypeString, value) + } + if value, ok := _u.mutation.ProjectID(); ok { + _spec.SetField(gcpserviceaccount.FieldProjectID, field.TypeString, value) + } + if value, ok := _u.mutation.DisplayName(); ok { + _spec.SetField(gcpserviceaccount.FieldDisplayName, field.TypeString, value) + } + if value, ok := _u.mutation.DefaultScopes(); ok { + _spec.SetField(gcpserviceaccount.FieldDefaultScopes, field.TypeString, value) + } + if value, ok := _u.mutation.Verified(); ok { + _spec.SetField(gcpserviceaccount.FieldVerified, field.TypeBool, value) + } + if value, ok := _u.mutation.VerifiedAt(); ok { + _spec.SetField(gcpserviceaccount.FieldVerifiedAt, field.TypeTime, value) + } + if _u.mutation.VerifiedAtCleared() { + _spec.ClearField(gcpserviceaccount.FieldVerifiedAt, field.TypeTime) + } + if value, ok := _u.mutation.CreatedBy(); ok { + _spec.SetField(gcpserviceaccount.FieldCreatedBy, field.TypeString, value) + } + if value, ok := _u.mutation.Managed(); ok { + _spec.SetField(gcpserviceaccount.FieldManaged, field.TypeBool, value) + } + if value, ok := _u.mutation.ManagedBy(); ok { + _spec.SetField(gcpserviceaccount.FieldManagedBy, field.TypeString, value) + } + _node = &GCPServiceAccount{config: _u.config} + _spec.Assign = _node.assignValues + _spec.ScanValues = _node.scanValues + if err = sqlgraph.UpdateNode(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{gcpserviceaccount.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + _u.mutation.done = true + return _node, nil +} diff --git a/pkg/ent/generate.go b/pkg/ent/generate.go index 143829ddf..5cb93fa96 100644 --- a/pkg/ent/generate.go +++ b/pkg/ent/generate.go @@ -14,4 +14,4 @@ package ent -//go:generate go run -mod=mod entgo.io/ent/cmd/ent generate ./schema +//go:generate go run -mod=mod entgo.io/ent/cmd/ent generate --feature sql/upsert,sql/lock ./schema diff --git a/pkg/ent/githubinstallation.go b/pkg/ent/githubinstallation.go new file mode 100644 index 000000000..7e5769850 --- /dev/null +++ b/pkg/ent/githubinstallation.go @@ -0,0 +1,172 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "fmt" + "strings" + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "github.com/GoogleCloudPlatform/scion/pkg/ent/githubinstallation" +) + +// GithubInstallation is the model entity for the GithubInstallation schema. +type GithubInstallation struct { + config `json:"-"` + // ID of the ent. + ID int64 `json:"id,omitempty"` + // AccountLogin holds the value of the "account_login" field. + AccountLogin string `json:"account_login,omitempty"` + // AccountType holds the value of the "account_type" field. + AccountType string `json:"account_type,omitempty"` + // AppID holds the value of the "app_id" field. + AppID int64 `json:"app_id,omitempty"` + // Repositories holds the value of the "repositories" field. + Repositories string `json:"repositories,omitempty"` + // Status holds the value of the "status" field. + Status string `json:"status,omitempty"` + // Created holds the value of the "created" field. + Created time.Time `json:"created,omitempty"` + // Updated holds the value of the "updated" field. + Updated time.Time `json:"updated,omitempty"` + selectValues sql.SelectValues +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*GithubInstallation) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case githubinstallation.FieldID, githubinstallation.FieldAppID: + values[i] = new(sql.NullInt64) + case githubinstallation.FieldAccountLogin, githubinstallation.FieldAccountType, githubinstallation.FieldRepositories, githubinstallation.FieldStatus: + values[i] = new(sql.NullString) + case githubinstallation.FieldCreated, githubinstallation.FieldUpdated: + values[i] = new(sql.NullTime) + default: + values[i] = new(sql.UnknownType) + } + } + return values, nil +} + +// assignValues assigns the values that were returned from sql.Rows (after scanning) +// to the GithubInstallation fields. +func (_m *GithubInstallation) assignValues(columns []string, values []any) error { + if m, n := len(values), len(columns); m < n { + return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) + } + for i := range columns { + switch columns[i] { + case githubinstallation.FieldID: + value, ok := values[i].(*sql.NullInt64) + if !ok { + return fmt.Errorf("unexpected type %T for field id", value) + } + _m.ID = int64(value.Int64) + case githubinstallation.FieldAccountLogin: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field account_login", values[i]) + } else if value.Valid { + _m.AccountLogin = value.String + } + case githubinstallation.FieldAccountType: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field account_type", values[i]) + } else if value.Valid { + _m.AccountType = value.String + } + case githubinstallation.FieldAppID: + if value, ok := values[i].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for field app_id", values[i]) + } else if value.Valid { + _m.AppID = value.Int64 + } + case githubinstallation.FieldRepositories: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field repositories", values[i]) + } else if value.Valid { + _m.Repositories = value.String + } + case githubinstallation.FieldStatus: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field status", values[i]) + } else if value.Valid { + _m.Status = value.String + } + case githubinstallation.FieldCreated: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field created", values[i]) + } else if value.Valid { + _m.Created = value.Time + } + case githubinstallation.FieldUpdated: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field updated", values[i]) + } else if value.Valid { + _m.Updated = value.Time + } + default: + _m.selectValues.Set(columns[i], values[i]) + } + } + return nil +} + +// Value returns the ent.Value that was dynamically selected and assigned to the GithubInstallation. +// This includes values selected through modifiers, order, etc. +func (_m *GithubInstallation) Value(name string) (ent.Value, error) { + return _m.selectValues.Get(name) +} + +// Update returns a builder for updating this GithubInstallation. +// Note that you need to call GithubInstallation.Unwrap() before calling this method if this GithubInstallation +// was returned from a transaction, and the transaction was committed or rolled back. +func (_m *GithubInstallation) Update() *GithubInstallationUpdateOne { + return NewGithubInstallationClient(_m.config).UpdateOne(_m) +} + +// Unwrap unwraps the GithubInstallation entity that was returned from a transaction after it was closed, +// so that all future queries will be executed through the driver which created the transaction. +func (_m *GithubInstallation) Unwrap() *GithubInstallation { + _tx, ok := _m.config.driver.(*txDriver) + if !ok { + panic("ent: GithubInstallation is not a transactional entity") + } + _m.config.driver = _tx.drv + return _m +} + +// String implements the fmt.Stringer. +func (_m *GithubInstallation) String() string { + var builder strings.Builder + builder.WriteString("GithubInstallation(") + builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID)) + builder.WriteString("account_login=") + builder.WriteString(_m.AccountLogin) + builder.WriteString(", ") + builder.WriteString("account_type=") + builder.WriteString(_m.AccountType) + builder.WriteString(", ") + builder.WriteString("app_id=") + builder.WriteString(fmt.Sprintf("%v", _m.AppID)) + builder.WriteString(", ") + builder.WriteString("repositories=") + builder.WriteString(_m.Repositories) + builder.WriteString(", ") + builder.WriteString("status=") + builder.WriteString(_m.Status) + builder.WriteString(", ") + builder.WriteString("created=") + builder.WriteString(_m.Created.Format(time.ANSIC)) + builder.WriteString(", ") + builder.WriteString("updated=") + builder.WriteString(_m.Updated.Format(time.ANSIC)) + builder.WriteByte(')') + return builder.String() +} + +// GithubInstallations is a parsable slice of GithubInstallation. +type GithubInstallations []*GithubInstallation diff --git a/pkg/ent/githubinstallation/githubinstallation.go b/pkg/ent/githubinstallation/githubinstallation.go new file mode 100644 index 000000000..a8fafee95 --- /dev/null +++ b/pkg/ent/githubinstallation/githubinstallation.go @@ -0,0 +1,114 @@ +// Code generated by ent, DO NOT EDIT. + +package githubinstallation + +import ( + "time" + + "entgo.io/ent/dialect/sql" +) + +const ( + // Label holds the string label denoting the githubinstallation type in the database. + Label = "github_installation" + // FieldID holds the string denoting the id field in the database. + FieldID = "installation_id" + // FieldAccountLogin holds the string denoting the account_login field in the database. + FieldAccountLogin = "account_login" + // FieldAccountType holds the string denoting the account_type field in the database. + FieldAccountType = "account_type" + // FieldAppID holds the string denoting the app_id field in the database. + FieldAppID = "app_id" + // FieldRepositories holds the string denoting the repositories field in the database. + FieldRepositories = "repositories" + // FieldStatus holds the string denoting the status field in the database. + FieldStatus = "status" + // FieldCreated holds the string denoting the created field in the database. + FieldCreated = "created" + // FieldUpdated holds the string denoting the updated field in the database. + FieldUpdated = "updated" + // Table holds the table name of the githubinstallation in the database. + Table = "github_installations" +) + +// Columns holds all SQL columns for githubinstallation fields. +var Columns = []string{ + FieldID, + FieldAccountLogin, + FieldAccountType, + FieldAppID, + FieldRepositories, + FieldStatus, + FieldCreated, + FieldUpdated, +} + +// ValidColumn reports if the column name is valid (part of the table columns). +func ValidColumn(column string) bool { + for i := range Columns { + if column == Columns[i] { + return true + } + } + return false +} + +var ( + // AccountLoginValidator is a validator for the "account_login" field. It is called by the builders before save. + AccountLoginValidator func(string) error + // DefaultAccountType holds the default value on creation for the "account_type" field. + DefaultAccountType string + // DefaultRepositories holds the default value on creation for the "repositories" field. + DefaultRepositories string + // DefaultStatus holds the default value on creation for the "status" field. + DefaultStatus string + // DefaultCreated holds the default value on creation for the "created" field. + DefaultCreated func() time.Time + // DefaultUpdated holds the default value on creation for the "updated" field. + DefaultUpdated func() time.Time + // UpdateDefaultUpdated holds the default value on update for the "updated" field. + UpdateDefaultUpdated func() time.Time +) + +// OrderOption defines the ordering options for the GithubInstallation queries. +type OrderOption func(*sql.Selector) + +// ByID orders the results by the id field. +func ByID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldID, opts...).ToFunc() +} + +// ByAccountLogin orders the results by the account_login field. +func ByAccountLogin(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldAccountLogin, opts...).ToFunc() +} + +// ByAccountType orders the results by the account_type field. +func ByAccountType(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldAccountType, opts...).ToFunc() +} + +// ByAppID orders the results by the app_id field. +func ByAppID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldAppID, opts...).ToFunc() +} + +// ByRepositories orders the results by the repositories field. +func ByRepositories(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldRepositories, opts...).ToFunc() +} + +// ByStatus orders the results by the status field. +func ByStatus(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldStatus, opts...).ToFunc() +} + +// ByCreated orders the results by the created field. +func ByCreated(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCreated, opts...).ToFunc() +} + +// ByUpdated orders the results by the updated field. +func ByUpdated(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldUpdated, opts...).ToFunc() +} diff --git a/pkg/ent/githubinstallation/where.go b/pkg/ent/githubinstallation/where.go new file mode 100644 index 000000000..1ec0a3304 --- /dev/null +++ b/pkg/ent/githubinstallation/where.go @@ -0,0 +1,485 @@ +// Code generated by ent, DO NOT EDIT. + +package githubinstallation + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" +) + +// ID filters vertices based on their ID field. +func ID(id int64) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldEQ(FieldID, id)) +} + +// IDEQ applies the EQ predicate on the ID field. +func IDEQ(id int64) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldEQ(FieldID, id)) +} + +// IDNEQ applies the NEQ predicate on the ID field. +func IDNEQ(id int64) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldNEQ(FieldID, id)) +} + +// IDIn applies the In predicate on the ID field. +func IDIn(ids ...int64) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldIn(FieldID, ids...)) +} + +// IDNotIn applies the NotIn predicate on the ID field. +func IDNotIn(ids ...int64) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldNotIn(FieldID, ids...)) +} + +// IDGT applies the GT predicate on the ID field. +func IDGT(id int64) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldGT(FieldID, id)) +} + +// IDGTE applies the GTE predicate on the ID field. +func IDGTE(id int64) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldGTE(FieldID, id)) +} + +// IDLT applies the LT predicate on the ID field. +func IDLT(id int64) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldLT(FieldID, id)) +} + +// IDLTE applies the LTE predicate on the ID field. +func IDLTE(id int64) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldLTE(FieldID, id)) +} + +// AccountLogin applies equality check predicate on the "account_login" field. It's identical to AccountLoginEQ. +func AccountLogin(v string) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldEQ(FieldAccountLogin, v)) +} + +// AccountType applies equality check predicate on the "account_type" field. It's identical to AccountTypeEQ. +func AccountType(v string) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldEQ(FieldAccountType, v)) +} + +// AppID applies equality check predicate on the "app_id" field. It's identical to AppIDEQ. +func AppID(v int64) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldEQ(FieldAppID, v)) +} + +// Repositories applies equality check predicate on the "repositories" field. It's identical to RepositoriesEQ. +func Repositories(v string) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldEQ(FieldRepositories, v)) +} + +// Status applies equality check predicate on the "status" field. It's identical to StatusEQ. +func Status(v string) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldEQ(FieldStatus, v)) +} + +// Created applies equality check predicate on the "created" field. It's identical to CreatedEQ. +func Created(v time.Time) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldEQ(FieldCreated, v)) +} + +// Updated applies equality check predicate on the "updated" field. It's identical to UpdatedEQ. +func Updated(v time.Time) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldEQ(FieldUpdated, v)) +} + +// AccountLoginEQ applies the EQ predicate on the "account_login" field. +func AccountLoginEQ(v string) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldEQ(FieldAccountLogin, v)) +} + +// AccountLoginNEQ applies the NEQ predicate on the "account_login" field. +func AccountLoginNEQ(v string) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldNEQ(FieldAccountLogin, v)) +} + +// AccountLoginIn applies the In predicate on the "account_login" field. +func AccountLoginIn(vs ...string) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldIn(FieldAccountLogin, vs...)) +} + +// AccountLoginNotIn applies the NotIn predicate on the "account_login" field. +func AccountLoginNotIn(vs ...string) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldNotIn(FieldAccountLogin, vs...)) +} + +// AccountLoginGT applies the GT predicate on the "account_login" field. +func AccountLoginGT(v string) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldGT(FieldAccountLogin, v)) +} + +// AccountLoginGTE applies the GTE predicate on the "account_login" field. +func AccountLoginGTE(v string) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldGTE(FieldAccountLogin, v)) +} + +// AccountLoginLT applies the LT predicate on the "account_login" field. +func AccountLoginLT(v string) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldLT(FieldAccountLogin, v)) +} + +// AccountLoginLTE applies the LTE predicate on the "account_login" field. +func AccountLoginLTE(v string) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldLTE(FieldAccountLogin, v)) +} + +// AccountLoginContains applies the Contains predicate on the "account_login" field. +func AccountLoginContains(v string) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldContains(FieldAccountLogin, v)) +} + +// AccountLoginHasPrefix applies the HasPrefix predicate on the "account_login" field. +func AccountLoginHasPrefix(v string) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldHasPrefix(FieldAccountLogin, v)) +} + +// AccountLoginHasSuffix applies the HasSuffix predicate on the "account_login" field. +func AccountLoginHasSuffix(v string) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldHasSuffix(FieldAccountLogin, v)) +} + +// AccountLoginEqualFold applies the EqualFold predicate on the "account_login" field. +func AccountLoginEqualFold(v string) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldEqualFold(FieldAccountLogin, v)) +} + +// AccountLoginContainsFold applies the ContainsFold predicate on the "account_login" field. +func AccountLoginContainsFold(v string) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldContainsFold(FieldAccountLogin, v)) +} + +// AccountTypeEQ applies the EQ predicate on the "account_type" field. +func AccountTypeEQ(v string) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldEQ(FieldAccountType, v)) +} + +// AccountTypeNEQ applies the NEQ predicate on the "account_type" field. +func AccountTypeNEQ(v string) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldNEQ(FieldAccountType, v)) +} + +// AccountTypeIn applies the In predicate on the "account_type" field. +func AccountTypeIn(vs ...string) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldIn(FieldAccountType, vs...)) +} + +// AccountTypeNotIn applies the NotIn predicate on the "account_type" field. +func AccountTypeNotIn(vs ...string) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldNotIn(FieldAccountType, vs...)) +} + +// AccountTypeGT applies the GT predicate on the "account_type" field. +func AccountTypeGT(v string) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldGT(FieldAccountType, v)) +} + +// AccountTypeGTE applies the GTE predicate on the "account_type" field. +func AccountTypeGTE(v string) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldGTE(FieldAccountType, v)) +} + +// AccountTypeLT applies the LT predicate on the "account_type" field. +func AccountTypeLT(v string) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldLT(FieldAccountType, v)) +} + +// AccountTypeLTE applies the LTE predicate on the "account_type" field. +func AccountTypeLTE(v string) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldLTE(FieldAccountType, v)) +} + +// AccountTypeContains applies the Contains predicate on the "account_type" field. +func AccountTypeContains(v string) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldContains(FieldAccountType, v)) +} + +// AccountTypeHasPrefix applies the HasPrefix predicate on the "account_type" field. +func AccountTypeHasPrefix(v string) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldHasPrefix(FieldAccountType, v)) +} + +// AccountTypeHasSuffix applies the HasSuffix predicate on the "account_type" field. +func AccountTypeHasSuffix(v string) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldHasSuffix(FieldAccountType, v)) +} + +// AccountTypeEqualFold applies the EqualFold predicate on the "account_type" field. +func AccountTypeEqualFold(v string) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldEqualFold(FieldAccountType, v)) +} + +// AccountTypeContainsFold applies the ContainsFold predicate on the "account_type" field. +func AccountTypeContainsFold(v string) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldContainsFold(FieldAccountType, v)) +} + +// AppIDEQ applies the EQ predicate on the "app_id" field. +func AppIDEQ(v int64) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldEQ(FieldAppID, v)) +} + +// AppIDNEQ applies the NEQ predicate on the "app_id" field. +func AppIDNEQ(v int64) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldNEQ(FieldAppID, v)) +} + +// AppIDIn applies the In predicate on the "app_id" field. +func AppIDIn(vs ...int64) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldIn(FieldAppID, vs...)) +} + +// AppIDNotIn applies the NotIn predicate on the "app_id" field. +func AppIDNotIn(vs ...int64) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldNotIn(FieldAppID, vs...)) +} + +// AppIDGT applies the GT predicate on the "app_id" field. +func AppIDGT(v int64) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldGT(FieldAppID, v)) +} + +// AppIDGTE applies the GTE predicate on the "app_id" field. +func AppIDGTE(v int64) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldGTE(FieldAppID, v)) +} + +// AppIDLT applies the LT predicate on the "app_id" field. +func AppIDLT(v int64) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldLT(FieldAppID, v)) +} + +// AppIDLTE applies the LTE predicate on the "app_id" field. +func AppIDLTE(v int64) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldLTE(FieldAppID, v)) +} + +// RepositoriesEQ applies the EQ predicate on the "repositories" field. +func RepositoriesEQ(v string) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldEQ(FieldRepositories, v)) +} + +// RepositoriesNEQ applies the NEQ predicate on the "repositories" field. +func RepositoriesNEQ(v string) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldNEQ(FieldRepositories, v)) +} + +// RepositoriesIn applies the In predicate on the "repositories" field. +func RepositoriesIn(vs ...string) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldIn(FieldRepositories, vs...)) +} + +// RepositoriesNotIn applies the NotIn predicate on the "repositories" field. +func RepositoriesNotIn(vs ...string) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldNotIn(FieldRepositories, vs...)) +} + +// RepositoriesGT applies the GT predicate on the "repositories" field. +func RepositoriesGT(v string) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldGT(FieldRepositories, v)) +} + +// RepositoriesGTE applies the GTE predicate on the "repositories" field. +func RepositoriesGTE(v string) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldGTE(FieldRepositories, v)) +} + +// RepositoriesLT applies the LT predicate on the "repositories" field. +func RepositoriesLT(v string) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldLT(FieldRepositories, v)) +} + +// RepositoriesLTE applies the LTE predicate on the "repositories" field. +func RepositoriesLTE(v string) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldLTE(FieldRepositories, v)) +} + +// RepositoriesContains applies the Contains predicate on the "repositories" field. +func RepositoriesContains(v string) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldContains(FieldRepositories, v)) +} + +// RepositoriesHasPrefix applies the HasPrefix predicate on the "repositories" field. +func RepositoriesHasPrefix(v string) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldHasPrefix(FieldRepositories, v)) +} + +// RepositoriesHasSuffix applies the HasSuffix predicate on the "repositories" field. +func RepositoriesHasSuffix(v string) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldHasSuffix(FieldRepositories, v)) +} + +// RepositoriesEqualFold applies the EqualFold predicate on the "repositories" field. +func RepositoriesEqualFold(v string) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldEqualFold(FieldRepositories, v)) +} + +// RepositoriesContainsFold applies the ContainsFold predicate on the "repositories" field. +func RepositoriesContainsFold(v string) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldContainsFold(FieldRepositories, v)) +} + +// StatusEQ applies the EQ predicate on the "status" field. +func StatusEQ(v string) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldEQ(FieldStatus, v)) +} + +// StatusNEQ applies the NEQ predicate on the "status" field. +func StatusNEQ(v string) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldNEQ(FieldStatus, v)) +} + +// StatusIn applies the In predicate on the "status" field. +func StatusIn(vs ...string) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldIn(FieldStatus, vs...)) +} + +// StatusNotIn applies the NotIn predicate on the "status" field. +func StatusNotIn(vs ...string) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldNotIn(FieldStatus, vs...)) +} + +// StatusGT applies the GT predicate on the "status" field. +func StatusGT(v string) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldGT(FieldStatus, v)) +} + +// StatusGTE applies the GTE predicate on the "status" field. +func StatusGTE(v string) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldGTE(FieldStatus, v)) +} + +// StatusLT applies the LT predicate on the "status" field. +func StatusLT(v string) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldLT(FieldStatus, v)) +} + +// StatusLTE applies the LTE predicate on the "status" field. +func StatusLTE(v string) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldLTE(FieldStatus, v)) +} + +// StatusContains applies the Contains predicate on the "status" field. +func StatusContains(v string) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldContains(FieldStatus, v)) +} + +// StatusHasPrefix applies the HasPrefix predicate on the "status" field. +func StatusHasPrefix(v string) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldHasPrefix(FieldStatus, v)) +} + +// StatusHasSuffix applies the HasSuffix predicate on the "status" field. +func StatusHasSuffix(v string) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldHasSuffix(FieldStatus, v)) +} + +// StatusEqualFold applies the EqualFold predicate on the "status" field. +func StatusEqualFold(v string) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldEqualFold(FieldStatus, v)) +} + +// StatusContainsFold applies the ContainsFold predicate on the "status" field. +func StatusContainsFold(v string) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldContainsFold(FieldStatus, v)) +} + +// CreatedEQ applies the EQ predicate on the "created" field. +func CreatedEQ(v time.Time) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldEQ(FieldCreated, v)) +} + +// CreatedNEQ applies the NEQ predicate on the "created" field. +func CreatedNEQ(v time.Time) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldNEQ(FieldCreated, v)) +} + +// CreatedIn applies the In predicate on the "created" field. +func CreatedIn(vs ...time.Time) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldIn(FieldCreated, vs...)) +} + +// CreatedNotIn applies the NotIn predicate on the "created" field. +func CreatedNotIn(vs ...time.Time) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldNotIn(FieldCreated, vs...)) +} + +// CreatedGT applies the GT predicate on the "created" field. +func CreatedGT(v time.Time) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldGT(FieldCreated, v)) +} + +// CreatedGTE applies the GTE predicate on the "created" field. +func CreatedGTE(v time.Time) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldGTE(FieldCreated, v)) +} + +// CreatedLT applies the LT predicate on the "created" field. +func CreatedLT(v time.Time) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldLT(FieldCreated, v)) +} + +// CreatedLTE applies the LTE predicate on the "created" field. +func CreatedLTE(v time.Time) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldLTE(FieldCreated, v)) +} + +// UpdatedEQ applies the EQ predicate on the "updated" field. +func UpdatedEQ(v time.Time) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldEQ(FieldUpdated, v)) +} + +// UpdatedNEQ applies the NEQ predicate on the "updated" field. +func UpdatedNEQ(v time.Time) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldNEQ(FieldUpdated, v)) +} + +// UpdatedIn applies the In predicate on the "updated" field. +func UpdatedIn(vs ...time.Time) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldIn(FieldUpdated, vs...)) +} + +// UpdatedNotIn applies the NotIn predicate on the "updated" field. +func UpdatedNotIn(vs ...time.Time) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldNotIn(FieldUpdated, vs...)) +} + +// UpdatedGT applies the GT predicate on the "updated" field. +func UpdatedGT(v time.Time) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldGT(FieldUpdated, v)) +} + +// UpdatedGTE applies the GTE predicate on the "updated" field. +func UpdatedGTE(v time.Time) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldGTE(FieldUpdated, v)) +} + +// UpdatedLT applies the LT predicate on the "updated" field. +func UpdatedLT(v time.Time) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldLT(FieldUpdated, v)) +} + +// UpdatedLTE applies the LTE predicate on the "updated" field. +func UpdatedLTE(v time.Time) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.FieldLTE(FieldUpdated, v)) +} + +// And groups predicates with the AND operator between them. +func And(predicates ...predicate.GithubInstallation) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.AndPredicates(predicates...)) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.GithubInstallation) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.OrPredicates(predicates...)) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.GithubInstallation) predicate.GithubInstallation { + return predicate.GithubInstallation(sql.NotPredicates(p)) +} diff --git a/pkg/ent/githubinstallation_create.go b/pkg/ent/githubinstallation_create.go new file mode 100644 index 000000000..b457bcc20 --- /dev/null +++ b/pkg/ent/githubinstallation_create.go @@ -0,0 +1,860 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/githubinstallation" +) + +// GithubInstallationCreate is the builder for creating a GithubInstallation entity. +type GithubInstallationCreate struct { + config + mutation *GithubInstallationMutation + hooks []Hook + conflict []sql.ConflictOption +} + +// SetAccountLogin sets the "account_login" field. +func (_c *GithubInstallationCreate) SetAccountLogin(v string) *GithubInstallationCreate { + _c.mutation.SetAccountLogin(v) + return _c +} + +// SetAccountType sets the "account_type" field. +func (_c *GithubInstallationCreate) SetAccountType(v string) *GithubInstallationCreate { + _c.mutation.SetAccountType(v) + return _c +} + +// SetNillableAccountType sets the "account_type" field if the given value is not nil. +func (_c *GithubInstallationCreate) SetNillableAccountType(v *string) *GithubInstallationCreate { + if v != nil { + _c.SetAccountType(*v) + } + return _c +} + +// SetAppID sets the "app_id" field. +func (_c *GithubInstallationCreate) SetAppID(v int64) *GithubInstallationCreate { + _c.mutation.SetAppID(v) + return _c +} + +// SetRepositories sets the "repositories" field. +func (_c *GithubInstallationCreate) SetRepositories(v string) *GithubInstallationCreate { + _c.mutation.SetRepositories(v) + return _c +} + +// SetNillableRepositories sets the "repositories" field if the given value is not nil. +func (_c *GithubInstallationCreate) SetNillableRepositories(v *string) *GithubInstallationCreate { + if v != nil { + _c.SetRepositories(*v) + } + return _c +} + +// SetStatus sets the "status" field. +func (_c *GithubInstallationCreate) SetStatus(v string) *GithubInstallationCreate { + _c.mutation.SetStatus(v) + return _c +} + +// SetNillableStatus sets the "status" field if the given value is not nil. +func (_c *GithubInstallationCreate) SetNillableStatus(v *string) *GithubInstallationCreate { + if v != nil { + _c.SetStatus(*v) + } + return _c +} + +// SetCreated sets the "created" field. +func (_c *GithubInstallationCreate) SetCreated(v time.Time) *GithubInstallationCreate { + _c.mutation.SetCreated(v) + return _c +} + +// SetNillableCreated sets the "created" field if the given value is not nil. +func (_c *GithubInstallationCreate) SetNillableCreated(v *time.Time) *GithubInstallationCreate { + if v != nil { + _c.SetCreated(*v) + } + return _c +} + +// SetUpdated sets the "updated" field. +func (_c *GithubInstallationCreate) SetUpdated(v time.Time) *GithubInstallationCreate { + _c.mutation.SetUpdated(v) + return _c +} + +// SetNillableUpdated sets the "updated" field if the given value is not nil. +func (_c *GithubInstallationCreate) SetNillableUpdated(v *time.Time) *GithubInstallationCreate { + if v != nil { + _c.SetUpdated(*v) + } + return _c +} + +// SetID sets the "id" field. +func (_c *GithubInstallationCreate) SetID(v int64) *GithubInstallationCreate { + _c.mutation.SetID(v) + return _c +} + +// Mutation returns the GithubInstallationMutation object of the builder. +func (_c *GithubInstallationCreate) Mutation() *GithubInstallationMutation { + return _c.mutation +} + +// Save creates the GithubInstallation in the database. +func (_c *GithubInstallationCreate) Save(ctx context.Context) (*GithubInstallation, error) { + _c.defaults() + return withHooks(ctx, _c.sqlSave, _c.mutation, _c.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (_c *GithubInstallationCreate) SaveX(ctx context.Context) *GithubInstallation { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *GithubInstallationCreate) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *GithubInstallationCreate) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_c *GithubInstallationCreate) defaults() { + if _, ok := _c.mutation.AccountType(); !ok { + v := githubinstallation.DefaultAccountType + _c.mutation.SetAccountType(v) + } + if _, ok := _c.mutation.Repositories(); !ok { + v := githubinstallation.DefaultRepositories + _c.mutation.SetRepositories(v) + } + if _, ok := _c.mutation.Status(); !ok { + v := githubinstallation.DefaultStatus + _c.mutation.SetStatus(v) + } + if _, ok := _c.mutation.Created(); !ok { + v := githubinstallation.DefaultCreated() + _c.mutation.SetCreated(v) + } + if _, ok := _c.mutation.Updated(); !ok { + v := githubinstallation.DefaultUpdated() + _c.mutation.SetUpdated(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_c *GithubInstallationCreate) check() error { + if _, ok := _c.mutation.AccountLogin(); !ok { + return &ValidationError{Name: "account_login", err: errors.New(`ent: missing required field "GithubInstallation.account_login"`)} + } + if v, ok := _c.mutation.AccountLogin(); ok { + if err := githubinstallation.AccountLoginValidator(v); err != nil { + return &ValidationError{Name: "account_login", err: fmt.Errorf(`ent: validator failed for field "GithubInstallation.account_login": %w`, err)} + } + } + if _, ok := _c.mutation.AccountType(); !ok { + return &ValidationError{Name: "account_type", err: errors.New(`ent: missing required field "GithubInstallation.account_type"`)} + } + if _, ok := _c.mutation.AppID(); !ok { + return &ValidationError{Name: "app_id", err: errors.New(`ent: missing required field "GithubInstallation.app_id"`)} + } + if _, ok := _c.mutation.Repositories(); !ok { + return &ValidationError{Name: "repositories", err: errors.New(`ent: missing required field "GithubInstallation.repositories"`)} + } + if _, ok := _c.mutation.Status(); !ok { + return &ValidationError{Name: "status", err: errors.New(`ent: missing required field "GithubInstallation.status"`)} + } + if _, ok := _c.mutation.Created(); !ok { + return &ValidationError{Name: "created", err: errors.New(`ent: missing required field "GithubInstallation.created"`)} + } + if _, ok := _c.mutation.Updated(); !ok { + return &ValidationError{Name: "updated", err: errors.New(`ent: missing required field "GithubInstallation.updated"`)} + } + return nil +} + +func (_c *GithubInstallationCreate) sqlSave(ctx context.Context) (*GithubInstallation, error) { + if err := _c.check(); err != nil { + return nil, err + } + _node, _spec := _c.createSpec() + if err := sqlgraph.CreateNode(ctx, _c.driver, _spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + if _spec.ID.Value != _node.ID { + id := _spec.ID.Value.(int64) + _node.ID = int64(id) + } + _c.mutation.id = &_node.ID + _c.mutation.done = true + return _node, nil +} + +func (_c *GithubInstallationCreate) createSpec() (*GithubInstallation, *sqlgraph.CreateSpec) { + var ( + _node = &GithubInstallation{config: _c.config} + _spec = sqlgraph.NewCreateSpec(githubinstallation.Table, sqlgraph.NewFieldSpec(githubinstallation.FieldID, field.TypeInt64)) + ) + _spec.OnConflict = _c.conflict + if id, ok := _c.mutation.ID(); ok { + _node.ID = id + _spec.ID.Value = id + } + if value, ok := _c.mutation.AccountLogin(); ok { + _spec.SetField(githubinstallation.FieldAccountLogin, field.TypeString, value) + _node.AccountLogin = value + } + if value, ok := _c.mutation.AccountType(); ok { + _spec.SetField(githubinstallation.FieldAccountType, field.TypeString, value) + _node.AccountType = value + } + if value, ok := _c.mutation.AppID(); ok { + _spec.SetField(githubinstallation.FieldAppID, field.TypeInt64, value) + _node.AppID = value + } + if value, ok := _c.mutation.Repositories(); ok { + _spec.SetField(githubinstallation.FieldRepositories, field.TypeString, value) + _node.Repositories = value + } + if value, ok := _c.mutation.Status(); ok { + _spec.SetField(githubinstallation.FieldStatus, field.TypeString, value) + _node.Status = value + } + if value, ok := _c.mutation.Created(); ok { + _spec.SetField(githubinstallation.FieldCreated, field.TypeTime, value) + _node.Created = value + } + if value, ok := _c.mutation.Updated(); ok { + _spec.SetField(githubinstallation.FieldUpdated, field.TypeTime, value) + _node.Updated = value + } + return _node, _spec +} + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.GithubInstallation.Create(). +// SetAccountLogin(v). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.GithubInstallationUpsert) { +// SetAccountLogin(v+v). +// }). +// Exec(ctx) +func (_c *GithubInstallationCreate) OnConflict(opts ...sql.ConflictOption) *GithubInstallationUpsertOne { + _c.conflict = opts + return &GithubInstallationUpsertOne{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.GithubInstallation.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *GithubInstallationCreate) OnConflictColumns(columns ...string) *GithubInstallationUpsertOne { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &GithubInstallationUpsertOne{ + create: _c, + } +} + +type ( + // GithubInstallationUpsertOne is the builder for "upsert"-ing + // one GithubInstallation node. + GithubInstallationUpsertOne struct { + create *GithubInstallationCreate + } + + // GithubInstallationUpsert is the "OnConflict" setter. + GithubInstallationUpsert struct { + *sql.UpdateSet + } +) + +// SetAccountLogin sets the "account_login" field. +func (u *GithubInstallationUpsert) SetAccountLogin(v string) *GithubInstallationUpsert { + u.Set(githubinstallation.FieldAccountLogin, v) + return u +} + +// UpdateAccountLogin sets the "account_login" field to the value that was provided on create. +func (u *GithubInstallationUpsert) UpdateAccountLogin() *GithubInstallationUpsert { + u.SetExcluded(githubinstallation.FieldAccountLogin) + return u +} + +// SetAccountType sets the "account_type" field. +func (u *GithubInstallationUpsert) SetAccountType(v string) *GithubInstallationUpsert { + u.Set(githubinstallation.FieldAccountType, v) + return u +} + +// UpdateAccountType sets the "account_type" field to the value that was provided on create. +func (u *GithubInstallationUpsert) UpdateAccountType() *GithubInstallationUpsert { + u.SetExcluded(githubinstallation.FieldAccountType) + return u +} + +// SetAppID sets the "app_id" field. +func (u *GithubInstallationUpsert) SetAppID(v int64) *GithubInstallationUpsert { + u.Set(githubinstallation.FieldAppID, v) + return u +} + +// UpdateAppID sets the "app_id" field to the value that was provided on create. +func (u *GithubInstallationUpsert) UpdateAppID() *GithubInstallationUpsert { + u.SetExcluded(githubinstallation.FieldAppID) + return u +} + +// AddAppID adds v to the "app_id" field. +func (u *GithubInstallationUpsert) AddAppID(v int64) *GithubInstallationUpsert { + u.Add(githubinstallation.FieldAppID, v) + return u +} + +// SetRepositories sets the "repositories" field. +func (u *GithubInstallationUpsert) SetRepositories(v string) *GithubInstallationUpsert { + u.Set(githubinstallation.FieldRepositories, v) + return u +} + +// UpdateRepositories sets the "repositories" field to the value that was provided on create. +func (u *GithubInstallationUpsert) UpdateRepositories() *GithubInstallationUpsert { + u.SetExcluded(githubinstallation.FieldRepositories) + return u +} + +// SetStatus sets the "status" field. +func (u *GithubInstallationUpsert) SetStatus(v string) *GithubInstallationUpsert { + u.Set(githubinstallation.FieldStatus, v) + return u +} + +// UpdateStatus sets the "status" field to the value that was provided on create. +func (u *GithubInstallationUpsert) UpdateStatus() *GithubInstallationUpsert { + u.SetExcluded(githubinstallation.FieldStatus) + return u +} + +// SetUpdated sets the "updated" field. +func (u *GithubInstallationUpsert) SetUpdated(v time.Time) *GithubInstallationUpsert { + u.Set(githubinstallation.FieldUpdated, v) + return u +} + +// UpdateUpdated sets the "updated" field to the value that was provided on create. +func (u *GithubInstallationUpsert) UpdateUpdated() *GithubInstallationUpsert { + u.SetExcluded(githubinstallation.FieldUpdated) + return u +} + +// UpdateNewValues updates the mutable fields using the new values that were set on create except the ID field. +// Using this option is equivalent to using: +// +// client.GithubInstallation.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(githubinstallation.FieldID) +// }), +// ). +// Exec(ctx) +func (u *GithubInstallationUpsertOne) UpdateNewValues() *GithubInstallationUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + if _, exists := u.create.mutation.ID(); exists { + s.SetIgnore(githubinstallation.FieldID) + } + if _, exists := u.create.mutation.Created(); exists { + s.SetIgnore(githubinstallation.FieldCreated) + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.GithubInstallation.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *GithubInstallationUpsertOne) Ignore() *GithubInstallationUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *GithubInstallationUpsertOne) DoNothing() *GithubInstallationUpsertOne { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the GithubInstallationCreate.OnConflict +// documentation for more info. +func (u *GithubInstallationUpsertOne) Update(set func(*GithubInstallationUpsert)) *GithubInstallationUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&GithubInstallationUpsert{UpdateSet: update}) + })) + return u +} + +// SetAccountLogin sets the "account_login" field. +func (u *GithubInstallationUpsertOne) SetAccountLogin(v string) *GithubInstallationUpsertOne { + return u.Update(func(s *GithubInstallationUpsert) { + s.SetAccountLogin(v) + }) +} + +// UpdateAccountLogin sets the "account_login" field to the value that was provided on create. +func (u *GithubInstallationUpsertOne) UpdateAccountLogin() *GithubInstallationUpsertOne { + return u.Update(func(s *GithubInstallationUpsert) { + s.UpdateAccountLogin() + }) +} + +// SetAccountType sets the "account_type" field. +func (u *GithubInstallationUpsertOne) SetAccountType(v string) *GithubInstallationUpsertOne { + return u.Update(func(s *GithubInstallationUpsert) { + s.SetAccountType(v) + }) +} + +// UpdateAccountType sets the "account_type" field to the value that was provided on create. +func (u *GithubInstallationUpsertOne) UpdateAccountType() *GithubInstallationUpsertOne { + return u.Update(func(s *GithubInstallationUpsert) { + s.UpdateAccountType() + }) +} + +// SetAppID sets the "app_id" field. +func (u *GithubInstallationUpsertOne) SetAppID(v int64) *GithubInstallationUpsertOne { + return u.Update(func(s *GithubInstallationUpsert) { + s.SetAppID(v) + }) +} + +// AddAppID adds v to the "app_id" field. +func (u *GithubInstallationUpsertOne) AddAppID(v int64) *GithubInstallationUpsertOne { + return u.Update(func(s *GithubInstallationUpsert) { + s.AddAppID(v) + }) +} + +// UpdateAppID sets the "app_id" field to the value that was provided on create. +func (u *GithubInstallationUpsertOne) UpdateAppID() *GithubInstallationUpsertOne { + return u.Update(func(s *GithubInstallationUpsert) { + s.UpdateAppID() + }) +} + +// SetRepositories sets the "repositories" field. +func (u *GithubInstallationUpsertOne) SetRepositories(v string) *GithubInstallationUpsertOne { + return u.Update(func(s *GithubInstallationUpsert) { + s.SetRepositories(v) + }) +} + +// UpdateRepositories sets the "repositories" field to the value that was provided on create. +func (u *GithubInstallationUpsertOne) UpdateRepositories() *GithubInstallationUpsertOne { + return u.Update(func(s *GithubInstallationUpsert) { + s.UpdateRepositories() + }) +} + +// SetStatus sets the "status" field. +func (u *GithubInstallationUpsertOne) SetStatus(v string) *GithubInstallationUpsertOne { + return u.Update(func(s *GithubInstallationUpsert) { + s.SetStatus(v) + }) +} + +// UpdateStatus sets the "status" field to the value that was provided on create. +func (u *GithubInstallationUpsertOne) UpdateStatus() *GithubInstallationUpsertOne { + return u.Update(func(s *GithubInstallationUpsert) { + s.UpdateStatus() + }) +} + +// SetUpdated sets the "updated" field. +func (u *GithubInstallationUpsertOne) SetUpdated(v time.Time) *GithubInstallationUpsertOne { + return u.Update(func(s *GithubInstallationUpsert) { + s.SetUpdated(v) + }) +} + +// UpdateUpdated sets the "updated" field to the value that was provided on create. +func (u *GithubInstallationUpsertOne) UpdateUpdated() *GithubInstallationUpsertOne { + return u.Update(func(s *GithubInstallationUpsert) { + s.UpdateUpdated() + }) +} + +// Exec executes the query. +func (u *GithubInstallationUpsertOne) Exec(ctx context.Context) error { + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for GithubInstallationCreate.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *GithubInstallationUpsertOne) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} + +// Exec executes the UPSERT query and returns the inserted/updated ID. +func (u *GithubInstallationUpsertOne) ID(ctx context.Context) (id int64, err error) { + node, err := u.create.Save(ctx) + if err != nil { + return id, err + } + return node.ID, nil +} + +// IDX is like ID, but panics if an error occurs. +func (u *GithubInstallationUpsertOne) IDX(ctx context.Context) int64 { + id, err := u.ID(ctx) + if err != nil { + panic(err) + } + return id +} + +// GithubInstallationCreateBulk is the builder for creating many GithubInstallation entities in bulk. +type GithubInstallationCreateBulk struct { + config + err error + builders []*GithubInstallationCreate + conflict []sql.ConflictOption +} + +// Save creates the GithubInstallation entities in the database. +func (_c *GithubInstallationCreateBulk) Save(ctx context.Context) ([]*GithubInstallation, error) { + if _c.err != nil { + return nil, _c.err + } + specs := make([]*sqlgraph.CreateSpec, len(_c.builders)) + nodes := make([]*GithubInstallation, len(_c.builders)) + mutators := make([]Mutator, len(_c.builders)) + for i := range _c.builders { + func(i int, root context.Context) { + builder := _c.builders[i] + builder.defaults() + var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { + mutation, ok := m.(*GithubInstallationMutation) + if !ok { + return nil, fmt.Errorf("unexpected mutation type %T", m) + } + if err := builder.check(); err != nil { + return nil, err + } + builder.mutation = mutation + var err error + nodes[i], specs[i] = builder.createSpec() + if i < len(mutators)-1 { + _, err = mutators[i+1].Mutate(root, _c.builders[i+1].mutation) + } else { + spec := &sqlgraph.BatchCreateSpec{Nodes: specs} + spec.OnConflict = _c.conflict + // Invoke the actual operation on the latest mutation in the chain. + if err = sqlgraph.BatchCreate(ctx, _c.driver, spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + } + } + if err != nil { + return nil, err + } + mutation.id = &nodes[i].ID + if specs[i].ID.Value != nil && nodes[i].ID == 0 { + id := specs[i].ID.Value.(int64) + nodes[i].ID = int64(id) + } + mutation.done = true + return nodes[i], nil + }) + for i := len(builder.hooks) - 1; i >= 0; i-- { + mut = builder.hooks[i](mut) + } + mutators[i] = mut + }(i, ctx) + } + if len(mutators) > 0 { + if _, err := mutators[0].Mutate(ctx, _c.builders[0].mutation); err != nil { + return nil, err + } + } + return nodes, nil +} + +// SaveX is like Save, but panics if an error occurs. +func (_c *GithubInstallationCreateBulk) SaveX(ctx context.Context) []*GithubInstallation { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *GithubInstallationCreateBulk) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *GithubInstallationCreateBulk) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.GithubInstallation.CreateBulk(builders...). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.GithubInstallationUpsert) { +// SetAccountLogin(v+v). +// }). +// Exec(ctx) +func (_c *GithubInstallationCreateBulk) OnConflict(opts ...sql.ConflictOption) *GithubInstallationUpsertBulk { + _c.conflict = opts + return &GithubInstallationUpsertBulk{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.GithubInstallation.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *GithubInstallationCreateBulk) OnConflictColumns(columns ...string) *GithubInstallationUpsertBulk { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &GithubInstallationUpsertBulk{ + create: _c, + } +} + +// GithubInstallationUpsertBulk is the builder for "upsert"-ing +// a bulk of GithubInstallation nodes. +type GithubInstallationUpsertBulk struct { + create *GithubInstallationCreateBulk +} + +// UpdateNewValues updates the mutable fields using the new values that +// were set on create. Using this option is equivalent to using: +// +// client.GithubInstallation.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(githubinstallation.FieldID) +// }), +// ). +// Exec(ctx) +func (u *GithubInstallationUpsertBulk) UpdateNewValues() *GithubInstallationUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + for _, b := range u.create.builders { + if _, exists := b.mutation.ID(); exists { + s.SetIgnore(githubinstallation.FieldID) + } + if _, exists := b.mutation.Created(); exists { + s.SetIgnore(githubinstallation.FieldCreated) + } + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.GithubInstallation.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *GithubInstallationUpsertBulk) Ignore() *GithubInstallationUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *GithubInstallationUpsertBulk) DoNothing() *GithubInstallationUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the GithubInstallationCreateBulk.OnConflict +// documentation for more info. +func (u *GithubInstallationUpsertBulk) Update(set func(*GithubInstallationUpsert)) *GithubInstallationUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&GithubInstallationUpsert{UpdateSet: update}) + })) + return u +} + +// SetAccountLogin sets the "account_login" field. +func (u *GithubInstallationUpsertBulk) SetAccountLogin(v string) *GithubInstallationUpsertBulk { + return u.Update(func(s *GithubInstallationUpsert) { + s.SetAccountLogin(v) + }) +} + +// UpdateAccountLogin sets the "account_login" field to the value that was provided on create. +func (u *GithubInstallationUpsertBulk) UpdateAccountLogin() *GithubInstallationUpsertBulk { + return u.Update(func(s *GithubInstallationUpsert) { + s.UpdateAccountLogin() + }) +} + +// SetAccountType sets the "account_type" field. +func (u *GithubInstallationUpsertBulk) SetAccountType(v string) *GithubInstallationUpsertBulk { + return u.Update(func(s *GithubInstallationUpsert) { + s.SetAccountType(v) + }) +} + +// UpdateAccountType sets the "account_type" field to the value that was provided on create. +func (u *GithubInstallationUpsertBulk) UpdateAccountType() *GithubInstallationUpsertBulk { + return u.Update(func(s *GithubInstallationUpsert) { + s.UpdateAccountType() + }) +} + +// SetAppID sets the "app_id" field. +func (u *GithubInstallationUpsertBulk) SetAppID(v int64) *GithubInstallationUpsertBulk { + return u.Update(func(s *GithubInstallationUpsert) { + s.SetAppID(v) + }) +} + +// AddAppID adds v to the "app_id" field. +func (u *GithubInstallationUpsertBulk) AddAppID(v int64) *GithubInstallationUpsertBulk { + return u.Update(func(s *GithubInstallationUpsert) { + s.AddAppID(v) + }) +} + +// UpdateAppID sets the "app_id" field to the value that was provided on create. +func (u *GithubInstallationUpsertBulk) UpdateAppID() *GithubInstallationUpsertBulk { + return u.Update(func(s *GithubInstallationUpsert) { + s.UpdateAppID() + }) +} + +// SetRepositories sets the "repositories" field. +func (u *GithubInstallationUpsertBulk) SetRepositories(v string) *GithubInstallationUpsertBulk { + return u.Update(func(s *GithubInstallationUpsert) { + s.SetRepositories(v) + }) +} + +// UpdateRepositories sets the "repositories" field to the value that was provided on create. +func (u *GithubInstallationUpsertBulk) UpdateRepositories() *GithubInstallationUpsertBulk { + return u.Update(func(s *GithubInstallationUpsert) { + s.UpdateRepositories() + }) +} + +// SetStatus sets the "status" field. +func (u *GithubInstallationUpsertBulk) SetStatus(v string) *GithubInstallationUpsertBulk { + return u.Update(func(s *GithubInstallationUpsert) { + s.SetStatus(v) + }) +} + +// UpdateStatus sets the "status" field to the value that was provided on create. +func (u *GithubInstallationUpsertBulk) UpdateStatus() *GithubInstallationUpsertBulk { + return u.Update(func(s *GithubInstallationUpsert) { + s.UpdateStatus() + }) +} + +// SetUpdated sets the "updated" field. +func (u *GithubInstallationUpsertBulk) SetUpdated(v time.Time) *GithubInstallationUpsertBulk { + return u.Update(func(s *GithubInstallationUpsert) { + s.SetUpdated(v) + }) +} + +// UpdateUpdated sets the "updated" field to the value that was provided on create. +func (u *GithubInstallationUpsertBulk) UpdateUpdated() *GithubInstallationUpsertBulk { + return u.Update(func(s *GithubInstallationUpsert) { + s.UpdateUpdated() + }) +} + +// Exec executes the query. +func (u *GithubInstallationUpsertBulk) Exec(ctx context.Context) error { + if u.create.err != nil { + return u.create.err + } + for i, b := range u.create.builders { + if len(b.conflict) != 0 { + return fmt.Errorf("ent: OnConflict was set for builder %d. Set it on the GithubInstallationCreateBulk instead", i) + } + } + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for GithubInstallationCreateBulk.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *GithubInstallationUpsertBulk) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/pkg/ent/githubinstallation_delete.go b/pkg/ent/githubinstallation_delete.go new file mode 100644 index 000000000..31647abc9 --- /dev/null +++ b/pkg/ent/githubinstallation_delete.go @@ -0,0 +1,88 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/githubinstallation" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" +) + +// GithubInstallationDelete is the builder for deleting a GithubInstallation entity. +type GithubInstallationDelete struct { + config + hooks []Hook + mutation *GithubInstallationMutation +} + +// Where appends a list predicates to the GithubInstallationDelete builder. +func (_d *GithubInstallationDelete) Where(ps ...predicate.GithubInstallation) *GithubInstallationDelete { + _d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (_d *GithubInstallationDelete) Exec(ctx context.Context) (int, error) { + return withHooks(ctx, _d.sqlExec, _d.mutation, _d.hooks) +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *GithubInstallationDelete) ExecX(ctx context.Context) int { + n, err := _d.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (_d *GithubInstallationDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(githubinstallation.Table, sqlgraph.NewFieldSpec(githubinstallation.FieldID, field.TypeInt64)) + if ps := _d.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + affected, err := sqlgraph.DeleteNodes(ctx, _d.driver, _spec) + if err != nil && sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + _d.mutation.done = true + return affected, err +} + +// GithubInstallationDeleteOne is the builder for deleting a single GithubInstallation entity. +type GithubInstallationDeleteOne struct { + _d *GithubInstallationDelete +} + +// Where appends a list predicates to the GithubInstallationDelete builder. +func (_d *GithubInstallationDeleteOne) Where(ps ...predicate.GithubInstallation) *GithubInstallationDeleteOne { + _d._d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query. +func (_d *GithubInstallationDeleteOne) Exec(ctx context.Context) error { + n, err := _d._d.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{githubinstallation.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *GithubInstallationDeleteOne) ExecX(ctx context.Context) { + if err := _d.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/pkg/ent/githubinstallation_query.go b/pkg/ent/githubinstallation_query.go new file mode 100644 index 000000000..3ab727ef4 --- /dev/null +++ b/pkg/ent/githubinstallation_query.go @@ -0,0 +1,564 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "fmt" + "math" + + "entgo.io/ent" + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/githubinstallation" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" +) + +// GithubInstallationQuery is the builder for querying GithubInstallation entities. +type GithubInstallationQuery struct { + config + ctx *QueryContext + order []githubinstallation.OrderOption + inters []Interceptor + predicates []predicate.GithubInstallation + modifiers []func(*sql.Selector) + // intermediate query (i.e. traversal path). + sql *sql.Selector + path func(context.Context) (*sql.Selector, error) +} + +// Where adds a new predicate for the GithubInstallationQuery builder. +func (_q *GithubInstallationQuery) Where(ps ...predicate.GithubInstallation) *GithubInstallationQuery { + _q.predicates = append(_q.predicates, ps...) + return _q +} + +// Limit the number of records to be returned by this query. +func (_q *GithubInstallationQuery) Limit(limit int) *GithubInstallationQuery { + _q.ctx.Limit = &limit + return _q +} + +// Offset to start from. +func (_q *GithubInstallationQuery) Offset(offset int) *GithubInstallationQuery { + _q.ctx.Offset = &offset + return _q +} + +// Unique configures the query builder to filter duplicate records on query. +// By default, unique is set to true, and can be disabled using this method. +func (_q *GithubInstallationQuery) Unique(unique bool) *GithubInstallationQuery { + _q.ctx.Unique = &unique + return _q +} + +// Order specifies how the records should be ordered. +func (_q *GithubInstallationQuery) Order(o ...githubinstallation.OrderOption) *GithubInstallationQuery { + _q.order = append(_q.order, o...) + return _q +} + +// First returns the first GithubInstallation entity from the query. +// Returns a *NotFoundError when no GithubInstallation was found. +func (_q *GithubInstallationQuery) First(ctx context.Context) (*GithubInstallation, error) { + nodes, err := _q.Limit(1).All(setContextOp(ctx, _q.ctx, ent.OpQueryFirst)) + if err != nil { + return nil, err + } + if len(nodes) == 0 { + return nil, &NotFoundError{githubinstallation.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (_q *GithubInstallationQuery) FirstX(ctx context.Context) *GithubInstallation { + node, err := _q.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// FirstID returns the first GithubInstallation ID from the query. +// Returns a *NotFoundError when no GithubInstallation ID was found. +func (_q *GithubInstallationQuery) FirstID(ctx context.Context) (id int64, err error) { + var ids []int64 + if ids, err = _q.Limit(1).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryFirstID)); err != nil { + return + } + if len(ids) == 0 { + err = &NotFoundError{githubinstallation.Label} + return + } + return ids[0], nil +} + +// FirstIDX is like FirstID, but panics if an error occurs. +func (_q *GithubInstallationQuery) FirstIDX(ctx context.Context) int64 { + id, err := _q.FirstID(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return id +} + +// Only returns a single GithubInstallation entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one GithubInstallation entity is found. +// Returns a *NotFoundError when no GithubInstallation entities are found. +func (_q *GithubInstallationQuery) Only(ctx context.Context) (*GithubInstallation, error) { + nodes, err := _q.Limit(2).All(setContextOp(ctx, _q.ctx, ent.OpQueryOnly)) + if err != nil { + return nil, err + } + switch len(nodes) { + case 1: + return nodes[0], nil + case 0: + return nil, &NotFoundError{githubinstallation.Label} + default: + return nil, &NotSingularError{githubinstallation.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (_q *GithubInstallationQuery) OnlyX(ctx context.Context) *GithubInstallation { + node, err := _q.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// OnlyID is like Only, but returns the only GithubInstallation ID in the query. +// Returns a *NotSingularError when more than one GithubInstallation ID is found. +// Returns a *NotFoundError when no entities are found. +func (_q *GithubInstallationQuery) OnlyID(ctx context.Context) (id int64, err error) { + var ids []int64 + if ids, err = _q.Limit(2).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryOnlyID)); err != nil { + return + } + switch len(ids) { + case 1: + id = ids[0] + case 0: + err = &NotFoundError{githubinstallation.Label} + default: + err = &NotSingularError{githubinstallation.Label} + } + return +} + +// OnlyIDX is like OnlyID, but panics if an error occurs. +func (_q *GithubInstallationQuery) OnlyIDX(ctx context.Context) int64 { + id, err := _q.OnlyID(ctx) + if err != nil { + panic(err) + } + return id +} + +// All executes the query and returns a list of GithubInstallations. +func (_q *GithubInstallationQuery) All(ctx context.Context) ([]*GithubInstallation, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryAll) + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*GithubInstallation, *GithubInstallationQuery]() + return withInterceptors[[]*GithubInstallation](ctx, _q, qr, _q.inters) +} + +// AllX is like All, but panics if an error occurs. +func (_q *GithubInstallationQuery) AllX(ctx context.Context) []*GithubInstallation { + nodes, err := _q.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// IDs executes the query and returns a list of GithubInstallation IDs. +func (_q *GithubInstallationQuery) IDs(ctx context.Context) (ids []int64, err error) { + if _q.ctx.Unique == nil && _q.path != nil { + _q.Unique(true) + } + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryIDs) + if err = _q.Select(githubinstallation.FieldID).Scan(ctx, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// IDsX is like IDs, but panics if an error occurs. +func (_q *GithubInstallationQuery) IDsX(ctx context.Context) []int64 { + ids, err := _q.IDs(ctx) + if err != nil { + panic(err) + } + return ids +} + +// Count returns the count of the given query. +func (_q *GithubInstallationQuery) Count(ctx context.Context) (int, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryCount) + if err := _q.prepareQuery(ctx); err != nil { + return 0, err + } + return withInterceptors[int](ctx, _q, querierCount[*GithubInstallationQuery](), _q.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (_q *GithubInstallationQuery) CountX(ctx context.Context) int { + count, err := _q.Count(ctx) + if err != nil { + panic(err) + } + return count +} + +// Exist returns true if the query has elements in the graph. +func (_q *GithubInstallationQuery) Exist(ctx context.Context) (bool, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryExist) + switch _, err := _q.FirstID(ctx); { + case IsNotFound(err): + return false, nil + case err != nil: + return false, fmt.Errorf("ent: check existence: %w", err) + default: + return true, nil + } +} + +// ExistX is like Exist, but panics if an error occurs. +func (_q *GithubInstallationQuery) ExistX(ctx context.Context) bool { + exist, err := _q.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the GithubInstallationQuery builder, including all associated steps. It can be +// used to prepare common query builders and use them differently after the clone is made. +func (_q *GithubInstallationQuery) Clone() *GithubInstallationQuery { + if _q == nil { + return nil + } + return &GithubInstallationQuery{ + config: _q.config, + ctx: _q.ctx.Clone(), + order: append([]githubinstallation.OrderOption{}, _q.order...), + inters: append([]Interceptor{}, _q.inters...), + predicates: append([]predicate.GithubInstallation{}, _q.predicates...), + // clone intermediate query. + sql: _q.sql.Clone(), + path: _q.path, + } +} + +// GroupBy is used to group vertices by one or more fields/columns. +// It is often used with aggregate functions, like: count, max, mean, min, sum. +// +// Example: +// +// var v []struct { +// AccountLogin string `json:"account_login,omitempty"` +// Count int `json:"count,omitempty"` +// } +// +// client.GithubInstallation.Query(). +// GroupBy(githubinstallation.FieldAccountLogin). +// Aggregate(ent.Count()). +// Scan(ctx, &v) +func (_q *GithubInstallationQuery) GroupBy(field string, fields ...string) *GithubInstallationGroupBy { + _q.ctx.Fields = append([]string{field}, fields...) + grbuild := &GithubInstallationGroupBy{build: _q} + grbuild.flds = &_q.ctx.Fields + grbuild.label = githubinstallation.Label + grbuild.scan = grbuild.Scan + return grbuild +} + +// Select allows the selection one or more fields/columns for the given query, +// instead of selecting all fields in the entity. +// +// Example: +// +// var v []struct { +// AccountLogin string `json:"account_login,omitempty"` +// } +// +// client.GithubInstallation.Query(). +// Select(githubinstallation.FieldAccountLogin). +// Scan(ctx, &v) +func (_q *GithubInstallationQuery) Select(fields ...string) *GithubInstallationSelect { + _q.ctx.Fields = append(_q.ctx.Fields, fields...) + sbuild := &GithubInstallationSelect{GithubInstallationQuery: _q} + sbuild.label = githubinstallation.Label + sbuild.flds, sbuild.scan = &_q.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a GithubInstallationSelect configured with the given aggregations. +func (_q *GithubInstallationQuery) Aggregate(fns ...AggregateFunc) *GithubInstallationSelect { + return _q.Select().Aggregate(fns...) +} + +func (_q *GithubInstallationQuery) prepareQuery(ctx context.Context) error { + for _, inter := range _q.inters { + if inter == nil { + return fmt.Errorf("ent: uninitialized interceptor (forgotten import ent/runtime?)") + } + if trv, ok := inter.(Traverser); ok { + if err := trv.Traverse(ctx, _q); err != nil { + return err + } + } + } + for _, f := range _q.ctx.Fields { + if !githubinstallation.ValidColumn(f) { + return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + } + if _q.path != nil { + prev, err := _q.path(ctx) + if err != nil { + return err + } + _q.sql = prev + } + return nil +} + +func (_q *GithubInstallationQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*GithubInstallation, error) { + var ( + nodes = []*GithubInstallation{} + _spec = _q.querySpec() + ) + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*GithubInstallation).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &GithubInstallation{config: _q.config} + nodes = append(nodes, node) + return node.assignValues(columns, values) + } + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } + for i := range hooks { + hooks[i](ctx, _spec) + } + if err := sqlgraph.QueryNodes(ctx, _q.driver, _spec); err != nil { + return nil, err + } + if len(nodes) == 0 { + return nodes, nil + } + return nodes, nil +} + +func (_q *GithubInstallationQuery) sqlCount(ctx context.Context) (int, error) { + _spec := _q.querySpec() + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } + _spec.Node.Columns = _q.ctx.Fields + if len(_q.ctx.Fields) > 0 { + _spec.Unique = _q.ctx.Unique != nil && *_q.ctx.Unique + } + return sqlgraph.CountNodes(ctx, _q.driver, _spec) +} + +func (_q *GithubInstallationQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(githubinstallation.Table, githubinstallation.Columns, sqlgraph.NewFieldSpec(githubinstallation.FieldID, field.TypeInt64)) + _spec.From = _q.sql + if unique := _q.ctx.Unique; unique != nil { + _spec.Unique = *unique + } else if _q.path != nil { + _spec.Unique = true + } + if fields := _q.ctx.Fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, githubinstallation.FieldID) + for i := range fields { + if fields[i] != githubinstallation.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, fields[i]) + } + } + } + if ps := _q.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if limit := _q.ctx.Limit; limit != nil { + _spec.Limit = *limit + } + if offset := _q.ctx.Offset; offset != nil { + _spec.Offset = *offset + } + if ps := _q.order; len(ps) > 0 { + _spec.Order = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + return _spec +} + +func (_q *GithubInstallationQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(_q.driver.Dialect()) + t1 := builder.Table(githubinstallation.Table) + columns := _q.ctx.Fields + if len(columns) == 0 { + columns = githubinstallation.Columns + } + selector := builder.Select(t1.Columns(columns...)...).From(t1) + if _q.sql != nil { + selector = _q.sql + selector.Select(selector.Columns(columns...)...) + } + if _q.ctx.Unique != nil && *_q.ctx.Unique { + selector.Distinct() + } + for _, m := range _q.modifiers { + m(selector) + } + for _, p := range _q.predicates { + p(selector) + } + for _, p := range _q.order { + p(selector) + } + if offset := _q.ctx.Offset; offset != nil { + // limit is mandatory for offset clause. We start + // with default value, and override it below if needed. + selector.Offset(*offset).Limit(math.MaxInt32) + } + if limit := _q.ctx.Limit; limit != nil { + selector.Limit(*limit) + } + return selector +} + +// ForUpdate locks the selected rows against concurrent updates, and prevent them from being +// updated, deleted or "selected ... for update" by other sessions, until the transaction is +// either committed or rolled-back. +func (_q *GithubInstallationQuery) ForUpdate(opts ...sql.LockOption) *GithubInstallationQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForUpdate(opts...) + }) + return _q +} + +// ForShare behaves similarly to ForUpdate, except that it acquires a shared mode lock +// on any rows that are read. Other sessions can read the rows, but cannot modify them +// until your transaction commits. +func (_q *GithubInstallationQuery) ForShare(opts ...sql.LockOption) *GithubInstallationQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForShare(opts...) + }) + return _q +} + +// GithubInstallationGroupBy is the group-by builder for GithubInstallation entities. +type GithubInstallationGroupBy struct { + selector + build *GithubInstallationQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (_g *GithubInstallationGroupBy) Aggregate(fns ...AggregateFunc) *GithubInstallationGroupBy { + _g.fns = append(_g.fns, fns...) + return _g +} + +// Scan applies the selector query and scans the result into the given value. +func (_g *GithubInstallationGroupBy) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, _g.build.ctx, ent.OpQueryGroupBy) + if err := _g.build.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*GithubInstallationQuery, *GithubInstallationGroupBy](ctx, _g.build, _g, _g.build.inters, v) +} + +func (_g *GithubInstallationGroupBy) sqlScan(ctx context.Context, root *GithubInstallationQuery, v any) error { + selector := root.sqlQuery(ctx).Select() + aggregation := make([]string, 0, len(_g.fns)) + for _, fn := range _g.fns { + aggregation = append(aggregation, fn(selector)) + } + if len(selector.SelectedColumns()) == 0 { + columns := make([]string, 0, len(*_g.flds)+len(_g.fns)) + for _, f := range *_g.flds { + columns = append(columns, selector.C(f)) + } + columns = append(columns, aggregation...) + selector.Select(columns...) + } + selector.GroupBy(selector.Columns(*_g.flds...)...) + if err := selector.Err(); err != nil { + return err + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := _g.build.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} + +// GithubInstallationSelect is the builder for selecting fields of GithubInstallation entities. +type GithubInstallationSelect struct { + *GithubInstallationQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (_s *GithubInstallationSelect) Aggregate(fns ...AggregateFunc) *GithubInstallationSelect { + _s.fns = append(_s.fns, fns...) + return _s +} + +// Scan applies the selector query and scans the result into the given value. +func (_s *GithubInstallationSelect) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, _s.ctx, ent.OpQuerySelect) + if err := _s.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*GithubInstallationQuery, *GithubInstallationSelect](ctx, _s.GithubInstallationQuery, _s, _s.inters, v) +} + +func (_s *GithubInstallationSelect) sqlScan(ctx context.Context, root *GithubInstallationQuery, v any) error { + selector := root.sqlQuery(ctx) + aggregation := make([]string, 0, len(_s.fns)) + for _, fn := range _s.fns { + aggregation = append(aggregation, fn(selector)) + } + switch n := len(*_s.selector.flds); { + case n == 0 && len(aggregation) > 0: + selector.Select(aggregation...) + case n != 0 && len(aggregation) > 0: + selector.AppendSelect(aggregation...) + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := _s.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} diff --git a/pkg/ent/githubinstallation_update.go b/pkg/ent/githubinstallation_update.go new file mode 100644 index 000000000..98e8d2941 --- /dev/null +++ b/pkg/ent/githubinstallation_update.go @@ -0,0 +1,428 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/githubinstallation" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" +) + +// GithubInstallationUpdate is the builder for updating GithubInstallation entities. +type GithubInstallationUpdate struct { + config + hooks []Hook + mutation *GithubInstallationMutation +} + +// Where appends a list predicates to the GithubInstallationUpdate builder. +func (_u *GithubInstallationUpdate) Where(ps ...predicate.GithubInstallation) *GithubInstallationUpdate { + _u.mutation.Where(ps...) + return _u +} + +// SetAccountLogin sets the "account_login" field. +func (_u *GithubInstallationUpdate) SetAccountLogin(v string) *GithubInstallationUpdate { + _u.mutation.SetAccountLogin(v) + return _u +} + +// SetNillableAccountLogin sets the "account_login" field if the given value is not nil. +func (_u *GithubInstallationUpdate) SetNillableAccountLogin(v *string) *GithubInstallationUpdate { + if v != nil { + _u.SetAccountLogin(*v) + } + return _u +} + +// SetAccountType sets the "account_type" field. +func (_u *GithubInstallationUpdate) SetAccountType(v string) *GithubInstallationUpdate { + _u.mutation.SetAccountType(v) + return _u +} + +// SetNillableAccountType sets the "account_type" field if the given value is not nil. +func (_u *GithubInstallationUpdate) SetNillableAccountType(v *string) *GithubInstallationUpdate { + if v != nil { + _u.SetAccountType(*v) + } + return _u +} + +// SetAppID sets the "app_id" field. +func (_u *GithubInstallationUpdate) SetAppID(v int64) *GithubInstallationUpdate { + _u.mutation.ResetAppID() + _u.mutation.SetAppID(v) + return _u +} + +// SetNillableAppID sets the "app_id" field if the given value is not nil. +func (_u *GithubInstallationUpdate) SetNillableAppID(v *int64) *GithubInstallationUpdate { + if v != nil { + _u.SetAppID(*v) + } + return _u +} + +// AddAppID adds value to the "app_id" field. +func (_u *GithubInstallationUpdate) AddAppID(v int64) *GithubInstallationUpdate { + _u.mutation.AddAppID(v) + return _u +} + +// SetRepositories sets the "repositories" field. +func (_u *GithubInstallationUpdate) SetRepositories(v string) *GithubInstallationUpdate { + _u.mutation.SetRepositories(v) + return _u +} + +// SetNillableRepositories sets the "repositories" field if the given value is not nil. +func (_u *GithubInstallationUpdate) SetNillableRepositories(v *string) *GithubInstallationUpdate { + if v != nil { + _u.SetRepositories(*v) + } + return _u +} + +// SetStatus sets the "status" field. +func (_u *GithubInstallationUpdate) SetStatus(v string) *GithubInstallationUpdate { + _u.mutation.SetStatus(v) + return _u +} + +// SetNillableStatus sets the "status" field if the given value is not nil. +func (_u *GithubInstallationUpdate) SetNillableStatus(v *string) *GithubInstallationUpdate { + if v != nil { + _u.SetStatus(*v) + } + return _u +} + +// SetUpdated sets the "updated" field. +func (_u *GithubInstallationUpdate) SetUpdated(v time.Time) *GithubInstallationUpdate { + _u.mutation.SetUpdated(v) + return _u +} + +// Mutation returns the GithubInstallationMutation object of the builder. +func (_u *GithubInstallationUpdate) Mutation() *GithubInstallationMutation { + return _u.mutation +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (_u *GithubInstallationUpdate) Save(ctx context.Context) (int, error) { + _u.defaults() + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *GithubInstallationUpdate) SaveX(ctx context.Context) int { + affected, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (_u *GithubInstallationUpdate) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *GithubInstallationUpdate) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_u *GithubInstallationUpdate) defaults() { + if _, ok := _u.mutation.Updated(); !ok { + v := githubinstallation.UpdateDefaultUpdated() + _u.mutation.SetUpdated(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_u *GithubInstallationUpdate) check() error { + if v, ok := _u.mutation.AccountLogin(); ok { + if err := githubinstallation.AccountLoginValidator(v); err != nil { + return &ValidationError{Name: "account_login", err: fmt.Errorf(`ent: validator failed for field "GithubInstallation.account_login": %w`, err)} + } + } + return nil +} + +func (_u *GithubInstallationUpdate) sqlSave(ctx context.Context) (_node int, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(githubinstallation.Table, githubinstallation.Columns, sqlgraph.NewFieldSpec(githubinstallation.FieldID, field.TypeInt64)) + if ps := _u.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := _u.mutation.AccountLogin(); ok { + _spec.SetField(githubinstallation.FieldAccountLogin, field.TypeString, value) + } + if value, ok := _u.mutation.AccountType(); ok { + _spec.SetField(githubinstallation.FieldAccountType, field.TypeString, value) + } + if value, ok := _u.mutation.AppID(); ok { + _spec.SetField(githubinstallation.FieldAppID, field.TypeInt64, value) + } + if value, ok := _u.mutation.AddedAppID(); ok { + _spec.AddField(githubinstallation.FieldAppID, field.TypeInt64, value) + } + if value, ok := _u.mutation.Repositories(); ok { + _spec.SetField(githubinstallation.FieldRepositories, field.TypeString, value) + } + if value, ok := _u.mutation.Status(); ok { + _spec.SetField(githubinstallation.FieldStatus, field.TypeString, value) + } + if value, ok := _u.mutation.Updated(); ok { + _spec.SetField(githubinstallation.FieldUpdated, field.TypeTime, value) + } + if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{githubinstallation.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + _u.mutation.done = true + return _node, nil +} + +// GithubInstallationUpdateOne is the builder for updating a single GithubInstallation entity. +type GithubInstallationUpdateOne struct { + config + fields []string + hooks []Hook + mutation *GithubInstallationMutation +} + +// SetAccountLogin sets the "account_login" field. +func (_u *GithubInstallationUpdateOne) SetAccountLogin(v string) *GithubInstallationUpdateOne { + _u.mutation.SetAccountLogin(v) + return _u +} + +// SetNillableAccountLogin sets the "account_login" field if the given value is not nil. +func (_u *GithubInstallationUpdateOne) SetNillableAccountLogin(v *string) *GithubInstallationUpdateOne { + if v != nil { + _u.SetAccountLogin(*v) + } + return _u +} + +// SetAccountType sets the "account_type" field. +func (_u *GithubInstallationUpdateOne) SetAccountType(v string) *GithubInstallationUpdateOne { + _u.mutation.SetAccountType(v) + return _u +} + +// SetNillableAccountType sets the "account_type" field if the given value is not nil. +func (_u *GithubInstallationUpdateOne) SetNillableAccountType(v *string) *GithubInstallationUpdateOne { + if v != nil { + _u.SetAccountType(*v) + } + return _u +} + +// SetAppID sets the "app_id" field. +func (_u *GithubInstallationUpdateOne) SetAppID(v int64) *GithubInstallationUpdateOne { + _u.mutation.ResetAppID() + _u.mutation.SetAppID(v) + return _u +} + +// SetNillableAppID sets the "app_id" field if the given value is not nil. +func (_u *GithubInstallationUpdateOne) SetNillableAppID(v *int64) *GithubInstallationUpdateOne { + if v != nil { + _u.SetAppID(*v) + } + return _u +} + +// AddAppID adds value to the "app_id" field. +func (_u *GithubInstallationUpdateOne) AddAppID(v int64) *GithubInstallationUpdateOne { + _u.mutation.AddAppID(v) + return _u +} + +// SetRepositories sets the "repositories" field. +func (_u *GithubInstallationUpdateOne) SetRepositories(v string) *GithubInstallationUpdateOne { + _u.mutation.SetRepositories(v) + return _u +} + +// SetNillableRepositories sets the "repositories" field if the given value is not nil. +func (_u *GithubInstallationUpdateOne) SetNillableRepositories(v *string) *GithubInstallationUpdateOne { + if v != nil { + _u.SetRepositories(*v) + } + return _u +} + +// SetStatus sets the "status" field. +func (_u *GithubInstallationUpdateOne) SetStatus(v string) *GithubInstallationUpdateOne { + _u.mutation.SetStatus(v) + return _u +} + +// SetNillableStatus sets the "status" field if the given value is not nil. +func (_u *GithubInstallationUpdateOne) SetNillableStatus(v *string) *GithubInstallationUpdateOne { + if v != nil { + _u.SetStatus(*v) + } + return _u +} + +// SetUpdated sets the "updated" field. +func (_u *GithubInstallationUpdateOne) SetUpdated(v time.Time) *GithubInstallationUpdateOne { + _u.mutation.SetUpdated(v) + return _u +} + +// Mutation returns the GithubInstallationMutation object of the builder. +func (_u *GithubInstallationUpdateOne) Mutation() *GithubInstallationMutation { + return _u.mutation +} + +// Where appends a list predicates to the GithubInstallationUpdate builder. +func (_u *GithubInstallationUpdateOne) Where(ps ...predicate.GithubInstallation) *GithubInstallationUpdateOne { + _u.mutation.Where(ps...) + return _u +} + +// Select allows selecting one or more fields (columns) of the returned entity. +// The default is selecting all fields defined in the entity schema. +func (_u *GithubInstallationUpdateOne) Select(field string, fields ...string) *GithubInstallationUpdateOne { + _u.fields = append([]string{field}, fields...) + return _u +} + +// Save executes the query and returns the updated GithubInstallation entity. +func (_u *GithubInstallationUpdateOne) Save(ctx context.Context) (*GithubInstallation, error) { + _u.defaults() + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *GithubInstallationUpdateOne) SaveX(ctx context.Context) *GithubInstallation { + node, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (_u *GithubInstallationUpdateOne) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *GithubInstallationUpdateOne) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_u *GithubInstallationUpdateOne) defaults() { + if _, ok := _u.mutation.Updated(); !ok { + v := githubinstallation.UpdateDefaultUpdated() + _u.mutation.SetUpdated(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_u *GithubInstallationUpdateOne) check() error { + if v, ok := _u.mutation.AccountLogin(); ok { + if err := githubinstallation.AccountLoginValidator(v); err != nil { + return &ValidationError{Name: "account_login", err: fmt.Errorf(`ent: validator failed for field "GithubInstallation.account_login": %w`, err)} + } + } + return nil +} + +func (_u *GithubInstallationUpdateOne) sqlSave(ctx context.Context) (_node *GithubInstallation, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(githubinstallation.Table, githubinstallation.Columns, sqlgraph.NewFieldSpec(githubinstallation.FieldID, field.TypeInt64)) + id, ok := _u.mutation.ID() + if !ok { + return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "GithubInstallation.id" for update`)} + } + _spec.Node.ID.Value = id + if fields := _u.fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, githubinstallation.FieldID) + for _, f := range fields { + if !githubinstallation.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + if f != githubinstallation.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, f) + } + } + } + if ps := _u.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := _u.mutation.AccountLogin(); ok { + _spec.SetField(githubinstallation.FieldAccountLogin, field.TypeString, value) + } + if value, ok := _u.mutation.AccountType(); ok { + _spec.SetField(githubinstallation.FieldAccountType, field.TypeString, value) + } + if value, ok := _u.mutation.AppID(); ok { + _spec.SetField(githubinstallation.FieldAppID, field.TypeInt64, value) + } + if value, ok := _u.mutation.AddedAppID(); ok { + _spec.AddField(githubinstallation.FieldAppID, field.TypeInt64, value) + } + if value, ok := _u.mutation.Repositories(); ok { + _spec.SetField(githubinstallation.FieldRepositories, field.TypeString, value) + } + if value, ok := _u.mutation.Status(); ok { + _spec.SetField(githubinstallation.FieldStatus, field.TypeString, value) + } + if value, ok := _u.mutation.Updated(); ok { + _spec.SetField(githubinstallation.FieldUpdated, field.TypeTime, value) + } + _node = &GithubInstallation{config: _u.config} + _spec.Assign = _node.assignValues + _spec.ScanValues = _node.scanValues + if err = sqlgraph.UpdateNode(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{githubinstallation.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + _u.mutation.done = true + return _node, nil +} diff --git a/pkg/ent/group_create.go b/pkg/ent/group_create.go index 48fad2925..43ee0d95d 100644 --- a/pkg/ent/group_create.go +++ b/pkg/ent/group_create.go @@ -8,6 +8,8 @@ import ( "fmt" "time" + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" "entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/schema/field" "github.com/GoogleCloudPlatform/scion/pkg/ent/group" @@ -22,6 +24,7 @@ type GroupCreate struct { config mutation *GroupMutation hooks []Hook + conflict []sql.ConflictOption } // SetName sets the "name" field. @@ -341,6 +344,7 @@ func (_c *GroupCreate) createSpec() (*Group, *sqlgraph.CreateSpec) { _node = &Group{config: _c.config} _spec = sqlgraph.NewCreateSpec(group.Table, sqlgraph.NewFieldSpec(group.FieldID, field.TypeUUID)) ) + _spec.OnConflict = _c.conflict if id, ok := _c.mutation.ID(); ok { _node.ID = id _spec.ID.Value = &id @@ -469,11 +473,488 @@ func (_c *GroupCreate) createSpec() (*Group, *sqlgraph.CreateSpec) { return _node, _spec } +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.Group.Create(). +// SetName(v). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.GroupUpsert) { +// SetName(v+v). +// }). +// Exec(ctx) +func (_c *GroupCreate) OnConflict(opts ...sql.ConflictOption) *GroupUpsertOne { + _c.conflict = opts + return &GroupUpsertOne{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.Group.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *GroupCreate) OnConflictColumns(columns ...string) *GroupUpsertOne { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &GroupUpsertOne{ + create: _c, + } +} + +type ( + // GroupUpsertOne is the builder for "upsert"-ing + // one Group node. + GroupUpsertOne struct { + create *GroupCreate + } + + // GroupUpsert is the "OnConflict" setter. + GroupUpsert struct { + *sql.UpdateSet + } +) + +// SetName sets the "name" field. +func (u *GroupUpsert) SetName(v string) *GroupUpsert { + u.Set(group.FieldName, v) + return u +} + +// UpdateName sets the "name" field to the value that was provided on create. +func (u *GroupUpsert) UpdateName() *GroupUpsert { + u.SetExcluded(group.FieldName) + return u +} + +// SetSlug sets the "slug" field. +func (u *GroupUpsert) SetSlug(v string) *GroupUpsert { + u.Set(group.FieldSlug, v) + return u +} + +// UpdateSlug sets the "slug" field to the value that was provided on create. +func (u *GroupUpsert) UpdateSlug() *GroupUpsert { + u.SetExcluded(group.FieldSlug) + return u +} + +// SetDescription sets the "description" field. +func (u *GroupUpsert) SetDescription(v string) *GroupUpsert { + u.Set(group.FieldDescription, v) + return u +} + +// UpdateDescription sets the "description" field to the value that was provided on create. +func (u *GroupUpsert) UpdateDescription() *GroupUpsert { + u.SetExcluded(group.FieldDescription) + return u +} + +// ClearDescription clears the value of the "description" field. +func (u *GroupUpsert) ClearDescription() *GroupUpsert { + u.SetNull(group.FieldDescription) + return u +} + +// SetGroupType sets the "group_type" field. +func (u *GroupUpsert) SetGroupType(v group.GroupType) *GroupUpsert { + u.Set(group.FieldGroupType, v) + return u +} + +// UpdateGroupType sets the "group_type" field to the value that was provided on create. +func (u *GroupUpsert) UpdateGroupType() *GroupUpsert { + u.SetExcluded(group.FieldGroupType) + return u +} + +// SetProjectID sets the "project_id" field. +func (u *GroupUpsert) SetProjectID(v uuid.UUID) *GroupUpsert { + u.Set(group.FieldProjectID, v) + return u +} + +// UpdateProjectID sets the "project_id" field to the value that was provided on create. +func (u *GroupUpsert) UpdateProjectID() *GroupUpsert { + u.SetExcluded(group.FieldProjectID) + return u +} + +// ClearProjectID clears the value of the "project_id" field. +func (u *GroupUpsert) ClearProjectID() *GroupUpsert { + u.SetNull(group.FieldProjectID) + return u +} + +// SetLabels sets the "labels" field. +func (u *GroupUpsert) SetLabels(v map[string]string) *GroupUpsert { + u.Set(group.FieldLabels, v) + return u +} + +// UpdateLabels sets the "labels" field to the value that was provided on create. +func (u *GroupUpsert) UpdateLabels() *GroupUpsert { + u.SetExcluded(group.FieldLabels) + return u +} + +// ClearLabels clears the value of the "labels" field. +func (u *GroupUpsert) ClearLabels() *GroupUpsert { + u.SetNull(group.FieldLabels) + return u +} + +// SetAnnotations sets the "annotations" field. +func (u *GroupUpsert) SetAnnotations(v map[string]string) *GroupUpsert { + u.Set(group.FieldAnnotations, v) + return u +} + +// UpdateAnnotations sets the "annotations" field to the value that was provided on create. +func (u *GroupUpsert) UpdateAnnotations() *GroupUpsert { + u.SetExcluded(group.FieldAnnotations) + return u +} + +// ClearAnnotations clears the value of the "annotations" field. +func (u *GroupUpsert) ClearAnnotations() *GroupUpsert { + u.SetNull(group.FieldAnnotations) + return u +} + +// SetUpdated sets the "updated" field. +func (u *GroupUpsert) SetUpdated(v time.Time) *GroupUpsert { + u.Set(group.FieldUpdated, v) + return u +} + +// UpdateUpdated sets the "updated" field to the value that was provided on create. +func (u *GroupUpsert) UpdateUpdated() *GroupUpsert { + u.SetExcluded(group.FieldUpdated) + return u +} + +// SetCreatedBy sets the "created_by" field. +func (u *GroupUpsert) SetCreatedBy(v string) *GroupUpsert { + u.Set(group.FieldCreatedBy, v) + return u +} + +// UpdateCreatedBy sets the "created_by" field to the value that was provided on create. +func (u *GroupUpsert) UpdateCreatedBy() *GroupUpsert { + u.SetExcluded(group.FieldCreatedBy) + return u +} + +// ClearCreatedBy clears the value of the "created_by" field. +func (u *GroupUpsert) ClearCreatedBy() *GroupUpsert { + u.SetNull(group.FieldCreatedBy) + return u +} + +// SetOwnerID sets the "owner_id" field. +func (u *GroupUpsert) SetOwnerID(v uuid.UUID) *GroupUpsert { + u.Set(group.FieldOwnerID, v) + return u +} + +// UpdateOwnerID sets the "owner_id" field to the value that was provided on create. +func (u *GroupUpsert) UpdateOwnerID() *GroupUpsert { + u.SetExcluded(group.FieldOwnerID) + return u +} + +// ClearOwnerID clears the value of the "owner_id" field. +func (u *GroupUpsert) ClearOwnerID() *GroupUpsert { + u.SetNull(group.FieldOwnerID) + return u +} + +// UpdateNewValues updates the mutable fields using the new values that were set on create except the ID field. +// Using this option is equivalent to using: +// +// client.Group.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(group.FieldID) +// }), +// ). +// Exec(ctx) +func (u *GroupUpsertOne) UpdateNewValues() *GroupUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + if _, exists := u.create.mutation.ID(); exists { + s.SetIgnore(group.FieldID) + } + if _, exists := u.create.mutation.Created(); exists { + s.SetIgnore(group.FieldCreated) + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.Group.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *GroupUpsertOne) Ignore() *GroupUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *GroupUpsertOne) DoNothing() *GroupUpsertOne { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the GroupCreate.OnConflict +// documentation for more info. +func (u *GroupUpsertOne) Update(set func(*GroupUpsert)) *GroupUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&GroupUpsert{UpdateSet: update}) + })) + return u +} + +// SetName sets the "name" field. +func (u *GroupUpsertOne) SetName(v string) *GroupUpsertOne { + return u.Update(func(s *GroupUpsert) { + s.SetName(v) + }) +} + +// UpdateName sets the "name" field to the value that was provided on create. +func (u *GroupUpsertOne) UpdateName() *GroupUpsertOne { + return u.Update(func(s *GroupUpsert) { + s.UpdateName() + }) +} + +// SetSlug sets the "slug" field. +func (u *GroupUpsertOne) SetSlug(v string) *GroupUpsertOne { + return u.Update(func(s *GroupUpsert) { + s.SetSlug(v) + }) +} + +// UpdateSlug sets the "slug" field to the value that was provided on create. +func (u *GroupUpsertOne) UpdateSlug() *GroupUpsertOne { + return u.Update(func(s *GroupUpsert) { + s.UpdateSlug() + }) +} + +// SetDescription sets the "description" field. +func (u *GroupUpsertOne) SetDescription(v string) *GroupUpsertOne { + return u.Update(func(s *GroupUpsert) { + s.SetDescription(v) + }) +} + +// UpdateDescription sets the "description" field to the value that was provided on create. +func (u *GroupUpsertOne) UpdateDescription() *GroupUpsertOne { + return u.Update(func(s *GroupUpsert) { + s.UpdateDescription() + }) +} + +// ClearDescription clears the value of the "description" field. +func (u *GroupUpsertOne) ClearDescription() *GroupUpsertOne { + return u.Update(func(s *GroupUpsert) { + s.ClearDescription() + }) +} + +// SetGroupType sets the "group_type" field. +func (u *GroupUpsertOne) SetGroupType(v group.GroupType) *GroupUpsertOne { + return u.Update(func(s *GroupUpsert) { + s.SetGroupType(v) + }) +} + +// UpdateGroupType sets the "group_type" field to the value that was provided on create. +func (u *GroupUpsertOne) UpdateGroupType() *GroupUpsertOne { + return u.Update(func(s *GroupUpsert) { + s.UpdateGroupType() + }) +} + +// SetProjectID sets the "project_id" field. +func (u *GroupUpsertOne) SetProjectID(v uuid.UUID) *GroupUpsertOne { + return u.Update(func(s *GroupUpsert) { + s.SetProjectID(v) + }) +} + +// UpdateProjectID sets the "project_id" field to the value that was provided on create. +func (u *GroupUpsertOne) UpdateProjectID() *GroupUpsertOne { + return u.Update(func(s *GroupUpsert) { + s.UpdateProjectID() + }) +} + +// ClearProjectID clears the value of the "project_id" field. +func (u *GroupUpsertOne) ClearProjectID() *GroupUpsertOne { + return u.Update(func(s *GroupUpsert) { + s.ClearProjectID() + }) +} + +// SetLabels sets the "labels" field. +func (u *GroupUpsertOne) SetLabels(v map[string]string) *GroupUpsertOne { + return u.Update(func(s *GroupUpsert) { + s.SetLabels(v) + }) +} + +// UpdateLabels sets the "labels" field to the value that was provided on create. +func (u *GroupUpsertOne) UpdateLabels() *GroupUpsertOne { + return u.Update(func(s *GroupUpsert) { + s.UpdateLabels() + }) +} + +// ClearLabels clears the value of the "labels" field. +func (u *GroupUpsertOne) ClearLabels() *GroupUpsertOne { + return u.Update(func(s *GroupUpsert) { + s.ClearLabels() + }) +} + +// SetAnnotations sets the "annotations" field. +func (u *GroupUpsertOne) SetAnnotations(v map[string]string) *GroupUpsertOne { + return u.Update(func(s *GroupUpsert) { + s.SetAnnotations(v) + }) +} + +// UpdateAnnotations sets the "annotations" field to the value that was provided on create. +func (u *GroupUpsertOne) UpdateAnnotations() *GroupUpsertOne { + return u.Update(func(s *GroupUpsert) { + s.UpdateAnnotations() + }) +} + +// ClearAnnotations clears the value of the "annotations" field. +func (u *GroupUpsertOne) ClearAnnotations() *GroupUpsertOne { + return u.Update(func(s *GroupUpsert) { + s.ClearAnnotations() + }) +} + +// SetUpdated sets the "updated" field. +func (u *GroupUpsertOne) SetUpdated(v time.Time) *GroupUpsertOne { + return u.Update(func(s *GroupUpsert) { + s.SetUpdated(v) + }) +} + +// UpdateUpdated sets the "updated" field to the value that was provided on create. +func (u *GroupUpsertOne) UpdateUpdated() *GroupUpsertOne { + return u.Update(func(s *GroupUpsert) { + s.UpdateUpdated() + }) +} + +// SetCreatedBy sets the "created_by" field. +func (u *GroupUpsertOne) SetCreatedBy(v string) *GroupUpsertOne { + return u.Update(func(s *GroupUpsert) { + s.SetCreatedBy(v) + }) +} + +// UpdateCreatedBy sets the "created_by" field to the value that was provided on create. +func (u *GroupUpsertOne) UpdateCreatedBy() *GroupUpsertOne { + return u.Update(func(s *GroupUpsert) { + s.UpdateCreatedBy() + }) +} + +// ClearCreatedBy clears the value of the "created_by" field. +func (u *GroupUpsertOne) ClearCreatedBy() *GroupUpsertOne { + return u.Update(func(s *GroupUpsert) { + s.ClearCreatedBy() + }) +} + +// SetOwnerID sets the "owner_id" field. +func (u *GroupUpsertOne) SetOwnerID(v uuid.UUID) *GroupUpsertOne { + return u.Update(func(s *GroupUpsert) { + s.SetOwnerID(v) + }) +} + +// UpdateOwnerID sets the "owner_id" field to the value that was provided on create. +func (u *GroupUpsertOne) UpdateOwnerID() *GroupUpsertOne { + return u.Update(func(s *GroupUpsert) { + s.UpdateOwnerID() + }) +} + +// ClearOwnerID clears the value of the "owner_id" field. +func (u *GroupUpsertOne) ClearOwnerID() *GroupUpsertOne { + return u.Update(func(s *GroupUpsert) { + s.ClearOwnerID() + }) +} + +// Exec executes the query. +func (u *GroupUpsertOne) Exec(ctx context.Context) error { + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for GroupCreate.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *GroupUpsertOne) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} + +// Exec executes the UPSERT query and returns the inserted/updated ID. +func (u *GroupUpsertOne) ID(ctx context.Context) (id uuid.UUID, err error) { + if u.create.driver.Dialect() == dialect.MySQL { + // In case of "ON CONFLICT", there is no way to get back non-numeric ID + // fields from the database since MySQL does not support the RETURNING clause. + return id, errors.New("ent: GroupUpsertOne.ID is not supported by MySQL driver. Use GroupUpsertOne.Exec instead") + } + node, err := u.create.Save(ctx) + if err != nil { + return id, err + } + return node.ID, nil +} + +// IDX is like ID, but panics if an error occurs. +func (u *GroupUpsertOne) IDX(ctx context.Context) uuid.UUID { + id, err := u.ID(ctx) + if err != nil { + panic(err) + } + return id +} + // GroupCreateBulk is the builder for creating many Group entities in bulk. type GroupCreateBulk struct { config err error builders []*GroupCreate + conflict []sql.ConflictOption } // Save creates the Group entities in the database. @@ -503,6 +984,7 @@ func (_c *GroupCreateBulk) Save(ctx context.Context) ([]*Group, error) { _, err = mutators[i+1].Mutate(root, _c.builders[i+1].mutation) } else { spec := &sqlgraph.BatchCreateSpec{Nodes: specs} + spec.OnConflict = _c.conflict // Invoke the actual operation on the latest mutation in the chain. if err = sqlgraph.BatchCreate(ctx, _c.driver, spec); err != nil { if sqlgraph.IsConstraintError(err) { @@ -552,3 +1034,302 @@ func (_c *GroupCreateBulk) ExecX(ctx context.Context) { panic(err) } } + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.Group.CreateBulk(builders...). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.GroupUpsert) { +// SetName(v+v). +// }). +// Exec(ctx) +func (_c *GroupCreateBulk) OnConflict(opts ...sql.ConflictOption) *GroupUpsertBulk { + _c.conflict = opts + return &GroupUpsertBulk{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.Group.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *GroupCreateBulk) OnConflictColumns(columns ...string) *GroupUpsertBulk { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &GroupUpsertBulk{ + create: _c, + } +} + +// GroupUpsertBulk is the builder for "upsert"-ing +// a bulk of Group nodes. +type GroupUpsertBulk struct { + create *GroupCreateBulk +} + +// UpdateNewValues updates the mutable fields using the new values that +// were set on create. Using this option is equivalent to using: +// +// client.Group.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(group.FieldID) +// }), +// ). +// Exec(ctx) +func (u *GroupUpsertBulk) UpdateNewValues() *GroupUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + for _, b := range u.create.builders { + if _, exists := b.mutation.ID(); exists { + s.SetIgnore(group.FieldID) + } + if _, exists := b.mutation.Created(); exists { + s.SetIgnore(group.FieldCreated) + } + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.Group.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *GroupUpsertBulk) Ignore() *GroupUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *GroupUpsertBulk) DoNothing() *GroupUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the GroupCreateBulk.OnConflict +// documentation for more info. +func (u *GroupUpsertBulk) Update(set func(*GroupUpsert)) *GroupUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&GroupUpsert{UpdateSet: update}) + })) + return u +} + +// SetName sets the "name" field. +func (u *GroupUpsertBulk) SetName(v string) *GroupUpsertBulk { + return u.Update(func(s *GroupUpsert) { + s.SetName(v) + }) +} + +// UpdateName sets the "name" field to the value that was provided on create. +func (u *GroupUpsertBulk) UpdateName() *GroupUpsertBulk { + return u.Update(func(s *GroupUpsert) { + s.UpdateName() + }) +} + +// SetSlug sets the "slug" field. +func (u *GroupUpsertBulk) SetSlug(v string) *GroupUpsertBulk { + return u.Update(func(s *GroupUpsert) { + s.SetSlug(v) + }) +} + +// UpdateSlug sets the "slug" field to the value that was provided on create. +func (u *GroupUpsertBulk) UpdateSlug() *GroupUpsertBulk { + return u.Update(func(s *GroupUpsert) { + s.UpdateSlug() + }) +} + +// SetDescription sets the "description" field. +func (u *GroupUpsertBulk) SetDescription(v string) *GroupUpsertBulk { + return u.Update(func(s *GroupUpsert) { + s.SetDescription(v) + }) +} + +// UpdateDescription sets the "description" field to the value that was provided on create. +func (u *GroupUpsertBulk) UpdateDescription() *GroupUpsertBulk { + return u.Update(func(s *GroupUpsert) { + s.UpdateDescription() + }) +} + +// ClearDescription clears the value of the "description" field. +func (u *GroupUpsertBulk) ClearDescription() *GroupUpsertBulk { + return u.Update(func(s *GroupUpsert) { + s.ClearDescription() + }) +} + +// SetGroupType sets the "group_type" field. +func (u *GroupUpsertBulk) SetGroupType(v group.GroupType) *GroupUpsertBulk { + return u.Update(func(s *GroupUpsert) { + s.SetGroupType(v) + }) +} + +// UpdateGroupType sets the "group_type" field to the value that was provided on create. +func (u *GroupUpsertBulk) UpdateGroupType() *GroupUpsertBulk { + return u.Update(func(s *GroupUpsert) { + s.UpdateGroupType() + }) +} + +// SetProjectID sets the "project_id" field. +func (u *GroupUpsertBulk) SetProjectID(v uuid.UUID) *GroupUpsertBulk { + return u.Update(func(s *GroupUpsert) { + s.SetProjectID(v) + }) +} + +// UpdateProjectID sets the "project_id" field to the value that was provided on create. +func (u *GroupUpsertBulk) UpdateProjectID() *GroupUpsertBulk { + return u.Update(func(s *GroupUpsert) { + s.UpdateProjectID() + }) +} + +// ClearProjectID clears the value of the "project_id" field. +func (u *GroupUpsertBulk) ClearProjectID() *GroupUpsertBulk { + return u.Update(func(s *GroupUpsert) { + s.ClearProjectID() + }) +} + +// SetLabels sets the "labels" field. +func (u *GroupUpsertBulk) SetLabels(v map[string]string) *GroupUpsertBulk { + return u.Update(func(s *GroupUpsert) { + s.SetLabels(v) + }) +} + +// UpdateLabels sets the "labels" field to the value that was provided on create. +func (u *GroupUpsertBulk) UpdateLabels() *GroupUpsertBulk { + return u.Update(func(s *GroupUpsert) { + s.UpdateLabels() + }) +} + +// ClearLabels clears the value of the "labels" field. +func (u *GroupUpsertBulk) ClearLabels() *GroupUpsertBulk { + return u.Update(func(s *GroupUpsert) { + s.ClearLabels() + }) +} + +// SetAnnotations sets the "annotations" field. +func (u *GroupUpsertBulk) SetAnnotations(v map[string]string) *GroupUpsertBulk { + return u.Update(func(s *GroupUpsert) { + s.SetAnnotations(v) + }) +} + +// UpdateAnnotations sets the "annotations" field to the value that was provided on create. +func (u *GroupUpsertBulk) UpdateAnnotations() *GroupUpsertBulk { + return u.Update(func(s *GroupUpsert) { + s.UpdateAnnotations() + }) +} + +// ClearAnnotations clears the value of the "annotations" field. +func (u *GroupUpsertBulk) ClearAnnotations() *GroupUpsertBulk { + return u.Update(func(s *GroupUpsert) { + s.ClearAnnotations() + }) +} + +// SetUpdated sets the "updated" field. +func (u *GroupUpsertBulk) SetUpdated(v time.Time) *GroupUpsertBulk { + return u.Update(func(s *GroupUpsert) { + s.SetUpdated(v) + }) +} + +// UpdateUpdated sets the "updated" field to the value that was provided on create. +func (u *GroupUpsertBulk) UpdateUpdated() *GroupUpsertBulk { + return u.Update(func(s *GroupUpsert) { + s.UpdateUpdated() + }) +} + +// SetCreatedBy sets the "created_by" field. +func (u *GroupUpsertBulk) SetCreatedBy(v string) *GroupUpsertBulk { + return u.Update(func(s *GroupUpsert) { + s.SetCreatedBy(v) + }) +} + +// UpdateCreatedBy sets the "created_by" field to the value that was provided on create. +func (u *GroupUpsertBulk) UpdateCreatedBy() *GroupUpsertBulk { + return u.Update(func(s *GroupUpsert) { + s.UpdateCreatedBy() + }) +} + +// ClearCreatedBy clears the value of the "created_by" field. +func (u *GroupUpsertBulk) ClearCreatedBy() *GroupUpsertBulk { + return u.Update(func(s *GroupUpsert) { + s.ClearCreatedBy() + }) +} + +// SetOwnerID sets the "owner_id" field. +func (u *GroupUpsertBulk) SetOwnerID(v uuid.UUID) *GroupUpsertBulk { + return u.Update(func(s *GroupUpsert) { + s.SetOwnerID(v) + }) +} + +// UpdateOwnerID sets the "owner_id" field to the value that was provided on create. +func (u *GroupUpsertBulk) UpdateOwnerID() *GroupUpsertBulk { + return u.Update(func(s *GroupUpsert) { + s.UpdateOwnerID() + }) +} + +// ClearOwnerID clears the value of the "owner_id" field. +func (u *GroupUpsertBulk) ClearOwnerID() *GroupUpsertBulk { + return u.Update(func(s *GroupUpsert) { + s.ClearOwnerID() + }) +} + +// Exec executes the query. +func (u *GroupUpsertBulk) Exec(ctx context.Context) error { + if u.create.err != nil { + return u.create.err + } + for i, b := range u.create.builders { + if len(b.conflict) != 0 { + return fmt.Errorf("ent: OnConflict was set for builder %d. Set it on the GroupCreateBulk instead", i) + } + } + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for GroupCreateBulk.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *GroupUpsertBulk) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/pkg/ent/group_query.go b/pkg/ent/group_query.go index 543950b2b..0a529fa1e 100644 --- a/pkg/ent/group_query.go +++ b/pkg/ent/group_query.go @@ -9,6 +9,7 @@ import ( "math" "entgo.io/ent" + "entgo.io/ent/dialect" "entgo.io/ent/dialect/sql" "entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/schema/field" @@ -32,6 +33,7 @@ type GroupQuery struct { withChildGroups *GroupQuery withOwner *UserQuery withPolicyBindings *PolicyBindingQuery + modifiers []func(*sql.Selector) // intermediate query (i.e. traversal path). sql *sql.Selector path func(context.Context) (*sql.Selector, error) @@ -531,6 +533,9 @@ func (_q *GroupQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*Group, node.Edges.loadedTypes = loadedTypes return node.assignValues(columns, values) } + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } for i := range hooks { hooks[i](ctx, _spec) } @@ -797,6 +802,9 @@ func (_q *GroupQuery) loadPolicyBindings(ctx context.Context, query *PolicyBindi func (_q *GroupQuery) sqlCount(ctx context.Context) (int, error) { _spec := _q.querySpec() + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } _spec.Node.Columns = _q.ctx.Fields if len(_q.ctx.Fields) > 0 { _spec.Unique = _q.ctx.Unique != nil && *_q.ctx.Unique @@ -862,6 +870,9 @@ func (_q *GroupQuery) sqlQuery(ctx context.Context) *sql.Selector { if _q.ctx.Unique != nil && *_q.ctx.Unique { selector.Distinct() } + for _, m := range _q.modifiers { + m(selector) + } for _, p := range _q.predicates { p(selector) } @@ -879,6 +890,32 @@ func (_q *GroupQuery) sqlQuery(ctx context.Context) *sql.Selector { return selector } +// ForUpdate locks the selected rows against concurrent updates, and prevent them from being +// updated, deleted or "selected ... for update" by other sessions, until the transaction is +// either committed or rolled-back. +func (_q *GroupQuery) ForUpdate(opts ...sql.LockOption) *GroupQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForUpdate(opts...) + }) + return _q +} + +// ForShare behaves similarly to ForUpdate, except that it acquires a shared mode lock +// on any rows that are read. Other sessions can read the rows, but cannot modify them +// until your transaction commits. +func (_q *GroupQuery) ForShare(opts ...sql.LockOption) *GroupQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForShare(opts...) + }) + return _q +} + // GroupGroupBy is the group-by builder for Group entities. type GroupGroupBy struct { selector diff --git a/pkg/ent/groupmembership_create.go b/pkg/ent/groupmembership_create.go index f3d24856d..60b8225b7 100644 --- a/pkg/ent/groupmembership_create.go +++ b/pkg/ent/groupmembership_create.go @@ -8,6 +8,8 @@ import ( "fmt" "time" + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" "entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/schema/field" "github.com/GoogleCloudPlatform/scion/pkg/ent/agent" @@ -22,6 +24,7 @@ type GroupMembershipCreate struct { config mutation *GroupMembershipMutation hooks []Hook + conflict []sql.ConflictOption } // SetRole sets the "role" field. @@ -228,6 +231,7 @@ func (_c *GroupMembershipCreate) createSpec() (*GroupMembership, *sqlgraph.Creat _node = &GroupMembership{config: _c.config} _spec = sqlgraph.NewCreateSpec(groupmembership.Table, sqlgraph.NewFieldSpec(groupmembership.FieldID, field.TypeUUID)) ) + _spec.OnConflict = _c.conflict if id, ok := _c.mutation.ID(); ok { _node.ID = id _spec.ID.Value = &id @@ -298,11 +302,319 @@ func (_c *GroupMembershipCreate) createSpec() (*GroupMembership, *sqlgraph.Creat return _node, _spec } +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.GroupMembership.Create(). +// SetRole(v). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.GroupMembershipUpsert) { +// SetRole(v+v). +// }). +// Exec(ctx) +func (_c *GroupMembershipCreate) OnConflict(opts ...sql.ConflictOption) *GroupMembershipUpsertOne { + _c.conflict = opts + return &GroupMembershipUpsertOne{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.GroupMembership.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *GroupMembershipCreate) OnConflictColumns(columns ...string) *GroupMembershipUpsertOne { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &GroupMembershipUpsertOne{ + create: _c, + } +} + +type ( + // GroupMembershipUpsertOne is the builder for "upsert"-ing + // one GroupMembership node. + GroupMembershipUpsertOne struct { + create *GroupMembershipCreate + } + + // GroupMembershipUpsert is the "OnConflict" setter. + GroupMembershipUpsert struct { + *sql.UpdateSet + } +) + +// SetRole sets the "role" field. +func (u *GroupMembershipUpsert) SetRole(v groupmembership.Role) *GroupMembershipUpsert { + u.Set(groupmembership.FieldRole, v) + return u +} + +// UpdateRole sets the "role" field to the value that was provided on create. +func (u *GroupMembershipUpsert) UpdateRole() *GroupMembershipUpsert { + u.SetExcluded(groupmembership.FieldRole) + return u +} + +// SetAddedBy sets the "added_by" field. +func (u *GroupMembershipUpsert) SetAddedBy(v string) *GroupMembershipUpsert { + u.Set(groupmembership.FieldAddedBy, v) + return u +} + +// UpdateAddedBy sets the "added_by" field to the value that was provided on create. +func (u *GroupMembershipUpsert) UpdateAddedBy() *GroupMembershipUpsert { + u.SetExcluded(groupmembership.FieldAddedBy) + return u +} + +// ClearAddedBy clears the value of the "added_by" field. +func (u *GroupMembershipUpsert) ClearAddedBy() *GroupMembershipUpsert { + u.SetNull(groupmembership.FieldAddedBy) + return u +} + +// SetGroupID sets the "group_id" field. +func (u *GroupMembershipUpsert) SetGroupID(v uuid.UUID) *GroupMembershipUpsert { + u.Set(groupmembership.FieldGroupID, v) + return u +} + +// UpdateGroupID sets the "group_id" field to the value that was provided on create. +func (u *GroupMembershipUpsert) UpdateGroupID() *GroupMembershipUpsert { + u.SetExcluded(groupmembership.FieldGroupID) + return u +} + +// SetUserID sets the "user_id" field. +func (u *GroupMembershipUpsert) SetUserID(v uuid.UUID) *GroupMembershipUpsert { + u.Set(groupmembership.FieldUserID, v) + return u +} + +// UpdateUserID sets the "user_id" field to the value that was provided on create. +func (u *GroupMembershipUpsert) UpdateUserID() *GroupMembershipUpsert { + u.SetExcluded(groupmembership.FieldUserID) + return u +} + +// ClearUserID clears the value of the "user_id" field. +func (u *GroupMembershipUpsert) ClearUserID() *GroupMembershipUpsert { + u.SetNull(groupmembership.FieldUserID) + return u +} + +// SetAgentID sets the "agent_id" field. +func (u *GroupMembershipUpsert) SetAgentID(v uuid.UUID) *GroupMembershipUpsert { + u.Set(groupmembership.FieldAgentID, v) + return u +} + +// UpdateAgentID sets the "agent_id" field to the value that was provided on create. +func (u *GroupMembershipUpsert) UpdateAgentID() *GroupMembershipUpsert { + u.SetExcluded(groupmembership.FieldAgentID) + return u +} + +// ClearAgentID clears the value of the "agent_id" field. +func (u *GroupMembershipUpsert) ClearAgentID() *GroupMembershipUpsert { + u.SetNull(groupmembership.FieldAgentID) + return u +} + +// UpdateNewValues updates the mutable fields using the new values that were set on create except the ID field. +// Using this option is equivalent to using: +// +// client.GroupMembership.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(groupmembership.FieldID) +// }), +// ). +// Exec(ctx) +func (u *GroupMembershipUpsertOne) UpdateNewValues() *GroupMembershipUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + if _, exists := u.create.mutation.ID(); exists { + s.SetIgnore(groupmembership.FieldID) + } + if _, exists := u.create.mutation.AddedAt(); exists { + s.SetIgnore(groupmembership.FieldAddedAt) + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.GroupMembership.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *GroupMembershipUpsertOne) Ignore() *GroupMembershipUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *GroupMembershipUpsertOne) DoNothing() *GroupMembershipUpsertOne { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the GroupMembershipCreate.OnConflict +// documentation for more info. +func (u *GroupMembershipUpsertOne) Update(set func(*GroupMembershipUpsert)) *GroupMembershipUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&GroupMembershipUpsert{UpdateSet: update}) + })) + return u +} + +// SetRole sets the "role" field. +func (u *GroupMembershipUpsertOne) SetRole(v groupmembership.Role) *GroupMembershipUpsertOne { + return u.Update(func(s *GroupMembershipUpsert) { + s.SetRole(v) + }) +} + +// UpdateRole sets the "role" field to the value that was provided on create. +func (u *GroupMembershipUpsertOne) UpdateRole() *GroupMembershipUpsertOne { + return u.Update(func(s *GroupMembershipUpsert) { + s.UpdateRole() + }) +} + +// SetAddedBy sets the "added_by" field. +func (u *GroupMembershipUpsertOne) SetAddedBy(v string) *GroupMembershipUpsertOne { + return u.Update(func(s *GroupMembershipUpsert) { + s.SetAddedBy(v) + }) +} + +// UpdateAddedBy sets the "added_by" field to the value that was provided on create. +func (u *GroupMembershipUpsertOne) UpdateAddedBy() *GroupMembershipUpsertOne { + return u.Update(func(s *GroupMembershipUpsert) { + s.UpdateAddedBy() + }) +} + +// ClearAddedBy clears the value of the "added_by" field. +func (u *GroupMembershipUpsertOne) ClearAddedBy() *GroupMembershipUpsertOne { + return u.Update(func(s *GroupMembershipUpsert) { + s.ClearAddedBy() + }) +} + +// SetGroupID sets the "group_id" field. +func (u *GroupMembershipUpsertOne) SetGroupID(v uuid.UUID) *GroupMembershipUpsertOne { + return u.Update(func(s *GroupMembershipUpsert) { + s.SetGroupID(v) + }) +} + +// UpdateGroupID sets the "group_id" field to the value that was provided on create. +func (u *GroupMembershipUpsertOne) UpdateGroupID() *GroupMembershipUpsertOne { + return u.Update(func(s *GroupMembershipUpsert) { + s.UpdateGroupID() + }) +} + +// SetUserID sets the "user_id" field. +func (u *GroupMembershipUpsertOne) SetUserID(v uuid.UUID) *GroupMembershipUpsertOne { + return u.Update(func(s *GroupMembershipUpsert) { + s.SetUserID(v) + }) +} + +// UpdateUserID sets the "user_id" field to the value that was provided on create. +func (u *GroupMembershipUpsertOne) UpdateUserID() *GroupMembershipUpsertOne { + return u.Update(func(s *GroupMembershipUpsert) { + s.UpdateUserID() + }) +} + +// ClearUserID clears the value of the "user_id" field. +func (u *GroupMembershipUpsertOne) ClearUserID() *GroupMembershipUpsertOne { + return u.Update(func(s *GroupMembershipUpsert) { + s.ClearUserID() + }) +} + +// SetAgentID sets the "agent_id" field. +func (u *GroupMembershipUpsertOne) SetAgentID(v uuid.UUID) *GroupMembershipUpsertOne { + return u.Update(func(s *GroupMembershipUpsert) { + s.SetAgentID(v) + }) +} + +// UpdateAgentID sets the "agent_id" field to the value that was provided on create. +func (u *GroupMembershipUpsertOne) UpdateAgentID() *GroupMembershipUpsertOne { + return u.Update(func(s *GroupMembershipUpsert) { + s.UpdateAgentID() + }) +} + +// ClearAgentID clears the value of the "agent_id" field. +func (u *GroupMembershipUpsertOne) ClearAgentID() *GroupMembershipUpsertOne { + return u.Update(func(s *GroupMembershipUpsert) { + s.ClearAgentID() + }) +} + +// Exec executes the query. +func (u *GroupMembershipUpsertOne) Exec(ctx context.Context) error { + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for GroupMembershipCreate.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *GroupMembershipUpsertOne) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} + +// Exec executes the UPSERT query and returns the inserted/updated ID. +func (u *GroupMembershipUpsertOne) ID(ctx context.Context) (id uuid.UUID, err error) { + if u.create.driver.Dialect() == dialect.MySQL { + // In case of "ON CONFLICT", there is no way to get back non-numeric ID + // fields from the database since MySQL does not support the RETURNING clause. + return id, errors.New("ent: GroupMembershipUpsertOne.ID is not supported by MySQL driver. Use GroupMembershipUpsertOne.Exec instead") + } + node, err := u.create.Save(ctx) + if err != nil { + return id, err + } + return node.ID, nil +} + +// IDX is like ID, but panics if an error occurs. +func (u *GroupMembershipUpsertOne) IDX(ctx context.Context) uuid.UUID { + id, err := u.ID(ctx) + if err != nil { + panic(err) + } + return id +} + // GroupMembershipCreateBulk is the builder for creating many GroupMembership entities in bulk. type GroupMembershipCreateBulk struct { config err error builders []*GroupMembershipCreate + conflict []sql.ConflictOption } // Save creates the GroupMembership entities in the database. @@ -332,6 +644,7 @@ func (_c *GroupMembershipCreateBulk) Save(ctx context.Context) ([]*GroupMembersh _, err = mutators[i+1].Mutate(root, _c.builders[i+1].mutation) } else { spec := &sqlgraph.BatchCreateSpec{Nodes: specs} + spec.OnConflict = _c.conflict // Invoke the actual operation on the latest mutation in the chain. if err = sqlgraph.BatchCreate(ctx, _c.driver, spec); err != nil { if sqlgraph.IsConstraintError(err) { @@ -381,3 +694,211 @@ func (_c *GroupMembershipCreateBulk) ExecX(ctx context.Context) { panic(err) } } + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.GroupMembership.CreateBulk(builders...). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.GroupMembershipUpsert) { +// SetRole(v+v). +// }). +// Exec(ctx) +func (_c *GroupMembershipCreateBulk) OnConflict(opts ...sql.ConflictOption) *GroupMembershipUpsertBulk { + _c.conflict = opts + return &GroupMembershipUpsertBulk{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.GroupMembership.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *GroupMembershipCreateBulk) OnConflictColumns(columns ...string) *GroupMembershipUpsertBulk { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &GroupMembershipUpsertBulk{ + create: _c, + } +} + +// GroupMembershipUpsertBulk is the builder for "upsert"-ing +// a bulk of GroupMembership nodes. +type GroupMembershipUpsertBulk struct { + create *GroupMembershipCreateBulk +} + +// UpdateNewValues updates the mutable fields using the new values that +// were set on create. Using this option is equivalent to using: +// +// client.GroupMembership.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(groupmembership.FieldID) +// }), +// ). +// Exec(ctx) +func (u *GroupMembershipUpsertBulk) UpdateNewValues() *GroupMembershipUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + for _, b := range u.create.builders { + if _, exists := b.mutation.ID(); exists { + s.SetIgnore(groupmembership.FieldID) + } + if _, exists := b.mutation.AddedAt(); exists { + s.SetIgnore(groupmembership.FieldAddedAt) + } + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.GroupMembership.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *GroupMembershipUpsertBulk) Ignore() *GroupMembershipUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *GroupMembershipUpsertBulk) DoNothing() *GroupMembershipUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the GroupMembershipCreateBulk.OnConflict +// documentation for more info. +func (u *GroupMembershipUpsertBulk) Update(set func(*GroupMembershipUpsert)) *GroupMembershipUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&GroupMembershipUpsert{UpdateSet: update}) + })) + return u +} + +// SetRole sets the "role" field. +func (u *GroupMembershipUpsertBulk) SetRole(v groupmembership.Role) *GroupMembershipUpsertBulk { + return u.Update(func(s *GroupMembershipUpsert) { + s.SetRole(v) + }) +} + +// UpdateRole sets the "role" field to the value that was provided on create. +func (u *GroupMembershipUpsertBulk) UpdateRole() *GroupMembershipUpsertBulk { + return u.Update(func(s *GroupMembershipUpsert) { + s.UpdateRole() + }) +} + +// SetAddedBy sets the "added_by" field. +func (u *GroupMembershipUpsertBulk) SetAddedBy(v string) *GroupMembershipUpsertBulk { + return u.Update(func(s *GroupMembershipUpsert) { + s.SetAddedBy(v) + }) +} + +// UpdateAddedBy sets the "added_by" field to the value that was provided on create. +func (u *GroupMembershipUpsertBulk) UpdateAddedBy() *GroupMembershipUpsertBulk { + return u.Update(func(s *GroupMembershipUpsert) { + s.UpdateAddedBy() + }) +} + +// ClearAddedBy clears the value of the "added_by" field. +func (u *GroupMembershipUpsertBulk) ClearAddedBy() *GroupMembershipUpsertBulk { + return u.Update(func(s *GroupMembershipUpsert) { + s.ClearAddedBy() + }) +} + +// SetGroupID sets the "group_id" field. +func (u *GroupMembershipUpsertBulk) SetGroupID(v uuid.UUID) *GroupMembershipUpsertBulk { + return u.Update(func(s *GroupMembershipUpsert) { + s.SetGroupID(v) + }) +} + +// UpdateGroupID sets the "group_id" field to the value that was provided on create. +func (u *GroupMembershipUpsertBulk) UpdateGroupID() *GroupMembershipUpsertBulk { + return u.Update(func(s *GroupMembershipUpsert) { + s.UpdateGroupID() + }) +} + +// SetUserID sets the "user_id" field. +func (u *GroupMembershipUpsertBulk) SetUserID(v uuid.UUID) *GroupMembershipUpsertBulk { + return u.Update(func(s *GroupMembershipUpsert) { + s.SetUserID(v) + }) +} + +// UpdateUserID sets the "user_id" field to the value that was provided on create. +func (u *GroupMembershipUpsertBulk) UpdateUserID() *GroupMembershipUpsertBulk { + return u.Update(func(s *GroupMembershipUpsert) { + s.UpdateUserID() + }) +} + +// ClearUserID clears the value of the "user_id" field. +func (u *GroupMembershipUpsertBulk) ClearUserID() *GroupMembershipUpsertBulk { + return u.Update(func(s *GroupMembershipUpsert) { + s.ClearUserID() + }) +} + +// SetAgentID sets the "agent_id" field. +func (u *GroupMembershipUpsertBulk) SetAgentID(v uuid.UUID) *GroupMembershipUpsertBulk { + return u.Update(func(s *GroupMembershipUpsert) { + s.SetAgentID(v) + }) +} + +// UpdateAgentID sets the "agent_id" field to the value that was provided on create. +func (u *GroupMembershipUpsertBulk) UpdateAgentID() *GroupMembershipUpsertBulk { + return u.Update(func(s *GroupMembershipUpsert) { + s.UpdateAgentID() + }) +} + +// ClearAgentID clears the value of the "agent_id" field. +func (u *GroupMembershipUpsertBulk) ClearAgentID() *GroupMembershipUpsertBulk { + return u.Update(func(s *GroupMembershipUpsert) { + s.ClearAgentID() + }) +} + +// Exec executes the query. +func (u *GroupMembershipUpsertBulk) Exec(ctx context.Context) error { + if u.create.err != nil { + return u.create.err + } + for i, b := range u.create.builders { + if len(b.conflict) != 0 { + return fmt.Errorf("ent: OnConflict was set for builder %d. Set it on the GroupMembershipCreateBulk instead", i) + } + } + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for GroupMembershipCreateBulk.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *GroupMembershipUpsertBulk) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/pkg/ent/groupmembership_query.go b/pkg/ent/groupmembership_query.go index 5884f9f86..b913d78e2 100644 --- a/pkg/ent/groupmembership_query.go +++ b/pkg/ent/groupmembership_query.go @@ -8,6 +8,7 @@ import ( "math" "entgo.io/ent" + "entgo.io/ent/dialect" "entgo.io/ent/dialect/sql" "entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/schema/field" @@ -29,6 +30,7 @@ type GroupMembershipQuery struct { withGroup *GroupQuery withUser *UserQuery withAgent *AgentQuery + modifiers []func(*sql.Selector) // intermediate query (i.e. traversal path). sql *sql.Selector path func(context.Context) (*sql.Selector, error) @@ -458,6 +460,9 @@ func (_q *GroupMembershipQuery) sqlAll(ctx context.Context, hooks ...queryHook) node.Edges.loadedTypes = loadedTypes return node.assignValues(columns, values) } + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } for i := range hooks { hooks[i](ctx, _spec) } @@ -584,6 +589,9 @@ func (_q *GroupMembershipQuery) loadAgent(ctx context.Context, query *AgentQuery func (_q *GroupMembershipQuery) sqlCount(ctx context.Context) (int, error) { _spec := _q.querySpec() + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } _spec.Node.Columns = _q.ctx.Fields if len(_q.ctx.Fields) > 0 { _spec.Unique = _q.ctx.Unique != nil && *_q.ctx.Unique @@ -655,6 +663,9 @@ func (_q *GroupMembershipQuery) sqlQuery(ctx context.Context) *sql.Selector { if _q.ctx.Unique != nil && *_q.ctx.Unique { selector.Distinct() } + for _, m := range _q.modifiers { + m(selector) + } for _, p := range _q.predicates { p(selector) } @@ -672,6 +683,32 @@ func (_q *GroupMembershipQuery) sqlQuery(ctx context.Context) *sql.Selector { return selector } +// ForUpdate locks the selected rows against concurrent updates, and prevent them from being +// updated, deleted or "selected ... for update" by other sessions, until the transaction is +// either committed or rolled-back. +func (_q *GroupMembershipQuery) ForUpdate(opts ...sql.LockOption) *GroupMembershipQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForUpdate(opts...) + }) + return _q +} + +// ForShare behaves similarly to ForUpdate, except that it acquires a shared mode lock +// on any rows that are read. Other sessions can read the rows, but cannot modify them +// until your transaction commits. +func (_q *GroupMembershipQuery) ForShare(opts ...sql.LockOption) *GroupMembershipQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForShare(opts...) + }) + return _q +} + // GroupMembershipGroupBy is the group-by builder for GroupMembership entities. type GroupMembershipGroupBy struct { selector diff --git a/pkg/ent/harnessconfig.go b/pkg/ent/harnessconfig.go new file mode 100644 index 000000000..4e85897ce --- /dev/null +++ b/pkg/ent/harnessconfig.go @@ -0,0 +1,329 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "fmt" + "strings" + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "github.com/GoogleCloudPlatform/scion/pkg/ent/harnessconfig" + "github.com/google/uuid" +) + +// HarnessConfig is the model entity for the HarnessConfig schema. +type HarnessConfig struct { + config `json:"-"` + // ID of the ent. + ID uuid.UUID `json:"id,omitempty"` + // Name holds the value of the "name" field. + Name string `json:"name,omitempty"` + // Slug holds the value of the "slug" field. + Slug string `json:"slug,omitempty"` + // DisplayName holds the value of the "display_name" field. + DisplayName string `json:"display_name,omitempty"` + // Description holds the value of the "description" field. + Description string `json:"description,omitempty"` + // Harness holds the value of the "harness" field. + Harness string `json:"harness,omitempty"` + // Config holds the value of the "config" field. + Config string `json:"config,omitempty"` + // ContentHash holds the value of the "content_hash" field. + ContentHash string `json:"content_hash,omitempty"` + // Scope holds the value of the "scope" field. + Scope string `json:"scope,omitempty"` + // ScopeID holds the value of the "scope_id" field. + ScopeID string `json:"scope_id,omitempty"` + // StorageURI holds the value of the "storage_uri" field. + StorageURI string `json:"storage_uri,omitempty"` + // StorageBucket holds the value of the "storage_bucket" field. + StorageBucket string `json:"storage_bucket,omitempty"` + // StoragePath holds the value of the "storage_path" field. + StoragePath string `json:"storage_path,omitempty"` + // Files holds the value of the "files" field. + Files string `json:"files,omitempty"` + // Locked holds the value of the "locked" field. + Locked bool `json:"locked,omitempty"` + // Status holds the value of the "status" field. + Status harnessconfig.Status `json:"status,omitempty"` + // OwnerID holds the value of the "owner_id" field. + OwnerID string `json:"owner_id,omitempty"` + // CreatedBy holds the value of the "created_by" field. + CreatedBy string `json:"created_by,omitempty"` + // UpdatedBy holds the value of the "updated_by" field. + UpdatedBy string `json:"updated_by,omitempty"` + // Visibility holds the value of the "visibility" field. + Visibility string `json:"visibility,omitempty"` + // Created holds the value of the "created" field. + Created time.Time `json:"created,omitempty"` + // Updated holds the value of the "updated" field. + Updated time.Time `json:"updated,omitempty"` + selectValues sql.SelectValues +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*HarnessConfig) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case harnessconfig.FieldLocked: + values[i] = new(sql.NullBool) + case harnessconfig.FieldName, harnessconfig.FieldSlug, harnessconfig.FieldDisplayName, harnessconfig.FieldDescription, harnessconfig.FieldHarness, harnessconfig.FieldConfig, harnessconfig.FieldContentHash, harnessconfig.FieldScope, harnessconfig.FieldScopeID, harnessconfig.FieldStorageURI, harnessconfig.FieldStorageBucket, harnessconfig.FieldStoragePath, harnessconfig.FieldFiles, harnessconfig.FieldStatus, harnessconfig.FieldOwnerID, harnessconfig.FieldCreatedBy, harnessconfig.FieldUpdatedBy, harnessconfig.FieldVisibility: + values[i] = new(sql.NullString) + case harnessconfig.FieldCreated, harnessconfig.FieldUpdated: + values[i] = new(sql.NullTime) + case harnessconfig.FieldID: + values[i] = new(uuid.UUID) + default: + values[i] = new(sql.UnknownType) + } + } + return values, nil +} + +// assignValues assigns the values that were returned from sql.Rows (after scanning) +// to the HarnessConfig fields. +func (_m *HarnessConfig) assignValues(columns []string, values []any) error { + if m, n := len(values), len(columns); m < n { + return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) + } + for i := range columns { + switch columns[i] { + case harnessconfig.FieldID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field id", values[i]) + } else if value != nil { + _m.ID = *value + } + case harnessconfig.FieldName: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field name", values[i]) + } else if value.Valid { + _m.Name = value.String + } + case harnessconfig.FieldSlug: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field slug", values[i]) + } else if value.Valid { + _m.Slug = value.String + } + case harnessconfig.FieldDisplayName: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field display_name", values[i]) + } else if value.Valid { + _m.DisplayName = value.String + } + case harnessconfig.FieldDescription: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field description", values[i]) + } else if value.Valid { + _m.Description = value.String + } + case harnessconfig.FieldHarness: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field harness", values[i]) + } else if value.Valid { + _m.Harness = value.String + } + case harnessconfig.FieldConfig: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field config", values[i]) + } else if value.Valid { + _m.Config = value.String + } + case harnessconfig.FieldContentHash: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field content_hash", values[i]) + } else if value.Valid { + _m.ContentHash = value.String + } + case harnessconfig.FieldScope: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field scope", values[i]) + } else if value.Valid { + _m.Scope = value.String + } + case harnessconfig.FieldScopeID: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field scope_id", values[i]) + } else if value.Valid { + _m.ScopeID = value.String + } + case harnessconfig.FieldStorageURI: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field storage_uri", values[i]) + } else if value.Valid { + _m.StorageURI = value.String + } + case harnessconfig.FieldStorageBucket: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field storage_bucket", values[i]) + } else if value.Valid { + _m.StorageBucket = value.String + } + case harnessconfig.FieldStoragePath: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field storage_path", values[i]) + } else if value.Valid { + _m.StoragePath = value.String + } + case harnessconfig.FieldFiles: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field files", values[i]) + } else if value.Valid { + _m.Files = value.String + } + case harnessconfig.FieldLocked: + if value, ok := values[i].(*sql.NullBool); !ok { + return fmt.Errorf("unexpected type %T for field locked", values[i]) + } else if value.Valid { + _m.Locked = value.Bool + } + case harnessconfig.FieldStatus: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field status", values[i]) + } else if value.Valid { + _m.Status = harnessconfig.Status(value.String) + } + case harnessconfig.FieldOwnerID: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field owner_id", values[i]) + } else if value.Valid { + _m.OwnerID = value.String + } + case harnessconfig.FieldCreatedBy: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field created_by", values[i]) + } else if value.Valid { + _m.CreatedBy = value.String + } + case harnessconfig.FieldUpdatedBy: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field updated_by", values[i]) + } else if value.Valid { + _m.UpdatedBy = value.String + } + case harnessconfig.FieldVisibility: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field visibility", values[i]) + } else if value.Valid { + _m.Visibility = value.String + } + case harnessconfig.FieldCreated: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field created", values[i]) + } else if value.Valid { + _m.Created = value.Time + } + case harnessconfig.FieldUpdated: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field updated", values[i]) + } else if value.Valid { + _m.Updated = value.Time + } + default: + _m.selectValues.Set(columns[i], values[i]) + } + } + return nil +} + +// Value returns the ent.Value that was dynamically selected and assigned to the HarnessConfig. +// This includes values selected through modifiers, order, etc. +func (_m *HarnessConfig) Value(name string) (ent.Value, error) { + return _m.selectValues.Get(name) +} + +// Update returns a builder for updating this HarnessConfig. +// Note that you need to call HarnessConfig.Unwrap() before calling this method if this HarnessConfig +// was returned from a transaction, and the transaction was committed or rolled back. +func (_m *HarnessConfig) Update() *HarnessConfigUpdateOne { + return NewHarnessConfigClient(_m.config).UpdateOne(_m) +} + +// Unwrap unwraps the HarnessConfig entity that was returned from a transaction after it was closed, +// so that all future queries will be executed through the driver which created the transaction. +func (_m *HarnessConfig) Unwrap() *HarnessConfig { + _tx, ok := _m.config.driver.(*txDriver) + if !ok { + panic("ent: HarnessConfig is not a transactional entity") + } + _m.config.driver = _tx.drv + return _m +} + +// String implements the fmt.Stringer. +func (_m *HarnessConfig) String() string { + var builder strings.Builder + builder.WriteString("HarnessConfig(") + builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID)) + builder.WriteString("name=") + builder.WriteString(_m.Name) + builder.WriteString(", ") + builder.WriteString("slug=") + builder.WriteString(_m.Slug) + builder.WriteString(", ") + builder.WriteString("display_name=") + builder.WriteString(_m.DisplayName) + builder.WriteString(", ") + builder.WriteString("description=") + builder.WriteString(_m.Description) + builder.WriteString(", ") + builder.WriteString("harness=") + builder.WriteString(_m.Harness) + builder.WriteString(", ") + builder.WriteString("config=") + builder.WriteString(_m.Config) + builder.WriteString(", ") + builder.WriteString("content_hash=") + builder.WriteString(_m.ContentHash) + builder.WriteString(", ") + builder.WriteString("scope=") + builder.WriteString(_m.Scope) + builder.WriteString(", ") + builder.WriteString("scope_id=") + builder.WriteString(_m.ScopeID) + builder.WriteString(", ") + builder.WriteString("storage_uri=") + builder.WriteString(_m.StorageURI) + builder.WriteString(", ") + builder.WriteString("storage_bucket=") + builder.WriteString(_m.StorageBucket) + builder.WriteString(", ") + builder.WriteString("storage_path=") + builder.WriteString(_m.StoragePath) + builder.WriteString(", ") + builder.WriteString("files=") + builder.WriteString(_m.Files) + builder.WriteString(", ") + builder.WriteString("locked=") + builder.WriteString(fmt.Sprintf("%v", _m.Locked)) + builder.WriteString(", ") + builder.WriteString("status=") + builder.WriteString(fmt.Sprintf("%v", _m.Status)) + builder.WriteString(", ") + builder.WriteString("owner_id=") + builder.WriteString(_m.OwnerID) + builder.WriteString(", ") + builder.WriteString("created_by=") + builder.WriteString(_m.CreatedBy) + builder.WriteString(", ") + builder.WriteString("updated_by=") + builder.WriteString(_m.UpdatedBy) + builder.WriteString(", ") + builder.WriteString("visibility=") + builder.WriteString(_m.Visibility) + builder.WriteString(", ") + builder.WriteString("created=") + builder.WriteString(_m.Created.Format(time.ANSIC)) + builder.WriteString(", ") + builder.WriteString("updated=") + builder.WriteString(_m.Updated.Format(time.ANSIC)) + builder.WriteByte(')') + return builder.String() +} + +// HarnessConfigs is a parsable slice of HarnessConfig. +type HarnessConfigs []*HarnessConfig diff --git a/pkg/ent/harnessconfig/harnessconfig.go b/pkg/ent/harnessconfig/harnessconfig.go new file mode 100644 index 000000000..9205933d7 --- /dev/null +++ b/pkg/ent/harnessconfig/harnessconfig.go @@ -0,0 +1,261 @@ +// Code generated by ent, DO NOT EDIT. + +package harnessconfig + +import ( + "fmt" + "time" + + "entgo.io/ent/dialect/sql" + "github.com/google/uuid" +) + +const ( + // Label holds the string label denoting the harnessconfig type in the database. + Label = "harness_config" + // FieldID holds the string denoting the id field in the database. + FieldID = "id" + // FieldName holds the string denoting the name field in the database. + FieldName = "name" + // FieldSlug holds the string denoting the slug field in the database. + FieldSlug = "slug" + // FieldDisplayName holds the string denoting the display_name field in the database. + FieldDisplayName = "display_name" + // FieldDescription holds the string denoting the description field in the database. + FieldDescription = "description" + // FieldHarness holds the string denoting the harness field in the database. + FieldHarness = "harness" + // FieldConfig holds the string denoting the config field in the database. + FieldConfig = "config" + // FieldContentHash holds the string denoting the content_hash field in the database. + FieldContentHash = "content_hash" + // FieldScope holds the string denoting the scope field in the database. + FieldScope = "scope" + // FieldScopeID holds the string denoting the scope_id field in the database. + FieldScopeID = "scope_id" + // FieldStorageURI holds the string denoting the storage_uri field in the database. + FieldStorageURI = "storage_uri" + // FieldStorageBucket holds the string denoting the storage_bucket field in the database. + FieldStorageBucket = "storage_bucket" + // FieldStoragePath holds the string denoting the storage_path field in the database. + FieldStoragePath = "storage_path" + // FieldFiles holds the string denoting the files field in the database. + FieldFiles = "files" + // FieldLocked holds the string denoting the locked field in the database. + FieldLocked = "locked" + // FieldStatus holds the string denoting the status field in the database. + FieldStatus = "status" + // FieldOwnerID holds the string denoting the owner_id field in the database. + FieldOwnerID = "owner_id" + // FieldCreatedBy holds the string denoting the created_by field in the database. + FieldCreatedBy = "created_by" + // FieldUpdatedBy holds the string denoting the updated_by field in the database. + FieldUpdatedBy = "updated_by" + // FieldVisibility holds the string denoting the visibility field in the database. + FieldVisibility = "visibility" + // FieldCreated holds the string denoting the created field in the database. + FieldCreated = "created" + // FieldUpdated holds the string denoting the updated field in the database. + FieldUpdated = "updated" + // Table holds the table name of the harnessconfig in the database. + Table = "harness_configs" +) + +// Columns holds all SQL columns for harnessconfig fields. +var Columns = []string{ + FieldID, + FieldName, + FieldSlug, + FieldDisplayName, + FieldDescription, + FieldHarness, + FieldConfig, + FieldContentHash, + FieldScope, + FieldScopeID, + FieldStorageURI, + FieldStorageBucket, + FieldStoragePath, + FieldFiles, + FieldLocked, + FieldStatus, + FieldOwnerID, + FieldCreatedBy, + FieldUpdatedBy, + FieldVisibility, + FieldCreated, + FieldUpdated, +} + +// ValidColumn reports if the column name is valid (part of the table columns). +func ValidColumn(column string) bool { + for i := range Columns { + if column == Columns[i] { + return true + } + } + return false +} + +var ( + // NameValidator is a validator for the "name" field. It is called by the builders before save. + NameValidator func(string) error + // SlugValidator is a validator for the "slug" field. It is called by the builders before save. + SlugValidator func(string) error + // HarnessValidator is a validator for the "harness" field. It is called by the builders before save. + HarnessValidator func(string) error + // DefaultScope holds the default value on creation for the "scope" field. + DefaultScope string + // DefaultLocked holds the default value on creation for the "locked" field. + DefaultLocked bool + // DefaultVisibility holds the default value on creation for the "visibility" field. + DefaultVisibility string + // DefaultCreated holds the default value on creation for the "created" field. + DefaultCreated func() time.Time + // DefaultUpdated holds the default value on creation for the "updated" field. + DefaultUpdated func() time.Time + // UpdateDefaultUpdated holds the default value on update for the "updated" field. + UpdateDefaultUpdated func() time.Time + // DefaultID holds the default value on creation for the "id" field. + DefaultID func() uuid.UUID +) + +// Status defines the type for the "status" enum field. +type Status string + +// StatusActive is the default value of the Status enum. +const DefaultStatus = StatusActive + +// Status values. +const ( + StatusPending Status = "pending" + StatusActive Status = "active" + StatusArchived Status = "archived" +) + +func (s Status) String() string { + return string(s) +} + +// StatusValidator is a validator for the "status" field enum values. It is called by the builders before save. +func StatusValidator(s Status) error { + switch s { + case StatusPending, StatusActive, StatusArchived: + return nil + default: + return fmt.Errorf("harnessconfig: invalid enum value for status field: %q", s) + } +} + +// OrderOption defines the ordering options for the HarnessConfig queries. +type OrderOption func(*sql.Selector) + +// ByID orders the results by the id field. +func ByID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldID, opts...).ToFunc() +} + +// ByName orders the results by the name field. +func ByName(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldName, opts...).ToFunc() +} + +// BySlug orders the results by the slug field. +func BySlug(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldSlug, opts...).ToFunc() +} + +// ByDisplayName orders the results by the display_name field. +func ByDisplayName(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldDisplayName, opts...).ToFunc() +} + +// ByDescription orders the results by the description field. +func ByDescription(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldDescription, opts...).ToFunc() +} + +// ByHarness orders the results by the harness field. +func ByHarness(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldHarness, opts...).ToFunc() +} + +// ByConfig orders the results by the config field. +func ByConfig(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldConfig, opts...).ToFunc() +} + +// ByContentHash orders the results by the content_hash field. +func ByContentHash(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldContentHash, opts...).ToFunc() +} + +// ByScope orders the results by the scope field. +func ByScope(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldScope, opts...).ToFunc() +} + +// ByScopeID orders the results by the scope_id field. +func ByScopeID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldScopeID, opts...).ToFunc() +} + +// ByStorageURI orders the results by the storage_uri field. +func ByStorageURI(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldStorageURI, opts...).ToFunc() +} + +// ByStorageBucket orders the results by the storage_bucket field. +func ByStorageBucket(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldStorageBucket, opts...).ToFunc() +} + +// ByStoragePath orders the results by the storage_path field. +func ByStoragePath(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldStoragePath, opts...).ToFunc() +} + +// ByFiles orders the results by the files field. +func ByFiles(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldFiles, opts...).ToFunc() +} + +// ByLocked orders the results by the locked field. +func ByLocked(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldLocked, opts...).ToFunc() +} + +// ByStatus orders the results by the status field. +func ByStatus(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldStatus, opts...).ToFunc() +} + +// ByOwnerID orders the results by the owner_id field. +func ByOwnerID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldOwnerID, opts...).ToFunc() +} + +// ByCreatedBy orders the results by the created_by field. +func ByCreatedBy(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCreatedBy, opts...).ToFunc() +} + +// ByUpdatedBy orders the results by the updated_by field. +func ByUpdatedBy(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldUpdatedBy, opts...).ToFunc() +} + +// ByVisibility orders the results by the visibility field. +func ByVisibility(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldVisibility, opts...).ToFunc() +} + +// ByCreated orders the results by the created field. +func ByCreated(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCreated, opts...).ToFunc() +} + +// ByUpdated orders the results by the updated field. +func ByUpdated(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldUpdated, opts...).ToFunc() +} diff --git a/pkg/ent/harnessconfig/where.go b/pkg/ent/harnessconfig/where.go new file mode 100644 index 000000000..24974e074 --- /dev/null +++ b/pkg/ent/harnessconfig/where.go @@ -0,0 +1,1506 @@ +// Code generated by ent, DO NOT EDIT. + +package harnessconfig + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/google/uuid" +) + +// ID filters vertices based on their ID field. +func ID(id uuid.UUID) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldEQ(FieldID, id)) +} + +// IDEQ applies the EQ predicate on the ID field. +func IDEQ(id uuid.UUID) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldEQ(FieldID, id)) +} + +// IDNEQ applies the NEQ predicate on the ID field. +func IDNEQ(id uuid.UUID) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldNEQ(FieldID, id)) +} + +// IDIn applies the In predicate on the ID field. +func IDIn(ids ...uuid.UUID) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldIn(FieldID, ids...)) +} + +// IDNotIn applies the NotIn predicate on the ID field. +func IDNotIn(ids ...uuid.UUID) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldNotIn(FieldID, ids...)) +} + +// IDGT applies the GT predicate on the ID field. +func IDGT(id uuid.UUID) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldGT(FieldID, id)) +} + +// IDGTE applies the GTE predicate on the ID field. +func IDGTE(id uuid.UUID) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldGTE(FieldID, id)) +} + +// IDLT applies the LT predicate on the ID field. +func IDLT(id uuid.UUID) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldLT(FieldID, id)) +} + +// IDLTE applies the LTE predicate on the ID field. +func IDLTE(id uuid.UUID) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldLTE(FieldID, id)) +} + +// Name applies equality check predicate on the "name" field. It's identical to NameEQ. +func Name(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldEQ(FieldName, v)) +} + +// Slug applies equality check predicate on the "slug" field. It's identical to SlugEQ. +func Slug(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldEQ(FieldSlug, v)) +} + +// DisplayName applies equality check predicate on the "display_name" field. It's identical to DisplayNameEQ. +func DisplayName(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldEQ(FieldDisplayName, v)) +} + +// Description applies equality check predicate on the "description" field. It's identical to DescriptionEQ. +func Description(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldEQ(FieldDescription, v)) +} + +// Harness applies equality check predicate on the "harness" field. It's identical to HarnessEQ. +func Harness(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldEQ(FieldHarness, v)) +} + +// Config applies equality check predicate on the "config" field. It's identical to ConfigEQ. +func Config(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldEQ(FieldConfig, v)) +} + +// ContentHash applies equality check predicate on the "content_hash" field. It's identical to ContentHashEQ. +func ContentHash(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldEQ(FieldContentHash, v)) +} + +// Scope applies equality check predicate on the "scope" field. It's identical to ScopeEQ. +func Scope(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldEQ(FieldScope, v)) +} + +// ScopeID applies equality check predicate on the "scope_id" field. It's identical to ScopeIDEQ. +func ScopeID(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldEQ(FieldScopeID, v)) +} + +// StorageURI applies equality check predicate on the "storage_uri" field. It's identical to StorageURIEQ. +func StorageURI(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldEQ(FieldStorageURI, v)) +} + +// StorageBucket applies equality check predicate on the "storage_bucket" field. It's identical to StorageBucketEQ. +func StorageBucket(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldEQ(FieldStorageBucket, v)) +} + +// StoragePath applies equality check predicate on the "storage_path" field. It's identical to StoragePathEQ. +func StoragePath(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldEQ(FieldStoragePath, v)) +} + +// Files applies equality check predicate on the "files" field. It's identical to FilesEQ. +func Files(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldEQ(FieldFiles, v)) +} + +// Locked applies equality check predicate on the "locked" field. It's identical to LockedEQ. +func Locked(v bool) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldEQ(FieldLocked, v)) +} + +// OwnerID applies equality check predicate on the "owner_id" field. It's identical to OwnerIDEQ. +func OwnerID(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldEQ(FieldOwnerID, v)) +} + +// CreatedBy applies equality check predicate on the "created_by" field. It's identical to CreatedByEQ. +func CreatedBy(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldEQ(FieldCreatedBy, v)) +} + +// UpdatedBy applies equality check predicate on the "updated_by" field. It's identical to UpdatedByEQ. +func UpdatedBy(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldEQ(FieldUpdatedBy, v)) +} + +// Visibility applies equality check predicate on the "visibility" field. It's identical to VisibilityEQ. +func Visibility(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldEQ(FieldVisibility, v)) +} + +// Created applies equality check predicate on the "created" field. It's identical to CreatedEQ. +func Created(v time.Time) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldEQ(FieldCreated, v)) +} + +// Updated applies equality check predicate on the "updated" field. It's identical to UpdatedEQ. +func Updated(v time.Time) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldEQ(FieldUpdated, v)) +} + +// NameEQ applies the EQ predicate on the "name" field. +func NameEQ(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldEQ(FieldName, v)) +} + +// NameNEQ applies the NEQ predicate on the "name" field. +func NameNEQ(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldNEQ(FieldName, v)) +} + +// NameIn applies the In predicate on the "name" field. +func NameIn(vs ...string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldIn(FieldName, vs...)) +} + +// NameNotIn applies the NotIn predicate on the "name" field. +func NameNotIn(vs ...string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldNotIn(FieldName, vs...)) +} + +// NameGT applies the GT predicate on the "name" field. +func NameGT(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldGT(FieldName, v)) +} + +// NameGTE applies the GTE predicate on the "name" field. +func NameGTE(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldGTE(FieldName, v)) +} + +// NameLT applies the LT predicate on the "name" field. +func NameLT(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldLT(FieldName, v)) +} + +// NameLTE applies the LTE predicate on the "name" field. +func NameLTE(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldLTE(FieldName, v)) +} + +// NameContains applies the Contains predicate on the "name" field. +func NameContains(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldContains(FieldName, v)) +} + +// NameHasPrefix applies the HasPrefix predicate on the "name" field. +func NameHasPrefix(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldHasPrefix(FieldName, v)) +} + +// NameHasSuffix applies the HasSuffix predicate on the "name" field. +func NameHasSuffix(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldHasSuffix(FieldName, v)) +} + +// NameEqualFold applies the EqualFold predicate on the "name" field. +func NameEqualFold(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldEqualFold(FieldName, v)) +} + +// NameContainsFold applies the ContainsFold predicate on the "name" field. +func NameContainsFold(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldContainsFold(FieldName, v)) +} + +// SlugEQ applies the EQ predicate on the "slug" field. +func SlugEQ(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldEQ(FieldSlug, v)) +} + +// SlugNEQ applies the NEQ predicate on the "slug" field. +func SlugNEQ(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldNEQ(FieldSlug, v)) +} + +// SlugIn applies the In predicate on the "slug" field. +func SlugIn(vs ...string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldIn(FieldSlug, vs...)) +} + +// SlugNotIn applies the NotIn predicate on the "slug" field. +func SlugNotIn(vs ...string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldNotIn(FieldSlug, vs...)) +} + +// SlugGT applies the GT predicate on the "slug" field. +func SlugGT(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldGT(FieldSlug, v)) +} + +// SlugGTE applies the GTE predicate on the "slug" field. +func SlugGTE(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldGTE(FieldSlug, v)) +} + +// SlugLT applies the LT predicate on the "slug" field. +func SlugLT(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldLT(FieldSlug, v)) +} + +// SlugLTE applies the LTE predicate on the "slug" field. +func SlugLTE(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldLTE(FieldSlug, v)) +} + +// SlugContains applies the Contains predicate on the "slug" field. +func SlugContains(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldContains(FieldSlug, v)) +} + +// SlugHasPrefix applies the HasPrefix predicate on the "slug" field. +func SlugHasPrefix(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldHasPrefix(FieldSlug, v)) +} + +// SlugHasSuffix applies the HasSuffix predicate on the "slug" field. +func SlugHasSuffix(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldHasSuffix(FieldSlug, v)) +} + +// SlugEqualFold applies the EqualFold predicate on the "slug" field. +func SlugEqualFold(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldEqualFold(FieldSlug, v)) +} + +// SlugContainsFold applies the ContainsFold predicate on the "slug" field. +func SlugContainsFold(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldContainsFold(FieldSlug, v)) +} + +// DisplayNameEQ applies the EQ predicate on the "display_name" field. +func DisplayNameEQ(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldEQ(FieldDisplayName, v)) +} + +// DisplayNameNEQ applies the NEQ predicate on the "display_name" field. +func DisplayNameNEQ(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldNEQ(FieldDisplayName, v)) +} + +// DisplayNameIn applies the In predicate on the "display_name" field. +func DisplayNameIn(vs ...string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldIn(FieldDisplayName, vs...)) +} + +// DisplayNameNotIn applies the NotIn predicate on the "display_name" field. +func DisplayNameNotIn(vs ...string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldNotIn(FieldDisplayName, vs...)) +} + +// DisplayNameGT applies the GT predicate on the "display_name" field. +func DisplayNameGT(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldGT(FieldDisplayName, v)) +} + +// DisplayNameGTE applies the GTE predicate on the "display_name" field. +func DisplayNameGTE(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldGTE(FieldDisplayName, v)) +} + +// DisplayNameLT applies the LT predicate on the "display_name" field. +func DisplayNameLT(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldLT(FieldDisplayName, v)) +} + +// DisplayNameLTE applies the LTE predicate on the "display_name" field. +func DisplayNameLTE(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldLTE(FieldDisplayName, v)) +} + +// DisplayNameContains applies the Contains predicate on the "display_name" field. +func DisplayNameContains(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldContains(FieldDisplayName, v)) +} + +// DisplayNameHasPrefix applies the HasPrefix predicate on the "display_name" field. +func DisplayNameHasPrefix(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldHasPrefix(FieldDisplayName, v)) +} + +// DisplayNameHasSuffix applies the HasSuffix predicate on the "display_name" field. +func DisplayNameHasSuffix(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldHasSuffix(FieldDisplayName, v)) +} + +// DisplayNameIsNil applies the IsNil predicate on the "display_name" field. +func DisplayNameIsNil() predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldIsNull(FieldDisplayName)) +} + +// DisplayNameNotNil applies the NotNil predicate on the "display_name" field. +func DisplayNameNotNil() predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldNotNull(FieldDisplayName)) +} + +// DisplayNameEqualFold applies the EqualFold predicate on the "display_name" field. +func DisplayNameEqualFold(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldEqualFold(FieldDisplayName, v)) +} + +// DisplayNameContainsFold applies the ContainsFold predicate on the "display_name" field. +func DisplayNameContainsFold(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldContainsFold(FieldDisplayName, v)) +} + +// DescriptionEQ applies the EQ predicate on the "description" field. +func DescriptionEQ(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldEQ(FieldDescription, v)) +} + +// DescriptionNEQ applies the NEQ predicate on the "description" field. +func DescriptionNEQ(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldNEQ(FieldDescription, v)) +} + +// DescriptionIn applies the In predicate on the "description" field. +func DescriptionIn(vs ...string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldIn(FieldDescription, vs...)) +} + +// DescriptionNotIn applies the NotIn predicate on the "description" field. +func DescriptionNotIn(vs ...string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldNotIn(FieldDescription, vs...)) +} + +// DescriptionGT applies the GT predicate on the "description" field. +func DescriptionGT(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldGT(FieldDescription, v)) +} + +// DescriptionGTE applies the GTE predicate on the "description" field. +func DescriptionGTE(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldGTE(FieldDescription, v)) +} + +// DescriptionLT applies the LT predicate on the "description" field. +func DescriptionLT(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldLT(FieldDescription, v)) +} + +// DescriptionLTE applies the LTE predicate on the "description" field. +func DescriptionLTE(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldLTE(FieldDescription, v)) +} + +// DescriptionContains applies the Contains predicate on the "description" field. +func DescriptionContains(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldContains(FieldDescription, v)) +} + +// DescriptionHasPrefix applies the HasPrefix predicate on the "description" field. +func DescriptionHasPrefix(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldHasPrefix(FieldDescription, v)) +} + +// DescriptionHasSuffix applies the HasSuffix predicate on the "description" field. +func DescriptionHasSuffix(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldHasSuffix(FieldDescription, v)) +} + +// DescriptionIsNil applies the IsNil predicate on the "description" field. +func DescriptionIsNil() predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldIsNull(FieldDescription)) +} + +// DescriptionNotNil applies the NotNil predicate on the "description" field. +func DescriptionNotNil() predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldNotNull(FieldDescription)) +} + +// DescriptionEqualFold applies the EqualFold predicate on the "description" field. +func DescriptionEqualFold(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldEqualFold(FieldDescription, v)) +} + +// DescriptionContainsFold applies the ContainsFold predicate on the "description" field. +func DescriptionContainsFold(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldContainsFold(FieldDescription, v)) +} + +// HarnessEQ applies the EQ predicate on the "harness" field. +func HarnessEQ(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldEQ(FieldHarness, v)) +} + +// HarnessNEQ applies the NEQ predicate on the "harness" field. +func HarnessNEQ(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldNEQ(FieldHarness, v)) +} + +// HarnessIn applies the In predicate on the "harness" field. +func HarnessIn(vs ...string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldIn(FieldHarness, vs...)) +} + +// HarnessNotIn applies the NotIn predicate on the "harness" field. +func HarnessNotIn(vs ...string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldNotIn(FieldHarness, vs...)) +} + +// HarnessGT applies the GT predicate on the "harness" field. +func HarnessGT(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldGT(FieldHarness, v)) +} + +// HarnessGTE applies the GTE predicate on the "harness" field. +func HarnessGTE(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldGTE(FieldHarness, v)) +} + +// HarnessLT applies the LT predicate on the "harness" field. +func HarnessLT(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldLT(FieldHarness, v)) +} + +// HarnessLTE applies the LTE predicate on the "harness" field. +func HarnessLTE(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldLTE(FieldHarness, v)) +} + +// HarnessContains applies the Contains predicate on the "harness" field. +func HarnessContains(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldContains(FieldHarness, v)) +} + +// HarnessHasPrefix applies the HasPrefix predicate on the "harness" field. +func HarnessHasPrefix(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldHasPrefix(FieldHarness, v)) +} + +// HarnessHasSuffix applies the HasSuffix predicate on the "harness" field. +func HarnessHasSuffix(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldHasSuffix(FieldHarness, v)) +} + +// HarnessEqualFold applies the EqualFold predicate on the "harness" field. +func HarnessEqualFold(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldEqualFold(FieldHarness, v)) +} + +// HarnessContainsFold applies the ContainsFold predicate on the "harness" field. +func HarnessContainsFold(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldContainsFold(FieldHarness, v)) +} + +// ConfigEQ applies the EQ predicate on the "config" field. +func ConfigEQ(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldEQ(FieldConfig, v)) +} + +// ConfigNEQ applies the NEQ predicate on the "config" field. +func ConfigNEQ(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldNEQ(FieldConfig, v)) +} + +// ConfigIn applies the In predicate on the "config" field. +func ConfigIn(vs ...string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldIn(FieldConfig, vs...)) +} + +// ConfigNotIn applies the NotIn predicate on the "config" field. +func ConfigNotIn(vs ...string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldNotIn(FieldConfig, vs...)) +} + +// ConfigGT applies the GT predicate on the "config" field. +func ConfigGT(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldGT(FieldConfig, v)) +} + +// ConfigGTE applies the GTE predicate on the "config" field. +func ConfigGTE(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldGTE(FieldConfig, v)) +} + +// ConfigLT applies the LT predicate on the "config" field. +func ConfigLT(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldLT(FieldConfig, v)) +} + +// ConfigLTE applies the LTE predicate on the "config" field. +func ConfigLTE(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldLTE(FieldConfig, v)) +} + +// ConfigContains applies the Contains predicate on the "config" field. +func ConfigContains(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldContains(FieldConfig, v)) +} + +// ConfigHasPrefix applies the HasPrefix predicate on the "config" field. +func ConfigHasPrefix(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldHasPrefix(FieldConfig, v)) +} + +// ConfigHasSuffix applies the HasSuffix predicate on the "config" field. +func ConfigHasSuffix(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldHasSuffix(FieldConfig, v)) +} + +// ConfigIsNil applies the IsNil predicate on the "config" field. +func ConfigIsNil() predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldIsNull(FieldConfig)) +} + +// ConfigNotNil applies the NotNil predicate on the "config" field. +func ConfigNotNil() predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldNotNull(FieldConfig)) +} + +// ConfigEqualFold applies the EqualFold predicate on the "config" field. +func ConfigEqualFold(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldEqualFold(FieldConfig, v)) +} + +// ConfigContainsFold applies the ContainsFold predicate on the "config" field. +func ConfigContainsFold(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldContainsFold(FieldConfig, v)) +} + +// ContentHashEQ applies the EQ predicate on the "content_hash" field. +func ContentHashEQ(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldEQ(FieldContentHash, v)) +} + +// ContentHashNEQ applies the NEQ predicate on the "content_hash" field. +func ContentHashNEQ(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldNEQ(FieldContentHash, v)) +} + +// ContentHashIn applies the In predicate on the "content_hash" field. +func ContentHashIn(vs ...string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldIn(FieldContentHash, vs...)) +} + +// ContentHashNotIn applies the NotIn predicate on the "content_hash" field. +func ContentHashNotIn(vs ...string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldNotIn(FieldContentHash, vs...)) +} + +// ContentHashGT applies the GT predicate on the "content_hash" field. +func ContentHashGT(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldGT(FieldContentHash, v)) +} + +// ContentHashGTE applies the GTE predicate on the "content_hash" field. +func ContentHashGTE(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldGTE(FieldContentHash, v)) +} + +// ContentHashLT applies the LT predicate on the "content_hash" field. +func ContentHashLT(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldLT(FieldContentHash, v)) +} + +// ContentHashLTE applies the LTE predicate on the "content_hash" field. +func ContentHashLTE(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldLTE(FieldContentHash, v)) +} + +// ContentHashContains applies the Contains predicate on the "content_hash" field. +func ContentHashContains(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldContains(FieldContentHash, v)) +} + +// ContentHashHasPrefix applies the HasPrefix predicate on the "content_hash" field. +func ContentHashHasPrefix(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldHasPrefix(FieldContentHash, v)) +} + +// ContentHashHasSuffix applies the HasSuffix predicate on the "content_hash" field. +func ContentHashHasSuffix(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldHasSuffix(FieldContentHash, v)) +} + +// ContentHashIsNil applies the IsNil predicate on the "content_hash" field. +func ContentHashIsNil() predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldIsNull(FieldContentHash)) +} + +// ContentHashNotNil applies the NotNil predicate on the "content_hash" field. +func ContentHashNotNil() predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldNotNull(FieldContentHash)) +} + +// ContentHashEqualFold applies the EqualFold predicate on the "content_hash" field. +func ContentHashEqualFold(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldEqualFold(FieldContentHash, v)) +} + +// ContentHashContainsFold applies the ContainsFold predicate on the "content_hash" field. +func ContentHashContainsFold(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldContainsFold(FieldContentHash, v)) +} + +// ScopeEQ applies the EQ predicate on the "scope" field. +func ScopeEQ(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldEQ(FieldScope, v)) +} + +// ScopeNEQ applies the NEQ predicate on the "scope" field. +func ScopeNEQ(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldNEQ(FieldScope, v)) +} + +// ScopeIn applies the In predicate on the "scope" field. +func ScopeIn(vs ...string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldIn(FieldScope, vs...)) +} + +// ScopeNotIn applies the NotIn predicate on the "scope" field. +func ScopeNotIn(vs ...string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldNotIn(FieldScope, vs...)) +} + +// ScopeGT applies the GT predicate on the "scope" field. +func ScopeGT(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldGT(FieldScope, v)) +} + +// ScopeGTE applies the GTE predicate on the "scope" field. +func ScopeGTE(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldGTE(FieldScope, v)) +} + +// ScopeLT applies the LT predicate on the "scope" field. +func ScopeLT(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldLT(FieldScope, v)) +} + +// ScopeLTE applies the LTE predicate on the "scope" field. +func ScopeLTE(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldLTE(FieldScope, v)) +} + +// ScopeContains applies the Contains predicate on the "scope" field. +func ScopeContains(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldContains(FieldScope, v)) +} + +// ScopeHasPrefix applies the HasPrefix predicate on the "scope" field. +func ScopeHasPrefix(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldHasPrefix(FieldScope, v)) +} + +// ScopeHasSuffix applies the HasSuffix predicate on the "scope" field. +func ScopeHasSuffix(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldHasSuffix(FieldScope, v)) +} + +// ScopeEqualFold applies the EqualFold predicate on the "scope" field. +func ScopeEqualFold(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldEqualFold(FieldScope, v)) +} + +// ScopeContainsFold applies the ContainsFold predicate on the "scope" field. +func ScopeContainsFold(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldContainsFold(FieldScope, v)) +} + +// ScopeIDEQ applies the EQ predicate on the "scope_id" field. +func ScopeIDEQ(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldEQ(FieldScopeID, v)) +} + +// ScopeIDNEQ applies the NEQ predicate on the "scope_id" field. +func ScopeIDNEQ(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldNEQ(FieldScopeID, v)) +} + +// ScopeIDIn applies the In predicate on the "scope_id" field. +func ScopeIDIn(vs ...string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldIn(FieldScopeID, vs...)) +} + +// ScopeIDNotIn applies the NotIn predicate on the "scope_id" field. +func ScopeIDNotIn(vs ...string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldNotIn(FieldScopeID, vs...)) +} + +// ScopeIDGT applies the GT predicate on the "scope_id" field. +func ScopeIDGT(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldGT(FieldScopeID, v)) +} + +// ScopeIDGTE applies the GTE predicate on the "scope_id" field. +func ScopeIDGTE(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldGTE(FieldScopeID, v)) +} + +// ScopeIDLT applies the LT predicate on the "scope_id" field. +func ScopeIDLT(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldLT(FieldScopeID, v)) +} + +// ScopeIDLTE applies the LTE predicate on the "scope_id" field. +func ScopeIDLTE(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldLTE(FieldScopeID, v)) +} + +// ScopeIDContains applies the Contains predicate on the "scope_id" field. +func ScopeIDContains(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldContains(FieldScopeID, v)) +} + +// ScopeIDHasPrefix applies the HasPrefix predicate on the "scope_id" field. +func ScopeIDHasPrefix(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldHasPrefix(FieldScopeID, v)) +} + +// ScopeIDHasSuffix applies the HasSuffix predicate on the "scope_id" field. +func ScopeIDHasSuffix(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldHasSuffix(FieldScopeID, v)) +} + +// ScopeIDIsNil applies the IsNil predicate on the "scope_id" field. +func ScopeIDIsNil() predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldIsNull(FieldScopeID)) +} + +// ScopeIDNotNil applies the NotNil predicate on the "scope_id" field. +func ScopeIDNotNil() predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldNotNull(FieldScopeID)) +} + +// ScopeIDEqualFold applies the EqualFold predicate on the "scope_id" field. +func ScopeIDEqualFold(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldEqualFold(FieldScopeID, v)) +} + +// ScopeIDContainsFold applies the ContainsFold predicate on the "scope_id" field. +func ScopeIDContainsFold(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldContainsFold(FieldScopeID, v)) +} + +// StorageURIEQ applies the EQ predicate on the "storage_uri" field. +func StorageURIEQ(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldEQ(FieldStorageURI, v)) +} + +// StorageURINEQ applies the NEQ predicate on the "storage_uri" field. +func StorageURINEQ(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldNEQ(FieldStorageURI, v)) +} + +// StorageURIIn applies the In predicate on the "storage_uri" field. +func StorageURIIn(vs ...string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldIn(FieldStorageURI, vs...)) +} + +// StorageURINotIn applies the NotIn predicate on the "storage_uri" field. +func StorageURINotIn(vs ...string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldNotIn(FieldStorageURI, vs...)) +} + +// StorageURIGT applies the GT predicate on the "storage_uri" field. +func StorageURIGT(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldGT(FieldStorageURI, v)) +} + +// StorageURIGTE applies the GTE predicate on the "storage_uri" field. +func StorageURIGTE(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldGTE(FieldStorageURI, v)) +} + +// StorageURILT applies the LT predicate on the "storage_uri" field. +func StorageURILT(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldLT(FieldStorageURI, v)) +} + +// StorageURILTE applies the LTE predicate on the "storage_uri" field. +func StorageURILTE(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldLTE(FieldStorageURI, v)) +} + +// StorageURIContains applies the Contains predicate on the "storage_uri" field. +func StorageURIContains(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldContains(FieldStorageURI, v)) +} + +// StorageURIHasPrefix applies the HasPrefix predicate on the "storage_uri" field. +func StorageURIHasPrefix(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldHasPrefix(FieldStorageURI, v)) +} + +// StorageURIHasSuffix applies the HasSuffix predicate on the "storage_uri" field. +func StorageURIHasSuffix(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldHasSuffix(FieldStorageURI, v)) +} + +// StorageURIIsNil applies the IsNil predicate on the "storage_uri" field. +func StorageURIIsNil() predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldIsNull(FieldStorageURI)) +} + +// StorageURINotNil applies the NotNil predicate on the "storage_uri" field. +func StorageURINotNil() predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldNotNull(FieldStorageURI)) +} + +// StorageURIEqualFold applies the EqualFold predicate on the "storage_uri" field. +func StorageURIEqualFold(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldEqualFold(FieldStorageURI, v)) +} + +// StorageURIContainsFold applies the ContainsFold predicate on the "storage_uri" field. +func StorageURIContainsFold(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldContainsFold(FieldStorageURI, v)) +} + +// StorageBucketEQ applies the EQ predicate on the "storage_bucket" field. +func StorageBucketEQ(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldEQ(FieldStorageBucket, v)) +} + +// StorageBucketNEQ applies the NEQ predicate on the "storage_bucket" field. +func StorageBucketNEQ(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldNEQ(FieldStorageBucket, v)) +} + +// StorageBucketIn applies the In predicate on the "storage_bucket" field. +func StorageBucketIn(vs ...string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldIn(FieldStorageBucket, vs...)) +} + +// StorageBucketNotIn applies the NotIn predicate on the "storage_bucket" field. +func StorageBucketNotIn(vs ...string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldNotIn(FieldStorageBucket, vs...)) +} + +// StorageBucketGT applies the GT predicate on the "storage_bucket" field. +func StorageBucketGT(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldGT(FieldStorageBucket, v)) +} + +// StorageBucketGTE applies the GTE predicate on the "storage_bucket" field. +func StorageBucketGTE(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldGTE(FieldStorageBucket, v)) +} + +// StorageBucketLT applies the LT predicate on the "storage_bucket" field. +func StorageBucketLT(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldLT(FieldStorageBucket, v)) +} + +// StorageBucketLTE applies the LTE predicate on the "storage_bucket" field. +func StorageBucketLTE(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldLTE(FieldStorageBucket, v)) +} + +// StorageBucketContains applies the Contains predicate on the "storage_bucket" field. +func StorageBucketContains(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldContains(FieldStorageBucket, v)) +} + +// StorageBucketHasPrefix applies the HasPrefix predicate on the "storage_bucket" field. +func StorageBucketHasPrefix(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldHasPrefix(FieldStorageBucket, v)) +} + +// StorageBucketHasSuffix applies the HasSuffix predicate on the "storage_bucket" field. +func StorageBucketHasSuffix(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldHasSuffix(FieldStorageBucket, v)) +} + +// StorageBucketIsNil applies the IsNil predicate on the "storage_bucket" field. +func StorageBucketIsNil() predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldIsNull(FieldStorageBucket)) +} + +// StorageBucketNotNil applies the NotNil predicate on the "storage_bucket" field. +func StorageBucketNotNil() predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldNotNull(FieldStorageBucket)) +} + +// StorageBucketEqualFold applies the EqualFold predicate on the "storage_bucket" field. +func StorageBucketEqualFold(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldEqualFold(FieldStorageBucket, v)) +} + +// StorageBucketContainsFold applies the ContainsFold predicate on the "storage_bucket" field. +func StorageBucketContainsFold(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldContainsFold(FieldStorageBucket, v)) +} + +// StoragePathEQ applies the EQ predicate on the "storage_path" field. +func StoragePathEQ(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldEQ(FieldStoragePath, v)) +} + +// StoragePathNEQ applies the NEQ predicate on the "storage_path" field. +func StoragePathNEQ(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldNEQ(FieldStoragePath, v)) +} + +// StoragePathIn applies the In predicate on the "storage_path" field. +func StoragePathIn(vs ...string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldIn(FieldStoragePath, vs...)) +} + +// StoragePathNotIn applies the NotIn predicate on the "storage_path" field. +func StoragePathNotIn(vs ...string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldNotIn(FieldStoragePath, vs...)) +} + +// StoragePathGT applies the GT predicate on the "storage_path" field. +func StoragePathGT(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldGT(FieldStoragePath, v)) +} + +// StoragePathGTE applies the GTE predicate on the "storage_path" field. +func StoragePathGTE(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldGTE(FieldStoragePath, v)) +} + +// StoragePathLT applies the LT predicate on the "storage_path" field. +func StoragePathLT(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldLT(FieldStoragePath, v)) +} + +// StoragePathLTE applies the LTE predicate on the "storage_path" field. +func StoragePathLTE(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldLTE(FieldStoragePath, v)) +} + +// StoragePathContains applies the Contains predicate on the "storage_path" field. +func StoragePathContains(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldContains(FieldStoragePath, v)) +} + +// StoragePathHasPrefix applies the HasPrefix predicate on the "storage_path" field. +func StoragePathHasPrefix(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldHasPrefix(FieldStoragePath, v)) +} + +// StoragePathHasSuffix applies the HasSuffix predicate on the "storage_path" field. +func StoragePathHasSuffix(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldHasSuffix(FieldStoragePath, v)) +} + +// StoragePathIsNil applies the IsNil predicate on the "storage_path" field. +func StoragePathIsNil() predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldIsNull(FieldStoragePath)) +} + +// StoragePathNotNil applies the NotNil predicate on the "storage_path" field. +func StoragePathNotNil() predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldNotNull(FieldStoragePath)) +} + +// StoragePathEqualFold applies the EqualFold predicate on the "storage_path" field. +func StoragePathEqualFold(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldEqualFold(FieldStoragePath, v)) +} + +// StoragePathContainsFold applies the ContainsFold predicate on the "storage_path" field. +func StoragePathContainsFold(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldContainsFold(FieldStoragePath, v)) +} + +// FilesEQ applies the EQ predicate on the "files" field. +func FilesEQ(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldEQ(FieldFiles, v)) +} + +// FilesNEQ applies the NEQ predicate on the "files" field. +func FilesNEQ(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldNEQ(FieldFiles, v)) +} + +// FilesIn applies the In predicate on the "files" field. +func FilesIn(vs ...string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldIn(FieldFiles, vs...)) +} + +// FilesNotIn applies the NotIn predicate on the "files" field. +func FilesNotIn(vs ...string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldNotIn(FieldFiles, vs...)) +} + +// FilesGT applies the GT predicate on the "files" field. +func FilesGT(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldGT(FieldFiles, v)) +} + +// FilesGTE applies the GTE predicate on the "files" field. +func FilesGTE(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldGTE(FieldFiles, v)) +} + +// FilesLT applies the LT predicate on the "files" field. +func FilesLT(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldLT(FieldFiles, v)) +} + +// FilesLTE applies the LTE predicate on the "files" field. +func FilesLTE(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldLTE(FieldFiles, v)) +} + +// FilesContains applies the Contains predicate on the "files" field. +func FilesContains(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldContains(FieldFiles, v)) +} + +// FilesHasPrefix applies the HasPrefix predicate on the "files" field. +func FilesHasPrefix(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldHasPrefix(FieldFiles, v)) +} + +// FilesHasSuffix applies the HasSuffix predicate on the "files" field. +func FilesHasSuffix(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldHasSuffix(FieldFiles, v)) +} + +// FilesIsNil applies the IsNil predicate on the "files" field. +func FilesIsNil() predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldIsNull(FieldFiles)) +} + +// FilesNotNil applies the NotNil predicate on the "files" field. +func FilesNotNil() predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldNotNull(FieldFiles)) +} + +// FilesEqualFold applies the EqualFold predicate on the "files" field. +func FilesEqualFold(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldEqualFold(FieldFiles, v)) +} + +// FilesContainsFold applies the ContainsFold predicate on the "files" field. +func FilesContainsFold(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldContainsFold(FieldFiles, v)) +} + +// LockedEQ applies the EQ predicate on the "locked" field. +func LockedEQ(v bool) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldEQ(FieldLocked, v)) +} + +// LockedNEQ applies the NEQ predicate on the "locked" field. +func LockedNEQ(v bool) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldNEQ(FieldLocked, v)) +} + +// StatusEQ applies the EQ predicate on the "status" field. +func StatusEQ(v Status) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldEQ(FieldStatus, v)) +} + +// StatusNEQ applies the NEQ predicate on the "status" field. +func StatusNEQ(v Status) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldNEQ(FieldStatus, v)) +} + +// StatusIn applies the In predicate on the "status" field. +func StatusIn(vs ...Status) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldIn(FieldStatus, vs...)) +} + +// StatusNotIn applies the NotIn predicate on the "status" field. +func StatusNotIn(vs ...Status) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldNotIn(FieldStatus, vs...)) +} + +// OwnerIDEQ applies the EQ predicate on the "owner_id" field. +func OwnerIDEQ(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldEQ(FieldOwnerID, v)) +} + +// OwnerIDNEQ applies the NEQ predicate on the "owner_id" field. +func OwnerIDNEQ(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldNEQ(FieldOwnerID, v)) +} + +// OwnerIDIn applies the In predicate on the "owner_id" field. +func OwnerIDIn(vs ...string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldIn(FieldOwnerID, vs...)) +} + +// OwnerIDNotIn applies the NotIn predicate on the "owner_id" field. +func OwnerIDNotIn(vs ...string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldNotIn(FieldOwnerID, vs...)) +} + +// OwnerIDGT applies the GT predicate on the "owner_id" field. +func OwnerIDGT(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldGT(FieldOwnerID, v)) +} + +// OwnerIDGTE applies the GTE predicate on the "owner_id" field. +func OwnerIDGTE(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldGTE(FieldOwnerID, v)) +} + +// OwnerIDLT applies the LT predicate on the "owner_id" field. +func OwnerIDLT(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldLT(FieldOwnerID, v)) +} + +// OwnerIDLTE applies the LTE predicate on the "owner_id" field. +func OwnerIDLTE(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldLTE(FieldOwnerID, v)) +} + +// OwnerIDContains applies the Contains predicate on the "owner_id" field. +func OwnerIDContains(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldContains(FieldOwnerID, v)) +} + +// OwnerIDHasPrefix applies the HasPrefix predicate on the "owner_id" field. +func OwnerIDHasPrefix(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldHasPrefix(FieldOwnerID, v)) +} + +// OwnerIDHasSuffix applies the HasSuffix predicate on the "owner_id" field. +func OwnerIDHasSuffix(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldHasSuffix(FieldOwnerID, v)) +} + +// OwnerIDIsNil applies the IsNil predicate on the "owner_id" field. +func OwnerIDIsNil() predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldIsNull(FieldOwnerID)) +} + +// OwnerIDNotNil applies the NotNil predicate on the "owner_id" field. +func OwnerIDNotNil() predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldNotNull(FieldOwnerID)) +} + +// OwnerIDEqualFold applies the EqualFold predicate on the "owner_id" field. +func OwnerIDEqualFold(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldEqualFold(FieldOwnerID, v)) +} + +// OwnerIDContainsFold applies the ContainsFold predicate on the "owner_id" field. +func OwnerIDContainsFold(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldContainsFold(FieldOwnerID, v)) +} + +// CreatedByEQ applies the EQ predicate on the "created_by" field. +func CreatedByEQ(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldEQ(FieldCreatedBy, v)) +} + +// CreatedByNEQ applies the NEQ predicate on the "created_by" field. +func CreatedByNEQ(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldNEQ(FieldCreatedBy, v)) +} + +// CreatedByIn applies the In predicate on the "created_by" field. +func CreatedByIn(vs ...string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldIn(FieldCreatedBy, vs...)) +} + +// CreatedByNotIn applies the NotIn predicate on the "created_by" field. +func CreatedByNotIn(vs ...string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldNotIn(FieldCreatedBy, vs...)) +} + +// CreatedByGT applies the GT predicate on the "created_by" field. +func CreatedByGT(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldGT(FieldCreatedBy, v)) +} + +// CreatedByGTE applies the GTE predicate on the "created_by" field. +func CreatedByGTE(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldGTE(FieldCreatedBy, v)) +} + +// CreatedByLT applies the LT predicate on the "created_by" field. +func CreatedByLT(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldLT(FieldCreatedBy, v)) +} + +// CreatedByLTE applies the LTE predicate on the "created_by" field. +func CreatedByLTE(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldLTE(FieldCreatedBy, v)) +} + +// CreatedByContains applies the Contains predicate on the "created_by" field. +func CreatedByContains(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldContains(FieldCreatedBy, v)) +} + +// CreatedByHasPrefix applies the HasPrefix predicate on the "created_by" field. +func CreatedByHasPrefix(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldHasPrefix(FieldCreatedBy, v)) +} + +// CreatedByHasSuffix applies the HasSuffix predicate on the "created_by" field. +func CreatedByHasSuffix(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldHasSuffix(FieldCreatedBy, v)) +} + +// CreatedByIsNil applies the IsNil predicate on the "created_by" field. +func CreatedByIsNil() predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldIsNull(FieldCreatedBy)) +} + +// CreatedByNotNil applies the NotNil predicate on the "created_by" field. +func CreatedByNotNil() predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldNotNull(FieldCreatedBy)) +} + +// CreatedByEqualFold applies the EqualFold predicate on the "created_by" field. +func CreatedByEqualFold(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldEqualFold(FieldCreatedBy, v)) +} + +// CreatedByContainsFold applies the ContainsFold predicate on the "created_by" field. +func CreatedByContainsFold(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldContainsFold(FieldCreatedBy, v)) +} + +// UpdatedByEQ applies the EQ predicate on the "updated_by" field. +func UpdatedByEQ(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldEQ(FieldUpdatedBy, v)) +} + +// UpdatedByNEQ applies the NEQ predicate on the "updated_by" field. +func UpdatedByNEQ(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldNEQ(FieldUpdatedBy, v)) +} + +// UpdatedByIn applies the In predicate on the "updated_by" field. +func UpdatedByIn(vs ...string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldIn(FieldUpdatedBy, vs...)) +} + +// UpdatedByNotIn applies the NotIn predicate on the "updated_by" field. +func UpdatedByNotIn(vs ...string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldNotIn(FieldUpdatedBy, vs...)) +} + +// UpdatedByGT applies the GT predicate on the "updated_by" field. +func UpdatedByGT(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldGT(FieldUpdatedBy, v)) +} + +// UpdatedByGTE applies the GTE predicate on the "updated_by" field. +func UpdatedByGTE(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldGTE(FieldUpdatedBy, v)) +} + +// UpdatedByLT applies the LT predicate on the "updated_by" field. +func UpdatedByLT(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldLT(FieldUpdatedBy, v)) +} + +// UpdatedByLTE applies the LTE predicate on the "updated_by" field. +func UpdatedByLTE(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldLTE(FieldUpdatedBy, v)) +} + +// UpdatedByContains applies the Contains predicate on the "updated_by" field. +func UpdatedByContains(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldContains(FieldUpdatedBy, v)) +} + +// UpdatedByHasPrefix applies the HasPrefix predicate on the "updated_by" field. +func UpdatedByHasPrefix(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldHasPrefix(FieldUpdatedBy, v)) +} + +// UpdatedByHasSuffix applies the HasSuffix predicate on the "updated_by" field. +func UpdatedByHasSuffix(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldHasSuffix(FieldUpdatedBy, v)) +} + +// UpdatedByIsNil applies the IsNil predicate on the "updated_by" field. +func UpdatedByIsNil() predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldIsNull(FieldUpdatedBy)) +} + +// UpdatedByNotNil applies the NotNil predicate on the "updated_by" field. +func UpdatedByNotNil() predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldNotNull(FieldUpdatedBy)) +} + +// UpdatedByEqualFold applies the EqualFold predicate on the "updated_by" field. +func UpdatedByEqualFold(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldEqualFold(FieldUpdatedBy, v)) +} + +// UpdatedByContainsFold applies the ContainsFold predicate on the "updated_by" field. +func UpdatedByContainsFold(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldContainsFold(FieldUpdatedBy, v)) +} + +// VisibilityEQ applies the EQ predicate on the "visibility" field. +func VisibilityEQ(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldEQ(FieldVisibility, v)) +} + +// VisibilityNEQ applies the NEQ predicate on the "visibility" field. +func VisibilityNEQ(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldNEQ(FieldVisibility, v)) +} + +// VisibilityIn applies the In predicate on the "visibility" field. +func VisibilityIn(vs ...string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldIn(FieldVisibility, vs...)) +} + +// VisibilityNotIn applies the NotIn predicate on the "visibility" field. +func VisibilityNotIn(vs ...string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldNotIn(FieldVisibility, vs...)) +} + +// VisibilityGT applies the GT predicate on the "visibility" field. +func VisibilityGT(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldGT(FieldVisibility, v)) +} + +// VisibilityGTE applies the GTE predicate on the "visibility" field. +func VisibilityGTE(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldGTE(FieldVisibility, v)) +} + +// VisibilityLT applies the LT predicate on the "visibility" field. +func VisibilityLT(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldLT(FieldVisibility, v)) +} + +// VisibilityLTE applies the LTE predicate on the "visibility" field. +func VisibilityLTE(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldLTE(FieldVisibility, v)) +} + +// VisibilityContains applies the Contains predicate on the "visibility" field. +func VisibilityContains(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldContains(FieldVisibility, v)) +} + +// VisibilityHasPrefix applies the HasPrefix predicate on the "visibility" field. +func VisibilityHasPrefix(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldHasPrefix(FieldVisibility, v)) +} + +// VisibilityHasSuffix applies the HasSuffix predicate on the "visibility" field. +func VisibilityHasSuffix(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldHasSuffix(FieldVisibility, v)) +} + +// VisibilityEqualFold applies the EqualFold predicate on the "visibility" field. +func VisibilityEqualFold(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldEqualFold(FieldVisibility, v)) +} + +// VisibilityContainsFold applies the ContainsFold predicate on the "visibility" field. +func VisibilityContainsFold(v string) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldContainsFold(FieldVisibility, v)) +} + +// CreatedEQ applies the EQ predicate on the "created" field. +func CreatedEQ(v time.Time) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldEQ(FieldCreated, v)) +} + +// CreatedNEQ applies the NEQ predicate on the "created" field. +func CreatedNEQ(v time.Time) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldNEQ(FieldCreated, v)) +} + +// CreatedIn applies the In predicate on the "created" field. +func CreatedIn(vs ...time.Time) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldIn(FieldCreated, vs...)) +} + +// CreatedNotIn applies the NotIn predicate on the "created" field. +func CreatedNotIn(vs ...time.Time) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldNotIn(FieldCreated, vs...)) +} + +// CreatedGT applies the GT predicate on the "created" field. +func CreatedGT(v time.Time) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldGT(FieldCreated, v)) +} + +// CreatedGTE applies the GTE predicate on the "created" field. +func CreatedGTE(v time.Time) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldGTE(FieldCreated, v)) +} + +// CreatedLT applies the LT predicate on the "created" field. +func CreatedLT(v time.Time) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldLT(FieldCreated, v)) +} + +// CreatedLTE applies the LTE predicate on the "created" field. +func CreatedLTE(v time.Time) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldLTE(FieldCreated, v)) +} + +// UpdatedEQ applies the EQ predicate on the "updated" field. +func UpdatedEQ(v time.Time) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldEQ(FieldUpdated, v)) +} + +// UpdatedNEQ applies the NEQ predicate on the "updated" field. +func UpdatedNEQ(v time.Time) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldNEQ(FieldUpdated, v)) +} + +// UpdatedIn applies the In predicate on the "updated" field. +func UpdatedIn(vs ...time.Time) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldIn(FieldUpdated, vs...)) +} + +// UpdatedNotIn applies the NotIn predicate on the "updated" field. +func UpdatedNotIn(vs ...time.Time) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldNotIn(FieldUpdated, vs...)) +} + +// UpdatedGT applies the GT predicate on the "updated" field. +func UpdatedGT(v time.Time) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldGT(FieldUpdated, v)) +} + +// UpdatedGTE applies the GTE predicate on the "updated" field. +func UpdatedGTE(v time.Time) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldGTE(FieldUpdated, v)) +} + +// UpdatedLT applies the LT predicate on the "updated" field. +func UpdatedLT(v time.Time) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldLT(FieldUpdated, v)) +} + +// UpdatedLTE applies the LTE predicate on the "updated" field. +func UpdatedLTE(v time.Time) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.FieldLTE(FieldUpdated, v)) +} + +// And groups predicates with the AND operator between them. +func And(predicates ...predicate.HarnessConfig) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.AndPredicates(predicates...)) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.HarnessConfig) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.OrPredicates(predicates...)) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.HarnessConfig) predicate.HarnessConfig { + return predicate.HarnessConfig(sql.NotPredicates(p)) +} diff --git a/pkg/ent/harnessconfig_create.go b/pkg/ent/harnessconfig_create.go new file mode 100644 index 000000000..7111759b8 --- /dev/null +++ b/pkg/ent/harnessconfig_create.go @@ -0,0 +1,1927 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/harnessconfig" + "github.com/google/uuid" +) + +// HarnessConfigCreate is the builder for creating a HarnessConfig entity. +type HarnessConfigCreate struct { + config + mutation *HarnessConfigMutation + hooks []Hook + conflict []sql.ConflictOption +} + +// SetName sets the "name" field. +func (_c *HarnessConfigCreate) SetName(v string) *HarnessConfigCreate { + _c.mutation.SetName(v) + return _c +} + +// SetSlug sets the "slug" field. +func (_c *HarnessConfigCreate) SetSlug(v string) *HarnessConfigCreate { + _c.mutation.SetSlug(v) + return _c +} + +// SetDisplayName sets the "display_name" field. +func (_c *HarnessConfigCreate) SetDisplayName(v string) *HarnessConfigCreate { + _c.mutation.SetDisplayName(v) + return _c +} + +// SetNillableDisplayName sets the "display_name" field if the given value is not nil. +func (_c *HarnessConfigCreate) SetNillableDisplayName(v *string) *HarnessConfigCreate { + if v != nil { + _c.SetDisplayName(*v) + } + return _c +} + +// SetDescription sets the "description" field. +func (_c *HarnessConfigCreate) SetDescription(v string) *HarnessConfigCreate { + _c.mutation.SetDescription(v) + return _c +} + +// SetNillableDescription sets the "description" field if the given value is not nil. +func (_c *HarnessConfigCreate) SetNillableDescription(v *string) *HarnessConfigCreate { + if v != nil { + _c.SetDescription(*v) + } + return _c +} + +// SetHarness sets the "harness" field. +func (_c *HarnessConfigCreate) SetHarness(v string) *HarnessConfigCreate { + _c.mutation.SetHarness(v) + return _c +} + +// SetConfig sets the "config" field. +func (_c *HarnessConfigCreate) SetConfig(v string) *HarnessConfigCreate { + _c.mutation.SetConfig(v) + return _c +} + +// SetNillableConfig sets the "config" field if the given value is not nil. +func (_c *HarnessConfigCreate) SetNillableConfig(v *string) *HarnessConfigCreate { + if v != nil { + _c.SetConfig(*v) + } + return _c +} + +// SetContentHash sets the "content_hash" field. +func (_c *HarnessConfigCreate) SetContentHash(v string) *HarnessConfigCreate { + _c.mutation.SetContentHash(v) + return _c +} + +// SetNillableContentHash sets the "content_hash" field if the given value is not nil. +func (_c *HarnessConfigCreate) SetNillableContentHash(v *string) *HarnessConfigCreate { + if v != nil { + _c.SetContentHash(*v) + } + return _c +} + +// SetScope sets the "scope" field. +func (_c *HarnessConfigCreate) SetScope(v string) *HarnessConfigCreate { + _c.mutation.SetScope(v) + return _c +} + +// SetNillableScope sets the "scope" field if the given value is not nil. +func (_c *HarnessConfigCreate) SetNillableScope(v *string) *HarnessConfigCreate { + if v != nil { + _c.SetScope(*v) + } + return _c +} + +// SetScopeID sets the "scope_id" field. +func (_c *HarnessConfigCreate) SetScopeID(v string) *HarnessConfigCreate { + _c.mutation.SetScopeID(v) + return _c +} + +// SetNillableScopeID sets the "scope_id" field if the given value is not nil. +func (_c *HarnessConfigCreate) SetNillableScopeID(v *string) *HarnessConfigCreate { + if v != nil { + _c.SetScopeID(*v) + } + return _c +} + +// SetStorageURI sets the "storage_uri" field. +func (_c *HarnessConfigCreate) SetStorageURI(v string) *HarnessConfigCreate { + _c.mutation.SetStorageURI(v) + return _c +} + +// SetNillableStorageURI sets the "storage_uri" field if the given value is not nil. +func (_c *HarnessConfigCreate) SetNillableStorageURI(v *string) *HarnessConfigCreate { + if v != nil { + _c.SetStorageURI(*v) + } + return _c +} + +// SetStorageBucket sets the "storage_bucket" field. +func (_c *HarnessConfigCreate) SetStorageBucket(v string) *HarnessConfigCreate { + _c.mutation.SetStorageBucket(v) + return _c +} + +// SetNillableStorageBucket sets the "storage_bucket" field if the given value is not nil. +func (_c *HarnessConfigCreate) SetNillableStorageBucket(v *string) *HarnessConfigCreate { + if v != nil { + _c.SetStorageBucket(*v) + } + return _c +} + +// SetStoragePath sets the "storage_path" field. +func (_c *HarnessConfigCreate) SetStoragePath(v string) *HarnessConfigCreate { + _c.mutation.SetStoragePath(v) + return _c +} + +// SetNillableStoragePath sets the "storage_path" field if the given value is not nil. +func (_c *HarnessConfigCreate) SetNillableStoragePath(v *string) *HarnessConfigCreate { + if v != nil { + _c.SetStoragePath(*v) + } + return _c +} + +// SetFiles sets the "files" field. +func (_c *HarnessConfigCreate) SetFiles(v string) *HarnessConfigCreate { + _c.mutation.SetFiles(v) + return _c +} + +// SetNillableFiles sets the "files" field if the given value is not nil. +func (_c *HarnessConfigCreate) SetNillableFiles(v *string) *HarnessConfigCreate { + if v != nil { + _c.SetFiles(*v) + } + return _c +} + +// SetLocked sets the "locked" field. +func (_c *HarnessConfigCreate) SetLocked(v bool) *HarnessConfigCreate { + _c.mutation.SetLocked(v) + return _c +} + +// SetNillableLocked sets the "locked" field if the given value is not nil. +func (_c *HarnessConfigCreate) SetNillableLocked(v *bool) *HarnessConfigCreate { + if v != nil { + _c.SetLocked(*v) + } + return _c +} + +// SetStatus sets the "status" field. +func (_c *HarnessConfigCreate) SetStatus(v harnessconfig.Status) *HarnessConfigCreate { + _c.mutation.SetStatus(v) + return _c +} + +// SetNillableStatus sets the "status" field if the given value is not nil. +func (_c *HarnessConfigCreate) SetNillableStatus(v *harnessconfig.Status) *HarnessConfigCreate { + if v != nil { + _c.SetStatus(*v) + } + return _c +} + +// SetOwnerID sets the "owner_id" field. +func (_c *HarnessConfigCreate) SetOwnerID(v string) *HarnessConfigCreate { + _c.mutation.SetOwnerID(v) + return _c +} + +// SetNillableOwnerID sets the "owner_id" field if the given value is not nil. +func (_c *HarnessConfigCreate) SetNillableOwnerID(v *string) *HarnessConfigCreate { + if v != nil { + _c.SetOwnerID(*v) + } + return _c +} + +// SetCreatedBy sets the "created_by" field. +func (_c *HarnessConfigCreate) SetCreatedBy(v string) *HarnessConfigCreate { + _c.mutation.SetCreatedBy(v) + return _c +} + +// SetNillableCreatedBy sets the "created_by" field if the given value is not nil. +func (_c *HarnessConfigCreate) SetNillableCreatedBy(v *string) *HarnessConfigCreate { + if v != nil { + _c.SetCreatedBy(*v) + } + return _c +} + +// SetUpdatedBy sets the "updated_by" field. +func (_c *HarnessConfigCreate) SetUpdatedBy(v string) *HarnessConfigCreate { + _c.mutation.SetUpdatedBy(v) + return _c +} + +// SetNillableUpdatedBy sets the "updated_by" field if the given value is not nil. +func (_c *HarnessConfigCreate) SetNillableUpdatedBy(v *string) *HarnessConfigCreate { + if v != nil { + _c.SetUpdatedBy(*v) + } + return _c +} + +// SetVisibility sets the "visibility" field. +func (_c *HarnessConfigCreate) SetVisibility(v string) *HarnessConfigCreate { + _c.mutation.SetVisibility(v) + return _c +} + +// SetNillableVisibility sets the "visibility" field if the given value is not nil. +func (_c *HarnessConfigCreate) SetNillableVisibility(v *string) *HarnessConfigCreate { + if v != nil { + _c.SetVisibility(*v) + } + return _c +} + +// SetCreated sets the "created" field. +func (_c *HarnessConfigCreate) SetCreated(v time.Time) *HarnessConfigCreate { + _c.mutation.SetCreated(v) + return _c +} + +// SetNillableCreated sets the "created" field if the given value is not nil. +func (_c *HarnessConfigCreate) SetNillableCreated(v *time.Time) *HarnessConfigCreate { + if v != nil { + _c.SetCreated(*v) + } + return _c +} + +// SetUpdated sets the "updated" field. +func (_c *HarnessConfigCreate) SetUpdated(v time.Time) *HarnessConfigCreate { + _c.mutation.SetUpdated(v) + return _c +} + +// SetNillableUpdated sets the "updated" field if the given value is not nil. +func (_c *HarnessConfigCreate) SetNillableUpdated(v *time.Time) *HarnessConfigCreate { + if v != nil { + _c.SetUpdated(*v) + } + return _c +} + +// SetID sets the "id" field. +func (_c *HarnessConfigCreate) SetID(v uuid.UUID) *HarnessConfigCreate { + _c.mutation.SetID(v) + return _c +} + +// SetNillableID sets the "id" field if the given value is not nil. +func (_c *HarnessConfigCreate) SetNillableID(v *uuid.UUID) *HarnessConfigCreate { + if v != nil { + _c.SetID(*v) + } + return _c +} + +// Mutation returns the HarnessConfigMutation object of the builder. +func (_c *HarnessConfigCreate) Mutation() *HarnessConfigMutation { + return _c.mutation +} + +// Save creates the HarnessConfig in the database. +func (_c *HarnessConfigCreate) Save(ctx context.Context) (*HarnessConfig, error) { + _c.defaults() + return withHooks(ctx, _c.sqlSave, _c.mutation, _c.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (_c *HarnessConfigCreate) SaveX(ctx context.Context) *HarnessConfig { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *HarnessConfigCreate) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *HarnessConfigCreate) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_c *HarnessConfigCreate) defaults() { + if _, ok := _c.mutation.Scope(); !ok { + v := harnessconfig.DefaultScope + _c.mutation.SetScope(v) + } + if _, ok := _c.mutation.Locked(); !ok { + v := harnessconfig.DefaultLocked + _c.mutation.SetLocked(v) + } + if _, ok := _c.mutation.Status(); !ok { + v := harnessconfig.DefaultStatus + _c.mutation.SetStatus(v) + } + if _, ok := _c.mutation.Visibility(); !ok { + v := harnessconfig.DefaultVisibility + _c.mutation.SetVisibility(v) + } + if _, ok := _c.mutation.Created(); !ok { + v := harnessconfig.DefaultCreated() + _c.mutation.SetCreated(v) + } + if _, ok := _c.mutation.Updated(); !ok { + v := harnessconfig.DefaultUpdated() + _c.mutation.SetUpdated(v) + } + if _, ok := _c.mutation.ID(); !ok { + v := harnessconfig.DefaultID() + _c.mutation.SetID(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_c *HarnessConfigCreate) check() error { + if _, ok := _c.mutation.Name(); !ok { + return &ValidationError{Name: "name", err: errors.New(`ent: missing required field "HarnessConfig.name"`)} + } + if v, ok := _c.mutation.Name(); ok { + if err := harnessconfig.NameValidator(v); err != nil { + return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "HarnessConfig.name": %w`, err)} + } + } + if _, ok := _c.mutation.Slug(); !ok { + return &ValidationError{Name: "slug", err: errors.New(`ent: missing required field "HarnessConfig.slug"`)} + } + if v, ok := _c.mutation.Slug(); ok { + if err := harnessconfig.SlugValidator(v); err != nil { + return &ValidationError{Name: "slug", err: fmt.Errorf(`ent: validator failed for field "HarnessConfig.slug": %w`, err)} + } + } + if _, ok := _c.mutation.Harness(); !ok { + return &ValidationError{Name: "harness", err: errors.New(`ent: missing required field "HarnessConfig.harness"`)} + } + if v, ok := _c.mutation.Harness(); ok { + if err := harnessconfig.HarnessValidator(v); err != nil { + return &ValidationError{Name: "harness", err: fmt.Errorf(`ent: validator failed for field "HarnessConfig.harness": %w`, err)} + } + } + if _, ok := _c.mutation.Scope(); !ok { + return &ValidationError{Name: "scope", err: errors.New(`ent: missing required field "HarnessConfig.scope"`)} + } + if _, ok := _c.mutation.Locked(); !ok { + return &ValidationError{Name: "locked", err: errors.New(`ent: missing required field "HarnessConfig.locked"`)} + } + if _, ok := _c.mutation.Status(); !ok { + return &ValidationError{Name: "status", err: errors.New(`ent: missing required field "HarnessConfig.status"`)} + } + if v, ok := _c.mutation.Status(); ok { + if err := harnessconfig.StatusValidator(v); err != nil { + return &ValidationError{Name: "status", err: fmt.Errorf(`ent: validator failed for field "HarnessConfig.status": %w`, err)} + } + } + if _, ok := _c.mutation.Visibility(); !ok { + return &ValidationError{Name: "visibility", err: errors.New(`ent: missing required field "HarnessConfig.visibility"`)} + } + if _, ok := _c.mutation.Created(); !ok { + return &ValidationError{Name: "created", err: errors.New(`ent: missing required field "HarnessConfig.created"`)} + } + if _, ok := _c.mutation.Updated(); !ok { + return &ValidationError{Name: "updated", err: errors.New(`ent: missing required field "HarnessConfig.updated"`)} + } + return nil +} + +func (_c *HarnessConfigCreate) sqlSave(ctx context.Context) (*HarnessConfig, error) { + if err := _c.check(); err != nil { + return nil, err + } + _node, _spec := _c.createSpec() + if err := sqlgraph.CreateNode(ctx, _c.driver, _spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + if _spec.ID.Value != nil { + if id, ok := _spec.ID.Value.(*uuid.UUID); ok { + _node.ID = *id + } else if err := _node.ID.Scan(_spec.ID.Value); err != nil { + return nil, err + } + } + _c.mutation.id = &_node.ID + _c.mutation.done = true + return _node, nil +} + +func (_c *HarnessConfigCreate) createSpec() (*HarnessConfig, *sqlgraph.CreateSpec) { + var ( + _node = &HarnessConfig{config: _c.config} + _spec = sqlgraph.NewCreateSpec(harnessconfig.Table, sqlgraph.NewFieldSpec(harnessconfig.FieldID, field.TypeUUID)) + ) + _spec.OnConflict = _c.conflict + if id, ok := _c.mutation.ID(); ok { + _node.ID = id + _spec.ID.Value = &id + } + if value, ok := _c.mutation.Name(); ok { + _spec.SetField(harnessconfig.FieldName, field.TypeString, value) + _node.Name = value + } + if value, ok := _c.mutation.Slug(); ok { + _spec.SetField(harnessconfig.FieldSlug, field.TypeString, value) + _node.Slug = value + } + if value, ok := _c.mutation.DisplayName(); ok { + _spec.SetField(harnessconfig.FieldDisplayName, field.TypeString, value) + _node.DisplayName = value + } + if value, ok := _c.mutation.Description(); ok { + _spec.SetField(harnessconfig.FieldDescription, field.TypeString, value) + _node.Description = value + } + if value, ok := _c.mutation.Harness(); ok { + _spec.SetField(harnessconfig.FieldHarness, field.TypeString, value) + _node.Harness = value + } + if value, ok := _c.mutation.Config(); ok { + _spec.SetField(harnessconfig.FieldConfig, field.TypeString, value) + _node.Config = value + } + if value, ok := _c.mutation.ContentHash(); ok { + _spec.SetField(harnessconfig.FieldContentHash, field.TypeString, value) + _node.ContentHash = value + } + if value, ok := _c.mutation.Scope(); ok { + _spec.SetField(harnessconfig.FieldScope, field.TypeString, value) + _node.Scope = value + } + if value, ok := _c.mutation.ScopeID(); ok { + _spec.SetField(harnessconfig.FieldScopeID, field.TypeString, value) + _node.ScopeID = value + } + if value, ok := _c.mutation.StorageURI(); ok { + _spec.SetField(harnessconfig.FieldStorageURI, field.TypeString, value) + _node.StorageURI = value + } + if value, ok := _c.mutation.StorageBucket(); ok { + _spec.SetField(harnessconfig.FieldStorageBucket, field.TypeString, value) + _node.StorageBucket = value + } + if value, ok := _c.mutation.StoragePath(); ok { + _spec.SetField(harnessconfig.FieldStoragePath, field.TypeString, value) + _node.StoragePath = value + } + if value, ok := _c.mutation.Files(); ok { + _spec.SetField(harnessconfig.FieldFiles, field.TypeString, value) + _node.Files = value + } + if value, ok := _c.mutation.Locked(); ok { + _spec.SetField(harnessconfig.FieldLocked, field.TypeBool, value) + _node.Locked = value + } + if value, ok := _c.mutation.Status(); ok { + _spec.SetField(harnessconfig.FieldStatus, field.TypeEnum, value) + _node.Status = value + } + if value, ok := _c.mutation.OwnerID(); ok { + _spec.SetField(harnessconfig.FieldOwnerID, field.TypeString, value) + _node.OwnerID = value + } + if value, ok := _c.mutation.CreatedBy(); ok { + _spec.SetField(harnessconfig.FieldCreatedBy, field.TypeString, value) + _node.CreatedBy = value + } + if value, ok := _c.mutation.UpdatedBy(); ok { + _spec.SetField(harnessconfig.FieldUpdatedBy, field.TypeString, value) + _node.UpdatedBy = value + } + if value, ok := _c.mutation.Visibility(); ok { + _spec.SetField(harnessconfig.FieldVisibility, field.TypeString, value) + _node.Visibility = value + } + if value, ok := _c.mutation.Created(); ok { + _spec.SetField(harnessconfig.FieldCreated, field.TypeTime, value) + _node.Created = value + } + if value, ok := _c.mutation.Updated(); ok { + _spec.SetField(harnessconfig.FieldUpdated, field.TypeTime, value) + _node.Updated = value + } + return _node, _spec +} + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.HarnessConfig.Create(). +// SetName(v). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.HarnessConfigUpsert) { +// SetName(v+v). +// }). +// Exec(ctx) +func (_c *HarnessConfigCreate) OnConflict(opts ...sql.ConflictOption) *HarnessConfigUpsertOne { + _c.conflict = opts + return &HarnessConfigUpsertOne{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.HarnessConfig.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *HarnessConfigCreate) OnConflictColumns(columns ...string) *HarnessConfigUpsertOne { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &HarnessConfigUpsertOne{ + create: _c, + } +} + +type ( + // HarnessConfigUpsertOne is the builder for "upsert"-ing + // one HarnessConfig node. + HarnessConfigUpsertOne struct { + create *HarnessConfigCreate + } + + // HarnessConfigUpsert is the "OnConflict" setter. + HarnessConfigUpsert struct { + *sql.UpdateSet + } +) + +// SetName sets the "name" field. +func (u *HarnessConfigUpsert) SetName(v string) *HarnessConfigUpsert { + u.Set(harnessconfig.FieldName, v) + return u +} + +// UpdateName sets the "name" field to the value that was provided on create. +func (u *HarnessConfigUpsert) UpdateName() *HarnessConfigUpsert { + u.SetExcluded(harnessconfig.FieldName) + return u +} + +// SetSlug sets the "slug" field. +func (u *HarnessConfigUpsert) SetSlug(v string) *HarnessConfigUpsert { + u.Set(harnessconfig.FieldSlug, v) + return u +} + +// UpdateSlug sets the "slug" field to the value that was provided on create. +func (u *HarnessConfigUpsert) UpdateSlug() *HarnessConfigUpsert { + u.SetExcluded(harnessconfig.FieldSlug) + return u +} + +// SetDisplayName sets the "display_name" field. +func (u *HarnessConfigUpsert) SetDisplayName(v string) *HarnessConfigUpsert { + u.Set(harnessconfig.FieldDisplayName, v) + return u +} + +// UpdateDisplayName sets the "display_name" field to the value that was provided on create. +func (u *HarnessConfigUpsert) UpdateDisplayName() *HarnessConfigUpsert { + u.SetExcluded(harnessconfig.FieldDisplayName) + return u +} + +// ClearDisplayName clears the value of the "display_name" field. +func (u *HarnessConfigUpsert) ClearDisplayName() *HarnessConfigUpsert { + u.SetNull(harnessconfig.FieldDisplayName) + return u +} + +// SetDescription sets the "description" field. +func (u *HarnessConfigUpsert) SetDescription(v string) *HarnessConfigUpsert { + u.Set(harnessconfig.FieldDescription, v) + return u +} + +// UpdateDescription sets the "description" field to the value that was provided on create. +func (u *HarnessConfigUpsert) UpdateDescription() *HarnessConfigUpsert { + u.SetExcluded(harnessconfig.FieldDescription) + return u +} + +// ClearDescription clears the value of the "description" field. +func (u *HarnessConfigUpsert) ClearDescription() *HarnessConfigUpsert { + u.SetNull(harnessconfig.FieldDescription) + return u +} + +// SetHarness sets the "harness" field. +func (u *HarnessConfigUpsert) SetHarness(v string) *HarnessConfigUpsert { + u.Set(harnessconfig.FieldHarness, v) + return u +} + +// UpdateHarness sets the "harness" field to the value that was provided on create. +func (u *HarnessConfigUpsert) UpdateHarness() *HarnessConfigUpsert { + u.SetExcluded(harnessconfig.FieldHarness) + return u +} + +// SetConfig sets the "config" field. +func (u *HarnessConfigUpsert) SetConfig(v string) *HarnessConfigUpsert { + u.Set(harnessconfig.FieldConfig, v) + return u +} + +// UpdateConfig sets the "config" field to the value that was provided on create. +func (u *HarnessConfigUpsert) UpdateConfig() *HarnessConfigUpsert { + u.SetExcluded(harnessconfig.FieldConfig) + return u +} + +// ClearConfig clears the value of the "config" field. +func (u *HarnessConfigUpsert) ClearConfig() *HarnessConfigUpsert { + u.SetNull(harnessconfig.FieldConfig) + return u +} + +// SetContentHash sets the "content_hash" field. +func (u *HarnessConfigUpsert) SetContentHash(v string) *HarnessConfigUpsert { + u.Set(harnessconfig.FieldContentHash, v) + return u +} + +// UpdateContentHash sets the "content_hash" field to the value that was provided on create. +func (u *HarnessConfigUpsert) UpdateContentHash() *HarnessConfigUpsert { + u.SetExcluded(harnessconfig.FieldContentHash) + return u +} + +// ClearContentHash clears the value of the "content_hash" field. +func (u *HarnessConfigUpsert) ClearContentHash() *HarnessConfigUpsert { + u.SetNull(harnessconfig.FieldContentHash) + return u +} + +// SetScope sets the "scope" field. +func (u *HarnessConfigUpsert) SetScope(v string) *HarnessConfigUpsert { + u.Set(harnessconfig.FieldScope, v) + return u +} + +// UpdateScope sets the "scope" field to the value that was provided on create. +func (u *HarnessConfigUpsert) UpdateScope() *HarnessConfigUpsert { + u.SetExcluded(harnessconfig.FieldScope) + return u +} + +// SetScopeID sets the "scope_id" field. +func (u *HarnessConfigUpsert) SetScopeID(v string) *HarnessConfigUpsert { + u.Set(harnessconfig.FieldScopeID, v) + return u +} + +// UpdateScopeID sets the "scope_id" field to the value that was provided on create. +func (u *HarnessConfigUpsert) UpdateScopeID() *HarnessConfigUpsert { + u.SetExcluded(harnessconfig.FieldScopeID) + return u +} + +// ClearScopeID clears the value of the "scope_id" field. +func (u *HarnessConfigUpsert) ClearScopeID() *HarnessConfigUpsert { + u.SetNull(harnessconfig.FieldScopeID) + return u +} + +// SetStorageURI sets the "storage_uri" field. +func (u *HarnessConfigUpsert) SetStorageURI(v string) *HarnessConfigUpsert { + u.Set(harnessconfig.FieldStorageURI, v) + return u +} + +// UpdateStorageURI sets the "storage_uri" field to the value that was provided on create. +func (u *HarnessConfigUpsert) UpdateStorageURI() *HarnessConfigUpsert { + u.SetExcluded(harnessconfig.FieldStorageURI) + return u +} + +// ClearStorageURI clears the value of the "storage_uri" field. +func (u *HarnessConfigUpsert) ClearStorageURI() *HarnessConfigUpsert { + u.SetNull(harnessconfig.FieldStorageURI) + return u +} + +// SetStorageBucket sets the "storage_bucket" field. +func (u *HarnessConfigUpsert) SetStorageBucket(v string) *HarnessConfigUpsert { + u.Set(harnessconfig.FieldStorageBucket, v) + return u +} + +// UpdateStorageBucket sets the "storage_bucket" field to the value that was provided on create. +func (u *HarnessConfigUpsert) UpdateStorageBucket() *HarnessConfigUpsert { + u.SetExcluded(harnessconfig.FieldStorageBucket) + return u +} + +// ClearStorageBucket clears the value of the "storage_bucket" field. +func (u *HarnessConfigUpsert) ClearStorageBucket() *HarnessConfigUpsert { + u.SetNull(harnessconfig.FieldStorageBucket) + return u +} + +// SetStoragePath sets the "storage_path" field. +func (u *HarnessConfigUpsert) SetStoragePath(v string) *HarnessConfigUpsert { + u.Set(harnessconfig.FieldStoragePath, v) + return u +} + +// UpdateStoragePath sets the "storage_path" field to the value that was provided on create. +func (u *HarnessConfigUpsert) UpdateStoragePath() *HarnessConfigUpsert { + u.SetExcluded(harnessconfig.FieldStoragePath) + return u +} + +// ClearStoragePath clears the value of the "storage_path" field. +func (u *HarnessConfigUpsert) ClearStoragePath() *HarnessConfigUpsert { + u.SetNull(harnessconfig.FieldStoragePath) + return u +} + +// SetFiles sets the "files" field. +func (u *HarnessConfigUpsert) SetFiles(v string) *HarnessConfigUpsert { + u.Set(harnessconfig.FieldFiles, v) + return u +} + +// UpdateFiles sets the "files" field to the value that was provided on create. +func (u *HarnessConfigUpsert) UpdateFiles() *HarnessConfigUpsert { + u.SetExcluded(harnessconfig.FieldFiles) + return u +} + +// ClearFiles clears the value of the "files" field. +func (u *HarnessConfigUpsert) ClearFiles() *HarnessConfigUpsert { + u.SetNull(harnessconfig.FieldFiles) + return u +} + +// SetLocked sets the "locked" field. +func (u *HarnessConfigUpsert) SetLocked(v bool) *HarnessConfigUpsert { + u.Set(harnessconfig.FieldLocked, v) + return u +} + +// UpdateLocked sets the "locked" field to the value that was provided on create. +func (u *HarnessConfigUpsert) UpdateLocked() *HarnessConfigUpsert { + u.SetExcluded(harnessconfig.FieldLocked) + return u +} + +// SetStatus sets the "status" field. +func (u *HarnessConfigUpsert) SetStatus(v harnessconfig.Status) *HarnessConfigUpsert { + u.Set(harnessconfig.FieldStatus, v) + return u +} + +// UpdateStatus sets the "status" field to the value that was provided on create. +func (u *HarnessConfigUpsert) UpdateStatus() *HarnessConfigUpsert { + u.SetExcluded(harnessconfig.FieldStatus) + return u +} + +// SetOwnerID sets the "owner_id" field. +func (u *HarnessConfigUpsert) SetOwnerID(v string) *HarnessConfigUpsert { + u.Set(harnessconfig.FieldOwnerID, v) + return u +} + +// UpdateOwnerID sets the "owner_id" field to the value that was provided on create. +func (u *HarnessConfigUpsert) UpdateOwnerID() *HarnessConfigUpsert { + u.SetExcluded(harnessconfig.FieldOwnerID) + return u +} + +// ClearOwnerID clears the value of the "owner_id" field. +func (u *HarnessConfigUpsert) ClearOwnerID() *HarnessConfigUpsert { + u.SetNull(harnessconfig.FieldOwnerID) + return u +} + +// SetCreatedBy sets the "created_by" field. +func (u *HarnessConfigUpsert) SetCreatedBy(v string) *HarnessConfigUpsert { + u.Set(harnessconfig.FieldCreatedBy, v) + return u +} + +// UpdateCreatedBy sets the "created_by" field to the value that was provided on create. +func (u *HarnessConfigUpsert) UpdateCreatedBy() *HarnessConfigUpsert { + u.SetExcluded(harnessconfig.FieldCreatedBy) + return u +} + +// ClearCreatedBy clears the value of the "created_by" field. +func (u *HarnessConfigUpsert) ClearCreatedBy() *HarnessConfigUpsert { + u.SetNull(harnessconfig.FieldCreatedBy) + return u +} + +// SetUpdatedBy sets the "updated_by" field. +func (u *HarnessConfigUpsert) SetUpdatedBy(v string) *HarnessConfigUpsert { + u.Set(harnessconfig.FieldUpdatedBy, v) + return u +} + +// UpdateUpdatedBy sets the "updated_by" field to the value that was provided on create. +func (u *HarnessConfigUpsert) UpdateUpdatedBy() *HarnessConfigUpsert { + u.SetExcluded(harnessconfig.FieldUpdatedBy) + return u +} + +// ClearUpdatedBy clears the value of the "updated_by" field. +func (u *HarnessConfigUpsert) ClearUpdatedBy() *HarnessConfigUpsert { + u.SetNull(harnessconfig.FieldUpdatedBy) + return u +} + +// SetVisibility sets the "visibility" field. +func (u *HarnessConfigUpsert) SetVisibility(v string) *HarnessConfigUpsert { + u.Set(harnessconfig.FieldVisibility, v) + return u +} + +// UpdateVisibility sets the "visibility" field to the value that was provided on create. +func (u *HarnessConfigUpsert) UpdateVisibility() *HarnessConfigUpsert { + u.SetExcluded(harnessconfig.FieldVisibility) + return u +} + +// SetUpdated sets the "updated" field. +func (u *HarnessConfigUpsert) SetUpdated(v time.Time) *HarnessConfigUpsert { + u.Set(harnessconfig.FieldUpdated, v) + return u +} + +// UpdateUpdated sets the "updated" field to the value that was provided on create. +func (u *HarnessConfigUpsert) UpdateUpdated() *HarnessConfigUpsert { + u.SetExcluded(harnessconfig.FieldUpdated) + return u +} + +// UpdateNewValues updates the mutable fields using the new values that were set on create except the ID field. +// Using this option is equivalent to using: +// +// client.HarnessConfig.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(harnessconfig.FieldID) +// }), +// ). +// Exec(ctx) +func (u *HarnessConfigUpsertOne) UpdateNewValues() *HarnessConfigUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + if _, exists := u.create.mutation.ID(); exists { + s.SetIgnore(harnessconfig.FieldID) + } + if _, exists := u.create.mutation.Created(); exists { + s.SetIgnore(harnessconfig.FieldCreated) + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.HarnessConfig.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *HarnessConfigUpsertOne) Ignore() *HarnessConfigUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *HarnessConfigUpsertOne) DoNothing() *HarnessConfigUpsertOne { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the HarnessConfigCreate.OnConflict +// documentation for more info. +func (u *HarnessConfigUpsertOne) Update(set func(*HarnessConfigUpsert)) *HarnessConfigUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&HarnessConfigUpsert{UpdateSet: update}) + })) + return u +} + +// SetName sets the "name" field. +func (u *HarnessConfigUpsertOne) SetName(v string) *HarnessConfigUpsertOne { + return u.Update(func(s *HarnessConfigUpsert) { + s.SetName(v) + }) +} + +// UpdateName sets the "name" field to the value that was provided on create. +func (u *HarnessConfigUpsertOne) UpdateName() *HarnessConfigUpsertOne { + return u.Update(func(s *HarnessConfigUpsert) { + s.UpdateName() + }) +} + +// SetSlug sets the "slug" field. +func (u *HarnessConfigUpsertOne) SetSlug(v string) *HarnessConfigUpsertOne { + return u.Update(func(s *HarnessConfigUpsert) { + s.SetSlug(v) + }) +} + +// UpdateSlug sets the "slug" field to the value that was provided on create. +func (u *HarnessConfigUpsertOne) UpdateSlug() *HarnessConfigUpsertOne { + return u.Update(func(s *HarnessConfigUpsert) { + s.UpdateSlug() + }) +} + +// SetDisplayName sets the "display_name" field. +func (u *HarnessConfigUpsertOne) SetDisplayName(v string) *HarnessConfigUpsertOne { + return u.Update(func(s *HarnessConfigUpsert) { + s.SetDisplayName(v) + }) +} + +// UpdateDisplayName sets the "display_name" field to the value that was provided on create. +func (u *HarnessConfigUpsertOne) UpdateDisplayName() *HarnessConfigUpsertOne { + return u.Update(func(s *HarnessConfigUpsert) { + s.UpdateDisplayName() + }) +} + +// ClearDisplayName clears the value of the "display_name" field. +func (u *HarnessConfigUpsertOne) ClearDisplayName() *HarnessConfigUpsertOne { + return u.Update(func(s *HarnessConfigUpsert) { + s.ClearDisplayName() + }) +} + +// SetDescription sets the "description" field. +func (u *HarnessConfigUpsertOne) SetDescription(v string) *HarnessConfigUpsertOne { + return u.Update(func(s *HarnessConfigUpsert) { + s.SetDescription(v) + }) +} + +// UpdateDescription sets the "description" field to the value that was provided on create. +func (u *HarnessConfigUpsertOne) UpdateDescription() *HarnessConfigUpsertOne { + return u.Update(func(s *HarnessConfigUpsert) { + s.UpdateDescription() + }) +} + +// ClearDescription clears the value of the "description" field. +func (u *HarnessConfigUpsertOne) ClearDescription() *HarnessConfigUpsertOne { + return u.Update(func(s *HarnessConfigUpsert) { + s.ClearDescription() + }) +} + +// SetHarness sets the "harness" field. +func (u *HarnessConfigUpsertOne) SetHarness(v string) *HarnessConfigUpsertOne { + return u.Update(func(s *HarnessConfigUpsert) { + s.SetHarness(v) + }) +} + +// UpdateHarness sets the "harness" field to the value that was provided on create. +func (u *HarnessConfigUpsertOne) UpdateHarness() *HarnessConfigUpsertOne { + return u.Update(func(s *HarnessConfigUpsert) { + s.UpdateHarness() + }) +} + +// SetConfig sets the "config" field. +func (u *HarnessConfigUpsertOne) SetConfig(v string) *HarnessConfigUpsertOne { + return u.Update(func(s *HarnessConfigUpsert) { + s.SetConfig(v) + }) +} + +// UpdateConfig sets the "config" field to the value that was provided on create. +func (u *HarnessConfigUpsertOne) UpdateConfig() *HarnessConfigUpsertOne { + return u.Update(func(s *HarnessConfigUpsert) { + s.UpdateConfig() + }) +} + +// ClearConfig clears the value of the "config" field. +func (u *HarnessConfigUpsertOne) ClearConfig() *HarnessConfigUpsertOne { + return u.Update(func(s *HarnessConfigUpsert) { + s.ClearConfig() + }) +} + +// SetContentHash sets the "content_hash" field. +func (u *HarnessConfigUpsertOne) SetContentHash(v string) *HarnessConfigUpsertOne { + return u.Update(func(s *HarnessConfigUpsert) { + s.SetContentHash(v) + }) +} + +// UpdateContentHash sets the "content_hash" field to the value that was provided on create. +func (u *HarnessConfigUpsertOne) UpdateContentHash() *HarnessConfigUpsertOne { + return u.Update(func(s *HarnessConfigUpsert) { + s.UpdateContentHash() + }) +} + +// ClearContentHash clears the value of the "content_hash" field. +func (u *HarnessConfigUpsertOne) ClearContentHash() *HarnessConfigUpsertOne { + return u.Update(func(s *HarnessConfigUpsert) { + s.ClearContentHash() + }) +} + +// SetScope sets the "scope" field. +func (u *HarnessConfigUpsertOne) SetScope(v string) *HarnessConfigUpsertOne { + return u.Update(func(s *HarnessConfigUpsert) { + s.SetScope(v) + }) +} + +// UpdateScope sets the "scope" field to the value that was provided on create. +func (u *HarnessConfigUpsertOne) UpdateScope() *HarnessConfigUpsertOne { + return u.Update(func(s *HarnessConfigUpsert) { + s.UpdateScope() + }) +} + +// SetScopeID sets the "scope_id" field. +func (u *HarnessConfigUpsertOne) SetScopeID(v string) *HarnessConfigUpsertOne { + return u.Update(func(s *HarnessConfigUpsert) { + s.SetScopeID(v) + }) +} + +// UpdateScopeID sets the "scope_id" field to the value that was provided on create. +func (u *HarnessConfigUpsertOne) UpdateScopeID() *HarnessConfigUpsertOne { + return u.Update(func(s *HarnessConfigUpsert) { + s.UpdateScopeID() + }) +} + +// ClearScopeID clears the value of the "scope_id" field. +func (u *HarnessConfigUpsertOne) ClearScopeID() *HarnessConfigUpsertOne { + return u.Update(func(s *HarnessConfigUpsert) { + s.ClearScopeID() + }) +} + +// SetStorageURI sets the "storage_uri" field. +func (u *HarnessConfigUpsertOne) SetStorageURI(v string) *HarnessConfigUpsertOne { + return u.Update(func(s *HarnessConfigUpsert) { + s.SetStorageURI(v) + }) +} + +// UpdateStorageURI sets the "storage_uri" field to the value that was provided on create. +func (u *HarnessConfigUpsertOne) UpdateStorageURI() *HarnessConfigUpsertOne { + return u.Update(func(s *HarnessConfigUpsert) { + s.UpdateStorageURI() + }) +} + +// ClearStorageURI clears the value of the "storage_uri" field. +func (u *HarnessConfigUpsertOne) ClearStorageURI() *HarnessConfigUpsertOne { + return u.Update(func(s *HarnessConfigUpsert) { + s.ClearStorageURI() + }) +} + +// SetStorageBucket sets the "storage_bucket" field. +func (u *HarnessConfigUpsertOne) SetStorageBucket(v string) *HarnessConfigUpsertOne { + return u.Update(func(s *HarnessConfigUpsert) { + s.SetStorageBucket(v) + }) +} + +// UpdateStorageBucket sets the "storage_bucket" field to the value that was provided on create. +func (u *HarnessConfigUpsertOne) UpdateStorageBucket() *HarnessConfigUpsertOne { + return u.Update(func(s *HarnessConfigUpsert) { + s.UpdateStorageBucket() + }) +} + +// ClearStorageBucket clears the value of the "storage_bucket" field. +func (u *HarnessConfigUpsertOne) ClearStorageBucket() *HarnessConfigUpsertOne { + return u.Update(func(s *HarnessConfigUpsert) { + s.ClearStorageBucket() + }) +} + +// SetStoragePath sets the "storage_path" field. +func (u *HarnessConfigUpsertOne) SetStoragePath(v string) *HarnessConfigUpsertOne { + return u.Update(func(s *HarnessConfigUpsert) { + s.SetStoragePath(v) + }) +} + +// UpdateStoragePath sets the "storage_path" field to the value that was provided on create. +func (u *HarnessConfigUpsertOne) UpdateStoragePath() *HarnessConfigUpsertOne { + return u.Update(func(s *HarnessConfigUpsert) { + s.UpdateStoragePath() + }) +} + +// ClearStoragePath clears the value of the "storage_path" field. +func (u *HarnessConfigUpsertOne) ClearStoragePath() *HarnessConfigUpsertOne { + return u.Update(func(s *HarnessConfigUpsert) { + s.ClearStoragePath() + }) +} + +// SetFiles sets the "files" field. +func (u *HarnessConfigUpsertOne) SetFiles(v string) *HarnessConfigUpsertOne { + return u.Update(func(s *HarnessConfigUpsert) { + s.SetFiles(v) + }) +} + +// UpdateFiles sets the "files" field to the value that was provided on create. +func (u *HarnessConfigUpsertOne) UpdateFiles() *HarnessConfigUpsertOne { + return u.Update(func(s *HarnessConfigUpsert) { + s.UpdateFiles() + }) +} + +// ClearFiles clears the value of the "files" field. +func (u *HarnessConfigUpsertOne) ClearFiles() *HarnessConfigUpsertOne { + return u.Update(func(s *HarnessConfigUpsert) { + s.ClearFiles() + }) +} + +// SetLocked sets the "locked" field. +func (u *HarnessConfigUpsertOne) SetLocked(v bool) *HarnessConfigUpsertOne { + return u.Update(func(s *HarnessConfigUpsert) { + s.SetLocked(v) + }) +} + +// UpdateLocked sets the "locked" field to the value that was provided on create. +func (u *HarnessConfigUpsertOne) UpdateLocked() *HarnessConfigUpsertOne { + return u.Update(func(s *HarnessConfigUpsert) { + s.UpdateLocked() + }) +} + +// SetStatus sets the "status" field. +func (u *HarnessConfigUpsertOne) SetStatus(v harnessconfig.Status) *HarnessConfigUpsertOne { + return u.Update(func(s *HarnessConfigUpsert) { + s.SetStatus(v) + }) +} + +// UpdateStatus sets the "status" field to the value that was provided on create. +func (u *HarnessConfigUpsertOne) UpdateStatus() *HarnessConfigUpsertOne { + return u.Update(func(s *HarnessConfigUpsert) { + s.UpdateStatus() + }) +} + +// SetOwnerID sets the "owner_id" field. +func (u *HarnessConfigUpsertOne) SetOwnerID(v string) *HarnessConfigUpsertOne { + return u.Update(func(s *HarnessConfigUpsert) { + s.SetOwnerID(v) + }) +} + +// UpdateOwnerID sets the "owner_id" field to the value that was provided on create. +func (u *HarnessConfigUpsertOne) UpdateOwnerID() *HarnessConfigUpsertOne { + return u.Update(func(s *HarnessConfigUpsert) { + s.UpdateOwnerID() + }) +} + +// ClearOwnerID clears the value of the "owner_id" field. +func (u *HarnessConfigUpsertOne) ClearOwnerID() *HarnessConfigUpsertOne { + return u.Update(func(s *HarnessConfigUpsert) { + s.ClearOwnerID() + }) +} + +// SetCreatedBy sets the "created_by" field. +func (u *HarnessConfigUpsertOne) SetCreatedBy(v string) *HarnessConfigUpsertOne { + return u.Update(func(s *HarnessConfigUpsert) { + s.SetCreatedBy(v) + }) +} + +// UpdateCreatedBy sets the "created_by" field to the value that was provided on create. +func (u *HarnessConfigUpsertOne) UpdateCreatedBy() *HarnessConfigUpsertOne { + return u.Update(func(s *HarnessConfigUpsert) { + s.UpdateCreatedBy() + }) +} + +// ClearCreatedBy clears the value of the "created_by" field. +func (u *HarnessConfigUpsertOne) ClearCreatedBy() *HarnessConfigUpsertOne { + return u.Update(func(s *HarnessConfigUpsert) { + s.ClearCreatedBy() + }) +} + +// SetUpdatedBy sets the "updated_by" field. +func (u *HarnessConfigUpsertOne) SetUpdatedBy(v string) *HarnessConfigUpsertOne { + return u.Update(func(s *HarnessConfigUpsert) { + s.SetUpdatedBy(v) + }) +} + +// UpdateUpdatedBy sets the "updated_by" field to the value that was provided on create. +func (u *HarnessConfigUpsertOne) UpdateUpdatedBy() *HarnessConfigUpsertOne { + return u.Update(func(s *HarnessConfigUpsert) { + s.UpdateUpdatedBy() + }) +} + +// ClearUpdatedBy clears the value of the "updated_by" field. +func (u *HarnessConfigUpsertOne) ClearUpdatedBy() *HarnessConfigUpsertOne { + return u.Update(func(s *HarnessConfigUpsert) { + s.ClearUpdatedBy() + }) +} + +// SetVisibility sets the "visibility" field. +func (u *HarnessConfigUpsertOne) SetVisibility(v string) *HarnessConfigUpsertOne { + return u.Update(func(s *HarnessConfigUpsert) { + s.SetVisibility(v) + }) +} + +// UpdateVisibility sets the "visibility" field to the value that was provided on create. +func (u *HarnessConfigUpsertOne) UpdateVisibility() *HarnessConfigUpsertOne { + return u.Update(func(s *HarnessConfigUpsert) { + s.UpdateVisibility() + }) +} + +// SetUpdated sets the "updated" field. +func (u *HarnessConfigUpsertOne) SetUpdated(v time.Time) *HarnessConfigUpsertOne { + return u.Update(func(s *HarnessConfigUpsert) { + s.SetUpdated(v) + }) +} + +// UpdateUpdated sets the "updated" field to the value that was provided on create. +func (u *HarnessConfigUpsertOne) UpdateUpdated() *HarnessConfigUpsertOne { + return u.Update(func(s *HarnessConfigUpsert) { + s.UpdateUpdated() + }) +} + +// Exec executes the query. +func (u *HarnessConfigUpsertOne) Exec(ctx context.Context) error { + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for HarnessConfigCreate.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *HarnessConfigUpsertOne) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} + +// Exec executes the UPSERT query and returns the inserted/updated ID. +func (u *HarnessConfigUpsertOne) ID(ctx context.Context) (id uuid.UUID, err error) { + if u.create.driver.Dialect() == dialect.MySQL { + // In case of "ON CONFLICT", there is no way to get back non-numeric ID + // fields from the database since MySQL does not support the RETURNING clause. + return id, errors.New("ent: HarnessConfigUpsertOne.ID is not supported by MySQL driver. Use HarnessConfigUpsertOne.Exec instead") + } + node, err := u.create.Save(ctx) + if err != nil { + return id, err + } + return node.ID, nil +} + +// IDX is like ID, but panics if an error occurs. +func (u *HarnessConfigUpsertOne) IDX(ctx context.Context) uuid.UUID { + id, err := u.ID(ctx) + if err != nil { + panic(err) + } + return id +} + +// HarnessConfigCreateBulk is the builder for creating many HarnessConfig entities in bulk. +type HarnessConfigCreateBulk struct { + config + err error + builders []*HarnessConfigCreate + conflict []sql.ConflictOption +} + +// Save creates the HarnessConfig entities in the database. +func (_c *HarnessConfigCreateBulk) Save(ctx context.Context) ([]*HarnessConfig, error) { + if _c.err != nil { + return nil, _c.err + } + specs := make([]*sqlgraph.CreateSpec, len(_c.builders)) + nodes := make([]*HarnessConfig, len(_c.builders)) + mutators := make([]Mutator, len(_c.builders)) + for i := range _c.builders { + func(i int, root context.Context) { + builder := _c.builders[i] + builder.defaults() + var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { + mutation, ok := m.(*HarnessConfigMutation) + if !ok { + return nil, fmt.Errorf("unexpected mutation type %T", m) + } + if err := builder.check(); err != nil { + return nil, err + } + builder.mutation = mutation + var err error + nodes[i], specs[i] = builder.createSpec() + if i < len(mutators)-1 { + _, err = mutators[i+1].Mutate(root, _c.builders[i+1].mutation) + } else { + spec := &sqlgraph.BatchCreateSpec{Nodes: specs} + spec.OnConflict = _c.conflict + // Invoke the actual operation on the latest mutation in the chain. + if err = sqlgraph.BatchCreate(ctx, _c.driver, spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + } + } + if err != nil { + return nil, err + } + mutation.id = &nodes[i].ID + mutation.done = true + return nodes[i], nil + }) + for i := len(builder.hooks) - 1; i >= 0; i-- { + mut = builder.hooks[i](mut) + } + mutators[i] = mut + }(i, ctx) + } + if len(mutators) > 0 { + if _, err := mutators[0].Mutate(ctx, _c.builders[0].mutation); err != nil { + return nil, err + } + } + return nodes, nil +} + +// SaveX is like Save, but panics if an error occurs. +func (_c *HarnessConfigCreateBulk) SaveX(ctx context.Context) []*HarnessConfig { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *HarnessConfigCreateBulk) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *HarnessConfigCreateBulk) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.HarnessConfig.CreateBulk(builders...). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.HarnessConfigUpsert) { +// SetName(v+v). +// }). +// Exec(ctx) +func (_c *HarnessConfigCreateBulk) OnConflict(opts ...sql.ConflictOption) *HarnessConfigUpsertBulk { + _c.conflict = opts + return &HarnessConfigUpsertBulk{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.HarnessConfig.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *HarnessConfigCreateBulk) OnConflictColumns(columns ...string) *HarnessConfigUpsertBulk { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &HarnessConfigUpsertBulk{ + create: _c, + } +} + +// HarnessConfigUpsertBulk is the builder for "upsert"-ing +// a bulk of HarnessConfig nodes. +type HarnessConfigUpsertBulk struct { + create *HarnessConfigCreateBulk +} + +// UpdateNewValues updates the mutable fields using the new values that +// were set on create. Using this option is equivalent to using: +// +// client.HarnessConfig.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(harnessconfig.FieldID) +// }), +// ). +// Exec(ctx) +func (u *HarnessConfigUpsertBulk) UpdateNewValues() *HarnessConfigUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + for _, b := range u.create.builders { + if _, exists := b.mutation.ID(); exists { + s.SetIgnore(harnessconfig.FieldID) + } + if _, exists := b.mutation.Created(); exists { + s.SetIgnore(harnessconfig.FieldCreated) + } + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.HarnessConfig.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *HarnessConfigUpsertBulk) Ignore() *HarnessConfigUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *HarnessConfigUpsertBulk) DoNothing() *HarnessConfigUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the HarnessConfigCreateBulk.OnConflict +// documentation for more info. +func (u *HarnessConfigUpsertBulk) Update(set func(*HarnessConfigUpsert)) *HarnessConfigUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&HarnessConfigUpsert{UpdateSet: update}) + })) + return u +} + +// SetName sets the "name" field. +func (u *HarnessConfigUpsertBulk) SetName(v string) *HarnessConfigUpsertBulk { + return u.Update(func(s *HarnessConfigUpsert) { + s.SetName(v) + }) +} + +// UpdateName sets the "name" field to the value that was provided on create. +func (u *HarnessConfigUpsertBulk) UpdateName() *HarnessConfigUpsertBulk { + return u.Update(func(s *HarnessConfigUpsert) { + s.UpdateName() + }) +} + +// SetSlug sets the "slug" field. +func (u *HarnessConfigUpsertBulk) SetSlug(v string) *HarnessConfigUpsertBulk { + return u.Update(func(s *HarnessConfigUpsert) { + s.SetSlug(v) + }) +} + +// UpdateSlug sets the "slug" field to the value that was provided on create. +func (u *HarnessConfigUpsertBulk) UpdateSlug() *HarnessConfigUpsertBulk { + return u.Update(func(s *HarnessConfigUpsert) { + s.UpdateSlug() + }) +} + +// SetDisplayName sets the "display_name" field. +func (u *HarnessConfigUpsertBulk) SetDisplayName(v string) *HarnessConfigUpsertBulk { + return u.Update(func(s *HarnessConfigUpsert) { + s.SetDisplayName(v) + }) +} + +// UpdateDisplayName sets the "display_name" field to the value that was provided on create. +func (u *HarnessConfigUpsertBulk) UpdateDisplayName() *HarnessConfigUpsertBulk { + return u.Update(func(s *HarnessConfigUpsert) { + s.UpdateDisplayName() + }) +} + +// ClearDisplayName clears the value of the "display_name" field. +func (u *HarnessConfigUpsertBulk) ClearDisplayName() *HarnessConfigUpsertBulk { + return u.Update(func(s *HarnessConfigUpsert) { + s.ClearDisplayName() + }) +} + +// SetDescription sets the "description" field. +func (u *HarnessConfigUpsertBulk) SetDescription(v string) *HarnessConfigUpsertBulk { + return u.Update(func(s *HarnessConfigUpsert) { + s.SetDescription(v) + }) +} + +// UpdateDescription sets the "description" field to the value that was provided on create. +func (u *HarnessConfigUpsertBulk) UpdateDescription() *HarnessConfigUpsertBulk { + return u.Update(func(s *HarnessConfigUpsert) { + s.UpdateDescription() + }) +} + +// ClearDescription clears the value of the "description" field. +func (u *HarnessConfigUpsertBulk) ClearDescription() *HarnessConfigUpsertBulk { + return u.Update(func(s *HarnessConfigUpsert) { + s.ClearDescription() + }) +} + +// SetHarness sets the "harness" field. +func (u *HarnessConfigUpsertBulk) SetHarness(v string) *HarnessConfigUpsertBulk { + return u.Update(func(s *HarnessConfigUpsert) { + s.SetHarness(v) + }) +} + +// UpdateHarness sets the "harness" field to the value that was provided on create. +func (u *HarnessConfigUpsertBulk) UpdateHarness() *HarnessConfigUpsertBulk { + return u.Update(func(s *HarnessConfigUpsert) { + s.UpdateHarness() + }) +} + +// SetConfig sets the "config" field. +func (u *HarnessConfigUpsertBulk) SetConfig(v string) *HarnessConfigUpsertBulk { + return u.Update(func(s *HarnessConfigUpsert) { + s.SetConfig(v) + }) +} + +// UpdateConfig sets the "config" field to the value that was provided on create. +func (u *HarnessConfigUpsertBulk) UpdateConfig() *HarnessConfigUpsertBulk { + return u.Update(func(s *HarnessConfigUpsert) { + s.UpdateConfig() + }) +} + +// ClearConfig clears the value of the "config" field. +func (u *HarnessConfigUpsertBulk) ClearConfig() *HarnessConfigUpsertBulk { + return u.Update(func(s *HarnessConfigUpsert) { + s.ClearConfig() + }) +} + +// SetContentHash sets the "content_hash" field. +func (u *HarnessConfigUpsertBulk) SetContentHash(v string) *HarnessConfigUpsertBulk { + return u.Update(func(s *HarnessConfigUpsert) { + s.SetContentHash(v) + }) +} + +// UpdateContentHash sets the "content_hash" field to the value that was provided on create. +func (u *HarnessConfigUpsertBulk) UpdateContentHash() *HarnessConfigUpsertBulk { + return u.Update(func(s *HarnessConfigUpsert) { + s.UpdateContentHash() + }) +} + +// ClearContentHash clears the value of the "content_hash" field. +func (u *HarnessConfigUpsertBulk) ClearContentHash() *HarnessConfigUpsertBulk { + return u.Update(func(s *HarnessConfigUpsert) { + s.ClearContentHash() + }) +} + +// SetScope sets the "scope" field. +func (u *HarnessConfigUpsertBulk) SetScope(v string) *HarnessConfigUpsertBulk { + return u.Update(func(s *HarnessConfigUpsert) { + s.SetScope(v) + }) +} + +// UpdateScope sets the "scope" field to the value that was provided on create. +func (u *HarnessConfigUpsertBulk) UpdateScope() *HarnessConfigUpsertBulk { + return u.Update(func(s *HarnessConfigUpsert) { + s.UpdateScope() + }) +} + +// SetScopeID sets the "scope_id" field. +func (u *HarnessConfigUpsertBulk) SetScopeID(v string) *HarnessConfigUpsertBulk { + return u.Update(func(s *HarnessConfigUpsert) { + s.SetScopeID(v) + }) +} + +// UpdateScopeID sets the "scope_id" field to the value that was provided on create. +func (u *HarnessConfigUpsertBulk) UpdateScopeID() *HarnessConfigUpsertBulk { + return u.Update(func(s *HarnessConfigUpsert) { + s.UpdateScopeID() + }) +} + +// ClearScopeID clears the value of the "scope_id" field. +func (u *HarnessConfigUpsertBulk) ClearScopeID() *HarnessConfigUpsertBulk { + return u.Update(func(s *HarnessConfigUpsert) { + s.ClearScopeID() + }) +} + +// SetStorageURI sets the "storage_uri" field. +func (u *HarnessConfigUpsertBulk) SetStorageURI(v string) *HarnessConfigUpsertBulk { + return u.Update(func(s *HarnessConfigUpsert) { + s.SetStorageURI(v) + }) +} + +// UpdateStorageURI sets the "storage_uri" field to the value that was provided on create. +func (u *HarnessConfigUpsertBulk) UpdateStorageURI() *HarnessConfigUpsertBulk { + return u.Update(func(s *HarnessConfigUpsert) { + s.UpdateStorageURI() + }) +} + +// ClearStorageURI clears the value of the "storage_uri" field. +func (u *HarnessConfigUpsertBulk) ClearStorageURI() *HarnessConfigUpsertBulk { + return u.Update(func(s *HarnessConfigUpsert) { + s.ClearStorageURI() + }) +} + +// SetStorageBucket sets the "storage_bucket" field. +func (u *HarnessConfigUpsertBulk) SetStorageBucket(v string) *HarnessConfigUpsertBulk { + return u.Update(func(s *HarnessConfigUpsert) { + s.SetStorageBucket(v) + }) +} + +// UpdateStorageBucket sets the "storage_bucket" field to the value that was provided on create. +func (u *HarnessConfigUpsertBulk) UpdateStorageBucket() *HarnessConfigUpsertBulk { + return u.Update(func(s *HarnessConfigUpsert) { + s.UpdateStorageBucket() + }) +} + +// ClearStorageBucket clears the value of the "storage_bucket" field. +func (u *HarnessConfigUpsertBulk) ClearStorageBucket() *HarnessConfigUpsertBulk { + return u.Update(func(s *HarnessConfigUpsert) { + s.ClearStorageBucket() + }) +} + +// SetStoragePath sets the "storage_path" field. +func (u *HarnessConfigUpsertBulk) SetStoragePath(v string) *HarnessConfigUpsertBulk { + return u.Update(func(s *HarnessConfigUpsert) { + s.SetStoragePath(v) + }) +} + +// UpdateStoragePath sets the "storage_path" field to the value that was provided on create. +func (u *HarnessConfigUpsertBulk) UpdateStoragePath() *HarnessConfigUpsertBulk { + return u.Update(func(s *HarnessConfigUpsert) { + s.UpdateStoragePath() + }) +} + +// ClearStoragePath clears the value of the "storage_path" field. +func (u *HarnessConfigUpsertBulk) ClearStoragePath() *HarnessConfigUpsertBulk { + return u.Update(func(s *HarnessConfigUpsert) { + s.ClearStoragePath() + }) +} + +// SetFiles sets the "files" field. +func (u *HarnessConfigUpsertBulk) SetFiles(v string) *HarnessConfigUpsertBulk { + return u.Update(func(s *HarnessConfigUpsert) { + s.SetFiles(v) + }) +} + +// UpdateFiles sets the "files" field to the value that was provided on create. +func (u *HarnessConfigUpsertBulk) UpdateFiles() *HarnessConfigUpsertBulk { + return u.Update(func(s *HarnessConfigUpsert) { + s.UpdateFiles() + }) +} + +// ClearFiles clears the value of the "files" field. +func (u *HarnessConfigUpsertBulk) ClearFiles() *HarnessConfigUpsertBulk { + return u.Update(func(s *HarnessConfigUpsert) { + s.ClearFiles() + }) +} + +// SetLocked sets the "locked" field. +func (u *HarnessConfigUpsertBulk) SetLocked(v bool) *HarnessConfigUpsertBulk { + return u.Update(func(s *HarnessConfigUpsert) { + s.SetLocked(v) + }) +} + +// UpdateLocked sets the "locked" field to the value that was provided on create. +func (u *HarnessConfigUpsertBulk) UpdateLocked() *HarnessConfigUpsertBulk { + return u.Update(func(s *HarnessConfigUpsert) { + s.UpdateLocked() + }) +} + +// SetStatus sets the "status" field. +func (u *HarnessConfigUpsertBulk) SetStatus(v harnessconfig.Status) *HarnessConfigUpsertBulk { + return u.Update(func(s *HarnessConfigUpsert) { + s.SetStatus(v) + }) +} + +// UpdateStatus sets the "status" field to the value that was provided on create. +func (u *HarnessConfigUpsertBulk) UpdateStatus() *HarnessConfigUpsertBulk { + return u.Update(func(s *HarnessConfigUpsert) { + s.UpdateStatus() + }) +} + +// SetOwnerID sets the "owner_id" field. +func (u *HarnessConfigUpsertBulk) SetOwnerID(v string) *HarnessConfigUpsertBulk { + return u.Update(func(s *HarnessConfigUpsert) { + s.SetOwnerID(v) + }) +} + +// UpdateOwnerID sets the "owner_id" field to the value that was provided on create. +func (u *HarnessConfigUpsertBulk) UpdateOwnerID() *HarnessConfigUpsertBulk { + return u.Update(func(s *HarnessConfigUpsert) { + s.UpdateOwnerID() + }) +} + +// ClearOwnerID clears the value of the "owner_id" field. +func (u *HarnessConfigUpsertBulk) ClearOwnerID() *HarnessConfigUpsertBulk { + return u.Update(func(s *HarnessConfigUpsert) { + s.ClearOwnerID() + }) +} + +// SetCreatedBy sets the "created_by" field. +func (u *HarnessConfigUpsertBulk) SetCreatedBy(v string) *HarnessConfigUpsertBulk { + return u.Update(func(s *HarnessConfigUpsert) { + s.SetCreatedBy(v) + }) +} + +// UpdateCreatedBy sets the "created_by" field to the value that was provided on create. +func (u *HarnessConfigUpsertBulk) UpdateCreatedBy() *HarnessConfigUpsertBulk { + return u.Update(func(s *HarnessConfigUpsert) { + s.UpdateCreatedBy() + }) +} + +// ClearCreatedBy clears the value of the "created_by" field. +func (u *HarnessConfigUpsertBulk) ClearCreatedBy() *HarnessConfigUpsertBulk { + return u.Update(func(s *HarnessConfigUpsert) { + s.ClearCreatedBy() + }) +} + +// SetUpdatedBy sets the "updated_by" field. +func (u *HarnessConfigUpsertBulk) SetUpdatedBy(v string) *HarnessConfigUpsertBulk { + return u.Update(func(s *HarnessConfigUpsert) { + s.SetUpdatedBy(v) + }) +} + +// UpdateUpdatedBy sets the "updated_by" field to the value that was provided on create. +func (u *HarnessConfigUpsertBulk) UpdateUpdatedBy() *HarnessConfigUpsertBulk { + return u.Update(func(s *HarnessConfigUpsert) { + s.UpdateUpdatedBy() + }) +} + +// ClearUpdatedBy clears the value of the "updated_by" field. +func (u *HarnessConfigUpsertBulk) ClearUpdatedBy() *HarnessConfigUpsertBulk { + return u.Update(func(s *HarnessConfigUpsert) { + s.ClearUpdatedBy() + }) +} + +// SetVisibility sets the "visibility" field. +func (u *HarnessConfigUpsertBulk) SetVisibility(v string) *HarnessConfigUpsertBulk { + return u.Update(func(s *HarnessConfigUpsert) { + s.SetVisibility(v) + }) +} + +// UpdateVisibility sets the "visibility" field to the value that was provided on create. +func (u *HarnessConfigUpsertBulk) UpdateVisibility() *HarnessConfigUpsertBulk { + return u.Update(func(s *HarnessConfigUpsert) { + s.UpdateVisibility() + }) +} + +// SetUpdated sets the "updated" field. +func (u *HarnessConfigUpsertBulk) SetUpdated(v time.Time) *HarnessConfigUpsertBulk { + return u.Update(func(s *HarnessConfigUpsert) { + s.SetUpdated(v) + }) +} + +// UpdateUpdated sets the "updated" field to the value that was provided on create. +func (u *HarnessConfigUpsertBulk) UpdateUpdated() *HarnessConfigUpsertBulk { + return u.Update(func(s *HarnessConfigUpsert) { + s.UpdateUpdated() + }) +} + +// Exec executes the query. +func (u *HarnessConfigUpsertBulk) Exec(ctx context.Context) error { + if u.create.err != nil { + return u.create.err + } + for i, b := range u.create.builders { + if len(b.conflict) != 0 { + return fmt.Errorf("ent: OnConflict was set for builder %d. Set it on the HarnessConfigCreateBulk instead", i) + } + } + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for HarnessConfigCreateBulk.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *HarnessConfigUpsertBulk) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/pkg/ent/harnessconfig_delete.go b/pkg/ent/harnessconfig_delete.go new file mode 100644 index 000000000..1d63f7f2b --- /dev/null +++ b/pkg/ent/harnessconfig_delete.go @@ -0,0 +1,88 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/harnessconfig" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" +) + +// HarnessConfigDelete is the builder for deleting a HarnessConfig entity. +type HarnessConfigDelete struct { + config + hooks []Hook + mutation *HarnessConfigMutation +} + +// Where appends a list predicates to the HarnessConfigDelete builder. +func (_d *HarnessConfigDelete) Where(ps ...predicate.HarnessConfig) *HarnessConfigDelete { + _d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (_d *HarnessConfigDelete) Exec(ctx context.Context) (int, error) { + return withHooks(ctx, _d.sqlExec, _d.mutation, _d.hooks) +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *HarnessConfigDelete) ExecX(ctx context.Context) int { + n, err := _d.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (_d *HarnessConfigDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(harnessconfig.Table, sqlgraph.NewFieldSpec(harnessconfig.FieldID, field.TypeUUID)) + if ps := _d.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + affected, err := sqlgraph.DeleteNodes(ctx, _d.driver, _spec) + if err != nil && sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + _d.mutation.done = true + return affected, err +} + +// HarnessConfigDeleteOne is the builder for deleting a single HarnessConfig entity. +type HarnessConfigDeleteOne struct { + _d *HarnessConfigDelete +} + +// Where appends a list predicates to the HarnessConfigDelete builder. +func (_d *HarnessConfigDeleteOne) Where(ps ...predicate.HarnessConfig) *HarnessConfigDeleteOne { + _d._d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query. +func (_d *HarnessConfigDeleteOne) Exec(ctx context.Context) error { + n, err := _d._d.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{harnessconfig.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *HarnessConfigDeleteOne) ExecX(ctx context.Context) { + if err := _d.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/pkg/ent/harnessconfig_query.go b/pkg/ent/harnessconfig_query.go new file mode 100644 index 000000000..c297ad239 --- /dev/null +++ b/pkg/ent/harnessconfig_query.go @@ -0,0 +1,565 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "fmt" + "math" + + "entgo.io/ent" + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/harnessconfig" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/google/uuid" +) + +// HarnessConfigQuery is the builder for querying HarnessConfig entities. +type HarnessConfigQuery struct { + config + ctx *QueryContext + order []harnessconfig.OrderOption + inters []Interceptor + predicates []predicate.HarnessConfig + modifiers []func(*sql.Selector) + // intermediate query (i.e. traversal path). + sql *sql.Selector + path func(context.Context) (*sql.Selector, error) +} + +// Where adds a new predicate for the HarnessConfigQuery builder. +func (_q *HarnessConfigQuery) Where(ps ...predicate.HarnessConfig) *HarnessConfigQuery { + _q.predicates = append(_q.predicates, ps...) + return _q +} + +// Limit the number of records to be returned by this query. +func (_q *HarnessConfigQuery) Limit(limit int) *HarnessConfigQuery { + _q.ctx.Limit = &limit + return _q +} + +// Offset to start from. +func (_q *HarnessConfigQuery) Offset(offset int) *HarnessConfigQuery { + _q.ctx.Offset = &offset + return _q +} + +// Unique configures the query builder to filter duplicate records on query. +// By default, unique is set to true, and can be disabled using this method. +func (_q *HarnessConfigQuery) Unique(unique bool) *HarnessConfigQuery { + _q.ctx.Unique = &unique + return _q +} + +// Order specifies how the records should be ordered. +func (_q *HarnessConfigQuery) Order(o ...harnessconfig.OrderOption) *HarnessConfigQuery { + _q.order = append(_q.order, o...) + return _q +} + +// First returns the first HarnessConfig entity from the query. +// Returns a *NotFoundError when no HarnessConfig was found. +func (_q *HarnessConfigQuery) First(ctx context.Context) (*HarnessConfig, error) { + nodes, err := _q.Limit(1).All(setContextOp(ctx, _q.ctx, ent.OpQueryFirst)) + if err != nil { + return nil, err + } + if len(nodes) == 0 { + return nil, &NotFoundError{harnessconfig.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (_q *HarnessConfigQuery) FirstX(ctx context.Context) *HarnessConfig { + node, err := _q.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// FirstID returns the first HarnessConfig ID from the query. +// Returns a *NotFoundError when no HarnessConfig ID was found. +func (_q *HarnessConfigQuery) FirstID(ctx context.Context) (id uuid.UUID, err error) { + var ids []uuid.UUID + if ids, err = _q.Limit(1).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryFirstID)); err != nil { + return + } + if len(ids) == 0 { + err = &NotFoundError{harnessconfig.Label} + return + } + return ids[0], nil +} + +// FirstIDX is like FirstID, but panics if an error occurs. +func (_q *HarnessConfigQuery) FirstIDX(ctx context.Context) uuid.UUID { + id, err := _q.FirstID(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return id +} + +// Only returns a single HarnessConfig entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one HarnessConfig entity is found. +// Returns a *NotFoundError when no HarnessConfig entities are found. +func (_q *HarnessConfigQuery) Only(ctx context.Context) (*HarnessConfig, error) { + nodes, err := _q.Limit(2).All(setContextOp(ctx, _q.ctx, ent.OpQueryOnly)) + if err != nil { + return nil, err + } + switch len(nodes) { + case 1: + return nodes[0], nil + case 0: + return nil, &NotFoundError{harnessconfig.Label} + default: + return nil, &NotSingularError{harnessconfig.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (_q *HarnessConfigQuery) OnlyX(ctx context.Context) *HarnessConfig { + node, err := _q.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// OnlyID is like Only, but returns the only HarnessConfig ID in the query. +// Returns a *NotSingularError when more than one HarnessConfig ID is found. +// Returns a *NotFoundError when no entities are found. +func (_q *HarnessConfigQuery) OnlyID(ctx context.Context) (id uuid.UUID, err error) { + var ids []uuid.UUID + if ids, err = _q.Limit(2).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryOnlyID)); err != nil { + return + } + switch len(ids) { + case 1: + id = ids[0] + case 0: + err = &NotFoundError{harnessconfig.Label} + default: + err = &NotSingularError{harnessconfig.Label} + } + return +} + +// OnlyIDX is like OnlyID, but panics if an error occurs. +func (_q *HarnessConfigQuery) OnlyIDX(ctx context.Context) uuid.UUID { + id, err := _q.OnlyID(ctx) + if err != nil { + panic(err) + } + return id +} + +// All executes the query and returns a list of HarnessConfigs. +func (_q *HarnessConfigQuery) All(ctx context.Context) ([]*HarnessConfig, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryAll) + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*HarnessConfig, *HarnessConfigQuery]() + return withInterceptors[[]*HarnessConfig](ctx, _q, qr, _q.inters) +} + +// AllX is like All, but panics if an error occurs. +func (_q *HarnessConfigQuery) AllX(ctx context.Context) []*HarnessConfig { + nodes, err := _q.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// IDs executes the query and returns a list of HarnessConfig IDs. +func (_q *HarnessConfigQuery) IDs(ctx context.Context) (ids []uuid.UUID, err error) { + if _q.ctx.Unique == nil && _q.path != nil { + _q.Unique(true) + } + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryIDs) + if err = _q.Select(harnessconfig.FieldID).Scan(ctx, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// IDsX is like IDs, but panics if an error occurs. +func (_q *HarnessConfigQuery) IDsX(ctx context.Context) []uuid.UUID { + ids, err := _q.IDs(ctx) + if err != nil { + panic(err) + } + return ids +} + +// Count returns the count of the given query. +func (_q *HarnessConfigQuery) Count(ctx context.Context) (int, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryCount) + if err := _q.prepareQuery(ctx); err != nil { + return 0, err + } + return withInterceptors[int](ctx, _q, querierCount[*HarnessConfigQuery](), _q.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (_q *HarnessConfigQuery) CountX(ctx context.Context) int { + count, err := _q.Count(ctx) + if err != nil { + panic(err) + } + return count +} + +// Exist returns true if the query has elements in the graph. +func (_q *HarnessConfigQuery) Exist(ctx context.Context) (bool, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryExist) + switch _, err := _q.FirstID(ctx); { + case IsNotFound(err): + return false, nil + case err != nil: + return false, fmt.Errorf("ent: check existence: %w", err) + default: + return true, nil + } +} + +// ExistX is like Exist, but panics if an error occurs. +func (_q *HarnessConfigQuery) ExistX(ctx context.Context) bool { + exist, err := _q.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the HarnessConfigQuery builder, including all associated steps. It can be +// used to prepare common query builders and use them differently after the clone is made. +func (_q *HarnessConfigQuery) Clone() *HarnessConfigQuery { + if _q == nil { + return nil + } + return &HarnessConfigQuery{ + config: _q.config, + ctx: _q.ctx.Clone(), + order: append([]harnessconfig.OrderOption{}, _q.order...), + inters: append([]Interceptor{}, _q.inters...), + predicates: append([]predicate.HarnessConfig{}, _q.predicates...), + // clone intermediate query. + sql: _q.sql.Clone(), + path: _q.path, + } +} + +// GroupBy is used to group vertices by one or more fields/columns. +// It is often used with aggregate functions, like: count, max, mean, min, sum. +// +// Example: +// +// var v []struct { +// Name string `json:"name,omitempty"` +// Count int `json:"count,omitempty"` +// } +// +// client.HarnessConfig.Query(). +// GroupBy(harnessconfig.FieldName). +// Aggregate(ent.Count()). +// Scan(ctx, &v) +func (_q *HarnessConfigQuery) GroupBy(field string, fields ...string) *HarnessConfigGroupBy { + _q.ctx.Fields = append([]string{field}, fields...) + grbuild := &HarnessConfigGroupBy{build: _q} + grbuild.flds = &_q.ctx.Fields + grbuild.label = harnessconfig.Label + grbuild.scan = grbuild.Scan + return grbuild +} + +// Select allows the selection one or more fields/columns for the given query, +// instead of selecting all fields in the entity. +// +// Example: +// +// var v []struct { +// Name string `json:"name,omitempty"` +// } +// +// client.HarnessConfig.Query(). +// Select(harnessconfig.FieldName). +// Scan(ctx, &v) +func (_q *HarnessConfigQuery) Select(fields ...string) *HarnessConfigSelect { + _q.ctx.Fields = append(_q.ctx.Fields, fields...) + sbuild := &HarnessConfigSelect{HarnessConfigQuery: _q} + sbuild.label = harnessconfig.Label + sbuild.flds, sbuild.scan = &_q.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a HarnessConfigSelect configured with the given aggregations. +func (_q *HarnessConfigQuery) Aggregate(fns ...AggregateFunc) *HarnessConfigSelect { + return _q.Select().Aggregate(fns...) +} + +func (_q *HarnessConfigQuery) prepareQuery(ctx context.Context) error { + for _, inter := range _q.inters { + if inter == nil { + return fmt.Errorf("ent: uninitialized interceptor (forgotten import ent/runtime?)") + } + if trv, ok := inter.(Traverser); ok { + if err := trv.Traverse(ctx, _q); err != nil { + return err + } + } + } + for _, f := range _q.ctx.Fields { + if !harnessconfig.ValidColumn(f) { + return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + } + if _q.path != nil { + prev, err := _q.path(ctx) + if err != nil { + return err + } + _q.sql = prev + } + return nil +} + +func (_q *HarnessConfigQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*HarnessConfig, error) { + var ( + nodes = []*HarnessConfig{} + _spec = _q.querySpec() + ) + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*HarnessConfig).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &HarnessConfig{config: _q.config} + nodes = append(nodes, node) + return node.assignValues(columns, values) + } + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } + for i := range hooks { + hooks[i](ctx, _spec) + } + if err := sqlgraph.QueryNodes(ctx, _q.driver, _spec); err != nil { + return nil, err + } + if len(nodes) == 0 { + return nodes, nil + } + return nodes, nil +} + +func (_q *HarnessConfigQuery) sqlCount(ctx context.Context) (int, error) { + _spec := _q.querySpec() + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } + _spec.Node.Columns = _q.ctx.Fields + if len(_q.ctx.Fields) > 0 { + _spec.Unique = _q.ctx.Unique != nil && *_q.ctx.Unique + } + return sqlgraph.CountNodes(ctx, _q.driver, _spec) +} + +func (_q *HarnessConfigQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(harnessconfig.Table, harnessconfig.Columns, sqlgraph.NewFieldSpec(harnessconfig.FieldID, field.TypeUUID)) + _spec.From = _q.sql + if unique := _q.ctx.Unique; unique != nil { + _spec.Unique = *unique + } else if _q.path != nil { + _spec.Unique = true + } + if fields := _q.ctx.Fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, harnessconfig.FieldID) + for i := range fields { + if fields[i] != harnessconfig.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, fields[i]) + } + } + } + if ps := _q.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if limit := _q.ctx.Limit; limit != nil { + _spec.Limit = *limit + } + if offset := _q.ctx.Offset; offset != nil { + _spec.Offset = *offset + } + if ps := _q.order; len(ps) > 0 { + _spec.Order = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + return _spec +} + +func (_q *HarnessConfigQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(_q.driver.Dialect()) + t1 := builder.Table(harnessconfig.Table) + columns := _q.ctx.Fields + if len(columns) == 0 { + columns = harnessconfig.Columns + } + selector := builder.Select(t1.Columns(columns...)...).From(t1) + if _q.sql != nil { + selector = _q.sql + selector.Select(selector.Columns(columns...)...) + } + if _q.ctx.Unique != nil && *_q.ctx.Unique { + selector.Distinct() + } + for _, m := range _q.modifiers { + m(selector) + } + for _, p := range _q.predicates { + p(selector) + } + for _, p := range _q.order { + p(selector) + } + if offset := _q.ctx.Offset; offset != nil { + // limit is mandatory for offset clause. We start + // with default value, and override it below if needed. + selector.Offset(*offset).Limit(math.MaxInt32) + } + if limit := _q.ctx.Limit; limit != nil { + selector.Limit(*limit) + } + return selector +} + +// ForUpdate locks the selected rows against concurrent updates, and prevent them from being +// updated, deleted or "selected ... for update" by other sessions, until the transaction is +// either committed or rolled-back. +func (_q *HarnessConfigQuery) ForUpdate(opts ...sql.LockOption) *HarnessConfigQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForUpdate(opts...) + }) + return _q +} + +// ForShare behaves similarly to ForUpdate, except that it acquires a shared mode lock +// on any rows that are read. Other sessions can read the rows, but cannot modify them +// until your transaction commits. +func (_q *HarnessConfigQuery) ForShare(opts ...sql.LockOption) *HarnessConfigQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForShare(opts...) + }) + return _q +} + +// HarnessConfigGroupBy is the group-by builder for HarnessConfig entities. +type HarnessConfigGroupBy struct { + selector + build *HarnessConfigQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (_g *HarnessConfigGroupBy) Aggregate(fns ...AggregateFunc) *HarnessConfigGroupBy { + _g.fns = append(_g.fns, fns...) + return _g +} + +// Scan applies the selector query and scans the result into the given value. +func (_g *HarnessConfigGroupBy) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, _g.build.ctx, ent.OpQueryGroupBy) + if err := _g.build.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*HarnessConfigQuery, *HarnessConfigGroupBy](ctx, _g.build, _g, _g.build.inters, v) +} + +func (_g *HarnessConfigGroupBy) sqlScan(ctx context.Context, root *HarnessConfigQuery, v any) error { + selector := root.sqlQuery(ctx).Select() + aggregation := make([]string, 0, len(_g.fns)) + for _, fn := range _g.fns { + aggregation = append(aggregation, fn(selector)) + } + if len(selector.SelectedColumns()) == 0 { + columns := make([]string, 0, len(*_g.flds)+len(_g.fns)) + for _, f := range *_g.flds { + columns = append(columns, selector.C(f)) + } + columns = append(columns, aggregation...) + selector.Select(columns...) + } + selector.GroupBy(selector.Columns(*_g.flds...)...) + if err := selector.Err(); err != nil { + return err + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := _g.build.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} + +// HarnessConfigSelect is the builder for selecting fields of HarnessConfig entities. +type HarnessConfigSelect struct { + *HarnessConfigQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (_s *HarnessConfigSelect) Aggregate(fns ...AggregateFunc) *HarnessConfigSelect { + _s.fns = append(_s.fns, fns...) + return _s +} + +// Scan applies the selector query and scans the result into the given value. +func (_s *HarnessConfigSelect) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, _s.ctx, ent.OpQuerySelect) + if err := _s.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*HarnessConfigQuery, *HarnessConfigSelect](ctx, _s.HarnessConfigQuery, _s, _s.inters, v) +} + +func (_s *HarnessConfigSelect) sqlScan(ctx context.Context, root *HarnessConfigQuery, v any) error { + selector := root.sqlQuery(ctx) + aggregation := make([]string, 0, len(_s.fns)) + for _, fn := range _s.fns { + aggregation = append(aggregation, fn(selector)) + } + switch n := len(*_s.selector.flds); { + case n == 0 && len(aggregation) > 0: + selector.Select(aggregation...) + case n != 0 && len(aggregation) > 0: + selector.AppendSelect(aggregation...) + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := _s.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} diff --git a/pkg/ent/harnessconfig_update.go b/pkg/ent/harnessconfig_update.go new file mode 100644 index 000000000..3e2319187 --- /dev/null +++ b/pkg/ent/harnessconfig_update.go @@ -0,0 +1,1130 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/harnessconfig" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" +) + +// HarnessConfigUpdate is the builder for updating HarnessConfig entities. +type HarnessConfigUpdate struct { + config + hooks []Hook + mutation *HarnessConfigMutation +} + +// Where appends a list predicates to the HarnessConfigUpdate builder. +func (_u *HarnessConfigUpdate) Where(ps ...predicate.HarnessConfig) *HarnessConfigUpdate { + _u.mutation.Where(ps...) + return _u +} + +// SetName sets the "name" field. +func (_u *HarnessConfigUpdate) SetName(v string) *HarnessConfigUpdate { + _u.mutation.SetName(v) + return _u +} + +// SetNillableName sets the "name" field if the given value is not nil. +func (_u *HarnessConfigUpdate) SetNillableName(v *string) *HarnessConfigUpdate { + if v != nil { + _u.SetName(*v) + } + return _u +} + +// SetSlug sets the "slug" field. +func (_u *HarnessConfigUpdate) SetSlug(v string) *HarnessConfigUpdate { + _u.mutation.SetSlug(v) + return _u +} + +// SetNillableSlug sets the "slug" field if the given value is not nil. +func (_u *HarnessConfigUpdate) SetNillableSlug(v *string) *HarnessConfigUpdate { + if v != nil { + _u.SetSlug(*v) + } + return _u +} + +// SetDisplayName sets the "display_name" field. +func (_u *HarnessConfigUpdate) SetDisplayName(v string) *HarnessConfigUpdate { + _u.mutation.SetDisplayName(v) + return _u +} + +// SetNillableDisplayName sets the "display_name" field if the given value is not nil. +func (_u *HarnessConfigUpdate) SetNillableDisplayName(v *string) *HarnessConfigUpdate { + if v != nil { + _u.SetDisplayName(*v) + } + return _u +} + +// ClearDisplayName clears the value of the "display_name" field. +func (_u *HarnessConfigUpdate) ClearDisplayName() *HarnessConfigUpdate { + _u.mutation.ClearDisplayName() + return _u +} + +// SetDescription sets the "description" field. +func (_u *HarnessConfigUpdate) SetDescription(v string) *HarnessConfigUpdate { + _u.mutation.SetDescription(v) + return _u +} + +// SetNillableDescription sets the "description" field if the given value is not nil. +func (_u *HarnessConfigUpdate) SetNillableDescription(v *string) *HarnessConfigUpdate { + if v != nil { + _u.SetDescription(*v) + } + return _u +} + +// ClearDescription clears the value of the "description" field. +func (_u *HarnessConfigUpdate) ClearDescription() *HarnessConfigUpdate { + _u.mutation.ClearDescription() + return _u +} + +// SetHarness sets the "harness" field. +func (_u *HarnessConfigUpdate) SetHarness(v string) *HarnessConfigUpdate { + _u.mutation.SetHarness(v) + return _u +} + +// SetNillableHarness sets the "harness" field if the given value is not nil. +func (_u *HarnessConfigUpdate) SetNillableHarness(v *string) *HarnessConfigUpdate { + if v != nil { + _u.SetHarness(*v) + } + return _u +} + +// SetConfig sets the "config" field. +func (_u *HarnessConfigUpdate) SetConfig(v string) *HarnessConfigUpdate { + _u.mutation.SetConfig(v) + return _u +} + +// SetNillableConfig sets the "config" field if the given value is not nil. +func (_u *HarnessConfigUpdate) SetNillableConfig(v *string) *HarnessConfigUpdate { + if v != nil { + _u.SetConfig(*v) + } + return _u +} + +// ClearConfig clears the value of the "config" field. +func (_u *HarnessConfigUpdate) ClearConfig() *HarnessConfigUpdate { + _u.mutation.ClearConfig() + return _u +} + +// SetContentHash sets the "content_hash" field. +func (_u *HarnessConfigUpdate) SetContentHash(v string) *HarnessConfigUpdate { + _u.mutation.SetContentHash(v) + return _u +} + +// SetNillableContentHash sets the "content_hash" field if the given value is not nil. +func (_u *HarnessConfigUpdate) SetNillableContentHash(v *string) *HarnessConfigUpdate { + if v != nil { + _u.SetContentHash(*v) + } + return _u +} + +// ClearContentHash clears the value of the "content_hash" field. +func (_u *HarnessConfigUpdate) ClearContentHash() *HarnessConfigUpdate { + _u.mutation.ClearContentHash() + return _u +} + +// SetScope sets the "scope" field. +func (_u *HarnessConfigUpdate) SetScope(v string) *HarnessConfigUpdate { + _u.mutation.SetScope(v) + return _u +} + +// SetNillableScope sets the "scope" field if the given value is not nil. +func (_u *HarnessConfigUpdate) SetNillableScope(v *string) *HarnessConfigUpdate { + if v != nil { + _u.SetScope(*v) + } + return _u +} + +// SetScopeID sets the "scope_id" field. +func (_u *HarnessConfigUpdate) SetScopeID(v string) *HarnessConfigUpdate { + _u.mutation.SetScopeID(v) + return _u +} + +// SetNillableScopeID sets the "scope_id" field if the given value is not nil. +func (_u *HarnessConfigUpdate) SetNillableScopeID(v *string) *HarnessConfigUpdate { + if v != nil { + _u.SetScopeID(*v) + } + return _u +} + +// ClearScopeID clears the value of the "scope_id" field. +func (_u *HarnessConfigUpdate) ClearScopeID() *HarnessConfigUpdate { + _u.mutation.ClearScopeID() + return _u +} + +// SetStorageURI sets the "storage_uri" field. +func (_u *HarnessConfigUpdate) SetStorageURI(v string) *HarnessConfigUpdate { + _u.mutation.SetStorageURI(v) + return _u +} + +// SetNillableStorageURI sets the "storage_uri" field if the given value is not nil. +func (_u *HarnessConfigUpdate) SetNillableStorageURI(v *string) *HarnessConfigUpdate { + if v != nil { + _u.SetStorageURI(*v) + } + return _u +} + +// ClearStorageURI clears the value of the "storage_uri" field. +func (_u *HarnessConfigUpdate) ClearStorageURI() *HarnessConfigUpdate { + _u.mutation.ClearStorageURI() + return _u +} + +// SetStorageBucket sets the "storage_bucket" field. +func (_u *HarnessConfigUpdate) SetStorageBucket(v string) *HarnessConfigUpdate { + _u.mutation.SetStorageBucket(v) + return _u +} + +// SetNillableStorageBucket sets the "storage_bucket" field if the given value is not nil. +func (_u *HarnessConfigUpdate) SetNillableStorageBucket(v *string) *HarnessConfigUpdate { + if v != nil { + _u.SetStorageBucket(*v) + } + return _u +} + +// ClearStorageBucket clears the value of the "storage_bucket" field. +func (_u *HarnessConfigUpdate) ClearStorageBucket() *HarnessConfigUpdate { + _u.mutation.ClearStorageBucket() + return _u +} + +// SetStoragePath sets the "storage_path" field. +func (_u *HarnessConfigUpdate) SetStoragePath(v string) *HarnessConfigUpdate { + _u.mutation.SetStoragePath(v) + return _u +} + +// SetNillableStoragePath sets the "storage_path" field if the given value is not nil. +func (_u *HarnessConfigUpdate) SetNillableStoragePath(v *string) *HarnessConfigUpdate { + if v != nil { + _u.SetStoragePath(*v) + } + return _u +} + +// ClearStoragePath clears the value of the "storage_path" field. +func (_u *HarnessConfigUpdate) ClearStoragePath() *HarnessConfigUpdate { + _u.mutation.ClearStoragePath() + return _u +} + +// SetFiles sets the "files" field. +func (_u *HarnessConfigUpdate) SetFiles(v string) *HarnessConfigUpdate { + _u.mutation.SetFiles(v) + return _u +} + +// SetNillableFiles sets the "files" field if the given value is not nil. +func (_u *HarnessConfigUpdate) SetNillableFiles(v *string) *HarnessConfigUpdate { + if v != nil { + _u.SetFiles(*v) + } + return _u +} + +// ClearFiles clears the value of the "files" field. +func (_u *HarnessConfigUpdate) ClearFiles() *HarnessConfigUpdate { + _u.mutation.ClearFiles() + return _u +} + +// SetLocked sets the "locked" field. +func (_u *HarnessConfigUpdate) SetLocked(v bool) *HarnessConfigUpdate { + _u.mutation.SetLocked(v) + return _u +} + +// SetNillableLocked sets the "locked" field if the given value is not nil. +func (_u *HarnessConfigUpdate) SetNillableLocked(v *bool) *HarnessConfigUpdate { + if v != nil { + _u.SetLocked(*v) + } + return _u +} + +// SetStatus sets the "status" field. +func (_u *HarnessConfigUpdate) SetStatus(v harnessconfig.Status) *HarnessConfigUpdate { + _u.mutation.SetStatus(v) + return _u +} + +// SetNillableStatus sets the "status" field if the given value is not nil. +func (_u *HarnessConfigUpdate) SetNillableStatus(v *harnessconfig.Status) *HarnessConfigUpdate { + if v != nil { + _u.SetStatus(*v) + } + return _u +} + +// SetOwnerID sets the "owner_id" field. +func (_u *HarnessConfigUpdate) SetOwnerID(v string) *HarnessConfigUpdate { + _u.mutation.SetOwnerID(v) + return _u +} + +// SetNillableOwnerID sets the "owner_id" field if the given value is not nil. +func (_u *HarnessConfigUpdate) SetNillableOwnerID(v *string) *HarnessConfigUpdate { + if v != nil { + _u.SetOwnerID(*v) + } + return _u +} + +// ClearOwnerID clears the value of the "owner_id" field. +func (_u *HarnessConfigUpdate) ClearOwnerID() *HarnessConfigUpdate { + _u.mutation.ClearOwnerID() + return _u +} + +// SetCreatedBy sets the "created_by" field. +func (_u *HarnessConfigUpdate) SetCreatedBy(v string) *HarnessConfigUpdate { + _u.mutation.SetCreatedBy(v) + return _u +} + +// SetNillableCreatedBy sets the "created_by" field if the given value is not nil. +func (_u *HarnessConfigUpdate) SetNillableCreatedBy(v *string) *HarnessConfigUpdate { + if v != nil { + _u.SetCreatedBy(*v) + } + return _u +} + +// ClearCreatedBy clears the value of the "created_by" field. +func (_u *HarnessConfigUpdate) ClearCreatedBy() *HarnessConfigUpdate { + _u.mutation.ClearCreatedBy() + return _u +} + +// SetUpdatedBy sets the "updated_by" field. +func (_u *HarnessConfigUpdate) SetUpdatedBy(v string) *HarnessConfigUpdate { + _u.mutation.SetUpdatedBy(v) + return _u +} + +// SetNillableUpdatedBy sets the "updated_by" field if the given value is not nil. +func (_u *HarnessConfigUpdate) SetNillableUpdatedBy(v *string) *HarnessConfigUpdate { + if v != nil { + _u.SetUpdatedBy(*v) + } + return _u +} + +// ClearUpdatedBy clears the value of the "updated_by" field. +func (_u *HarnessConfigUpdate) ClearUpdatedBy() *HarnessConfigUpdate { + _u.mutation.ClearUpdatedBy() + return _u +} + +// SetVisibility sets the "visibility" field. +func (_u *HarnessConfigUpdate) SetVisibility(v string) *HarnessConfigUpdate { + _u.mutation.SetVisibility(v) + return _u +} + +// SetNillableVisibility sets the "visibility" field if the given value is not nil. +func (_u *HarnessConfigUpdate) SetNillableVisibility(v *string) *HarnessConfigUpdate { + if v != nil { + _u.SetVisibility(*v) + } + return _u +} + +// SetUpdated sets the "updated" field. +func (_u *HarnessConfigUpdate) SetUpdated(v time.Time) *HarnessConfigUpdate { + _u.mutation.SetUpdated(v) + return _u +} + +// Mutation returns the HarnessConfigMutation object of the builder. +func (_u *HarnessConfigUpdate) Mutation() *HarnessConfigMutation { + return _u.mutation +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (_u *HarnessConfigUpdate) Save(ctx context.Context) (int, error) { + _u.defaults() + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *HarnessConfigUpdate) SaveX(ctx context.Context) int { + affected, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (_u *HarnessConfigUpdate) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *HarnessConfigUpdate) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_u *HarnessConfigUpdate) defaults() { + if _, ok := _u.mutation.Updated(); !ok { + v := harnessconfig.UpdateDefaultUpdated() + _u.mutation.SetUpdated(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_u *HarnessConfigUpdate) check() error { + if v, ok := _u.mutation.Name(); ok { + if err := harnessconfig.NameValidator(v); err != nil { + return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "HarnessConfig.name": %w`, err)} + } + } + if v, ok := _u.mutation.Slug(); ok { + if err := harnessconfig.SlugValidator(v); err != nil { + return &ValidationError{Name: "slug", err: fmt.Errorf(`ent: validator failed for field "HarnessConfig.slug": %w`, err)} + } + } + if v, ok := _u.mutation.Harness(); ok { + if err := harnessconfig.HarnessValidator(v); err != nil { + return &ValidationError{Name: "harness", err: fmt.Errorf(`ent: validator failed for field "HarnessConfig.harness": %w`, err)} + } + } + if v, ok := _u.mutation.Status(); ok { + if err := harnessconfig.StatusValidator(v); err != nil { + return &ValidationError{Name: "status", err: fmt.Errorf(`ent: validator failed for field "HarnessConfig.status": %w`, err)} + } + } + return nil +} + +func (_u *HarnessConfigUpdate) sqlSave(ctx context.Context) (_node int, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(harnessconfig.Table, harnessconfig.Columns, sqlgraph.NewFieldSpec(harnessconfig.FieldID, field.TypeUUID)) + if ps := _u.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := _u.mutation.Name(); ok { + _spec.SetField(harnessconfig.FieldName, field.TypeString, value) + } + if value, ok := _u.mutation.Slug(); ok { + _spec.SetField(harnessconfig.FieldSlug, field.TypeString, value) + } + if value, ok := _u.mutation.DisplayName(); ok { + _spec.SetField(harnessconfig.FieldDisplayName, field.TypeString, value) + } + if _u.mutation.DisplayNameCleared() { + _spec.ClearField(harnessconfig.FieldDisplayName, field.TypeString) + } + if value, ok := _u.mutation.Description(); ok { + _spec.SetField(harnessconfig.FieldDescription, field.TypeString, value) + } + if _u.mutation.DescriptionCleared() { + _spec.ClearField(harnessconfig.FieldDescription, field.TypeString) + } + if value, ok := _u.mutation.Harness(); ok { + _spec.SetField(harnessconfig.FieldHarness, field.TypeString, value) + } + if value, ok := _u.mutation.Config(); ok { + _spec.SetField(harnessconfig.FieldConfig, field.TypeString, value) + } + if _u.mutation.ConfigCleared() { + _spec.ClearField(harnessconfig.FieldConfig, field.TypeString) + } + if value, ok := _u.mutation.ContentHash(); ok { + _spec.SetField(harnessconfig.FieldContentHash, field.TypeString, value) + } + if _u.mutation.ContentHashCleared() { + _spec.ClearField(harnessconfig.FieldContentHash, field.TypeString) + } + if value, ok := _u.mutation.Scope(); ok { + _spec.SetField(harnessconfig.FieldScope, field.TypeString, value) + } + if value, ok := _u.mutation.ScopeID(); ok { + _spec.SetField(harnessconfig.FieldScopeID, field.TypeString, value) + } + if _u.mutation.ScopeIDCleared() { + _spec.ClearField(harnessconfig.FieldScopeID, field.TypeString) + } + if value, ok := _u.mutation.StorageURI(); ok { + _spec.SetField(harnessconfig.FieldStorageURI, field.TypeString, value) + } + if _u.mutation.StorageURICleared() { + _spec.ClearField(harnessconfig.FieldStorageURI, field.TypeString) + } + if value, ok := _u.mutation.StorageBucket(); ok { + _spec.SetField(harnessconfig.FieldStorageBucket, field.TypeString, value) + } + if _u.mutation.StorageBucketCleared() { + _spec.ClearField(harnessconfig.FieldStorageBucket, field.TypeString) + } + if value, ok := _u.mutation.StoragePath(); ok { + _spec.SetField(harnessconfig.FieldStoragePath, field.TypeString, value) + } + if _u.mutation.StoragePathCleared() { + _spec.ClearField(harnessconfig.FieldStoragePath, field.TypeString) + } + if value, ok := _u.mutation.Files(); ok { + _spec.SetField(harnessconfig.FieldFiles, field.TypeString, value) + } + if _u.mutation.FilesCleared() { + _spec.ClearField(harnessconfig.FieldFiles, field.TypeString) + } + if value, ok := _u.mutation.Locked(); ok { + _spec.SetField(harnessconfig.FieldLocked, field.TypeBool, value) + } + if value, ok := _u.mutation.Status(); ok { + _spec.SetField(harnessconfig.FieldStatus, field.TypeEnum, value) + } + if value, ok := _u.mutation.OwnerID(); ok { + _spec.SetField(harnessconfig.FieldOwnerID, field.TypeString, value) + } + if _u.mutation.OwnerIDCleared() { + _spec.ClearField(harnessconfig.FieldOwnerID, field.TypeString) + } + if value, ok := _u.mutation.CreatedBy(); ok { + _spec.SetField(harnessconfig.FieldCreatedBy, field.TypeString, value) + } + if _u.mutation.CreatedByCleared() { + _spec.ClearField(harnessconfig.FieldCreatedBy, field.TypeString) + } + if value, ok := _u.mutation.UpdatedBy(); ok { + _spec.SetField(harnessconfig.FieldUpdatedBy, field.TypeString, value) + } + if _u.mutation.UpdatedByCleared() { + _spec.ClearField(harnessconfig.FieldUpdatedBy, field.TypeString) + } + if value, ok := _u.mutation.Visibility(); ok { + _spec.SetField(harnessconfig.FieldVisibility, field.TypeString, value) + } + if value, ok := _u.mutation.Updated(); ok { + _spec.SetField(harnessconfig.FieldUpdated, field.TypeTime, value) + } + if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{harnessconfig.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + _u.mutation.done = true + return _node, nil +} + +// HarnessConfigUpdateOne is the builder for updating a single HarnessConfig entity. +type HarnessConfigUpdateOne struct { + config + fields []string + hooks []Hook + mutation *HarnessConfigMutation +} + +// SetName sets the "name" field. +func (_u *HarnessConfigUpdateOne) SetName(v string) *HarnessConfigUpdateOne { + _u.mutation.SetName(v) + return _u +} + +// SetNillableName sets the "name" field if the given value is not nil. +func (_u *HarnessConfigUpdateOne) SetNillableName(v *string) *HarnessConfigUpdateOne { + if v != nil { + _u.SetName(*v) + } + return _u +} + +// SetSlug sets the "slug" field. +func (_u *HarnessConfigUpdateOne) SetSlug(v string) *HarnessConfigUpdateOne { + _u.mutation.SetSlug(v) + return _u +} + +// SetNillableSlug sets the "slug" field if the given value is not nil. +func (_u *HarnessConfigUpdateOne) SetNillableSlug(v *string) *HarnessConfigUpdateOne { + if v != nil { + _u.SetSlug(*v) + } + return _u +} + +// SetDisplayName sets the "display_name" field. +func (_u *HarnessConfigUpdateOne) SetDisplayName(v string) *HarnessConfigUpdateOne { + _u.mutation.SetDisplayName(v) + return _u +} + +// SetNillableDisplayName sets the "display_name" field if the given value is not nil. +func (_u *HarnessConfigUpdateOne) SetNillableDisplayName(v *string) *HarnessConfigUpdateOne { + if v != nil { + _u.SetDisplayName(*v) + } + return _u +} + +// ClearDisplayName clears the value of the "display_name" field. +func (_u *HarnessConfigUpdateOne) ClearDisplayName() *HarnessConfigUpdateOne { + _u.mutation.ClearDisplayName() + return _u +} + +// SetDescription sets the "description" field. +func (_u *HarnessConfigUpdateOne) SetDescription(v string) *HarnessConfigUpdateOne { + _u.mutation.SetDescription(v) + return _u +} + +// SetNillableDescription sets the "description" field if the given value is not nil. +func (_u *HarnessConfigUpdateOne) SetNillableDescription(v *string) *HarnessConfigUpdateOne { + if v != nil { + _u.SetDescription(*v) + } + return _u +} + +// ClearDescription clears the value of the "description" field. +func (_u *HarnessConfigUpdateOne) ClearDescription() *HarnessConfigUpdateOne { + _u.mutation.ClearDescription() + return _u +} + +// SetHarness sets the "harness" field. +func (_u *HarnessConfigUpdateOne) SetHarness(v string) *HarnessConfigUpdateOne { + _u.mutation.SetHarness(v) + return _u +} + +// SetNillableHarness sets the "harness" field if the given value is not nil. +func (_u *HarnessConfigUpdateOne) SetNillableHarness(v *string) *HarnessConfigUpdateOne { + if v != nil { + _u.SetHarness(*v) + } + return _u +} + +// SetConfig sets the "config" field. +func (_u *HarnessConfigUpdateOne) SetConfig(v string) *HarnessConfigUpdateOne { + _u.mutation.SetConfig(v) + return _u +} + +// SetNillableConfig sets the "config" field if the given value is not nil. +func (_u *HarnessConfigUpdateOne) SetNillableConfig(v *string) *HarnessConfigUpdateOne { + if v != nil { + _u.SetConfig(*v) + } + return _u +} + +// ClearConfig clears the value of the "config" field. +func (_u *HarnessConfigUpdateOne) ClearConfig() *HarnessConfigUpdateOne { + _u.mutation.ClearConfig() + return _u +} + +// SetContentHash sets the "content_hash" field. +func (_u *HarnessConfigUpdateOne) SetContentHash(v string) *HarnessConfigUpdateOne { + _u.mutation.SetContentHash(v) + return _u +} + +// SetNillableContentHash sets the "content_hash" field if the given value is not nil. +func (_u *HarnessConfigUpdateOne) SetNillableContentHash(v *string) *HarnessConfigUpdateOne { + if v != nil { + _u.SetContentHash(*v) + } + return _u +} + +// ClearContentHash clears the value of the "content_hash" field. +func (_u *HarnessConfigUpdateOne) ClearContentHash() *HarnessConfigUpdateOne { + _u.mutation.ClearContentHash() + return _u +} + +// SetScope sets the "scope" field. +func (_u *HarnessConfigUpdateOne) SetScope(v string) *HarnessConfigUpdateOne { + _u.mutation.SetScope(v) + return _u +} + +// SetNillableScope sets the "scope" field if the given value is not nil. +func (_u *HarnessConfigUpdateOne) SetNillableScope(v *string) *HarnessConfigUpdateOne { + if v != nil { + _u.SetScope(*v) + } + return _u +} + +// SetScopeID sets the "scope_id" field. +func (_u *HarnessConfigUpdateOne) SetScopeID(v string) *HarnessConfigUpdateOne { + _u.mutation.SetScopeID(v) + return _u +} + +// SetNillableScopeID sets the "scope_id" field if the given value is not nil. +func (_u *HarnessConfigUpdateOne) SetNillableScopeID(v *string) *HarnessConfigUpdateOne { + if v != nil { + _u.SetScopeID(*v) + } + return _u +} + +// ClearScopeID clears the value of the "scope_id" field. +func (_u *HarnessConfigUpdateOne) ClearScopeID() *HarnessConfigUpdateOne { + _u.mutation.ClearScopeID() + return _u +} + +// SetStorageURI sets the "storage_uri" field. +func (_u *HarnessConfigUpdateOne) SetStorageURI(v string) *HarnessConfigUpdateOne { + _u.mutation.SetStorageURI(v) + return _u +} + +// SetNillableStorageURI sets the "storage_uri" field if the given value is not nil. +func (_u *HarnessConfigUpdateOne) SetNillableStorageURI(v *string) *HarnessConfigUpdateOne { + if v != nil { + _u.SetStorageURI(*v) + } + return _u +} + +// ClearStorageURI clears the value of the "storage_uri" field. +func (_u *HarnessConfigUpdateOne) ClearStorageURI() *HarnessConfigUpdateOne { + _u.mutation.ClearStorageURI() + return _u +} + +// SetStorageBucket sets the "storage_bucket" field. +func (_u *HarnessConfigUpdateOne) SetStorageBucket(v string) *HarnessConfigUpdateOne { + _u.mutation.SetStorageBucket(v) + return _u +} + +// SetNillableStorageBucket sets the "storage_bucket" field if the given value is not nil. +func (_u *HarnessConfigUpdateOne) SetNillableStorageBucket(v *string) *HarnessConfigUpdateOne { + if v != nil { + _u.SetStorageBucket(*v) + } + return _u +} + +// ClearStorageBucket clears the value of the "storage_bucket" field. +func (_u *HarnessConfigUpdateOne) ClearStorageBucket() *HarnessConfigUpdateOne { + _u.mutation.ClearStorageBucket() + return _u +} + +// SetStoragePath sets the "storage_path" field. +func (_u *HarnessConfigUpdateOne) SetStoragePath(v string) *HarnessConfigUpdateOne { + _u.mutation.SetStoragePath(v) + return _u +} + +// SetNillableStoragePath sets the "storage_path" field if the given value is not nil. +func (_u *HarnessConfigUpdateOne) SetNillableStoragePath(v *string) *HarnessConfigUpdateOne { + if v != nil { + _u.SetStoragePath(*v) + } + return _u +} + +// ClearStoragePath clears the value of the "storage_path" field. +func (_u *HarnessConfigUpdateOne) ClearStoragePath() *HarnessConfigUpdateOne { + _u.mutation.ClearStoragePath() + return _u +} + +// SetFiles sets the "files" field. +func (_u *HarnessConfigUpdateOne) SetFiles(v string) *HarnessConfigUpdateOne { + _u.mutation.SetFiles(v) + return _u +} + +// SetNillableFiles sets the "files" field if the given value is not nil. +func (_u *HarnessConfigUpdateOne) SetNillableFiles(v *string) *HarnessConfigUpdateOne { + if v != nil { + _u.SetFiles(*v) + } + return _u +} + +// ClearFiles clears the value of the "files" field. +func (_u *HarnessConfigUpdateOne) ClearFiles() *HarnessConfigUpdateOne { + _u.mutation.ClearFiles() + return _u +} + +// SetLocked sets the "locked" field. +func (_u *HarnessConfigUpdateOne) SetLocked(v bool) *HarnessConfigUpdateOne { + _u.mutation.SetLocked(v) + return _u +} + +// SetNillableLocked sets the "locked" field if the given value is not nil. +func (_u *HarnessConfigUpdateOne) SetNillableLocked(v *bool) *HarnessConfigUpdateOne { + if v != nil { + _u.SetLocked(*v) + } + return _u +} + +// SetStatus sets the "status" field. +func (_u *HarnessConfigUpdateOne) SetStatus(v harnessconfig.Status) *HarnessConfigUpdateOne { + _u.mutation.SetStatus(v) + return _u +} + +// SetNillableStatus sets the "status" field if the given value is not nil. +func (_u *HarnessConfigUpdateOne) SetNillableStatus(v *harnessconfig.Status) *HarnessConfigUpdateOne { + if v != nil { + _u.SetStatus(*v) + } + return _u +} + +// SetOwnerID sets the "owner_id" field. +func (_u *HarnessConfigUpdateOne) SetOwnerID(v string) *HarnessConfigUpdateOne { + _u.mutation.SetOwnerID(v) + return _u +} + +// SetNillableOwnerID sets the "owner_id" field if the given value is not nil. +func (_u *HarnessConfigUpdateOne) SetNillableOwnerID(v *string) *HarnessConfigUpdateOne { + if v != nil { + _u.SetOwnerID(*v) + } + return _u +} + +// ClearOwnerID clears the value of the "owner_id" field. +func (_u *HarnessConfigUpdateOne) ClearOwnerID() *HarnessConfigUpdateOne { + _u.mutation.ClearOwnerID() + return _u +} + +// SetCreatedBy sets the "created_by" field. +func (_u *HarnessConfigUpdateOne) SetCreatedBy(v string) *HarnessConfigUpdateOne { + _u.mutation.SetCreatedBy(v) + return _u +} + +// SetNillableCreatedBy sets the "created_by" field if the given value is not nil. +func (_u *HarnessConfigUpdateOne) SetNillableCreatedBy(v *string) *HarnessConfigUpdateOne { + if v != nil { + _u.SetCreatedBy(*v) + } + return _u +} + +// ClearCreatedBy clears the value of the "created_by" field. +func (_u *HarnessConfigUpdateOne) ClearCreatedBy() *HarnessConfigUpdateOne { + _u.mutation.ClearCreatedBy() + return _u +} + +// SetUpdatedBy sets the "updated_by" field. +func (_u *HarnessConfigUpdateOne) SetUpdatedBy(v string) *HarnessConfigUpdateOne { + _u.mutation.SetUpdatedBy(v) + return _u +} + +// SetNillableUpdatedBy sets the "updated_by" field if the given value is not nil. +func (_u *HarnessConfigUpdateOne) SetNillableUpdatedBy(v *string) *HarnessConfigUpdateOne { + if v != nil { + _u.SetUpdatedBy(*v) + } + return _u +} + +// ClearUpdatedBy clears the value of the "updated_by" field. +func (_u *HarnessConfigUpdateOne) ClearUpdatedBy() *HarnessConfigUpdateOne { + _u.mutation.ClearUpdatedBy() + return _u +} + +// SetVisibility sets the "visibility" field. +func (_u *HarnessConfigUpdateOne) SetVisibility(v string) *HarnessConfigUpdateOne { + _u.mutation.SetVisibility(v) + return _u +} + +// SetNillableVisibility sets the "visibility" field if the given value is not nil. +func (_u *HarnessConfigUpdateOne) SetNillableVisibility(v *string) *HarnessConfigUpdateOne { + if v != nil { + _u.SetVisibility(*v) + } + return _u +} + +// SetUpdated sets the "updated" field. +func (_u *HarnessConfigUpdateOne) SetUpdated(v time.Time) *HarnessConfigUpdateOne { + _u.mutation.SetUpdated(v) + return _u +} + +// Mutation returns the HarnessConfigMutation object of the builder. +func (_u *HarnessConfigUpdateOne) Mutation() *HarnessConfigMutation { + return _u.mutation +} + +// Where appends a list predicates to the HarnessConfigUpdate builder. +func (_u *HarnessConfigUpdateOne) Where(ps ...predicate.HarnessConfig) *HarnessConfigUpdateOne { + _u.mutation.Where(ps...) + return _u +} + +// Select allows selecting one or more fields (columns) of the returned entity. +// The default is selecting all fields defined in the entity schema. +func (_u *HarnessConfigUpdateOne) Select(field string, fields ...string) *HarnessConfigUpdateOne { + _u.fields = append([]string{field}, fields...) + return _u +} + +// Save executes the query and returns the updated HarnessConfig entity. +func (_u *HarnessConfigUpdateOne) Save(ctx context.Context) (*HarnessConfig, error) { + _u.defaults() + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *HarnessConfigUpdateOne) SaveX(ctx context.Context) *HarnessConfig { + node, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (_u *HarnessConfigUpdateOne) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *HarnessConfigUpdateOne) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_u *HarnessConfigUpdateOne) defaults() { + if _, ok := _u.mutation.Updated(); !ok { + v := harnessconfig.UpdateDefaultUpdated() + _u.mutation.SetUpdated(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_u *HarnessConfigUpdateOne) check() error { + if v, ok := _u.mutation.Name(); ok { + if err := harnessconfig.NameValidator(v); err != nil { + return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "HarnessConfig.name": %w`, err)} + } + } + if v, ok := _u.mutation.Slug(); ok { + if err := harnessconfig.SlugValidator(v); err != nil { + return &ValidationError{Name: "slug", err: fmt.Errorf(`ent: validator failed for field "HarnessConfig.slug": %w`, err)} + } + } + if v, ok := _u.mutation.Harness(); ok { + if err := harnessconfig.HarnessValidator(v); err != nil { + return &ValidationError{Name: "harness", err: fmt.Errorf(`ent: validator failed for field "HarnessConfig.harness": %w`, err)} + } + } + if v, ok := _u.mutation.Status(); ok { + if err := harnessconfig.StatusValidator(v); err != nil { + return &ValidationError{Name: "status", err: fmt.Errorf(`ent: validator failed for field "HarnessConfig.status": %w`, err)} + } + } + return nil +} + +func (_u *HarnessConfigUpdateOne) sqlSave(ctx context.Context) (_node *HarnessConfig, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(harnessconfig.Table, harnessconfig.Columns, sqlgraph.NewFieldSpec(harnessconfig.FieldID, field.TypeUUID)) + id, ok := _u.mutation.ID() + if !ok { + return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "HarnessConfig.id" for update`)} + } + _spec.Node.ID.Value = id + if fields := _u.fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, harnessconfig.FieldID) + for _, f := range fields { + if !harnessconfig.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + if f != harnessconfig.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, f) + } + } + } + if ps := _u.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := _u.mutation.Name(); ok { + _spec.SetField(harnessconfig.FieldName, field.TypeString, value) + } + if value, ok := _u.mutation.Slug(); ok { + _spec.SetField(harnessconfig.FieldSlug, field.TypeString, value) + } + if value, ok := _u.mutation.DisplayName(); ok { + _spec.SetField(harnessconfig.FieldDisplayName, field.TypeString, value) + } + if _u.mutation.DisplayNameCleared() { + _spec.ClearField(harnessconfig.FieldDisplayName, field.TypeString) + } + if value, ok := _u.mutation.Description(); ok { + _spec.SetField(harnessconfig.FieldDescription, field.TypeString, value) + } + if _u.mutation.DescriptionCleared() { + _spec.ClearField(harnessconfig.FieldDescription, field.TypeString) + } + if value, ok := _u.mutation.Harness(); ok { + _spec.SetField(harnessconfig.FieldHarness, field.TypeString, value) + } + if value, ok := _u.mutation.Config(); ok { + _spec.SetField(harnessconfig.FieldConfig, field.TypeString, value) + } + if _u.mutation.ConfigCleared() { + _spec.ClearField(harnessconfig.FieldConfig, field.TypeString) + } + if value, ok := _u.mutation.ContentHash(); ok { + _spec.SetField(harnessconfig.FieldContentHash, field.TypeString, value) + } + if _u.mutation.ContentHashCleared() { + _spec.ClearField(harnessconfig.FieldContentHash, field.TypeString) + } + if value, ok := _u.mutation.Scope(); ok { + _spec.SetField(harnessconfig.FieldScope, field.TypeString, value) + } + if value, ok := _u.mutation.ScopeID(); ok { + _spec.SetField(harnessconfig.FieldScopeID, field.TypeString, value) + } + if _u.mutation.ScopeIDCleared() { + _spec.ClearField(harnessconfig.FieldScopeID, field.TypeString) + } + if value, ok := _u.mutation.StorageURI(); ok { + _spec.SetField(harnessconfig.FieldStorageURI, field.TypeString, value) + } + if _u.mutation.StorageURICleared() { + _spec.ClearField(harnessconfig.FieldStorageURI, field.TypeString) + } + if value, ok := _u.mutation.StorageBucket(); ok { + _spec.SetField(harnessconfig.FieldStorageBucket, field.TypeString, value) + } + if _u.mutation.StorageBucketCleared() { + _spec.ClearField(harnessconfig.FieldStorageBucket, field.TypeString) + } + if value, ok := _u.mutation.StoragePath(); ok { + _spec.SetField(harnessconfig.FieldStoragePath, field.TypeString, value) + } + if _u.mutation.StoragePathCleared() { + _spec.ClearField(harnessconfig.FieldStoragePath, field.TypeString) + } + if value, ok := _u.mutation.Files(); ok { + _spec.SetField(harnessconfig.FieldFiles, field.TypeString, value) + } + if _u.mutation.FilesCleared() { + _spec.ClearField(harnessconfig.FieldFiles, field.TypeString) + } + if value, ok := _u.mutation.Locked(); ok { + _spec.SetField(harnessconfig.FieldLocked, field.TypeBool, value) + } + if value, ok := _u.mutation.Status(); ok { + _spec.SetField(harnessconfig.FieldStatus, field.TypeEnum, value) + } + if value, ok := _u.mutation.OwnerID(); ok { + _spec.SetField(harnessconfig.FieldOwnerID, field.TypeString, value) + } + if _u.mutation.OwnerIDCleared() { + _spec.ClearField(harnessconfig.FieldOwnerID, field.TypeString) + } + if value, ok := _u.mutation.CreatedBy(); ok { + _spec.SetField(harnessconfig.FieldCreatedBy, field.TypeString, value) + } + if _u.mutation.CreatedByCleared() { + _spec.ClearField(harnessconfig.FieldCreatedBy, field.TypeString) + } + if value, ok := _u.mutation.UpdatedBy(); ok { + _spec.SetField(harnessconfig.FieldUpdatedBy, field.TypeString, value) + } + if _u.mutation.UpdatedByCleared() { + _spec.ClearField(harnessconfig.FieldUpdatedBy, field.TypeString) + } + if value, ok := _u.mutation.Visibility(); ok { + _spec.SetField(harnessconfig.FieldVisibility, field.TypeString, value) + } + if value, ok := _u.mutation.Updated(); ok { + _spec.SetField(harnessconfig.FieldUpdated, field.TypeTime, value) + } + _node = &HarnessConfig{config: _u.config} + _spec.Assign = _node.assignValues + _spec.ScanValues = _node.scanValues + if err = sqlgraph.UpdateNode(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{harnessconfig.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + _u.mutation.done = true + return _node, nil +} diff --git a/pkg/ent/hook/hook.go b/pkg/ent/hook/hook.go index 889987add..743dd42be 100644 --- a/pkg/ent/hook/hook.go +++ b/pkg/ent/hook/hook.go @@ -33,6 +33,90 @@ func (f AgentFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.AgentMutation", m) } +// The AllowListEntryFunc type is an adapter to allow the use of ordinary +// function as AllowListEntry mutator. +type AllowListEntryFunc func(context.Context, *ent.AllowListEntryMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f AllowListEntryFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if mv, ok := m.(*ent.AllowListEntryMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.AllowListEntryMutation", m) +} + +// The ApiKeyFunc type is an adapter to allow the use of ordinary +// function as ApiKey mutator. +type ApiKeyFunc func(context.Context, *ent.ApiKeyMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f ApiKeyFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if mv, ok := m.(*ent.ApiKeyMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.ApiKeyMutation", m) +} + +// The BrokerJoinTokenFunc type is an adapter to allow the use of ordinary +// function as BrokerJoinToken mutator. +type BrokerJoinTokenFunc func(context.Context, *ent.BrokerJoinTokenMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f BrokerJoinTokenFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if mv, ok := m.(*ent.BrokerJoinTokenMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.BrokerJoinTokenMutation", m) +} + +// The BrokerSecretFunc type is an adapter to allow the use of ordinary +// function as BrokerSecret mutator. +type BrokerSecretFunc func(context.Context, *ent.BrokerSecretMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f BrokerSecretFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if mv, ok := m.(*ent.BrokerSecretMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.BrokerSecretMutation", m) +} + +// The EnvVarFunc type is an adapter to allow the use of ordinary +// function as EnvVar mutator. +type EnvVarFunc func(context.Context, *ent.EnvVarMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f EnvVarFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if mv, ok := m.(*ent.EnvVarMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.EnvVarMutation", m) +} + +// The GCPServiceAccountFunc type is an adapter to allow the use of ordinary +// function as GCPServiceAccount mutator. +type GCPServiceAccountFunc func(context.Context, *ent.GCPServiceAccountMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f GCPServiceAccountFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if mv, ok := m.(*ent.GCPServiceAccountMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.GCPServiceAccountMutation", m) +} + +// The GithubInstallationFunc type is an adapter to allow the use of ordinary +// function as GithubInstallation mutator. +type GithubInstallationFunc func(context.Context, *ent.GithubInstallationMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f GithubInstallationFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if mv, ok := m.(*ent.GithubInstallationMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.GithubInstallationMutation", m) +} + // The GroupFunc type is an adapter to allow the use of ordinary // function as Group mutator. type GroupFunc func(context.Context, *ent.GroupMutation) (ent.Value, error) @@ -57,6 +141,90 @@ func (f GroupMembershipFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Va return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.GroupMembershipMutation", m) } +// The HarnessConfigFunc type is an adapter to allow the use of ordinary +// function as HarnessConfig mutator. +type HarnessConfigFunc func(context.Context, *ent.HarnessConfigMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f HarnessConfigFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if mv, ok := m.(*ent.HarnessConfigMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.HarnessConfigMutation", m) +} + +// The InviteCodeFunc type is an adapter to allow the use of ordinary +// function as InviteCode mutator. +type InviteCodeFunc func(context.Context, *ent.InviteCodeMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f InviteCodeFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if mv, ok := m.(*ent.InviteCodeMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.InviteCodeMutation", m) +} + +// The MaintenanceOperationFunc type is an adapter to allow the use of ordinary +// function as MaintenanceOperation mutator. +type MaintenanceOperationFunc func(context.Context, *ent.MaintenanceOperationMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f MaintenanceOperationFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if mv, ok := m.(*ent.MaintenanceOperationMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.MaintenanceOperationMutation", m) +} + +// The MaintenanceOperationRunFunc type is an adapter to allow the use of ordinary +// function as MaintenanceOperationRun mutator. +type MaintenanceOperationRunFunc func(context.Context, *ent.MaintenanceOperationRunMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f MaintenanceOperationRunFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if mv, ok := m.(*ent.MaintenanceOperationRunMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.MaintenanceOperationRunMutation", m) +} + +// The MessageFunc type is an adapter to allow the use of ordinary +// function as Message mutator. +type MessageFunc func(context.Context, *ent.MessageMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f MessageFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if mv, ok := m.(*ent.MessageMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.MessageMutation", m) +} + +// The NotificationFunc type is an adapter to allow the use of ordinary +// function as Notification mutator. +type NotificationFunc func(context.Context, *ent.NotificationMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f NotificationFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if mv, ok := m.(*ent.NotificationMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.NotificationMutation", m) +} + +// The NotificationSubscriptionFunc type is an adapter to allow the use of ordinary +// function as NotificationSubscription mutator. +type NotificationSubscriptionFunc func(context.Context, *ent.NotificationSubscriptionMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f NotificationSubscriptionFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if mv, ok := m.(*ent.NotificationSubscriptionMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.NotificationSubscriptionMutation", m) +} + // The PolicyBindingFunc type is an adapter to allow the use of ordinary // function as PolicyBinding mutator. type PolicyBindingFunc func(context.Context, *ent.PolicyBindingMutation) (ent.Value, error) @@ -81,6 +249,102 @@ func (f ProjectFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, err return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.ProjectMutation", m) } +// The ProjectContributorFunc type is an adapter to allow the use of ordinary +// function as ProjectContributor mutator. +type ProjectContributorFunc func(context.Context, *ent.ProjectContributorMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f ProjectContributorFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if mv, ok := m.(*ent.ProjectContributorMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.ProjectContributorMutation", m) +} + +// The ProjectSyncStateFunc type is an adapter to allow the use of ordinary +// function as ProjectSyncState mutator. +type ProjectSyncStateFunc func(context.Context, *ent.ProjectSyncStateMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f ProjectSyncStateFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if mv, ok := m.(*ent.ProjectSyncStateMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.ProjectSyncStateMutation", m) +} + +// The RuntimeBrokerFunc type is an adapter to allow the use of ordinary +// function as RuntimeBroker mutator. +type RuntimeBrokerFunc func(context.Context, *ent.RuntimeBrokerMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f RuntimeBrokerFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if mv, ok := m.(*ent.RuntimeBrokerMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.RuntimeBrokerMutation", m) +} + +// The ScheduleFunc type is an adapter to allow the use of ordinary +// function as Schedule mutator. +type ScheduleFunc func(context.Context, *ent.ScheduleMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f ScheduleFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if mv, ok := m.(*ent.ScheduleMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.ScheduleMutation", m) +} + +// The ScheduledEventFunc type is an adapter to allow the use of ordinary +// function as ScheduledEvent mutator. +type ScheduledEventFunc func(context.Context, *ent.ScheduledEventMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f ScheduledEventFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if mv, ok := m.(*ent.ScheduledEventMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.ScheduledEventMutation", m) +} + +// The SecretFunc type is an adapter to allow the use of ordinary +// function as Secret mutator. +type SecretFunc func(context.Context, *ent.SecretMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f SecretFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if mv, ok := m.(*ent.SecretMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.SecretMutation", m) +} + +// The SubscriptionTemplateFunc type is an adapter to allow the use of ordinary +// function as SubscriptionTemplate mutator. +type SubscriptionTemplateFunc func(context.Context, *ent.SubscriptionTemplateMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f SubscriptionTemplateFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if mv, ok := m.(*ent.SubscriptionTemplateMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.SubscriptionTemplateMutation", m) +} + +// The TemplateFunc type is an adapter to allow the use of ordinary +// function as Template mutator. +type TemplateFunc func(context.Context, *ent.TemplateMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f TemplateFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if mv, ok := m.(*ent.TemplateMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.TemplateMutation", m) +} + // The UserFunc type is an adapter to allow the use of ordinary // function as User mutator. type UserFunc func(context.Context, *ent.UserMutation) (ent.Value, error) @@ -93,6 +357,18 @@ func (f UserFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.UserMutation", m) } +// The UserAccessTokenFunc type is an adapter to allow the use of ordinary +// function as UserAccessToken mutator. +type UserAccessTokenFunc func(context.Context, *ent.UserAccessTokenMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f UserAccessTokenFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if mv, ok := m.(*ent.UserAccessTokenMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.UserAccessTokenMutation", m) +} + // Condition is a hook condition function. type Condition func(context.Context, ent.Mutation) bool diff --git a/pkg/ent/invitecode.go b/pkg/ent/invitecode.go new file mode 100644 index 000000000..09c2479b5 --- /dev/null +++ b/pkg/ent/invitecode.go @@ -0,0 +1,198 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "fmt" + "strings" + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "github.com/GoogleCloudPlatform/scion/pkg/ent/invitecode" + "github.com/google/uuid" +) + +// InviteCode is the model entity for the InviteCode schema. +type InviteCode struct { + config `json:"-"` + // ID of the ent. + ID uuid.UUID `json:"id,omitempty"` + // CodeHash holds the value of the "code_hash" field. + CodeHash string `json:"-"` + // CodePrefix holds the value of the "code_prefix" field. + CodePrefix string `json:"code_prefix,omitempty"` + // MaxUses holds the value of the "max_uses" field. + MaxUses int `json:"max_uses,omitempty"` + // UseCount holds the value of the "use_count" field. + UseCount int `json:"use_count,omitempty"` + // ExpiresAt holds the value of the "expires_at" field. + ExpiresAt time.Time `json:"expires_at,omitempty"` + // Revoked holds the value of the "revoked" field. + Revoked bool `json:"revoked,omitempty"` + // CreatedBy holds the value of the "created_by" field. + CreatedBy string `json:"created_by,omitempty"` + // Note holds the value of the "note" field. + Note string `json:"note,omitempty"` + // Created holds the value of the "created" field. + Created time.Time `json:"created,omitempty"` + selectValues sql.SelectValues +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*InviteCode) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case invitecode.FieldRevoked: + values[i] = new(sql.NullBool) + case invitecode.FieldMaxUses, invitecode.FieldUseCount: + values[i] = new(sql.NullInt64) + case invitecode.FieldCodeHash, invitecode.FieldCodePrefix, invitecode.FieldCreatedBy, invitecode.FieldNote: + values[i] = new(sql.NullString) + case invitecode.FieldExpiresAt, invitecode.FieldCreated: + values[i] = new(sql.NullTime) + case invitecode.FieldID: + values[i] = new(uuid.UUID) + default: + values[i] = new(sql.UnknownType) + } + } + return values, nil +} + +// assignValues assigns the values that were returned from sql.Rows (after scanning) +// to the InviteCode fields. +func (_m *InviteCode) assignValues(columns []string, values []any) error { + if m, n := len(values), len(columns); m < n { + return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) + } + for i := range columns { + switch columns[i] { + case invitecode.FieldID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field id", values[i]) + } else if value != nil { + _m.ID = *value + } + case invitecode.FieldCodeHash: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field code_hash", values[i]) + } else if value.Valid { + _m.CodeHash = value.String + } + case invitecode.FieldCodePrefix: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field code_prefix", values[i]) + } else if value.Valid { + _m.CodePrefix = value.String + } + case invitecode.FieldMaxUses: + if value, ok := values[i].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for field max_uses", values[i]) + } else if value.Valid { + _m.MaxUses = int(value.Int64) + } + case invitecode.FieldUseCount: + if value, ok := values[i].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for field use_count", values[i]) + } else if value.Valid { + _m.UseCount = int(value.Int64) + } + case invitecode.FieldExpiresAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field expires_at", values[i]) + } else if value.Valid { + _m.ExpiresAt = value.Time + } + case invitecode.FieldRevoked: + if value, ok := values[i].(*sql.NullBool); !ok { + return fmt.Errorf("unexpected type %T for field revoked", values[i]) + } else if value.Valid { + _m.Revoked = value.Bool + } + case invitecode.FieldCreatedBy: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field created_by", values[i]) + } else if value.Valid { + _m.CreatedBy = value.String + } + case invitecode.FieldNote: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field note", values[i]) + } else if value.Valid { + _m.Note = value.String + } + case invitecode.FieldCreated: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field created", values[i]) + } else if value.Valid { + _m.Created = value.Time + } + default: + _m.selectValues.Set(columns[i], values[i]) + } + } + return nil +} + +// Value returns the ent.Value that was dynamically selected and assigned to the InviteCode. +// This includes values selected through modifiers, order, etc. +func (_m *InviteCode) Value(name string) (ent.Value, error) { + return _m.selectValues.Get(name) +} + +// Update returns a builder for updating this InviteCode. +// Note that you need to call InviteCode.Unwrap() before calling this method if this InviteCode +// was returned from a transaction, and the transaction was committed or rolled back. +func (_m *InviteCode) Update() *InviteCodeUpdateOne { + return NewInviteCodeClient(_m.config).UpdateOne(_m) +} + +// Unwrap unwraps the InviteCode entity that was returned from a transaction after it was closed, +// so that all future queries will be executed through the driver which created the transaction. +func (_m *InviteCode) Unwrap() *InviteCode { + _tx, ok := _m.config.driver.(*txDriver) + if !ok { + panic("ent: InviteCode is not a transactional entity") + } + _m.config.driver = _tx.drv + return _m +} + +// String implements the fmt.Stringer. +func (_m *InviteCode) String() string { + var builder strings.Builder + builder.WriteString("InviteCode(") + builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID)) + builder.WriteString("code_hash=") + builder.WriteString(", ") + builder.WriteString("code_prefix=") + builder.WriteString(_m.CodePrefix) + builder.WriteString(", ") + builder.WriteString("max_uses=") + builder.WriteString(fmt.Sprintf("%v", _m.MaxUses)) + builder.WriteString(", ") + builder.WriteString("use_count=") + builder.WriteString(fmt.Sprintf("%v", _m.UseCount)) + builder.WriteString(", ") + builder.WriteString("expires_at=") + builder.WriteString(_m.ExpiresAt.Format(time.ANSIC)) + builder.WriteString(", ") + builder.WriteString("revoked=") + builder.WriteString(fmt.Sprintf("%v", _m.Revoked)) + builder.WriteString(", ") + builder.WriteString("created_by=") + builder.WriteString(_m.CreatedBy) + builder.WriteString(", ") + builder.WriteString("note=") + builder.WriteString(_m.Note) + builder.WriteString(", ") + builder.WriteString("created=") + builder.WriteString(_m.Created.Format(time.ANSIC)) + builder.WriteByte(')') + return builder.String() +} + +// InviteCodes is a parsable slice of InviteCode. +type InviteCodes []*InviteCode diff --git a/pkg/ent/invitecode/invitecode.go b/pkg/ent/invitecode/invitecode.go new file mode 100644 index 000000000..b19d09528 --- /dev/null +++ b/pkg/ent/invitecode/invitecode.go @@ -0,0 +1,135 @@ +// Code generated by ent, DO NOT EDIT. + +package invitecode + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "github.com/google/uuid" +) + +const ( + // Label holds the string label denoting the invitecode type in the database. + Label = "invite_code" + // FieldID holds the string denoting the id field in the database. + FieldID = "id" + // FieldCodeHash holds the string denoting the code_hash field in the database. + FieldCodeHash = "code_hash" + // FieldCodePrefix holds the string denoting the code_prefix field in the database. + FieldCodePrefix = "code_prefix" + // FieldMaxUses holds the string denoting the max_uses field in the database. + FieldMaxUses = "max_uses" + // FieldUseCount holds the string denoting the use_count field in the database. + FieldUseCount = "use_count" + // FieldExpiresAt holds the string denoting the expires_at field in the database. + FieldExpiresAt = "expires_at" + // FieldRevoked holds the string denoting the revoked field in the database. + FieldRevoked = "revoked" + // FieldCreatedBy holds the string denoting the created_by field in the database. + FieldCreatedBy = "created_by" + // FieldNote holds the string denoting the note field in the database. + FieldNote = "note" + // FieldCreated holds the string denoting the created field in the database. + FieldCreated = "created" + // Table holds the table name of the invitecode in the database. + Table = "invite_codes" +) + +// Columns holds all SQL columns for invitecode fields. +var Columns = []string{ + FieldID, + FieldCodeHash, + FieldCodePrefix, + FieldMaxUses, + FieldUseCount, + FieldExpiresAt, + FieldRevoked, + FieldCreatedBy, + FieldNote, + FieldCreated, +} + +// ValidColumn reports if the column name is valid (part of the table columns). +func ValidColumn(column string) bool { + for i := range Columns { + if column == Columns[i] { + return true + } + } + return false +} + +var ( + // CodeHashValidator is a validator for the "code_hash" field. It is called by the builders before save. + CodeHashValidator func(string) error + // CodePrefixValidator is a validator for the "code_prefix" field. It is called by the builders before save. + CodePrefixValidator func(string) error + // DefaultMaxUses holds the default value on creation for the "max_uses" field. + DefaultMaxUses int + // DefaultUseCount holds the default value on creation for the "use_count" field. + DefaultUseCount int + // DefaultRevoked holds the default value on creation for the "revoked" field. + DefaultRevoked bool + // CreatedByValidator is a validator for the "created_by" field. It is called by the builders before save. + CreatedByValidator func(string) error + // DefaultNote holds the default value on creation for the "note" field. + DefaultNote string + // DefaultCreated holds the default value on creation for the "created" field. + DefaultCreated func() time.Time + // DefaultID holds the default value on creation for the "id" field. + DefaultID func() uuid.UUID +) + +// OrderOption defines the ordering options for the InviteCode queries. +type OrderOption func(*sql.Selector) + +// ByID orders the results by the id field. +func ByID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldID, opts...).ToFunc() +} + +// ByCodeHash orders the results by the code_hash field. +func ByCodeHash(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCodeHash, opts...).ToFunc() +} + +// ByCodePrefix orders the results by the code_prefix field. +func ByCodePrefix(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCodePrefix, opts...).ToFunc() +} + +// ByMaxUses orders the results by the max_uses field. +func ByMaxUses(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldMaxUses, opts...).ToFunc() +} + +// ByUseCount orders the results by the use_count field. +func ByUseCount(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldUseCount, opts...).ToFunc() +} + +// ByExpiresAt orders the results by the expires_at field. +func ByExpiresAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldExpiresAt, opts...).ToFunc() +} + +// ByRevoked orders the results by the revoked field. +func ByRevoked(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldRevoked, opts...).ToFunc() +} + +// ByCreatedBy orders the results by the created_by field. +func ByCreatedBy(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCreatedBy, opts...).ToFunc() +} + +// ByNote orders the results by the note field. +func ByNote(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldNote, opts...).ToFunc() +} + +// ByCreated orders the results by the created field. +func ByCreated(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCreated, opts...).ToFunc() +} diff --git a/pkg/ent/invitecode/where.go b/pkg/ent/invitecode/where.go new file mode 100644 index 000000000..416cd9c71 --- /dev/null +++ b/pkg/ent/invitecode/where.go @@ -0,0 +1,546 @@ +// Code generated by ent, DO NOT EDIT. + +package invitecode + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/google/uuid" +) + +// ID filters vertices based on their ID field. +func ID(id uuid.UUID) predicate.InviteCode { + return predicate.InviteCode(sql.FieldEQ(FieldID, id)) +} + +// IDEQ applies the EQ predicate on the ID field. +func IDEQ(id uuid.UUID) predicate.InviteCode { + return predicate.InviteCode(sql.FieldEQ(FieldID, id)) +} + +// IDNEQ applies the NEQ predicate on the ID field. +func IDNEQ(id uuid.UUID) predicate.InviteCode { + return predicate.InviteCode(sql.FieldNEQ(FieldID, id)) +} + +// IDIn applies the In predicate on the ID field. +func IDIn(ids ...uuid.UUID) predicate.InviteCode { + return predicate.InviteCode(sql.FieldIn(FieldID, ids...)) +} + +// IDNotIn applies the NotIn predicate on the ID field. +func IDNotIn(ids ...uuid.UUID) predicate.InviteCode { + return predicate.InviteCode(sql.FieldNotIn(FieldID, ids...)) +} + +// IDGT applies the GT predicate on the ID field. +func IDGT(id uuid.UUID) predicate.InviteCode { + return predicate.InviteCode(sql.FieldGT(FieldID, id)) +} + +// IDGTE applies the GTE predicate on the ID field. +func IDGTE(id uuid.UUID) predicate.InviteCode { + return predicate.InviteCode(sql.FieldGTE(FieldID, id)) +} + +// IDLT applies the LT predicate on the ID field. +func IDLT(id uuid.UUID) predicate.InviteCode { + return predicate.InviteCode(sql.FieldLT(FieldID, id)) +} + +// IDLTE applies the LTE predicate on the ID field. +func IDLTE(id uuid.UUID) predicate.InviteCode { + return predicate.InviteCode(sql.FieldLTE(FieldID, id)) +} + +// CodeHash applies equality check predicate on the "code_hash" field. It's identical to CodeHashEQ. +func CodeHash(v string) predicate.InviteCode { + return predicate.InviteCode(sql.FieldEQ(FieldCodeHash, v)) +} + +// CodePrefix applies equality check predicate on the "code_prefix" field. It's identical to CodePrefixEQ. +func CodePrefix(v string) predicate.InviteCode { + return predicate.InviteCode(sql.FieldEQ(FieldCodePrefix, v)) +} + +// MaxUses applies equality check predicate on the "max_uses" field. It's identical to MaxUsesEQ. +func MaxUses(v int) predicate.InviteCode { + return predicate.InviteCode(sql.FieldEQ(FieldMaxUses, v)) +} + +// UseCount applies equality check predicate on the "use_count" field. It's identical to UseCountEQ. +func UseCount(v int) predicate.InviteCode { + return predicate.InviteCode(sql.FieldEQ(FieldUseCount, v)) +} + +// ExpiresAt applies equality check predicate on the "expires_at" field. It's identical to ExpiresAtEQ. +func ExpiresAt(v time.Time) predicate.InviteCode { + return predicate.InviteCode(sql.FieldEQ(FieldExpiresAt, v)) +} + +// Revoked applies equality check predicate on the "revoked" field. It's identical to RevokedEQ. +func Revoked(v bool) predicate.InviteCode { + return predicate.InviteCode(sql.FieldEQ(FieldRevoked, v)) +} + +// CreatedBy applies equality check predicate on the "created_by" field. It's identical to CreatedByEQ. +func CreatedBy(v string) predicate.InviteCode { + return predicate.InviteCode(sql.FieldEQ(FieldCreatedBy, v)) +} + +// Note applies equality check predicate on the "note" field. It's identical to NoteEQ. +func Note(v string) predicate.InviteCode { + return predicate.InviteCode(sql.FieldEQ(FieldNote, v)) +} + +// Created applies equality check predicate on the "created" field. It's identical to CreatedEQ. +func Created(v time.Time) predicate.InviteCode { + return predicate.InviteCode(sql.FieldEQ(FieldCreated, v)) +} + +// CodeHashEQ applies the EQ predicate on the "code_hash" field. +func CodeHashEQ(v string) predicate.InviteCode { + return predicate.InviteCode(sql.FieldEQ(FieldCodeHash, v)) +} + +// CodeHashNEQ applies the NEQ predicate on the "code_hash" field. +func CodeHashNEQ(v string) predicate.InviteCode { + return predicate.InviteCode(sql.FieldNEQ(FieldCodeHash, v)) +} + +// CodeHashIn applies the In predicate on the "code_hash" field. +func CodeHashIn(vs ...string) predicate.InviteCode { + return predicate.InviteCode(sql.FieldIn(FieldCodeHash, vs...)) +} + +// CodeHashNotIn applies the NotIn predicate on the "code_hash" field. +func CodeHashNotIn(vs ...string) predicate.InviteCode { + return predicate.InviteCode(sql.FieldNotIn(FieldCodeHash, vs...)) +} + +// CodeHashGT applies the GT predicate on the "code_hash" field. +func CodeHashGT(v string) predicate.InviteCode { + return predicate.InviteCode(sql.FieldGT(FieldCodeHash, v)) +} + +// CodeHashGTE applies the GTE predicate on the "code_hash" field. +func CodeHashGTE(v string) predicate.InviteCode { + return predicate.InviteCode(sql.FieldGTE(FieldCodeHash, v)) +} + +// CodeHashLT applies the LT predicate on the "code_hash" field. +func CodeHashLT(v string) predicate.InviteCode { + return predicate.InviteCode(sql.FieldLT(FieldCodeHash, v)) +} + +// CodeHashLTE applies the LTE predicate on the "code_hash" field. +func CodeHashLTE(v string) predicate.InviteCode { + return predicate.InviteCode(sql.FieldLTE(FieldCodeHash, v)) +} + +// CodeHashContains applies the Contains predicate on the "code_hash" field. +func CodeHashContains(v string) predicate.InviteCode { + return predicate.InviteCode(sql.FieldContains(FieldCodeHash, v)) +} + +// CodeHashHasPrefix applies the HasPrefix predicate on the "code_hash" field. +func CodeHashHasPrefix(v string) predicate.InviteCode { + return predicate.InviteCode(sql.FieldHasPrefix(FieldCodeHash, v)) +} + +// CodeHashHasSuffix applies the HasSuffix predicate on the "code_hash" field. +func CodeHashHasSuffix(v string) predicate.InviteCode { + return predicate.InviteCode(sql.FieldHasSuffix(FieldCodeHash, v)) +} + +// CodeHashEqualFold applies the EqualFold predicate on the "code_hash" field. +func CodeHashEqualFold(v string) predicate.InviteCode { + return predicate.InviteCode(sql.FieldEqualFold(FieldCodeHash, v)) +} + +// CodeHashContainsFold applies the ContainsFold predicate on the "code_hash" field. +func CodeHashContainsFold(v string) predicate.InviteCode { + return predicate.InviteCode(sql.FieldContainsFold(FieldCodeHash, v)) +} + +// CodePrefixEQ applies the EQ predicate on the "code_prefix" field. +func CodePrefixEQ(v string) predicate.InviteCode { + return predicate.InviteCode(sql.FieldEQ(FieldCodePrefix, v)) +} + +// CodePrefixNEQ applies the NEQ predicate on the "code_prefix" field. +func CodePrefixNEQ(v string) predicate.InviteCode { + return predicate.InviteCode(sql.FieldNEQ(FieldCodePrefix, v)) +} + +// CodePrefixIn applies the In predicate on the "code_prefix" field. +func CodePrefixIn(vs ...string) predicate.InviteCode { + return predicate.InviteCode(sql.FieldIn(FieldCodePrefix, vs...)) +} + +// CodePrefixNotIn applies the NotIn predicate on the "code_prefix" field. +func CodePrefixNotIn(vs ...string) predicate.InviteCode { + return predicate.InviteCode(sql.FieldNotIn(FieldCodePrefix, vs...)) +} + +// CodePrefixGT applies the GT predicate on the "code_prefix" field. +func CodePrefixGT(v string) predicate.InviteCode { + return predicate.InviteCode(sql.FieldGT(FieldCodePrefix, v)) +} + +// CodePrefixGTE applies the GTE predicate on the "code_prefix" field. +func CodePrefixGTE(v string) predicate.InviteCode { + return predicate.InviteCode(sql.FieldGTE(FieldCodePrefix, v)) +} + +// CodePrefixLT applies the LT predicate on the "code_prefix" field. +func CodePrefixLT(v string) predicate.InviteCode { + return predicate.InviteCode(sql.FieldLT(FieldCodePrefix, v)) +} + +// CodePrefixLTE applies the LTE predicate on the "code_prefix" field. +func CodePrefixLTE(v string) predicate.InviteCode { + return predicate.InviteCode(sql.FieldLTE(FieldCodePrefix, v)) +} + +// CodePrefixContains applies the Contains predicate on the "code_prefix" field. +func CodePrefixContains(v string) predicate.InviteCode { + return predicate.InviteCode(sql.FieldContains(FieldCodePrefix, v)) +} + +// CodePrefixHasPrefix applies the HasPrefix predicate on the "code_prefix" field. +func CodePrefixHasPrefix(v string) predicate.InviteCode { + return predicate.InviteCode(sql.FieldHasPrefix(FieldCodePrefix, v)) +} + +// CodePrefixHasSuffix applies the HasSuffix predicate on the "code_prefix" field. +func CodePrefixHasSuffix(v string) predicate.InviteCode { + return predicate.InviteCode(sql.FieldHasSuffix(FieldCodePrefix, v)) +} + +// CodePrefixEqualFold applies the EqualFold predicate on the "code_prefix" field. +func CodePrefixEqualFold(v string) predicate.InviteCode { + return predicate.InviteCode(sql.FieldEqualFold(FieldCodePrefix, v)) +} + +// CodePrefixContainsFold applies the ContainsFold predicate on the "code_prefix" field. +func CodePrefixContainsFold(v string) predicate.InviteCode { + return predicate.InviteCode(sql.FieldContainsFold(FieldCodePrefix, v)) +} + +// MaxUsesEQ applies the EQ predicate on the "max_uses" field. +func MaxUsesEQ(v int) predicate.InviteCode { + return predicate.InviteCode(sql.FieldEQ(FieldMaxUses, v)) +} + +// MaxUsesNEQ applies the NEQ predicate on the "max_uses" field. +func MaxUsesNEQ(v int) predicate.InviteCode { + return predicate.InviteCode(sql.FieldNEQ(FieldMaxUses, v)) +} + +// MaxUsesIn applies the In predicate on the "max_uses" field. +func MaxUsesIn(vs ...int) predicate.InviteCode { + return predicate.InviteCode(sql.FieldIn(FieldMaxUses, vs...)) +} + +// MaxUsesNotIn applies the NotIn predicate on the "max_uses" field. +func MaxUsesNotIn(vs ...int) predicate.InviteCode { + return predicate.InviteCode(sql.FieldNotIn(FieldMaxUses, vs...)) +} + +// MaxUsesGT applies the GT predicate on the "max_uses" field. +func MaxUsesGT(v int) predicate.InviteCode { + return predicate.InviteCode(sql.FieldGT(FieldMaxUses, v)) +} + +// MaxUsesGTE applies the GTE predicate on the "max_uses" field. +func MaxUsesGTE(v int) predicate.InviteCode { + return predicate.InviteCode(sql.FieldGTE(FieldMaxUses, v)) +} + +// MaxUsesLT applies the LT predicate on the "max_uses" field. +func MaxUsesLT(v int) predicate.InviteCode { + return predicate.InviteCode(sql.FieldLT(FieldMaxUses, v)) +} + +// MaxUsesLTE applies the LTE predicate on the "max_uses" field. +func MaxUsesLTE(v int) predicate.InviteCode { + return predicate.InviteCode(sql.FieldLTE(FieldMaxUses, v)) +} + +// UseCountEQ applies the EQ predicate on the "use_count" field. +func UseCountEQ(v int) predicate.InviteCode { + return predicate.InviteCode(sql.FieldEQ(FieldUseCount, v)) +} + +// UseCountNEQ applies the NEQ predicate on the "use_count" field. +func UseCountNEQ(v int) predicate.InviteCode { + return predicate.InviteCode(sql.FieldNEQ(FieldUseCount, v)) +} + +// UseCountIn applies the In predicate on the "use_count" field. +func UseCountIn(vs ...int) predicate.InviteCode { + return predicate.InviteCode(sql.FieldIn(FieldUseCount, vs...)) +} + +// UseCountNotIn applies the NotIn predicate on the "use_count" field. +func UseCountNotIn(vs ...int) predicate.InviteCode { + return predicate.InviteCode(sql.FieldNotIn(FieldUseCount, vs...)) +} + +// UseCountGT applies the GT predicate on the "use_count" field. +func UseCountGT(v int) predicate.InviteCode { + return predicate.InviteCode(sql.FieldGT(FieldUseCount, v)) +} + +// UseCountGTE applies the GTE predicate on the "use_count" field. +func UseCountGTE(v int) predicate.InviteCode { + return predicate.InviteCode(sql.FieldGTE(FieldUseCount, v)) +} + +// UseCountLT applies the LT predicate on the "use_count" field. +func UseCountLT(v int) predicate.InviteCode { + return predicate.InviteCode(sql.FieldLT(FieldUseCount, v)) +} + +// UseCountLTE applies the LTE predicate on the "use_count" field. +func UseCountLTE(v int) predicate.InviteCode { + return predicate.InviteCode(sql.FieldLTE(FieldUseCount, v)) +} + +// ExpiresAtEQ applies the EQ predicate on the "expires_at" field. +func ExpiresAtEQ(v time.Time) predicate.InviteCode { + return predicate.InviteCode(sql.FieldEQ(FieldExpiresAt, v)) +} + +// ExpiresAtNEQ applies the NEQ predicate on the "expires_at" field. +func ExpiresAtNEQ(v time.Time) predicate.InviteCode { + return predicate.InviteCode(sql.FieldNEQ(FieldExpiresAt, v)) +} + +// ExpiresAtIn applies the In predicate on the "expires_at" field. +func ExpiresAtIn(vs ...time.Time) predicate.InviteCode { + return predicate.InviteCode(sql.FieldIn(FieldExpiresAt, vs...)) +} + +// ExpiresAtNotIn applies the NotIn predicate on the "expires_at" field. +func ExpiresAtNotIn(vs ...time.Time) predicate.InviteCode { + return predicate.InviteCode(sql.FieldNotIn(FieldExpiresAt, vs...)) +} + +// ExpiresAtGT applies the GT predicate on the "expires_at" field. +func ExpiresAtGT(v time.Time) predicate.InviteCode { + return predicate.InviteCode(sql.FieldGT(FieldExpiresAt, v)) +} + +// ExpiresAtGTE applies the GTE predicate on the "expires_at" field. +func ExpiresAtGTE(v time.Time) predicate.InviteCode { + return predicate.InviteCode(sql.FieldGTE(FieldExpiresAt, v)) +} + +// ExpiresAtLT applies the LT predicate on the "expires_at" field. +func ExpiresAtLT(v time.Time) predicate.InviteCode { + return predicate.InviteCode(sql.FieldLT(FieldExpiresAt, v)) +} + +// ExpiresAtLTE applies the LTE predicate on the "expires_at" field. +func ExpiresAtLTE(v time.Time) predicate.InviteCode { + return predicate.InviteCode(sql.FieldLTE(FieldExpiresAt, v)) +} + +// RevokedEQ applies the EQ predicate on the "revoked" field. +func RevokedEQ(v bool) predicate.InviteCode { + return predicate.InviteCode(sql.FieldEQ(FieldRevoked, v)) +} + +// RevokedNEQ applies the NEQ predicate on the "revoked" field. +func RevokedNEQ(v bool) predicate.InviteCode { + return predicate.InviteCode(sql.FieldNEQ(FieldRevoked, v)) +} + +// CreatedByEQ applies the EQ predicate on the "created_by" field. +func CreatedByEQ(v string) predicate.InviteCode { + return predicate.InviteCode(sql.FieldEQ(FieldCreatedBy, v)) +} + +// CreatedByNEQ applies the NEQ predicate on the "created_by" field. +func CreatedByNEQ(v string) predicate.InviteCode { + return predicate.InviteCode(sql.FieldNEQ(FieldCreatedBy, v)) +} + +// CreatedByIn applies the In predicate on the "created_by" field. +func CreatedByIn(vs ...string) predicate.InviteCode { + return predicate.InviteCode(sql.FieldIn(FieldCreatedBy, vs...)) +} + +// CreatedByNotIn applies the NotIn predicate on the "created_by" field. +func CreatedByNotIn(vs ...string) predicate.InviteCode { + return predicate.InviteCode(sql.FieldNotIn(FieldCreatedBy, vs...)) +} + +// CreatedByGT applies the GT predicate on the "created_by" field. +func CreatedByGT(v string) predicate.InviteCode { + return predicate.InviteCode(sql.FieldGT(FieldCreatedBy, v)) +} + +// CreatedByGTE applies the GTE predicate on the "created_by" field. +func CreatedByGTE(v string) predicate.InviteCode { + return predicate.InviteCode(sql.FieldGTE(FieldCreatedBy, v)) +} + +// CreatedByLT applies the LT predicate on the "created_by" field. +func CreatedByLT(v string) predicate.InviteCode { + return predicate.InviteCode(sql.FieldLT(FieldCreatedBy, v)) +} + +// CreatedByLTE applies the LTE predicate on the "created_by" field. +func CreatedByLTE(v string) predicate.InviteCode { + return predicate.InviteCode(sql.FieldLTE(FieldCreatedBy, v)) +} + +// CreatedByContains applies the Contains predicate on the "created_by" field. +func CreatedByContains(v string) predicate.InviteCode { + return predicate.InviteCode(sql.FieldContains(FieldCreatedBy, v)) +} + +// CreatedByHasPrefix applies the HasPrefix predicate on the "created_by" field. +func CreatedByHasPrefix(v string) predicate.InviteCode { + return predicate.InviteCode(sql.FieldHasPrefix(FieldCreatedBy, v)) +} + +// CreatedByHasSuffix applies the HasSuffix predicate on the "created_by" field. +func CreatedByHasSuffix(v string) predicate.InviteCode { + return predicate.InviteCode(sql.FieldHasSuffix(FieldCreatedBy, v)) +} + +// CreatedByEqualFold applies the EqualFold predicate on the "created_by" field. +func CreatedByEqualFold(v string) predicate.InviteCode { + return predicate.InviteCode(sql.FieldEqualFold(FieldCreatedBy, v)) +} + +// CreatedByContainsFold applies the ContainsFold predicate on the "created_by" field. +func CreatedByContainsFold(v string) predicate.InviteCode { + return predicate.InviteCode(sql.FieldContainsFold(FieldCreatedBy, v)) +} + +// NoteEQ applies the EQ predicate on the "note" field. +func NoteEQ(v string) predicate.InviteCode { + return predicate.InviteCode(sql.FieldEQ(FieldNote, v)) +} + +// NoteNEQ applies the NEQ predicate on the "note" field. +func NoteNEQ(v string) predicate.InviteCode { + return predicate.InviteCode(sql.FieldNEQ(FieldNote, v)) +} + +// NoteIn applies the In predicate on the "note" field. +func NoteIn(vs ...string) predicate.InviteCode { + return predicate.InviteCode(sql.FieldIn(FieldNote, vs...)) +} + +// NoteNotIn applies the NotIn predicate on the "note" field. +func NoteNotIn(vs ...string) predicate.InviteCode { + return predicate.InviteCode(sql.FieldNotIn(FieldNote, vs...)) +} + +// NoteGT applies the GT predicate on the "note" field. +func NoteGT(v string) predicate.InviteCode { + return predicate.InviteCode(sql.FieldGT(FieldNote, v)) +} + +// NoteGTE applies the GTE predicate on the "note" field. +func NoteGTE(v string) predicate.InviteCode { + return predicate.InviteCode(sql.FieldGTE(FieldNote, v)) +} + +// NoteLT applies the LT predicate on the "note" field. +func NoteLT(v string) predicate.InviteCode { + return predicate.InviteCode(sql.FieldLT(FieldNote, v)) +} + +// NoteLTE applies the LTE predicate on the "note" field. +func NoteLTE(v string) predicate.InviteCode { + return predicate.InviteCode(sql.FieldLTE(FieldNote, v)) +} + +// NoteContains applies the Contains predicate on the "note" field. +func NoteContains(v string) predicate.InviteCode { + return predicate.InviteCode(sql.FieldContains(FieldNote, v)) +} + +// NoteHasPrefix applies the HasPrefix predicate on the "note" field. +func NoteHasPrefix(v string) predicate.InviteCode { + return predicate.InviteCode(sql.FieldHasPrefix(FieldNote, v)) +} + +// NoteHasSuffix applies the HasSuffix predicate on the "note" field. +func NoteHasSuffix(v string) predicate.InviteCode { + return predicate.InviteCode(sql.FieldHasSuffix(FieldNote, v)) +} + +// NoteEqualFold applies the EqualFold predicate on the "note" field. +func NoteEqualFold(v string) predicate.InviteCode { + return predicate.InviteCode(sql.FieldEqualFold(FieldNote, v)) +} + +// NoteContainsFold applies the ContainsFold predicate on the "note" field. +func NoteContainsFold(v string) predicate.InviteCode { + return predicate.InviteCode(sql.FieldContainsFold(FieldNote, v)) +} + +// CreatedEQ applies the EQ predicate on the "created" field. +func CreatedEQ(v time.Time) predicate.InviteCode { + return predicate.InviteCode(sql.FieldEQ(FieldCreated, v)) +} + +// CreatedNEQ applies the NEQ predicate on the "created" field. +func CreatedNEQ(v time.Time) predicate.InviteCode { + return predicate.InviteCode(sql.FieldNEQ(FieldCreated, v)) +} + +// CreatedIn applies the In predicate on the "created" field. +func CreatedIn(vs ...time.Time) predicate.InviteCode { + return predicate.InviteCode(sql.FieldIn(FieldCreated, vs...)) +} + +// CreatedNotIn applies the NotIn predicate on the "created" field. +func CreatedNotIn(vs ...time.Time) predicate.InviteCode { + return predicate.InviteCode(sql.FieldNotIn(FieldCreated, vs...)) +} + +// CreatedGT applies the GT predicate on the "created" field. +func CreatedGT(v time.Time) predicate.InviteCode { + return predicate.InviteCode(sql.FieldGT(FieldCreated, v)) +} + +// CreatedGTE applies the GTE predicate on the "created" field. +func CreatedGTE(v time.Time) predicate.InviteCode { + return predicate.InviteCode(sql.FieldGTE(FieldCreated, v)) +} + +// CreatedLT applies the LT predicate on the "created" field. +func CreatedLT(v time.Time) predicate.InviteCode { + return predicate.InviteCode(sql.FieldLT(FieldCreated, v)) +} + +// CreatedLTE applies the LTE predicate on the "created" field. +func CreatedLTE(v time.Time) predicate.InviteCode { + return predicate.InviteCode(sql.FieldLTE(FieldCreated, v)) +} + +// And groups predicates with the AND operator between them. +func And(predicates ...predicate.InviteCode) predicate.InviteCode { + return predicate.InviteCode(sql.AndPredicates(predicates...)) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.InviteCode) predicate.InviteCode { + return predicate.InviteCode(sql.OrPredicates(predicates...)) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.InviteCode) predicate.InviteCode { + return predicate.InviteCode(sql.NotPredicates(p)) +} diff --git a/pkg/ent/invitecode_create.go b/pkg/ent/invitecode_create.go new file mode 100644 index 000000000..5668318fb --- /dev/null +++ b/pkg/ent/invitecode_create.go @@ -0,0 +1,1014 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/invitecode" + "github.com/google/uuid" +) + +// InviteCodeCreate is the builder for creating a InviteCode entity. +type InviteCodeCreate struct { + config + mutation *InviteCodeMutation + hooks []Hook + conflict []sql.ConflictOption +} + +// SetCodeHash sets the "code_hash" field. +func (_c *InviteCodeCreate) SetCodeHash(v string) *InviteCodeCreate { + _c.mutation.SetCodeHash(v) + return _c +} + +// SetCodePrefix sets the "code_prefix" field. +func (_c *InviteCodeCreate) SetCodePrefix(v string) *InviteCodeCreate { + _c.mutation.SetCodePrefix(v) + return _c +} + +// SetMaxUses sets the "max_uses" field. +func (_c *InviteCodeCreate) SetMaxUses(v int) *InviteCodeCreate { + _c.mutation.SetMaxUses(v) + return _c +} + +// SetNillableMaxUses sets the "max_uses" field if the given value is not nil. +func (_c *InviteCodeCreate) SetNillableMaxUses(v *int) *InviteCodeCreate { + if v != nil { + _c.SetMaxUses(*v) + } + return _c +} + +// SetUseCount sets the "use_count" field. +func (_c *InviteCodeCreate) SetUseCount(v int) *InviteCodeCreate { + _c.mutation.SetUseCount(v) + return _c +} + +// SetNillableUseCount sets the "use_count" field if the given value is not nil. +func (_c *InviteCodeCreate) SetNillableUseCount(v *int) *InviteCodeCreate { + if v != nil { + _c.SetUseCount(*v) + } + return _c +} + +// SetExpiresAt sets the "expires_at" field. +func (_c *InviteCodeCreate) SetExpiresAt(v time.Time) *InviteCodeCreate { + _c.mutation.SetExpiresAt(v) + return _c +} + +// SetRevoked sets the "revoked" field. +func (_c *InviteCodeCreate) SetRevoked(v bool) *InviteCodeCreate { + _c.mutation.SetRevoked(v) + return _c +} + +// SetNillableRevoked sets the "revoked" field if the given value is not nil. +func (_c *InviteCodeCreate) SetNillableRevoked(v *bool) *InviteCodeCreate { + if v != nil { + _c.SetRevoked(*v) + } + return _c +} + +// SetCreatedBy sets the "created_by" field. +func (_c *InviteCodeCreate) SetCreatedBy(v string) *InviteCodeCreate { + _c.mutation.SetCreatedBy(v) + return _c +} + +// SetNote sets the "note" field. +func (_c *InviteCodeCreate) SetNote(v string) *InviteCodeCreate { + _c.mutation.SetNote(v) + return _c +} + +// SetNillableNote sets the "note" field if the given value is not nil. +func (_c *InviteCodeCreate) SetNillableNote(v *string) *InviteCodeCreate { + if v != nil { + _c.SetNote(*v) + } + return _c +} + +// SetCreated sets the "created" field. +func (_c *InviteCodeCreate) SetCreated(v time.Time) *InviteCodeCreate { + _c.mutation.SetCreated(v) + return _c +} + +// SetNillableCreated sets the "created" field if the given value is not nil. +func (_c *InviteCodeCreate) SetNillableCreated(v *time.Time) *InviteCodeCreate { + if v != nil { + _c.SetCreated(*v) + } + return _c +} + +// SetID sets the "id" field. +func (_c *InviteCodeCreate) SetID(v uuid.UUID) *InviteCodeCreate { + _c.mutation.SetID(v) + return _c +} + +// SetNillableID sets the "id" field if the given value is not nil. +func (_c *InviteCodeCreate) SetNillableID(v *uuid.UUID) *InviteCodeCreate { + if v != nil { + _c.SetID(*v) + } + return _c +} + +// Mutation returns the InviteCodeMutation object of the builder. +func (_c *InviteCodeCreate) Mutation() *InviteCodeMutation { + return _c.mutation +} + +// Save creates the InviteCode in the database. +func (_c *InviteCodeCreate) Save(ctx context.Context) (*InviteCode, error) { + _c.defaults() + return withHooks(ctx, _c.sqlSave, _c.mutation, _c.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (_c *InviteCodeCreate) SaveX(ctx context.Context) *InviteCode { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *InviteCodeCreate) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *InviteCodeCreate) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_c *InviteCodeCreate) defaults() { + if _, ok := _c.mutation.MaxUses(); !ok { + v := invitecode.DefaultMaxUses + _c.mutation.SetMaxUses(v) + } + if _, ok := _c.mutation.UseCount(); !ok { + v := invitecode.DefaultUseCount + _c.mutation.SetUseCount(v) + } + if _, ok := _c.mutation.Revoked(); !ok { + v := invitecode.DefaultRevoked + _c.mutation.SetRevoked(v) + } + if _, ok := _c.mutation.Note(); !ok { + v := invitecode.DefaultNote + _c.mutation.SetNote(v) + } + if _, ok := _c.mutation.Created(); !ok { + v := invitecode.DefaultCreated() + _c.mutation.SetCreated(v) + } + if _, ok := _c.mutation.ID(); !ok { + v := invitecode.DefaultID() + _c.mutation.SetID(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_c *InviteCodeCreate) check() error { + if _, ok := _c.mutation.CodeHash(); !ok { + return &ValidationError{Name: "code_hash", err: errors.New(`ent: missing required field "InviteCode.code_hash"`)} + } + if v, ok := _c.mutation.CodeHash(); ok { + if err := invitecode.CodeHashValidator(v); err != nil { + return &ValidationError{Name: "code_hash", err: fmt.Errorf(`ent: validator failed for field "InviteCode.code_hash": %w`, err)} + } + } + if _, ok := _c.mutation.CodePrefix(); !ok { + return &ValidationError{Name: "code_prefix", err: errors.New(`ent: missing required field "InviteCode.code_prefix"`)} + } + if v, ok := _c.mutation.CodePrefix(); ok { + if err := invitecode.CodePrefixValidator(v); err != nil { + return &ValidationError{Name: "code_prefix", err: fmt.Errorf(`ent: validator failed for field "InviteCode.code_prefix": %w`, err)} + } + } + if _, ok := _c.mutation.MaxUses(); !ok { + return &ValidationError{Name: "max_uses", err: errors.New(`ent: missing required field "InviteCode.max_uses"`)} + } + if _, ok := _c.mutation.UseCount(); !ok { + return &ValidationError{Name: "use_count", err: errors.New(`ent: missing required field "InviteCode.use_count"`)} + } + if _, ok := _c.mutation.ExpiresAt(); !ok { + return &ValidationError{Name: "expires_at", err: errors.New(`ent: missing required field "InviteCode.expires_at"`)} + } + if _, ok := _c.mutation.Revoked(); !ok { + return &ValidationError{Name: "revoked", err: errors.New(`ent: missing required field "InviteCode.revoked"`)} + } + if _, ok := _c.mutation.CreatedBy(); !ok { + return &ValidationError{Name: "created_by", err: errors.New(`ent: missing required field "InviteCode.created_by"`)} + } + if v, ok := _c.mutation.CreatedBy(); ok { + if err := invitecode.CreatedByValidator(v); err != nil { + return &ValidationError{Name: "created_by", err: fmt.Errorf(`ent: validator failed for field "InviteCode.created_by": %w`, err)} + } + } + if _, ok := _c.mutation.Note(); !ok { + return &ValidationError{Name: "note", err: errors.New(`ent: missing required field "InviteCode.note"`)} + } + if _, ok := _c.mutation.Created(); !ok { + return &ValidationError{Name: "created", err: errors.New(`ent: missing required field "InviteCode.created"`)} + } + return nil +} + +func (_c *InviteCodeCreate) sqlSave(ctx context.Context) (*InviteCode, error) { + if err := _c.check(); err != nil { + return nil, err + } + _node, _spec := _c.createSpec() + if err := sqlgraph.CreateNode(ctx, _c.driver, _spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + if _spec.ID.Value != nil { + if id, ok := _spec.ID.Value.(*uuid.UUID); ok { + _node.ID = *id + } else if err := _node.ID.Scan(_spec.ID.Value); err != nil { + return nil, err + } + } + _c.mutation.id = &_node.ID + _c.mutation.done = true + return _node, nil +} + +func (_c *InviteCodeCreate) createSpec() (*InviteCode, *sqlgraph.CreateSpec) { + var ( + _node = &InviteCode{config: _c.config} + _spec = sqlgraph.NewCreateSpec(invitecode.Table, sqlgraph.NewFieldSpec(invitecode.FieldID, field.TypeUUID)) + ) + _spec.OnConflict = _c.conflict + if id, ok := _c.mutation.ID(); ok { + _node.ID = id + _spec.ID.Value = &id + } + if value, ok := _c.mutation.CodeHash(); ok { + _spec.SetField(invitecode.FieldCodeHash, field.TypeString, value) + _node.CodeHash = value + } + if value, ok := _c.mutation.CodePrefix(); ok { + _spec.SetField(invitecode.FieldCodePrefix, field.TypeString, value) + _node.CodePrefix = value + } + if value, ok := _c.mutation.MaxUses(); ok { + _spec.SetField(invitecode.FieldMaxUses, field.TypeInt, value) + _node.MaxUses = value + } + if value, ok := _c.mutation.UseCount(); ok { + _spec.SetField(invitecode.FieldUseCount, field.TypeInt, value) + _node.UseCount = value + } + if value, ok := _c.mutation.ExpiresAt(); ok { + _spec.SetField(invitecode.FieldExpiresAt, field.TypeTime, value) + _node.ExpiresAt = value + } + if value, ok := _c.mutation.Revoked(); ok { + _spec.SetField(invitecode.FieldRevoked, field.TypeBool, value) + _node.Revoked = value + } + if value, ok := _c.mutation.CreatedBy(); ok { + _spec.SetField(invitecode.FieldCreatedBy, field.TypeString, value) + _node.CreatedBy = value + } + if value, ok := _c.mutation.Note(); ok { + _spec.SetField(invitecode.FieldNote, field.TypeString, value) + _node.Note = value + } + if value, ok := _c.mutation.Created(); ok { + _spec.SetField(invitecode.FieldCreated, field.TypeTime, value) + _node.Created = value + } + return _node, _spec +} + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.InviteCode.Create(). +// SetCodeHash(v). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.InviteCodeUpsert) { +// SetCodeHash(v+v). +// }). +// Exec(ctx) +func (_c *InviteCodeCreate) OnConflict(opts ...sql.ConflictOption) *InviteCodeUpsertOne { + _c.conflict = opts + return &InviteCodeUpsertOne{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.InviteCode.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *InviteCodeCreate) OnConflictColumns(columns ...string) *InviteCodeUpsertOne { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &InviteCodeUpsertOne{ + create: _c, + } +} + +type ( + // InviteCodeUpsertOne is the builder for "upsert"-ing + // one InviteCode node. + InviteCodeUpsertOne struct { + create *InviteCodeCreate + } + + // InviteCodeUpsert is the "OnConflict" setter. + InviteCodeUpsert struct { + *sql.UpdateSet + } +) + +// SetCodeHash sets the "code_hash" field. +func (u *InviteCodeUpsert) SetCodeHash(v string) *InviteCodeUpsert { + u.Set(invitecode.FieldCodeHash, v) + return u +} + +// UpdateCodeHash sets the "code_hash" field to the value that was provided on create. +func (u *InviteCodeUpsert) UpdateCodeHash() *InviteCodeUpsert { + u.SetExcluded(invitecode.FieldCodeHash) + return u +} + +// SetCodePrefix sets the "code_prefix" field. +func (u *InviteCodeUpsert) SetCodePrefix(v string) *InviteCodeUpsert { + u.Set(invitecode.FieldCodePrefix, v) + return u +} + +// UpdateCodePrefix sets the "code_prefix" field to the value that was provided on create. +func (u *InviteCodeUpsert) UpdateCodePrefix() *InviteCodeUpsert { + u.SetExcluded(invitecode.FieldCodePrefix) + return u +} + +// SetMaxUses sets the "max_uses" field. +func (u *InviteCodeUpsert) SetMaxUses(v int) *InviteCodeUpsert { + u.Set(invitecode.FieldMaxUses, v) + return u +} + +// UpdateMaxUses sets the "max_uses" field to the value that was provided on create. +func (u *InviteCodeUpsert) UpdateMaxUses() *InviteCodeUpsert { + u.SetExcluded(invitecode.FieldMaxUses) + return u +} + +// AddMaxUses adds v to the "max_uses" field. +func (u *InviteCodeUpsert) AddMaxUses(v int) *InviteCodeUpsert { + u.Add(invitecode.FieldMaxUses, v) + return u +} + +// SetUseCount sets the "use_count" field. +func (u *InviteCodeUpsert) SetUseCount(v int) *InviteCodeUpsert { + u.Set(invitecode.FieldUseCount, v) + return u +} + +// UpdateUseCount sets the "use_count" field to the value that was provided on create. +func (u *InviteCodeUpsert) UpdateUseCount() *InviteCodeUpsert { + u.SetExcluded(invitecode.FieldUseCount) + return u +} + +// AddUseCount adds v to the "use_count" field. +func (u *InviteCodeUpsert) AddUseCount(v int) *InviteCodeUpsert { + u.Add(invitecode.FieldUseCount, v) + return u +} + +// SetExpiresAt sets the "expires_at" field. +func (u *InviteCodeUpsert) SetExpiresAt(v time.Time) *InviteCodeUpsert { + u.Set(invitecode.FieldExpiresAt, v) + return u +} + +// UpdateExpiresAt sets the "expires_at" field to the value that was provided on create. +func (u *InviteCodeUpsert) UpdateExpiresAt() *InviteCodeUpsert { + u.SetExcluded(invitecode.FieldExpiresAt) + return u +} + +// SetRevoked sets the "revoked" field. +func (u *InviteCodeUpsert) SetRevoked(v bool) *InviteCodeUpsert { + u.Set(invitecode.FieldRevoked, v) + return u +} + +// UpdateRevoked sets the "revoked" field to the value that was provided on create. +func (u *InviteCodeUpsert) UpdateRevoked() *InviteCodeUpsert { + u.SetExcluded(invitecode.FieldRevoked) + return u +} + +// SetCreatedBy sets the "created_by" field. +func (u *InviteCodeUpsert) SetCreatedBy(v string) *InviteCodeUpsert { + u.Set(invitecode.FieldCreatedBy, v) + return u +} + +// UpdateCreatedBy sets the "created_by" field to the value that was provided on create. +func (u *InviteCodeUpsert) UpdateCreatedBy() *InviteCodeUpsert { + u.SetExcluded(invitecode.FieldCreatedBy) + return u +} + +// SetNote sets the "note" field. +func (u *InviteCodeUpsert) SetNote(v string) *InviteCodeUpsert { + u.Set(invitecode.FieldNote, v) + return u +} + +// UpdateNote sets the "note" field to the value that was provided on create. +func (u *InviteCodeUpsert) UpdateNote() *InviteCodeUpsert { + u.SetExcluded(invitecode.FieldNote) + return u +} + +// UpdateNewValues updates the mutable fields using the new values that were set on create except the ID field. +// Using this option is equivalent to using: +// +// client.InviteCode.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(invitecode.FieldID) +// }), +// ). +// Exec(ctx) +func (u *InviteCodeUpsertOne) UpdateNewValues() *InviteCodeUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + if _, exists := u.create.mutation.ID(); exists { + s.SetIgnore(invitecode.FieldID) + } + if _, exists := u.create.mutation.Created(); exists { + s.SetIgnore(invitecode.FieldCreated) + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.InviteCode.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *InviteCodeUpsertOne) Ignore() *InviteCodeUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *InviteCodeUpsertOne) DoNothing() *InviteCodeUpsertOne { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the InviteCodeCreate.OnConflict +// documentation for more info. +func (u *InviteCodeUpsertOne) Update(set func(*InviteCodeUpsert)) *InviteCodeUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&InviteCodeUpsert{UpdateSet: update}) + })) + return u +} + +// SetCodeHash sets the "code_hash" field. +func (u *InviteCodeUpsertOne) SetCodeHash(v string) *InviteCodeUpsertOne { + return u.Update(func(s *InviteCodeUpsert) { + s.SetCodeHash(v) + }) +} + +// UpdateCodeHash sets the "code_hash" field to the value that was provided on create. +func (u *InviteCodeUpsertOne) UpdateCodeHash() *InviteCodeUpsertOne { + return u.Update(func(s *InviteCodeUpsert) { + s.UpdateCodeHash() + }) +} + +// SetCodePrefix sets the "code_prefix" field. +func (u *InviteCodeUpsertOne) SetCodePrefix(v string) *InviteCodeUpsertOne { + return u.Update(func(s *InviteCodeUpsert) { + s.SetCodePrefix(v) + }) +} + +// UpdateCodePrefix sets the "code_prefix" field to the value that was provided on create. +func (u *InviteCodeUpsertOne) UpdateCodePrefix() *InviteCodeUpsertOne { + return u.Update(func(s *InviteCodeUpsert) { + s.UpdateCodePrefix() + }) +} + +// SetMaxUses sets the "max_uses" field. +func (u *InviteCodeUpsertOne) SetMaxUses(v int) *InviteCodeUpsertOne { + return u.Update(func(s *InviteCodeUpsert) { + s.SetMaxUses(v) + }) +} + +// AddMaxUses adds v to the "max_uses" field. +func (u *InviteCodeUpsertOne) AddMaxUses(v int) *InviteCodeUpsertOne { + return u.Update(func(s *InviteCodeUpsert) { + s.AddMaxUses(v) + }) +} + +// UpdateMaxUses sets the "max_uses" field to the value that was provided on create. +func (u *InviteCodeUpsertOne) UpdateMaxUses() *InviteCodeUpsertOne { + return u.Update(func(s *InviteCodeUpsert) { + s.UpdateMaxUses() + }) +} + +// SetUseCount sets the "use_count" field. +func (u *InviteCodeUpsertOne) SetUseCount(v int) *InviteCodeUpsertOne { + return u.Update(func(s *InviteCodeUpsert) { + s.SetUseCount(v) + }) +} + +// AddUseCount adds v to the "use_count" field. +func (u *InviteCodeUpsertOne) AddUseCount(v int) *InviteCodeUpsertOne { + return u.Update(func(s *InviteCodeUpsert) { + s.AddUseCount(v) + }) +} + +// UpdateUseCount sets the "use_count" field to the value that was provided on create. +func (u *InviteCodeUpsertOne) UpdateUseCount() *InviteCodeUpsertOne { + return u.Update(func(s *InviteCodeUpsert) { + s.UpdateUseCount() + }) +} + +// SetExpiresAt sets the "expires_at" field. +func (u *InviteCodeUpsertOne) SetExpiresAt(v time.Time) *InviteCodeUpsertOne { + return u.Update(func(s *InviteCodeUpsert) { + s.SetExpiresAt(v) + }) +} + +// UpdateExpiresAt sets the "expires_at" field to the value that was provided on create. +func (u *InviteCodeUpsertOne) UpdateExpiresAt() *InviteCodeUpsertOne { + return u.Update(func(s *InviteCodeUpsert) { + s.UpdateExpiresAt() + }) +} + +// SetRevoked sets the "revoked" field. +func (u *InviteCodeUpsertOne) SetRevoked(v bool) *InviteCodeUpsertOne { + return u.Update(func(s *InviteCodeUpsert) { + s.SetRevoked(v) + }) +} + +// UpdateRevoked sets the "revoked" field to the value that was provided on create. +func (u *InviteCodeUpsertOne) UpdateRevoked() *InviteCodeUpsertOne { + return u.Update(func(s *InviteCodeUpsert) { + s.UpdateRevoked() + }) +} + +// SetCreatedBy sets the "created_by" field. +func (u *InviteCodeUpsertOne) SetCreatedBy(v string) *InviteCodeUpsertOne { + return u.Update(func(s *InviteCodeUpsert) { + s.SetCreatedBy(v) + }) +} + +// UpdateCreatedBy sets the "created_by" field to the value that was provided on create. +func (u *InviteCodeUpsertOne) UpdateCreatedBy() *InviteCodeUpsertOne { + return u.Update(func(s *InviteCodeUpsert) { + s.UpdateCreatedBy() + }) +} + +// SetNote sets the "note" field. +func (u *InviteCodeUpsertOne) SetNote(v string) *InviteCodeUpsertOne { + return u.Update(func(s *InviteCodeUpsert) { + s.SetNote(v) + }) +} + +// UpdateNote sets the "note" field to the value that was provided on create. +func (u *InviteCodeUpsertOne) UpdateNote() *InviteCodeUpsertOne { + return u.Update(func(s *InviteCodeUpsert) { + s.UpdateNote() + }) +} + +// Exec executes the query. +func (u *InviteCodeUpsertOne) Exec(ctx context.Context) error { + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for InviteCodeCreate.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *InviteCodeUpsertOne) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} + +// Exec executes the UPSERT query and returns the inserted/updated ID. +func (u *InviteCodeUpsertOne) ID(ctx context.Context) (id uuid.UUID, err error) { + if u.create.driver.Dialect() == dialect.MySQL { + // In case of "ON CONFLICT", there is no way to get back non-numeric ID + // fields from the database since MySQL does not support the RETURNING clause. + return id, errors.New("ent: InviteCodeUpsertOne.ID is not supported by MySQL driver. Use InviteCodeUpsertOne.Exec instead") + } + node, err := u.create.Save(ctx) + if err != nil { + return id, err + } + return node.ID, nil +} + +// IDX is like ID, but panics if an error occurs. +func (u *InviteCodeUpsertOne) IDX(ctx context.Context) uuid.UUID { + id, err := u.ID(ctx) + if err != nil { + panic(err) + } + return id +} + +// InviteCodeCreateBulk is the builder for creating many InviteCode entities in bulk. +type InviteCodeCreateBulk struct { + config + err error + builders []*InviteCodeCreate + conflict []sql.ConflictOption +} + +// Save creates the InviteCode entities in the database. +func (_c *InviteCodeCreateBulk) Save(ctx context.Context) ([]*InviteCode, error) { + if _c.err != nil { + return nil, _c.err + } + specs := make([]*sqlgraph.CreateSpec, len(_c.builders)) + nodes := make([]*InviteCode, len(_c.builders)) + mutators := make([]Mutator, len(_c.builders)) + for i := range _c.builders { + func(i int, root context.Context) { + builder := _c.builders[i] + builder.defaults() + var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { + mutation, ok := m.(*InviteCodeMutation) + if !ok { + return nil, fmt.Errorf("unexpected mutation type %T", m) + } + if err := builder.check(); err != nil { + return nil, err + } + builder.mutation = mutation + var err error + nodes[i], specs[i] = builder.createSpec() + if i < len(mutators)-1 { + _, err = mutators[i+1].Mutate(root, _c.builders[i+1].mutation) + } else { + spec := &sqlgraph.BatchCreateSpec{Nodes: specs} + spec.OnConflict = _c.conflict + // Invoke the actual operation on the latest mutation in the chain. + if err = sqlgraph.BatchCreate(ctx, _c.driver, spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + } + } + if err != nil { + return nil, err + } + mutation.id = &nodes[i].ID + mutation.done = true + return nodes[i], nil + }) + for i := len(builder.hooks) - 1; i >= 0; i-- { + mut = builder.hooks[i](mut) + } + mutators[i] = mut + }(i, ctx) + } + if len(mutators) > 0 { + if _, err := mutators[0].Mutate(ctx, _c.builders[0].mutation); err != nil { + return nil, err + } + } + return nodes, nil +} + +// SaveX is like Save, but panics if an error occurs. +func (_c *InviteCodeCreateBulk) SaveX(ctx context.Context) []*InviteCode { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *InviteCodeCreateBulk) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *InviteCodeCreateBulk) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.InviteCode.CreateBulk(builders...). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.InviteCodeUpsert) { +// SetCodeHash(v+v). +// }). +// Exec(ctx) +func (_c *InviteCodeCreateBulk) OnConflict(opts ...sql.ConflictOption) *InviteCodeUpsertBulk { + _c.conflict = opts + return &InviteCodeUpsertBulk{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.InviteCode.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *InviteCodeCreateBulk) OnConflictColumns(columns ...string) *InviteCodeUpsertBulk { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &InviteCodeUpsertBulk{ + create: _c, + } +} + +// InviteCodeUpsertBulk is the builder for "upsert"-ing +// a bulk of InviteCode nodes. +type InviteCodeUpsertBulk struct { + create *InviteCodeCreateBulk +} + +// UpdateNewValues updates the mutable fields using the new values that +// were set on create. Using this option is equivalent to using: +// +// client.InviteCode.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(invitecode.FieldID) +// }), +// ). +// Exec(ctx) +func (u *InviteCodeUpsertBulk) UpdateNewValues() *InviteCodeUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + for _, b := range u.create.builders { + if _, exists := b.mutation.ID(); exists { + s.SetIgnore(invitecode.FieldID) + } + if _, exists := b.mutation.Created(); exists { + s.SetIgnore(invitecode.FieldCreated) + } + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.InviteCode.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *InviteCodeUpsertBulk) Ignore() *InviteCodeUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *InviteCodeUpsertBulk) DoNothing() *InviteCodeUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the InviteCodeCreateBulk.OnConflict +// documentation for more info. +func (u *InviteCodeUpsertBulk) Update(set func(*InviteCodeUpsert)) *InviteCodeUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&InviteCodeUpsert{UpdateSet: update}) + })) + return u +} + +// SetCodeHash sets the "code_hash" field. +func (u *InviteCodeUpsertBulk) SetCodeHash(v string) *InviteCodeUpsertBulk { + return u.Update(func(s *InviteCodeUpsert) { + s.SetCodeHash(v) + }) +} + +// UpdateCodeHash sets the "code_hash" field to the value that was provided on create. +func (u *InviteCodeUpsertBulk) UpdateCodeHash() *InviteCodeUpsertBulk { + return u.Update(func(s *InviteCodeUpsert) { + s.UpdateCodeHash() + }) +} + +// SetCodePrefix sets the "code_prefix" field. +func (u *InviteCodeUpsertBulk) SetCodePrefix(v string) *InviteCodeUpsertBulk { + return u.Update(func(s *InviteCodeUpsert) { + s.SetCodePrefix(v) + }) +} + +// UpdateCodePrefix sets the "code_prefix" field to the value that was provided on create. +func (u *InviteCodeUpsertBulk) UpdateCodePrefix() *InviteCodeUpsertBulk { + return u.Update(func(s *InviteCodeUpsert) { + s.UpdateCodePrefix() + }) +} + +// SetMaxUses sets the "max_uses" field. +func (u *InviteCodeUpsertBulk) SetMaxUses(v int) *InviteCodeUpsertBulk { + return u.Update(func(s *InviteCodeUpsert) { + s.SetMaxUses(v) + }) +} + +// AddMaxUses adds v to the "max_uses" field. +func (u *InviteCodeUpsertBulk) AddMaxUses(v int) *InviteCodeUpsertBulk { + return u.Update(func(s *InviteCodeUpsert) { + s.AddMaxUses(v) + }) +} + +// UpdateMaxUses sets the "max_uses" field to the value that was provided on create. +func (u *InviteCodeUpsertBulk) UpdateMaxUses() *InviteCodeUpsertBulk { + return u.Update(func(s *InviteCodeUpsert) { + s.UpdateMaxUses() + }) +} + +// SetUseCount sets the "use_count" field. +func (u *InviteCodeUpsertBulk) SetUseCount(v int) *InviteCodeUpsertBulk { + return u.Update(func(s *InviteCodeUpsert) { + s.SetUseCount(v) + }) +} + +// AddUseCount adds v to the "use_count" field. +func (u *InviteCodeUpsertBulk) AddUseCount(v int) *InviteCodeUpsertBulk { + return u.Update(func(s *InviteCodeUpsert) { + s.AddUseCount(v) + }) +} + +// UpdateUseCount sets the "use_count" field to the value that was provided on create. +func (u *InviteCodeUpsertBulk) UpdateUseCount() *InviteCodeUpsertBulk { + return u.Update(func(s *InviteCodeUpsert) { + s.UpdateUseCount() + }) +} + +// SetExpiresAt sets the "expires_at" field. +func (u *InviteCodeUpsertBulk) SetExpiresAt(v time.Time) *InviteCodeUpsertBulk { + return u.Update(func(s *InviteCodeUpsert) { + s.SetExpiresAt(v) + }) +} + +// UpdateExpiresAt sets the "expires_at" field to the value that was provided on create. +func (u *InviteCodeUpsertBulk) UpdateExpiresAt() *InviteCodeUpsertBulk { + return u.Update(func(s *InviteCodeUpsert) { + s.UpdateExpiresAt() + }) +} + +// SetRevoked sets the "revoked" field. +func (u *InviteCodeUpsertBulk) SetRevoked(v bool) *InviteCodeUpsertBulk { + return u.Update(func(s *InviteCodeUpsert) { + s.SetRevoked(v) + }) +} + +// UpdateRevoked sets the "revoked" field to the value that was provided on create. +func (u *InviteCodeUpsertBulk) UpdateRevoked() *InviteCodeUpsertBulk { + return u.Update(func(s *InviteCodeUpsert) { + s.UpdateRevoked() + }) +} + +// SetCreatedBy sets the "created_by" field. +func (u *InviteCodeUpsertBulk) SetCreatedBy(v string) *InviteCodeUpsertBulk { + return u.Update(func(s *InviteCodeUpsert) { + s.SetCreatedBy(v) + }) +} + +// UpdateCreatedBy sets the "created_by" field to the value that was provided on create. +func (u *InviteCodeUpsertBulk) UpdateCreatedBy() *InviteCodeUpsertBulk { + return u.Update(func(s *InviteCodeUpsert) { + s.UpdateCreatedBy() + }) +} + +// SetNote sets the "note" field. +func (u *InviteCodeUpsertBulk) SetNote(v string) *InviteCodeUpsertBulk { + return u.Update(func(s *InviteCodeUpsert) { + s.SetNote(v) + }) +} + +// UpdateNote sets the "note" field to the value that was provided on create. +func (u *InviteCodeUpsertBulk) UpdateNote() *InviteCodeUpsertBulk { + return u.Update(func(s *InviteCodeUpsert) { + s.UpdateNote() + }) +} + +// Exec executes the query. +func (u *InviteCodeUpsertBulk) Exec(ctx context.Context) error { + if u.create.err != nil { + return u.create.err + } + for i, b := range u.create.builders { + if len(b.conflict) != 0 { + return fmt.Errorf("ent: OnConflict was set for builder %d. Set it on the InviteCodeCreateBulk instead", i) + } + } + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for InviteCodeCreateBulk.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *InviteCodeUpsertBulk) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/pkg/ent/invitecode_delete.go b/pkg/ent/invitecode_delete.go new file mode 100644 index 000000000..c3fffaff9 --- /dev/null +++ b/pkg/ent/invitecode_delete.go @@ -0,0 +1,88 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/invitecode" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" +) + +// InviteCodeDelete is the builder for deleting a InviteCode entity. +type InviteCodeDelete struct { + config + hooks []Hook + mutation *InviteCodeMutation +} + +// Where appends a list predicates to the InviteCodeDelete builder. +func (_d *InviteCodeDelete) Where(ps ...predicate.InviteCode) *InviteCodeDelete { + _d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (_d *InviteCodeDelete) Exec(ctx context.Context) (int, error) { + return withHooks(ctx, _d.sqlExec, _d.mutation, _d.hooks) +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *InviteCodeDelete) ExecX(ctx context.Context) int { + n, err := _d.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (_d *InviteCodeDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(invitecode.Table, sqlgraph.NewFieldSpec(invitecode.FieldID, field.TypeUUID)) + if ps := _d.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + affected, err := sqlgraph.DeleteNodes(ctx, _d.driver, _spec) + if err != nil && sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + _d.mutation.done = true + return affected, err +} + +// InviteCodeDeleteOne is the builder for deleting a single InviteCode entity. +type InviteCodeDeleteOne struct { + _d *InviteCodeDelete +} + +// Where appends a list predicates to the InviteCodeDelete builder. +func (_d *InviteCodeDeleteOne) Where(ps ...predicate.InviteCode) *InviteCodeDeleteOne { + _d._d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query. +func (_d *InviteCodeDeleteOne) Exec(ctx context.Context) error { + n, err := _d._d.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{invitecode.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *InviteCodeDeleteOne) ExecX(ctx context.Context) { + if err := _d.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/pkg/ent/invitecode_query.go b/pkg/ent/invitecode_query.go new file mode 100644 index 000000000..231959563 --- /dev/null +++ b/pkg/ent/invitecode_query.go @@ -0,0 +1,565 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "fmt" + "math" + + "entgo.io/ent" + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/invitecode" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/google/uuid" +) + +// InviteCodeQuery is the builder for querying InviteCode entities. +type InviteCodeQuery struct { + config + ctx *QueryContext + order []invitecode.OrderOption + inters []Interceptor + predicates []predicate.InviteCode + modifiers []func(*sql.Selector) + // intermediate query (i.e. traversal path). + sql *sql.Selector + path func(context.Context) (*sql.Selector, error) +} + +// Where adds a new predicate for the InviteCodeQuery builder. +func (_q *InviteCodeQuery) Where(ps ...predicate.InviteCode) *InviteCodeQuery { + _q.predicates = append(_q.predicates, ps...) + return _q +} + +// Limit the number of records to be returned by this query. +func (_q *InviteCodeQuery) Limit(limit int) *InviteCodeQuery { + _q.ctx.Limit = &limit + return _q +} + +// Offset to start from. +func (_q *InviteCodeQuery) Offset(offset int) *InviteCodeQuery { + _q.ctx.Offset = &offset + return _q +} + +// Unique configures the query builder to filter duplicate records on query. +// By default, unique is set to true, and can be disabled using this method. +func (_q *InviteCodeQuery) Unique(unique bool) *InviteCodeQuery { + _q.ctx.Unique = &unique + return _q +} + +// Order specifies how the records should be ordered. +func (_q *InviteCodeQuery) Order(o ...invitecode.OrderOption) *InviteCodeQuery { + _q.order = append(_q.order, o...) + return _q +} + +// First returns the first InviteCode entity from the query. +// Returns a *NotFoundError when no InviteCode was found. +func (_q *InviteCodeQuery) First(ctx context.Context) (*InviteCode, error) { + nodes, err := _q.Limit(1).All(setContextOp(ctx, _q.ctx, ent.OpQueryFirst)) + if err != nil { + return nil, err + } + if len(nodes) == 0 { + return nil, &NotFoundError{invitecode.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (_q *InviteCodeQuery) FirstX(ctx context.Context) *InviteCode { + node, err := _q.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// FirstID returns the first InviteCode ID from the query. +// Returns a *NotFoundError when no InviteCode ID was found. +func (_q *InviteCodeQuery) FirstID(ctx context.Context) (id uuid.UUID, err error) { + var ids []uuid.UUID + if ids, err = _q.Limit(1).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryFirstID)); err != nil { + return + } + if len(ids) == 0 { + err = &NotFoundError{invitecode.Label} + return + } + return ids[0], nil +} + +// FirstIDX is like FirstID, but panics if an error occurs. +func (_q *InviteCodeQuery) FirstIDX(ctx context.Context) uuid.UUID { + id, err := _q.FirstID(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return id +} + +// Only returns a single InviteCode entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one InviteCode entity is found. +// Returns a *NotFoundError when no InviteCode entities are found. +func (_q *InviteCodeQuery) Only(ctx context.Context) (*InviteCode, error) { + nodes, err := _q.Limit(2).All(setContextOp(ctx, _q.ctx, ent.OpQueryOnly)) + if err != nil { + return nil, err + } + switch len(nodes) { + case 1: + return nodes[0], nil + case 0: + return nil, &NotFoundError{invitecode.Label} + default: + return nil, &NotSingularError{invitecode.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (_q *InviteCodeQuery) OnlyX(ctx context.Context) *InviteCode { + node, err := _q.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// OnlyID is like Only, but returns the only InviteCode ID in the query. +// Returns a *NotSingularError when more than one InviteCode ID is found. +// Returns a *NotFoundError when no entities are found. +func (_q *InviteCodeQuery) OnlyID(ctx context.Context) (id uuid.UUID, err error) { + var ids []uuid.UUID + if ids, err = _q.Limit(2).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryOnlyID)); err != nil { + return + } + switch len(ids) { + case 1: + id = ids[0] + case 0: + err = &NotFoundError{invitecode.Label} + default: + err = &NotSingularError{invitecode.Label} + } + return +} + +// OnlyIDX is like OnlyID, but panics if an error occurs. +func (_q *InviteCodeQuery) OnlyIDX(ctx context.Context) uuid.UUID { + id, err := _q.OnlyID(ctx) + if err != nil { + panic(err) + } + return id +} + +// All executes the query and returns a list of InviteCodes. +func (_q *InviteCodeQuery) All(ctx context.Context) ([]*InviteCode, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryAll) + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*InviteCode, *InviteCodeQuery]() + return withInterceptors[[]*InviteCode](ctx, _q, qr, _q.inters) +} + +// AllX is like All, but panics if an error occurs. +func (_q *InviteCodeQuery) AllX(ctx context.Context) []*InviteCode { + nodes, err := _q.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// IDs executes the query and returns a list of InviteCode IDs. +func (_q *InviteCodeQuery) IDs(ctx context.Context) (ids []uuid.UUID, err error) { + if _q.ctx.Unique == nil && _q.path != nil { + _q.Unique(true) + } + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryIDs) + if err = _q.Select(invitecode.FieldID).Scan(ctx, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// IDsX is like IDs, but panics if an error occurs. +func (_q *InviteCodeQuery) IDsX(ctx context.Context) []uuid.UUID { + ids, err := _q.IDs(ctx) + if err != nil { + panic(err) + } + return ids +} + +// Count returns the count of the given query. +func (_q *InviteCodeQuery) Count(ctx context.Context) (int, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryCount) + if err := _q.prepareQuery(ctx); err != nil { + return 0, err + } + return withInterceptors[int](ctx, _q, querierCount[*InviteCodeQuery](), _q.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (_q *InviteCodeQuery) CountX(ctx context.Context) int { + count, err := _q.Count(ctx) + if err != nil { + panic(err) + } + return count +} + +// Exist returns true if the query has elements in the graph. +func (_q *InviteCodeQuery) Exist(ctx context.Context) (bool, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryExist) + switch _, err := _q.FirstID(ctx); { + case IsNotFound(err): + return false, nil + case err != nil: + return false, fmt.Errorf("ent: check existence: %w", err) + default: + return true, nil + } +} + +// ExistX is like Exist, but panics if an error occurs. +func (_q *InviteCodeQuery) ExistX(ctx context.Context) bool { + exist, err := _q.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the InviteCodeQuery builder, including all associated steps. It can be +// used to prepare common query builders and use them differently after the clone is made. +func (_q *InviteCodeQuery) Clone() *InviteCodeQuery { + if _q == nil { + return nil + } + return &InviteCodeQuery{ + config: _q.config, + ctx: _q.ctx.Clone(), + order: append([]invitecode.OrderOption{}, _q.order...), + inters: append([]Interceptor{}, _q.inters...), + predicates: append([]predicate.InviteCode{}, _q.predicates...), + // clone intermediate query. + sql: _q.sql.Clone(), + path: _q.path, + } +} + +// GroupBy is used to group vertices by one or more fields/columns. +// It is often used with aggregate functions, like: count, max, mean, min, sum. +// +// Example: +// +// var v []struct { +// CodeHash string `json:"code_hash,omitempty"` +// Count int `json:"count,omitempty"` +// } +// +// client.InviteCode.Query(). +// GroupBy(invitecode.FieldCodeHash). +// Aggregate(ent.Count()). +// Scan(ctx, &v) +func (_q *InviteCodeQuery) GroupBy(field string, fields ...string) *InviteCodeGroupBy { + _q.ctx.Fields = append([]string{field}, fields...) + grbuild := &InviteCodeGroupBy{build: _q} + grbuild.flds = &_q.ctx.Fields + grbuild.label = invitecode.Label + grbuild.scan = grbuild.Scan + return grbuild +} + +// Select allows the selection one or more fields/columns for the given query, +// instead of selecting all fields in the entity. +// +// Example: +// +// var v []struct { +// CodeHash string `json:"code_hash,omitempty"` +// } +// +// client.InviteCode.Query(). +// Select(invitecode.FieldCodeHash). +// Scan(ctx, &v) +func (_q *InviteCodeQuery) Select(fields ...string) *InviteCodeSelect { + _q.ctx.Fields = append(_q.ctx.Fields, fields...) + sbuild := &InviteCodeSelect{InviteCodeQuery: _q} + sbuild.label = invitecode.Label + sbuild.flds, sbuild.scan = &_q.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a InviteCodeSelect configured with the given aggregations. +func (_q *InviteCodeQuery) Aggregate(fns ...AggregateFunc) *InviteCodeSelect { + return _q.Select().Aggregate(fns...) +} + +func (_q *InviteCodeQuery) prepareQuery(ctx context.Context) error { + for _, inter := range _q.inters { + if inter == nil { + return fmt.Errorf("ent: uninitialized interceptor (forgotten import ent/runtime?)") + } + if trv, ok := inter.(Traverser); ok { + if err := trv.Traverse(ctx, _q); err != nil { + return err + } + } + } + for _, f := range _q.ctx.Fields { + if !invitecode.ValidColumn(f) { + return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + } + if _q.path != nil { + prev, err := _q.path(ctx) + if err != nil { + return err + } + _q.sql = prev + } + return nil +} + +func (_q *InviteCodeQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*InviteCode, error) { + var ( + nodes = []*InviteCode{} + _spec = _q.querySpec() + ) + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*InviteCode).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &InviteCode{config: _q.config} + nodes = append(nodes, node) + return node.assignValues(columns, values) + } + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } + for i := range hooks { + hooks[i](ctx, _spec) + } + if err := sqlgraph.QueryNodes(ctx, _q.driver, _spec); err != nil { + return nil, err + } + if len(nodes) == 0 { + return nodes, nil + } + return nodes, nil +} + +func (_q *InviteCodeQuery) sqlCount(ctx context.Context) (int, error) { + _spec := _q.querySpec() + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } + _spec.Node.Columns = _q.ctx.Fields + if len(_q.ctx.Fields) > 0 { + _spec.Unique = _q.ctx.Unique != nil && *_q.ctx.Unique + } + return sqlgraph.CountNodes(ctx, _q.driver, _spec) +} + +func (_q *InviteCodeQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(invitecode.Table, invitecode.Columns, sqlgraph.NewFieldSpec(invitecode.FieldID, field.TypeUUID)) + _spec.From = _q.sql + if unique := _q.ctx.Unique; unique != nil { + _spec.Unique = *unique + } else if _q.path != nil { + _spec.Unique = true + } + if fields := _q.ctx.Fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, invitecode.FieldID) + for i := range fields { + if fields[i] != invitecode.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, fields[i]) + } + } + } + if ps := _q.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if limit := _q.ctx.Limit; limit != nil { + _spec.Limit = *limit + } + if offset := _q.ctx.Offset; offset != nil { + _spec.Offset = *offset + } + if ps := _q.order; len(ps) > 0 { + _spec.Order = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + return _spec +} + +func (_q *InviteCodeQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(_q.driver.Dialect()) + t1 := builder.Table(invitecode.Table) + columns := _q.ctx.Fields + if len(columns) == 0 { + columns = invitecode.Columns + } + selector := builder.Select(t1.Columns(columns...)...).From(t1) + if _q.sql != nil { + selector = _q.sql + selector.Select(selector.Columns(columns...)...) + } + if _q.ctx.Unique != nil && *_q.ctx.Unique { + selector.Distinct() + } + for _, m := range _q.modifiers { + m(selector) + } + for _, p := range _q.predicates { + p(selector) + } + for _, p := range _q.order { + p(selector) + } + if offset := _q.ctx.Offset; offset != nil { + // limit is mandatory for offset clause. We start + // with default value, and override it below if needed. + selector.Offset(*offset).Limit(math.MaxInt32) + } + if limit := _q.ctx.Limit; limit != nil { + selector.Limit(*limit) + } + return selector +} + +// ForUpdate locks the selected rows against concurrent updates, and prevent them from being +// updated, deleted or "selected ... for update" by other sessions, until the transaction is +// either committed or rolled-back. +func (_q *InviteCodeQuery) ForUpdate(opts ...sql.LockOption) *InviteCodeQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForUpdate(opts...) + }) + return _q +} + +// ForShare behaves similarly to ForUpdate, except that it acquires a shared mode lock +// on any rows that are read. Other sessions can read the rows, but cannot modify them +// until your transaction commits. +func (_q *InviteCodeQuery) ForShare(opts ...sql.LockOption) *InviteCodeQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForShare(opts...) + }) + return _q +} + +// InviteCodeGroupBy is the group-by builder for InviteCode entities. +type InviteCodeGroupBy struct { + selector + build *InviteCodeQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (_g *InviteCodeGroupBy) Aggregate(fns ...AggregateFunc) *InviteCodeGroupBy { + _g.fns = append(_g.fns, fns...) + return _g +} + +// Scan applies the selector query and scans the result into the given value. +func (_g *InviteCodeGroupBy) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, _g.build.ctx, ent.OpQueryGroupBy) + if err := _g.build.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*InviteCodeQuery, *InviteCodeGroupBy](ctx, _g.build, _g, _g.build.inters, v) +} + +func (_g *InviteCodeGroupBy) sqlScan(ctx context.Context, root *InviteCodeQuery, v any) error { + selector := root.sqlQuery(ctx).Select() + aggregation := make([]string, 0, len(_g.fns)) + for _, fn := range _g.fns { + aggregation = append(aggregation, fn(selector)) + } + if len(selector.SelectedColumns()) == 0 { + columns := make([]string, 0, len(*_g.flds)+len(_g.fns)) + for _, f := range *_g.flds { + columns = append(columns, selector.C(f)) + } + columns = append(columns, aggregation...) + selector.Select(columns...) + } + selector.GroupBy(selector.Columns(*_g.flds...)...) + if err := selector.Err(); err != nil { + return err + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := _g.build.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} + +// InviteCodeSelect is the builder for selecting fields of InviteCode entities. +type InviteCodeSelect struct { + *InviteCodeQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (_s *InviteCodeSelect) Aggregate(fns ...AggregateFunc) *InviteCodeSelect { + _s.fns = append(_s.fns, fns...) + return _s +} + +// Scan applies the selector query and scans the result into the given value. +func (_s *InviteCodeSelect) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, _s.ctx, ent.OpQuerySelect) + if err := _s.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*InviteCodeQuery, *InviteCodeSelect](ctx, _s.InviteCodeQuery, _s, _s.inters, v) +} + +func (_s *InviteCodeSelect) sqlScan(ctx context.Context, root *InviteCodeQuery, v any) error { + selector := root.sqlQuery(ctx) + aggregation := make([]string, 0, len(_s.fns)) + for _, fn := range _s.fns { + aggregation = append(aggregation, fn(selector)) + } + switch n := len(*_s.selector.flds); { + case n == 0 && len(aggregation) > 0: + selector.Select(aggregation...) + case n != 0 && len(aggregation) > 0: + selector.AppendSelect(aggregation...) + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := _s.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} diff --git a/pkg/ent/invitecode_update.go b/pkg/ent/invitecode_update.go new file mode 100644 index 000000000..a4f1e70c7 --- /dev/null +++ b/pkg/ent/invitecode_update.go @@ -0,0 +1,534 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/invitecode" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" +) + +// InviteCodeUpdate is the builder for updating InviteCode entities. +type InviteCodeUpdate struct { + config + hooks []Hook + mutation *InviteCodeMutation +} + +// Where appends a list predicates to the InviteCodeUpdate builder. +func (_u *InviteCodeUpdate) Where(ps ...predicate.InviteCode) *InviteCodeUpdate { + _u.mutation.Where(ps...) + return _u +} + +// SetCodeHash sets the "code_hash" field. +func (_u *InviteCodeUpdate) SetCodeHash(v string) *InviteCodeUpdate { + _u.mutation.SetCodeHash(v) + return _u +} + +// SetNillableCodeHash sets the "code_hash" field if the given value is not nil. +func (_u *InviteCodeUpdate) SetNillableCodeHash(v *string) *InviteCodeUpdate { + if v != nil { + _u.SetCodeHash(*v) + } + return _u +} + +// SetCodePrefix sets the "code_prefix" field. +func (_u *InviteCodeUpdate) SetCodePrefix(v string) *InviteCodeUpdate { + _u.mutation.SetCodePrefix(v) + return _u +} + +// SetNillableCodePrefix sets the "code_prefix" field if the given value is not nil. +func (_u *InviteCodeUpdate) SetNillableCodePrefix(v *string) *InviteCodeUpdate { + if v != nil { + _u.SetCodePrefix(*v) + } + return _u +} + +// SetMaxUses sets the "max_uses" field. +func (_u *InviteCodeUpdate) SetMaxUses(v int) *InviteCodeUpdate { + _u.mutation.ResetMaxUses() + _u.mutation.SetMaxUses(v) + return _u +} + +// SetNillableMaxUses sets the "max_uses" field if the given value is not nil. +func (_u *InviteCodeUpdate) SetNillableMaxUses(v *int) *InviteCodeUpdate { + if v != nil { + _u.SetMaxUses(*v) + } + return _u +} + +// AddMaxUses adds value to the "max_uses" field. +func (_u *InviteCodeUpdate) AddMaxUses(v int) *InviteCodeUpdate { + _u.mutation.AddMaxUses(v) + return _u +} + +// SetUseCount sets the "use_count" field. +func (_u *InviteCodeUpdate) SetUseCount(v int) *InviteCodeUpdate { + _u.mutation.ResetUseCount() + _u.mutation.SetUseCount(v) + return _u +} + +// SetNillableUseCount sets the "use_count" field if the given value is not nil. +func (_u *InviteCodeUpdate) SetNillableUseCount(v *int) *InviteCodeUpdate { + if v != nil { + _u.SetUseCount(*v) + } + return _u +} + +// AddUseCount adds value to the "use_count" field. +func (_u *InviteCodeUpdate) AddUseCount(v int) *InviteCodeUpdate { + _u.mutation.AddUseCount(v) + return _u +} + +// SetExpiresAt sets the "expires_at" field. +func (_u *InviteCodeUpdate) SetExpiresAt(v time.Time) *InviteCodeUpdate { + _u.mutation.SetExpiresAt(v) + return _u +} + +// SetNillableExpiresAt sets the "expires_at" field if the given value is not nil. +func (_u *InviteCodeUpdate) SetNillableExpiresAt(v *time.Time) *InviteCodeUpdate { + if v != nil { + _u.SetExpiresAt(*v) + } + return _u +} + +// SetRevoked sets the "revoked" field. +func (_u *InviteCodeUpdate) SetRevoked(v bool) *InviteCodeUpdate { + _u.mutation.SetRevoked(v) + return _u +} + +// SetNillableRevoked sets the "revoked" field if the given value is not nil. +func (_u *InviteCodeUpdate) SetNillableRevoked(v *bool) *InviteCodeUpdate { + if v != nil { + _u.SetRevoked(*v) + } + return _u +} + +// SetCreatedBy sets the "created_by" field. +func (_u *InviteCodeUpdate) SetCreatedBy(v string) *InviteCodeUpdate { + _u.mutation.SetCreatedBy(v) + return _u +} + +// SetNillableCreatedBy sets the "created_by" field if the given value is not nil. +func (_u *InviteCodeUpdate) SetNillableCreatedBy(v *string) *InviteCodeUpdate { + if v != nil { + _u.SetCreatedBy(*v) + } + return _u +} + +// SetNote sets the "note" field. +func (_u *InviteCodeUpdate) SetNote(v string) *InviteCodeUpdate { + _u.mutation.SetNote(v) + return _u +} + +// SetNillableNote sets the "note" field if the given value is not nil. +func (_u *InviteCodeUpdate) SetNillableNote(v *string) *InviteCodeUpdate { + if v != nil { + _u.SetNote(*v) + } + return _u +} + +// Mutation returns the InviteCodeMutation object of the builder. +func (_u *InviteCodeUpdate) Mutation() *InviteCodeMutation { + return _u.mutation +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (_u *InviteCodeUpdate) Save(ctx context.Context) (int, error) { + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *InviteCodeUpdate) SaveX(ctx context.Context) int { + affected, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (_u *InviteCodeUpdate) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *InviteCodeUpdate) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_u *InviteCodeUpdate) check() error { + if v, ok := _u.mutation.CodeHash(); ok { + if err := invitecode.CodeHashValidator(v); err != nil { + return &ValidationError{Name: "code_hash", err: fmt.Errorf(`ent: validator failed for field "InviteCode.code_hash": %w`, err)} + } + } + if v, ok := _u.mutation.CodePrefix(); ok { + if err := invitecode.CodePrefixValidator(v); err != nil { + return &ValidationError{Name: "code_prefix", err: fmt.Errorf(`ent: validator failed for field "InviteCode.code_prefix": %w`, err)} + } + } + if v, ok := _u.mutation.CreatedBy(); ok { + if err := invitecode.CreatedByValidator(v); err != nil { + return &ValidationError{Name: "created_by", err: fmt.Errorf(`ent: validator failed for field "InviteCode.created_by": %w`, err)} + } + } + return nil +} + +func (_u *InviteCodeUpdate) sqlSave(ctx context.Context) (_node int, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(invitecode.Table, invitecode.Columns, sqlgraph.NewFieldSpec(invitecode.FieldID, field.TypeUUID)) + if ps := _u.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := _u.mutation.CodeHash(); ok { + _spec.SetField(invitecode.FieldCodeHash, field.TypeString, value) + } + if value, ok := _u.mutation.CodePrefix(); ok { + _spec.SetField(invitecode.FieldCodePrefix, field.TypeString, value) + } + if value, ok := _u.mutation.MaxUses(); ok { + _spec.SetField(invitecode.FieldMaxUses, field.TypeInt, value) + } + if value, ok := _u.mutation.AddedMaxUses(); ok { + _spec.AddField(invitecode.FieldMaxUses, field.TypeInt, value) + } + if value, ok := _u.mutation.UseCount(); ok { + _spec.SetField(invitecode.FieldUseCount, field.TypeInt, value) + } + if value, ok := _u.mutation.AddedUseCount(); ok { + _spec.AddField(invitecode.FieldUseCount, field.TypeInt, value) + } + if value, ok := _u.mutation.ExpiresAt(); ok { + _spec.SetField(invitecode.FieldExpiresAt, field.TypeTime, value) + } + if value, ok := _u.mutation.Revoked(); ok { + _spec.SetField(invitecode.FieldRevoked, field.TypeBool, value) + } + if value, ok := _u.mutation.CreatedBy(); ok { + _spec.SetField(invitecode.FieldCreatedBy, field.TypeString, value) + } + if value, ok := _u.mutation.Note(); ok { + _spec.SetField(invitecode.FieldNote, field.TypeString, value) + } + if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{invitecode.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + _u.mutation.done = true + return _node, nil +} + +// InviteCodeUpdateOne is the builder for updating a single InviteCode entity. +type InviteCodeUpdateOne struct { + config + fields []string + hooks []Hook + mutation *InviteCodeMutation +} + +// SetCodeHash sets the "code_hash" field. +func (_u *InviteCodeUpdateOne) SetCodeHash(v string) *InviteCodeUpdateOne { + _u.mutation.SetCodeHash(v) + return _u +} + +// SetNillableCodeHash sets the "code_hash" field if the given value is not nil. +func (_u *InviteCodeUpdateOne) SetNillableCodeHash(v *string) *InviteCodeUpdateOne { + if v != nil { + _u.SetCodeHash(*v) + } + return _u +} + +// SetCodePrefix sets the "code_prefix" field. +func (_u *InviteCodeUpdateOne) SetCodePrefix(v string) *InviteCodeUpdateOne { + _u.mutation.SetCodePrefix(v) + return _u +} + +// SetNillableCodePrefix sets the "code_prefix" field if the given value is not nil. +func (_u *InviteCodeUpdateOne) SetNillableCodePrefix(v *string) *InviteCodeUpdateOne { + if v != nil { + _u.SetCodePrefix(*v) + } + return _u +} + +// SetMaxUses sets the "max_uses" field. +func (_u *InviteCodeUpdateOne) SetMaxUses(v int) *InviteCodeUpdateOne { + _u.mutation.ResetMaxUses() + _u.mutation.SetMaxUses(v) + return _u +} + +// SetNillableMaxUses sets the "max_uses" field if the given value is not nil. +func (_u *InviteCodeUpdateOne) SetNillableMaxUses(v *int) *InviteCodeUpdateOne { + if v != nil { + _u.SetMaxUses(*v) + } + return _u +} + +// AddMaxUses adds value to the "max_uses" field. +func (_u *InviteCodeUpdateOne) AddMaxUses(v int) *InviteCodeUpdateOne { + _u.mutation.AddMaxUses(v) + return _u +} + +// SetUseCount sets the "use_count" field. +func (_u *InviteCodeUpdateOne) SetUseCount(v int) *InviteCodeUpdateOne { + _u.mutation.ResetUseCount() + _u.mutation.SetUseCount(v) + return _u +} + +// SetNillableUseCount sets the "use_count" field if the given value is not nil. +func (_u *InviteCodeUpdateOne) SetNillableUseCount(v *int) *InviteCodeUpdateOne { + if v != nil { + _u.SetUseCount(*v) + } + return _u +} + +// AddUseCount adds value to the "use_count" field. +func (_u *InviteCodeUpdateOne) AddUseCount(v int) *InviteCodeUpdateOne { + _u.mutation.AddUseCount(v) + return _u +} + +// SetExpiresAt sets the "expires_at" field. +func (_u *InviteCodeUpdateOne) SetExpiresAt(v time.Time) *InviteCodeUpdateOne { + _u.mutation.SetExpiresAt(v) + return _u +} + +// SetNillableExpiresAt sets the "expires_at" field if the given value is not nil. +func (_u *InviteCodeUpdateOne) SetNillableExpiresAt(v *time.Time) *InviteCodeUpdateOne { + if v != nil { + _u.SetExpiresAt(*v) + } + return _u +} + +// SetRevoked sets the "revoked" field. +func (_u *InviteCodeUpdateOne) SetRevoked(v bool) *InviteCodeUpdateOne { + _u.mutation.SetRevoked(v) + return _u +} + +// SetNillableRevoked sets the "revoked" field if the given value is not nil. +func (_u *InviteCodeUpdateOne) SetNillableRevoked(v *bool) *InviteCodeUpdateOne { + if v != nil { + _u.SetRevoked(*v) + } + return _u +} + +// SetCreatedBy sets the "created_by" field. +func (_u *InviteCodeUpdateOne) SetCreatedBy(v string) *InviteCodeUpdateOne { + _u.mutation.SetCreatedBy(v) + return _u +} + +// SetNillableCreatedBy sets the "created_by" field if the given value is not nil. +func (_u *InviteCodeUpdateOne) SetNillableCreatedBy(v *string) *InviteCodeUpdateOne { + if v != nil { + _u.SetCreatedBy(*v) + } + return _u +} + +// SetNote sets the "note" field. +func (_u *InviteCodeUpdateOne) SetNote(v string) *InviteCodeUpdateOne { + _u.mutation.SetNote(v) + return _u +} + +// SetNillableNote sets the "note" field if the given value is not nil. +func (_u *InviteCodeUpdateOne) SetNillableNote(v *string) *InviteCodeUpdateOne { + if v != nil { + _u.SetNote(*v) + } + return _u +} + +// Mutation returns the InviteCodeMutation object of the builder. +func (_u *InviteCodeUpdateOne) Mutation() *InviteCodeMutation { + return _u.mutation +} + +// Where appends a list predicates to the InviteCodeUpdate builder. +func (_u *InviteCodeUpdateOne) Where(ps ...predicate.InviteCode) *InviteCodeUpdateOne { + _u.mutation.Where(ps...) + return _u +} + +// Select allows selecting one or more fields (columns) of the returned entity. +// The default is selecting all fields defined in the entity schema. +func (_u *InviteCodeUpdateOne) Select(field string, fields ...string) *InviteCodeUpdateOne { + _u.fields = append([]string{field}, fields...) + return _u +} + +// Save executes the query and returns the updated InviteCode entity. +func (_u *InviteCodeUpdateOne) Save(ctx context.Context) (*InviteCode, error) { + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *InviteCodeUpdateOne) SaveX(ctx context.Context) *InviteCode { + node, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (_u *InviteCodeUpdateOne) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *InviteCodeUpdateOne) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_u *InviteCodeUpdateOne) check() error { + if v, ok := _u.mutation.CodeHash(); ok { + if err := invitecode.CodeHashValidator(v); err != nil { + return &ValidationError{Name: "code_hash", err: fmt.Errorf(`ent: validator failed for field "InviteCode.code_hash": %w`, err)} + } + } + if v, ok := _u.mutation.CodePrefix(); ok { + if err := invitecode.CodePrefixValidator(v); err != nil { + return &ValidationError{Name: "code_prefix", err: fmt.Errorf(`ent: validator failed for field "InviteCode.code_prefix": %w`, err)} + } + } + if v, ok := _u.mutation.CreatedBy(); ok { + if err := invitecode.CreatedByValidator(v); err != nil { + return &ValidationError{Name: "created_by", err: fmt.Errorf(`ent: validator failed for field "InviteCode.created_by": %w`, err)} + } + } + return nil +} + +func (_u *InviteCodeUpdateOne) sqlSave(ctx context.Context) (_node *InviteCode, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(invitecode.Table, invitecode.Columns, sqlgraph.NewFieldSpec(invitecode.FieldID, field.TypeUUID)) + id, ok := _u.mutation.ID() + if !ok { + return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "InviteCode.id" for update`)} + } + _spec.Node.ID.Value = id + if fields := _u.fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, invitecode.FieldID) + for _, f := range fields { + if !invitecode.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + if f != invitecode.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, f) + } + } + } + if ps := _u.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := _u.mutation.CodeHash(); ok { + _spec.SetField(invitecode.FieldCodeHash, field.TypeString, value) + } + if value, ok := _u.mutation.CodePrefix(); ok { + _spec.SetField(invitecode.FieldCodePrefix, field.TypeString, value) + } + if value, ok := _u.mutation.MaxUses(); ok { + _spec.SetField(invitecode.FieldMaxUses, field.TypeInt, value) + } + if value, ok := _u.mutation.AddedMaxUses(); ok { + _spec.AddField(invitecode.FieldMaxUses, field.TypeInt, value) + } + if value, ok := _u.mutation.UseCount(); ok { + _spec.SetField(invitecode.FieldUseCount, field.TypeInt, value) + } + if value, ok := _u.mutation.AddedUseCount(); ok { + _spec.AddField(invitecode.FieldUseCount, field.TypeInt, value) + } + if value, ok := _u.mutation.ExpiresAt(); ok { + _spec.SetField(invitecode.FieldExpiresAt, field.TypeTime, value) + } + if value, ok := _u.mutation.Revoked(); ok { + _spec.SetField(invitecode.FieldRevoked, field.TypeBool, value) + } + if value, ok := _u.mutation.CreatedBy(); ok { + _spec.SetField(invitecode.FieldCreatedBy, field.TypeString, value) + } + if value, ok := _u.mutation.Note(); ok { + _spec.SetField(invitecode.FieldNote, field.TypeString, value) + } + _node = &InviteCode{config: _u.config} + _spec.Assign = _node.assignValues + _spec.ScanValues = _node.scanValues + if err = sqlgraph.UpdateNode(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{invitecode.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + _u.mutation.done = true + return _node, nil +} diff --git a/pkg/ent/maintenanceoperation.go b/pkg/ent/maintenanceoperation.go new file mode 100644 index 000000000..2a8f6ab52 --- /dev/null +++ b/pkg/ent/maintenanceoperation.go @@ -0,0 +1,223 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "fmt" + "strings" + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "github.com/GoogleCloudPlatform/scion/pkg/ent/maintenanceoperation" + "github.com/google/uuid" +) + +// MaintenanceOperation is the model entity for the MaintenanceOperation schema. +type MaintenanceOperation struct { + config `json:"-"` + // ID of the ent. + ID uuid.UUID `json:"id,omitempty"` + // Key holds the value of the "key" field. + Key string `json:"key,omitempty"` + // Title holds the value of the "title" field. + Title string `json:"title,omitempty"` + // Description holds the value of the "description" field. + Description string `json:"description,omitempty"` + // Category holds the value of the "category" field. + Category string `json:"category,omitempty"` + // Status holds the value of the "status" field. + Status string `json:"status,omitempty"` + // StartedAt holds the value of the "started_at" field. + StartedAt *time.Time `json:"started_at,omitempty"` + // CompletedAt holds the value of the "completed_at" field. + CompletedAt *time.Time `json:"completed_at,omitempty"` + // StartedBy holds the value of the "started_by" field. + StartedBy string `json:"started_by,omitempty"` + // Result holds the value of the "result" field. + Result string `json:"result,omitempty"` + // Metadata holds the value of the "metadata" field. + Metadata string `json:"metadata,omitempty"` + // Created holds the value of the "created" field. + Created time.Time `json:"created,omitempty"` + selectValues sql.SelectValues +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*MaintenanceOperation) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case maintenanceoperation.FieldKey, maintenanceoperation.FieldTitle, maintenanceoperation.FieldDescription, maintenanceoperation.FieldCategory, maintenanceoperation.FieldStatus, maintenanceoperation.FieldStartedBy, maintenanceoperation.FieldResult, maintenanceoperation.FieldMetadata: + values[i] = new(sql.NullString) + case maintenanceoperation.FieldStartedAt, maintenanceoperation.FieldCompletedAt, maintenanceoperation.FieldCreated: + values[i] = new(sql.NullTime) + case maintenanceoperation.FieldID: + values[i] = new(uuid.UUID) + default: + values[i] = new(sql.UnknownType) + } + } + return values, nil +} + +// assignValues assigns the values that were returned from sql.Rows (after scanning) +// to the MaintenanceOperation fields. +func (_m *MaintenanceOperation) assignValues(columns []string, values []any) error { + if m, n := len(values), len(columns); m < n { + return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) + } + for i := range columns { + switch columns[i] { + case maintenanceoperation.FieldID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field id", values[i]) + } else if value != nil { + _m.ID = *value + } + case maintenanceoperation.FieldKey: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field key", values[i]) + } else if value.Valid { + _m.Key = value.String + } + case maintenanceoperation.FieldTitle: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field title", values[i]) + } else if value.Valid { + _m.Title = value.String + } + case maintenanceoperation.FieldDescription: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field description", values[i]) + } else if value.Valid { + _m.Description = value.String + } + case maintenanceoperation.FieldCategory: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field category", values[i]) + } else if value.Valid { + _m.Category = value.String + } + case maintenanceoperation.FieldStatus: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field status", values[i]) + } else if value.Valid { + _m.Status = value.String + } + case maintenanceoperation.FieldStartedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field started_at", values[i]) + } else if value.Valid { + _m.StartedAt = new(time.Time) + *_m.StartedAt = value.Time + } + case maintenanceoperation.FieldCompletedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field completed_at", values[i]) + } else if value.Valid { + _m.CompletedAt = new(time.Time) + *_m.CompletedAt = value.Time + } + case maintenanceoperation.FieldStartedBy: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field started_by", values[i]) + } else if value.Valid { + _m.StartedBy = value.String + } + case maintenanceoperation.FieldResult: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field result", values[i]) + } else if value.Valid { + _m.Result = value.String + } + case maintenanceoperation.FieldMetadata: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field metadata", values[i]) + } else if value.Valid { + _m.Metadata = value.String + } + case maintenanceoperation.FieldCreated: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field created", values[i]) + } else if value.Valid { + _m.Created = value.Time + } + default: + _m.selectValues.Set(columns[i], values[i]) + } + } + return nil +} + +// Value returns the ent.Value that was dynamically selected and assigned to the MaintenanceOperation. +// This includes values selected through modifiers, order, etc. +func (_m *MaintenanceOperation) Value(name string) (ent.Value, error) { + return _m.selectValues.Get(name) +} + +// Update returns a builder for updating this MaintenanceOperation. +// Note that you need to call MaintenanceOperation.Unwrap() before calling this method if this MaintenanceOperation +// was returned from a transaction, and the transaction was committed or rolled back. +func (_m *MaintenanceOperation) Update() *MaintenanceOperationUpdateOne { + return NewMaintenanceOperationClient(_m.config).UpdateOne(_m) +} + +// Unwrap unwraps the MaintenanceOperation entity that was returned from a transaction after it was closed, +// so that all future queries will be executed through the driver which created the transaction. +func (_m *MaintenanceOperation) Unwrap() *MaintenanceOperation { + _tx, ok := _m.config.driver.(*txDriver) + if !ok { + panic("ent: MaintenanceOperation is not a transactional entity") + } + _m.config.driver = _tx.drv + return _m +} + +// String implements the fmt.Stringer. +func (_m *MaintenanceOperation) String() string { + var builder strings.Builder + builder.WriteString("MaintenanceOperation(") + builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID)) + builder.WriteString("key=") + builder.WriteString(_m.Key) + builder.WriteString(", ") + builder.WriteString("title=") + builder.WriteString(_m.Title) + builder.WriteString(", ") + builder.WriteString("description=") + builder.WriteString(_m.Description) + builder.WriteString(", ") + builder.WriteString("category=") + builder.WriteString(_m.Category) + builder.WriteString(", ") + builder.WriteString("status=") + builder.WriteString(_m.Status) + builder.WriteString(", ") + if v := _m.StartedAt; v != nil { + builder.WriteString("started_at=") + builder.WriteString(v.Format(time.ANSIC)) + } + builder.WriteString(", ") + if v := _m.CompletedAt; v != nil { + builder.WriteString("completed_at=") + builder.WriteString(v.Format(time.ANSIC)) + } + builder.WriteString(", ") + builder.WriteString("started_by=") + builder.WriteString(_m.StartedBy) + builder.WriteString(", ") + builder.WriteString("result=") + builder.WriteString(_m.Result) + builder.WriteString(", ") + builder.WriteString("metadata=") + builder.WriteString(_m.Metadata) + builder.WriteString(", ") + builder.WriteString("created=") + builder.WriteString(_m.Created.Format(time.ANSIC)) + builder.WriteByte(')') + return builder.String() +} + +// MaintenanceOperations is a parsable slice of MaintenanceOperation. +type MaintenanceOperations []*MaintenanceOperation diff --git a/pkg/ent/maintenanceoperation/maintenanceoperation.go b/pkg/ent/maintenanceoperation/maintenanceoperation.go new file mode 100644 index 000000000..29c36d452 --- /dev/null +++ b/pkg/ent/maintenanceoperation/maintenanceoperation.go @@ -0,0 +1,149 @@ +// Code generated by ent, DO NOT EDIT. + +package maintenanceoperation + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "github.com/google/uuid" +) + +const ( + // Label holds the string label denoting the maintenanceoperation type in the database. + Label = "maintenance_operation" + // FieldID holds the string denoting the id field in the database. + FieldID = "id" + // FieldKey holds the string denoting the key field in the database. + FieldKey = "key" + // FieldTitle holds the string denoting the title field in the database. + FieldTitle = "title" + // FieldDescription holds the string denoting the description field in the database. + FieldDescription = "description" + // FieldCategory holds the string denoting the category field in the database. + FieldCategory = "category" + // FieldStatus holds the string denoting the status field in the database. + FieldStatus = "status" + // FieldStartedAt holds the string denoting the started_at field in the database. + FieldStartedAt = "started_at" + // FieldCompletedAt holds the string denoting the completed_at field in the database. + FieldCompletedAt = "completed_at" + // FieldStartedBy holds the string denoting the started_by field in the database. + FieldStartedBy = "started_by" + // FieldResult holds the string denoting the result field in the database. + FieldResult = "result" + // FieldMetadata holds the string denoting the metadata field in the database. + FieldMetadata = "metadata" + // FieldCreated holds the string denoting the created field in the database. + FieldCreated = "created" + // Table holds the table name of the maintenanceoperation in the database. + Table = "maintenance_operations" +) + +// Columns holds all SQL columns for maintenanceoperation fields. +var Columns = []string{ + FieldID, + FieldKey, + FieldTitle, + FieldDescription, + FieldCategory, + FieldStatus, + FieldStartedAt, + FieldCompletedAt, + FieldStartedBy, + FieldResult, + FieldMetadata, + FieldCreated, +} + +// ValidColumn reports if the column name is valid (part of the table columns). +func ValidColumn(column string) bool { + for i := range Columns { + if column == Columns[i] { + return true + } + } + return false +} + +var ( + // KeyValidator is a validator for the "key" field. It is called by the builders before save. + KeyValidator func(string) error + // TitleValidator is a validator for the "title" field. It is called by the builders before save. + TitleValidator func(string) error + // DefaultDescription holds the default value on creation for the "description" field. + DefaultDescription string + // CategoryValidator is a validator for the "category" field. It is called by the builders before save. + CategoryValidator func(string) error + // DefaultStatus holds the default value on creation for the "status" field. + DefaultStatus string + // DefaultMetadata holds the default value on creation for the "metadata" field. + DefaultMetadata string + // DefaultCreated holds the default value on creation for the "created" field. + DefaultCreated func() time.Time + // DefaultID holds the default value on creation for the "id" field. + DefaultID func() uuid.UUID +) + +// OrderOption defines the ordering options for the MaintenanceOperation queries. +type OrderOption func(*sql.Selector) + +// ByID orders the results by the id field. +func ByID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldID, opts...).ToFunc() +} + +// ByKey orders the results by the key field. +func ByKey(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldKey, opts...).ToFunc() +} + +// ByTitle orders the results by the title field. +func ByTitle(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldTitle, opts...).ToFunc() +} + +// ByDescription orders the results by the description field. +func ByDescription(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldDescription, opts...).ToFunc() +} + +// ByCategory orders the results by the category field. +func ByCategory(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCategory, opts...).ToFunc() +} + +// ByStatus orders the results by the status field. +func ByStatus(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldStatus, opts...).ToFunc() +} + +// ByStartedAt orders the results by the started_at field. +func ByStartedAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldStartedAt, opts...).ToFunc() +} + +// ByCompletedAt orders the results by the completed_at field. +func ByCompletedAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCompletedAt, opts...).ToFunc() +} + +// ByStartedBy orders the results by the started_by field. +func ByStartedBy(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldStartedBy, opts...).ToFunc() +} + +// ByResult orders the results by the result field. +func ByResult(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldResult, opts...).ToFunc() +} + +// ByMetadata orders the results by the metadata field. +func ByMetadata(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldMetadata, opts...).ToFunc() +} + +// ByCreated orders the results by the created field. +func ByCreated(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCreated, opts...).ToFunc() +} diff --git a/pkg/ent/maintenanceoperation/where.go b/pkg/ent/maintenanceoperation/where.go new file mode 100644 index 000000000..3fd4f77b3 --- /dev/null +++ b/pkg/ent/maintenanceoperation/where.go @@ -0,0 +1,806 @@ +// Code generated by ent, DO NOT EDIT. + +package maintenanceoperation + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/google/uuid" +) + +// ID filters vertices based on their ID field. +func ID(id uuid.UUID) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldEQ(FieldID, id)) +} + +// IDEQ applies the EQ predicate on the ID field. +func IDEQ(id uuid.UUID) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldEQ(FieldID, id)) +} + +// IDNEQ applies the NEQ predicate on the ID field. +func IDNEQ(id uuid.UUID) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldNEQ(FieldID, id)) +} + +// IDIn applies the In predicate on the ID field. +func IDIn(ids ...uuid.UUID) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldIn(FieldID, ids...)) +} + +// IDNotIn applies the NotIn predicate on the ID field. +func IDNotIn(ids ...uuid.UUID) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldNotIn(FieldID, ids...)) +} + +// IDGT applies the GT predicate on the ID field. +func IDGT(id uuid.UUID) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldGT(FieldID, id)) +} + +// IDGTE applies the GTE predicate on the ID field. +func IDGTE(id uuid.UUID) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldGTE(FieldID, id)) +} + +// IDLT applies the LT predicate on the ID field. +func IDLT(id uuid.UUID) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldLT(FieldID, id)) +} + +// IDLTE applies the LTE predicate on the ID field. +func IDLTE(id uuid.UUID) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldLTE(FieldID, id)) +} + +// Key applies equality check predicate on the "key" field. It's identical to KeyEQ. +func Key(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldEQ(FieldKey, v)) +} + +// Title applies equality check predicate on the "title" field. It's identical to TitleEQ. +func Title(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldEQ(FieldTitle, v)) +} + +// Description applies equality check predicate on the "description" field. It's identical to DescriptionEQ. +func Description(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldEQ(FieldDescription, v)) +} + +// Category applies equality check predicate on the "category" field. It's identical to CategoryEQ. +func Category(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldEQ(FieldCategory, v)) +} + +// Status applies equality check predicate on the "status" field. It's identical to StatusEQ. +func Status(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldEQ(FieldStatus, v)) +} + +// StartedAt applies equality check predicate on the "started_at" field. It's identical to StartedAtEQ. +func StartedAt(v time.Time) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldEQ(FieldStartedAt, v)) +} + +// CompletedAt applies equality check predicate on the "completed_at" field. It's identical to CompletedAtEQ. +func CompletedAt(v time.Time) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldEQ(FieldCompletedAt, v)) +} + +// StartedBy applies equality check predicate on the "started_by" field. It's identical to StartedByEQ. +func StartedBy(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldEQ(FieldStartedBy, v)) +} + +// Result applies equality check predicate on the "result" field. It's identical to ResultEQ. +func Result(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldEQ(FieldResult, v)) +} + +// Metadata applies equality check predicate on the "metadata" field. It's identical to MetadataEQ. +func Metadata(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldEQ(FieldMetadata, v)) +} + +// Created applies equality check predicate on the "created" field. It's identical to CreatedEQ. +func Created(v time.Time) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldEQ(FieldCreated, v)) +} + +// KeyEQ applies the EQ predicate on the "key" field. +func KeyEQ(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldEQ(FieldKey, v)) +} + +// KeyNEQ applies the NEQ predicate on the "key" field. +func KeyNEQ(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldNEQ(FieldKey, v)) +} + +// KeyIn applies the In predicate on the "key" field. +func KeyIn(vs ...string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldIn(FieldKey, vs...)) +} + +// KeyNotIn applies the NotIn predicate on the "key" field. +func KeyNotIn(vs ...string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldNotIn(FieldKey, vs...)) +} + +// KeyGT applies the GT predicate on the "key" field. +func KeyGT(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldGT(FieldKey, v)) +} + +// KeyGTE applies the GTE predicate on the "key" field. +func KeyGTE(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldGTE(FieldKey, v)) +} + +// KeyLT applies the LT predicate on the "key" field. +func KeyLT(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldLT(FieldKey, v)) +} + +// KeyLTE applies the LTE predicate on the "key" field. +func KeyLTE(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldLTE(FieldKey, v)) +} + +// KeyContains applies the Contains predicate on the "key" field. +func KeyContains(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldContains(FieldKey, v)) +} + +// KeyHasPrefix applies the HasPrefix predicate on the "key" field. +func KeyHasPrefix(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldHasPrefix(FieldKey, v)) +} + +// KeyHasSuffix applies the HasSuffix predicate on the "key" field. +func KeyHasSuffix(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldHasSuffix(FieldKey, v)) +} + +// KeyEqualFold applies the EqualFold predicate on the "key" field. +func KeyEqualFold(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldEqualFold(FieldKey, v)) +} + +// KeyContainsFold applies the ContainsFold predicate on the "key" field. +func KeyContainsFold(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldContainsFold(FieldKey, v)) +} + +// TitleEQ applies the EQ predicate on the "title" field. +func TitleEQ(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldEQ(FieldTitle, v)) +} + +// TitleNEQ applies the NEQ predicate on the "title" field. +func TitleNEQ(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldNEQ(FieldTitle, v)) +} + +// TitleIn applies the In predicate on the "title" field. +func TitleIn(vs ...string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldIn(FieldTitle, vs...)) +} + +// TitleNotIn applies the NotIn predicate on the "title" field. +func TitleNotIn(vs ...string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldNotIn(FieldTitle, vs...)) +} + +// TitleGT applies the GT predicate on the "title" field. +func TitleGT(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldGT(FieldTitle, v)) +} + +// TitleGTE applies the GTE predicate on the "title" field. +func TitleGTE(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldGTE(FieldTitle, v)) +} + +// TitleLT applies the LT predicate on the "title" field. +func TitleLT(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldLT(FieldTitle, v)) +} + +// TitleLTE applies the LTE predicate on the "title" field. +func TitleLTE(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldLTE(FieldTitle, v)) +} + +// TitleContains applies the Contains predicate on the "title" field. +func TitleContains(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldContains(FieldTitle, v)) +} + +// TitleHasPrefix applies the HasPrefix predicate on the "title" field. +func TitleHasPrefix(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldHasPrefix(FieldTitle, v)) +} + +// TitleHasSuffix applies the HasSuffix predicate on the "title" field. +func TitleHasSuffix(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldHasSuffix(FieldTitle, v)) +} + +// TitleEqualFold applies the EqualFold predicate on the "title" field. +func TitleEqualFold(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldEqualFold(FieldTitle, v)) +} + +// TitleContainsFold applies the ContainsFold predicate on the "title" field. +func TitleContainsFold(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldContainsFold(FieldTitle, v)) +} + +// DescriptionEQ applies the EQ predicate on the "description" field. +func DescriptionEQ(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldEQ(FieldDescription, v)) +} + +// DescriptionNEQ applies the NEQ predicate on the "description" field. +func DescriptionNEQ(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldNEQ(FieldDescription, v)) +} + +// DescriptionIn applies the In predicate on the "description" field. +func DescriptionIn(vs ...string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldIn(FieldDescription, vs...)) +} + +// DescriptionNotIn applies the NotIn predicate on the "description" field. +func DescriptionNotIn(vs ...string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldNotIn(FieldDescription, vs...)) +} + +// DescriptionGT applies the GT predicate on the "description" field. +func DescriptionGT(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldGT(FieldDescription, v)) +} + +// DescriptionGTE applies the GTE predicate on the "description" field. +func DescriptionGTE(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldGTE(FieldDescription, v)) +} + +// DescriptionLT applies the LT predicate on the "description" field. +func DescriptionLT(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldLT(FieldDescription, v)) +} + +// DescriptionLTE applies the LTE predicate on the "description" field. +func DescriptionLTE(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldLTE(FieldDescription, v)) +} + +// DescriptionContains applies the Contains predicate on the "description" field. +func DescriptionContains(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldContains(FieldDescription, v)) +} + +// DescriptionHasPrefix applies the HasPrefix predicate on the "description" field. +func DescriptionHasPrefix(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldHasPrefix(FieldDescription, v)) +} + +// DescriptionHasSuffix applies the HasSuffix predicate on the "description" field. +func DescriptionHasSuffix(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldHasSuffix(FieldDescription, v)) +} + +// DescriptionEqualFold applies the EqualFold predicate on the "description" field. +func DescriptionEqualFold(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldEqualFold(FieldDescription, v)) +} + +// DescriptionContainsFold applies the ContainsFold predicate on the "description" field. +func DescriptionContainsFold(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldContainsFold(FieldDescription, v)) +} + +// CategoryEQ applies the EQ predicate on the "category" field. +func CategoryEQ(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldEQ(FieldCategory, v)) +} + +// CategoryNEQ applies the NEQ predicate on the "category" field. +func CategoryNEQ(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldNEQ(FieldCategory, v)) +} + +// CategoryIn applies the In predicate on the "category" field. +func CategoryIn(vs ...string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldIn(FieldCategory, vs...)) +} + +// CategoryNotIn applies the NotIn predicate on the "category" field. +func CategoryNotIn(vs ...string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldNotIn(FieldCategory, vs...)) +} + +// CategoryGT applies the GT predicate on the "category" field. +func CategoryGT(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldGT(FieldCategory, v)) +} + +// CategoryGTE applies the GTE predicate on the "category" field. +func CategoryGTE(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldGTE(FieldCategory, v)) +} + +// CategoryLT applies the LT predicate on the "category" field. +func CategoryLT(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldLT(FieldCategory, v)) +} + +// CategoryLTE applies the LTE predicate on the "category" field. +func CategoryLTE(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldLTE(FieldCategory, v)) +} + +// CategoryContains applies the Contains predicate on the "category" field. +func CategoryContains(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldContains(FieldCategory, v)) +} + +// CategoryHasPrefix applies the HasPrefix predicate on the "category" field. +func CategoryHasPrefix(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldHasPrefix(FieldCategory, v)) +} + +// CategoryHasSuffix applies the HasSuffix predicate on the "category" field. +func CategoryHasSuffix(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldHasSuffix(FieldCategory, v)) +} + +// CategoryEqualFold applies the EqualFold predicate on the "category" field. +func CategoryEqualFold(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldEqualFold(FieldCategory, v)) +} + +// CategoryContainsFold applies the ContainsFold predicate on the "category" field. +func CategoryContainsFold(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldContainsFold(FieldCategory, v)) +} + +// StatusEQ applies the EQ predicate on the "status" field. +func StatusEQ(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldEQ(FieldStatus, v)) +} + +// StatusNEQ applies the NEQ predicate on the "status" field. +func StatusNEQ(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldNEQ(FieldStatus, v)) +} + +// StatusIn applies the In predicate on the "status" field. +func StatusIn(vs ...string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldIn(FieldStatus, vs...)) +} + +// StatusNotIn applies the NotIn predicate on the "status" field. +func StatusNotIn(vs ...string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldNotIn(FieldStatus, vs...)) +} + +// StatusGT applies the GT predicate on the "status" field. +func StatusGT(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldGT(FieldStatus, v)) +} + +// StatusGTE applies the GTE predicate on the "status" field. +func StatusGTE(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldGTE(FieldStatus, v)) +} + +// StatusLT applies the LT predicate on the "status" field. +func StatusLT(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldLT(FieldStatus, v)) +} + +// StatusLTE applies the LTE predicate on the "status" field. +func StatusLTE(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldLTE(FieldStatus, v)) +} + +// StatusContains applies the Contains predicate on the "status" field. +func StatusContains(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldContains(FieldStatus, v)) +} + +// StatusHasPrefix applies the HasPrefix predicate on the "status" field. +func StatusHasPrefix(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldHasPrefix(FieldStatus, v)) +} + +// StatusHasSuffix applies the HasSuffix predicate on the "status" field. +func StatusHasSuffix(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldHasSuffix(FieldStatus, v)) +} + +// StatusEqualFold applies the EqualFold predicate on the "status" field. +func StatusEqualFold(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldEqualFold(FieldStatus, v)) +} + +// StatusContainsFold applies the ContainsFold predicate on the "status" field. +func StatusContainsFold(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldContainsFold(FieldStatus, v)) +} + +// StartedAtEQ applies the EQ predicate on the "started_at" field. +func StartedAtEQ(v time.Time) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldEQ(FieldStartedAt, v)) +} + +// StartedAtNEQ applies the NEQ predicate on the "started_at" field. +func StartedAtNEQ(v time.Time) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldNEQ(FieldStartedAt, v)) +} + +// StartedAtIn applies the In predicate on the "started_at" field. +func StartedAtIn(vs ...time.Time) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldIn(FieldStartedAt, vs...)) +} + +// StartedAtNotIn applies the NotIn predicate on the "started_at" field. +func StartedAtNotIn(vs ...time.Time) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldNotIn(FieldStartedAt, vs...)) +} + +// StartedAtGT applies the GT predicate on the "started_at" field. +func StartedAtGT(v time.Time) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldGT(FieldStartedAt, v)) +} + +// StartedAtGTE applies the GTE predicate on the "started_at" field. +func StartedAtGTE(v time.Time) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldGTE(FieldStartedAt, v)) +} + +// StartedAtLT applies the LT predicate on the "started_at" field. +func StartedAtLT(v time.Time) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldLT(FieldStartedAt, v)) +} + +// StartedAtLTE applies the LTE predicate on the "started_at" field. +func StartedAtLTE(v time.Time) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldLTE(FieldStartedAt, v)) +} + +// StartedAtIsNil applies the IsNil predicate on the "started_at" field. +func StartedAtIsNil() predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldIsNull(FieldStartedAt)) +} + +// StartedAtNotNil applies the NotNil predicate on the "started_at" field. +func StartedAtNotNil() predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldNotNull(FieldStartedAt)) +} + +// CompletedAtEQ applies the EQ predicate on the "completed_at" field. +func CompletedAtEQ(v time.Time) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldEQ(FieldCompletedAt, v)) +} + +// CompletedAtNEQ applies the NEQ predicate on the "completed_at" field. +func CompletedAtNEQ(v time.Time) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldNEQ(FieldCompletedAt, v)) +} + +// CompletedAtIn applies the In predicate on the "completed_at" field. +func CompletedAtIn(vs ...time.Time) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldIn(FieldCompletedAt, vs...)) +} + +// CompletedAtNotIn applies the NotIn predicate on the "completed_at" field. +func CompletedAtNotIn(vs ...time.Time) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldNotIn(FieldCompletedAt, vs...)) +} + +// CompletedAtGT applies the GT predicate on the "completed_at" field. +func CompletedAtGT(v time.Time) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldGT(FieldCompletedAt, v)) +} + +// CompletedAtGTE applies the GTE predicate on the "completed_at" field. +func CompletedAtGTE(v time.Time) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldGTE(FieldCompletedAt, v)) +} + +// CompletedAtLT applies the LT predicate on the "completed_at" field. +func CompletedAtLT(v time.Time) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldLT(FieldCompletedAt, v)) +} + +// CompletedAtLTE applies the LTE predicate on the "completed_at" field. +func CompletedAtLTE(v time.Time) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldLTE(FieldCompletedAt, v)) +} + +// CompletedAtIsNil applies the IsNil predicate on the "completed_at" field. +func CompletedAtIsNil() predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldIsNull(FieldCompletedAt)) +} + +// CompletedAtNotNil applies the NotNil predicate on the "completed_at" field. +func CompletedAtNotNil() predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldNotNull(FieldCompletedAt)) +} + +// StartedByEQ applies the EQ predicate on the "started_by" field. +func StartedByEQ(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldEQ(FieldStartedBy, v)) +} + +// StartedByNEQ applies the NEQ predicate on the "started_by" field. +func StartedByNEQ(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldNEQ(FieldStartedBy, v)) +} + +// StartedByIn applies the In predicate on the "started_by" field. +func StartedByIn(vs ...string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldIn(FieldStartedBy, vs...)) +} + +// StartedByNotIn applies the NotIn predicate on the "started_by" field. +func StartedByNotIn(vs ...string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldNotIn(FieldStartedBy, vs...)) +} + +// StartedByGT applies the GT predicate on the "started_by" field. +func StartedByGT(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldGT(FieldStartedBy, v)) +} + +// StartedByGTE applies the GTE predicate on the "started_by" field. +func StartedByGTE(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldGTE(FieldStartedBy, v)) +} + +// StartedByLT applies the LT predicate on the "started_by" field. +func StartedByLT(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldLT(FieldStartedBy, v)) +} + +// StartedByLTE applies the LTE predicate on the "started_by" field. +func StartedByLTE(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldLTE(FieldStartedBy, v)) +} + +// StartedByContains applies the Contains predicate on the "started_by" field. +func StartedByContains(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldContains(FieldStartedBy, v)) +} + +// StartedByHasPrefix applies the HasPrefix predicate on the "started_by" field. +func StartedByHasPrefix(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldHasPrefix(FieldStartedBy, v)) +} + +// StartedByHasSuffix applies the HasSuffix predicate on the "started_by" field. +func StartedByHasSuffix(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldHasSuffix(FieldStartedBy, v)) +} + +// StartedByIsNil applies the IsNil predicate on the "started_by" field. +func StartedByIsNil() predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldIsNull(FieldStartedBy)) +} + +// StartedByNotNil applies the NotNil predicate on the "started_by" field. +func StartedByNotNil() predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldNotNull(FieldStartedBy)) +} + +// StartedByEqualFold applies the EqualFold predicate on the "started_by" field. +func StartedByEqualFold(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldEqualFold(FieldStartedBy, v)) +} + +// StartedByContainsFold applies the ContainsFold predicate on the "started_by" field. +func StartedByContainsFold(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldContainsFold(FieldStartedBy, v)) +} + +// ResultEQ applies the EQ predicate on the "result" field. +func ResultEQ(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldEQ(FieldResult, v)) +} + +// ResultNEQ applies the NEQ predicate on the "result" field. +func ResultNEQ(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldNEQ(FieldResult, v)) +} + +// ResultIn applies the In predicate on the "result" field. +func ResultIn(vs ...string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldIn(FieldResult, vs...)) +} + +// ResultNotIn applies the NotIn predicate on the "result" field. +func ResultNotIn(vs ...string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldNotIn(FieldResult, vs...)) +} + +// ResultGT applies the GT predicate on the "result" field. +func ResultGT(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldGT(FieldResult, v)) +} + +// ResultGTE applies the GTE predicate on the "result" field. +func ResultGTE(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldGTE(FieldResult, v)) +} + +// ResultLT applies the LT predicate on the "result" field. +func ResultLT(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldLT(FieldResult, v)) +} + +// ResultLTE applies the LTE predicate on the "result" field. +func ResultLTE(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldLTE(FieldResult, v)) +} + +// ResultContains applies the Contains predicate on the "result" field. +func ResultContains(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldContains(FieldResult, v)) +} + +// ResultHasPrefix applies the HasPrefix predicate on the "result" field. +func ResultHasPrefix(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldHasPrefix(FieldResult, v)) +} + +// ResultHasSuffix applies the HasSuffix predicate on the "result" field. +func ResultHasSuffix(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldHasSuffix(FieldResult, v)) +} + +// ResultIsNil applies the IsNil predicate on the "result" field. +func ResultIsNil() predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldIsNull(FieldResult)) +} + +// ResultNotNil applies the NotNil predicate on the "result" field. +func ResultNotNil() predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldNotNull(FieldResult)) +} + +// ResultEqualFold applies the EqualFold predicate on the "result" field. +func ResultEqualFold(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldEqualFold(FieldResult, v)) +} + +// ResultContainsFold applies the ContainsFold predicate on the "result" field. +func ResultContainsFold(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldContainsFold(FieldResult, v)) +} + +// MetadataEQ applies the EQ predicate on the "metadata" field. +func MetadataEQ(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldEQ(FieldMetadata, v)) +} + +// MetadataNEQ applies the NEQ predicate on the "metadata" field. +func MetadataNEQ(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldNEQ(FieldMetadata, v)) +} + +// MetadataIn applies the In predicate on the "metadata" field. +func MetadataIn(vs ...string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldIn(FieldMetadata, vs...)) +} + +// MetadataNotIn applies the NotIn predicate on the "metadata" field. +func MetadataNotIn(vs ...string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldNotIn(FieldMetadata, vs...)) +} + +// MetadataGT applies the GT predicate on the "metadata" field. +func MetadataGT(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldGT(FieldMetadata, v)) +} + +// MetadataGTE applies the GTE predicate on the "metadata" field. +func MetadataGTE(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldGTE(FieldMetadata, v)) +} + +// MetadataLT applies the LT predicate on the "metadata" field. +func MetadataLT(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldLT(FieldMetadata, v)) +} + +// MetadataLTE applies the LTE predicate on the "metadata" field. +func MetadataLTE(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldLTE(FieldMetadata, v)) +} + +// MetadataContains applies the Contains predicate on the "metadata" field. +func MetadataContains(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldContains(FieldMetadata, v)) +} + +// MetadataHasPrefix applies the HasPrefix predicate on the "metadata" field. +func MetadataHasPrefix(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldHasPrefix(FieldMetadata, v)) +} + +// MetadataHasSuffix applies the HasSuffix predicate on the "metadata" field. +func MetadataHasSuffix(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldHasSuffix(FieldMetadata, v)) +} + +// MetadataEqualFold applies the EqualFold predicate on the "metadata" field. +func MetadataEqualFold(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldEqualFold(FieldMetadata, v)) +} + +// MetadataContainsFold applies the ContainsFold predicate on the "metadata" field. +func MetadataContainsFold(v string) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldContainsFold(FieldMetadata, v)) +} + +// CreatedEQ applies the EQ predicate on the "created" field. +func CreatedEQ(v time.Time) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldEQ(FieldCreated, v)) +} + +// CreatedNEQ applies the NEQ predicate on the "created" field. +func CreatedNEQ(v time.Time) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldNEQ(FieldCreated, v)) +} + +// CreatedIn applies the In predicate on the "created" field. +func CreatedIn(vs ...time.Time) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldIn(FieldCreated, vs...)) +} + +// CreatedNotIn applies the NotIn predicate on the "created" field. +func CreatedNotIn(vs ...time.Time) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldNotIn(FieldCreated, vs...)) +} + +// CreatedGT applies the GT predicate on the "created" field. +func CreatedGT(v time.Time) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldGT(FieldCreated, v)) +} + +// CreatedGTE applies the GTE predicate on the "created" field. +func CreatedGTE(v time.Time) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldGTE(FieldCreated, v)) +} + +// CreatedLT applies the LT predicate on the "created" field. +func CreatedLT(v time.Time) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldLT(FieldCreated, v)) +} + +// CreatedLTE applies the LTE predicate on the "created" field. +func CreatedLTE(v time.Time) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.FieldLTE(FieldCreated, v)) +} + +// And groups predicates with the AND operator between them. +func And(predicates ...predicate.MaintenanceOperation) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.AndPredicates(predicates...)) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.MaintenanceOperation) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.OrPredicates(predicates...)) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.MaintenanceOperation) predicate.MaintenanceOperation { + return predicate.MaintenanceOperation(sql.NotPredicates(p)) +} diff --git a/pkg/ent/maintenanceoperation_create.go b/pkg/ent/maintenanceoperation_create.go new file mode 100644 index 000000000..eefc2cf6d --- /dev/null +++ b/pkg/ent/maintenanceoperation_create.go @@ -0,0 +1,1168 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/maintenanceoperation" + "github.com/google/uuid" +) + +// MaintenanceOperationCreate is the builder for creating a MaintenanceOperation entity. +type MaintenanceOperationCreate struct { + config + mutation *MaintenanceOperationMutation + hooks []Hook + conflict []sql.ConflictOption +} + +// SetKey sets the "key" field. +func (_c *MaintenanceOperationCreate) SetKey(v string) *MaintenanceOperationCreate { + _c.mutation.SetKey(v) + return _c +} + +// SetTitle sets the "title" field. +func (_c *MaintenanceOperationCreate) SetTitle(v string) *MaintenanceOperationCreate { + _c.mutation.SetTitle(v) + return _c +} + +// SetDescription sets the "description" field. +func (_c *MaintenanceOperationCreate) SetDescription(v string) *MaintenanceOperationCreate { + _c.mutation.SetDescription(v) + return _c +} + +// SetNillableDescription sets the "description" field if the given value is not nil. +func (_c *MaintenanceOperationCreate) SetNillableDescription(v *string) *MaintenanceOperationCreate { + if v != nil { + _c.SetDescription(*v) + } + return _c +} + +// SetCategory sets the "category" field. +func (_c *MaintenanceOperationCreate) SetCategory(v string) *MaintenanceOperationCreate { + _c.mutation.SetCategory(v) + return _c +} + +// SetStatus sets the "status" field. +func (_c *MaintenanceOperationCreate) SetStatus(v string) *MaintenanceOperationCreate { + _c.mutation.SetStatus(v) + return _c +} + +// SetNillableStatus sets the "status" field if the given value is not nil. +func (_c *MaintenanceOperationCreate) SetNillableStatus(v *string) *MaintenanceOperationCreate { + if v != nil { + _c.SetStatus(*v) + } + return _c +} + +// SetStartedAt sets the "started_at" field. +func (_c *MaintenanceOperationCreate) SetStartedAt(v time.Time) *MaintenanceOperationCreate { + _c.mutation.SetStartedAt(v) + return _c +} + +// SetNillableStartedAt sets the "started_at" field if the given value is not nil. +func (_c *MaintenanceOperationCreate) SetNillableStartedAt(v *time.Time) *MaintenanceOperationCreate { + if v != nil { + _c.SetStartedAt(*v) + } + return _c +} + +// SetCompletedAt sets the "completed_at" field. +func (_c *MaintenanceOperationCreate) SetCompletedAt(v time.Time) *MaintenanceOperationCreate { + _c.mutation.SetCompletedAt(v) + return _c +} + +// SetNillableCompletedAt sets the "completed_at" field if the given value is not nil. +func (_c *MaintenanceOperationCreate) SetNillableCompletedAt(v *time.Time) *MaintenanceOperationCreate { + if v != nil { + _c.SetCompletedAt(*v) + } + return _c +} + +// SetStartedBy sets the "started_by" field. +func (_c *MaintenanceOperationCreate) SetStartedBy(v string) *MaintenanceOperationCreate { + _c.mutation.SetStartedBy(v) + return _c +} + +// SetNillableStartedBy sets the "started_by" field if the given value is not nil. +func (_c *MaintenanceOperationCreate) SetNillableStartedBy(v *string) *MaintenanceOperationCreate { + if v != nil { + _c.SetStartedBy(*v) + } + return _c +} + +// SetResult sets the "result" field. +func (_c *MaintenanceOperationCreate) SetResult(v string) *MaintenanceOperationCreate { + _c.mutation.SetResult(v) + return _c +} + +// SetNillableResult sets the "result" field if the given value is not nil. +func (_c *MaintenanceOperationCreate) SetNillableResult(v *string) *MaintenanceOperationCreate { + if v != nil { + _c.SetResult(*v) + } + return _c +} + +// SetMetadata sets the "metadata" field. +func (_c *MaintenanceOperationCreate) SetMetadata(v string) *MaintenanceOperationCreate { + _c.mutation.SetMetadata(v) + return _c +} + +// SetNillableMetadata sets the "metadata" field if the given value is not nil. +func (_c *MaintenanceOperationCreate) SetNillableMetadata(v *string) *MaintenanceOperationCreate { + if v != nil { + _c.SetMetadata(*v) + } + return _c +} + +// SetCreated sets the "created" field. +func (_c *MaintenanceOperationCreate) SetCreated(v time.Time) *MaintenanceOperationCreate { + _c.mutation.SetCreated(v) + return _c +} + +// SetNillableCreated sets the "created" field if the given value is not nil. +func (_c *MaintenanceOperationCreate) SetNillableCreated(v *time.Time) *MaintenanceOperationCreate { + if v != nil { + _c.SetCreated(*v) + } + return _c +} + +// SetID sets the "id" field. +func (_c *MaintenanceOperationCreate) SetID(v uuid.UUID) *MaintenanceOperationCreate { + _c.mutation.SetID(v) + return _c +} + +// SetNillableID sets the "id" field if the given value is not nil. +func (_c *MaintenanceOperationCreate) SetNillableID(v *uuid.UUID) *MaintenanceOperationCreate { + if v != nil { + _c.SetID(*v) + } + return _c +} + +// Mutation returns the MaintenanceOperationMutation object of the builder. +func (_c *MaintenanceOperationCreate) Mutation() *MaintenanceOperationMutation { + return _c.mutation +} + +// Save creates the MaintenanceOperation in the database. +func (_c *MaintenanceOperationCreate) Save(ctx context.Context) (*MaintenanceOperation, error) { + _c.defaults() + return withHooks(ctx, _c.sqlSave, _c.mutation, _c.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (_c *MaintenanceOperationCreate) SaveX(ctx context.Context) *MaintenanceOperation { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *MaintenanceOperationCreate) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *MaintenanceOperationCreate) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_c *MaintenanceOperationCreate) defaults() { + if _, ok := _c.mutation.Description(); !ok { + v := maintenanceoperation.DefaultDescription + _c.mutation.SetDescription(v) + } + if _, ok := _c.mutation.Status(); !ok { + v := maintenanceoperation.DefaultStatus + _c.mutation.SetStatus(v) + } + if _, ok := _c.mutation.Metadata(); !ok { + v := maintenanceoperation.DefaultMetadata + _c.mutation.SetMetadata(v) + } + if _, ok := _c.mutation.Created(); !ok { + v := maintenanceoperation.DefaultCreated() + _c.mutation.SetCreated(v) + } + if _, ok := _c.mutation.ID(); !ok { + v := maintenanceoperation.DefaultID() + _c.mutation.SetID(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_c *MaintenanceOperationCreate) check() error { + if _, ok := _c.mutation.Key(); !ok { + return &ValidationError{Name: "key", err: errors.New(`ent: missing required field "MaintenanceOperation.key"`)} + } + if v, ok := _c.mutation.Key(); ok { + if err := maintenanceoperation.KeyValidator(v); err != nil { + return &ValidationError{Name: "key", err: fmt.Errorf(`ent: validator failed for field "MaintenanceOperation.key": %w`, err)} + } + } + if _, ok := _c.mutation.Title(); !ok { + return &ValidationError{Name: "title", err: errors.New(`ent: missing required field "MaintenanceOperation.title"`)} + } + if v, ok := _c.mutation.Title(); ok { + if err := maintenanceoperation.TitleValidator(v); err != nil { + return &ValidationError{Name: "title", err: fmt.Errorf(`ent: validator failed for field "MaintenanceOperation.title": %w`, err)} + } + } + if _, ok := _c.mutation.Description(); !ok { + return &ValidationError{Name: "description", err: errors.New(`ent: missing required field "MaintenanceOperation.description"`)} + } + if _, ok := _c.mutation.Category(); !ok { + return &ValidationError{Name: "category", err: errors.New(`ent: missing required field "MaintenanceOperation.category"`)} + } + if v, ok := _c.mutation.Category(); ok { + if err := maintenanceoperation.CategoryValidator(v); err != nil { + return &ValidationError{Name: "category", err: fmt.Errorf(`ent: validator failed for field "MaintenanceOperation.category": %w`, err)} + } + } + if _, ok := _c.mutation.Status(); !ok { + return &ValidationError{Name: "status", err: errors.New(`ent: missing required field "MaintenanceOperation.status"`)} + } + if _, ok := _c.mutation.Metadata(); !ok { + return &ValidationError{Name: "metadata", err: errors.New(`ent: missing required field "MaintenanceOperation.metadata"`)} + } + if _, ok := _c.mutation.Created(); !ok { + return &ValidationError{Name: "created", err: errors.New(`ent: missing required field "MaintenanceOperation.created"`)} + } + return nil +} + +func (_c *MaintenanceOperationCreate) sqlSave(ctx context.Context) (*MaintenanceOperation, error) { + if err := _c.check(); err != nil { + return nil, err + } + _node, _spec := _c.createSpec() + if err := sqlgraph.CreateNode(ctx, _c.driver, _spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + if _spec.ID.Value != nil { + if id, ok := _spec.ID.Value.(*uuid.UUID); ok { + _node.ID = *id + } else if err := _node.ID.Scan(_spec.ID.Value); err != nil { + return nil, err + } + } + _c.mutation.id = &_node.ID + _c.mutation.done = true + return _node, nil +} + +func (_c *MaintenanceOperationCreate) createSpec() (*MaintenanceOperation, *sqlgraph.CreateSpec) { + var ( + _node = &MaintenanceOperation{config: _c.config} + _spec = sqlgraph.NewCreateSpec(maintenanceoperation.Table, sqlgraph.NewFieldSpec(maintenanceoperation.FieldID, field.TypeUUID)) + ) + _spec.OnConflict = _c.conflict + if id, ok := _c.mutation.ID(); ok { + _node.ID = id + _spec.ID.Value = &id + } + if value, ok := _c.mutation.Key(); ok { + _spec.SetField(maintenanceoperation.FieldKey, field.TypeString, value) + _node.Key = value + } + if value, ok := _c.mutation.Title(); ok { + _spec.SetField(maintenanceoperation.FieldTitle, field.TypeString, value) + _node.Title = value + } + if value, ok := _c.mutation.Description(); ok { + _spec.SetField(maintenanceoperation.FieldDescription, field.TypeString, value) + _node.Description = value + } + if value, ok := _c.mutation.Category(); ok { + _spec.SetField(maintenanceoperation.FieldCategory, field.TypeString, value) + _node.Category = value + } + if value, ok := _c.mutation.Status(); ok { + _spec.SetField(maintenanceoperation.FieldStatus, field.TypeString, value) + _node.Status = value + } + if value, ok := _c.mutation.StartedAt(); ok { + _spec.SetField(maintenanceoperation.FieldStartedAt, field.TypeTime, value) + _node.StartedAt = &value + } + if value, ok := _c.mutation.CompletedAt(); ok { + _spec.SetField(maintenanceoperation.FieldCompletedAt, field.TypeTime, value) + _node.CompletedAt = &value + } + if value, ok := _c.mutation.StartedBy(); ok { + _spec.SetField(maintenanceoperation.FieldStartedBy, field.TypeString, value) + _node.StartedBy = value + } + if value, ok := _c.mutation.Result(); ok { + _spec.SetField(maintenanceoperation.FieldResult, field.TypeString, value) + _node.Result = value + } + if value, ok := _c.mutation.Metadata(); ok { + _spec.SetField(maintenanceoperation.FieldMetadata, field.TypeString, value) + _node.Metadata = value + } + if value, ok := _c.mutation.Created(); ok { + _spec.SetField(maintenanceoperation.FieldCreated, field.TypeTime, value) + _node.Created = value + } + return _node, _spec +} + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.MaintenanceOperation.Create(). +// SetKey(v). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.MaintenanceOperationUpsert) { +// SetKey(v+v). +// }). +// Exec(ctx) +func (_c *MaintenanceOperationCreate) OnConflict(opts ...sql.ConflictOption) *MaintenanceOperationUpsertOne { + _c.conflict = opts + return &MaintenanceOperationUpsertOne{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.MaintenanceOperation.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *MaintenanceOperationCreate) OnConflictColumns(columns ...string) *MaintenanceOperationUpsertOne { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &MaintenanceOperationUpsertOne{ + create: _c, + } +} + +type ( + // MaintenanceOperationUpsertOne is the builder for "upsert"-ing + // one MaintenanceOperation node. + MaintenanceOperationUpsertOne struct { + create *MaintenanceOperationCreate + } + + // MaintenanceOperationUpsert is the "OnConflict" setter. + MaintenanceOperationUpsert struct { + *sql.UpdateSet + } +) + +// SetKey sets the "key" field. +func (u *MaintenanceOperationUpsert) SetKey(v string) *MaintenanceOperationUpsert { + u.Set(maintenanceoperation.FieldKey, v) + return u +} + +// UpdateKey sets the "key" field to the value that was provided on create. +func (u *MaintenanceOperationUpsert) UpdateKey() *MaintenanceOperationUpsert { + u.SetExcluded(maintenanceoperation.FieldKey) + return u +} + +// SetTitle sets the "title" field. +func (u *MaintenanceOperationUpsert) SetTitle(v string) *MaintenanceOperationUpsert { + u.Set(maintenanceoperation.FieldTitle, v) + return u +} + +// UpdateTitle sets the "title" field to the value that was provided on create. +func (u *MaintenanceOperationUpsert) UpdateTitle() *MaintenanceOperationUpsert { + u.SetExcluded(maintenanceoperation.FieldTitle) + return u +} + +// SetDescription sets the "description" field. +func (u *MaintenanceOperationUpsert) SetDescription(v string) *MaintenanceOperationUpsert { + u.Set(maintenanceoperation.FieldDescription, v) + return u +} + +// UpdateDescription sets the "description" field to the value that was provided on create. +func (u *MaintenanceOperationUpsert) UpdateDescription() *MaintenanceOperationUpsert { + u.SetExcluded(maintenanceoperation.FieldDescription) + return u +} + +// SetCategory sets the "category" field. +func (u *MaintenanceOperationUpsert) SetCategory(v string) *MaintenanceOperationUpsert { + u.Set(maintenanceoperation.FieldCategory, v) + return u +} + +// UpdateCategory sets the "category" field to the value that was provided on create. +func (u *MaintenanceOperationUpsert) UpdateCategory() *MaintenanceOperationUpsert { + u.SetExcluded(maintenanceoperation.FieldCategory) + return u +} + +// SetStatus sets the "status" field. +func (u *MaintenanceOperationUpsert) SetStatus(v string) *MaintenanceOperationUpsert { + u.Set(maintenanceoperation.FieldStatus, v) + return u +} + +// UpdateStatus sets the "status" field to the value that was provided on create. +func (u *MaintenanceOperationUpsert) UpdateStatus() *MaintenanceOperationUpsert { + u.SetExcluded(maintenanceoperation.FieldStatus) + return u +} + +// SetStartedAt sets the "started_at" field. +func (u *MaintenanceOperationUpsert) SetStartedAt(v time.Time) *MaintenanceOperationUpsert { + u.Set(maintenanceoperation.FieldStartedAt, v) + return u +} + +// UpdateStartedAt sets the "started_at" field to the value that was provided on create. +func (u *MaintenanceOperationUpsert) UpdateStartedAt() *MaintenanceOperationUpsert { + u.SetExcluded(maintenanceoperation.FieldStartedAt) + return u +} + +// ClearStartedAt clears the value of the "started_at" field. +func (u *MaintenanceOperationUpsert) ClearStartedAt() *MaintenanceOperationUpsert { + u.SetNull(maintenanceoperation.FieldStartedAt) + return u +} + +// SetCompletedAt sets the "completed_at" field. +func (u *MaintenanceOperationUpsert) SetCompletedAt(v time.Time) *MaintenanceOperationUpsert { + u.Set(maintenanceoperation.FieldCompletedAt, v) + return u +} + +// UpdateCompletedAt sets the "completed_at" field to the value that was provided on create. +func (u *MaintenanceOperationUpsert) UpdateCompletedAt() *MaintenanceOperationUpsert { + u.SetExcluded(maintenanceoperation.FieldCompletedAt) + return u +} + +// ClearCompletedAt clears the value of the "completed_at" field. +func (u *MaintenanceOperationUpsert) ClearCompletedAt() *MaintenanceOperationUpsert { + u.SetNull(maintenanceoperation.FieldCompletedAt) + return u +} + +// SetStartedBy sets the "started_by" field. +func (u *MaintenanceOperationUpsert) SetStartedBy(v string) *MaintenanceOperationUpsert { + u.Set(maintenanceoperation.FieldStartedBy, v) + return u +} + +// UpdateStartedBy sets the "started_by" field to the value that was provided on create. +func (u *MaintenanceOperationUpsert) UpdateStartedBy() *MaintenanceOperationUpsert { + u.SetExcluded(maintenanceoperation.FieldStartedBy) + return u +} + +// ClearStartedBy clears the value of the "started_by" field. +func (u *MaintenanceOperationUpsert) ClearStartedBy() *MaintenanceOperationUpsert { + u.SetNull(maintenanceoperation.FieldStartedBy) + return u +} + +// SetResult sets the "result" field. +func (u *MaintenanceOperationUpsert) SetResult(v string) *MaintenanceOperationUpsert { + u.Set(maintenanceoperation.FieldResult, v) + return u +} + +// UpdateResult sets the "result" field to the value that was provided on create. +func (u *MaintenanceOperationUpsert) UpdateResult() *MaintenanceOperationUpsert { + u.SetExcluded(maintenanceoperation.FieldResult) + return u +} + +// ClearResult clears the value of the "result" field. +func (u *MaintenanceOperationUpsert) ClearResult() *MaintenanceOperationUpsert { + u.SetNull(maintenanceoperation.FieldResult) + return u +} + +// SetMetadata sets the "metadata" field. +func (u *MaintenanceOperationUpsert) SetMetadata(v string) *MaintenanceOperationUpsert { + u.Set(maintenanceoperation.FieldMetadata, v) + return u +} + +// UpdateMetadata sets the "metadata" field to the value that was provided on create. +func (u *MaintenanceOperationUpsert) UpdateMetadata() *MaintenanceOperationUpsert { + u.SetExcluded(maintenanceoperation.FieldMetadata) + return u +} + +// UpdateNewValues updates the mutable fields using the new values that were set on create except the ID field. +// Using this option is equivalent to using: +// +// client.MaintenanceOperation.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(maintenanceoperation.FieldID) +// }), +// ). +// Exec(ctx) +func (u *MaintenanceOperationUpsertOne) UpdateNewValues() *MaintenanceOperationUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + if _, exists := u.create.mutation.ID(); exists { + s.SetIgnore(maintenanceoperation.FieldID) + } + if _, exists := u.create.mutation.Created(); exists { + s.SetIgnore(maintenanceoperation.FieldCreated) + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.MaintenanceOperation.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *MaintenanceOperationUpsertOne) Ignore() *MaintenanceOperationUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *MaintenanceOperationUpsertOne) DoNothing() *MaintenanceOperationUpsertOne { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the MaintenanceOperationCreate.OnConflict +// documentation for more info. +func (u *MaintenanceOperationUpsertOne) Update(set func(*MaintenanceOperationUpsert)) *MaintenanceOperationUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&MaintenanceOperationUpsert{UpdateSet: update}) + })) + return u +} + +// SetKey sets the "key" field. +func (u *MaintenanceOperationUpsertOne) SetKey(v string) *MaintenanceOperationUpsertOne { + return u.Update(func(s *MaintenanceOperationUpsert) { + s.SetKey(v) + }) +} + +// UpdateKey sets the "key" field to the value that was provided on create. +func (u *MaintenanceOperationUpsertOne) UpdateKey() *MaintenanceOperationUpsertOne { + return u.Update(func(s *MaintenanceOperationUpsert) { + s.UpdateKey() + }) +} + +// SetTitle sets the "title" field. +func (u *MaintenanceOperationUpsertOne) SetTitle(v string) *MaintenanceOperationUpsertOne { + return u.Update(func(s *MaintenanceOperationUpsert) { + s.SetTitle(v) + }) +} + +// UpdateTitle sets the "title" field to the value that was provided on create. +func (u *MaintenanceOperationUpsertOne) UpdateTitle() *MaintenanceOperationUpsertOne { + return u.Update(func(s *MaintenanceOperationUpsert) { + s.UpdateTitle() + }) +} + +// SetDescription sets the "description" field. +func (u *MaintenanceOperationUpsertOne) SetDescription(v string) *MaintenanceOperationUpsertOne { + return u.Update(func(s *MaintenanceOperationUpsert) { + s.SetDescription(v) + }) +} + +// UpdateDescription sets the "description" field to the value that was provided on create. +func (u *MaintenanceOperationUpsertOne) UpdateDescription() *MaintenanceOperationUpsertOne { + return u.Update(func(s *MaintenanceOperationUpsert) { + s.UpdateDescription() + }) +} + +// SetCategory sets the "category" field. +func (u *MaintenanceOperationUpsertOne) SetCategory(v string) *MaintenanceOperationUpsertOne { + return u.Update(func(s *MaintenanceOperationUpsert) { + s.SetCategory(v) + }) +} + +// UpdateCategory sets the "category" field to the value that was provided on create. +func (u *MaintenanceOperationUpsertOne) UpdateCategory() *MaintenanceOperationUpsertOne { + return u.Update(func(s *MaintenanceOperationUpsert) { + s.UpdateCategory() + }) +} + +// SetStatus sets the "status" field. +func (u *MaintenanceOperationUpsertOne) SetStatus(v string) *MaintenanceOperationUpsertOne { + return u.Update(func(s *MaintenanceOperationUpsert) { + s.SetStatus(v) + }) +} + +// UpdateStatus sets the "status" field to the value that was provided on create. +func (u *MaintenanceOperationUpsertOne) UpdateStatus() *MaintenanceOperationUpsertOne { + return u.Update(func(s *MaintenanceOperationUpsert) { + s.UpdateStatus() + }) +} + +// SetStartedAt sets the "started_at" field. +func (u *MaintenanceOperationUpsertOne) SetStartedAt(v time.Time) *MaintenanceOperationUpsertOne { + return u.Update(func(s *MaintenanceOperationUpsert) { + s.SetStartedAt(v) + }) +} + +// UpdateStartedAt sets the "started_at" field to the value that was provided on create. +func (u *MaintenanceOperationUpsertOne) UpdateStartedAt() *MaintenanceOperationUpsertOne { + return u.Update(func(s *MaintenanceOperationUpsert) { + s.UpdateStartedAt() + }) +} + +// ClearStartedAt clears the value of the "started_at" field. +func (u *MaintenanceOperationUpsertOne) ClearStartedAt() *MaintenanceOperationUpsertOne { + return u.Update(func(s *MaintenanceOperationUpsert) { + s.ClearStartedAt() + }) +} + +// SetCompletedAt sets the "completed_at" field. +func (u *MaintenanceOperationUpsertOne) SetCompletedAt(v time.Time) *MaintenanceOperationUpsertOne { + return u.Update(func(s *MaintenanceOperationUpsert) { + s.SetCompletedAt(v) + }) +} + +// UpdateCompletedAt sets the "completed_at" field to the value that was provided on create. +func (u *MaintenanceOperationUpsertOne) UpdateCompletedAt() *MaintenanceOperationUpsertOne { + return u.Update(func(s *MaintenanceOperationUpsert) { + s.UpdateCompletedAt() + }) +} + +// ClearCompletedAt clears the value of the "completed_at" field. +func (u *MaintenanceOperationUpsertOne) ClearCompletedAt() *MaintenanceOperationUpsertOne { + return u.Update(func(s *MaintenanceOperationUpsert) { + s.ClearCompletedAt() + }) +} + +// SetStartedBy sets the "started_by" field. +func (u *MaintenanceOperationUpsertOne) SetStartedBy(v string) *MaintenanceOperationUpsertOne { + return u.Update(func(s *MaintenanceOperationUpsert) { + s.SetStartedBy(v) + }) +} + +// UpdateStartedBy sets the "started_by" field to the value that was provided on create. +func (u *MaintenanceOperationUpsertOne) UpdateStartedBy() *MaintenanceOperationUpsertOne { + return u.Update(func(s *MaintenanceOperationUpsert) { + s.UpdateStartedBy() + }) +} + +// ClearStartedBy clears the value of the "started_by" field. +func (u *MaintenanceOperationUpsertOne) ClearStartedBy() *MaintenanceOperationUpsertOne { + return u.Update(func(s *MaintenanceOperationUpsert) { + s.ClearStartedBy() + }) +} + +// SetResult sets the "result" field. +func (u *MaintenanceOperationUpsertOne) SetResult(v string) *MaintenanceOperationUpsertOne { + return u.Update(func(s *MaintenanceOperationUpsert) { + s.SetResult(v) + }) +} + +// UpdateResult sets the "result" field to the value that was provided on create. +func (u *MaintenanceOperationUpsertOne) UpdateResult() *MaintenanceOperationUpsertOne { + return u.Update(func(s *MaintenanceOperationUpsert) { + s.UpdateResult() + }) +} + +// ClearResult clears the value of the "result" field. +func (u *MaintenanceOperationUpsertOne) ClearResult() *MaintenanceOperationUpsertOne { + return u.Update(func(s *MaintenanceOperationUpsert) { + s.ClearResult() + }) +} + +// SetMetadata sets the "metadata" field. +func (u *MaintenanceOperationUpsertOne) SetMetadata(v string) *MaintenanceOperationUpsertOne { + return u.Update(func(s *MaintenanceOperationUpsert) { + s.SetMetadata(v) + }) +} + +// UpdateMetadata sets the "metadata" field to the value that was provided on create. +func (u *MaintenanceOperationUpsertOne) UpdateMetadata() *MaintenanceOperationUpsertOne { + return u.Update(func(s *MaintenanceOperationUpsert) { + s.UpdateMetadata() + }) +} + +// Exec executes the query. +func (u *MaintenanceOperationUpsertOne) Exec(ctx context.Context) error { + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for MaintenanceOperationCreate.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *MaintenanceOperationUpsertOne) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} + +// Exec executes the UPSERT query and returns the inserted/updated ID. +func (u *MaintenanceOperationUpsertOne) ID(ctx context.Context) (id uuid.UUID, err error) { + if u.create.driver.Dialect() == dialect.MySQL { + // In case of "ON CONFLICT", there is no way to get back non-numeric ID + // fields from the database since MySQL does not support the RETURNING clause. + return id, errors.New("ent: MaintenanceOperationUpsertOne.ID is not supported by MySQL driver. Use MaintenanceOperationUpsertOne.Exec instead") + } + node, err := u.create.Save(ctx) + if err != nil { + return id, err + } + return node.ID, nil +} + +// IDX is like ID, but panics if an error occurs. +func (u *MaintenanceOperationUpsertOne) IDX(ctx context.Context) uuid.UUID { + id, err := u.ID(ctx) + if err != nil { + panic(err) + } + return id +} + +// MaintenanceOperationCreateBulk is the builder for creating many MaintenanceOperation entities in bulk. +type MaintenanceOperationCreateBulk struct { + config + err error + builders []*MaintenanceOperationCreate + conflict []sql.ConflictOption +} + +// Save creates the MaintenanceOperation entities in the database. +func (_c *MaintenanceOperationCreateBulk) Save(ctx context.Context) ([]*MaintenanceOperation, error) { + if _c.err != nil { + return nil, _c.err + } + specs := make([]*sqlgraph.CreateSpec, len(_c.builders)) + nodes := make([]*MaintenanceOperation, len(_c.builders)) + mutators := make([]Mutator, len(_c.builders)) + for i := range _c.builders { + func(i int, root context.Context) { + builder := _c.builders[i] + builder.defaults() + var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { + mutation, ok := m.(*MaintenanceOperationMutation) + if !ok { + return nil, fmt.Errorf("unexpected mutation type %T", m) + } + if err := builder.check(); err != nil { + return nil, err + } + builder.mutation = mutation + var err error + nodes[i], specs[i] = builder.createSpec() + if i < len(mutators)-1 { + _, err = mutators[i+1].Mutate(root, _c.builders[i+1].mutation) + } else { + spec := &sqlgraph.BatchCreateSpec{Nodes: specs} + spec.OnConflict = _c.conflict + // Invoke the actual operation on the latest mutation in the chain. + if err = sqlgraph.BatchCreate(ctx, _c.driver, spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + } + } + if err != nil { + return nil, err + } + mutation.id = &nodes[i].ID + mutation.done = true + return nodes[i], nil + }) + for i := len(builder.hooks) - 1; i >= 0; i-- { + mut = builder.hooks[i](mut) + } + mutators[i] = mut + }(i, ctx) + } + if len(mutators) > 0 { + if _, err := mutators[0].Mutate(ctx, _c.builders[0].mutation); err != nil { + return nil, err + } + } + return nodes, nil +} + +// SaveX is like Save, but panics if an error occurs. +func (_c *MaintenanceOperationCreateBulk) SaveX(ctx context.Context) []*MaintenanceOperation { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *MaintenanceOperationCreateBulk) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *MaintenanceOperationCreateBulk) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.MaintenanceOperation.CreateBulk(builders...). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.MaintenanceOperationUpsert) { +// SetKey(v+v). +// }). +// Exec(ctx) +func (_c *MaintenanceOperationCreateBulk) OnConflict(opts ...sql.ConflictOption) *MaintenanceOperationUpsertBulk { + _c.conflict = opts + return &MaintenanceOperationUpsertBulk{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.MaintenanceOperation.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *MaintenanceOperationCreateBulk) OnConflictColumns(columns ...string) *MaintenanceOperationUpsertBulk { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &MaintenanceOperationUpsertBulk{ + create: _c, + } +} + +// MaintenanceOperationUpsertBulk is the builder for "upsert"-ing +// a bulk of MaintenanceOperation nodes. +type MaintenanceOperationUpsertBulk struct { + create *MaintenanceOperationCreateBulk +} + +// UpdateNewValues updates the mutable fields using the new values that +// were set on create. Using this option is equivalent to using: +// +// client.MaintenanceOperation.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(maintenanceoperation.FieldID) +// }), +// ). +// Exec(ctx) +func (u *MaintenanceOperationUpsertBulk) UpdateNewValues() *MaintenanceOperationUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + for _, b := range u.create.builders { + if _, exists := b.mutation.ID(); exists { + s.SetIgnore(maintenanceoperation.FieldID) + } + if _, exists := b.mutation.Created(); exists { + s.SetIgnore(maintenanceoperation.FieldCreated) + } + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.MaintenanceOperation.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *MaintenanceOperationUpsertBulk) Ignore() *MaintenanceOperationUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *MaintenanceOperationUpsertBulk) DoNothing() *MaintenanceOperationUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the MaintenanceOperationCreateBulk.OnConflict +// documentation for more info. +func (u *MaintenanceOperationUpsertBulk) Update(set func(*MaintenanceOperationUpsert)) *MaintenanceOperationUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&MaintenanceOperationUpsert{UpdateSet: update}) + })) + return u +} + +// SetKey sets the "key" field. +func (u *MaintenanceOperationUpsertBulk) SetKey(v string) *MaintenanceOperationUpsertBulk { + return u.Update(func(s *MaintenanceOperationUpsert) { + s.SetKey(v) + }) +} + +// UpdateKey sets the "key" field to the value that was provided on create. +func (u *MaintenanceOperationUpsertBulk) UpdateKey() *MaintenanceOperationUpsertBulk { + return u.Update(func(s *MaintenanceOperationUpsert) { + s.UpdateKey() + }) +} + +// SetTitle sets the "title" field. +func (u *MaintenanceOperationUpsertBulk) SetTitle(v string) *MaintenanceOperationUpsertBulk { + return u.Update(func(s *MaintenanceOperationUpsert) { + s.SetTitle(v) + }) +} + +// UpdateTitle sets the "title" field to the value that was provided on create. +func (u *MaintenanceOperationUpsertBulk) UpdateTitle() *MaintenanceOperationUpsertBulk { + return u.Update(func(s *MaintenanceOperationUpsert) { + s.UpdateTitle() + }) +} + +// SetDescription sets the "description" field. +func (u *MaintenanceOperationUpsertBulk) SetDescription(v string) *MaintenanceOperationUpsertBulk { + return u.Update(func(s *MaintenanceOperationUpsert) { + s.SetDescription(v) + }) +} + +// UpdateDescription sets the "description" field to the value that was provided on create. +func (u *MaintenanceOperationUpsertBulk) UpdateDescription() *MaintenanceOperationUpsertBulk { + return u.Update(func(s *MaintenanceOperationUpsert) { + s.UpdateDescription() + }) +} + +// SetCategory sets the "category" field. +func (u *MaintenanceOperationUpsertBulk) SetCategory(v string) *MaintenanceOperationUpsertBulk { + return u.Update(func(s *MaintenanceOperationUpsert) { + s.SetCategory(v) + }) +} + +// UpdateCategory sets the "category" field to the value that was provided on create. +func (u *MaintenanceOperationUpsertBulk) UpdateCategory() *MaintenanceOperationUpsertBulk { + return u.Update(func(s *MaintenanceOperationUpsert) { + s.UpdateCategory() + }) +} + +// SetStatus sets the "status" field. +func (u *MaintenanceOperationUpsertBulk) SetStatus(v string) *MaintenanceOperationUpsertBulk { + return u.Update(func(s *MaintenanceOperationUpsert) { + s.SetStatus(v) + }) +} + +// UpdateStatus sets the "status" field to the value that was provided on create. +func (u *MaintenanceOperationUpsertBulk) UpdateStatus() *MaintenanceOperationUpsertBulk { + return u.Update(func(s *MaintenanceOperationUpsert) { + s.UpdateStatus() + }) +} + +// SetStartedAt sets the "started_at" field. +func (u *MaintenanceOperationUpsertBulk) SetStartedAt(v time.Time) *MaintenanceOperationUpsertBulk { + return u.Update(func(s *MaintenanceOperationUpsert) { + s.SetStartedAt(v) + }) +} + +// UpdateStartedAt sets the "started_at" field to the value that was provided on create. +func (u *MaintenanceOperationUpsertBulk) UpdateStartedAt() *MaintenanceOperationUpsertBulk { + return u.Update(func(s *MaintenanceOperationUpsert) { + s.UpdateStartedAt() + }) +} + +// ClearStartedAt clears the value of the "started_at" field. +func (u *MaintenanceOperationUpsertBulk) ClearStartedAt() *MaintenanceOperationUpsertBulk { + return u.Update(func(s *MaintenanceOperationUpsert) { + s.ClearStartedAt() + }) +} + +// SetCompletedAt sets the "completed_at" field. +func (u *MaintenanceOperationUpsertBulk) SetCompletedAt(v time.Time) *MaintenanceOperationUpsertBulk { + return u.Update(func(s *MaintenanceOperationUpsert) { + s.SetCompletedAt(v) + }) +} + +// UpdateCompletedAt sets the "completed_at" field to the value that was provided on create. +func (u *MaintenanceOperationUpsertBulk) UpdateCompletedAt() *MaintenanceOperationUpsertBulk { + return u.Update(func(s *MaintenanceOperationUpsert) { + s.UpdateCompletedAt() + }) +} + +// ClearCompletedAt clears the value of the "completed_at" field. +func (u *MaintenanceOperationUpsertBulk) ClearCompletedAt() *MaintenanceOperationUpsertBulk { + return u.Update(func(s *MaintenanceOperationUpsert) { + s.ClearCompletedAt() + }) +} + +// SetStartedBy sets the "started_by" field. +func (u *MaintenanceOperationUpsertBulk) SetStartedBy(v string) *MaintenanceOperationUpsertBulk { + return u.Update(func(s *MaintenanceOperationUpsert) { + s.SetStartedBy(v) + }) +} + +// UpdateStartedBy sets the "started_by" field to the value that was provided on create. +func (u *MaintenanceOperationUpsertBulk) UpdateStartedBy() *MaintenanceOperationUpsertBulk { + return u.Update(func(s *MaintenanceOperationUpsert) { + s.UpdateStartedBy() + }) +} + +// ClearStartedBy clears the value of the "started_by" field. +func (u *MaintenanceOperationUpsertBulk) ClearStartedBy() *MaintenanceOperationUpsertBulk { + return u.Update(func(s *MaintenanceOperationUpsert) { + s.ClearStartedBy() + }) +} + +// SetResult sets the "result" field. +func (u *MaintenanceOperationUpsertBulk) SetResult(v string) *MaintenanceOperationUpsertBulk { + return u.Update(func(s *MaintenanceOperationUpsert) { + s.SetResult(v) + }) +} + +// UpdateResult sets the "result" field to the value that was provided on create. +func (u *MaintenanceOperationUpsertBulk) UpdateResult() *MaintenanceOperationUpsertBulk { + return u.Update(func(s *MaintenanceOperationUpsert) { + s.UpdateResult() + }) +} + +// ClearResult clears the value of the "result" field. +func (u *MaintenanceOperationUpsertBulk) ClearResult() *MaintenanceOperationUpsertBulk { + return u.Update(func(s *MaintenanceOperationUpsert) { + s.ClearResult() + }) +} + +// SetMetadata sets the "metadata" field. +func (u *MaintenanceOperationUpsertBulk) SetMetadata(v string) *MaintenanceOperationUpsertBulk { + return u.Update(func(s *MaintenanceOperationUpsert) { + s.SetMetadata(v) + }) +} + +// UpdateMetadata sets the "metadata" field to the value that was provided on create. +func (u *MaintenanceOperationUpsertBulk) UpdateMetadata() *MaintenanceOperationUpsertBulk { + return u.Update(func(s *MaintenanceOperationUpsert) { + s.UpdateMetadata() + }) +} + +// Exec executes the query. +func (u *MaintenanceOperationUpsertBulk) Exec(ctx context.Context) error { + if u.create.err != nil { + return u.create.err + } + for i, b := range u.create.builders { + if len(b.conflict) != 0 { + return fmt.Errorf("ent: OnConflict was set for builder %d. Set it on the MaintenanceOperationCreateBulk instead", i) + } + } + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for MaintenanceOperationCreateBulk.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *MaintenanceOperationUpsertBulk) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/pkg/ent/maintenanceoperation_delete.go b/pkg/ent/maintenanceoperation_delete.go new file mode 100644 index 000000000..1e533174a --- /dev/null +++ b/pkg/ent/maintenanceoperation_delete.go @@ -0,0 +1,88 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/maintenanceoperation" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" +) + +// MaintenanceOperationDelete is the builder for deleting a MaintenanceOperation entity. +type MaintenanceOperationDelete struct { + config + hooks []Hook + mutation *MaintenanceOperationMutation +} + +// Where appends a list predicates to the MaintenanceOperationDelete builder. +func (_d *MaintenanceOperationDelete) Where(ps ...predicate.MaintenanceOperation) *MaintenanceOperationDelete { + _d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (_d *MaintenanceOperationDelete) Exec(ctx context.Context) (int, error) { + return withHooks(ctx, _d.sqlExec, _d.mutation, _d.hooks) +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *MaintenanceOperationDelete) ExecX(ctx context.Context) int { + n, err := _d.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (_d *MaintenanceOperationDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(maintenanceoperation.Table, sqlgraph.NewFieldSpec(maintenanceoperation.FieldID, field.TypeUUID)) + if ps := _d.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + affected, err := sqlgraph.DeleteNodes(ctx, _d.driver, _spec) + if err != nil && sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + _d.mutation.done = true + return affected, err +} + +// MaintenanceOperationDeleteOne is the builder for deleting a single MaintenanceOperation entity. +type MaintenanceOperationDeleteOne struct { + _d *MaintenanceOperationDelete +} + +// Where appends a list predicates to the MaintenanceOperationDelete builder. +func (_d *MaintenanceOperationDeleteOne) Where(ps ...predicate.MaintenanceOperation) *MaintenanceOperationDeleteOne { + _d._d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query. +func (_d *MaintenanceOperationDeleteOne) Exec(ctx context.Context) error { + n, err := _d._d.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{maintenanceoperation.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *MaintenanceOperationDeleteOne) ExecX(ctx context.Context) { + if err := _d.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/pkg/ent/maintenanceoperation_query.go b/pkg/ent/maintenanceoperation_query.go new file mode 100644 index 000000000..8e75f5966 --- /dev/null +++ b/pkg/ent/maintenanceoperation_query.go @@ -0,0 +1,565 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "fmt" + "math" + + "entgo.io/ent" + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/maintenanceoperation" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/google/uuid" +) + +// MaintenanceOperationQuery is the builder for querying MaintenanceOperation entities. +type MaintenanceOperationQuery struct { + config + ctx *QueryContext + order []maintenanceoperation.OrderOption + inters []Interceptor + predicates []predicate.MaintenanceOperation + modifiers []func(*sql.Selector) + // intermediate query (i.e. traversal path). + sql *sql.Selector + path func(context.Context) (*sql.Selector, error) +} + +// Where adds a new predicate for the MaintenanceOperationQuery builder. +func (_q *MaintenanceOperationQuery) Where(ps ...predicate.MaintenanceOperation) *MaintenanceOperationQuery { + _q.predicates = append(_q.predicates, ps...) + return _q +} + +// Limit the number of records to be returned by this query. +func (_q *MaintenanceOperationQuery) Limit(limit int) *MaintenanceOperationQuery { + _q.ctx.Limit = &limit + return _q +} + +// Offset to start from. +func (_q *MaintenanceOperationQuery) Offset(offset int) *MaintenanceOperationQuery { + _q.ctx.Offset = &offset + return _q +} + +// Unique configures the query builder to filter duplicate records on query. +// By default, unique is set to true, and can be disabled using this method. +func (_q *MaintenanceOperationQuery) Unique(unique bool) *MaintenanceOperationQuery { + _q.ctx.Unique = &unique + return _q +} + +// Order specifies how the records should be ordered. +func (_q *MaintenanceOperationQuery) Order(o ...maintenanceoperation.OrderOption) *MaintenanceOperationQuery { + _q.order = append(_q.order, o...) + return _q +} + +// First returns the first MaintenanceOperation entity from the query. +// Returns a *NotFoundError when no MaintenanceOperation was found. +func (_q *MaintenanceOperationQuery) First(ctx context.Context) (*MaintenanceOperation, error) { + nodes, err := _q.Limit(1).All(setContextOp(ctx, _q.ctx, ent.OpQueryFirst)) + if err != nil { + return nil, err + } + if len(nodes) == 0 { + return nil, &NotFoundError{maintenanceoperation.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (_q *MaintenanceOperationQuery) FirstX(ctx context.Context) *MaintenanceOperation { + node, err := _q.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// FirstID returns the first MaintenanceOperation ID from the query. +// Returns a *NotFoundError when no MaintenanceOperation ID was found. +func (_q *MaintenanceOperationQuery) FirstID(ctx context.Context) (id uuid.UUID, err error) { + var ids []uuid.UUID + if ids, err = _q.Limit(1).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryFirstID)); err != nil { + return + } + if len(ids) == 0 { + err = &NotFoundError{maintenanceoperation.Label} + return + } + return ids[0], nil +} + +// FirstIDX is like FirstID, but panics if an error occurs. +func (_q *MaintenanceOperationQuery) FirstIDX(ctx context.Context) uuid.UUID { + id, err := _q.FirstID(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return id +} + +// Only returns a single MaintenanceOperation entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one MaintenanceOperation entity is found. +// Returns a *NotFoundError when no MaintenanceOperation entities are found. +func (_q *MaintenanceOperationQuery) Only(ctx context.Context) (*MaintenanceOperation, error) { + nodes, err := _q.Limit(2).All(setContextOp(ctx, _q.ctx, ent.OpQueryOnly)) + if err != nil { + return nil, err + } + switch len(nodes) { + case 1: + return nodes[0], nil + case 0: + return nil, &NotFoundError{maintenanceoperation.Label} + default: + return nil, &NotSingularError{maintenanceoperation.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (_q *MaintenanceOperationQuery) OnlyX(ctx context.Context) *MaintenanceOperation { + node, err := _q.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// OnlyID is like Only, but returns the only MaintenanceOperation ID in the query. +// Returns a *NotSingularError when more than one MaintenanceOperation ID is found. +// Returns a *NotFoundError when no entities are found. +func (_q *MaintenanceOperationQuery) OnlyID(ctx context.Context) (id uuid.UUID, err error) { + var ids []uuid.UUID + if ids, err = _q.Limit(2).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryOnlyID)); err != nil { + return + } + switch len(ids) { + case 1: + id = ids[0] + case 0: + err = &NotFoundError{maintenanceoperation.Label} + default: + err = &NotSingularError{maintenanceoperation.Label} + } + return +} + +// OnlyIDX is like OnlyID, but panics if an error occurs. +func (_q *MaintenanceOperationQuery) OnlyIDX(ctx context.Context) uuid.UUID { + id, err := _q.OnlyID(ctx) + if err != nil { + panic(err) + } + return id +} + +// All executes the query and returns a list of MaintenanceOperations. +func (_q *MaintenanceOperationQuery) All(ctx context.Context) ([]*MaintenanceOperation, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryAll) + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*MaintenanceOperation, *MaintenanceOperationQuery]() + return withInterceptors[[]*MaintenanceOperation](ctx, _q, qr, _q.inters) +} + +// AllX is like All, but panics if an error occurs. +func (_q *MaintenanceOperationQuery) AllX(ctx context.Context) []*MaintenanceOperation { + nodes, err := _q.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// IDs executes the query and returns a list of MaintenanceOperation IDs. +func (_q *MaintenanceOperationQuery) IDs(ctx context.Context) (ids []uuid.UUID, err error) { + if _q.ctx.Unique == nil && _q.path != nil { + _q.Unique(true) + } + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryIDs) + if err = _q.Select(maintenanceoperation.FieldID).Scan(ctx, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// IDsX is like IDs, but panics if an error occurs. +func (_q *MaintenanceOperationQuery) IDsX(ctx context.Context) []uuid.UUID { + ids, err := _q.IDs(ctx) + if err != nil { + panic(err) + } + return ids +} + +// Count returns the count of the given query. +func (_q *MaintenanceOperationQuery) Count(ctx context.Context) (int, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryCount) + if err := _q.prepareQuery(ctx); err != nil { + return 0, err + } + return withInterceptors[int](ctx, _q, querierCount[*MaintenanceOperationQuery](), _q.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (_q *MaintenanceOperationQuery) CountX(ctx context.Context) int { + count, err := _q.Count(ctx) + if err != nil { + panic(err) + } + return count +} + +// Exist returns true if the query has elements in the graph. +func (_q *MaintenanceOperationQuery) Exist(ctx context.Context) (bool, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryExist) + switch _, err := _q.FirstID(ctx); { + case IsNotFound(err): + return false, nil + case err != nil: + return false, fmt.Errorf("ent: check existence: %w", err) + default: + return true, nil + } +} + +// ExistX is like Exist, but panics if an error occurs. +func (_q *MaintenanceOperationQuery) ExistX(ctx context.Context) bool { + exist, err := _q.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the MaintenanceOperationQuery builder, including all associated steps. It can be +// used to prepare common query builders and use them differently after the clone is made. +func (_q *MaintenanceOperationQuery) Clone() *MaintenanceOperationQuery { + if _q == nil { + return nil + } + return &MaintenanceOperationQuery{ + config: _q.config, + ctx: _q.ctx.Clone(), + order: append([]maintenanceoperation.OrderOption{}, _q.order...), + inters: append([]Interceptor{}, _q.inters...), + predicates: append([]predicate.MaintenanceOperation{}, _q.predicates...), + // clone intermediate query. + sql: _q.sql.Clone(), + path: _q.path, + } +} + +// GroupBy is used to group vertices by one or more fields/columns. +// It is often used with aggregate functions, like: count, max, mean, min, sum. +// +// Example: +// +// var v []struct { +// Key string `json:"key,omitempty"` +// Count int `json:"count,omitempty"` +// } +// +// client.MaintenanceOperation.Query(). +// GroupBy(maintenanceoperation.FieldKey). +// Aggregate(ent.Count()). +// Scan(ctx, &v) +func (_q *MaintenanceOperationQuery) GroupBy(field string, fields ...string) *MaintenanceOperationGroupBy { + _q.ctx.Fields = append([]string{field}, fields...) + grbuild := &MaintenanceOperationGroupBy{build: _q} + grbuild.flds = &_q.ctx.Fields + grbuild.label = maintenanceoperation.Label + grbuild.scan = grbuild.Scan + return grbuild +} + +// Select allows the selection one or more fields/columns for the given query, +// instead of selecting all fields in the entity. +// +// Example: +// +// var v []struct { +// Key string `json:"key,omitempty"` +// } +// +// client.MaintenanceOperation.Query(). +// Select(maintenanceoperation.FieldKey). +// Scan(ctx, &v) +func (_q *MaintenanceOperationQuery) Select(fields ...string) *MaintenanceOperationSelect { + _q.ctx.Fields = append(_q.ctx.Fields, fields...) + sbuild := &MaintenanceOperationSelect{MaintenanceOperationQuery: _q} + sbuild.label = maintenanceoperation.Label + sbuild.flds, sbuild.scan = &_q.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a MaintenanceOperationSelect configured with the given aggregations. +func (_q *MaintenanceOperationQuery) Aggregate(fns ...AggregateFunc) *MaintenanceOperationSelect { + return _q.Select().Aggregate(fns...) +} + +func (_q *MaintenanceOperationQuery) prepareQuery(ctx context.Context) error { + for _, inter := range _q.inters { + if inter == nil { + return fmt.Errorf("ent: uninitialized interceptor (forgotten import ent/runtime?)") + } + if trv, ok := inter.(Traverser); ok { + if err := trv.Traverse(ctx, _q); err != nil { + return err + } + } + } + for _, f := range _q.ctx.Fields { + if !maintenanceoperation.ValidColumn(f) { + return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + } + if _q.path != nil { + prev, err := _q.path(ctx) + if err != nil { + return err + } + _q.sql = prev + } + return nil +} + +func (_q *MaintenanceOperationQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*MaintenanceOperation, error) { + var ( + nodes = []*MaintenanceOperation{} + _spec = _q.querySpec() + ) + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*MaintenanceOperation).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &MaintenanceOperation{config: _q.config} + nodes = append(nodes, node) + return node.assignValues(columns, values) + } + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } + for i := range hooks { + hooks[i](ctx, _spec) + } + if err := sqlgraph.QueryNodes(ctx, _q.driver, _spec); err != nil { + return nil, err + } + if len(nodes) == 0 { + return nodes, nil + } + return nodes, nil +} + +func (_q *MaintenanceOperationQuery) sqlCount(ctx context.Context) (int, error) { + _spec := _q.querySpec() + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } + _spec.Node.Columns = _q.ctx.Fields + if len(_q.ctx.Fields) > 0 { + _spec.Unique = _q.ctx.Unique != nil && *_q.ctx.Unique + } + return sqlgraph.CountNodes(ctx, _q.driver, _spec) +} + +func (_q *MaintenanceOperationQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(maintenanceoperation.Table, maintenanceoperation.Columns, sqlgraph.NewFieldSpec(maintenanceoperation.FieldID, field.TypeUUID)) + _spec.From = _q.sql + if unique := _q.ctx.Unique; unique != nil { + _spec.Unique = *unique + } else if _q.path != nil { + _spec.Unique = true + } + if fields := _q.ctx.Fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, maintenanceoperation.FieldID) + for i := range fields { + if fields[i] != maintenanceoperation.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, fields[i]) + } + } + } + if ps := _q.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if limit := _q.ctx.Limit; limit != nil { + _spec.Limit = *limit + } + if offset := _q.ctx.Offset; offset != nil { + _spec.Offset = *offset + } + if ps := _q.order; len(ps) > 0 { + _spec.Order = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + return _spec +} + +func (_q *MaintenanceOperationQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(_q.driver.Dialect()) + t1 := builder.Table(maintenanceoperation.Table) + columns := _q.ctx.Fields + if len(columns) == 0 { + columns = maintenanceoperation.Columns + } + selector := builder.Select(t1.Columns(columns...)...).From(t1) + if _q.sql != nil { + selector = _q.sql + selector.Select(selector.Columns(columns...)...) + } + if _q.ctx.Unique != nil && *_q.ctx.Unique { + selector.Distinct() + } + for _, m := range _q.modifiers { + m(selector) + } + for _, p := range _q.predicates { + p(selector) + } + for _, p := range _q.order { + p(selector) + } + if offset := _q.ctx.Offset; offset != nil { + // limit is mandatory for offset clause. We start + // with default value, and override it below if needed. + selector.Offset(*offset).Limit(math.MaxInt32) + } + if limit := _q.ctx.Limit; limit != nil { + selector.Limit(*limit) + } + return selector +} + +// ForUpdate locks the selected rows against concurrent updates, and prevent them from being +// updated, deleted or "selected ... for update" by other sessions, until the transaction is +// either committed or rolled-back. +func (_q *MaintenanceOperationQuery) ForUpdate(opts ...sql.LockOption) *MaintenanceOperationQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForUpdate(opts...) + }) + return _q +} + +// ForShare behaves similarly to ForUpdate, except that it acquires a shared mode lock +// on any rows that are read. Other sessions can read the rows, but cannot modify them +// until your transaction commits. +func (_q *MaintenanceOperationQuery) ForShare(opts ...sql.LockOption) *MaintenanceOperationQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForShare(opts...) + }) + return _q +} + +// MaintenanceOperationGroupBy is the group-by builder for MaintenanceOperation entities. +type MaintenanceOperationGroupBy struct { + selector + build *MaintenanceOperationQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (_g *MaintenanceOperationGroupBy) Aggregate(fns ...AggregateFunc) *MaintenanceOperationGroupBy { + _g.fns = append(_g.fns, fns...) + return _g +} + +// Scan applies the selector query and scans the result into the given value. +func (_g *MaintenanceOperationGroupBy) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, _g.build.ctx, ent.OpQueryGroupBy) + if err := _g.build.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*MaintenanceOperationQuery, *MaintenanceOperationGroupBy](ctx, _g.build, _g, _g.build.inters, v) +} + +func (_g *MaintenanceOperationGroupBy) sqlScan(ctx context.Context, root *MaintenanceOperationQuery, v any) error { + selector := root.sqlQuery(ctx).Select() + aggregation := make([]string, 0, len(_g.fns)) + for _, fn := range _g.fns { + aggregation = append(aggregation, fn(selector)) + } + if len(selector.SelectedColumns()) == 0 { + columns := make([]string, 0, len(*_g.flds)+len(_g.fns)) + for _, f := range *_g.flds { + columns = append(columns, selector.C(f)) + } + columns = append(columns, aggregation...) + selector.Select(columns...) + } + selector.GroupBy(selector.Columns(*_g.flds...)...) + if err := selector.Err(); err != nil { + return err + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := _g.build.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} + +// MaintenanceOperationSelect is the builder for selecting fields of MaintenanceOperation entities. +type MaintenanceOperationSelect struct { + *MaintenanceOperationQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (_s *MaintenanceOperationSelect) Aggregate(fns ...AggregateFunc) *MaintenanceOperationSelect { + _s.fns = append(_s.fns, fns...) + return _s +} + +// Scan applies the selector query and scans the result into the given value. +func (_s *MaintenanceOperationSelect) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, _s.ctx, ent.OpQuerySelect) + if err := _s.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*MaintenanceOperationQuery, *MaintenanceOperationSelect](ctx, _s.MaintenanceOperationQuery, _s, _s.inters, v) +} + +func (_s *MaintenanceOperationSelect) sqlScan(ctx context.Context, root *MaintenanceOperationQuery, v any) error { + selector := root.sqlQuery(ctx) + aggregation := make([]string, 0, len(_s.fns)) + for _, fn := range _s.fns { + aggregation = append(aggregation, fn(selector)) + } + switch n := len(*_s.selector.flds); { + case n == 0 && len(aggregation) > 0: + selector.Select(aggregation...) + case n != 0 && len(aggregation) > 0: + selector.AppendSelect(aggregation...) + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := _s.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} diff --git a/pkg/ent/maintenanceoperation_update.go b/pkg/ent/maintenanceoperation_update.go new file mode 100644 index 000000000..703ea8250 --- /dev/null +++ b/pkg/ent/maintenanceoperation_update.go @@ -0,0 +1,634 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/maintenanceoperation" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" +) + +// MaintenanceOperationUpdate is the builder for updating MaintenanceOperation entities. +type MaintenanceOperationUpdate struct { + config + hooks []Hook + mutation *MaintenanceOperationMutation +} + +// Where appends a list predicates to the MaintenanceOperationUpdate builder. +func (_u *MaintenanceOperationUpdate) Where(ps ...predicate.MaintenanceOperation) *MaintenanceOperationUpdate { + _u.mutation.Where(ps...) + return _u +} + +// SetKey sets the "key" field. +func (_u *MaintenanceOperationUpdate) SetKey(v string) *MaintenanceOperationUpdate { + _u.mutation.SetKey(v) + return _u +} + +// SetNillableKey sets the "key" field if the given value is not nil. +func (_u *MaintenanceOperationUpdate) SetNillableKey(v *string) *MaintenanceOperationUpdate { + if v != nil { + _u.SetKey(*v) + } + return _u +} + +// SetTitle sets the "title" field. +func (_u *MaintenanceOperationUpdate) SetTitle(v string) *MaintenanceOperationUpdate { + _u.mutation.SetTitle(v) + return _u +} + +// SetNillableTitle sets the "title" field if the given value is not nil. +func (_u *MaintenanceOperationUpdate) SetNillableTitle(v *string) *MaintenanceOperationUpdate { + if v != nil { + _u.SetTitle(*v) + } + return _u +} + +// SetDescription sets the "description" field. +func (_u *MaintenanceOperationUpdate) SetDescription(v string) *MaintenanceOperationUpdate { + _u.mutation.SetDescription(v) + return _u +} + +// SetNillableDescription sets the "description" field if the given value is not nil. +func (_u *MaintenanceOperationUpdate) SetNillableDescription(v *string) *MaintenanceOperationUpdate { + if v != nil { + _u.SetDescription(*v) + } + return _u +} + +// SetCategory sets the "category" field. +func (_u *MaintenanceOperationUpdate) SetCategory(v string) *MaintenanceOperationUpdate { + _u.mutation.SetCategory(v) + return _u +} + +// SetNillableCategory sets the "category" field if the given value is not nil. +func (_u *MaintenanceOperationUpdate) SetNillableCategory(v *string) *MaintenanceOperationUpdate { + if v != nil { + _u.SetCategory(*v) + } + return _u +} + +// SetStatus sets the "status" field. +func (_u *MaintenanceOperationUpdate) SetStatus(v string) *MaintenanceOperationUpdate { + _u.mutation.SetStatus(v) + return _u +} + +// SetNillableStatus sets the "status" field if the given value is not nil. +func (_u *MaintenanceOperationUpdate) SetNillableStatus(v *string) *MaintenanceOperationUpdate { + if v != nil { + _u.SetStatus(*v) + } + return _u +} + +// SetStartedAt sets the "started_at" field. +func (_u *MaintenanceOperationUpdate) SetStartedAt(v time.Time) *MaintenanceOperationUpdate { + _u.mutation.SetStartedAt(v) + return _u +} + +// SetNillableStartedAt sets the "started_at" field if the given value is not nil. +func (_u *MaintenanceOperationUpdate) SetNillableStartedAt(v *time.Time) *MaintenanceOperationUpdate { + if v != nil { + _u.SetStartedAt(*v) + } + return _u +} + +// ClearStartedAt clears the value of the "started_at" field. +func (_u *MaintenanceOperationUpdate) ClearStartedAt() *MaintenanceOperationUpdate { + _u.mutation.ClearStartedAt() + return _u +} + +// SetCompletedAt sets the "completed_at" field. +func (_u *MaintenanceOperationUpdate) SetCompletedAt(v time.Time) *MaintenanceOperationUpdate { + _u.mutation.SetCompletedAt(v) + return _u +} + +// SetNillableCompletedAt sets the "completed_at" field if the given value is not nil. +func (_u *MaintenanceOperationUpdate) SetNillableCompletedAt(v *time.Time) *MaintenanceOperationUpdate { + if v != nil { + _u.SetCompletedAt(*v) + } + return _u +} + +// ClearCompletedAt clears the value of the "completed_at" field. +func (_u *MaintenanceOperationUpdate) ClearCompletedAt() *MaintenanceOperationUpdate { + _u.mutation.ClearCompletedAt() + return _u +} + +// SetStartedBy sets the "started_by" field. +func (_u *MaintenanceOperationUpdate) SetStartedBy(v string) *MaintenanceOperationUpdate { + _u.mutation.SetStartedBy(v) + return _u +} + +// SetNillableStartedBy sets the "started_by" field if the given value is not nil. +func (_u *MaintenanceOperationUpdate) SetNillableStartedBy(v *string) *MaintenanceOperationUpdate { + if v != nil { + _u.SetStartedBy(*v) + } + return _u +} + +// ClearStartedBy clears the value of the "started_by" field. +func (_u *MaintenanceOperationUpdate) ClearStartedBy() *MaintenanceOperationUpdate { + _u.mutation.ClearStartedBy() + return _u +} + +// SetResult sets the "result" field. +func (_u *MaintenanceOperationUpdate) SetResult(v string) *MaintenanceOperationUpdate { + _u.mutation.SetResult(v) + return _u +} + +// SetNillableResult sets the "result" field if the given value is not nil. +func (_u *MaintenanceOperationUpdate) SetNillableResult(v *string) *MaintenanceOperationUpdate { + if v != nil { + _u.SetResult(*v) + } + return _u +} + +// ClearResult clears the value of the "result" field. +func (_u *MaintenanceOperationUpdate) ClearResult() *MaintenanceOperationUpdate { + _u.mutation.ClearResult() + return _u +} + +// SetMetadata sets the "metadata" field. +func (_u *MaintenanceOperationUpdate) SetMetadata(v string) *MaintenanceOperationUpdate { + _u.mutation.SetMetadata(v) + return _u +} + +// SetNillableMetadata sets the "metadata" field if the given value is not nil. +func (_u *MaintenanceOperationUpdate) SetNillableMetadata(v *string) *MaintenanceOperationUpdate { + if v != nil { + _u.SetMetadata(*v) + } + return _u +} + +// Mutation returns the MaintenanceOperationMutation object of the builder. +func (_u *MaintenanceOperationUpdate) Mutation() *MaintenanceOperationMutation { + return _u.mutation +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (_u *MaintenanceOperationUpdate) Save(ctx context.Context) (int, error) { + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *MaintenanceOperationUpdate) SaveX(ctx context.Context) int { + affected, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (_u *MaintenanceOperationUpdate) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *MaintenanceOperationUpdate) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_u *MaintenanceOperationUpdate) check() error { + if v, ok := _u.mutation.Key(); ok { + if err := maintenanceoperation.KeyValidator(v); err != nil { + return &ValidationError{Name: "key", err: fmt.Errorf(`ent: validator failed for field "MaintenanceOperation.key": %w`, err)} + } + } + if v, ok := _u.mutation.Title(); ok { + if err := maintenanceoperation.TitleValidator(v); err != nil { + return &ValidationError{Name: "title", err: fmt.Errorf(`ent: validator failed for field "MaintenanceOperation.title": %w`, err)} + } + } + if v, ok := _u.mutation.Category(); ok { + if err := maintenanceoperation.CategoryValidator(v); err != nil { + return &ValidationError{Name: "category", err: fmt.Errorf(`ent: validator failed for field "MaintenanceOperation.category": %w`, err)} + } + } + return nil +} + +func (_u *MaintenanceOperationUpdate) sqlSave(ctx context.Context) (_node int, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(maintenanceoperation.Table, maintenanceoperation.Columns, sqlgraph.NewFieldSpec(maintenanceoperation.FieldID, field.TypeUUID)) + if ps := _u.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := _u.mutation.Key(); ok { + _spec.SetField(maintenanceoperation.FieldKey, field.TypeString, value) + } + if value, ok := _u.mutation.Title(); ok { + _spec.SetField(maintenanceoperation.FieldTitle, field.TypeString, value) + } + if value, ok := _u.mutation.Description(); ok { + _spec.SetField(maintenanceoperation.FieldDescription, field.TypeString, value) + } + if value, ok := _u.mutation.Category(); ok { + _spec.SetField(maintenanceoperation.FieldCategory, field.TypeString, value) + } + if value, ok := _u.mutation.Status(); ok { + _spec.SetField(maintenanceoperation.FieldStatus, field.TypeString, value) + } + if value, ok := _u.mutation.StartedAt(); ok { + _spec.SetField(maintenanceoperation.FieldStartedAt, field.TypeTime, value) + } + if _u.mutation.StartedAtCleared() { + _spec.ClearField(maintenanceoperation.FieldStartedAt, field.TypeTime) + } + if value, ok := _u.mutation.CompletedAt(); ok { + _spec.SetField(maintenanceoperation.FieldCompletedAt, field.TypeTime, value) + } + if _u.mutation.CompletedAtCleared() { + _spec.ClearField(maintenanceoperation.FieldCompletedAt, field.TypeTime) + } + if value, ok := _u.mutation.StartedBy(); ok { + _spec.SetField(maintenanceoperation.FieldStartedBy, field.TypeString, value) + } + if _u.mutation.StartedByCleared() { + _spec.ClearField(maintenanceoperation.FieldStartedBy, field.TypeString) + } + if value, ok := _u.mutation.Result(); ok { + _spec.SetField(maintenanceoperation.FieldResult, field.TypeString, value) + } + if _u.mutation.ResultCleared() { + _spec.ClearField(maintenanceoperation.FieldResult, field.TypeString) + } + if value, ok := _u.mutation.Metadata(); ok { + _spec.SetField(maintenanceoperation.FieldMetadata, field.TypeString, value) + } + if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{maintenanceoperation.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + _u.mutation.done = true + return _node, nil +} + +// MaintenanceOperationUpdateOne is the builder for updating a single MaintenanceOperation entity. +type MaintenanceOperationUpdateOne struct { + config + fields []string + hooks []Hook + mutation *MaintenanceOperationMutation +} + +// SetKey sets the "key" field. +func (_u *MaintenanceOperationUpdateOne) SetKey(v string) *MaintenanceOperationUpdateOne { + _u.mutation.SetKey(v) + return _u +} + +// SetNillableKey sets the "key" field if the given value is not nil. +func (_u *MaintenanceOperationUpdateOne) SetNillableKey(v *string) *MaintenanceOperationUpdateOne { + if v != nil { + _u.SetKey(*v) + } + return _u +} + +// SetTitle sets the "title" field. +func (_u *MaintenanceOperationUpdateOne) SetTitle(v string) *MaintenanceOperationUpdateOne { + _u.mutation.SetTitle(v) + return _u +} + +// SetNillableTitle sets the "title" field if the given value is not nil. +func (_u *MaintenanceOperationUpdateOne) SetNillableTitle(v *string) *MaintenanceOperationUpdateOne { + if v != nil { + _u.SetTitle(*v) + } + return _u +} + +// SetDescription sets the "description" field. +func (_u *MaintenanceOperationUpdateOne) SetDescription(v string) *MaintenanceOperationUpdateOne { + _u.mutation.SetDescription(v) + return _u +} + +// SetNillableDescription sets the "description" field if the given value is not nil. +func (_u *MaintenanceOperationUpdateOne) SetNillableDescription(v *string) *MaintenanceOperationUpdateOne { + if v != nil { + _u.SetDescription(*v) + } + return _u +} + +// SetCategory sets the "category" field. +func (_u *MaintenanceOperationUpdateOne) SetCategory(v string) *MaintenanceOperationUpdateOne { + _u.mutation.SetCategory(v) + return _u +} + +// SetNillableCategory sets the "category" field if the given value is not nil. +func (_u *MaintenanceOperationUpdateOne) SetNillableCategory(v *string) *MaintenanceOperationUpdateOne { + if v != nil { + _u.SetCategory(*v) + } + return _u +} + +// SetStatus sets the "status" field. +func (_u *MaintenanceOperationUpdateOne) SetStatus(v string) *MaintenanceOperationUpdateOne { + _u.mutation.SetStatus(v) + return _u +} + +// SetNillableStatus sets the "status" field if the given value is not nil. +func (_u *MaintenanceOperationUpdateOne) SetNillableStatus(v *string) *MaintenanceOperationUpdateOne { + if v != nil { + _u.SetStatus(*v) + } + return _u +} + +// SetStartedAt sets the "started_at" field. +func (_u *MaintenanceOperationUpdateOne) SetStartedAt(v time.Time) *MaintenanceOperationUpdateOne { + _u.mutation.SetStartedAt(v) + return _u +} + +// SetNillableStartedAt sets the "started_at" field if the given value is not nil. +func (_u *MaintenanceOperationUpdateOne) SetNillableStartedAt(v *time.Time) *MaintenanceOperationUpdateOne { + if v != nil { + _u.SetStartedAt(*v) + } + return _u +} + +// ClearStartedAt clears the value of the "started_at" field. +func (_u *MaintenanceOperationUpdateOne) ClearStartedAt() *MaintenanceOperationUpdateOne { + _u.mutation.ClearStartedAt() + return _u +} + +// SetCompletedAt sets the "completed_at" field. +func (_u *MaintenanceOperationUpdateOne) SetCompletedAt(v time.Time) *MaintenanceOperationUpdateOne { + _u.mutation.SetCompletedAt(v) + return _u +} + +// SetNillableCompletedAt sets the "completed_at" field if the given value is not nil. +func (_u *MaintenanceOperationUpdateOne) SetNillableCompletedAt(v *time.Time) *MaintenanceOperationUpdateOne { + if v != nil { + _u.SetCompletedAt(*v) + } + return _u +} + +// ClearCompletedAt clears the value of the "completed_at" field. +func (_u *MaintenanceOperationUpdateOne) ClearCompletedAt() *MaintenanceOperationUpdateOne { + _u.mutation.ClearCompletedAt() + return _u +} + +// SetStartedBy sets the "started_by" field. +func (_u *MaintenanceOperationUpdateOne) SetStartedBy(v string) *MaintenanceOperationUpdateOne { + _u.mutation.SetStartedBy(v) + return _u +} + +// SetNillableStartedBy sets the "started_by" field if the given value is not nil. +func (_u *MaintenanceOperationUpdateOne) SetNillableStartedBy(v *string) *MaintenanceOperationUpdateOne { + if v != nil { + _u.SetStartedBy(*v) + } + return _u +} + +// ClearStartedBy clears the value of the "started_by" field. +func (_u *MaintenanceOperationUpdateOne) ClearStartedBy() *MaintenanceOperationUpdateOne { + _u.mutation.ClearStartedBy() + return _u +} + +// SetResult sets the "result" field. +func (_u *MaintenanceOperationUpdateOne) SetResult(v string) *MaintenanceOperationUpdateOne { + _u.mutation.SetResult(v) + return _u +} + +// SetNillableResult sets the "result" field if the given value is not nil. +func (_u *MaintenanceOperationUpdateOne) SetNillableResult(v *string) *MaintenanceOperationUpdateOne { + if v != nil { + _u.SetResult(*v) + } + return _u +} + +// ClearResult clears the value of the "result" field. +func (_u *MaintenanceOperationUpdateOne) ClearResult() *MaintenanceOperationUpdateOne { + _u.mutation.ClearResult() + return _u +} + +// SetMetadata sets the "metadata" field. +func (_u *MaintenanceOperationUpdateOne) SetMetadata(v string) *MaintenanceOperationUpdateOne { + _u.mutation.SetMetadata(v) + return _u +} + +// SetNillableMetadata sets the "metadata" field if the given value is not nil. +func (_u *MaintenanceOperationUpdateOne) SetNillableMetadata(v *string) *MaintenanceOperationUpdateOne { + if v != nil { + _u.SetMetadata(*v) + } + return _u +} + +// Mutation returns the MaintenanceOperationMutation object of the builder. +func (_u *MaintenanceOperationUpdateOne) Mutation() *MaintenanceOperationMutation { + return _u.mutation +} + +// Where appends a list predicates to the MaintenanceOperationUpdate builder. +func (_u *MaintenanceOperationUpdateOne) Where(ps ...predicate.MaintenanceOperation) *MaintenanceOperationUpdateOne { + _u.mutation.Where(ps...) + return _u +} + +// Select allows selecting one or more fields (columns) of the returned entity. +// The default is selecting all fields defined in the entity schema. +func (_u *MaintenanceOperationUpdateOne) Select(field string, fields ...string) *MaintenanceOperationUpdateOne { + _u.fields = append([]string{field}, fields...) + return _u +} + +// Save executes the query and returns the updated MaintenanceOperation entity. +func (_u *MaintenanceOperationUpdateOne) Save(ctx context.Context) (*MaintenanceOperation, error) { + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *MaintenanceOperationUpdateOne) SaveX(ctx context.Context) *MaintenanceOperation { + node, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (_u *MaintenanceOperationUpdateOne) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *MaintenanceOperationUpdateOne) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_u *MaintenanceOperationUpdateOne) check() error { + if v, ok := _u.mutation.Key(); ok { + if err := maintenanceoperation.KeyValidator(v); err != nil { + return &ValidationError{Name: "key", err: fmt.Errorf(`ent: validator failed for field "MaintenanceOperation.key": %w`, err)} + } + } + if v, ok := _u.mutation.Title(); ok { + if err := maintenanceoperation.TitleValidator(v); err != nil { + return &ValidationError{Name: "title", err: fmt.Errorf(`ent: validator failed for field "MaintenanceOperation.title": %w`, err)} + } + } + if v, ok := _u.mutation.Category(); ok { + if err := maintenanceoperation.CategoryValidator(v); err != nil { + return &ValidationError{Name: "category", err: fmt.Errorf(`ent: validator failed for field "MaintenanceOperation.category": %w`, err)} + } + } + return nil +} + +func (_u *MaintenanceOperationUpdateOne) sqlSave(ctx context.Context) (_node *MaintenanceOperation, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(maintenanceoperation.Table, maintenanceoperation.Columns, sqlgraph.NewFieldSpec(maintenanceoperation.FieldID, field.TypeUUID)) + id, ok := _u.mutation.ID() + if !ok { + return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "MaintenanceOperation.id" for update`)} + } + _spec.Node.ID.Value = id + if fields := _u.fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, maintenanceoperation.FieldID) + for _, f := range fields { + if !maintenanceoperation.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + if f != maintenanceoperation.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, f) + } + } + } + if ps := _u.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := _u.mutation.Key(); ok { + _spec.SetField(maintenanceoperation.FieldKey, field.TypeString, value) + } + if value, ok := _u.mutation.Title(); ok { + _spec.SetField(maintenanceoperation.FieldTitle, field.TypeString, value) + } + if value, ok := _u.mutation.Description(); ok { + _spec.SetField(maintenanceoperation.FieldDescription, field.TypeString, value) + } + if value, ok := _u.mutation.Category(); ok { + _spec.SetField(maintenanceoperation.FieldCategory, field.TypeString, value) + } + if value, ok := _u.mutation.Status(); ok { + _spec.SetField(maintenanceoperation.FieldStatus, field.TypeString, value) + } + if value, ok := _u.mutation.StartedAt(); ok { + _spec.SetField(maintenanceoperation.FieldStartedAt, field.TypeTime, value) + } + if _u.mutation.StartedAtCleared() { + _spec.ClearField(maintenanceoperation.FieldStartedAt, field.TypeTime) + } + if value, ok := _u.mutation.CompletedAt(); ok { + _spec.SetField(maintenanceoperation.FieldCompletedAt, field.TypeTime, value) + } + if _u.mutation.CompletedAtCleared() { + _spec.ClearField(maintenanceoperation.FieldCompletedAt, field.TypeTime) + } + if value, ok := _u.mutation.StartedBy(); ok { + _spec.SetField(maintenanceoperation.FieldStartedBy, field.TypeString, value) + } + if _u.mutation.StartedByCleared() { + _spec.ClearField(maintenanceoperation.FieldStartedBy, field.TypeString) + } + if value, ok := _u.mutation.Result(); ok { + _spec.SetField(maintenanceoperation.FieldResult, field.TypeString, value) + } + if _u.mutation.ResultCleared() { + _spec.ClearField(maintenanceoperation.FieldResult, field.TypeString) + } + if value, ok := _u.mutation.Metadata(); ok { + _spec.SetField(maintenanceoperation.FieldMetadata, field.TypeString, value) + } + _node = &MaintenanceOperation{config: _u.config} + _spec.Assign = _node.assignValues + _spec.ScanValues = _node.scanValues + if err = sqlgraph.UpdateNode(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{maintenanceoperation.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + _u.mutation.done = true + return _node, nil +} diff --git a/pkg/ent/maintenanceoperationrun.go b/pkg/ent/maintenanceoperationrun.go new file mode 100644 index 000000000..1c5240d7e --- /dev/null +++ b/pkg/ent/maintenanceoperationrun.go @@ -0,0 +1,176 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "fmt" + "strings" + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "github.com/GoogleCloudPlatform/scion/pkg/ent/maintenanceoperationrun" + "github.com/google/uuid" +) + +// MaintenanceOperationRun is the model entity for the MaintenanceOperationRun schema. +type MaintenanceOperationRun struct { + config `json:"-"` + // ID of the ent. + ID uuid.UUID `json:"id,omitempty"` + // OperationKey holds the value of the "operation_key" field. + OperationKey string `json:"operation_key,omitempty"` + // Status holds the value of the "status" field. + Status string `json:"status,omitempty"` + // StartedAt holds the value of the "started_at" field. + StartedAt time.Time `json:"started_at,omitempty"` + // CompletedAt holds the value of the "completed_at" field. + CompletedAt *time.Time `json:"completed_at,omitempty"` + // StartedBy holds the value of the "started_by" field. + StartedBy string `json:"started_by,omitempty"` + // Result holds the value of the "result" field. + Result string `json:"result,omitempty"` + // Log holds the value of the "log" field. + Log string `json:"log,omitempty"` + selectValues sql.SelectValues +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*MaintenanceOperationRun) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case maintenanceoperationrun.FieldOperationKey, maintenanceoperationrun.FieldStatus, maintenanceoperationrun.FieldStartedBy, maintenanceoperationrun.FieldResult, maintenanceoperationrun.FieldLog: + values[i] = new(sql.NullString) + case maintenanceoperationrun.FieldStartedAt, maintenanceoperationrun.FieldCompletedAt: + values[i] = new(sql.NullTime) + case maintenanceoperationrun.FieldID: + values[i] = new(uuid.UUID) + default: + values[i] = new(sql.UnknownType) + } + } + return values, nil +} + +// assignValues assigns the values that were returned from sql.Rows (after scanning) +// to the MaintenanceOperationRun fields. +func (_m *MaintenanceOperationRun) assignValues(columns []string, values []any) error { + if m, n := len(values), len(columns); m < n { + return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) + } + for i := range columns { + switch columns[i] { + case maintenanceoperationrun.FieldID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field id", values[i]) + } else if value != nil { + _m.ID = *value + } + case maintenanceoperationrun.FieldOperationKey: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field operation_key", values[i]) + } else if value.Valid { + _m.OperationKey = value.String + } + case maintenanceoperationrun.FieldStatus: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field status", values[i]) + } else if value.Valid { + _m.Status = value.String + } + case maintenanceoperationrun.FieldStartedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field started_at", values[i]) + } else if value.Valid { + _m.StartedAt = value.Time + } + case maintenanceoperationrun.FieldCompletedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field completed_at", values[i]) + } else if value.Valid { + _m.CompletedAt = new(time.Time) + *_m.CompletedAt = value.Time + } + case maintenanceoperationrun.FieldStartedBy: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field started_by", values[i]) + } else if value.Valid { + _m.StartedBy = value.String + } + case maintenanceoperationrun.FieldResult: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field result", values[i]) + } else if value.Valid { + _m.Result = value.String + } + case maintenanceoperationrun.FieldLog: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field log", values[i]) + } else if value.Valid { + _m.Log = value.String + } + default: + _m.selectValues.Set(columns[i], values[i]) + } + } + return nil +} + +// Value returns the ent.Value that was dynamically selected and assigned to the MaintenanceOperationRun. +// This includes values selected through modifiers, order, etc. +func (_m *MaintenanceOperationRun) Value(name string) (ent.Value, error) { + return _m.selectValues.Get(name) +} + +// Update returns a builder for updating this MaintenanceOperationRun. +// Note that you need to call MaintenanceOperationRun.Unwrap() before calling this method if this MaintenanceOperationRun +// was returned from a transaction, and the transaction was committed or rolled back. +func (_m *MaintenanceOperationRun) Update() *MaintenanceOperationRunUpdateOne { + return NewMaintenanceOperationRunClient(_m.config).UpdateOne(_m) +} + +// Unwrap unwraps the MaintenanceOperationRun entity that was returned from a transaction after it was closed, +// so that all future queries will be executed through the driver which created the transaction. +func (_m *MaintenanceOperationRun) Unwrap() *MaintenanceOperationRun { + _tx, ok := _m.config.driver.(*txDriver) + if !ok { + panic("ent: MaintenanceOperationRun is not a transactional entity") + } + _m.config.driver = _tx.drv + return _m +} + +// String implements the fmt.Stringer. +func (_m *MaintenanceOperationRun) String() string { + var builder strings.Builder + builder.WriteString("MaintenanceOperationRun(") + builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID)) + builder.WriteString("operation_key=") + builder.WriteString(_m.OperationKey) + builder.WriteString(", ") + builder.WriteString("status=") + builder.WriteString(_m.Status) + builder.WriteString(", ") + builder.WriteString("started_at=") + builder.WriteString(_m.StartedAt.Format(time.ANSIC)) + builder.WriteString(", ") + if v := _m.CompletedAt; v != nil { + builder.WriteString("completed_at=") + builder.WriteString(v.Format(time.ANSIC)) + } + builder.WriteString(", ") + builder.WriteString("started_by=") + builder.WriteString(_m.StartedBy) + builder.WriteString(", ") + builder.WriteString("result=") + builder.WriteString(_m.Result) + builder.WriteString(", ") + builder.WriteString("log=") + builder.WriteString(_m.Log) + builder.WriteByte(')') + return builder.String() +} + +// MaintenanceOperationRuns is a parsable slice of MaintenanceOperationRun. +type MaintenanceOperationRuns []*MaintenanceOperationRun diff --git a/pkg/ent/maintenanceoperationrun/maintenanceoperationrun.go b/pkg/ent/maintenanceoperationrun/maintenanceoperationrun.go new file mode 100644 index 000000000..bc3721459 --- /dev/null +++ b/pkg/ent/maintenanceoperationrun/maintenanceoperationrun.go @@ -0,0 +1,111 @@ +// Code generated by ent, DO NOT EDIT. + +package maintenanceoperationrun + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "github.com/google/uuid" +) + +const ( + // Label holds the string label denoting the maintenanceoperationrun type in the database. + Label = "maintenance_operation_run" + // FieldID holds the string denoting the id field in the database. + FieldID = "id" + // FieldOperationKey holds the string denoting the operation_key field in the database. + FieldOperationKey = "operation_key" + // FieldStatus holds the string denoting the status field in the database. + FieldStatus = "status" + // FieldStartedAt holds the string denoting the started_at field in the database. + FieldStartedAt = "started_at" + // FieldCompletedAt holds the string denoting the completed_at field in the database. + FieldCompletedAt = "completed_at" + // FieldStartedBy holds the string denoting the started_by field in the database. + FieldStartedBy = "started_by" + // FieldResult holds the string denoting the result field in the database. + FieldResult = "result" + // FieldLog holds the string denoting the log field in the database. + FieldLog = "log" + // Table holds the table name of the maintenanceoperationrun in the database. + Table = "maintenance_operation_runs" +) + +// Columns holds all SQL columns for maintenanceoperationrun fields. +var Columns = []string{ + FieldID, + FieldOperationKey, + FieldStatus, + FieldStartedAt, + FieldCompletedAt, + FieldStartedBy, + FieldResult, + FieldLog, +} + +// ValidColumn reports if the column name is valid (part of the table columns). +func ValidColumn(column string) bool { + for i := range Columns { + if column == Columns[i] { + return true + } + } + return false +} + +var ( + // OperationKeyValidator is a validator for the "operation_key" field. It is called by the builders before save. + OperationKeyValidator func(string) error + // DefaultStatus holds the default value on creation for the "status" field. + DefaultStatus string + // DefaultStartedAt holds the default value on creation for the "started_at" field. + DefaultStartedAt func() time.Time + // DefaultLog holds the default value on creation for the "log" field. + DefaultLog string + // DefaultID holds the default value on creation for the "id" field. + DefaultID func() uuid.UUID +) + +// OrderOption defines the ordering options for the MaintenanceOperationRun queries. +type OrderOption func(*sql.Selector) + +// ByID orders the results by the id field. +func ByID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldID, opts...).ToFunc() +} + +// ByOperationKey orders the results by the operation_key field. +func ByOperationKey(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldOperationKey, opts...).ToFunc() +} + +// ByStatus orders the results by the status field. +func ByStatus(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldStatus, opts...).ToFunc() +} + +// ByStartedAt orders the results by the started_at field. +func ByStartedAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldStartedAt, opts...).ToFunc() +} + +// ByCompletedAt orders the results by the completed_at field. +func ByCompletedAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCompletedAt, opts...).ToFunc() +} + +// ByStartedBy orders the results by the started_by field. +func ByStartedBy(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldStartedBy, opts...).ToFunc() +} + +// ByResult orders the results by the result field. +func ByResult(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldResult, opts...).ToFunc() +} + +// ByLog orders the results by the log field. +func ByLog(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldLog, opts...).ToFunc() +} diff --git a/pkg/ent/maintenanceoperationrun/where.go b/pkg/ent/maintenanceoperationrun/where.go new file mode 100644 index 000000000..dcb586cb2 --- /dev/null +++ b/pkg/ent/maintenanceoperationrun/where.go @@ -0,0 +1,541 @@ +// Code generated by ent, DO NOT EDIT. + +package maintenanceoperationrun + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/google/uuid" +) + +// ID filters vertices based on their ID field. +func ID(id uuid.UUID) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldEQ(FieldID, id)) +} + +// IDEQ applies the EQ predicate on the ID field. +func IDEQ(id uuid.UUID) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldEQ(FieldID, id)) +} + +// IDNEQ applies the NEQ predicate on the ID field. +func IDNEQ(id uuid.UUID) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldNEQ(FieldID, id)) +} + +// IDIn applies the In predicate on the ID field. +func IDIn(ids ...uuid.UUID) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldIn(FieldID, ids...)) +} + +// IDNotIn applies the NotIn predicate on the ID field. +func IDNotIn(ids ...uuid.UUID) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldNotIn(FieldID, ids...)) +} + +// IDGT applies the GT predicate on the ID field. +func IDGT(id uuid.UUID) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldGT(FieldID, id)) +} + +// IDGTE applies the GTE predicate on the ID field. +func IDGTE(id uuid.UUID) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldGTE(FieldID, id)) +} + +// IDLT applies the LT predicate on the ID field. +func IDLT(id uuid.UUID) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldLT(FieldID, id)) +} + +// IDLTE applies the LTE predicate on the ID field. +func IDLTE(id uuid.UUID) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldLTE(FieldID, id)) +} + +// OperationKey applies equality check predicate on the "operation_key" field. It's identical to OperationKeyEQ. +func OperationKey(v string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldEQ(FieldOperationKey, v)) +} + +// Status applies equality check predicate on the "status" field. It's identical to StatusEQ. +func Status(v string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldEQ(FieldStatus, v)) +} + +// StartedAt applies equality check predicate on the "started_at" field. It's identical to StartedAtEQ. +func StartedAt(v time.Time) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldEQ(FieldStartedAt, v)) +} + +// CompletedAt applies equality check predicate on the "completed_at" field. It's identical to CompletedAtEQ. +func CompletedAt(v time.Time) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldEQ(FieldCompletedAt, v)) +} + +// StartedBy applies equality check predicate on the "started_by" field. It's identical to StartedByEQ. +func StartedBy(v string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldEQ(FieldStartedBy, v)) +} + +// Result applies equality check predicate on the "result" field. It's identical to ResultEQ. +func Result(v string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldEQ(FieldResult, v)) +} + +// Log applies equality check predicate on the "log" field. It's identical to LogEQ. +func Log(v string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldEQ(FieldLog, v)) +} + +// OperationKeyEQ applies the EQ predicate on the "operation_key" field. +func OperationKeyEQ(v string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldEQ(FieldOperationKey, v)) +} + +// OperationKeyNEQ applies the NEQ predicate on the "operation_key" field. +func OperationKeyNEQ(v string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldNEQ(FieldOperationKey, v)) +} + +// OperationKeyIn applies the In predicate on the "operation_key" field. +func OperationKeyIn(vs ...string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldIn(FieldOperationKey, vs...)) +} + +// OperationKeyNotIn applies the NotIn predicate on the "operation_key" field. +func OperationKeyNotIn(vs ...string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldNotIn(FieldOperationKey, vs...)) +} + +// OperationKeyGT applies the GT predicate on the "operation_key" field. +func OperationKeyGT(v string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldGT(FieldOperationKey, v)) +} + +// OperationKeyGTE applies the GTE predicate on the "operation_key" field. +func OperationKeyGTE(v string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldGTE(FieldOperationKey, v)) +} + +// OperationKeyLT applies the LT predicate on the "operation_key" field. +func OperationKeyLT(v string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldLT(FieldOperationKey, v)) +} + +// OperationKeyLTE applies the LTE predicate on the "operation_key" field. +func OperationKeyLTE(v string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldLTE(FieldOperationKey, v)) +} + +// OperationKeyContains applies the Contains predicate on the "operation_key" field. +func OperationKeyContains(v string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldContains(FieldOperationKey, v)) +} + +// OperationKeyHasPrefix applies the HasPrefix predicate on the "operation_key" field. +func OperationKeyHasPrefix(v string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldHasPrefix(FieldOperationKey, v)) +} + +// OperationKeyHasSuffix applies the HasSuffix predicate on the "operation_key" field. +func OperationKeyHasSuffix(v string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldHasSuffix(FieldOperationKey, v)) +} + +// OperationKeyEqualFold applies the EqualFold predicate on the "operation_key" field. +func OperationKeyEqualFold(v string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldEqualFold(FieldOperationKey, v)) +} + +// OperationKeyContainsFold applies the ContainsFold predicate on the "operation_key" field. +func OperationKeyContainsFold(v string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldContainsFold(FieldOperationKey, v)) +} + +// StatusEQ applies the EQ predicate on the "status" field. +func StatusEQ(v string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldEQ(FieldStatus, v)) +} + +// StatusNEQ applies the NEQ predicate on the "status" field. +func StatusNEQ(v string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldNEQ(FieldStatus, v)) +} + +// StatusIn applies the In predicate on the "status" field. +func StatusIn(vs ...string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldIn(FieldStatus, vs...)) +} + +// StatusNotIn applies the NotIn predicate on the "status" field. +func StatusNotIn(vs ...string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldNotIn(FieldStatus, vs...)) +} + +// StatusGT applies the GT predicate on the "status" field. +func StatusGT(v string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldGT(FieldStatus, v)) +} + +// StatusGTE applies the GTE predicate on the "status" field. +func StatusGTE(v string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldGTE(FieldStatus, v)) +} + +// StatusLT applies the LT predicate on the "status" field. +func StatusLT(v string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldLT(FieldStatus, v)) +} + +// StatusLTE applies the LTE predicate on the "status" field. +func StatusLTE(v string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldLTE(FieldStatus, v)) +} + +// StatusContains applies the Contains predicate on the "status" field. +func StatusContains(v string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldContains(FieldStatus, v)) +} + +// StatusHasPrefix applies the HasPrefix predicate on the "status" field. +func StatusHasPrefix(v string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldHasPrefix(FieldStatus, v)) +} + +// StatusHasSuffix applies the HasSuffix predicate on the "status" field. +func StatusHasSuffix(v string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldHasSuffix(FieldStatus, v)) +} + +// StatusEqualFold applies the EqualFold predicate on the "status" field. +func StatusEqualFold(v string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldEqualFold(FieldStatus, v)) +} + +// StatusContainsFold applies the ContainsFold predicate on the "status" field. +func StatusContainsFold(v string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldContainsFold(FieldStatus, v)) +} + +// StartedAtEQ applies the EQ predicate on the "started_at" field. +func StartedAtEQ(v time.Time) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldEQ(FieldStartedAt, v)) +} + +// StartedAtNEQ applies the NEQ predicate on the "started_at" field. +func StartedAtNEQ(v time.Time) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldNEQ(FieldStartedAt, v)) +} + +// StartedAtIn applies the In predicate on the "started_at" field. +func StartedAtIn(vs ...time.Time) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldIn(FieldStartedAt, vs...)) +} + +// StartedAtNotIn applies the NotIn predicate on the "started_at" field. +func StartedAtNotIn(vs ...time.Time) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldNotIn(FieldStartedAt, vs...)) +} + +// StartedAtGT applies the GT predicate on the "started_at" field. +func StartedAtGT(v time.Time) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldGT(FieldStartedAt, v)) +} + +// StartedAtGTE applies the GTE predicate on the "started_at" field. +func StartedAtGTE(v time.Time) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldGTE(FieldStartedAt, v)) +} + +// StartedAtLT applies the LT predicate on the "started_at" field. +func StartedAtLT(v time.Time) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldLT(FieldStartedAt, v)) +} + +// StartedAtLTE applies the LTE predicate on the "started_at" field. +func StartedAtLTE(v time.Time) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldLTE(FieldStartedAt, v)) +} + +// CompletedAtEQ applies the EQ predicate on the "completed_at" field. +func CompletedAtEQ(v time.Time) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldEQ(FieldCompletedAt, v)) +} + +// CompletedAtNEQ applies the NEQ predicate on the "completed_at" field. +func CompletedAtNEQ(v time.Time) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldNEQ(FieldCompletedAt, v)) +} + +// CompletedAtIn applies the In predicate on the "completed_at" field. +func CompletedAtIn(vs ...time.Time) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldIn(FieldCompletedAt, vs...)) +} + +// CompletedAtNotIn applies the NotIn predicate on the "completed_at" field. +func CompletedAtNotIn(vs ...time.Time) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldNotIn(FieldCompletedAt, vs...)) +} + +// CompletedAtGT applies the GT predicate on the "completed_at" field. +func CompletedAtGT(v time.Time) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldGT(FieldCompletedAt, v)) +} + +// CompletedAtGTE applies the GTE predicate on the "completed_at" field. +func CompletedAtGTE(v time.Time) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldGTE(FieldCompletedAt, v)) +} + +// CompletedAtLT applies the LT predicate on the "completed_at" field. +func CompletedAtLT(v time.Time) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldLT(FieldCompletedAt, v)) +} + +// CompletedAtLTE applies the LTE predicate on the "completed_at" field. +func CompletedAtLTE(v time.Time) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldLTE(FieldCompletedAt, v)) +} + +// CompletedAtIsNil applies the IsNil predicate on the "completed_at" field. +func CompletedAtIsNil() predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldIsNull(FieldCompletedAt)) +} + +// CompletedAtNotNil applies the NotNil predicate on the "completed_at" field. +func CompletedAtNotNil() predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldNotNull(FieldCompletedAt)) +} + +// StartedByEQ applies the EQ predicate on the "started_by" field. +func StartedByEQ(v string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldEQ(FieldStartedBy, v)) +} + +// StartedByNEQ applies the NEQ predicate on the "started_by" field. +func StartedByNEQ(v string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldNEQ(FieldStartedBy, v)) +} + +// StartedByIn applies the In predicate on the "started_by" field. +func StartedByIn(vs ...string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldIn(FieldStartedBy, vs...)) +} + +// StartedByNotIn applies the NotIn predicate on the "started_by" field. +func StartedByNotIn(vs ...string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldNotIn(FieldStartedBy, vs...)) +} + +// StartedByGT applies the GT predicate on the "started_by" field. +func StartedByGT(v string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldGT(FieldStartedBy, v)) +} + +// StartedByGTE applies the GTE predicate on the "started_by" field. +func StartedByGTE(v string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldGTE(FieldStartedBy, v)) +} + +// StartedByLT applies the LT predicate on the "started_by" field. +func StartedByLT(v string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldLT(FieldStartedBy, v)) +} + +// StartedByLTE applies the LTE predicate on the "started_by" field. +func StartedByLTE(v string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldLTE(FieldStartedBy, v)) +} + +// StartedByContains applies the Contains predicate on the "started_by" field. +func StartedByContains(v string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldContains(FieldStartedBy, v)) +} + +// StartedByHasPrefix applies the HasPrefix predicate on the "started_by" field. +func StartedByHasPrefix(v string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldHasPrefix(FieldStartedBy, v)) +} + +// StartedByHasSuffix applies the HasSuffix predicate on the "started_by" field. +func StartedByHasSuffix(v string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldHasSuffix(FieldStartedBy, v)) +} + +// StartedByIsNil applies the IsNil predicate on the "started_by" field. +func StartedByIsNil() predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldIsNull(FieldStartedBy)) +} + +// StartedByNotNil applies the NotNil predicate on the "started_by" field. +func StartedByNotNil() predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldNotNull(FieldStartedBy)) +} + +// StartedByEqualFold applies the EqualFold predicate on the "started_by" field. +func StartedByEqualFold(v string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldEqualFold(FieldStartedBy, v)) +} + +// StartedByContainsFold applies the ContainsFold predicate on the "started_by" field. +func StartedByContainsFold(v string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldContainsFold(FieldStartedBy, v)) +} + +// ResultEQ applies the EQ predicate on the "result" field. +func ResultEQ(v string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldEQ(FieldResult, v)) +} + +// ResultNEQ applies the NEQ predicate on the "result" field. +func ResultNEQ(v string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldNEQ(FieldResult, v)) +} + +// ResultIn applies the In predicate on the "result" field. +func ResultIn(vs ...string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldIn(FieldResult, vs...)) +} + +// ResultNotIn applies the NotIn predicate on the "result" field. +func ResultNotIn(vs ...string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldNotIn(FieldResult, vs...)) +} + +// ResultGT applies the GT predicate on the "result" field. +func ResultGT(v string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldGT(FieldResult, v)) +} + +// ResultGTE applies the GTE predicate on the "result" field. +func ResultGTE(v string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldGTE(FieldResult, v)) +} + +// ResultLT applies the LT predicate on the "result" field. +func ResultLT(v string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldLT(FieldResult, v)) +} + +// ResultLTE applies the LTE predicate on the "result" field. +func ResultLTE(v string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldLTE(FieldResult, v)) +} + +// ResultContains applies the Contains predicate on the "result" field. +func ResultContains(v string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldContains(FieldResult, v)) +} + +// ResultHasPrefix applies the HasPrefix predicate on the "result" field. +func ResultHasPrefix(v string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldHasPrefix(FieldResult, v)) +} + +// ResultHasSuffix applies the HasSuffix predicate on the "result" field. +func ResultHasSuffix(v string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldHasSuffix(FieldResult, v)) +} + +// ResultIsNil applies the IsNil predicate on the "result" field. +func ResultIsNil() predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldIsNull(FieldResult)) +} + +// ResultNotNil applies the NotNil predicate on the "result" field. +func ResultNotNil() predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldNotNull(FieldResult)) +} + +// ResultEqualFold applies the EqualFold predicate on the "result" field. +func ResultEqualFold(v string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldEqualFold(FieldResult, v)) +} + +// ResultContainsFold applies the ContainsFold predicate on the "result" field. +func ResultContainsFold(v string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldContainsFold(FieldResult, v)) +} + +// LogEQ applies the EQ predicate on the "log" field. +func LogEQ(v string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldEQ(FieldLog, v)) +} + +// LogNEQ applies the NEQ predicate on the "log" field. +func LogNEQ(v string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldNEQ(FieldLog, v)) +} + +// LogIn applies the In predicate on the "log" field. +func LogIn(vs ...string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldIn(FieldLog, vs...)) +} + +// LogNotIn applies the NotIn predicate on the "log" field. +func LogNotIn(vs ...string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldNotIn(FieldLog, vs...)) +} + +// LogGT applies the GT predicate on the "log" field. +func LogGT(v string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldGT(FieldLog, v)) +} + +// LogGTE applies the GTE predicate on the "log" field. +func LogGTE(v string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldGTE(FieldLog, v)) +} + +// LogLT applies the LT predicate on the "log" field. +func LogLT(v string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldLT(FieldLog, v)) +} + +// LogLTE applies the LTE predicate on the "log" field. +func LogLTE(v string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldLTE(FieldLog, v)) +} + +// LogContains applies the Contains predicate on the "log" field. +func LogContains(v string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldContains(FieldLog, v)) +} + +// LogHasPrefix applies the HasPrefix predicate on the "log" field. +func LogHasPrefix(v string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldHasPrefix(FieldLog, v)) +} + +// LogHasSuffix applies the HasSuffix predicate on the "log" field. +func LogHasSuffix(v string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldHasSuffix(FieldLog, v)) +} + +// LogEqualFold applies the EqualFold predicate on the "log" field. +func LogEqualFold(v string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldEqualFold(FieldLog, v)) +} + +// LogContainsFold applies the ContainsFold predicate on the "log" field. +func LogContainsFold(v string) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.FieldContainsFold(FieldLog, v)) +} + +// And groups predicates with the AND operator between them. +func And(predicates ...predicate.MaintenanceOperationRun) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.AndPredicates(predicates...)) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.MaintenanceOperationRun) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.OrPredicates(predicates...)) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.MaintenanceOperationRun) predicate.MaintenanceOperationRun { + return predicate.MaintenanceOperationRun(sql.NotPredicates(p)) +} diff --git a/pkg/ent/maintenanceoperationrun_create.go b/pkg/ent/maintenanceoperationrun_create.go new file mode 100644 index 000000000..5743296e3 --- /dev/null +++ b/pkg/ent/maintenanceoperationrun_create.go @@ -0,0 +1,909 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/maintenanceoperationrun" + "github.com/google/uuid" +) + +// MaintenanceOperationRunCreate is the builder for creating a MaintenanceOperationRun entity. +type MaintenanceOperationRunCreate struct { + config + mutation *MaintenanceOperationRunMutation + hooks []Hook + conflict []sql.ConflictOption +} + +// SetOperationKey sets the "operation_key" field. +func (_c *MaintenanceOperationRunCreate) SetOperationKey(v string) *MaintenanceOperationRunCreate { + _c.mutation.SetOperationKey(v) + return _c +} + +// SetStatus sets the "status" field. +func (_c *MaintenanceOperationRunCreate) SetStatus(v string) *MaintenanceOperationRunCreate { + _c.mutation.SetStatus(v) + return _c +} + +// SetNillableStatus sets the "status" field if the given value is not nil. +func (_c *MaintenanceOperationRunCreate) SetNillableStatus(v *string) *MaintenanceOperationRunCreate { + if v != nil { + _c.SetStatus(*v) + } + return _c +} + +// SetStartedAt sets the "started_at" field. +func (_c *MaintenanceOperationRunCreate) SetStartedAt(v time.Time) *MaintenanceOperationRunCreate { + _c.mutation.SetStartedAt(v) + return _c +} + +// SetNillableStartedAt sets the "started_at" field if the given value is not nil. +func (_c *MaintenanceOperationRunCreate) SetNillableStartedAt(v *time.Time) *MaintenanceOperationRunCreate { + if v != nil { + _c.SetStartedAt(*v) + } + return _c +} + +// SetCompletedAt sets the "completed_at" field. +func (_c *MaintenanceOperationRunCreate) SetCompletedAt(v time.Time) *MaintenanceOperationRunCreate { + _c.mutation.SetCompletedAt(v) + return _c +} + +// SetNillableCompletedAt sets the "completed_at" field if the given value is not nil. +func (_c *MaintenanceOperationRunCreate) SetNillableCompletedAt(v *time.Time) *MaintenanceOperationRunCreate { + if v != nil { + _c.SetCompletedAt(*v) + } + return _c +} + +// SetStartedBy sets the "started_by" field. +func (_c *MaintenanceOperationRunCreate) SetStartedBy(v string) *MaintenanceOperationRunCreate { + _c.mutation.SetStartedBy(v) + return _c +} + +// SetNillableStartedBy sets the "started_by" field if the given value is not nil. +func (_c *MaintenanceOperationRunCreate) SetNillableStartedBy(v *string) *MaintenanceOperationRunCreate { + if v != nil { + _c.SetStartedBy(*v) + } + return _c +} + +// SetResult sets the "result" field. +func (_c *MaintenanceOperationRunCreate) SetResult(v string) *MaintenanceOperationRunCreate { + _c.mutation.SetResult(v) + return _c +} + +// SetNillableResult sets the "result" field if the given value is not nil. +func (_c *MaintenanceOperationRunCreate) SetNillableResult(v *string) *MaintenanceOperationRunCreate { + if v != nil { + _c.SetResult(*v) + } + return _c +} + +// SetLog sets the "log" field. +func (_c *MaintenanceOperationRunCreate) SetLog(v string) *MaintenanceOperationRunCreate { + _c.mutation.SetLog(v) + return _c +} + +// SetNillableLog sets the "log" field if the given value is not nil. +func (_c *MaintenanceOperationRunCreate) SetNillableLog(v *string) *MaintenanceOperationRunCreate { + if v != nil { + _c.SetLog(*v) + } + return _c +} + +// SetID sets the "id" field. +func (_c *MaintenanceOperationRunCreate) SetID(v uuid.UUID) *MaintenanceOperationRunCreate { + _c.mutation.SetID(v) + return _c +} + +// SetNillableID sets the "id" field if the given value is not nil. +func (_c *MaintenanceOperationRunCreate) SetNillableID(v *uuid.UUID) *MaintenanceOperationRunCreate { + if v != nil { + _c.SetID(*v) + } + return _c +} + +// Mutation returns the MaintenanceOperationRunMutation object of the builder. +func (_c *MaintenanceOperationRunCreate) Mutation() *MaintenanceOperationRunMutation { + return _c.mutation +} + +// Save creates the MaintenanceOperationRun in the database. +func (_c *MaintenanceOperationRunCreate) Save(ctx context.Context) (*MaintenanceOperationRun, error) { + _c.defaults() + return withHooks(ctx, _c.sqlSave, _c.mutation, _c.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (_c *MaintenanceOperationRunCreate) SaveX(ctx context.Context) *MaintenanceOperationRun { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *MaintenanceOperationRunCreate) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *MaintenanceOperationRunCreate) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_c *MaintenanceOperationRunCreate) defaults() { + if _, ok := _c.mutation.Status(); !ok { + v := maintenanceoperationrun.DefaultStatus + _c.mutation.SetStatus(v) + } + if _, ok := _c.mutation.StartedAt(); !ok { + v := maintenanceoperationrun.DefaultStartedAt() + _c.mutation.SetStartedAt(v) + } + if _, ok := _c.mutation.Log(); !ok { + v := maintenanceoperationrun.DefaultLog + _c.mutation.SetLog(v) + } + if _, ok := _c.mutation.ID(); !ok { + v := maintenanceoperationrun.DefaultID() + _c.mutation.SetID(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_c *MaintenanceOperationRunCreate) check() error { + if _, ok := _c.mutation.OperationKey(); !ok { + return &ValidationError{Name: "operation_key", err: errors.New(`ent: missing required field "MaintenanceOperationRun.operation_key"`)} + } + if v, ok := _c.mutation.OperationKey(); ok { + if err := maintenanceoperationrun.OperationKeyValidator(v); err != nil { + return &ValidationError{Name: "operation_key", err: fmt.Errorf(`ent: validator failed for field "MaintenanceOperationRun.operation_key": %w`, err)} + } + } + if _, ok := _c.mutation.Status(); !ok { + return &ValidationError{Name: "status", err: errors.New(`ent: missing required field "MaintenanceOperationRun.status"`)} + } + if _, ok := _c.mutation.StartedAt(); !ok { + return &ValidationError{Name: "started_at", err: errors.New(`ent: missing required field "MaintenanceOperationRun.started_at"`)} + } + if _, ok := _c.mutation.Log(); !ok { + return &ValidationError{Name: "log", err: errors.New(`ent: missing required field "MaintenanceOperationRun.log"`)} + } + return nil +} + +func (_c *MaintenanceOperationRunCreate) sqlSave(ctx context.Context) (*MaintenanceOperationRun, error) { + if err := _c.check(); err != nil { + return nil, err + } + _node, _spec := _c.createSpec() + if err := sqlgraph.CreateNode(ctx, _c.driver, _spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + if _spec.ID.Value != nil { + if id, ok := _spec.ID.Value.(*uuid.UUID); ok { + _node.ID = *id + } else if err := _node.ID.Scan(_spec.ID.Value); err != nil { + return nil, err + } + } + _c.mutation.id = &_node.ID + _c.mutation.done = true + return _node, nil +} + +func (_c *MaintenanceOperationRunCreate) createSpec() (*MaintenanceOperationRun, *sqlgraph.CreateSpec) { + var ( + _node = &MaintenanceOperationRun{config: _c.config} + _spec = sqlgraph.NewCreateSpec(maintenanceoperationrun.Table, sqlgraph.NewFieldSpec(maintenanceoperationrun.FieldID, field.TypeUUID)) + ) + _spec.OnConflict = _c.conflict + if id, ok := _c.mutation.ID(); ok { + _node.ID = id + _spec.ID.Value = &id + } + if value, ok := _c.mutation.OperationKey(); ok { + _spec.SetField(maintenanceoperationrun.FieldOperationKey, field.TypeString, value) + _node.OperationKey = value + } + if value, ok := _c.mutation.Status(); ok { + _spec.SetField(maintenanceoperationrun.FieldStatus, field.TypeString, value) + _node.Status = value + } + if value, ok := _c.mutation.StartedAt(); ok { + _spec.SetField(maintenanceoperationrun.FieldStartedAt, field.TypeTime, value) + _node.StartedAt = value + } + if value, ok := _c.mutation.CompletedAt(); ok { + _spec.SetField(maintenanceoperationrun.FieldCompletedAt, field.TypeTime, value) + _node.CompletedAt = &value + } + if value, ok := _c.mutation.StartedBy(); ok { + _spec.SetField(maintenanceoperationrun.FieldStartedBy, field.TypeString, value) + _node.StartedBy = value + } + if value, ok := _c.mutation.Result(); ok { + _spec.SetField(maintenanceoperationrun.FieldResult, field.TypeString, value) + _node.Result = value + } + if value, ok := _c.mutation.Log(); ok { + _spec.SetField(maintenanceoperationrun.FieldLog, field.TypeString, value) + _node.Log = value + } + return _node, _spec +} + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.MaintenanceOperationRun.Create(). +// SetOperationKey(v). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.MaintenanceOperationRunUpsert) { +// SetOperationKey(v+v). +// }). +// Exec(ctx) +func (_c *MaintenanceOperationRunCreate) OnConflict(opts ...sql.ConflictOption) *MaintenanceOperationRunUpsertOne { + _c.conflict = opts + return &MaintenanceOperationRunUpsertOne{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.MaintenanceOperationRun.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *MaintenanceOperationRunCreate) OnConflictColumns(columns ...string) *MaintenanceOperationRunUpsertOne { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &MaintenanceOperationRunUpsertOne{ + create: _c, + } +} + +type ( + // MaintenanceOperationRunUpsertOne is the builder for "upsert"-ing + // one MaintenanceOperationRun node. + MaintenanceOperationRunUpsertOne struct { + create *MaintenanceOperationRunCreate + } + + // MaintenanceOperationRunUpsert is the "OnConflict" setter. + MaintenanceOperationRunUpsert struct { + *sql.UpdateSet + } +) + +// SetOperationKey sets the "operation_key" field. +func (u *MaintenanceOperationRunUpsert) SetOperationKey(v string) *MaintenanceOperationRunUpsert { + u.Set(maintenanceoperationrun.FieldOperationKey, v) + return u +} + +// UpdateOperationKey sets the "operation_key" field to the value that was provided on create. +func (u *MaintenanceOperationRunUpsert) UpdateOperationKey() *MaintenanceOperationRunUpsert { + u.SetExcluded(maintenanceoperationrun.FieldOperationKey) + return u +} + +// SetStatus sets the "status" field. +func (u *MaintenanceOperationRunUpsert) SetStatus(v string) *MaintenanceOperationRunUpsert { + u.Set(maintenanceoperationrun.FieldStatus, v) + return u +} + +// UpdateStatus sets the "status" field to the value that was provided on create. +func (u *MaintenanceOperationRunUpsert) UpdateStatus() *MaintenanceOperationRunUpsert { + u.SetExcluded(maintenanceoperationrun.FieldStatus) + return u +} + +// SetCompletedAt sets the "completed_at" field. +func (u *MaintenanceOperationRunUpsert) SetCompletedAt(v time.Time) *MaintenanceOperationRunUpsert { + u.Set(maintenanceoperationrun.FieldCompletedAt, v) + return u +} + +// UpdateCompletedAt sets the "completed_at" field to the value that was provided on create. +func (u *MaintenanceOperationRunUpsert) UpdateCompletedAt() *MaintenanceOperationRunUpsert { + u.SetExcluded(maintenanceoperationrun.FieldCompletedAt) + return u +} + +// ClearCompletedAt clears the value of the "completed_at" field. +func (u *MaintenanceOperationRunUpsert) ClearCompletedAt() *MaintenanceOperationRunUpsert { + u.SetNull(maintenanceoperationrun.FieldCompletedAt) + return u +} + +// SetStartedBy sets the "started_by" field. +func (u *MaintenanceOperationRunUpsert) SetStartedBy(v string) *MaintenanceOperationRunUpsert { + u.Set(maintenanceoperationrun.FieldStartedBy, v) + return u +} + +// UpdateStartedBy sets the "started_by" field to the value that was provided on create. +func (u *MaintenanceOperationRunUpsert) UpdateStartedBy() *MaintenanceOperationRunUpsert { + u.SetExcluded(maintenanceoperationrun.FieldStartedBy) + return u +} + +// ClearStartedBy clears the value of the "started_by" field. +func (u *MaintenanceOperationRunUpsert) ClearStartedBy() *MaintenanceOperationRunUpsert { + u.SetNull(maintenanceoperationrun.FieldStartedBy) + return u +} + +// SetResult sets the "result" field. +func (u *MaintenanceOperationRunUpsert) SetResult(v string) *MaintenanceOperationRunUpsert { + u.Set(maintenanceoperationrun.FieldResult, v) + return u +} + +// UpdateResult sets the "result" field to the value that was provided on create. +func (u *MaintenanceOperationRunUpsert) UpdateResult() *MaintenanceOperationRunUpsert { + u.SetExcluded(maintenanceoperationrun.FieldResult) + return u +} + +// ClearResult clears the value of the "result" field. +func (u *MaintenanceOperationRunUpsert) ClearResult() *MaintenanceOperationRunUpsert { + u.SetNull(maintenanceoperationrun.FieldResult) + return u +} + +// SetLog sets the "log" field. +func (u *MaintenanceOperationRunUpsert) SetLog(v string) *MaintenanceOperationRunUpsert { + u.Set(maintenanceoperationrun.FieldLog, v) + return u +} + +// UpdateLog sets the "log" field to the value that was provided on create. +func (u *MaintenanceOperationRunUpsert) UpdateLog() *MaintenanceOperationRunUpsert { + u.SetExcluded(maintenanceoperationrun.FieldLog) + return u +} + +// UpdateNewValues updates the mutable fields using the new values that were set on create except the ID field. +// Using this option is equivalent to using: +// +// client.MaintenanceOperationRun.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(maintenanceoperationrun.FieldID) +// }), +// ). +// Exec(ctx) +func (u *MaintenanceOperationRunUpsertOne) UpdateNewValues() *MaintenanceOperationRunUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + if _, exists := u.create.mutation.ID(); exists { + s.SetIgnore(maintenanceoperationrun.FieldID) + } + if _, exists := u.create.mutation.StartedAt(); exists { + s.SetIgnore(maintenanceoperationrun.FieldStartedAt) + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.MaintenanceOperationRun.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *MaintenanceOperationRunUpsertOne) Ignore() *MaintenanceOperationRunUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *MaintenanceOperationRunUpsertOne) DoNothing() *MaintenanceOperationRunUpsertOne { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the MaintenanceOperationRunCreate.OnConflict +// documentation for more info. +func (u *MaintenanceOperationRunUpsertOne) Update(set func(*MaintenanceOperationRunUpsert)) *MaintenanceOperationRunUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&MaintenanceOperationRunUpsert{UpdateSet: update}) + })) + return u +} + +// SetOperationKey sets the "operation_key" field. +func (u *MaintenanceOperationRunUpsertOne) SetOperationKey(v string) *MaintenanceOperationRunUpsertOne { + return u.Update(func(s *MaintenanceOperationRunUpsert) { + s.SetOperationKey(v) + }) +} + +// UpdateOperationKey sets the "operation_key" field to the value that was provided on create. +func (u *MaintenanceOperationRunUpsertOne) UpdateOperationKey() *MaintenanceOperationRunUpsertOne { + return u.Update(func(s *MaintenanceOperationRunUpsert) { + s.UpdateOperationKey() + }) +} + +// SetStatus sets the "status" field. +func (u *MaintenanceOperationRunUpsertOne) SetStatus(v string) *MaintenanceOperationRunUpsertOne { + return u.Update(func(s *MaintenanceOperationRunUpsert) { + s.SetStatus(v) + }) +} + +// UpdateStatus sets the "status" field to the value that was provided on create. +func (u *MaintenanceOperationRunUpsertOne) UpdateStatus() *MaintenanceOperationRunUpsertOne { + return u.Update(func(s *MaintenanceOperationRunUpsert) { + s.UpdateStatus() + }) +} + +// SetCompletedAt sets the "completed_at" field. +func (u *MaintenanceOperationRunUpsertOne) SetCompletedAt(v time.Time) *MaintenanceOperationRunUpsertOne { + return u.Update(func(s *MaintenanceOperationRunUpsert) { + s.SetCompletedAt(v) + }) +} + +// UpdateCompletedAt sets the "completed_at" field to the value that was provided on create. +func (u *MaintenanceOperationRunUpsertOne) UpdateCompletedAt() *MaintenanceOperationRunUpsertOne { + return u.Update(func(s *MaintenanceOperationRunUpsert) { + s.UpdateCompletedAt() + }) +} + +// ClearCompletedAt clears the value of the "completed_at" field. +func (u *MaintenanceOperationRunUpsertOne) ClearCompletedAt() *MaintenanceOperationRunUpsertOne { + return u.Update(func(s *MaintenanceOperationRunUpsert) { + s.ClearCompletedAt() + }) +} + +// SetStartedBy sets the "started_by" field. +func (u *MaintenanceOperationRunUpsertOne) SetStartedBy(v string) *MaintenanceOperationRunUpsertOne { + return u.Update(func(s *MaintenanceOperationRunUpsert) { + s.SetStartedBy(v) + }) +} + +// UpdateStartedBy sets the "started_by" field to the value that was provided on create. +func (u *MaintenanceOperationRunUpsertOne) UpdateStartedBy() *MaintenanceOperationRunUpsertOne { + return u.Update(func(s *MaintenanceOperationRunUpsert) { + s.UpdateStartedBy() + }) +} + +// ClearStartedBy clears the value of the "started_by" field. +func (u *MaintenanceOperationRunUpsertOne) ClearStartedBy() *MaintenanceOperationRunUpsertOne { + return u.Update(func(s *MaintenanceOperationRunUpsert) { + s.ClearStartedBy() + }) +} + +// SetResult sets the "result" field. +func (u *MaintenanceOperationRunUpsertOne) SetResult(v string) *MaintenanceOperationRunUpsertOne { + return u.Update(func(s *MaintenanceOperationRunUpsert) { + s.SetResult(v) + }) +} + +// UpdateResult sets the "result" field to the value that was provided on create. +func (u *MaintenanceOperationRunUpsertOne) UpdateResult() *MaintenanceOperationRunUpsertOne { + return u.Update(func(s *MaintenanceOperationRunUpsert) { + s.UpdateResult() + }) +} + +// ClearResult clears the value of the "result" field. +func (u *MaintenanceOperationRunUpsertOne) ClearResult() *MaintenanceOperationRunUpsertOne { + return u.Update(func(s *MaintenanceOperationRunUpsert) { + s.ClearResult() + }) +} + +// SetLog sets the "log" field. +func (u *MaintenanceOperationRunUpsertOne) SetLog(v string) *MaintenanceOperationRunUpsertOne { + return u.Update(func(s *MaintenanceOperationRunUpsert) { + s.SetLog(v) + }) +} + +// UpdateLog sets the "log" field to the value that was provided on create. +func (u *MaintenanceOperationRunUpsertOne) UpdateLog() *MaintenanceOperationRunUpsertOne { + return u.Update(func(s *MaintenanceOperationRunUpsert) { + s.UpdateLog() + }) +} + +// Exec executes the query. +func (u *MaintenanceOperationRunUpsertOne) Exec(ctx context.Context) error { + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for MaintenanceOperationRunCreate.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *MaintenanceOperationRunUpsertOne) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} + +// Exec executes the UPSERT query and returns the inserted/updated ID. +func (u *MaintenanceOperationRunUpsertOne) ID(ctx context.Context) (id uuid.UUID, err error) { + if u.create.driver.Dialect() == dialect.MySQL { + // In case of "ON CONFLICT", there is no way to get back non-numeric ID + // fields from the database since MySQL does not support the RETURNING clause. + return id, errors.New("ent: MaintenanceOperationRunUpsertOne.ID is not supported by MySQL driver. Use MaintenanceOperationRunUpsertOne.Exec instead") + } + node, err := u.create.Save(ctx) + if err != nil { + return id, err + } + return node.ID, nil +} + +// IDX is like ID, but panics if an error occurs. +func (u *MaintenanceOperationRunUpsertOne) IDX(ctx context.Context) uuid.UUID { + id, err := u.ID(ctx) + if err != nil { + panic(err) + } + return id +} + +// MaintenanceOperationRunCreateBulk is the builder for creating many MaintenanceOperationRun entities in bulk. +type MaintenanceOperationRunCreateBulk struct { + config + err error + builders []*MaintenanceOperationRunCreate + conflict []sql.ConflictOption +} + +// Save creates the MaintenanceOperationRun entities in the database. +func (_c *MaintenanceOperationRunCreateBulk) Save(ctx context.Context) ([]*MaintenanceOperationRun, error) { + if _c.err != nil { + return nil, _c.err + } + specs := make([]*sqlgraph.CreateSpec, len(_c.builders)) + nodes := make([]*MaintenanceOperationRun, len(_c.builders)) + mutators := make([]Mutator, len(_c.builders)) + for i := range _c.builders { + func(i int, root context.Context) { + builder := _c.builders[i] + builder.defaults() + var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { + mutation, ok := m.(*MaintenanceOperationRunMutation) + if !ok { + return nil, fmt.Errorf("unexpected mutation type %T", m) + } + if err := builder.check(); err != nil { + return nil, err + } + builder.mutation = mutation + var err error + nodes[i], specs[i] = builder.createSpec() + if i < len(mutators)-1 { + _, err = mutators[i+1].Mutate(root, _c.builders[i+1].mutation) + } else { + spec := &sqlgraph.BatchCreateSpec{Nodes: specs} + spec.OnConflict = _c.conflict + // Invoke the actual operation on the latest mutation in the chain. + if err = sqlgraph.BatchCreate(ctx, _c.driver, spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + } + } + if err != nil { + return nil, err + } + mutation.id = &nodes[i].ID + mutation.done = true + return nodes[i], nil + }) + for i := len(builder.hooks) - 1; i >= 0; i-- { + mut = builder.hooks[i](mut) + } + mutators[i] = mut + }(i, ctx) + } + if len(mutators) > 0 { + if _, err := mutators[0].Mutate(ctx, _c.builders[0].mutation); err != nil { + return nil, err + } + } + return nodes, nil +} + +// SaveX is like Save, but panics if an error occurs. +func (_c *MaintenanceOperationRunCreateBulk) SaveX(ctx context.Context) []*MaintenanceOperationRun { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *MaintenanceOperationRunCreateBulk) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *MaintenanceOperationRunCreateBulk) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.MaintenanceOperationRun.CreateBulk(builders...). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.MaintenanceOperationRunUpsert) { +// SetOperationKey(v+v). +// }). +// Exec(ctx) +func (_c *MaintenanceOperationRunCreateBulk) OnConflict(opts ...sql.ConflictOption) *MaintenanceOperationRunUpsertBulk { + _c.conflict = opts + return &MaintenanceOperationRunUpsertBulk{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.MaintenanceOperationRun.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *MaintenanceOperationRunCreateBulk) OnConflictColumns(columns ...string) *MaintenanceOperationRunUpsertBulk { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &MaintenanceOperationRunUpsertBulk{ + create: _c, + } +} + +// MaintenanceOperationRunUpsertBulk is the builder for "upsert"-ing +// a bulk of MaintenanceOperationRun nodes. +type MaintenanceOperationRunUpsertBulk struct { + create *MaintenanceOperationRunCreateBulk +} + +// UpdateNewValues updates the mutable fields using the new values that +// were set on create. Using this option is equivalent to using: +// +// client.MaintenanceOperationRun.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(maintenanceoperationrun.FieldID) +// }), +// ). +// Exec(ctx) +func (u *MaintenanceOperationRunUpsertBulk) UpdateNewValues() *MaintenanceOperationRunUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + for _, b := range u.create.builders { + if _, exists := b.mutation.ID(); exists { + s.SetIgnore(maintenanceoperationrun.FieldID) + } + if _, exists := b.mutation.StartedAt(); exists { + s.SetIgnore(maintenanceoperationrun.FieldStartedAt) + } + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.MaintenanceOperationRun.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *MaintenanceOperationRunUpsertBulk) Ignore() *MaintenanceOperationRunUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *MaintenanceOperationRunUpsertBulk) DoNothing() *MaintenanceOperationRunUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the MaintenanceOperationRunCreateBulk.OnConflict +// documentation for more info. +func (u *MaintenanceOperationRunUpsertBulk) Update(set func(*MaintenanceOperationRunUpsert)) *MaintenanceOperationRunUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&MaintenanceOperationRunUpsert{UpdateSet: update}) + })) + return u +} + +// SetOperationKey sets the "operation_key" field. +func (u *MaintenanceOperationRunUpsertBulk) SetOperationKey(v string) *MaintenanceOperationRunUpsertBulk { + return u.Update(func(s *MaintenanceOperationRunUpsert) { + s.SetOperationKey(v) + }) +} + +// UpdateOperationKey sets the "operation_key" field to the value that was provided on create. +func (u *MaintenanceOperationRunUpsertBulk) UpdateOperationKey() *MaintenanceOperationRunUpsertBulk { + return u.Update(func(s *MaintenanceOperationRunUpsert) { + s.UpdateOperationKey() + }) +} + +// SetStatus sets the "status" field. +func (u *MaintenanceOperationRunUpsertBulk) SetStatus(v string) *MaintenanceOperationRunUpsertBulk { + return u.Update(func(s *MaintenanceOperationRunUpsert) { + s.SetStatus(v) + }) +} + +// UpdateStatus sets the "status" field to the value that was provided on create. +func (u *MaintenanceOperationRunUpsertBulk) UpdateStatus() *MaintenanceOperationRunUpsertBulk { + return u.Update(func(s *MaintenanceOperationRunUpsert) { + s.UpdateStatus() + }) +} + +// SetCompletedAt sets the "completed_at" field. +func (u *MaintenanceOperationRunUpsertBulk) SetCompletedAt(v time.Time) *MaintenanceOperationRunUpsertBulk { + return u.Update(func(s *MaintenanceOperationRunUpsert) { + s.SetCompletedAt(v) + }) +} + +// UpdateCompletedAt sets the "completed_at" field to the value that was provided on create. +func (u *MaintenanceOperationRunUpsertBulk) UpdateCompletedAt() *MaintenanceOperationRunUpsertBulk { + return u.Update(func(s *MaintenanceOperationRunUpsert) { + s.UpdateCompletedAt() + }) +} + +// ClearCompletedAt clears the value of the "completed_at" field. +func (u *MaintenanceOperationRunUpsertBulk) ClearCompletedAt() *MaintenanceOperationRunUpsertBulk { + return u.Update(func(s *MaintenanceOperationRunUpsert) { + s.ClearCompletedAt() + }) +} + +// SetStartedBy sets the "started_by" field. +func (u *MaintenanceOperationRunUpsertBulk) SetStartedBy(v string) *MaintenanceOperationRunUpsertBulk { + return u.Update(func(s *MaintenanceOperationRunUpsert) { + s.SetStartedBy(v) + }) +} + +// UpdateStartedBy sets the "started_by" field to the value that was provided on create. +func (u *MaintenanceOperationRunUpsertBulk) UpdateStartedBy() *MaintenanceOperationRunUpsertBulk { + return u.Update(func(s *MaintenanceOperationRunUpsert) { + s.UpdateStartedBy() + }) +} + +// ClearStartedBy clears the value of the "started_by" field. +func (u *MaintenanceOperationRunUpsertBulk) ClearStartedBy() *MaintenanceOperationRunUpsertBulk { + return u.Update(func(s *MaintenanceOperationRunUpsert) { + s.ClearStartedBy() + }) +} + +// SetResult sets the "result" field. +func (u *MaintenanceOperationRunUpsertBulk) SetResult(v string) *MaintenanceOperationRunUpsertBulk { + return u.Update(func(s *MaintenanceOperationRunUpsert) { + s.SetResult(v) + }) +} + +// UpdateResult sets the "result" field to the value that was provided on create. +func (u *MaintenanceOperationRunUpsertBulk) UpdateResult() *MaintenanceOperationRunUpsertBulk { + return u.Update(func(s *MaintenanceOperationRunUpsert) { + s.UpdateResult() + }) +} + +// ClearResult clears the value of the "result" field. +func (u *MaintenanceOperationRunUpsertBulk) ClearResult() *MaintenanceOperationRunUpsertBulk { + return u.Update(func(s *MaintenanceOperationRunUpsert) { + s.ClearResult() + }) +} + +// SetLog sets the "log" field. +func (u *MaintenanceOperationRunUpsertBulk) SetLog(v string) *MaintenanceOperationRunUpsertBulk { + return u.Update(func(s *MaintenanceOperationRunUpsert) { + s.SetLog(v) + }) +} + +// UpdateLog sets the "log" field to the value that was provided on create. +func (u *MaintenanceOperationRunUpsertBulk) UpdateLog() *MaintenanceOperationRunUpsertBulk { + return u.Update(func(s *MaintenanceOperationRunUpsert) { + s.UpdateLog() + }) +} + +// Exec executes the query. +func (u *MaintenanceOperationRunUpsertBulk) Exec(ctx context.Context) error { + if u.create.err != nil { + return u.create.err + } + for i, b := range u.create.builders { + if len(b.conflict) != 0 { + return fmt.Errorf("ent: OnConflict was set for builder %d. Set it on the MaintenanceOperationRunCreateBulk instead", i) + } + } + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for MaintenanceOperationRunCreateBulk.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *MaintenanceOperationRunUpsertBulk) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/pkg/ent/maintenanceoperationrun_delete.go b/pkg/ent/maintenanceoperationrun_delete.go new file mode 100644 index 000000000..52e40a36f --- /dev/null +++ b/pkg/ent/maintenanceoperationrun_delete.go @@ -0,0 +1,88 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/maintenanceoperationrun" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" +) + +// MaintenanceOperationRunDelete is the builder for deleting a MaintenanceOperationRun entity. +type MaintenanceOperationRunDelete struct { + config + hooks []Hook + mutation *MaintenanceOperationRunMutation +} + +// Where appends a list predicates to the MaintenanceOperationRunDelete builder. +func (_d *MaintenanceOperationRunDelete) Where(ps ...predicate.MaintenanceOperationRun) *MaintenanceOperationRunDelete { + _d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (_d *MaintenanceOperationRunDelete) Exec(ctx context.Context) (int, error) { + return withHooks(ctx, _d.sqlExec, _d.mutation, _d.hooks) +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *MaintenanceOperationRunDelete) ExecX(ctx context.Context) int { + n, err := _d.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (_d *MaintenanceOperationRunDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(maintenanceoperationrun.Table, sqlgraph.NewFieldSpec(maintenanceoperationrun.FieldID, field.TypeUUID)) + if ps := _d.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + affected, err := sqlgraph.DeleteNodes(ctx, _d.driver, _spec) + if err != nil && sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + _d.mutation.done = true + return affected, err +} + +// MaintenanceOperationRunDeleteOne is the builder for deleting a single MaintenanceOperationRun entity. +type MaintenanceOperationRunDeleteOne struct { + _d *MaintenanceOperationRunDelete +} + +// Where appends a list predicates to the MaintenanceOperationRunDelete builder. +func (_d *MaintenanceOperationRunDeleteOne) Where(ps ...predicate.MaintenanceOperationRun) *MaintenanceOperationRunDeleteOne { + _d._d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query. +func (_d *MaintenanceOperationRunDeleteOne) Exec(ctx context.Context) error { + n, err := _d._d.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{maintenanceoperationrun.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *MaintenanceOperationRunDeleteOne) ExecX(ctx context.Context) { + if err := _d.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/pkg/ent/maintenanceoperationrun_query.go b/pkg/ent/maintenanceoperationrun_query.go new file mode 100644 index 000000000..1b3f9450e --- /dev/null +++ b/pkg/ent/maintenanceoperationrun_query.go @@ -0,0 +1,565 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "fmt" + "math" + + "entgo.io/ent" + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/maintenanceoperationrun" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/google/uuid" +) + +// MaintenanceOperationRunQuery is the builder for querying MaintenanceOperationRun entities. +type MaintenanceOperationRunQuery struct { + config + ctx *QueryContext + order []maintenanceoperationrun.OrderOption + inters []Interceptor + predicates []predicate.MaintenanceOperationRun + modifiers []func(*sql.Selector) + // intermediate query (i.e. traversal path). + sql *sql.Selector + path func(context.Context) (*sql.Selector, error) +} + +// Where adds a new predicate for the MaintenanceOperationRunQuery builder. +func (_q *MaintenanceOperationRunQuery) Where(ps ...predicate.MaintenanceOperationRun) *MaintenanceOperationRunQuery { + _q.predicates = append(_q.predicates, ps...) + return _q +} + +// Limit the number of records to be returned by this query. +func (_q *MaintenanceOperationRunQuery) Limit(limit int) *MaintenanceOperationRunQuery { + _q.ctx.Limit = &limit + return _q +} + +// Offset to start from. +func (_q *MaintenanceOperationRunQuery) Offset(offset int) *MaintenanceOperationRunQuery { + _q.ctx.Offset = &offset + return _q +} + +// Unique configures the query builder to filter duplicate records on query. +// By default, unique is set to true, and can be disabled using this method. +func (_q *MaintenanceOperationRunQuery) Unique(unique bool) *MaintenanceOperationRunQuery { + _q.ctx.Unique = &unique + return _q +} + +// Order specifies how the records should be ordered. +func (_q *MaintenanceOperationRunQuery) Order(o ...maintenanceoperationrun.OrderOption) *MaintenanceOperationRunQuery { + _q.order = append(_q.order, o...) + return _q +} + +// First returns the first MaintenanceOperationRun entity from the query. +// Returns a *NotFoundError when no MaintenanceOperationRun was found. +func (_q *MaintenanceOperationRunQuery) First(ctx context.Context) (*MaintenanceOperationRun, error) { + nodes, err := _q.Limit(1).All(setContextOp(ctx, _q.ctx, ent.OpQueryFirst)) + if err != nil { + return nil, err + } + if len(nodes) == 0 { + return nil, &NotFoundError{maintenanceoperationrun.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (_q *MaintenanceOperationRunQuery) FirstX(ctx context.Context) *MaintenanceOperationRun { + node, err := _q.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// FirstID returns the first MaintenanceOperationRun ID from the query. +// Returns a *NotFoundError when no MaintenanceOperationRun ID was found. +func (_q *MaintenanceOperationRunQuery) FirstID(ctx context.Context) (id uuid.UUID, err error) { + var ids []uuid.UUID + if ids, err = _q.Limit(1).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryFirstID)); err != nil { + return + } + if len(ids) == 0 { + err = &NotFoundError{maintenanceoperationrun.Label} + return + } + return ids[0], nil +} + +// FirstIDX is like FirstID, but panics if an error occurs. +func (_q *MaintenanceOperationRunQuery) FirstIDX(ctx context.Context) uuid.UUID { + id, err := _q.FirstID(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return id +} + +// Only returns a single MaintenanceOperationRun entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one MaintenanceOperationRun entity is found. +// Returns a *NotFoundError when no MaintenanceOperationRun entities are found. +func (_q *MaintenanceOperationRunQuery) Only(ctx context.Context) (*MaintenanceOperationRun, error) { + nodes, err := _q.Limit(2).All(setContextOp(ctx, _q.ctx, ent.OpQueryOnly)) + if err != nil { + return nil, err + } + switch len(nodes) { + case 1: + return nodes[0], nil + case 0: + return nil, &NotFoundError{maintenanceoperationrun.Label} + default: + return nil, &NotSingularError{maintenanceoperationrun.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (_q *MaintenanceOperationRunQuery) OnlyX(ctx context.Context) *MaintenanceOperationRun { + node, err := _q.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// OnlyID is like Only, but returns the only MaintenanceOperationRun ID in the query. +// Returns a *NotSingularError when more than one MaintenanceOperationRun ID is found. +// Returns a *NotFoundError when no entities are found. +func (_q *MaintenanceOperationRunQuery) OnlyID(ctx context.Context) (id uuid.UUID, err error) { + var ids []uuid.UUID + if ids, err = _q.Limit(2).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryOnlyID)); err != nil { + return + } + switch len(ids) { + case 1: + id = ids[0] + case 0: + err = &NotFoundError{maintenanceoperationrun.Label} + default: + err = &NotSingularError{maintenanceoperationrun.Label} + } + return +} + +// OnlyIDX is like OnlyID, but panics if an error occurs. +func (_q *MaintenanceOperationRunQuery) OnlyIDX(ctx context.Context) uuid.UUID { + id, err := _q.OnlyID(ctx) + if err != nil { + panic(err) + } + return id +} + +// All executes the query and returns a list of MaintenanceOperationRuns. +func (_q *MaintenanceOperationRunQuery) All(ctx context.Context) ([]*MaintenanceOperationRun, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryAll) + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*MaintenanceOperationRun, *MaintenanceOperationRunQuery]() + return withInterceptors[[]*MaintenanceOperationRun](ctx, _q, qr, _q.inters) +} + +// AllX is like All, but panics if an error occurs. +func (_q *MaintenanceOperationRunQuery) AllX(ctx context.Context) []*MaintenanceOperationRun { + nodes, err := _q.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// IDs executes the query and returns a list of MaintenanceOperationRun IDs. +func (_q *MaintenanceOperationRunQuery) IDs(ctx context.Context) (ids []uuid.UUID, err error) { + if _q.ctx.Unique == nil && _q.path != nil { + _q.Unique(true) + } + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryIDs) + if err = _q.Select(maintenanceoperationrun.FieldID).Scan(ctx, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// IDsX is like IDs, but panics if an error occurs. +func (_q *MaintenanceOperationRunQuery) IDsX(ctx context.Context) []uuid.UUID { + ids, err := _q.IDs(ctx) + if err != nil { + panic(err) + } + return ids +} + +// Count returns the count of the given query. +func (_q *MaintenanceOperationRunQuery) Count(ctx context.Context) (int, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryCount) + if err := _q.prepareQuery(ctx); err != nil { + return 0, err + } + return withInterceptors[int](ctx, _q, querierCount[*MaintenanceOperationRunQuery](), _q.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (_q *MaintenanceOperationRunQuery) CountX(ctx context.Context) int { + count, err := _q.Count(ctx) + if err != nil { + panic(err) + } + return count +} + +// Exist returns true if the query has elements in the graph. +func (_q *MaintenanceOperationRunQuery) Exist(ctx context.Context) (bool, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryExist) + switch _, err := _q.FirstID(ctx); { + case IsNotFound(err): + return false, nil + case err != nil: + return false, fmt.Errorf("ent: check existence: %w", err) + default: + return true, nil + } +} + +// ExistX is like Exist, but panics if an error occurs. +func (_q *MaintenanceOperationRunQuery) ExistX(ctx context.Context) bool { + exist, err := _q.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the MaintenanceOperationRunQuery builder, including all associated steps. It can be +// used to prepare common query builders and use them differently after the clone is made. +func (_q *MaintenanceOperationRunQuery) Clone() *MaintenanceOperationRunQuery { + if _q == nil { + return nil + } + return &MaintenanceOperationRunQuery{ + config: _q.config, + ctx: _q.ctx.Clone(), + order: append([]maintenanceoperationrun.OrderOption{}, _q.order...), + inters: append([]Interceptor{}, _q.inters...), + predicates: append([]predicate.MaintenanceOperationRun{}, _q.predicates...), + // clone intermediate query. + sql: _q.sql.Clone(), + path: _q.path, + } +} + +// GroupBy is used to group vertices by one or more fields/columns. +// It is often used with aggregate functions, like: count, max, mean, min, sum. +// +// Example: +// +// var v []struct { +// OperationKey string `json:"operation_key,omitempty"` +// Count int `json:"count,omitempty"` +// } +// +// client.MaintenanceOperationRun.Query(). +// GroupBy(maintenanceoperationrun.FieldOperationKey). +// Aggregate(ent.Count()). +// Scan(ctx, &v) +func (_q *MaintenanceOperationRunQuery) GroupBy(field string, fields ...string) *MaintenanceOperationRunGroupBy { + _q.ctx.Fields = append([]string{field}, fields...) + grbuild := &MaintenanceOperationRunGroupBy{build: _q} + grbuild.flds = &_q.ctx.Fields + grbuild.label = maintenanceoperationrun.Label + grbuild.scan = grbuild.Scan + return grbuild +} + +// Select allows the selection one or more fields/columns for the given query, +// instead of selecting all fields in the entity. +// +// Example: +// +// var v []struct { +// OperationKey string `json:"operation_key,omitempty"` +// } +// +// client.MaintenanceOperationRun.Query(). +// Select(maintenanceoperationrun.FieldOperationKey). +// Scan(ctx, &v) +func (_q *MaintenanceOperationRunQuery) Select(fields ...string) *MaintenanceOperationRunSelect { + _q.ctx.Fields = append(_q.ctx.Fields, fields...) + sbuild := &MaintenanceOperationRunSelect{MaintenanceOperationRunQuery: _q} + sbuild.label = maintenanceoperationrun.Label + sbuild.flds, sbuild.scan = &_q.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a MaintenanceOperationRunSelect configured with the given aggregations. +func (_q *MaintenanceOperationRunQuery) Aggregate(fns ...AggregateFunc) *MaintenanceOperationRunSelect { + return _q.Select().Aggregate(fns...) +} + +func (_q *MaintenanceOperationRunQuery) prepareQuery(ctx context.Context) error { + for _, inter := range _q.inters { + if inter == nil { + return fmt.Errorf("ent: uninitialized interceptor (forgotten import ent/runtime?)") + } + if trv, ok := inter.(Traverser); ok { + if err := trv.Traverse(ctx, _q); err != nil { + return err + } + } + } + for _, f := range _q.ctx.Fields { + if !maintenanceoperationrun.ValidColumn(f) { + return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + } + if _q.path != nil { + prev, err := _q.path(ctx) + if err != nil { + return err + } + _q.sql = prev + } + return nil +} + +func (_q *MaintenanceOperationRunQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*MaintenanceOperationRun, error) { + var ( + nodes = []*MaintenanceOperationRun{} + _spec = _q.querySpec() + ) + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*MaintenanceOperationRun).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &MaintenanceOperationRun{config: _q.config} + nodes = append(nodes, node) + return node.assignValues(columns, values) + } + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } + for i := range hooks { + hooks[i](ctx, _spec) + } + if err := sqlgraph.QueryNodes(ctx, _q.driver, _spec); err != nil { + return nil, err + } + if len(nodes) == 0 { + return nodes, nil + } + return nodes, nil +} + +func (_q *MaintenanceOperationRunQuery) sqlCount(ctx context.Context) (int, error) { + _spec := _q.querySpec() + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } + _spec.Node.Columns = _q.ctx.Fields + if len(_q.ctx.Fields) > 0 { + _spec.Unique = _q.ctx.Unique != nil && *_q.ctx.Unique + } + return sqlgraph.CountNodes(ctx, _q.driver, _spec) +} + +func (_q *MaintenanceOperationRunQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(maintenanceoperationrun.Table, maintenanceoperationrun.Columns, sqlgraph.NewFieldSpec(maintenanceoperationrun.FieldID, field.TypeUUID)) + _spec.From = _q.sql + if unique := _q.ctx.Unique; unique != nil { + _spec.Unique = *unique + } else if _q.path != nil { + _spec.Unique = true + } + if fields := _q.ctx.Fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, maintenanceoperationrun.FieldID) + for i := range fields { + if fields[i] != maintenanceoperationrun.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, fields[i]) + } + } + } + if ps := _q.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if limit := _q.ctx.Limit; limit != nil { + _spec.Limit = *limit + } + if offset := _q.ctx.Offset; offset != nil { + _spec.Offset = *offset + } + if ps := _q.order; len(ps) > 0 { + _spec.Order = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + return _spec +} + +func (_q *MaintenanceOperationRunQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(_q.driver.Dialect()) + t1 := builder.Table(maintenanceoperationrun.Table) + columns := _q.ctx.Fields + if len(columns) == 0 { + columns = maintenanceoperationrun.Columns + } + selector := builder.Select(t1.Columns(columns...)...).From(t1) + if _q.sql != nil { + selector = _q.sql + selector.Select(selector.Columns(columns...)...) + } + if _q.ctx.Unique != nil && *_q.ctx.Unique { + selector.Distinct() + } + for _, m := range _q.modifiers { + m(selector) + } + for _, p := range _q.predicates { + p(selector) + } + for _, p := range _q.order { + p(selector) + } + if offset := _q.ctx.Offset; offset != nil { + // limit is mandatory for offset clause. We start + // with default value, and override it below if needed. + selector.Offset(*offset).Limit(math.MaxInt32) + } + if limit := _q.ctx.Limit; limit != nil { + selector.Limit(*limit) + } + return selector +} + +// ForUpdate locks the selected rows against concurrent updates, and prevent them from being +// updated, deleted or "selected ... for update" by other sessions, until the transaction is +// either committed or rolled-back. +func (_q *MaintenanceOperationRunQuery) ForUpdate(opts ...sql.LockOption) *MaintenanceOperationRunQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForUpdate(opts...) + }) + return _q +} + +// ForShare behaves similarly to ForUpdate, except that it acquires a shared mode lock +// on any rows that are read. Other sessions can read the rows, but cannot modify them +// until your transaction commits. +func (_q *MaintenanceOperationRunQuery) ForShare(opts ...sql.LockOption) *MaintenanceOperationRunQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForShare(opts...) + }) + return _q +} + +// MaintenanceOperationRunGroupBy is the group-by builder for MaintenanceOperationRun entities. +type MaintenanceOperationRunGroupBy struct { + selector + build *MaintenanceOperationRunQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (_g *MaintenanceOperationRunGroupBy) Aggregate(fns ...AggregateFunc) *MaintenanceOperationRunGroupBy { + _g.fns = append(_g.fns, fns...) + return _g +} + +// Scan applies the selector query and scans the result into the given value. +func (_g *MaintenanceOperationRunGroupBy) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, _g.build.ctx, ent.OpQueryGroupBy) + if err := _g.build.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*MaintenanceOperationRunQuery, *MaintenanceOperationRunGroupBy](ctx, _g.build, _g, _g.build.inters, v) +} + +func (_g *MaintenanceOperationRunGroupBy) sqlScan(ctx context.Context, root *MaintenanceOperationRunQuery, v any) error { + selector := root.sqlQuery(ctx).Select() + aggregation := make([]string, 0, len(_g.fns)) + for _, fn := range _g.fns { + aggregation = append(aggregation, fn(selector)) + } + if len(selector.SelectedColumns()) == 0 { + columns := make([]string, 0, len(*_g.flds)+len(_g.fns)) + for _, f := range *_g.flds { + columns = append(columns, selector.C(f)) + } + columns = append(columns, aggregation...) + selector.Select(columns...) + } + selector.GroupBy(selector.Columns(*_g.flds...)...) + if err := selector.Err(); err != nil { + return err + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := _g.build.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} + +// MaintenanceOperationRunSelect is the builder for selecting fields of MaintenanceOperationRun entities. +type MaintenanceOperationRunSelect struct { + *MaintenanceOperationRunQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (_s *MaintenanceOperationRunSelect) Aggregate(fns ...AggregateFunc) *MaintenanceOperationRunSelect { + _s.fns = append(_s.fns, fns...) + return _s +} + +// Scan applies the selector query and scans the result into the given value. +func (_s *MaintenanceOperationRunSelect) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, _s.ctx, ent.OpQuerySelect) + if err := _s.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*MaintenanceOperationRunQuery, *MaintenanceOperationRunSelect](ctx, _s.MaintenanceOperationRunQuery, _s, _s.inters, v) +} + +func (_s *MaintenanceOperationRunSelect) sqlScan(ctx context.Context, root *MaintenanceOperationRunQuery, v any) error { + selector := root.sqlQuery(ctx) + aggregation := make([]string, 0, len(_s.fns)) + for _, fn := range _s.fns { + aggregation = append(aggregation, fn(selector)) + } + switch n := len(*_s.selector.flds); { + case n == 0 && len(aggregation) > 0: + selector.Select(aggregation...) + case n != 0 && len(aggregation) > 0: + selector.AppendSelect(aggregation...) + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := _s.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} diff --git a/pkg/ent/maintenanceoperationrun_update.go b/pkg/ent/maintenanceoperationrun_update.go new file mode 100644 index 000000000..aaf8065f7 --- /dev/null +++ b/pkg/ent/maintenanceoperationrun_update.go @@ -0,0 +1,460 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/maintenanceoperationrun" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" +) + +// MaintenanceOperationRunUpdate is the builder for updating MaintenanceOperationRun entities. +type MaintenanceOperationRunUpdate struct { + config + hooks []Hook + mutation *MaintenanceOperationRunMutation +} + +// Where appends a list predicates to the MaintenanceOperationRunUpdate builder. +func (_u *MaintenanceOperationRunUpdate) Where(ps ...predicate.MaintenanceOperationRun) *MaintenanceOperationRunUpdate { + _u.mutation.Where(ps...) + return _u +} + +// SetOperationKey sets the "operation_key" field. +func (_u *MaintenanceOperationRunUpdate) SetOperationKey(v string) *MaintenanceOperationRunUpdate { + _u.mutation.SetOperationKey(v) + return _u +} + +// SetNillableOperationKey sets the "operation_key" field if the given value is not nil. +func (_u *MaintenanceOperationRunUpdate) SetNillableOperationKey(v *string) *MaintenanceOperationRunUpdate { + if v != nil { + _u.SetOperationKey(*v) + } + return _u +} + +// SetStatus sets the "status" field. +func (_u *MaintenanceOperationRunUpdate) SetStatus(v string) *MaintenanceOperationRunUpdate { + _u.mutation.SetStatus(v) + return _u +} + +// SetNillableStatus sets the "status" field if the given value is not nil. +func (_u *MaintenanceOperationRunUpdate) SetNillableStatus(v *string) *MaintenanceOperationRunUpdate { + if v != nil { + _u.SetStatus(*v) + } + return _u +} + +// SetCompletedAt sets the "completed_at" field. +func (_u *MaintenanceOperationRunUpdate) SetCompletedAt(v time.Time) *MaintenanceOperationRunUpdate { + _u.mutation.SetCompletedAt(v) + return _u +} + +// SetNillableCompletedAt sets the "completed_at" field if the given value is not nil. +func (_u *MaintenanceOperationRunUpdate) SetNillableCompletedAt(v *time.Time) *MaintenanceOperationRunUpdate { + if v != nil { + _u.SetCompletedAt(*v) + } + return _u +} + +// ClearCompletedAt clears the value of the "completed_at" field. +func (_u *MaintenanceOperationRunUpdate) ClearCompletedAt() *MaintenanceOperationRunUpdate { + _u.mutation.ClearCompletedAt() + return _u +} + +// SetStartedBy sets the "started_by" field. +func (_u *MaintenanceOperationRunUpdate) SetStartedBy(v string) *MaintenanceOperationRunUpdate { + _u.mutation.SetStartedBy(v) + return _u +} + +// SetNillableStartedBy sets the "started_by" field if the given value is not nil. +func (_u *MaintenanceOperationRunUpdate) SetNillableStartedBy(v *string) *MaintenanceOperationRunUpdate { + if v != nil { + _u.SetStartedBy(*v) + } + return _u +} + +// ClearStartedBy clears the value of the "started_by" field. +func (_u *MaintenanceOperationRunUpdate) ClearStartedBy() *MaintenanceOperationRunUpdate { + _u.mutation.ClearStartedBy() + return _u +} + +// SetResult sets the "result" field. +func (_u *MaintenanceOperationRunUpdate) SetResult(v string) *MaintenanceOperationRunUpdate { + _u.mutation.SetResult(v) + return _u +} + +// SetNillableResult sets the "result" field if the given value is not nil. +func (_u *MaintenanceOperationRunUpdate) SetNillableResult(v *string) *MaintenanceOperationRunUpdate { + if v != nil { + _u.SetResult(*v) + } + return _u +} + +// ClearResult clears the value of the "result" field. +func (_u *MaintenanceOperationRunUpdate) ClearResult() *MaintenanceOperationRunUpdate { + _u.mutation.ClearResult() + return _u +} + +// SetLog sets the "log" field. +func (_u *MaintenanceOperationRunUpdate) SetLog(v string) *MaintenanceOperationRunUpdate { + _u.mutation.SetLog(v) + return _u +} + +// SetNillableLog sets the "log" field if the given value is not nil. +func (_u *MaintenanceOperationRunUpdate) SetNillableLog(v *string) *MaintenanceOperationRunUpdate { + if v != nil { + _u.SetLog(*v) + } + return _u +} + +// Mutation returns the MaintenanceOperationRunMutation object of the builder. +func (_u *MaintenanceOperationRunUpdate) Mutation() *MaintenanceOperationRunMutation { + return _u.mutation +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (_u *MaintenanceOperationRunUpdate) Save(ctx context.Context) (int, error) { + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *MaintenanceOperationRunUpdate) SaveX(ctx context.Context) int { + affected, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (_u *MaintenanceOperationRunUpdate) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *MaintenanceOperationRunUpdate) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_u *MaintenanceOperationRunUpdate) check() error { + if v, ok := _u.mutation.OperationKey(); ok { + if err := maintenanceoperationrun.OperationKeyValidator(v); err != nil { + return &ValidationError{Name: "operation_key", err: fmt.Errorf(`ent: validator failed for field "MaintenanceOperationRun.operation_key": %w`, err)} + } + } + return nil +} + +func (_u *MaintenanceOperationRunUpdate) sqlSave(ctx context.Context) (_node int, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(maintenanceoperationrun.Table, maintenanceoperationrun.Columns, sqlgraph.NewFieldSpec(maintenanceoperationrun.FieldID, field.TypeUUID)) + if ps := _u.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := _u.mutation.OperationKey(); ok { + _spec.SetField(maintenanceoperationrun.FieldOperationKey, field.TypeString, value) + } + if value, ok := _u.mutation.Status(); ok { + _spec.SetField(maintenanceoperationrun.FieldStatus, field.TypeString, value) + } + if value, ok := _u.mutation.CompletedAt(); ok { + _spec.SetField(maintenanceoperationrun.FieldCompletedAt, field.TypeTime, value) + } + if _u.mutation.CompletedAtCleared() { + _spec.ClearField(maintenanceoperationrun.FieldCompletedAt, field.TypeTime) + } + if value, ok := _u.mutation.StartedBy(); ok { + _spec.SetField(maintenanceoperationrun.FieldStartedBy, field.TypeString, value) + } + if _u.mutation.StartedByCleared() { + _spec.ClearField(maintenanceoperationrun.FieldStartedBy, field.TypeString) + } + if value, ok := _u.mutation.Result(); ok { + _spec.SetField(maintenanceoperationrun.FieldResult, field.TypeString, value) + } + if _u.mutation.ResultCleared() { + _spec.ClearField(maintenanceoperationrun.FieldResult, field.TypeString) + } + if value, ok := _u.mutation.Log(); ok { + _spec.SetField(maintenanceoperationrun.FieldLog, field.TypeString, value) + } + if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{maintenanceoperationrun.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + _u.mutation.done = true + return _node, nil +} + +// MaintenanceOperationRunUpdateOne is the builder for updating a single MaintenanceOperationRun entity. +type MaintenanceOperationRunUpdateOne struct { + config + fields []string + hooks []Hook + mutation *MaintenanceOperationRunMutation +} + +// SetOperationKey sets the "operation_key" field. +func (_u *MaintenanceOperationRunUpdateOne) SetOperationKey(v string) *MaintenanceOperationRunUpdateOne { + _u.mutation.SetOperationKey(v) + return _u +} + +// SetNillableOperationKey sets the "operation_key" field if the given value is not nil. +func (_u *MaintenanceOperationRunUpdateOne) SetNillableOperationKey(v *string) *MaintenanceOperationRunUpdateOne { + if v != nil { + _u.SetOperationKey(*v) + } + return _u +} + +// SetStatus sets the "status" field. +func (_u *MaintenanceOperationRunUpdateOne) SetStatus(v string) *MaintenanceOperationRunUpdateOne { + _u.mutation.SetStatus(v) + return _u +} + +// SetNillableStatus sets the "status" field if the given value is not nil. +func (_u *MaintenanceOperationRunUpdateOne) SetNillableStatus(v *string) *MaintenanceOperationRunUpdateOne { + if v != nil { + _u.SetStatus(*v) + } + return _u +} + +// SetCompletedAt sets the "completed_at" field. +func (_u *MaintenanceOperationRunUpdateOne) SetCompletedAt(v time.Time) *MaintenanceOperationRunUpdateOne { + _u.mutation.SetCompletedAt(v) + return _u +} + +// SetNillableCompletedAt sets the "completed_at" field if the given value is not nil. +func (_u *MaintenanceOperationRunUpdateOne) SetNillableCompletedAt(v *time.Time) *MaintenanceOperationRunUpdateOne { + if v != nil { + _u.SetCompletedAt(*v) + } + return _u +} + +// ClearCompletedAt clears the value of the "completed_at" field. +func (_u *MaintenanceOperationRunUpdateOne) ClearCompletedAt() *MaintenanceOperationRunUpdateOne { + _u.mutation.ClearCompletedAt() + return _u +} + +// SetStartedBy sets the "started_by" field. +func (_u *MaintenanceOperationRunUpdateOne) SetStartedBy(v string) *MaintenanceOperationRunUpdateOne { + _u.mutation.SetStartedBy(v) + return _u +} + +// SetNillableStartedBy sets the "started_by" field if the given value is not nil. +func (_u *MaintenanceOperationRunUpdateOne) SetNillableStartedBy(v *string) *MaintenanceOperationRunUpdateOne { + if v != nil { + _u.SetStartedBy(*v) + } + return _u +} + +// ClearStartedBy clears the value of the "started_by" field. +func (_u *MaintenanceOperationRunUpdateOne) ClearStartedBy() *MaintenanceOperationRunUpdateOne { + _u.mutation.ClearStartedBy() + return _u +} + +// SetResult sets the "result" field. +func (_u *MaintenanceOperationRunUpdateOne) SetResult(v string) *MaintenanceOperationRunUpdateOne { + _u.mutation.SetResult(v) + return _u +} + +// SetNillableResult sets the "result" field if the given value is not nil. +func (_u *MaintenanceOperationRunUpdateOne) SetNillableResult(v *string) *MaintenanceOperationRunUpdateOne { + if v != nil { + _u.SetResult(*v) + } + return _u +} + +// ClearResult clears the value of the "result" field. +func (_u *MaintenanceOperationRunUpdateOne) ClearResult() *MaintenanceOperationRunUpdateOne { + _u.mutation.ClearResult() + return _u +} + +// SetLog sets the "log" field. +func (_u *MaintenanceOperationRunUpdateOne) SetLog(v string) *MaintenanceOperationRunUpdateOne { + _u.mutation.SetLog(v) + return _u +} + +// SetNillableLog sets the "log" field if the given value is not nil. +func (_u *MaintenanceOperationRunUpdateOne) SetNillableLog(v *string) *MaintenanceOperationRunUpdateOne { + if v != nil { + _u.SetLog(*v) + } + return _u +} + +// Mutation returns the MaintenanceOperationRunMutation object of the builder. +func (_u *MaintenanceOperationRunUpdateOne) Mutation() *MaintenanceOperationRunMutation { + return _u.mutation +} + +// Where appends a list predicates to the MaintenanceOperationRunUpdate builder. +func (_u *MaintenanceOperationRunUpdateOne) Where(ps ...predicate.MaintenanceOperationRun) *MaintenanceOperationRunUpdateOne { + _u.mutation.Where(ps...) + return _u +} + +// Select allows selecting one or more fields (columns) of the returned entity. +// The default is selecting all fields defined in the entity schema. +func (_u *MaintenanceOperationRunUpdateOne) Select(field string, fields ...string) *MaintenanceOperationRunUpdateOne { + _u.fields = append([]string{field}, fields...) + return _u +} + +// Save executes the query and returns the updated MaintenanceOperationRun entity. +func (_u *MaintenanceOperationRunUpdateOne) Save(ctx context.Context) (*MaintenanceOperationRun, error) { + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *MaintenanceOperationRunUpdateOne) SaveX(ctx context.Context) *MaintenanceOperationRun { + node, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (_u *MaintenanceOperationRunUpdateOne) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *MaintenanceOperationRunUpdateOne) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_u *MaintenanceOperationRunUpdateOne) check() error { + if v, ok := _u.mutation.OperationKey(); ok { + if err := maintenanceoperationrun.OperationKeyValidator(v); err != nil { + return &ValidationError{Name: "operation_key", err: fmt.Errorf(`ent: validator failed for field "MaintenanceOperationRun.operation_key": %w`, err)} + } + } + return nil +} + +func (_u *MaintenanceOperationRunUpdateOne) sqlSave(ctx context.Context) (_node *MaintenanceOperationRun, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(maintenanceoperationrun.Table, maintenanceoperationrun.Columns, sqlgraph.NewFieldSpec(maintenanceoperationrun.FieldID, field.TypeUUID)) + id, ok := _u.mutation.ID() + if !ok { + return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "MaintenanceOperationRun.id" for update`)} + } + _spec.Node.ID.Value = id + if fields := _u.fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, maintenanceoperationrun.FieldID) + for _, f := range fields { + if !maintenanceoperationrun.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + if f != maintenanceoperationrun.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, f) + } + } + } + if ps := _u.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := _u.mutation.OperationKey(); ok { + _spec.SetField(maintenanceoperationrun.FieldOperationKey, field.TypeString, value) + } + if value, ok := _u.mutation.Status(); ok { + _spec.SetField(maintenanceoperationrun.FieldStatus, field.TypeString, value) + } + if value, ok := _u.mutation.CompletedAt(); ok { + _spec.SetField(maintenanceoperationrun.FieldCompletedAt, field.TypeTime, value) + } + if _u.mutation.CompletedAtCleared() { + _spec.ClearField(maintenanceoperationrun.FieldCompletedAt, field.TypeTime) + } + if value, ok := _u.mutation.StartedBy(); ok { + _spec.SetField(maintenanceoperationrun.FieldStartedBy, field.TypeString, value) + } + if _u.mutation.StartedByCleared() { + _spec.ClearField(maintenanceoperationrun.FieldStartedBy, field.TypeString) + } + if value, ok := _u.mutation.Result(); ok { + _spec.SetField(maintenanceoperationrun.FieldResult, field.TypeString, value) + } + if _u.mutation.ResultCleared() { + _spec.ClearField(maintenanceoperationrun.FieldResult, field.TypeString) + } + if value, ok := _u.mutation.Log(); ok { + _spec.SetField(maintenanceoperationrun.FieldLog, field.TypeString, value) + } + _node = &MaintenanceOperationRun{config: _u.config} + _spec.Assign = _node.assignValues + _spec.ScanValues = _node.scanValues + if err = sqlgraph.UpdateNode(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{maintenanceoperationrun.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + _u.mutation.done = true + return _node, nil +} diff --git a/pkg/ent/message.go b/pkg/ent/message.go new file mode 100644 index 000000000..d4aaee729 --- /dev/null +++ b/pkg/ent/message.go @@ -0,0 +1,241 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "fmt" + "strings" + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "github.com/GoogleCloudPlatform/scion/pkg/ent/message" + "github.com/google/uuid" +) + +// Message is the model entity for the Message schema. +type Message struct { + config `json:"-"` + // ID of the ent. + ID uuid.UUID `json:"id,omitempty"` + // ProjectID holds the value of the "project_id" field. + ProjectID uuid.UUID `json:"project_id,omitempty"` + // Sender holds the value of the "sender" field. + Sender string `json:"sender,omitempty"` + // SenderID holds the value of the "sender_id" field. + SenderID string `json:"sender_id,omitempty"` + // Recipient holds the value of the "recipient" field. + Recipient string `json:"recipient,omitempty"` + // RecipientID holds the value of the "recipient_id" field. + RecipientID string `json:"recipient_id,omitempty"` + // Msg holds the value of the "msg" field. + Msg string `json:"msg,omitempty"` + // Type holds the value of the "type" field. + Type string `json:"type,omitempty"` + // Urgent holds the value of the "urgent" field. + Urgent bool `json:"urgent,omitempty"` + // Broadcasted holds the value of the "broadcasted" field. + Broadcasted bool `json:"broadcasted,omitempty"` + // Read holds the value of the "read" field. + Read bool `json:"read,omitempty"` + // AgentID holds the value of the "agent_id" field. + AgentID string `json:"agent_id,omitempty"` + // GroupID holds the value of the "group_id" field. + GroupID string `json:"group_id,omitempty"` + // Created holds the value of the "created" field. + Created time.Time `json:"created,omitempty"` + selectValues sql.SelectValues +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*Message) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case message.FieldUrgent, message.FieldBroadcasted, message.FieldRead: + values[i] = new(sql.NullBool) + case message.FieldSender, message.FieldSenderID, message.FieldRecipient, message.FieldRecipientID, message.FieldMsg, message.FieldType, message.FieldAgentID, message.FieldGroupID: + values[i] = new(sql.NullString) + case message.FieldCreated: + values[i] = new(sql.NullTime) + case message.FieldID, message.FieldProjectID: + values[i] = new(uuid.UUID) + default: + values[i] = new(sql.UnknownType) + } + } + return values, nil +} + +// assignValues assigns the values that were returned from sql.Rows (after scanning) +// to the Message fields. +func (_m *Message) assignValues(columns []string, values []any) error { + if m, n := len(values), len(columns); m < n { + return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) + } + for i := range columns { + switch columns[i] { + case message.FieldID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field id", values[i]) + } else if value != nil { + _m.ID = *value + } + case message.FieldProjectID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field project_id", values[i]) + } else if value != nil { + _m.ProjectID = *value + } + case message.FieldSender: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field sender", values[i]) + } else if value.Valid { + _m.Sender = value.String + } + case message.FieldSenderID: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field sender_id", values[i]) + } else if value.Valid { + _m.SenderID = value.String + } + case message.FieldRecipient: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field recipient", values[i]) + } else if value.Valid { + _m.Recipient = value.String + } + case message.FieldRecipientID: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field recipient_id", values[i]) + } else if value.Valid { + _m.RecipientID = value.String + } + case message.FieldMsg: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field msg", values[i]) + } else if value.Valid { + _m.Msg = value.String + } + case message.FieldType: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field type", values[i]) + } else if value.Valid { + _m.Type = value.String + } + case message.FieldUrgent: + if value, ok := values[i].(*sql.NullBool); !ok { + return fmt.Errorf("unexpected type %T for field urgent", values[i]) + } else if value.Valid { + _m.Urgent = value.Bool + } + case message.FieldBroadcasted: + if value, ok := values[i].(*sql.NullBool); !ok { + return fmt.Errorf("unexpected type %T for field broadcasted", values[i]) + } else if value.Valid { + _m.Broadcasted = value.Bool + } + case message.FieldRead: + if value, ok := values[i].(*sql.NullBool); !ok { + return fmt.Errorf("unexpected type %T for field read", values[i]) + } else if value.Valid { + _m.Read = value.Bool + } + case message.FieldAgentID: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field agent_id", values[i]) + } else if value.Valid { + _m.AgentID = value.String + } + case message.FieldGroupID: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field group_id", values[i]) + } else if value.Valid { + _m.GroupID = value.String + } + case message.FieldCreated: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field created", values[i]) + } else if value.Valid { + _m.Created = value.Time + } + default: + _m.selectValues.Set(columns[i], values[i]) + } + } + return nil +} + +// Value returns the ent.Value that was dynamically selected and assigned to the Message. +// This includes values selected through modifiers, order, etc. +func (_m *Message) Value(name string) (ent.Value, error) { + return _m.selectValues.Get(name) +} + +// Update returns a builder for updating this Message. +// Note that you need to call Message.Unwrap() before calling this method if this Message +// was returned from a transaction, and the transaction was committed or rolled back. +func (_m *Message) Update() *MessageUpdateOne { + return NewMessageClient(_m.config).UpdateOne(_m) +} + +// Unwrap unwraps the Message entity that was returned from a transaction after it was closed, +// so that all future queries will be executed through the driver which created the transaction. +func (_m *Message) Unwrap() *Message { + _tx, ok := _m.config.driver.(*txDriver) + if !ok { + panic("ent: Message is not a transactional entity") + } + _m.config.driver = _tx.drv + return _m +} + +// String implements the fmt.Stringer. +func (_m *Message) String() string { + var builder strings.Builder + builder.WriteString("Message(") + builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID)) + builder.WriteString("project_id=") + builder.WriteString(fmt.Sprintf("%v", _m.ProjectID)) + builder.WriteString(", ") + builder.WriteString("sender=") + builder.WriteString(_m.Sender) + builder.WriteString(", ") + builder.WriteString("sender_id=") + builder.WriteString(_m.SenderID) + builder.WriteString(", ") + builder.WriteString("recipient=") + builder.WriteString(_m.Recipient) + builder.WriteString(", ") + builder.WriteString("recipient_id=") + builder.WriteString(_m.RecipientID) + builder.WriteString(", ") + builder.WriteString("msg=") + builder.WriteString(_m.Msg) + builder.WriteString(", ") + builder.WriteString("type=") + builder.WriteString(_m.Type) + builder.WriteString(", ") + builder.WriteString("urgent=") + builder.WriteString(fmt.Sprintf("%v", _m.Urgent)) + builder.WriteString(", ") + builder.WriteString("broadcasted=") + builder.WriteString(fmt.Sprintf("%v", _m.Broadcasted)) + builder.WriteString(", ") + builder.WriteString("read=") + builder.WriteString(fmt.Sprintf("%v", _m.Read)) + builder.WriteString(", ") + builder.WriteString("agent_id=") + builder.WriteString(_m.AgentID) + builder.WriteString(", ") + builder.WriteString("group_id=") + builder.WriteString(_m.GroupID) + builder.WriteString(", ") + builder.WriteString("created=") + builder.WriteString(_m.Created.Format(time.ANSIC)) + builder.WriteByte(')') + return builder.String() +} + +// Messages is a parsable slice of Message. +type Messages []*Message diff --git a/pkg/ent/message/message.go b/pkg/ent/message/message.go new file mode 100644 index 000000000..13aa68ae6 --- /dev/null +++ b/pkg/ent/message/message.go @@ -0,0 +1,167 @@ +// Code generated by ent, DO NOT EDIT. + +package message + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "github.com/google/uuid" +) + +const ( + // Label holds the string label denoting the message type in the database. + Label = "message" + // FieldID holds the string denoting the id field in the database. + FieldID = "id" + // FieldProjectID holds the string denoting the project_id field in the database. + FieldProjectID = "project_id" + // FieldSender holds the string denoting the sender field in the database. + FieldSender = "sender" + // FieldSenderID holds the string denoting the sender_id field in the database. + FieldSenderID = "sender_id" + // FieldRecipient holds the string denoting the recipient field in the database. + FieldRecipient = "recipient" + // FieldRecipientID holds the string denoting the recipient_id field in the database. + FieldRecipientID = "recipient_id" + // FieldMsg holds the string denoting the msg field in the database. + FieldMsg = "msg" + // FieldType holds the string denoting the type field in the database. + FieldType = "type" + // FieldUrgent holds the string denoting the urgent field in the database. + FieldUrgent = "urgent" + // FieldBroadcasted holds the string denoting the broadcasted field in the database. + FieldBroadcasted = "broadcasted" + // FieldRead holds the string denoting the read field in the database. + FieldRead = "read" + // FieldAgentID holds the string denoting the agent_id field in the database. + FieldAgentID = "agent_id" + // FieldGroupID holds the string denoting the group_id field in the database. + FieldGroupID = "group_id" + // FieldCreated holds the string denoting the created field in the database. + FieldCreated = "created" + // Table holds the table name of the message in the database. + Table = "messages" +) + +// Columns holds all SQL columns for message fields. +var Columns = []string{ + FieldID, + FieldProjectID, + FieldSender, + FieldSenderID, + FieldRecipient, + FieldRecipientID, + FieldMsg, + FieldType, + FieldUrgent, + FieldBroadcasted, + FieldRead, + FieldAgentID, + FieldGroupID, + FieldCreated, +} + +// ValidColumn reports if the column name is valid (part of the table columns). +func ValidColumn(column string) bool { + for i := range Columns { + if column == Columns[i] { + return true + } + } + return false +} + +var ( + // SenderValidator is a validator for the "sender" field. It is called by the builders before save. + SenderValidator func(string) error + // RecipientValidator is a validator for the "recipient" field. It is called by the builders before save. + RecipientValidator func(string) error + // MsgValidator is a validator for the "msg" field. It is called by the builders before save. + MsgValidator func(string) error + // DefaultType holds the default value on creation for the "type" field. + DefaultType string + // DefaultUrgent holds the default value on creation for the "urgent" field. + DefaultUrgent bool + // DefaultBroadcasted holds the default value on creation for the "broadcasted" field. + DefaultBroadcasted bool + // DefaultRead holds the default value on creation for the "read" field. + DefaultRead bool + // DefaultCreated holds the default value on creation for the "created" field. + DefaultCreated func() time.Time + // DefaultID holds the default value on creation for the "id" field. + DefaultID func() uuid.UUID +) + +// OrderOption defines the ordering options for the Message queries. +type OrderOption func(*sql.Selector) + +// ByID orders the results by the id field. +func ByID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldID, opts...).ToFunc() +} + +// ByProjectID orders the results by the project_id field. +func ByProjectID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldProjectID, opts...).ToFunc() +} + +// BySender orders the results by the sender field. +func BySender(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldSender, opts...).ToFunc() +} + +// BySenderID orders the results by the sender_id field. +func BySenderID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldSenderID, opts...).ToFunc() +} + +// ByRecipient orders the results by the recipient field. +func ByRecipient(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldRecipient, opts...).ToFunc() +} + +// ByRecipientID orders the results by the recipient_id field. +func ByRecipientID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldRecipientID, opts...).ToFunc() +} + +// ByMsg orders the results by the msg field. +func ByMsg(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldMsg, opts...).ToFunc() +} + +// ByType orders the results by the type field. +func ByType(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldType, opts...).ToFunc() +} + +// ByUrgent orders the results by the urgent field. +func ByUrgent(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldUrgent, opts...).ToFunc() +} + +// ByBroadcasted orders the results by the broadcasted field. +func ByBroadcasted(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldBroadcasted, opts...).ToFunc() +} + +// ByRead orders the results by the read field. +func ByRead(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldRead, opts...).ToFunc() +} + +// ByAgentID orders the results by the agent_id field. +func ByAgentID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldAgentID, opts...).ToFunc() +} + +// ByGroupID orders the results by the group_id field. +func ByGroupID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldGroupID, opts...).ToFunc() +} + +// ByCreated orders the results by the created field. +func ByCreated(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCreated, opts...).ToFunc() +} diff --git a/pkg/ent/message/where.go b/pkg/ent/message/where.go new file mode 100644 index 000000000..4cbf1a3a3 --- /dev/null +++ b/pkg/ent/message/where.go @@ -0,0 +1,806 @@ +// Code generated by ent, DO NOT EDIT. + +package message + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/google/uuid" +) + +// ID filters vertices based on their ID field. +func ID(id uuid.UUID) predicate.Message { + return predicate.Message(sql.FieldEQ(FieldID, id)) +} + +// IDEQ applies the EQ predicate on the ID field. +func IDEQ(id uuid.UUID) predicate.Message { + return predicate.Message(sql.FieldEQ(FieldID, id)) +} + +// IDNEQ applies the NEQ predicate on the ID field. +func IDNEQ(id uuid.UUID) predicate.Message { + return predicate.Message(sql.FieldNEQ(FieldID, id)) +} + +// IDIn applies the In predicate on the ID field. +func IDIn(ids ...uuid.UUID) predicate.Message { + return predicate.Message(sql.FieldIn(FieldID, ids...)) +} + +// IDNotIn applies the NotIn predicate on the ID field. +func IDNotIn(ids ...uuid.UUID) predicate.Message { + return predicate.Message(sql.FieldNotIn(FieldID, ids...)) +} + +// IDGT applies the GT predicate on the ID field. +func IDGT(id uuid.UUID) predicate.Message { + return predicate.Message(sql.FieldGT(FieldID, id)) +} + +// IDGTE applies the GTE predicate on the ID field. +func IDGTE(id uuid.UUID) predicate.Message { + return predicate.Message(sql.FieldGTE(FieldID, id)) +} + +// IDLT applies the LT predicate on the ID field. +func IDLT(id uuid.UUID) predicate.Message { + return predicate.Message(sql.FieldLT(FieldID, id)) +} + +// IDLTE applies the LTE predicate on the ID field. +func IDLTE(id uuid.UUID) predicate.Message { + return predicate.Message(sql.FieldLTE(FieldID, id)) +} + +// ProjectID applies equality check predicate on the "project_id" field. It's identical to ProjectIDEQ. +func ProjectID(v uuid.UUID) predicate.Message { + return predicate.Message(sql.FieldEQ(FieldProjectID, v)) +} + +// Sender applies equality check predicate on the "sender" field. It's identical to SenderEQ. +func Sender(v string) predicate.Message { + return predicate.Message(sql.FieldEQ(FieldSender, v)) +} + +// SenderID applies equality check predicate on the "sender_id" field. It's identical to SenderIDEQ. +func SenderID(v string) predicate.Message { + return predicate.Message(sql.FieldEQ(FieldSenderID, v)) +} + +// Recipient applies equality check predicate on the "recipient" field. It's identical to RecipientEQ. +func Recipient(v string) predicate.Message { + return predicate.Message(sql.FieldEQ(FieldRecipient, v)) +} + +// RecipientID applies equality check predicate on the "recipient_id" field. It's identical to RecipientIDEQ. +func RecipientID(v string) predicate.Message { + return predicate.Message(sql.FieldEQ(FieldRecipientID, v)) +} + +// Msg applies equality check predicate on the "msg" field. It's identical to MsgEQ. +func Msg(v string) predicate.Message { + return predicate.Message(sql.FieldEQ(FieldMsg, v)) +} + +// Type applies equality check predicate on the "type" field. It's identical to TypeEQ. +func Type(v string) predicate.Message { + return predicate.Message(sql.FieldEQ(FieldType, v)) +} + +// Urgent applies equality check predicate on the "urgent" field. It's identical to UrgentEQ. +func Urgent(v bool) predicate.Message { + return predicate.Message(sql.FieldEQ(FieldUrgent, v)) +} + +// Broadcasted applies equality check predicate on the "broadcasted" field. It's identical to BroadcastedEQ. +func Broadcasted(v bool) predicate.Message { + return predicate.Message(sql.FieldEQ(FieldBroadcasted, v)) +} + +// Read applies equality check predicate on the "read" field. It's identical to ReadEQ. +func Read(v bool) predicate.Message { + return predicate.Message(sql.FieldEQ(FieldRead, v)) +} + +// AgentID applies equality check predicate on the "agent_id" field. It's identical to AgentIDEQ. +func AgentID(v string) predicate.Message { + return predicate.Message(sql.FieldEQ(FieldAgentID, v)) +} + +// GroupID applies equality check predicate on the "group_id" field. It's identical to GroupIDEQ. +func GroupID(v string) predicate.Message { + return predicate.Message(sql.FieldEQ(FieldGroupID, v)) +} + +// Created applies equality check predicate on the "created" field. It's identical to CreatedEQ. +func Created(v time.Time) predicate.Message { + return predicate.Message(sql.FieldEQ(FieldCreated, v)) +} + +// ProjectIDEQ applies the EQ predicate on the "project_id" field. +func ProjectIDEQ(v uuid.UUID) predicate.Message { + return predicate.Message(sql.FieldEQ(FieldProjectID, v)) +} + +// ProjectIDNEQ applies the NEQ predicate on the "project_id" field. +func ProjectIDNEQ(v uuid.UUID) predicate.Message { + return predicate.Message(sql.FieldNEQ(FieldProjectID, v)) +} + +// ProjectIDIn applies the In predicate on the "project_id" field. +func ProjectIDIn(vs ...uuid.UUID) predicate.Message { + return predicate.Message(sql.FieldIn(FieldProjectID, vs...)) +} + +// ProjectIDNotIn applies the NotIn predicate on the "project_id" field. +func ProjectIDNotIn(vs ...uuid.UUID) predicate.Message { + return predicate.Message(sql.FieldNotIn(FieldProjectID, vs...)) +} + +// ProjectIDGT applies the GT predicate on the "project_id" field. +func ProjectIDGT(v uuid.UUID) predicate.Message { + return predicate.Message(sql.FieldGT(FieldProjectID, v)) +} + +// ProjectIDGTE applies the GTE predicate on the "project_id" field. +func ProjectIDGTE(v uuid.UUID) predicate.Message { + return predicate.Message(sql.FieldGTE(FieldProjectID, v)) +} + +// ProjectIDLT applies the LT predicate on the "project_id" field. +func ProjectIDLT(v uuid.UUID) predicate.Message { + return predicate.Message(sql.FieldLT(FieldProjectID, v)) +} + +// ProjectIDLTE applies the LTE predicate on the "project_id" field. +func ProjectIDLTE(v uuid.UUID) predicate.Message { + return predicate.Message(sql.FieldLTE(FieldProjectID, v)) +} + +// SenderEQ applies the EQ predicate on the "sender" field. +func SenderEQ(v string) predicate.Message { + return predicate.Message(sql.FieldEQ(FieldSender, v)) +} + +// SenderNEQ applies the NEQ predicate on the "sender" field. +func SenderNEQ(v string) predicate.Message { + return predicate.Message(sql.FieldNEQ(FieldSender, v)) +} + +// SenderIn applies the In predicate on the "sender" field. +func SenderIn(vs ...string) predicate.Message { + return predicate.Message(sql.FieldIn(FieldSender, vs...)) +} + +// SenderNotIn applies the NotIn predicate on the "sender" field. +func SenderNotIn(vs ...string) predicate.Message { + return predicate.Message(sql.FieldNotIn(FieldSender, vs...)) +} + +// SenderGT applies the GT predicate on the "sender" field. +func SenderGT(v string) predicate.Message { + return predicate.Message(sql.FieldGT(FieldSender, v)) +} + +// SenderGTE applies the GTE predicate on the "sender" field. +func SenderGTE(v string) predicate.Message { + return predicate.Message(sql.FieldGTE(FieldSender, v)) +} + +// SenderLT applies the LT predicate on the "sender" field. +func SenderLT(v string) predicate.Message { + return predicate.Message(sql.FieldLT(FieldSender, v)) +} + +// SenderLTE applies the LTE predicate on the "sender" field. +func SenderLTE(v string) predicate.Message { + return predicate.Message(sql.FieldLTE(FieldSender, v)) +} + +// SenderContains applies the Contains predicate on the "sender" field. +func SenderContains(v string) predicate.Message { + return predicate.Message(sql.FieldContains(FieldSender, v)) +} + +// SenderHasPrefix applies the HasPrefix predicate on the "sender" field. +func SenderHasPrefix(v string) predicate.Message { + return predicate.Message(sql.FieldHasPrefix(FieldSender, v)) +} + +// SenderHasSuffix applies the HasSuffix predicate on the "sender" field. +func SenderHasSuffix(v string) predicate.Message { + return predicate.Message(sql.FieldHasSuffix(FieldSender, v)) +} + +// SenderEqualFold applies the EqualFold predicate on the "sender" field. +func SenderEqualFold(v string) predicate.Message { + return predicate.Message(sql.FieldEqualFold(FieldSender, v)) +} + +// SenderContainsFold applies the ContainsFold predicate on the "sender" field. +func SenderContainsFold(v string) predicate.Message { + return predicate.Message(sql.FieldContainsFold(FieldSender, v)) +} + +// SenderIDEQ applies the EQ predicate on the "sender_id" field. +func SenderIDEQ(v string) predicate.Message { + return predicate.Message(sql.FieldEQ(FieldSenderID, v)) +} + +// SenderIDNEQ applies the NEQ predicate on the "sender_id" field. +func SenderIDNEQ(v string) predicate.Message { + return predicate.Message(sql.FieldNEQ(FieldSenderID, v)) +} + +// SenderIDIn applies the In predicate on the "sender_id" field. +func SenderIDIn(vs ...string) predicate.Message { + return predicate.Message(sql.FieldIn(FieldSenderID, vs...)) +} + +// SenderIDNotIn applies the NotIn predicate on the "sender_id" field. +func SenderIDNotIn(vs ...string) predicate.Message { + return predicate.Message(sql.FieldNotIn(FieldSenderID, vs...)) +} + +// SenderIDGT applies the GT predicate on the "sender_id" field. +func SenderIDGT(v string) predicate.Message { + return predicate.Message(sql.FieldGT(FieldSenderID, v)) +} + +// SenderIDGTE applies the GTE predicate on the "sender_id" field. +func SenderIDGTE(v string) predicate.Message { + return predicate.Message(sql.FieldGTE(FieldSenderID, v)) +} + +// SenderIDLT applies the LT predicate on the "sender_id" field. +func SenderIDLT(v string) predicate.Message { + return predicate.Message(sql.FieldLT(FieldSenderID, v)) +} + +// SenderIDLTE applies the LTE predicate on the "sender_id" field. +func SenderIDLTE(v string) predicate.Message { + return predicate.Message(sql.FieldLTE(FieldSenderID, v)) +} + +// SenderIDContains applies the Contains predicate on the "sender_id" field. +func SenderIDContains(v string) predicate.Message { + return predicate.Message(sql.FieldContains(FieldSenderID, v)) +} + +// SenderIDHasPrefix applies the HasPrefix predicate on the "sender_id" field. +func SenderIDHasPrefix(v string) predicate.Message { + return predicate.Message(sql.FieldHasPrefix(FieldSenderID, v)) +} + +// SenderIDHasSuffix applies the HasSuffix predicate on the "sender_id" field. +func SenderIDHasSuffix(v string) predicate.Message { + return predicate.Message(sql.FieldHasSuffix(FieldSenderID, v)) +} + +// SenderIDIsNil applies the IsNil predicate on the "sender_id" field. +func SenderIDIsNil() predicate.Message { + return predicate.Message(sql.FieldIsNull(FieldSenderID)) +} + +// SenderIDNotNil applies the NotNil predicate on the "sender_id" field. +func SenderIDNotNil() predicate.Message { + return predicate.Message(sql.FieldNotNull(FieldSenderID)) +} + +// SenderIDEqualFold applies the EqualFold predicate on the "sender_id" field. +func SenderIDEqualFold(v string) predicate.Message { + return predicate.Message(sql.FieldEqualFold(FieldSenderID, v)) +} + +// SenderIDContainsFold applies the ContainsFold predicate on the "sender_id" field. +func SenderIDContainsFold(v string) predicate.Message { + return predicate.Message(sql.FieldContainsFold(FieldSenderID, v)) +} + +// RecipientEQ applies the EQ predicate on the "recipient" field. +func RecipientEQ(v string) predicate.Message { + return predicate.Message(sql.FieldEQ(FieldRecipient, v)) +} + +// RecipientNEQ applies the NEQ predicate on the "recipient" field. +func RecipientNEQ(v string) predicate.Message { + return predicate.Message(sql.FieldNEQ(FieldRecipient, v)) +} + +// RecipientIn applies the In predicate on the "recipient" field. +func RecipientIn(vs ...string) predicate.Message { + return predicate.Message(sql.FieldIn(FieldRecipient, vs...)) +} + +// RecipientNotIn applies the NotIn predicate on the "recipient" field. +func RecipientNotIn(vs ...string) predicate.Message { + return predicate.Message(sql.FieldNotIn(FieldRecipient, vs...)) +} + +// RecipientGT applies the GT predicate on the "recipient" field. +func RecipientGT(v string) predicate.Message { + return predicate.Message(sql.FieldGT(FieldRecipient, v)) +} + +// RecipientGTE applies the GTE predicate on the "recipient" field. +func RecipientGTE(v string) predicate.Message { + return predicate.Message(sql.FieldGTE(FieldRecipient, v)) +} + +// RecipientLT applies the LT predicate on the "recipient" field. +func RecipientLT(v string) predicate.Message { + return predicate.Message(sql.FieldLT(FieldRecipient, v)) +} + +// RecipientLTE applies the LTE predicate on the "recipient" field. +func RecipientLTE(v string) predicate.Message { + return predicate.Message(sql.FieldLTE(FieldRecipient, v)) +} + +// RecipientContains applies the Contains predicate on the "recipient" field. +func RecipientContains(v string) predicate.Message { + return predicate.Message(sql.FieldContains(FieldRecipient, v)) +} + +// RecipientHasPrefix applies the HasPrefix predicate on the "recipient" field. +func RecipientHasPrefix(v string) predicate.Message { + return predicate.Message(sql.FieldHasPrefix(FieldRecipient, v)) +} + +// RecipientHasSuffix applies the HasSuffix predicate on the "recipient" field. +func RecipientHasSuffix(v string) predicate.Message { + return predicate.Message(sql.FieldHasSuffix(FieldRecipient, v)) +} + +// RecipientEqualFold applies the EqualFold predicate on the "recipient" field. +func RecipientEqualFold(v string) predicate.Message { + return predicate.Message(sql.FieldEqualFold(FieldRecipient, v)) +} + +// RecipientContainsFold applies the ContainsFold predicate on the "recipient" field. +func RecipientContainsFold(v string) predicate.Message { + return predicate.Message(sql.FieldContainsFold(FieldRecipient, v)) +} + +// RecipientIDEQ applies the EQ predicate on the "recipient_id" field. +func RecipientIDEQ(v string) predicate.Message { + return predicate.Message(sql.FieldEQ(FieldRecipientID, v)) +} + +// RecipientIDNEQ applies the NEQ predicate on the "recipient_id" field. +func RecipientIDNEQ(v string) predicate.Message { + return predicate.Message(sql.FieldNEQ(FieldRecipientID, v)) +} + +// RecipientIDIn applies the In predicate on the "recipient_id" field. +func RecipientIDIn(vs ...string) predicate.Message { + return predicate.Message(sql.FieldIn(FieldRecipientID, vs...)) +} + +// RecipientIDNotIn applies the NotIn predicate on the "recipient_id" field. +func RecipientIDNotIn(vs ...string) predicate.Message { + return predicate.Message(sql.FieldNotIn(FieldRecipientID, vs...)) +} + +// RecipientIDGT applies the GT predicate on the "recipient_id" field. +func RecipientIDGT(v string) predicate.Message { + return predicate.Message(sql.FieldGT(FieldRecipientID, v)) +} + +// RecipientIDGTE applies the GTE predicate on the "recipient_id" field. +func RecipientIDGTE(v string) predicate.Message { + return predicate.Message(sql.FieldGTE(FieldRecipientID, v)) +} + +// RecipientIDLT applies the LT predicate on the "recipient_id" field. +func RecipientIDLT(v string) predicate.Message { + return predicate.Message(sql.FieldLT(FieldRecipientID, v)) +} + +// RecipientIDLTE applies the LTE predicate on the "recipient_id" field. +func RecipientIDLTE(v string) predicate.Message { + return predicate.Message(sql.FieldLTE(FieldRecipientID, v)) +} + +// RecipientIDContains applies the Contains predicate on the "recipient_id" field. +func RecipientIDContains(v string) predicate.Message { + return predicate.Message(sql.FieldContains(FieldRecipientID, v)) +} + +// RecipientIDHasPrefix applies the HasPrefix predicate on the "recipient_id" field. +func RecipientIDHasPrefix(v string) predicate.Message { + return predicate.Message(sql.FieldHasPrefix(FieldRecipientID, v)) +} + +// RecipientIDHasSuffix applies the HasSuffix predicate on the "recipient_id" field. +func RecipientIDHasSuffix(v string) predicate.Message { + return predicate.Message(sql.FieldHasSuffix(FieldRecipientID, v)) +} + +// RecipientIDIsNil applies the IsNil predicate on the "recipient_id" field. +func RecipientIDIsNil() predicate.Message { + return predicate.Message(sql.FieldIsNull(FieldRecipientID)) +} + +// RecipientIDNotNil applies the NotNil predicate on the "recipient_id" field. +func RecipientIDNotNil() predicate.Message { + return predicate.Message(sql.FieldNotNull(FieldRecipientID)) +} + +// RecipientIDEqualFold applies the EqualFold predicate on the "recipient_id" field. +func RecipientIDEqualFold(v string) predicate.Message { + return predicate.Message(sql.FieldEqualFold(FieldRecipientID, v)) +} + +// RecipientIDContainsFold applies the ContainsFold predicate on the "recipient_id" field. +func RecipientIDContainsFold(v string) predicate.Message { + return predicate.Message(sql.FieldContainsFold(FieldRecipientID, v)) +} + +// MsgEQ applies the EQ predicate on the "msg" field. +func MsgEQ(v string) predicate.Message { + return predicate.Message(sql.FieldEQ(FieldMsg, v)) +} + +// MsgNEQ applies the NEQ predicate on the "msg" field. +func MsgNEQ(v string) predicate.Message { + return predicate.Message(sql.FieldNEQ(FieldMsg, v)) +} + +// MsgIn applies the In predicate on the "msg" field. +func MsgIn(vs ...string) predicate.Message { + return predicate.Message(sql.FieldIn(FieldMsg, vs...)) +} + +// MsgNotIn applies the NotIn predicate on the "msg" field. +func MsgNotIn(vs ...string) predicate.Message { + return predicate.Message(sql.FieldNotIn(FieldMsg, vs...)) +} + +// MsgGT applies the GT predicate on the "msg" field. +func MsgGT(v string) predicate.Message { + return predicate.Message(sql.FieldGT(FieldMsg, v)) +} + +// MsgGTE applies the GTE predicate on the "msg" field. +func MsgGTE(v string) predicate.Message { + return predicate.Message(sql.FieldGTE(FieldMsg, v)) +} + +// MsgLT applies the LT predicate on the "msg" field. +func MsgLT(v string) predicate.Message { + return predicate.Message(sql.FieldLT(FieldMsg, v)) +} + +// MsgLTE applies the LTE predicate on the "msg" field. +func MsgLTE(v string) predicate.Message { + return predicate.Message(sql.FieldLTE(FieldMsg, v)) +} + +// MsgContains applies the Contains predicate on the "msg" field. +func MsgContains(v string) predicate.Message { + return predicate.Message(sql.FieldContains(FieldMsg, v)) +} + +// MsgHasPrefix applies the HasPrefix predicate on the "msg" field. +func MsgHasPrefix(v string) predicate.Message { + return predicate.Message(sql.FieldHasPrefix(FieldMsg, v)) +} + +// MsgHasSuffix applies the HasSuffix predicate on the "msg" field. +func MsgHasSuffix(v string) predicate.Message { + return predicate.Message(sql.FieldHasSuffix(FieldMsg, v)) +} + +// MsgEqualFold applies the EqualFold predicate on the "msg" field. +func MsgEqualFold(v string) predicate.Message { + return predicate.Message(sql.FieldEqualFold(FieldMsg, v)) +} + +// MsgContainsFold applies the ContainsFold predicate on the "msg" field. +func MsgContainsFold(v string) predicate.Message { + return predicate.Message(sql.FieldContainsFold(FieldMsg, v)) +} + +// TypeEQ applies the EQ predicate on the "type" field. +func TypeEQ(v string) predicate.Message { + return predicate.Message(sql.FieldEQ(FieldType, v)) +} + +// TypeNEQ applies the NEQ predicate on the "type" field. +func TypeNEQ(v string) predicate.Message { + return predicate.Message(sql.FieldNEQ(FieldType, v)) +} + +// TypeIn applies the In predicate on the "type" field. +func TypeIn(vs ...string) predicate.Message { + return predicate.Message(sql.FieldIn(FieldType, vs...)) +} + +// TypeNotIn applies the NotIn predicate on the "type" field. +func TypeNotIn(vs ...string) predicate.Message { + return predicate.Message(sql.FieldNotIn(FieldType, vs...)) +} + +// TypeGT applies the GT predicate on the "type" field. +func TypeGT(v string) predicate.Message { + return predicate.Message(sql.FieldGT(FieldType, v)) +} + +// TypeGTE applies the GTE predicate on the "type" field. +func TypeGTE(v string) predicate.Message { + return predicate.Message(sql.FieldGTE(FieldType, v)) +} + +// TypeLT applies the LT predicate on the "type" field. +func TypeLT(v string) predicate.Message { + return predicate.Message(sql.FieldLT(FieldType, v)) +} + +// TypeLTE applies the LTE predicate on the "type" field. +func TypeLTE(v string) predicate.Message { + return predicate.Message(sql.FieldLTE(FieldType, v)) +} + +// TypeContains applies the Contains predicate on the "type" field. +func TypeContains(v string) predicate.Message { + return predicate.Message(sql.FieldContains(FieldType, v)) +} + +// TypeHasPrefix applies the HasPrefix predicate on the "type" field. +func TypeHasPrefix(v string) predicate.Message { + return predicate.Message(sql.FieldHasPrefix(FieldType, v)) +} + +// TypeHasSuffix applies the HasSuffix predicate on the "type" field. +func TypeHasSuffix(v string) predicate.Message { + return predicate.Message(sql.FieldHasSuffix(FieldType, v)) +} + +// TypeEqualFold applies the EqualFold predicate on the "type" field. +func TypeEqualFold(v string) predicate.Message { + return predicate.Message(sql.FieldEqualFold(FieldType, v)) +} + +// TypeContainsFold applies the ContainsFold predicate on the "type" field. +func TypeContainsFold(v string) predicate.Message { + return predicate.Message(sql.FieldContainsFold(FieldType, v)) +} + +// UrgentEQ applies the EQ predicate on the "urgent" field. +func UrgentEQ(v bool) predicate.Message { + return predicate.Message(sql.FieldEQ(FieldUrgent, v)) +} + +// UrgentNEQ applies the NEQ predicate on the "urgent" field. +func UrgentNEQ(v bool) predicate.Message { + return predicate.Message(sql.FieldNEQ(FieldUrgent, v)) +} + +// BroadcastedEQ applies the EQ predicate on the "broadcasted" field. +func BroadcastedEQ(v bool) predicate.Message { + return predicate.Message(sql.FieldEQ(FieldBroadcasted, v)) +} + +// BroadcastedNEQ applies the NEQ predicate on the "broadcasted" field. +func BroadcastedNEQ(v bool) predicate.Message { + return predicate.Message(sql.FieldNEQ(FieldBroadcasted, v)) +} + +// ReadEQ applies the EQ predicate on the "read" field. +func ReadEQ(v bool) predicate.Message { + return predicate.Message(sql.FieldEQ(FieldRead, v)) +} + +// ReadNEQ applies the NEQ predicate on the "read" field. +func ReadNEQ(v bool) predicate.Message { + return predicate.Message(sql.FieldNEQ(FieldRead, v)) +} + +// AgentIDEQ applies the EQ predicate on the "agent_id" field. +func AgentIDEQ(v string) predicate.Message { + return predicate.Message(sql.FieldEQ(FieldAgentID, v)) +} + +// AgentIDNEQ applies the NEQ predicate on the "agent_id" field. +func AgentIDNEQ(v string) predicate.Message { + return predicate.Message(sql.FieldNEQ(FieldAgentID, v)) +} + +// AgentIDIn applies the In predicate on the "agent_id" field. +func AgentIDIn(vs ...string) predicate.Message { + return predicate.Message(sql.FieldIn(FieldAgentID, vs...)) +} + +// AgentIDNotIn applies the NotIn predicate on the "agent_id" field. +func AgentIDNotIn(vs ...string) predicate.Message { + return predicate.Message(sql.FieldNotIn(FieldAgentID, vs...)) +} + +// AgentIDGT applies the GT predicate on the "agent_id" field. +func AgentIDGT(v string) predicate.Message { + return predicate.Message(sql.FieldGT(FieldAgentID, v)) +} + +// AgentIDGTE applies the GTE predicate on the "agent_id" field. +func AgentIDGTE(v string) predicate.Message { + return predicate.Message(sql.FieldGTE(FieldAgentID, v)) +} + +// AgentIDLT applies the LT predicate on the "agent_id" field. +func AgentIDLT(v string) predicate.Message { + return predicate.Message(sql.FieldLT(FieldAgentID, v)) +} + +// AgentIDLTE applies the LTE predicate on the "agent_id" field. +func AgentIDLTE(v string) predicate.Message { + return predicate.Message(sql.FieldLTE(FieldAgentID, v)) +} + +// AgentIDContains applies the Contains predicate on the "agent_id" field. +func AgentIDContains(v string) predicate.Message { + return predicate.Message(sql.FieldContains(FieldAgentID, v)) +} + +// AgentIDHasPrefix applies the HasPrefix predicate on the "agent_id" field. +func AgentIDHasPrefix(v string) predicate.Message { + return predicate.Message(sql.FieldHasPrefix(FieldAgentID, v)) +} + +// AgentIDHasSuffix applies the HasSuffix predicate on the "agent_id" field. +func AgentIDHasSuffix(v string) predicate.Message { + return predicate.Message(sql.FieldHasSuffix(FieldAgentID, v)) +} + +// AgentIDIsNil applies the IsNil predicate on the "agent_id" field. +func AgentIDIsNil() predicate.Message { + return predicate.Message(sql.FieldIsNull(FieldAgentID)) +} + +// AgentIDNotNil applies the NotNil predicate on the "agent_id" field. +func AgentIDNotNil() predicate.Message { + return predicate.Message(sql.FieldNotNull(FieldAgentID)) +} + +// AgentIDEqualFold applies the EqualFold predicate on the "agent_id" field. +func AgentIDEqualFold(v string) predicate.Message { + return predicate.Message(sql.FieldEqualFold(FieldAgentID, v)) +} + +// AgentIDContainsFold applies the ContainsFold predicate on the "agent_id" field. +func AgentIDContainsFold(v string) predicate.Message { + return predicate.Message(sql.FieldContainsFold(FieldAgentID, v)) +} + +// GroupIDEQ applies the EQ predicate on the "group_id" field. +func GroupIDEQ(v string) predicate.Message { + return predicate.Message(sql.FieldEQ(FieldGroupID, v)) +} + +// GroupIDNEQ applies the NEQ predicate on the "group_id" field. +func GroupIDNEQ(v string) predicate.Message { + return predicate.Message(sql.FieldNEQ(FieldGroupID, v)) +} + +// GroupIDIn applies the In predicate on the "group_id" field. +func GroupIDIn(vs ...string) predicate.Message { + return predicate.Message(sql.FieldIn(FieldGroupID, vs...)) +} + +// GroupIDNotIn applies the NotIn predicate on the "group_id" field. +func GroupIDNotIn(vs ...string) predicate.Message { + return predicate.Message(sql.FieldNotIn(FieldGroupID, vs...)) +} + +// GroupIDGT applies the GT predicate on the "group_id" field. +func GroupIDGT(v string) predicate.Message { + return predicate.Message(sql.FieldGT(FieldGroupID, v)) +} + +// GroupIDGTE applies the GTE predicate on the "group_id" field. +func GroupIDGTE(v string) predicate.Message { + return predicate.Message(sql.FieldGTE(FieldGroupID, v)) +} + +// GroupIDLT applies the LT predicate on the "group_id" field. +func GroupIDLT(v string) predicate.Message { + return predicate.Message(sql.FieldLT(FieldGroupID, v)) +} + +// GroupIDLTE applies the LTE predicate on the "group_id" field. +func GroupIDLTE(v string) predicate.Message { + return predicate.Message(sql.FieldLTE(FieldGroupID, v)) +} + +// GroupIDContains applies the Contains predicate on the "group_id" field. +func GroupIDContains(v string) predicate.Message { + return predicate.Message(sql.FieldContains(FieldGroupID, v)) +} + +// GroupIDHasPrefix applies the HasPrefix predicate on the "group_id" field. +func GroupIDHasPrefix(v string) predicate.Message { + return predicate.Message(sql.FieldHasPrefix(FieldGroupID, v)) +} + +// GroupIDHasSuffix applies the HasSuffix predicate on the "group_id" field. +func GroupIDHasSuffix(v string) predicate.Message { + return predicate.Message(sql.FieldHasSuffix(FieldGroupID, v)) +} + +// GroupIDIsNil applies the IsNil predicate on the "group_id" field. +func GroupIDIsNil() predicate.Message { + return predicate.Message(sql.FieldIsNull(FieldGroupID)) +} + +// GroupIDNotNil applies the NotNil predicate on the "group_id" field. +func GroupIDNotNil() predicate.Message { + return predicate.Message(sql.FieldNotNull(FieldGroupID)) +} + +// GroupIDEqualFold applies the EqualFold predicate on the "group_id" field. +func GroupIDEqualFold(v string) predicate.Message { + return predicate.Message(sql.FieldEqualFold(FieldGroupID, v)) +} + +// GroupIDContainsFold applies the ContainsFold predicate on the "group_id" field. +func GroupIDContainsFold(v string) predicate.Message { + return predicate.Message(sql.FieldContainsFold(FieldGroupID, v)) +} + +// CreatedEQ applies the EQ predicate on the "created" field. +func CreatedEQ(v time.Time) predicate.Message { + return predicate.Message(sql.FieldEQ(FieldCreated, v)) +} + +// CreatedNEQ applies the NEQ predicate on the "created" field. +func CreatedNEQ(v time.Time) predicate.Message { + return predicate.Message(sql.FieldNEQ(FieldCreated, v)) +} + +// CreatedIn applies the In predicate on the "created" field. +func CreatedIn(vs ...time.Time) predicate.Message { + return predicate.Message(sql.FieldIn(FieldCreated, vs...)) +} + +// CreatedNotIn applies the NotIn predicate on the "created" field. +func CreatedNotIn(vs ...time.Time) predicate.Message { + return predicate.Message(sql.FieldNotIn(FieldCreated, vs...)) +} + +// CreatedGT applies the GT predicate on the "created" field. +func CreatedGT(v time.Time) predicate.Message { + return predicate.Message(sql.FieldGT(FieldCreated, v)) +} + +// CreatedGTE applies the GTE predicate on the "created" field. +func CreatedGTE(v time.Time) predicate.Message { + return predicate.Message(sql.FieldGTE(FieldCreated, v)) +} + +// CreatedLT applies the LT predicate on the "created" field. +func CreatedLT(v time.Time) predicate.Message { + return predicate.Message(sql.FieldLT(FieldCreated, v)) +} + +// CreatedLTE applies the LTE predicate on the "created" field. +func CreatedLTE(v time.Time) predicate.Message { + return predicate.Message(sql.FieldLTE(FieldCreated, v)) +} + +// And groups predicates with the AND operator between them. +func And(predicates ...predicate.Message) predicate.Message { + return predicate.Message(sql.AndPredicates(predicates...)) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.Message) predicate.Message { + return predicate.Message(sql.OrPredicates(predicates...)) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.Message) predicate.Message { + return predicate.Message(sql.NotPredicates(p)) +} diff --git a/pkg/ent/message_create.go b/pkg/ent/message_create.go new file mode 100644 index 000000000..198a9b98f --- /dev/null +++ b/pkg/ent/message_create.go @@ -0,0 +1,1286 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/message" + "github.com/google/uuid" +) + +// MessageCreate is the builder for creating a Message entity. +type MessageCreate struct { + config + mutation *MessageMutation + hooks []Hook + conflict []sql.ConflictOption +} + +// SetProjectID sets the "project_id" field. +func (_c *MessageCreate) SetProjectID(v uuid.UUID) *MessageCreate { + _c.mutation.SetProjectID(v) + return _c +} + +// SetSender sets the "sender" field. +func (_c *MessageCreate) SetSender(v string) *MessageCreate { + _c.mutation.SetSender(v) + return _c +} + +// SetSenderID sets the "sender_id" field. +func (_c *MessageCreate) SetSenderID(v string) *MessageCreate { + _c.mutation.SetSenderID(v) + return _c +} + +// SetNillableSenderID sets the "sender_id" field if the given value is not nil. +func (_c *MessageCreate) SetNillableSenderID(v *string) *MessageCreate { + if v != nil { + _c.SetSenderID(*v) + } + return _c +} + +// SetRecipient sets the "recipient" field. +func (_c *MessageCreate) SetRecipient(v string) *MessageCreate { + _c.mutation.SetRecipient(v) + return _c +} + +// SetRecipientID sets the "recipient_id" field. +func (_c *MessageCreate) SetRecipientID(v string) *MessageCreate { + _c.mutation.SetRecipientID(v) + return _c +} + +// SetNillableRecipientID sets the "recipient_id" field if the given value is not nil. +func (_c *MessageCreate) SetNillableRecipientID(v *string) *MessageCreate { + if v != nil { + _c.SetRecipientID(*v) + } + return _c +} + +// SetMsg sets the "msg" field. +func (_c *MessageCreate) SetMsg(v string) *MessageCreate { + _c.mutation.SetMsg(v) + return _c +} + +// SetType sets the "type" field. +func (_c *MessageCreate) SetType(v string) *MessageCreate { + _c.mutation.SetType(v) + return _c +} + +// SetNillableType sets the "type" field if the given value is not nil. +func (_c *MessageCreate) SetNillableType(v *string) *MessageCreate { + if v != nil { + _c.SetType(*v) + } + return _c +} + +// SetUrgent sets the "urgent" field. +func (_c *MessageCreate) SetUrgent(v bool) *MessageCreate { + _c.mutation.SetUrgent(v) + return _c +} + +// SetNillableUrgent sets the "urgent" field if the given value is not nil. +func (_c *MessageCreate) SetNillableUrgent(v *bool) *MessageCreate { + if v != nil { + _c.SetUrgent(*v) + } + return _c +} + +// SetBroadcasted sets the "broadcasted" field. +func (_c *MessageCreate) SetBroadcasted(v bool) *MessageCreate { + _c.mutation.SetBroadcasted(v) + return _c +} + +// SetNillableBroadcasted sets the "broadcasted" field if the given value is not nil. +func (_c *MessageCreate) SetNillableBroadcasted(v *bool) *MessageCreate { + if v != nil { + _c.SetBroadcasted(*v) + } + return _c +} + +// SetRead sets the "read" field. +func (_c *MessageCreate) SetRead(v bool) *MessageCreate { + _c.mutation.SetRead(v) + return _c +} + +// SetNillableRead sets the "read" field if the given value is not nil. +func (_c *MessageCreate) SetNillableRead(v *bool) *MessageCreate { + if v != nil { + _c.SetRead(*v) + } + return _c +} + +// SetAgentID sets the "agent_id" field. +func (_c *MessageCreate) SetAgentID(v string) *MessageCreate { + _c.mutation.SetAgentID(v) + return _c +} + +// SetNillableAgentID sets the "agent_id" field if the given value is not nil. +func (_c *MessageCreate) SetNillableAgentID(v *string) *MessageCreate { + if v != nil { + _c.SetAgentID(*v) + } + return _c +} + +// SetGroupID sets the "group_id" field. +func (_c *MessageCreate) SetGroupID(v string) *MessageCreate { + _c.mutation.SetGroupID(v) + return _c +} + +// SetNillableGroupID sets the "group_id" field if the given value is not nil. +func (_c *MessageCreate) SetNillableGroupID(v *string) *MessageCreate { + if v != nil { + _c.SetGroupID(*v) + } + return _c +} + +// SetCreated sets the "created" field. +func (_c *MessageCreate) SetCreated(v time.Time) *MessageCreate { + _c.mutation.SetCreated(v) + return _c +} + +// SetNillableCreated sets the "created" field if the given value is not nil. +func (_c *MessageCreate) SetNillableCreated(v *time.Time) *MessageCreate { + if v != nil { + _c.SetCreated(*v) + } + return _c +} + +// SetID sets the "id" field. +func (_c *MessageCreate) SetID(v uuid.UUID) *MessageCreate { + _c.mutation.SetID(v) + return _c +} + +// SetNillableID sets the "id" field if the given value is not nil. +func (_c *MessageCreate) SetNillableID(v *uuid.UUID) *MessageCreate { + if v != nil { + _c.SetID(*v) + } + return _c +} + +// Mutation returns the MessageMutation object of the builder. +func (_c *MessageCreate) Mutation() *MessageMutation { + return _c.mutation +} + +// Save creates the Message in the database. +func (_c *MessageCreate) Save(ctx context.Context) (*Message, error) { + _c.defaults() + return withHooks(ctx, _c.sqlSave, _c.mutation, _c.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (_c *MessageCreate) SaveX(ctx context.Context) *Message { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *MessageCreate) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *MessageCreate) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_c *MessageCreate) defaults() { + if _, ok := _c.mutation.GetType(); !ok { + v := message.DefaultType + _c.mutation.SetType(v) + } + if _, ok := _c.mutation.Urgent(); !ok { + v := message.DefaultUrgent + _c.mutation.SetUrgent(v) + } + if _, ok := _c.mutation.Broadcasted(); !ok { + v := message.DefaultBroadcasted + _c.mutation.SetBroadcasted(v) + } + if _, ok := _c.mutation.Read(); !ok { + v := message.DefaultRead + _c.mutation.SetRead(v) + } + if _, ok := _c.mutation.Created(); !ok { + v := message.DefaultCreated() + _c.mutation.SetCreated(v) + } + if _, ok := _c.mutation.ID(); !ok { + v := message.DefaultID() + _c.mutation.SetID(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_c *MessageCreate) check() error { + if _, ok := _c.mutation.ProjectID(); !ok { + return &ValidationError{Name: "project_id", err: errors.New(`ent: missing required field "Message.project_id"`)} + } + if _, ok := _c.mutation.Sender(); !ok { + return &ValidationError{Name: "sender", err: errors.New(`ent: missing required field "Message.sender"`)} + } + if v, ok := _c.mutation.Sender(); ok { + if err := message.SenderValidator(v); err != nil { + return &ValidationError{Name: "sender", err: fmt.Errorf(`ent: validator failed for field "Message.sender": %w`, err)} + } + } + if _, ok := _c.mutation.Recipient(); !ok { + return &ValidationError{Name: "recipient", err: errors.New(`ent: missing required field "Message.recipient"`)} + } + if v, ok := _c.mutation.Recipient(); ok { + if err := message.RecipientValidator(v); err != nil { + return &ValidationError{Name: "recipient", err: fmt.Errorf(`ent: validator failed for field "Message.recipient": %w`, err)} + } + } + if _, ok := _c.mutation.Msg(); !ok { + return &ValidationError{Name: "msg", err: errors.New(`ent: missing required field "Message.msg"`)} + } + if v, ok := _c.mutation.Msg(); ok { + if err := message.MsgValidator(v); err != nil { + return &ValidationError{Name: "msg", err: fmt.Errorf(`ent: validator failed for field "Message.msg": %w`, err)} + } + } + if _, ok := _c.mutation.GetType(); !ok { + return &ValidationError{Name: "type", err: errors.New(`ent: missing required field "Message.type"`)} + } + if _, ok := _c.mutation.Urgent(); !ok { + return &ValidationError{Name: "urgent", err: errors.New(`ent: missing required field "Message.urgent"`)} + } + if _, ok := _c.mutation.Broadcasted(); !ok { + return &ValidationError{Name: "broadcasted", err: errors.New(`ent: missing required field "Message.broadcasted"`)} + } + if _, ok := _c.mutation.Read(); !ok { + return &ValidationError{Name: "read", err: errors.New(`ent: missing required field "Message.read"`)} + } + if _, ok := _c.mutation.Created(); !ok { + return &ValidationError{Name: "created", err: errors.New(`ent: missing required field "Message.created"`)} + } + return nil +} + +func (_c *MessageCreate) sqlSave(ctx context.Context) (*Message, error) { + if err := _c.check(); err != nil { + return nil, err + } + _node, _spec := _c.createSpec() + if err := sqlgraph.CreateNode(ctx, _c.driver, _spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + if _spec.ID.Value != nil { + if id, ok := _spec.ID.Value.(*uuid.UUID); ok { + _node.ID = *id + } else if err := _node.ID.Scan(_spec.ID.Value); err != nil { + return nil, err + } + } + _c.mutation.id = &_node.ID + _c.mutation.done = true + return _node, nil +} + +func (_c *MessageCreate) createSpec() (*Message, *sqlgraph.CreateSpec) { + var ( + _node = &Message{config: _c.config} + _spec = sqlgraph.NewCreateSpec(message.Table, sqlgraph.NewFieldSpec(message.FieldID, field.TypeUUID)) + ) + _spec.OnConflict = _c.conflict + if id, ok := _c.mutation.ID(); ok { + _node.ID = id + _spec.ID.Value = &id + } + if value, ok := _c.mutation.ProjectID(); ok { + _spec.SetField(message.FieldProjectID, field.TypeUUID, value) + _node.ProjectID = value + } + if value, ok := _c.mutation.Sender(); ok { + _spec.SetField(message.FieldSender, field.TypeString, value) + _node.Sender = value + } + if value, ok := _c.mutation.SenderID(); ok { + _spec.SetField(message.FieldSenderID, field.TypeString, value) + _node.SenderID = value + } + if value, ok := _c.mutation.Recipient(); ok { + _spec.SetField(message.FieldRecipient, field.TypeString, value) + _node.Recipient = value + } + if value, ok := _c.mutation.RecipientID(); ok { + _spec.SetField(message.FieldRecipientID, field.TypeString, value) + _node.RecipientID = value + } + if value, ok := _c.mutation.Msg(); ok { + _spec.SetField(message.FieldMsg, field.TypeString, value) + _node.Msg = value + } + if value, ok := _c.mutation.GetType(); ok { + _spec.SetField(message.FieldType, field.TypeString, value) + _node.Type = value + } + if value, ok := _c.mutation.Urgent(); ok { + _spec.SetField(message.FieldUrgent, field.TypeBool, value) + _node.Urgent = value + } + if value, ok := _c.mutation.Broadcasted(); ok { + _spec.SetField(message.FieldBroadcasted, field.TypeBool, value) + _node.Broadcasted = value + } + if value, ok := _c.mutation.Read(); ok { + _spec.SetField(message.FieldRead, field.TypeBool, value) + _node.Read = value + } + if value, ok := _c.mutation.AgentID(); ok { + _spec.SetField(message.FieldAgentID, field.TypeString, value) + _node.AgentID = value + } + if value, ok := _c.mutation.GroupID(); ok { + _spec.SetField(message.FieldGroupID, field.TypeString, value) + _node.GroupID = value + } + if value, ok := _c.mutation.Created(); ok { + _spec.SetField(message.FieldCreated, field.TypeTime, value) + _node.Created = value + } + return _node, _spec +} + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.Message.Create(). +// SetProjectID(v). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.MessageUpsert) { +// SetProjectID(v+v). +// }). +// Exec(ctx) +func (_c *MessageCreate) OnConflict(opts ...sql.ConflictOption) *MessageUpsertOne { + _c.conflict = opts + return &MessageUpsertOne{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.Message.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *MessageCreate) OnConflictColumns(columns ...string) *MessageUpsertOne { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &MessageUpsertOne{ + create: _c, + } +} + +type ( + // MessageUpsertOne is the builder for "upsert"-ing + // one Message node. + MessageUpsertOne struct { + create *MessageCreate + } + + // MessageUpsert is the "OnConflict" setter. + MessageUpsert struct { + *sql.UpdateSet + } +) + +// SetProjectID sets the "project_id" field. +func (u *MessageUpsert) SetProjectID(v uuid.UUID) *MessageUpsert { + u.Set(message.FieldProjectID, v) + return u +} + +// UpdateProjectID sets the "project_id" field to the value that was provided on create. +func (u *MessageUpsert) UpdateProjectID() *MessageUpsert { + u.SetExcluded(message.FieldProjectID) + return u +} + +// SetSender sets the "sender" field. +func (u *MessageUpsert) SetSender(v string) *MessageUpsert { + u.Set(message.FieldSender, v) + return u +} + +// UpdateSender sets the "sender" field to the value that was provided on create. +func (u *MessageUpsert) UpdateSender() *MessageUpsert { + u.SetExcluded(message.FieldSender) + return u +} + +// SetSenderID sets the "sender_id" field. +func (u *MessageUpsert) SetSenderID(v string) *MessageUpsert { + u.Set(message.FieldSenderID, v) + return u +} + +// UpdateSenderID sets the "sender_id" field to the value that was provided on create. +func (u *MessageUpsert) UpdateSenderID() *MessageUpsert { + u.SetExcluded(message.FieldSenderID) + return u +} + +// ClearSenderID clears the value of the "sender_id" field. +func (u *MessageUpsert) ClearSenderID() *MessageUpsert { + u.SetNull(message.FieldSenderID) + return u +} + +// SetRecipient sets the "recipient" field. +func (u *MessageUpsert) SetRecipient(v string) *MessageUpsert { + u.Set(message.FieldRecipient, v) + return u +} + +// UpdateRecipient sets the "recipient" field to the value that was provided on create. +func (u *MessageUpsert) UpdateRecipient() *MessageUpsert { + u.SetExcluded(message.FieldRecipient) + return u +} + +// SetRecipientID sets the "recipient_id" field. +func (u *MessageUpsert) SetRecipientID(v string) *MessageUpsert { + u.Set(message.FieldRecipientID, v) + return u +} + +// UpdateRecipientID sets the "recipient_id" field to the value that was provided on create. +func (u *MessageUpsert) UpdateRecipientID() *MessageUpsert { + u.SetExcluded(message.FieldRecipientID) + return u +} + +// ClearRecipientID clears the value of the "recipient_id" field. +func (u *MessageUpsert) ClearRecipientID() *MessageUpsert { + u.SetNull(message.FieldRecipientID) + return u +} + +// SetMsg sets the "msg" field. +func (u *MessageUpsert) SetMsg(v string) *MessageUpsert { + u.Set(message.FieldMsg, v) + return u +} + +// UpdateMsg sets the "msg" field to the value that was provided on create. +func (u *MessageUpsert) UpdateMsg() *MessageUpsert { + u.SetExcluded(message.FieldMsg) + return u +} + +// SetType sets the "type" field. +func (u *MessageUpsert) SetType(v string) *MessageUpsert { + u.Set(message.FieldType, v) + return u +} + +// UpdateType sets the "type" field to the value that was provided on create. +func (u *MessageUpsert) UpdateType() *MessageUpsert { + u.SetExcluded(message.FieldType) + return u +} + +// SetUrgent sets the "urgent" field. +func (u *MessageUpsert) SetUrgent(v bool) *MessageUpsert { + u.Set(message.FieldUrgent, v) + return u +} + +// UpdateUrgent sets the "urgent" field to the value that was provided on create. +func (u *MessageUpsert) UpdateUrgent() *MessageUpsert { + u.SetExcluded(message.FieldUrgent) + return u +} + +// SetBroadcasted sets the "broadcasted" field. +func (u *MessageUpsert) SetBroadcasted(v bool) *MessageUpsert { + u.Set(message.FieldBroadcasted, v) + return u +} + +// UpdateBroadcasted sets the "broadcasted" field to the value that was provided on create. +func (u *MessageUpsert) UpdateBroadcasted() *MessageUpsert { + u.SetExcluded(message.FieldBroadcasted) + return u +} + +// SetRead sets the "read" field. +func (u *MessageUpsert) SetRead(v bool) *MessageUpsert { + u.Set(message.FieldRead, v) + return u +} + +// UpdateRead sets the "read" field to the value that was provided on create. +func (u *MessageUpsert) UpdateRead() *MessageUpsert { + u.SetExcluded(message.FieldRead) + return u +} + +// SetAgentID sets the "agent_id" field. +func (u *MessageUpsert) SetAgentID(v string) *MessageUpsert { + u.Set(message.FieldAgentID, v) + return u +} + +// UpdateAgentID sets the "agent_id" field to the value that was provided on create. +func (u *MessageUpsert) UpdateAgentID() *MessageUpsert { + u.SetExcluded(message.FieldAgentID) + return u +} + +// ClearAgentID clears the value of the "agent_id" field. +func (u *MessageUpsert) ClearAgentID() *MessageUpsert { + u.SetNull(message.FieldAgentID) + return u +} + +// SetGroupID sets the "group_id" field. +func (u *MessageUpsert) SetGroupID(v string) *MessageUpsert { + u.Set(message.FieldGroupID, v) + return u +} + +// UpdateGroupID sets the "group_id" field to the value that was provided on create. +func (u *MessageUpsert) UpdateGroupID() *MessageUpsert { + u.SetExcluded(message.FieldGroupID) + return u +} + +// ClearGroupID clears the value of the "group_id" field. +func (u *MessageUpsert) ClearGroupID() *MessageUpsert { + u.SetNull(message.FieldGroupID) + return u +} + +// UpdateNewValues updates the mutable fields using the new values that were set on create except the ID field. +// Using this option is equivalent to using: +// +// client.Message.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(message.FieldID) +// }), +// ). +// Exec(ctx) +func (u *MessageUpsertOne) UpdateNewValues() *MessageUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + if _, exists := u.create.mutation.ID(); exists { + s.SetIgnore(message.FieldID) + } + if _, exists := u.create.mutation.Created(); exists { + s.SetIgnore(message.FieldCreated) + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.Message.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *MessageUpsertOne) Ignore() *MessageUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *MessageUpsertOne) DoNothing() *MessageUpsertOne { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the MessageCreate.OnConflict +// documentation for more info. +func (u *MessageUpsertOne) Update(set func(*MessageUpsert)) *MessageUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&MessageUpsert{UpdateSet: update}) + })) + return u +} + +// SetProjectID sets the "project_id" field. +func (u *MessageUpsertOne) SetProjectID(v uuid.UUID) *MessageUpsertOne { + return u.Update(func(s *MessageUpsert) { + s.SetProjectID(v) + }) +} + +// UpdateProjectID sets the "project_id" field to the value that was provided on create. +func (u *MessageUpsertOne) UpdateProjectID() *MessageUpsertOne { + return u.Update(func(s *MessageUpsert) { + s.UpdateProjectID() + }) +} + +// SetSender sets the "sender" field. +func (u *MessageUpsertOne) SetSender(v string) *MessageUpsertOne { + return u.Update(func(s *MessageUpsert) { + s.SetSender(v) + }) +} + +// UpdateSender sets the "sender" field to the value that was provided on create. +func (u *MessageUpsertOne) UpdateSender() *MessageUpsertOne { + return u.Update(func(s *MessageUpsert) { + s.UpdateSender() + }) +} + +// SetSenderID sets the "sender_id" field. +func (u *MessageUpsertOne) SetSenderID(v string) *MessageUpsertOne { + return u.Update(func(s *MessageUpsert) { + s.SetSenderID(v) + }) +} + +// UpdateSenderID sets the "sender_id" field to the value that was provided on create. +func (u *MessageUpsertOne) UpdateSenderID() *MessageUpsertOne { + return u.Update(func(s *MessageUpsert) { + s.UpdateSenderID() + }) +} + +// ClearSenderID clears the value of the "sender_id" field. +func (u *MessageUpsertOne) ClearSenderID() *MessageUpsertOne { + return u.Update(func(s *MessageUpsert) { + s.ClearSenderID() + }) +} + +// SetRecipient sets the "recipient" field. +func (u *MessageUpsertOne) SetRecipient(v string) *MessageUpsertOne { + return u.Update(func(s *MessageUpsert) { + s.SetRecipient(v) + }) +} + +// UpdateRecipient sets the "recipient" field to the value that was provided on create. +func (u *MessageUpsertOne) UpdateRecipient() *MessageUpsertOne { + return u.Update(func(s *MessageUpsert) { + s.UpdateRecipient() + }) +} + +// SetRecipientID sets the "recipient_id" field. +func (u *MessageUpsertOne) SetRecipientID(v string) *MessageUpsertOne { + return u.Update(func(s *MessageUpsert) { + s.SetRecipientID(v) + }) +} + +// UpdateRecipientID sets the "recipient_id" field to the value that was provided on create. +func (u *MessageUpsertOne) UpdateRecipientID() *MessageUpsertOne { + return u.Update(func(s *MessageUpsert) { + s.UpdateRecipientID() + }) +} + +// ClearRecipientID clears the value of the "recipient_id" field. +func (u *MessageUpsertOne) ClearRecipientID() *MessageUpsertOne { + return u.Update(func(s *MessageUpsert) { + s.ClearRecipientID() + }) +} + +// SetMsg sets the "msg" field. +func (u *MessageUpsertOne) SetMsg(v string) *MessageUpsertOne { + return u.Update(func(s *MessageUpsert) { + s.SetMsg(v) + }) +} + +// UpdateMsg sets the "msg" field to the value that was provided on create. +func (u *MessageUpsertOne) UpdateMsg() *MessageUpsertOne { + return u.Update(func(s *MessageUpsert) { + s.UpdateMsg() + }) +} + +// SetType sets the "type" field. +func (u *MessageUpsertOne) SetType(v string) *MessageUpsertOne { + return u.Update(func(s *MessageUpsert) { + s.SetType(v) + }) +} + +// UpdateType sets the "type" field to the value that was provided on create. +func (u *MessageUpsertOne) UpdateType() *MessageUpsertOne { + return u.Update(func(s *MessageUpsert) { + s.UpdateType() + }) +} + +// SetUrgent sets the "urgent" field. +func (u *MessageUpsertOne) SetUrgent(v bool) *MessageUpsertOne { + return u.Update(func(s *MessageUpsert) { + s.SetUrgent(v) + }) +} + +// UpdateUrgent sets the "urgent" field to the value that was provided on create. +func (u *MessageUpsertOne) UpdateUrgent() *MessageUpsertOne { + return u.Update(func(s *MessageUpsert) { + s.UpdateUrgent() + }) +} + +// SetBroadcasted sets the "broadcasted" field. +func (u *MessageUpsertOne) SetBroadcasted(v bool) *MessageUpsertOne { + return u.Update(func(s *MessageUpsert) { + s.SetBroadcasted(v) + }) +} + +// UpdateBroadcasted sets the "broadcasted" field to the value that was provided on create. +func (u *MessageUpsertOne) UpdateBroadcasted() *MessageUpsertOne { + return u.Update(func(s *MessageUpsert) { + s.UpdateBroadcasted() + }) +} + +// SetRead sets the "read" field. +func (u *MessageUpsertOne) SetRead(v bool) *MessageUpsertOne { + return u.Update(func(s *MessageUpsert) { + s.SetRead(v) + }) +} + +// UpdateRead sets the "read" field to the value that was provided on create. +func (u *MessageUpsertOne) UpdateRead() *MessageUpsertOne { + return u.Update(func(s *MessageUpsert) { + s.UpdateRead() + }) +} + +// SetAgentID sets the "agent_id" field. +func (u *MessageUpsertOne) SetAgentID(v string) *MessageUpsertOne { + return u.Update(func(s *MessageUpsert) { + s.SetAgentID(v) + }) +} + +// UpdateAgentID sets the "agent_id" field to the value that was provided on create. +func (u *MessageUpsertOne) UpdateAgentID() *MessageUpsertOne { + return u.Update(func(s *MessageUpsert) { + s.UpdateAgentID() + }) +} + +// ClearAgentID clears the value of the "agent_id" field. +func (u *MessageUpsertOne) ClearAgentID() *MessageUpsertOne { + return u.Update(func(s *MessageUpsert) { + s.ClearAgentID() + }) +} + +// SetGroupID sets the "group_id" field. +func (u *MessageUpsertOne) SetGroupID(v string) *MessageUpsertOne { + return u.Update(func(s *MessageUpsert) { + s.SetGroupID(v) + }) +} + +// UpdateGroupID sets the "group_id" field to the value that was provided on create. +func (u *MessageUpsertOne) UpdateGroupID() *MessageUpsertOne { + return u.Update(func(s *MessageUpsert) { + s.UpdateGroupID() + }) +} + +// ClearGroupID clears the value of the "group_id" field. +func (u *MessageUpsertOne) ClearGroupID() *MessageUpsertOne { + return u.Update(func(s *MessageUpsert) { + s.ClearGroupID() + }) +} + +// Exec executes the query. +func (u *MessageUpsertOne) Exec(ctx context.Context) error { + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for MessageCreate.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *MessageUpsertOne) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} + +// Exec executes the UPSERT query and returns the inserted/updated ID. +func (u *MessageUpsertOne) ID(ctx context.Context) (id uuid.UUID, err error) { + if u.create.driver.Dialect() == dialect.MySQL { + // In case of "ON CONFLICT", there is no way to get back non-numeric ID + // fields from the database since MySQL does not support the RETURNING clause. + return id, errors.New("ent: MessageUpsertOne.ID is not supported by MySQL driver. Use MessageUpsertOne.Exec instead") + } + node, err := u.create.Save(ctx) + if err != nil { + return id, err + } + return node.ID, nil +} + +// IDX is like ID, but panics if an error occurs. +func (u *MessageUpsertOne) IDX(ctx context.Context) uuid.UUID { + id, err := u.ID(ctx) + if err != nil { + panic(err) + } + return id +} + +// MessageCreateBulk is the builder for creating many Message entities in bulk. +type MessageCreateBulk struct { + config + err error + builders []*MessageCreate + conflict []sql.ConflictOption +} + +// Save creates the Message entities in the database. +func (_c *MessageCreateBulk) Save(ctx context.Context) ([]*Message, error) { + if _c.err != nil { + return nil, _c.err + } + specs := make([]*sqlgraph.CreateSpec, len(_c.builders)) + nodes := make([]*Message, len(_c.builders)) + mutators := make([]Mutator, len(_c.builders)) + for i := range _c.builders { + func(i int, root context.Context) { + builder := _c.builders[i] + builder.defaults() + var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { + mutation, ok := m.(*MessageMutation) + if !ok { + return nil, fmt.Errorf("unexpected mutation type %T", m) + } + if err := builder.check(); err != nil { + return nil, err + } + builder.mutation = mutation + var err error + nodes[i], specs[i] = builder.createSpec() + if i < len(mutators)-1 { + _, err = mutators[i+1].Mutate(root, _c.builders[i+1].mutation) + } else { + spec := &sqlgraph.BatchCreateSpec{Nodes: specs} + spec.OnConflict = _c.conflict + // Invoke the actual operation on the latest mutation in the chain. + if err = sqlgraph.BatchCreate(ctx, _c.driver, spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + } + } + if err != nil { + return nil, err + } + mutation.id = &nodes[i].ID + mutation.done = true + return nodes[i], nil + }) + for i := len(builder.hooks) - 1; i >= 0; i-- { + mut = builder.hooks[i](mut) + } + mutators[i] = mut + }(i, ctx) + } + if len(mutators) > 0 { + if _, err := mutators[0].Mutate(ctx, _c.builders[0].mutation); err != nil { + return nil, err + } + } + return nodes, nil +} + +// SaveX is like Save, but panics if an error occurs. +func (_c *MessageCreateBulk) SaveX(ctx context.Context) []*Message { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *MessageCreateBulk) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *MessageCreateBulk) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.Message.CreateBulk(builders...). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.MessageUpsert) { +// SetProjectID(v+v). +// }). +// Exec(ctx) +func (_c *MessageCreateBulk) OnConflict(opts ...sql.ConflictOption) *MessageUpsertBulk { + _c.conflict = opts + return &MessageUpsertBulk{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.Message.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *MessageCreateBulk) OnConflictColumns(columns ...string) *MessageUpsertBulk { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &MessageUpsertBulk{ + create: _c, + } +} + +// MessageUpsertBulk is the builder for "upsert"-ing +// a bulk of Message nodes. +type MessageUpsertBulk struct { + create *MessageCreateBulk +} + +// UpdateNewValues updates the mutable fields using the new values that +// were set on create. Using this option is equivalent to using: +// +// client.Message.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(message.FieldID) +// }), +// ). +// Exec(ctx) +func (u *MessageUpsertBulk) UpdateNewValues() *MessageUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + for _, b := range u.create.builders { + if _, exists := b.mutation.ID(); exists { + s.SetIgnore(message.FieldID) + } + if _, exists := b.mutation.Created(); exists { + s.SetIgnore(message.FieldCreated) + } + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.Message.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *MessageUpsertBulk) Ignore() *MessageUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *MessageUpsertBulk) DoNothing() *MessageUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the MessageCreateBulk.OnConflict +// documentation for more info. +func (u *MessageUpsertBulk) Update(set func(*MessageUpsert)) *MessageUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&MessageUpsert{UpdateSet: update}) + })) + return u +} + +// SetProjectID sets the "project_id" field. +func (u *MessageUpsertBulk) SetProjectID(v uuid.UUID) *MessageUpsertBulk { + return u.Update(func(s *MessageUpsert) { + s.SetProjectID(v) + }) +} + +// UpdateProjectID sets the "project_id" field to the value that was provided on create. +func (u *MessageUpsertBulk) UpdateProjectID() *MessageUpsertBulk { + return u.Update(func(s *MessageUpsert) { + s.UpdateProjectID() + }) +} + +// SetSender sets the "sender" field. +func (u *MessageUpsertBulk) SetSender(v string) *MessageUpsertBulk { + return u.Update(func(s *MessageUpsert) { + s.SetSender(v) + }) +} + +// UpdateSender sets the "sender" field to the value that was provided on create. +func (u *MessageUpsertBulk) UpdateSender() *MessageUpsertBulk { + return u.Update(func(s *MessageUpsert) { + s.UpdateSender() + }) +} + +// SetSenderID sets the "sender_id" field. +func (u *MessageUpsertBulk) SetSenderID(v string) *MessageUpsertBulk { + return u.Update(func(s *MessageUpsert) { + s.SetSenderID(v) + }) +} + +// UpdateSenderID sets the "sender_id" field to the value that was provided on create. +func (u *MessageUpsertBulk) UpdateSenderID() *MessageUpsertBulk { + return u.Update(func(s *MessageUpsert) { + s.UpdateSenderID() + }) +} + +// ClearSenderID clears the value of the "sender_id" field. +func (u *MessageUpsertBulk) ClearSenderID() *MessageUpsertBulk { + return u.Update(func(s *MessageUpsert) { + s.ClearSenderID() + }) +} + +// SetRecipient sets the "recipient" field. +func (u *MessageUpsertBulk) SetRecipient(v string) *MessageUpsertBulk { + return u.Update(func(s *MessageUpsert) { + s.SetRecipient(v) + }) +} + +// UpdateRecipient sets the "recipient" field to the value that was provided on create. +func (u *MessageUpsertBulk) UpdateRecipient() *MessageUpsertBulk { + return u.Update(func(s *MessageUpsert) { + s.UpdateRecipient() + }) +} + +// SetRecipientID sets the "recipient_id" field. +func (u *MessageUpsertBulk) SetRecipientID(v string) *MessageUpsertBulk { + return u.Update(func(s *MessageUpsert) { + s.SetRecipientID(v) + }) +} + +// UpdateRecipientID sets the "recipient_id" field to the value that was provided on create. +func (u *MessageUpsertBulk) UpdateRecipientID() *MessageUpsertBulk { + return u.Update(func(s *MessageUpsert) { + s.UpdateRecipientID() + }) +} + +// ClearRecipientID clears the value of the "recipient_id" field. +func (u *MessageUpsertBulk) ClearRecipientID() *MessageUpsertBulk { + return u.Update(func(s *MessageUpsert) { + s.ClearRecipientID() + }) +} + +// SetMsg sets the "msg" field. +func (u *MessageUpsertBulk) SetMsg(v string) *MessageUpsertBulk { + return u.Update(func(s *MessageUpsert) { + s.SetMsg(v) + }) +} + +// UpdateMsg sets the "msg" field to the value that was provided on create. +func (u *MessageUpsertBulk) UpdateMsg() *MessageUpsertBulk { + return u.Update(func(s *MessageUpsert) { + s.UpdateMsg() + }) +} + +// SetType sets the "type" field. +func (u *MessageUpsertBulk) SetType(v string) *MessageUpsertBulk { + return u.Update(func(s *MessageUpsert) { + s.SetType(v) + }) +} + +// UpdateType sets the "type" field to the value that was provided on create. +func (u *MessageUpsertBulk) UpdateType() *MessageUpsertBulk { + return u.Update(func(s *MessageUpsert) { + s.UpdateType() + }) +} + +// SetUrgent sets the "urgent" field. +func (u *MessageUpsertBulk) SetUrgent(v bool) *MessageUpsertBulk { + return u.Update(func(s *MessageUpsert) { + s.SetUrgent(v) + }) +} + +// UpdateUrgent sets the "urgent" field to the value that was provided on create. +func (u *MessageUpsertBulk) UpdateUrgent() *MessageUpsertBulk { + return u.Update(func(s *MessageUpsert) { + s.UpdateUrgent() + }) +} + +// SetBroadcasted sets the "broadcasted" field. +func (u *MessageUpsertBulk) SetBroadcasted(v bool) *MessageUpsertBulk { + return u.Update(func(s *MessageUpsert) { + s.SetBroadcasted(v) + }) +} + +// UpdateBroadcasted sets the "broadcasted" field to the value that was provided on create. +func (u *MessageUpsertBulk) UpdateBroadcasted() *MessageUpsertBulk { + return u.Update(func(s *MessageUpsert) { + s.UpdateBroadcasted() + }) +} + +// SetRead sets the "read" field. +func (u *MessageUpsertBulk) SetRead(v bool) *MessageUpsertBulk { + return u.Update(func(s *MessageUpsert) { + s.SetRead(v) + }) +} + +// UpdateRead sets the "read" field to the value that was provided on create. +func (u *MessageUpsertBulk) UpdateRead() *MessageUpsertBulk { + return u.Update(func(s *MessageUpsert) { + s.UpdateRead() + }) +} + +// SetAgentID sets the "agent_id" field. +func (u *MessageUpsertBulk) SetAgentID(v string) *MessageUpsertBulk { + return u.Update(func(s *MessageUpsert) { + s.SetAgentID(v) + }) +} + +// UpdateAgentID sets the "agent_id" field to the value that was provided on create. +func (u *MessageUpsertBulk) UpdateAgentID() *MessageUpsertBulk { + return u.Update(func(s *MessageUpsert) { + s.UpdateAgentID() + }) +} + +// ClearAgentID clears the value of the "agent_id" field. +func (u *MessageUpsertBulk) ClearAgentID() *MessageUpsertBulk { + return u.Update(func(s *MessageUpsert) { + s.ClearAgentID() + }) +} + +// SetGroupID sets the "group_id" field. +func (u *MessageUpsertBulk) SetGroupID(v string) *MessageUpsertBulk { + return u.Update(func(s *MessageUpsert) { + s.SetGroupID(v) + }) +} + +// UpdateGroupID sets the "group_id" field to the value that was provided on create. +func (u *MessageUpsertBulk) UpdateGroupID() *MessageUpsertBulk { + return u.Update(func(s *MessageUpsert) { + s.UpdateGroupID() + }) +} + +// ClearGroupID clears the value of the "group_id" field. +func (u *MessageUpsertBulk) ClearGroupID() *MessageUpsertBulk { + return u.Update(func(s *MessageUpsert) { + s.ClearGroupID() + }) +} + +// Exec executes the query. +func (u *MessageUpsertBulk) Exec(ctx context.Context) error { + if u.create.err != nil { + return u.create.err + } + for i, b := range u.create.builders { + if len(b.conflict) != 0 { + return fmt.Errorf("ent: OnConflict was set for builder %d. Set it on the MessageCreateBulk instead", i) + } + } + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for MessageCreateBulk.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *MessageUpsertBulk) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/pkg/ent/message_delete.go b/pkg/ent/message_delete.go new file mode 100644 index 000000000..18db7ee5d --- /dev/null +++ b/pkg/ent/message_delete.go @@ -0,0 +1,88 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/message" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" +) + +// MessageDelete is the builder for deleting a Message entity. +type MessageDelete struct { + config + hooks []Hook + mutation *MessageMutation +} + +// Where appends a list predicates to the MessageDelete builder. +func (_d *MessageDelete) Where(ps ...predicate.Message) *MessageDelete { + _d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (_d *MessageDelete) Exec(ctx context.Context) (int, error) { + return withHooks(ctx, _d.sqlExec, _d.mutation, _d.hooks) +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *MessageDelete) ExecX(ctx context.Context) int { + n, err := _d.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (_d *MessageDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(message.Table, sqlgraph.NewFieldSpec(message.FieldID, field.TypeUUID)) + if ps := _d.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + affected, err := sqlgraph.DeleteNodes(ctx, _d.driver, _spec) + if err != nil && sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + _d.mutation.done = true + return affected, err +} + +// MessageDeleteOne is the builder for deleting a single Message entity. +type MessageDeleteOne struct { + _d *MessageDelete +} + +// Where appends a list predicates to the MessageDelete builder. +func (_d *MessageDeleteOne) Where(ps ...predicate.Message) *MessageDeleteOne { + _d._d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query. +func (_d *MessageDeleteOne) Exec(ctx context.Context) error { + n, err := _d._d.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{message.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *MessageDeleteOne) ExecX(ctx context.Context) { + if err := _d.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/pkg/ent/message_query.go b/pkg/ent/message_query.go new file mode 100644 index 000000000..3f0667250 --- /dev/null +++ b/pkg/ent/message_query.go @@ -0,0 +1,565 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "fmt" + "math" + + "entgo.io/ent" + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/message" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/google/uuid" +) + +// MessageQuery is the builder for querying Message entities. +type MessageQuery struct { + config + ctx *QueryContext + order []message.OrderOption + inters []Interceptor + predicates []predicate.Message + modifiers []func(*sql.Selector) + // intermediate query (i.e. traversal path). + sql *sql.Selector + path func(context.Context) (*sql.Selector, error) +} + +// Where adds a new predicate for the MessageQuery builder. +func (_q *MessageQuery) Where(ps ...predicate.Message) *MessageQuery { + _q.predicates = append(_q.predicates, ps...) + return _q +} + +// Limit the number of records to be returned by this query. +func (_q *MessageQuery) Limit(limit int) *MessageQuery { + _q.ctx.Limit = &limit + return _q +} + +// Offset to start from. +func (_q *MessageQuery) Offset(offset int) *MessageQuery { + _q.ctx.Offset = &offset + return _q +} + +// Unique configures the query builder to filter duplicate records on query. +// By default, unique is set to true, and can be disabled using this method. +func (_q *MessageQuery) Unique(unique bool) *MessageQuery { + _q.ctx.Unique = &unique + return _q +} + +// Order specifies how the records should be ordered. +func (_q *MessageQuery) Order(o ...message.OrderOption) *MessageQuery { + _q.order = append(_q.order, o...) + return _q +} + +// First returns the first Message entity from the query. +// Returns a *NotFoundError when no Message was found. +func (_q *MessageQuery) First(ctx context.Context) (*Message, error) { + nodes, err := _q.Limit(1).All(setContextOp(ctx, _q.ctx, ent.OpQueryFirst)) + if err != nil { + return nil, err + } + if len(nodes) == 0 { + return nil, &NotFoundError{message.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (_q *MessageQuery) FirstX(ctx context.Context) *Message { + node, err := _q.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// FirstID returns the first Message ID from the query. +// Returns a *NotFoundError when no Message ID was found. +func (_q *MessageQuery) FirstID(ctx context.Context) (id uuid.UUID, err error) { + var ids []uuid.UUID + if ids, err = _q.Limit(1).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryFirstID)); err != nil { + return + } + if len(ids) == 0 { + err = &NotFoundError{message.Label} + return + } + return ids[0], nil +} + +// FirstIDX is like FirstID, but panics if an error occurs. +func (_q *MessageQuery) FirstIDX(ctx context.Context) uuid.UUID { + id, err := _q.FirstID(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return id +} + +// Only returns a single Message entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one Message entity is found. +// Returns a *NotFoundError when no Message entities are found. +func (_q *MessageQuery) Only(ctx context.Context) (*Message, error) { + nodes, err := _q.Limit(2).All(setContextOp(ctx, _q.ctx, ent.OpQueryOnly)) + if err != nil { + return nil, err + } + switch len(nodes) { + case 1: + return nodes[0], nil + case 0: + return nil, &NotFoundError{message.Label} + default: + return nil, &NotSingularError{message.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (_q *MessageQuery) OnlyX(ctx context.Context) *Message { + node, err := _q.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// OnlyID is like Only, but returns the only Message ID in the query. +// Returns a *NotSingularError when more than one Message ID is found. +// Returns a *NotFoundError when no entities are found. +func (_q *MessageQuery) OnlyID(ctx context.Context) (id uuid.UUID, err error) { + var ids []uuid.UUID + if ids, err = _q.Limit(2).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryOnlyID)); err != nil { + return + } + switch len(ids) { + case 1: + id = ids[0] + case 0: + err = &NotFoundError{message.Label} + default: + err = &NotSingularError{message.Label} + } + return +} + +// OnlyIDX is like OnlyID, but panics if an error occurs. +func (_q *MessageQuery) OnlyIDX(ctx context.Context) uuid.UUID { + id, err := _q.OnlyID(ctx) + if err != nil { + panic(err) + } + return id +} + +// All executes the query and returns a list of Messages. +func (_q *MessageQuery) All(ctx context.Context) ([]*Message, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryAll) + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*Message, *MessageQuery]() + return withInterceptors[[]*Message](ctx, _q, qr, _q.inters) +} + +// AllX is like All, but panics if an error occurs. +func (_q *MessageQuery) AllX(ctx context.Context) []*Message { + nodes, err := _q.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// IDs executes the query and returns a list of Message IDs. +func (_q *MessageQuery) IDs(ctx context.Context) (ids []uuid.UUID, err error) { + if _q.ctx.Unique == nil && _q.path != nil { + _q.Unique(true) + } + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryIDs) + if err = _q.Select(message.FieldID).Scan(ctx, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// IDsX is like IDs, but panics if an error occurs. +func (_q *MessageQuery) IDsX(ctx context.Context) []uuid.UUID { + ids, err := _q.IDs(ctx) + if err != nil { + panic(err) + } + return ids +} + +// Count returns the count of the given query. +func (_q *MessageQuery) Count(ctx context.Context) (int, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryCount) + if err := _q.prepareQuery(ctx); err != nil { + return 0, err + } + return withInterceptors[int](ctx, _q, querierCount[*MessageQuery](), _q.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (_q *MessageQuery) CountX(ctx context.Context) int { + count, err := _q.Count(ctx) + if err != nil { + panic(err) + } + return count +} + +// Exist returns true if the query has elements in the graph. +func (_q *MessageQuery) Exist(ctx context.Context) (bool, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryExist) + switch _, err := _q.FirstID(ctx); { + case IsNotFound(err): + return false, nil + case err != nil: + return false, fmt.Errorf("ent: check existence: %w", err) + default: + return true, nil + } +} + +// ExistX is like Exist, but panics if an error occurs. +func (_q *MessageQuery) ExistX(ctx context.Context) bool { + exist, err := _q.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the MessageQuery builder, including all associated steps. It can be +// used to prepare common query builders and use them differently after the clone is made. +func (_q *MessageQuery) Clone() *MessageQuery { + if _q == nil { + return nil + } + return &MessageQuery{ + config: _q.config, + ctx: _q.ctx.Clone(), + order: append([]message.OrderOption{}, _q.order...), + inters: append([]Interceptor{}, _q.inters...), + predicates: append([]predicate.Message{}, _q.predicates...), + // clone intermediate query. + sql: _q.sql.Clone(), + path: _q.path, + } +} + +// GroupBy is used to group vertices by one or more fields/columns. +// It is often used with aggregate functions, like: count, max, mean, min, sum. +// +// Example: +// +// var v []struct { +// ProjectID uuid.UUID `json:"project_id,omitempty"` +// Count int `json:"count,omitempty"` +// } +// +// client.Message.Query(). +// GroupBy(message.FieldProjectID). +// Aggregate(ent.Count()). +// Scan(ctx, &v) +func (_q *MessageQuery) GroupBy(field string, fields ...string) *MessageGroupBy { + _q.ctx.Fields = append([]string{field}, fields...) + grbuild := &MessageGroupBy{build: _q} + grbuild.flds = &_q.ctx.Fields + grbuild.label = message.Label + grbuild.scan = grbuild.Scan + return grbuild +} + +// Select allows the selection one or more fields/columns for the given query, +// instead of selecting all fields in the entity. +// +// Example: +// +// var v []struct { +// ProjectID uuid.UUID `json:"project_id,omitempty"` +// } +// +// client.Message.Query(). +// Select(message.FieldProjectID). +// Scan(ctx, &v) +func (_q *MessageQuery) Select(fields ...string) *MessageSelect { + _q.ctx.Fields = append(_q.ctx.Fields, fields...) + sbuild := &MessageSelect{MessageQuery: _q} + sbuild.label = message.Label + sbuild.flds, sbuild.scan = &_q.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a MessageSelect configured with the given aggregations. +func (_q *MessageQuery) Aggregate(fns ...AggregateFunc) *MessageSelect { + return _q.Select().Aggregate(fns...) +} + +func (_q *MessageQuery) prepareQuery(ctx context.Context) error { + for _, inter := range _q.inters { + if inter == nil { + return fmt.Errorf("ent: uninitialized interceptor (forgotten import ent/runtime?)") + } + if trv, ok := inter.(Traverser); ok { + if err := trv.Traverse(ctx, _q); err != nil { + return err + } + } + } + for _, f := range _q.ctx.Fields { + if !message.ValidColumn(f) { + return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + } + if _q.path != nil { + prev, err := _q.path(ctx) + if err != nil { + return err + } + _q.sql = prev + } + return nil +} + +func (_q *MessageQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*Message, error) { + var ( + nodes = []*Message{} + _spec = _q.querySpec() + ) + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*Message).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &Message{config: _q.config} + nodes = append(nodes, node) + return node.assignValues(columns, values) + } + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } + for i := range hooks { + hooks[i](ctx, _spec) + } + if err := sqlgraph.QueryNodes(ctx, _q.driver, _spec); err != nil { + return nil, err + } + if len(nodes) == 0 { + return nodes, nil + } + return nodes, nil +} + +func (_q *MessageQuery) sqlCount(ctx context.Context) (int, error) { + _spec := _q.querySpec() + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } + _spec.Node.Columns = _q.ctx.Fields + if len(_q.ctx.Fields) > 0 { + _spec.Unique = _q.ctx.Unique != nil && *_q.ctx.Unique + } + return sqlgraph.CountNodes(ctx, _q.driver, _spec) +} + +func (_q *MessageQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(message.Table, message.Columns, sqlgraph.NewFieldSpec(message.FieldID, field.TypeUUID)) + _spec.From = _q.sql + if unique := _q.ctx.Unique; unique != nil { + _spec.Unique = *unique + } else if _q.path != nil { + _spec.Unique = true + } + if fields := _q.ctx.Fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, message.FieldID) + for i := range fields { + if fields[i] != message.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, fields[i]) + } + } + } + if ps := _q.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if limit := _q.ctx.Limit; limit != nil { + _spec.Limit = *limit + } + if offset := _q.ctx.Offset; offset != nil { + _spec.Offset = *offset + } + if ps := _q.order; len(ps) > 0 { + _spec.Order = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + return _spec +} + +func (_q *MessageQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(_q.driver.Dialect()) + t1 := builder.Table(message.Table) + columns := _q.ctx.Fields + if len(columns) == 0 { + columns = message.Columns + } + selector := builder.Select(t1.Columns(columns...)...).From(t1) + if _q.sql != nil { + selector = _q.sql + selector.Select(selector.Columns(columns...)...) + } + if _q.ctx.Unique != nil && *_q.ctx.Unique { + selector.Distinct() + } + for _, m := range _q.modifiers { + m(selector) + } + for _, p := range _q.predicates { + p(selector) + } + for _, p := range _q.order { + p(selector) + } + if offset := _q.ctx.Offset; offset != nil { + // limit is mandatory for offset clause. We start + // with default value, and override it below if needed. + selector.Offset(*offset).Limit(math.MaxInt32) + } + if limit := _q.ctx.Limit; limit != nil { + selector.Limit(*limit) + } + return selector +} + +// ForUpdate locks the selected rows against concurrent updates, and prevent them from being +// updated, deleted or "selected ... for update" by other sessions, until the transaction is +// either committed or rolled-back. +func (_q *MessageQuery) ForUpdate(opts ...sql.LockOption) *MessageQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForUpdate(opts...) + }) + return _q +} + +// ForShare behaves similarly to ForUpdate, except that it acquires a shared mode lock +// on any rows that are read. Other sessions can read the rows, but cannot modify them +// until your transaction commits. +func (_q *MessageQuery) ForShare(opts ...sql.LockOption) *MessageQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForShare(opts...) + }) + return _q +} + +// MessageGroupBy is the group-by builder for Message entities. +type MessageGroupBy struct { + selector + build *MessageQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (_g *MessageGroupBy) Aggregate(fns ...AggregateFunc) *MessageGroupBy { + _g.fns = append(_g.fns, fns...) + return _g +} + +// Scan applies the selector query and scans the result into the given value. +func (_g *MessageGroupBy) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, _g.build.ctx, ent.OpQueryGroupBy) + if err := _g.build.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*MessageQuery, *MessageGroupBy](ctx, _g.build, _g, _g.build.inters, v) +} + +func (_g *MessageGroupBy) sqlScan(ctx context.Context, root *MessageQuery, v any) error { + selector := root.sqlQuery(ctx).Select() + aggregation := make([]string, 0, len(_g.fns)) + for _, fn := range _g.fns { + aggregation = append(aggregation, fn(selector)) + } + if len(selector.SelectedColumns()) == 0 { + columns := make([]string, 0, len(*_g.flds)+len(_g.fns)) + for _, f := range *_g.flds { + columns = append(columns, selector.C(f)) + } + columns = append(columns, aggregation...) + selector.Select(columns...) + } + selector.GroupBy(selector.Columns(*_g.flds...)...) + if err := selector.Err(); err != nil { + return err + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := _g.build.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} + +// MessageSelect is the builder for selecting fields of Message entities. +type MessageSelect struct { + *MessageQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (_s *MessageSelect) Aggregate(fns ...AggregateFunc) *MessageSelect { + _s.fns = append(_s.fns, fns...) + return _s +} + +// Scan applies the selector query and scans the result into the given value. +func (_s *MessageSelect) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, _s.ctx, ent.OpQuerySelect) + if err := _s.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*MessageQuery, *MessageSelect](ctx, _s.MessageQuery, _s, _s.inters, v) +} + +func (_s *MessageSelect) sqlScan(ctx context.Context, root *MessageQuery, v any) error { + selector := root.sqlQuery(ctx) + aggregation := make([]string, 0, len(_s.fns)) + for _, fn := range _s.fns { + aggregation = append(aggregation, fn(selector)) + } + switch n := len(*_s.selector.flds); { + case n == 0 && len(aggregation) > 0: + selector.Select(aggregation...) + case n != 0 && len(aggregation) > 0: + selector.AppendSelect(aggregation...) + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := _s.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} diff --git a/pkg/ent/message_update.go b/pkg/ent/message_update.go new file mode 100644 index 000000000..0fdce57a7 --- /dev/null +++ b/pkg/ent/message_update.go @@ -0,0 +1,702 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/message" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/google/uuid" +) + +// MessageUpdate is the builder for updating Message entities. +type MessageUpdate struct { + config + hooks []Hook + mutation *MessageMutation +} + +// Where appends a list predicates to the MessageUpdate builder. +func (_u *MessageUpdate) Where(ps ...predicate.Message) *MessageUpdate { + _u.mutation.Where(ps...) + return _u +} + +// SetProjectID sets the "project_id" field. +func (_u *MessageUpdate) SetProjectID(v uuid.UUID) *MessageUpdate { + _u.mutation.SetProjectID(v) + return _u +} + +// SetNillableProjectID sets the "project_id" field if the given value is not nil. +func (_u *MessageUpdate) SetNillableProjectID(v *uuid.UUID) *MessageUpdate { + if v != nil { + _u.SetProjectID(*v) + } + return _u +} + +// SetSender sets the "sender" field. +func (_u *MessageUpdate) SetSender(v string) *MessageUpdate { + _u.mutation.SetSender(v) + return _u +} + +// SetNillableSender sets the "sender" field if the given value is not nil. +func (_u *MessageUpdate) SetNillableSender(v *string) *MessageUpdate { + if v != nil { + _u.SetSender(*v) + } + return _u +} + +// SetSenderID sets the "sender_id" field. +func (_u *MessageUpdate) SetSenderID(v string) *MessageUpdate { + _u.mutation.SetSenderID(v) + return _u +} + +// SetNillableSenderID sets the "sender_id" field if the given value is not nil. +func (_u *MessageUpdate) SetNillableSenderID(v *string) *MessageUpdate { + if v != nil { + _u.SetSenderID(*v) + } + return _u +} + +// ClearSenderID clears the value of the "sender_id" field. +func (_u *MessageUpdate) ClearSenderID() *MessageUpdate { + _u.mutation.ClearSenderID() + return _u +} + +// SetRecipient sets the "recipient" field. +func (_u *MessageUpdate) SetRecipient(v string) *MessageUpdate { + _u.mutation.SetRecipient(v) + return _u +} + +// SetNillableRecipient sets the "recipient" field if the given value is not nil. +func (_u *MessageUpdate) SetNillableRecipient(v *string) *MessageUpdate { + if v != nil { + _u.SetRecipient(*v) + } + return _u +} + +// SetRecipientID sets the "recipient_id" field. +func (_u *MessageUpdate) SetRecipientID(v string) *MessageUpdate { + _u.mutation.SetRecipientID(v) + return _u +} + +// SetNillableRecipientID sets the "recipient_id" field if the given value is not nil. +func (_u *MessageUpdate) SetNillableRecipientID(v *string) *MessageUpdate { + if v != nil { + _u.SetRecipientID(*v) + } + return _u +} + +// ClearRecipientID clears the value of the "recipient_id" field. +func (_u *MessageUpdate) ClearRecipientID() *MessageUpdate { + _u.mutation.ClearRecipientID() + return _u +} + +// SetMsg sets the "msg" field. +func (_u *MessageUpdate) SetMsg(v string) *MessageUpdate { + _u.mutation.SetMsg(v) + return _u +} + +// SetNillableMsg sets the "msg" field if the given value is not nil. +func (_u *MessageUpdate) SetNillableMsg(v *string) *MessageUpdate { + if v != nil { + _u.SetMsg(*v) + } + return _u +} + +// SetType sets the "type" field. +func (_u *MessageUpdate) SetType(v string) *MessageUpdate { + _u.mutation.SetType(v) + return _u +} + +// SetNillableType sets the "type" field if the given value is not nil. +func (_u *MessageUpdate) SetNillableType(v *string) *MessageUpdate { + if v != nil { + _u.SetType(*v) + } + return _u +} + +// SetUrgent sets the "urgent" field. +func (_u *MessageUpdate) SetUrgent(v bool) *MessageUpdate { + _u.mutation.SetUrgent(v) + return _u +} + +// SetNillableUrgent sets the "urgent" field if the given value is not nil. +func (_u *MessageUpdate) SetNillableUrgent(v *bool) *MessageUpdate { + if v != nil { + _u.SetUrgent(*v) + } + return _u +} + +// SetBroadcasted sets the "broadcasted" field. +func (_u *MessageUpdate) SetBroadcasted(v bool) *MessageUpdate { + _u.mutation.SetBroadcasted(v) + return _u +} + +// SetNillableBroadcasted sets the "broadcasted" field if the given value is not nil. +func (_u *MessageUpdate) SetNillableBroadcasted(v *bool) *MessageUpdate { + if v != nil { + _u.SetBroadcasted(*v) + } + return _u +} + +// SetRead sets the "read" field. +func (_u *MessageUpdate) SetRead(v bool) *MessageUpdate { + _u.mutation.SetRead(v) + return _u +} + +// SetNillableRead sets the "read" field if the given value is not nil. +func (_u *MessageUpdate) SetNillableRead(v *bool) *MessageUpdate { + if v != nil { + _u.SetRead(*v) + } + return _u +} + +// SetAgentID sets the "agent_id" field. +func (_u *MessageUpdate) SetAgentID(v string) *MessageUpdate { + _u.mutation.SetAgentID(v) + return _u +} + +// SetNillableAgentID sets the "agent_id" field if the given value is not nil. +func (_u *MessageUpdate) SetNillableAgentID(v *string) *MessageUpdate { + if v != nil { + _u.SetAgentID(*v) + } + return _u +} + +// ClearAgentID clears the value of the "agent_id" field. +func (_u *MessageUpdate) ClearAgentID() *MessageUpdate { + _u.mutation.ClearAgentID() + return _u +} + +// SetGroupID sets the "group_id" field. +func (_u *MessageUpdate) SetGroupID(v string) *MessageUpdate { + _u.mutation.SetGroupID(v) + return _u +} + +// SetNillableGroupID sets the "group_id" field if the given value is not nil. +func (_u *MessageUpdate) SetNillableGroupID(v *string) *MessageUpdate { + if v != nil { + _u.SetGroupID(*v) + } + return _u +} + +// ClearGroupID clears the value of the "group_id" field. +func (_u *MessageUpdate) ClearGroupID() *MessageUpdate { + _u.mutation.ClearGroupID() + return _u +} + +// Mutation returns the MessageMutation object of the builder. +func (_u *MessageUpdate) Mutation() *MessageMutation { + return _u.mutation +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (_u *MessageUpdate) Save(ctx context.Context) (int, error) { + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *MessageUpdate) SaveX(ctx context.Context) int { + affected, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (_u *MessageUpdate) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *MessageUpdate) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_u *MessageUpdate) check() error { + if v, ok := _u.mutation.Sender(); ok { + if err := message.SenderValidator(v); err != nil { + return &ValidationError{Name: "sender", err: fmt.Errorf(`ent: validator failed for field "Message.sender": %w`, err)} + } + } + if v, ok := _u.mutation.Recipient(); ok { + if err := message.RecipientValidator(v); err != nil { + return &ValidationError{Name: "recipient", err: fmt.Errorf(`ent: validator failed for field "Message.recipient": %w`, err)} + } + } + if v, ok := _u.mutation.Msg(); ok { + if err := message.MsgValidator(v); err != nil { + return &ValidationError{Name: "msg", err: fmt.Errorf(`ent: validator failed for field "Message.msg": %w`, err)} + } + } + return nil +} + +func (_u *MessageUpdate) sqlSave(ctx context.Context) (_node int, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(message.Table, message.Columns, sqlgraph.NewFieldSpec(message.FieldID, field.TypeUUID)) + if ps := _u.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := _u.mutation.ProjectID(); ok { + _spec.SetField(message.FieldProjectID, field.TypeUUID, value) + } + if value, ok := _u.mutation.Sender(); ok { + _spec.SetField(message.FieldSender, field.TypeString, value) + } + if value, ok := _u.mutation.SenderID(); ok { + _spec.SetField(message.FieldSenderID, field.TypeString, value) + } + if _u.mutation.SenderIDCleared() { + _spec.ClearField(message.FieldSenderID, field.TypeString) + } + if value, ok := _u.mutation.Recipient(); ok { + _spec.SetField(message.FieldRecipient, field.TypeString, value) + } + if value, ok := _u.mutation.RecipientID(); ok { + _spec.SetField(message.FieldRecipientID, field.TypeString, value) + } + if _u.mutation.RecipientIDCleared() { + _spec.ClearField(message.FieldRecipientID, field.TypeString) + } + if value, ok := _u.mutation.Msg(); ok { + _spec.SetField(message.FieldMsg, field.TypeString, value) + } + if value, ok := _u.mutation.GetType(); ok { + _spec.SetField(message.FieldType, field.TypeString, value) + } + if value, ok := _u.mutation.Urgent(); ok { + _spec.SetField(message.FieldUrgent, field.TypeBool, value) + } + if value, ok := _u.mutation.Broadcasted(); ok { + _spec.SetField(message.FieldBroadcasted, field.TypeBool, value) + } + if value, ok := _u.mutation.Read(); ok { + _spec.SetField(message.FieldRead, field.TypeBool, value) + } + if value, ok := _u.mutation.AgentID(); ok { + _spec.SetField(message.FieldAgentID, field.TypeString, value) + } + if _u.mutation.AgentIDCleared() { + _spec.ClearField(message.FieldAgentID, field.TypeString) + } + if value, ok := _u.mutation.GroupID(); ok { + _spec.SetField(message.FieldGroupID, field.TypeString, value) + } + if _u.mutation.GroupIDCleared() { + _spec.ClearField(message.FieldGroupID, field.TypeString) + } + if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{message.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + _u.mutation.done = true + return _node, nil +} + +// MessageUpdateOne is the builder for updating a single Message entity. +type MessageUpdateOne struct { + config + fields []string + hooks []Hook + mutation *MessageMutation +} + +// SetProjectID sets the "project_id" field. +func (_u *MessageUpdateOne) SetProjectID(v uuid.UUID) *MessageUpdateOne { + _u.mutation.SetProjectID(v) + return _u +} + +// SetNillableProjectID sets the "project_id" field if the given value is not nil. +func (_u *MessageUpdateOne) SetNillableProjectID(v *uuid.UUID) *MessageUpdateOne { + if v != nil { + _u.SetProjectID(*v) + } + return _u +} + +// SetSender sets the "sender" field. +func (_u *MessageUpdateOne) SetSender(v string) *MessageUpdateOne { + _u.mutation.SetSender(v) + return _u +} + +// SetNillableSender sets the "sender" field if the given value is not nil. +func (_u *MessageUpdateOne) SetNillableSender(v *string) *MessageUpdateOne { + if v != nil { + _u.SetSender(*v) + } + return _u +} + +// SetSenderID sets the "sender_id" field. +func (_u *MessageUpdateOne) SetSenderID(v string) *MessageUpdateOne { + _u.mutation.SetSenderID(v) + return _u +} + +// SetNillableSenderID sets the "sender_id" field if the given value is not nil. +func (_u *MessageUpdateOne) SetNillableSenderID(v *string) *MessageUpdateOne { + if v != nil { + _u.SetSenderID(*v) + } + return _u +} + +// ClearSenderID clears the value of the "sender_id" field. +func (_u *MessageUpdateOne) ClearSenderID() *MessageUpdateOne { + _u.mutation.ClearSenderID() + return _u +} + +// SetRecipient sets the "recipient" field. +func (_u *MessageUpdateOne) SetRecipient(v string) *MessageUpdateOne { + _u.mutation.SetRecipient(v) + return _u +} + +// SetNillableRecipient sets the "recipient" field if the given value is not nil. +func (_u *MessageUpdateOne) SetNillableRecipient(v *string) *MessageUpdateOne { + if v != nil { + _u.SetRecipient(*v) + } + return _u +} + +// SetRecipientID sets the "recipient_id" field. +func (_u *MessageUpdateOne) SetRecipientID(v string) *MessageUpdateOne { + _u.mutation.SetRecipientID(v) + return _u +} + +// SetNillableRecipientID sets the "recipient_id" field if the given value is not nil. +func (_u *MessageUpdateOne) SetNillableRecipientID(v *string) *MessageUpdateOne { + if v != nil { + _u.SetRecipientID(*v) + } + return _u +} + +// ClearRecipientID clears the value of the "recipient_id" field. +func (_u *MessageUpdateOne) ClearRecipientID() *MessageUpdateOne { + _u.mutation.ClearRecipientID() + return _u +} + +// SetMsg sets the "msg" field. +func (_u *MessageUpdateOne) SetMsg(v string) *MessageUpdateOne { + _u.mutation.SetMsg(v) + return _u +} + +// SetNillableMsg sets the "msg" field if the given value is not nil. +func (_u *MessageUpdateOne) SetNillableMsg(v *string) *MessageUpdateOne { + if v != nil { + _u.SetMsg(*v) + } + return _u +} + +// SetType sets the "type" field. +func (_u *MessageUpdateOne) SetType(v string) *MessageUpdateOne { + _u.mutation.SetType(v) + return _u +} + +// SetNillableType sets the "type" field if the given value is not nil. +func (_u *MessageUpdateOne) SetNillableType(v *string) *MessageUpdateOne { + if v != nil { + _u.SetType(*v) + } + return _u +} + +// SetUrgent sets the "urgent" field. +func (_u *MessageUpdateOne) SetUrgent(v bool) *MessageUpdateOne { + _u.mutation.SetUrgent(v) + return _u +} + +// SetNillableUrgent sets the "urgent" field if the given value is not nil. +func (_u *MessageUpdateOne) SetNillableUrgent(v *bool) *MessageUpdateOne { + if v != nil { + _u.SetUrgent(*v) + } + return _u +} + +// SetBroadcasted sets the "broadcasted" field. +func (_u *MessageUpdateOne) SetBroadcasted(v bool) *MessageUpdateOne { + _u.mutation.SetBroadcasted(v) + return _u +} + +// SetNillableBroadcasted sets the "broadcasted" field if the given value is not nil. +func (_u *MessageUpdateOne) SetNillableBroadcasted(v *bool) *MessageUpdateOne { + if v != nil { + _u.SetBroadcasted(*v) + } + return _u +} + +// SetRead sets the "read" field. +func (_u *MessageUpdateOne) SetRead(v bool) *MessageUpdateOne { + _u.mutation.SetRead(v) + return _u +} + +// SetNillableRead sets the "read" field if the given value is not nil. +func (_u *MessageUpdateOne) SetNillableRead(v *bool) *MessageUpdateOne { + if v != nil { + _u.SetRead(*v) + } + return _u +} + +// SetAgentID sets the "agent_id" field. +func (_u *MessageUpdateOne) SetAgentID(v string) *MessageUpdateOne { + _u.mutation.SetAgentID(v) + return _u +} + +// SetNillableAgentID sets the "agent_id" field if the given value is not nil. +func (_u *MessageUpdateOne) SetNillableAgentID(v *string) *MessageUpdateOne { + if v != nil { + _u.SetAgentID(*v) + } + return _u +} + +// ClearAgentID clears the value of the "agent_id" field. +func (_u *MessageUpdateOne) ClearAgentID() *MessageUpdateOne { + _u.mutation.ClearAgentID() + return _u +} + +// SetGroupID sets the "group_id" field. +func (_u *MessageUpdateOne) SetGroupID(v string) *MessageUpdateOne { + _u.mutation.SetGroupID(v) + return _u +} + +// SetNillableGroupID sets the "group_id" field if the given value is not nil. +func (_u *MessageUpdateOne) SetNillableGroupID(v *string) *MessageUpdateOne { + if v != nil { + _u.SetGroupID(*v) + } + return _u +} + +// ClearGroupID clears the value of the "group_id" field. +func (_u *MessageUpdateOne) ClearGroupID() *MessageUpdateOne { + _u.mutation.ClearGroupID() + return _u +} + +// Mutation returns the MessageMutation object of the builder. +func (_u *MessageUpdateOne) Mutation() *MessageMutation { + return _u.mutation +} + +// Where appends a list predicates to the MessageUpdate builder. +func (_u *MessageUpdateOne) Where(ps ...predicate.Message) *MessageUpdateOne { + _u.mutation.Where(ps...) + return _u +} + +// Select allows selecting one or more fields (columns) of the returned entity. +// The default is selecting all fields defined in the entity schema. +func (_u *MessageUpdateOne) Select(field string, fields ...string) *MessageUpdateOne { + _u.fields = append([]string{field}, fields...) + return _u +} + +// Save executes the query and returns the updated Message entity. +func (_u *MessageUpdateOne) Save(ctx context.Context) (*Message, error) { + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *MessageUpdateOne) SaveX(ctx context.Context) *Message { + node, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (_u *MessageUpdateOne) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *MessageUpdateOne) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_u *MessageUpdateOne) check() error { + if v, ok := _u.mutation.Sender(); ok { + if err := message.SenderValidator(v); err != nil { + return &ValidationError{Name: "sender", err: fmt.Errorf(`ent: validator failed for field "Message.sender": %w`, err)} + } + } + if v, ok := _u.mutation.Recipient(); ok { + if err := message.RecipientValidator(v); err != nil { + return &ValidationError{Name: "recipient", err: fmt.Errorf(`ent: validator failed for field "Message.recipient": %w`, err)} + } + } + if v, ok := _u.mutation.Msg(); ok { + if err := message.MsgValidator(v); err != nil { + return &ValidationError{Name: "msg", err: fmt.Errorf(`ent: validator failed for field "Message.msg": %w`, err)} + } + } + return nil +} + +func (_u *MessageUpdateOne) sqlSave(ctx context.Context) (_node *Message, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(message.Table, message.Columns, sqlgraph.NewFieldSpec(message.FieldID, field.TypeUUID)) + id, ok := _u.mutation.ID() + if !ok { + return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "Message.id" for update`)} + } + _spec.Node.ID.Value = id + if fields := _u.fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, message.FieldID) + for _, f := range fields { + if !message.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + if f != message.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, f) + } + } + } + if ps := _u.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := _u.mutation.ProjectID(); ok { + _spec.SetField(message.FieldProjectID, field.TypeUUID, value) + } + if value, ok := _u.mutation.Sender(); ok { + _spec.SetField(message.FieldSender, field.TypeString, value) + } + if value, ok := _u.mutation.SenderID(); ok { + _spec.SetField(message.FieldSenderID, field.TypeString, value) + } + if _u.mutation.SenderIDCleared() { + _spec.ClearField(message.FieldSenderID, field.TypeString) + } + if value, ok := _u.mutation.Recipient(); ok { + _spec.SetField(message.FieldRecipient, field.TypeString, value) + } + if value, ok := _u.mutation.RecipientID(); ok { + _spec.SetField(message.FieldRecipientID, field.TypeString, value) + } + if _u.mutation.RecipientIDCleared() { + _spec.ClearField(message.FieldRecipientID, field.TypeString) + } + if value, ok := _u.mutation.Msg(); ok { + _spec.SetField(message.FieldMsg, field.TypeString, value) + } + if value, ok := _u.mutation.GetType(); ok { + _spec.SetField(message.FieldType, field.TypeString, value) + } + if value, ok := _u.mutation.Urgent(); ok { + _spec.SetField(message.FieldUrgent, field.TypeBool, value) + } + if value, ok := _u.mutation.Broadcasted(); ok { + _spec.SetField(message.FieldBroadcasted, field.TypeBool, value) + } + if value, ok := _u.mutation.Read(); ok { + _spec.SetField(message.FieldRead, field.TypeBool, value) + } + if value, ok := _u.mutation.AgentID(); ok { + _spec.SetField(message.FieldAgentID, field.TypeString, value) + } + if _u.mutation.AgentIDCleared() { + _spec.ClearField(message.FieldAgentID, field.TypeString) + } + if value, ok := _u.mutation.GroupID(); ok { + _spec.SetField(message.FieldGroupID, field.TypeString, value) + } + if _u.mutation.GroupIDCleared() { + _spec.ClearField(message.FieldGroupID, field.TypeString) + } + _node = &Message{config: _u.config} + _spec.Assign = _node.assignValues + _spec.ScanValues = _node.scanValues + if err = sqlgraph.UpdateNode(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{message.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + _u.mutation.done = true + return _node, nil +} diff --git a/pkg/ent/migrate/schema.go b/pkg/ent/migrate/schema.go index 5b465bcc1..c12fac15e 100644 --- a/pkg/ent/migrate/schema.go +++ b/pkg/ent/migrate/schema.go @@ -43,8 +43,33 @@ var ( {Name: "status", Type: field.TypeEnum, Enums: []string{"created", "provisioning", "cloning", "starting", "running", "suspended", "stopping", "stopped", "error"}, Default: "created"}, {Name: "delegation_enabled", Type: field.TypeBool, Default: false}, {Name: "visibility", Type: field.TypeString, Default: "private"}, + {Name: "labels", Type: field.TypeJSON, Nullable: true}, + {Name: "annotations", Type: field.TypeJSON, Nullable: true}, + {Name: "phase", Type: field.TypeString, Nullable: true}, + {Name: "activity", Type: field.TypeString, Nullable: true}, + {Name: "tool_name", Type: field.TypeString, Nullable: true}, + {Name: "connection_state", Type: field.TypeString, Nullable: true}, + {Name: "container_status", Type: field.TypeString, Nullable: true}, + {Name: "runtime_state", Type: field.TypeString, Nullable: true}, + {Name: "stalled_from_activity", Type: field.TypeString, Nullable: true}, + {Name: "current_turns", Type: field.TypeInt, Default: 0}, + {Name: "current_model_calls", Type: field.TypeInt, Default: 0}, + {Name: "image", Type: field.TypeString, Nullable: true}, + {Name: "detached", Type: field.TypeBool, Default: false}, + {Name: "runtime", Type: field.TypeString, Nullable: true}, + {Name: "runtime_broker_id", Type: field.TypeString, Nullable: true}, + {Name: "web_pty_enabled", Type: field.TypeBool, Default: false}, + {Name: "task_summary", Type: field.TypeString, Nullable: true}, + {Name: "message", Type: field.TypeString, Nullable: true}, + {Name: "applied_config", Type: field.TypeString, Nullable: true, Size: 2147483647}, + {Name: "ancestry", Type: field.TypeJSON, Nullable: true}, {Name: "created", Type: field.TypeTime}, {Name: "updated", Type: field.TypeTime}, + {Name: "last_seen", Type: field.TypeTime, Nullable: true}, + {Name: "last_activity_event", Type: field.TypeTime, Nullable: true}, + {Name: "started_at", Type: field.TypeTime, Nullable: true}, + {Name: "deleted_at", Type: field.TypeTime, Nullable: true}, + {Name: "state_version", Type: field.TypeInt64, Default: 1}, {Name: "project_id", Type: field.TypeUUID}, {Name: "created_by", Type: field.TypeUUID, Nullable: true}, {Name: "owner_id", Type: field.TypeUUID, Nullable: true}, @@ -57,19 +82,19 @@ var ( ForeignKeys: []*schema.ForeignKey{ { Symbol: "agents_projects_agents", - Columns: []*schema.Column{AgentsColumns[9]}, + Columns: []*schema.Column{AgentsColumns[34]}, RefColumns: []*schema.Column{ProjectsColumns[0]}, OnDelete: schema.NoAction, }, { Symbol: "agents_users_created_agents", - Columns: []*schema.Column{AgentsColumns[10]}, + Columns: []*schema.Column{AgentsColumns[35]}, RefColumns: []*schema.Column{UsersColumns[0]}, OnDelete: schema.SetNull, }, { Symbol: "agents_users_owned_agents", - Columns: []*schema.Column{AgentsColumns[11]}, + Columns: []*schema.Column{AgentsColumns[36]}, RefColumns: []*schema.Column{UsersColumns[0]}, OnDelete: schema.SetNull, }, @@ -78,10 +103,179 @@ var ( { Name: "agent_slug_project_id", Unique: true, - Columns: []*schema.Column{AgentsColumns[1], AgentsColumns[9]}, + Columns: []*schema.Column{AgentsColumns[1], AgentsColumns[34]}, + }, + }, + } + // AllowListColumns holds the columns for the "allow_list" table. + AllowListColumns = []*schema.Column{ + {Name: "id", Type: field.TypeUUID}, + {Name: "email", Type: field.TypeString, Unique: true}, + {Name: "note", Type: field.TypeString, Default: ""}, + {Name: "added_by", Type: field.TypeString}, + {Name: "invite_id", Type: field.TypeString, Nullable: true}, + {Name: "created", Type: field.TypeTime}, + } + // AllowListTable holds the schema information for the "allow_list" table. + AllowListTable = &schema.Table{ + Name: "allow_list", + Columns: AllowListColumns, + PrimaryKey: []*schema.Column{AllowListColumns[0]}, + Indexes: []*schema.Index{ + { + Name: "allowlistentry_created_id", + Unique: false, + Columns: []*schema.Column{AllowListColumns[5], AllowListColumns[0]}, + }, + }, + } + // APIKeysColumns holds the columns for the "api_keys" table. + APIKeysColumns = []*schema.Column{ + {Name: "id", Type: field.TypeUUID}, + {Name: "user_id", Type: field.TypeUUID}, + {Name: "name", Type: field.TypeString, Nullable: true}, + {Name: "prefix", Type: field.TypeString, Nullable: true}, + {Name: "key_hash", Type: field.TypeString, Unique: true}, + {Name: "scopes", Type: field.TypeString, Nullable: true}, + {Name: "revoked", Type: field.TypeBool, Default: false}, + {Name: "expires_at", Type: field.TypeTime, Nullable: true}, + {Name: "last_used", Type: field.TypeTime, Nullable: true}, + {Name: "created", Type: field.TypeTime}, + } + // APIKeysTable holds the schema information for the "api_keys" table. + APIKeysTable = &schema.Table{ + Name: "api_keys", + Columns: APIKeysColumns, + PrimaryKey: []*schema.Column{APIKeysColumns[0]}, + Indexes: []*schema.Index{ + { + Name: "apikey_user_id", + Unique: false, + Columns: []*schema.Column{APIKeysColumns[1]}, }, }, } + // BrokerJoinTokensColumns holds the columns for the "broker_join_tokens" table. + BrokerJoinTokensColumns = []*schema.Column{ + {Name: "broker_id", Type: field.TypeUUID}, + {Name: "token_hash", Type: field.TypeString, Unique: true}, + {Name: "expires_at", Type: field.TypeTime}, + {Name: "created_by", Type: field.TypeString}, + {Name: "created", Type: field.TypeTime}, + } + // BrokerJoinTokensTable holds the schema information for the "broker_join_tokens" table. + BrokerJoinTokensTable = &schema.Table{ + Name: "broker_join_tokens", + Columns: BrokerJoinTokensColumns, + PrimaryKey: []*schema.Column{BrokerJoinTokensColumns[0]}, + Indexes: []*schema.Index{ + { + Name: "brokerjointoken_expires_at", + Unique: false, + Columns: []*schema.Column{BrokerJoinTokensColumns[2]}, + }, + }, + } + // BrokerSecretsColumns holds the columns for the "broker_secrets" table. + BrokerSecretsColumns = []*schema.Column{ + {Name: "broker_id", Type: field.TypeUUID}, + {Name: "secret_key", Type: field.TypeBytes}, + {Name: "algorithm", Type: field.TypeString, Default: "hmac-sha256"}, + {Name: "rotated_at", Type: field.TypeTime, Nullable: true}, + {Name: "expires_at", Type: field.TypeTime, Nullable: true}, + {Name: "status", Type: field.TypeString, Default: "active"}, + {Name: "created", Type: field.TypeTime}, + } + // BrokerSecretsTable holds the schema information for the "broker_secrets" table. + BrokerSecretsTable = &schema.Table{ + Name: "broker_secrets", + Columns: BrokerSecretsColumns, + PrimaryKey: []*schema.Column{BrokerSecretsColumns[0]}, + } + // EnvVarsColumns holds the columns for the "env_vars" table. + EnvVarsColumns = []*schema.Column{ + {Name: "id", Type: field.TypeUUID}, + {Name: "key", Type: field.TypeString}, + {Name: "value", Type: field.TypeString}, + {Name: "scope", Type: field.TypeString}, + {Name: "scope_id", Type: field.TypeString}, + {Name: "description", Type: field.TypeString, Nullable: true}, + {Name: "sensitive", Type: field.TypeBool, Default: false}, + {Name: "injection_mode", Type: field.TypeEnum, Enums: []string{"always", "as_needed"}, Default: "as_needed"}, + {Name: "secret", Type: field.TypeBool, Default: false}, + {Name: "created_by", Type: field.TypeString, Nullable: true}, + {Name: "created", Type: field.TypeTime}, + {Name: "updated", Type: field.TypeTime}, + } + // EnvVarsTable holds the schema information for the "env_vars" table. + EnvVarsTable = &schema.Table{ + Name: "env_vars", + Columns: EnvVarsColumns, + PrimaryKey: []*schema.Column{EnvVarsColumns[0]}, + Indexes: []*schema.Index{ + { + Name: "envvar_key_scope_scope_id", + Unique: true, + Columns: []*schema.Column{EnvVarsColumns[1], EnvVarsColumns[3], EnvVarsColumns[4]}, + }, + { + Name: "envvar_scope_scope_id", + Unique: false, + Columns: []*schema.Column{EnvVarsColumns[3], EnvVarsColumns[4]}, + }, + }, + } + // GcpServiceAccountsColumns holds the columns for the "gcp_service_accounts" table. + GcpServiceAccountsColumns = []*schema.Column{ + {Name: "id", Type: field.TypeUUID}, + {Name: "scope", Type: field.TypeString}, + {Name: "scope_id", Type: field.TypeString}, + {Name: "email", Type: field.TypeString}, + {Name: "project_id", Type: field.TypeString}, + {Name: "display_name", Type: field.TypeString, Default: ""}, + {Name: "default_scopes", Type: field.TypeString, Default: ""}, + {Name: "verified", Type: field.TypeBool, Default: false}, + {Name: "verified_at", Type: field.TypeTime, Nullable: true}, + {Name: "created_by", Type: field.TypeString, Default: ""}, + {Name: "managed", Type: field.TypeBool, Default: false}, + {Name: "managed_by", Type: field.TypeString, Default: ""}, + {Name: "created", Type: field.TypeTime}, + } + // GcpServiceAccountsTable holds the schema information for the "gcp_service_accounts" table. + GcpServiceAccountsTable = &schema.Table{ + Name: "gcp_service_accounts", + Columns: GcpServiceAccountsColumns, + PrimaryKey: []*schema.Column{GcpServiceAccountsColumns[0]}, + Indexes: []*schema.Index{ + { + Name: "gcpserviceaccount_email_scope_scope_id", + Unique: true, + Columns: []*schema.Column{GcpServiceAccountsColumns[3], GcpServiceAccountsColumns[1], GcpServiceAccountsColumns[2]}, + }, + { + Name: "gcpserviceaccount_scope_scope_id", + Unique: false, + Columns: []*schema.Column{GcpServiceAccountsColumns[1], GcpServiceAccountsColumns[2]}, + }, + }, + } + // GithubInstallationsColumns holds the columns for the "github_installations" table. + GithubInstallationsColumns = []*schema.Column{ + {Name: "installation_id", Type: field.TypeInt64, Increment: true}, + {Name: "account_login", Type: field.TypeString}, + {Name: "account_type", Type: field.TypeString, Default: "Organization"}, + {Name: "app_id", Type: field.TypeInt64}, + {Name: "repositories", Type: field.TypeString, Default: "[]"}, + {Name: "status", Type: field.TypeString, Default: "active"}, + {Name: "created", Type: field.TypeTime}, + {Name: "updated", Type: field.TypeTime}, + } + // GithubInstallationsTable holds the schema information for the "github_installations" table. + GithubInstallationsTable = &schema.Table{ + Name: "github_installations", + Columns: GithubInstallationsColumns, + PrimaryKey: []*schema.Column{GithubInstallationsColumns[0]}, + } // GroupsColumns holds the columns for the "groups" table. GroupsColumns = []*schema.Column{ {Name: "id", Type: field.TypeUUID}, @@ -159,6 +353,232 @@ var ( }, }, } + // HarnessConfigsColumns holds the columns for the "harness_configs" table. + HarnessConfigsColumns = []*schema.Column{ + {Name: "id", Type: field.TypeUUID}, + {Name: "name", Type: field.TypeString}, + {Name: "slug", Type: field.TypeString}, + {Name: "display_name", Type: field.TypeString, Nullable: true}, + {Name: "description", Type: field.TypeString, Nullable: true}, + {Name: "harness", Type: field.TypeString}, + {Name: "config", Type: field.TypeString, Nullable: true}, + {Name: "content_hash", Type: field.TypeString, Nullable: true}, + {Name: "scope", Type: field.TypeString, Default: "global"}, + {Name: "scope_id", Type: field.TypeString, Nullable: true}, + {Name: "storage_uri", Type: field.TypeString, Nullable: true}, + {Name: "storage_bucket", Type: field.TypeString, Nullable: true}, + {Name: "storage_path", Type: field.TypeString, Nullable: true}, + {Name: "files", Type: field.TypeString, Nullable: true}, + {Name: "locked", Type: field.TypeBool, Default: false}, + {Name: "status", Type: field.TypeEnum, Enums: []string{"pending", "active", "archived"}, Default: "active"}, + {Name: "owner_id", Type: field.TypeString, Nullable: true}, + {Name: "created_by", Type: field.TypeString, Nullable: true}, + {Name: "updated_by", Type: field.TypeString, Nullable: true}, + {Name: "visibility", Type: field.TypeString, Default: "private"}, + {Name: "created", Type: field.TypeTime}, + {Name: "updated", Type: field.TypeTime}, + } + // HarnessConfigsTable holds the schema information for the "harness_configs" table. + HarnessConfigsTable = &schema.Table{ + Name: "harness_configs", + Columns: HarnessConfigsColumns, + PrimaryKey: []*schema.Column{HarnessConfigsColumns[0]}, + Indexes: []*schema.Index{ + { + Name: "harnessconfig_slug_scope", + Unique: false, + Columns: []*schema.Column{HarnessConfigsColumns[2], HarnessConfigsColumns[8]}, + }, + { + Name: "harnessconfig_harness", + Unique: false, + Columns: []*schema.Column{HarnessConfigsColumns[5]}, + }, + { + Name: "harnessconfig_status", + Unique: false, + Columns: []*schema.Column{HarnessConfigsColumns[15]}, + }, + { + Name: "harnessconfig_content_hash", + Unique: false, + Columns: []*schema.Column{HarnessConfigsColumns[7]}, + }, + }, + } + // InviteCodesColumns holds the columns for the "invite_codes" table. + InviteCodesColumns = []*schema.Column{ + {Name: "id", Type: field.TypeUUID}, + {Name: "code_hash", Type: field.TypeString, Unique: true}, + {Name: "code_prefix", Type: field.TypeString}, + {Name: "max_uses", Type: field.TypeInt, Default: 1}, + {Name: "use_count", Type: field.TypeInt, Default: 0}, + {Name: "expires_at", Type: field.TypeTime}, + {Name: "revoked", Type: field.TypeBool, Default: false}, + {Name: "created_by", Type: field.TypeString}, + {Name: "note", Type: field.TypeString, Default: ""}, + {Name: "created", Type: field.TypeTime}, + } + // InviteCodesTable holds the schema information for the "invite_codes" table. + InviteCodesTable = &schema.Table{ + Name: "invite_codes", + Columns: InviteCodesColumns, + PrimaryKey: []*schema.Column{InviteCodesColumns[0]}, + Indexes: []*schema.Index{ + { + Name: "invitecode_expires_at", + Unique: false, + Columns: []*schema.Column{InviteCodesColumns[5]}, + }, + }, + } + // MaintenanceOperationsColumns holds the columns for the "maintenance_operations" table. + MaintenanceOperationsColumns = []*schema.Column{ + {Name: "id", Type: field.TypeUUID}, + {Name: "key", Type: field.TypeString, Unique: true}, + {Name: "title", Type: field.TypeString}, + {Name: "description", Type: field.TypeString, Default: ""}, + {Name: "category", Type: field.TypeString}, + {Name: "status", Type: field.TypeString, Default: "pending"}, + {Name: "started_at", Type: field.TypeTime, Nullable: true}, + {Name: "completed_at", Type: field.TypeTime, Nullable: true}, + {Name: "started_by", Type: field.TypeString, Nullable: true}, + {Name: "result", Type: field.TypeString, Nullable: true}, + {Name: "metadata", Type: field.TypeString, Default: "{}"}, + {Name: "created", Type: field.TypeTime}, + } + // MaintenanceOperationsTable holds the schema information for the "maintenance_operations" table. + MaintenanceOperationsTable = &schema.Table{ + Name: "maintenance_operations", + Columns: MaintenanceOperationsColumns, + PrimaryKey: []*schema.Column{MaintenanceOperationsColumns[0]}, + } + // MaintenanceOperationRunsColumns holds the columns for the "maintenance_operation_runs" table. + MaintenanceOperationRunsColumns = []*schema.Column{ + {Name: "id", Type: field.TypeUUID}, + {Name: "operation_key", Type: field.TypeString}, + {Name: "status", Type: field.TypeString, Default: "running"}, + {Name: "started_at", Type: field.TypeTime}, + {Name: "completed_at", Type: field.TypeTime, Nullable: true}, + {Name: "started_by", Type: field.TypeString, Nullable: true}, + {Name: "result", Type: field.TypeString, Nullable: true}, + {Name: "log", Type: field.TypeString, Default: ""}, + } + // MaintenanceOperationRunsTable holds the schema information for the "maintenance_operation_runs" table. + MaintenanceOperationRunsTable = &schema.Table{ + Name: "maintenance_operation_runs", + Columns: MaintenanceOperationRunsColumns, + PrimaryKey: []*schema.Column{MaintenanceOperationRunsColumns[0]}, + Indexes: []*schema.Index{ + { + Name: "maintenanceoperationrun_operation_key", + Unique: false, + Columns: []*schema.Column{MaintenanceOperationRunsColumns[1]}, + }, + }, + } + // MessagesColumns holds the columns for the "messages" table. + MessagesColumns = []*schema.Column{ + {Name: "id", Type: field.TypeUUID}, + {Name: "project_id", Type: field.TypeUUID}, + {Name: "sender", Type: field.TypeString}, + {Name: "sender_id", Type: field.TypeString, Nullable: true}, + {Name: "recipient", Type: field.TypeString}, + {Name: "recipient_id", Type: field.TypeString, Nullable: true}, + {Name: "msg", Type: field.TypeString}, + {Name: "type", Type: field.TypeString, Default: "instruction"}, + {Name: "urgent", Type: field.TypeBool, Default: false}, + {Name: "broadcasted", Type: field.TypeBool, Default: false}, + {Name: "read", Type: field.TypeBool, Default: false}, + {Name: "agent_id", Type: field.TypeString, Nullable: true}, + {Name: "group_id", Type: field.TypeString, Nullable: true}, + {Name: "created", Type: field.TypeTime}, + } + // MessagesTable holds the schema information for the "messages" table. + MessagesTable = &schema.Table{ + Name: "messages", + Columns: MessagesColumns, + PrimaryKey: []*schema.Column{MessagesColumns[0]}, + Indexes: []*schema.Index{ + { + Name: "message_project_id", + Unique: false, + Columns: []*schema.Column{MessagesColumns[1]}, + }, + { + Name: "message_recipient_recipient_id", + Unique: false, + Columns: []*schema.Column{MessagesColumns[4], MessagesColumns[5]}, + }, + { + Name: "message_created", + Unique: false, + Columns: []*schema.Column{MessagesColumns[13]}, + }, + }, + } + // NotificationsColumns holds the columns for the "notifications" table. + NotificationsColumns = []*schema.Column{ + {Name: "id", Type: field.TypeUUID}, + {Name: "subscription_id", Type: field.TypeUUID}, + {Name: "agent_id", Type: field.TypeUUID}, + {Name: "project_id", Type: field.TypeUUID}, + {Name: "subscriber_type", Type: field.TypeString}, + {Name: "subscriber_id", Type: field.TypeString}, + {Name: "status", Type: field.TypeString}, + {Name: "message", Type: field.TypeString}, + {Name: "dispatched", Type: field.TypeBool, Default: false}, + {Name: "acknowledged", Type: field.TypeBool, Default: false}, + {Name: "created", Type: field.TypeTime}, + } + // NotificationsTable holds the schema information for the "notifications" table. + NotificationsTable = &schema.Table{ + Name: "notifications", + Columns: NotificationsColumns, + PrimaryKey: []*schema.Column{NotificationsColumns[0]}, + Indexes: []*schema.Index{ + { + Name: "notification_subscription_id", + Unique: false, + Columns: []*schema.Column{NotificationsColumns[1]}, + }, + { + Name: "notification_project_id_subscriber_type_subscriber_id", + Unique: false, + Columns: []*schema.Column{NotificationsColumns[3], NotificationsColumns[4], NotificationsColumns[5]}, + }, + }, + } + // NotificationSubscriptionsColumns holds the columns for the "notification_subscriptions" table. + NotificationSubscriptionsColumns = []*schema.Column{ + {Name: "id", Type: field.TypeUUID}, + {Name: "scope", Type: field.TypeString, Default: "agent"}, + {Name: "agent_id", Type: field.TypeUUID, Nullable: true}, + {Name: "subscriber_type", Type: field.TypeString, Default: "agent"}, + {Name: "subscriber_id", Type: field.TypeString}, + {Name: "project_id", Type: field.TypeUUID}, + {Name: "trigger_activities", Type: field.TypeString}, + {Name: "created_by", Type: field.TypeString}, + {Name: "created", Type: field.TypeTime}, + } + // NotificationSubscriptionsTable holds the schema information for the "notification_subscriptions" table. + NotificationSubscriptionsTable = &schema.Table{ + Name: "notification_subscriptions", + Columns: NotificationSubscriptionsColumns, + PrimaryKey: []*schema.Column{NotificationSubscriptionsColumns[0]}, + Indexes: []*schema.Index{ + { + Name: "notificationsubscription_scope_agent_id_subscriber_type_subscriber_id_project_id", + Unique: false, + Columns: []*schema.Column{NotificationSubscriptionsColumns[1], NotificationSubscriptionsColumns[2], NotificationSubscriptionsColumns[3], NotificationSubscriptionsColumns[4], NotificationSubscriptionsColumns[5]}, + }, + { + Name: "notificationsubscription_project_id", + Unique: false, + Columns: []*schema.Column{NotificationSubscriptionsColumns[5]}, + }, + }, + } // PolicyBindingsColumns holds the columns for the "policy_bindings" table. PolicyBindingsColumns = []*schema.Column{ {Name: "id", Type: field.TypeUUID}, @@ -208,13 +628,19 @@ var ( {Name: "name", Type: field.TypeString}, {Name: "slug", Type: field.TypeString, Unique: true}, {Name: "git_remote", Type: field.TypeString, Nullable: true}, + {Name: "default_runtime_broker_id", Type: field.TypeString, Nullable: true}, {Name: "labels", Type: field.TypeJSON, Nullable: true}, {Name: "annotations", Type: field.TypeJSON, Nullable: true}, + {Name: "shared_dirs", Type: field.TypeString, Nullable: true}, {Name: "created", Type: field.TypeTime}, {Name: "updated", Type: field.TypeTime}, {Name: "created_by", Type: field.TypeString, Nullable: true}, {Name: "owner_id", Type: field.TypeString, Nullable: true}, {Name: "visibility", Type: field.TypeString, Default: "private"}, + {Name: "github_installation_id", Type: field.TypeInt64, Nullable: true}, + {Name: "github_permissions", Type: field.TypeString, Nullable: true}, + {Name: "github_app_status", Type: field.TypeString, Nullable: true}, + {Name: "git_identity", Type: field.TypeString, Nullable: true}, } // ProjectsTable holds the schema information for the "projects" table. ProjectsTable = &schema.Table{ @@ -222,6 +648,293 @@ var ( Columns: ProjectsColumns, PrimaryKey: []*schema.Column{ProjectsColumns[0]}, } + // ProjectContributorsColumns holds the columns for the "project_contributors" table. + ProjectContributorsColumns = []*schema.Column{ + {Name: "id", Type: field.TypeUUID}, + {Name: "project_id", Type: field.TypeUUID}, + {Name: "broker_id", Type: field.TypeUUID}, + {Name: "broker_name", Type: field.TypeString}, + {Name: "mode", Type: field.TypeString, Default: "connected"}, + {Name: "status", Type: field.TypeString, Default: "offline"}, + {Name: "profiles", Type: field.TypeString, Nullable: true}, + {Name: "last_seen", Type: field.TypeTime, Nullable: true}, + {Name: "local_path", Type: field.TypeString, Nullable: true}, + {Name: "linked_by", Type: field.TypeString, Nullable: true}, + {Name: "linked_at", Type: field.TypeTime, Nullable: true}, + } + // ProjectContributorsTable holds the schema information for the "project_contributors" table. + ProjectContributorsTable = &schema.Table{ + Name: "project_contributors", + Columns: ProjectContributorsColumns, + PrimaryKey: []*schema.Column{ProjectContributorsColumns[0]}, + Indexes: []*schema.Index{ + { + Name: "projectcontributor_project_id_broker_id", + Unique: true, + Columns: []*schema.Column{ProjectContributorsColumns[1], ProjectContributorsColumns[2]}, + }, + { + Name: "projectcontributor_broker_id", + Unique: false, + Columns: []*schema.Column{ProjectContributorsColumns[2]}, + }, + }, + } + // ProjectSyncStateColumns holds the columns for the "project_sync_state" table. + ProjectSyncStateColumns = []*schema.Column{ + {Name: "id", Type: field.TypeUUID}, + {Name: "project_id", Type: field.TypeUUID}, + {Name: "broker_id", Type: field.TypeString, Default: ""}, + {Name: "last_sync_time", Type: field.TypeTime, Nullable: true}, + {Name: "last_commit_sha", Type: field.TypeString, Nullable: true}, + {Name: "file_count", Type: field.TypeInt, Default: 0}, + {Name: "total_bytes", Type: field.TypeInt64, Default: 0}, + } + // ProjectSyncStateTable holds the schema information for the "project_sync_state" table. + ProjectSyncStateTable = &schema.Table{ + Name: "project_sync_state", + Columns: ProjectSyncStateColumns, + PrimaryKey: []*schema.Column{ProjectSyncStateColumns[0]}, + Indexes: []*schema.Index{ + { + Name: "projectsyncstate_project_id_broker_id", + Unique: true, + Columns: []*schema.Column{ProjectSyncStateColumns[1], ProjectSyncStateColumns[2]}, + }, + }, + } + // RuntimeBrokersColumns holds the columns for the "runtime_brokers" table. + RuntimeBrokersColumns = []*schema.Column{ + {Name: "id", Type: field.TypeUUID}, + {Name: "name", Type: field.TypeString}, + {Name: "slug", Type: field.TypeString}, + {Name: "type", Type: field.TypeString, Nullable: true}, + {Name: "mode", Type: field.TypeString, Default: "connected"}, + {Name: "version", Type: field.TypeString, Nullable: true}, + {Name: "lock_version", Type: field.TypeInt64, Default: 0}, + {Name: "status", Type: field.TypeString, Default: "offline"}, + {Name: "connection_state", Type: field.TypeString, Default: "disconnected"}, + {Name: "last_heartbeat", Type: field.TypeTime, Nullable: true}, + {Name: "capabilities", Type: field.TypeString, Nullable: true}, + {Name: "supported_harnesses", Type: field.TypeString, Nullable: true}, + {Name: "resources", Type: field.TypeString, Nullable: true}, + {Name: "runtimes", Type: field.TypeString, Nullable: true}, + {Name: "labels", Type: field.TypeString, Nullable: true}, + {Name: "annotations", Type: field.TypeString, Nullable: true}, + {Name: "endpoint", Type: field.TypeString, Nullable: true}, + {Name: "created_by", Type: field.TypeString, Nullable: true}, + {Name: "auto_provide", Type: field.TypeBool, Default: false}, + {Name: "created", Type: field.TypeTime}, + {Name: "updated", Type: field.TypeTime}, + } + // RuntimeBrokersTable holds the schema information for the "runtime_brokers" table. + RuntimeBrokersTable = &schema.Table{ + Name: "runtime_brokers", + Columns: RuntimeBrokersColumns, + PrimaryKey: []*schema.Column{RuntimeBrokersColumns[0]}, + Indexes: []*schema.Index{ + { + Name: "runtimebroker_slug", + Unique: false, + Columns: []*schema.Column{RuntimeBrokersColumns[2]}, + }, + { + Name: "runtimebroker_status", + Unique: false, + Columns: []*schema.Column{RuntimeBrokersColumns[7]}, + }, + }, + } + // SchedulesColumns holds the columns for the "schedules" table. + SchedulesColumns = []*schema.Column{ + {Name: "id", Type: field.TypeUUID}, + {Name: "project_id", Type: field.TypeUUID}, + {Name: "name", Type: field.TypeString}, + {Name: "cron_expr", Type: field.TypeString}, + {Name: "event_type", Type: field.TypeString}, + {Name: "payload", Type: field.TypeString, Default: "{}"}, + {Name: "status", Type: field.TypeString, Default: "active"}, + {Name: "next_run_at", Type: field.TypeTime, Nullable: true}, + {Name: "last_run_at", Type: field.TypeTime, Nullable: true}, + {Name: "last_run_status", Type: field.TypeString, Nullable: true}, + {Name: "last_run_error", Type: field.TypeString, Nullable: true}, + {Name: "run_count", Type: field.TypeInt, Default: 0}, + {Name: "error_count", Type: field.TypeInt, Default: 0}, + {Name: "created_by", Type: field.TypeString, Nullable: true}, + {Name: "created", Type: field.TypeTime}, + {Name: "updated", Type: field.TypeTime}, + } + // SchedulesTable holds the schema information for the "schedules" table. + SchedulesTable = &schema.Table{ + Name: "schedules", + Columns: SchedulesColumns, + PrimaryKey: []*schema.Column{SchedulesColumns[0]}, + Indexes: []*schema.Index{ + { + Name: "schedule_project_id_name", + Unique: true, + Columns: []*schema.Column{SchedulesColumns[1], SchedulesColumns[2]}, + }, + { + Name: "schedule_next_run_at", + Unique: false, + Columns: []*schema.Column{SchedulesColumns[7]}, + }, + }, + } + // ScheduledEventsColumns holds the columns for the "scheduled_events" table. + ScheduledEventsColumns = []*schema.Column{ + {Name: "id", Type: field.TypeUUID}, + {Name: "project_id", Type: field.TypeUUID}, + {Name: "event_type", Type: field.TypeString}, + {Name: "fire_at", Type: field.TypeTime}, + {Name: "payload", Type: field.TypeString}, + {Name: "status", Type: field.TypeString, Default: "pending"}, + {Name: "created_by", Type: field.TypeString, Nullable: true}, + {Name: "fired_at", Type: field.TypeTime, Nullable: true}, + {Name: "error", Type: field.TypeString, Nullable: true}, + {Name: "schedule_id", Type: field.TypeString, Nullable: true}, + {Name: "created", Type: field.TypeTime}, + } + // ScheduledEventsTable holds the schema information for the "scheduled_events" table. + ScheduledEventsTable = &schema.Table{ + Name: "scheduled_events", + Columns: ScheduledEventsColumns, + PrimaryKey: []*schema.Column{ScheduledEventsColumns[0]}, + Indexes: []*schema.Index{ + { + Name: "scheduledevent_fire_at", + Unique: false, + Columns: []*schema.Column{ScheduledEventsColumns[3]}, + }, + { + Name: "scheduledevent_project_id", + Unique: false, + Columns: []*schema.Column{ScheduledEventsColumns[1]}, + }, + { + Name: "scheduledevent_status", + Unique: false, + Columns: []*schema.Column{ScheduledEventsColumns[5]}, + }, + }, + } + // SecretsColumns holds the columns for the "secrets" table. + SecretsColumns = []*schema.Column{ + {Name: "id", Type: field.TypeUUID}, + {Name: "key", Type: field.TypeString}, + {Name: "encrypted_value", Type: field.TypeString}, + {Name: "secret_ref", Type: field.TypeString, Nullable: true}, + {Name: "secret_type", Type: field.TypeEnum, Enums: []string{"environment", "variable", "file", "internal"}, Default: "environment"}, + {Name: "target", Type: field.TypeString, Nullable: true}, + {Name: "scope", Type: field.TypeString}, + {Name: "scope_id", Type: field.TypeString}, + {Name: "description", Type: field.TypeString, Nullable: true}, + {Name: "injection_mode", Type: field.TypeEnum, Enums: []string{"always", "as_needed"}, Default: "as_needed"}, + {Name: "allow_progeny", Type: field.TypeBool, Default: false}, + {Name: "version", Type: field.TypeInt, Default: 1}, + {Name: "created_by", Type: field.TypeString, Nullable: true}, + {Name: "updated_by", Type: field.TypeString, Nullable: true}, + {Name: "created", Type: field.TypeTime}, + {Name: "updated", Type: field.TypeTime}, + } + // SecretsTable holds the schema information for the "secrets" table. + SecretsTable = &schema.Table{ + Name: "secrets", + Columns: SecretsColumns, + PrimaryKey: []*schema.Column{SecretsColumns[0]}, + Indexes: []*schema.Index{ + { + Name: "secret_key_scope_scope_id", + Unique: true, + Columns: []*schema.Column{SecretsColumns[1], SecretsColumns[6], SecretsColumns[7]}, + }, + { + Name: "secret_scope_scope_id", + Unique: false, + Columns: []*schema.Column{SecretsColumns[6], SecretsColumns[7]}, + }, + }, + } + // SubscriptionTemplatesColumns holds the columns for the "subscription_templates" table. + SubscriptionTemplatesColumns = []*schema.Column{ + {Name: "id", Type: field.TypeUUID}, + {Name: "name", Type: field.TypeString}, + {Name: "scope", Type: field.TypeString, Default: "project"}, + {Name: "trigger_activities", Type: field.TypeString}, + {Name: "project_id", Type: field.TypeUUID, Nullable: true}, + {Name: "created_by", Type: field.TypeString}, + } + // SubscriptionTemplatesTable holds the schema information for the "subscription_templates" table. + SubscriptionTemplatesTable = &schema.Table{ + Name: "subscription_templates", + Columns: SubscriptionTemplatesColumns, + PrimaryKey: []*schema.Column{SubscriptionTemplatesColumns[0]}, + Indexes: []*schema.Index{ + { + Name: "subscriptiontemplate_project_id_name", + Unique: true, + Columns: []*schema.Column{SubscriptionTemplatesColumns[4], SubscriptionTemplatesColumns[1]}, + }, + }, + } + // TemplatesColumns holds the columns for the "templates" table. + TemplatesColumns = []*schema.Column{ + {Name: "id", Type: field.TypeUUID}, + {Name: "name", Type: field.TypeString}, + {Name: "slug", Type: field.TypeString}, + {Name: "display_name", Type: field.TypeString, Nullable: true}, + {Name: "description", Type: field.TypeString, Nullable: true}, + {Name: "harness", Type: field.TypeString}, + {Name: "default_harness_config", Type: field.TypeString, Nullable: true}, + {Name: "image", Type: field.TypeString, Nullable: true}, + {Name: "config", Type: field.TypeString, Nullable: true}, + {Name: "content_hash", Type: field.TypeString, Nullable: true}, + {Name: "scope", Type: field.TypeString, Default: "global"}, + {Name: "scope_id", Type: field.TypeString, Nullable: true}, + {Name: "project_id", Type: field.TypeString, Nullable: true}, + {Name: "storage_uri", Type: field.TypeString, Nullable: true}, + {Name: "storage_bucket", Type: field.TypeString, Nullable: true}, + {Name: "storage_path", Type: field.TypeString, Nullable: true}, + {Name: "files", Type: field.TypeString, Nullable: true}, + {Name: "base_template", Type: field.TypeString, Nullable: true}, + {Name: "locked", Type: field.TypeBool, Default: false}, + {Name: "status", Type: field.TypeEnum, Enums: []string{"pending", "active", "archived"}, Default: "active"}, + {Name: "owner_id", Type: field.TypeString, Nullable: true}, + {Name: "created_by", Type: field.TypeString, Nullable: true}, + {Name: "updated_by", Type: field.TypeString, Nullable: true}, + {Name: "visibility", Type: field.TypeString, Default: "private"}, + {Name: "created", Type: field.TypeTime}, + {Name: "updated", Type: field.TypeTime}, + } + // TemplatesTable holds the schema information for the "templates" table. + TemplatesTable = &schema.Table{ + Name: "templates", + Columns: TemplatesColumns, + PrimaryKey: []*schema.Column{TemplatesColumns[0]}, + Indexes: []*schema.Index{ + { + Name: "template_slug_scope", + Unique: false, + Columns: []*schema.Column{TemplatesColumns[2], TemplatesColumns[10]}, + }, + { + Name: "template_harness", + Unique: false, + Columns: []*schema.Column{TemplatesColumns[5]}, + }, + { + Name: "template_status", + Unique: false, + Columns: []*schema.Column{TemplatesColumns[19]}, + }, + { + Name: "template_content_hash", + Unique: false, + Columns: []*schema.Column{TemplatesColumns[9]}, + }, + }, + } // UsersColumns holds the columns for the "users" table. UsersColumns = []*schema.Column{ {Name: "id", Type: field.TypeUUID}, @@ -233,12 +946,52 @@ var ( {Name: "preferences", Type: field.TypeJSON, Nullable: true}, {Name: "created", Type: field.TypeTime}, {Name: "last_login", Type: field.TypeTime, Nullable: true}, + {Name: "last_seen", Type: field.TypeTime, Nullable: true}, } // UsersTable holds the schema information for the "users" table. UsersTable = &schema.Table{ Name: "users", Columns: UsersColumns, PrimaryKey: []*schema.Column{UsersColumns[0]}, + Indexes: []*schema.Index{ + { + Name: "user_last_seen", + Unique: false, + Columns: []*schema.Column{UsersColumns[9]}, + }, + }, + } + // UserAccessTokensColumns holds the columns for the "user_access_tokens" table. + UserAccessTokensColumns = []*schema.Column{ + {Name: "id", Type: field.TypeUUID}, + {Name: "user_id", Type: field.TypeUUID}, + {Name: "name", Type: field.TypeString}, + {Name: "prefix", Type: field.TypeString}, + {Name: "key_hash", Type: field.TypeString, Unique: true}, + {Name: "project_id", Type: field.TypeUUID}, + {Name: "scopes", Type: field.TypeString}, + {Name: "revoked", Type: field.TypeBool, Default: false}, + {Name: "expires_at", Type: field.TypeTime, Nullable: true}, + {Name: "last_used", Type: field.TypeTime, Nullable: true}, + {Name: "created", Type: field.TypeTime}, + } + // UserAccessTokensTable holds the schema information for the "user_access_tokens" table. + UserAccessTokensTable = &schema.Table{ + Name: "user_access_tokens", + Columns: UserAccessTokensColumns, + PrimaryKey: []*schema.Column{UserAccessTokensColumns[0]}, + Indexes: []*schema.Index{ + { + Name: "useraccesstoken_user_id", + Unique: false, + Columns: []*schema.Column{UserAccessTokensColumns[1]}, + }, + { + Name: "useraccesstoken_project_id", + Unique: false, + Columns: []*schema.Column{UserAccessTokensColumns[5]}, + }, + }, } // GroupChildGroupsColumns holds the columns for the "group_child_groups" table. GroupChildGroupsColumns = []*schema.Column{ @@ -269,11 +1022,34 @@ var ( Tables = []*schema.Table{ AccessPoliciesTable, AgentsTable, + AllowListTable, + APIKeysTable, + BrokerJoinTokensTable, + BrokerSecretsTable, + EnvVarsTable, + GcpServiceAccountsTable, + GithubInstallationsTable, GroupsTable, GroupMembershipsTable, + HarnessConfigsTable, + InviteCodesTable, + MaintenanceOperationsTable, + MaintenanceOperationRunsTable, + MessagesTable, + NotificationsTable, + NotificationSubscriptionsTable, PolicyBindingsTable, ProjectsTable, + ProjectContributorsTable, + ProjectSyncStateTable, + RuntimeBrokersTable, + SchedulesTable, + ScheduledEventsTable, + SecretsTable, + SubscriptionTemplatesTable, + TemplatesTable, UsersTable, + UserAccessTokensTable, GroupChildGroupsTable, } ) @@ -282,10 +1058,52 @@ func init() { AgentsTable.ForeignKeys[0].RefTable = ProjectsTable AgentsTable.ForeignKeys[1].RefTable = UsersTable AgentsTable.ForeignKeys[2].RefTable = UsersTable + AllowListTable.Annotation = &entsql.Annotation{ + Table: "allow_list", + } + APIKeysTable.Annotation = &entsql.Annotation{ + Table: "api_keys", + } + BrokerJoinTokensTable.Annotation = &entsql.Annotation{ + Table: "broker_join_tokens", + } + BrokerSecretsTable.Annotation = &entsql.Annotation{ + Table: "broker_secrets", + } + EnvVarsTable.Annotation = &entsql.Annotation{ + Table: "env_vars", + } + GcpServiceAccountsTable.Annotation = &entsql.Annotation{ + Table: "gcp_service_accounts", + } + GithubInstallationsTable.Annotation = &entsql.Annotation{ + Table: "github_installations", + } GroupsTable.ForeignKeys[0].RefTable = UsersTable GroupMembershipsTable.ForeignKeys[0].RefTable = GroupsTable GroupMembershipsTable.ForeignKeys[1].RefTable = UsersTable GroupMembershipsTable.ForeignKeys[2].RefTable = AgentsTable + HarnessConfigsTable.Annotation = &entsql.Annotation{ + Table: "harness_configs", + } + InviteCodesTable.Annotation = &entsql.Annotation{ + Table: "invite_codes", + } + MaintenanceOperationsTable.Annotation = &entsql.Annotation{ + Table: "maintenance_operations", + } + MaintenanceOperationRunsTable.Annotation = &entsql.Annotation{ + Table: "maintenance_operation_runs", + } + MessagesTable.Annotation = &entsql.Annotation{ + Table: "messages", + } + NotificationsTable.Annotation = &entsql.Annotation{ + Table: "notifications", + } + NotificationSubscriptionsTable.Annotation = &entsql.Annotation{ + Table: "notification_subscriptions", + } PolicyBindingsTable.ForeignKeys[0].RefTable = AccessPoliciesTable PolicyBindingsTable.ForeignKeys[1].RefTable = UsersTable PolicyBindingsTable.ForeignKeys[2].RefTable = GroupsTable @@ -293,6 +1111,33 @@ func init() { ProjectsTable.Annotation = &entsql.Annotation{ Table: "projects", } + ProjectContributorsTable.Annotation = &entsql.Annotation{ + Table: "project_contributors", + } + ProjectSyncStateTable.Annotation = &entsql.Annotation{ + Table: "project_sync_state", + } + RuntimeBrokersTable.Annotation = &entsql.Annotation{ + Table: "runtime_brokers", + } + SchedulesTable.Annotation = &entsql.Annotation{ + Table: "schedules", + } + ScheduledEventsTable.Annotation = &entsql.Annotation{ + Table: "scheduled_events", + } + SecretsTable.Annotation = &entsql.Annotation{ + Table: "secrets", + } + SubscriptionTemplatesTable.Annotation = &entsql.Annotation{ + Table: "subscription_templates", + } + TemplatesTable.Annotation = &entsql.Annotation{ + Table: "templates", + } + UserAccessTokensTable.Annotation = &entsql.Annotation{ + Table: "user_access_tokens", + } GroupChildGroupsTable.ForeignKeys[0].RefTable = GroupsTable GroupChildGroupsTable.ForeignKeys[1].RefTable = GroupsTable } diff --git a/pkg/ent/mutation.go b/pkg/ent/mutation.go index 023803a8f..37ce86a44 100644 --- a/pkg/ent/mutation.go +++ b/pkg/ent/mutation.go @@ -13,13 +13,36 @@ import ( "entgo.io/ent/dialect/sql" "github.com/GoogleCloudPlatform/scion/pkg/ent/accesspolicy" "github.com/GoogleCloudPlatform/scion/pkg/ent/agent" + "github.com/GoogleCloudPlatform/scion/pkg/ent/allowlistentry" + "github.com/GoogleCloudPlatform/scion/pkg/ent/apikey" + "github.com/GoogleCloudPlatform/scion/pkg/ent/brokerjointoken" + "github.com/GoogleCloudPlatform/scion/pkg/ent/brokersecret" + "github.com/GoogleCloudPlatform/scion/pkg/ent/envvar" + "github.com/GoogleCloudPlatform/scion/pkg/ent/gcpserviceaccount" + "github.com/GoogleCloudPlatform/scion/pkg/ent/githubinstallation" "github.com/GoogleCloudPlatform/scion/pkg/ent/group" "github.com/GoogleCloudPlatform/scion/pkg/ent/groupmembership" + "github.com/GoogleCloudPlatform/scion/pkg/ent/harnessconfig" + "github.com/GoogleCloudPlatform/scion/pkg/ent/invitecode" + "github.com/GoogleCloudPlatform/scion/pkg/ent/maintenanceoperation" + "github.com/GoogleCloudPlatform/scion/pkg/ent/maintenanceoperationrun" + "github.com/GoogleCloudPlatform/scion/pkg/ent/message" + "github.com/GoogleCloudPlatform/scion/pkg/ent/notification" + "github.com/GoogleCloudPlatform/scion/pkg/ent/notificationsubscription" "github.com/GoogleCloudPlatform/scion/pkg/ent/policybinding" "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" "github.com/GoogleCloudPlatform/scion/pkg/ent/project" + "github.com/GoogleCloudPlatform/scion/pkg/ent/projectcontributor" + "github.com/GoogleCloudPlatform/scion/pkg/ent/projectsyncstate" + "github.com/GoogleCloudPlatform/scion/pkg/ent/runtimebroker" + "github.com/GoogleCloudPlatform/scion/pkg/ent/schedule" + "github.com/GoogleCloudPlatform/scion/pkg/ent/scheduledevent" "github.com/GoogleCloudPlatform/scion/pkg/ent/schema" + "github.com/GoogleCloudPlatform/scion/pkg/ent/secret" + "github.com/GoogleCloudPlatform/scion/pkg/ent/subscriptiontemplate" + "github.com/GoogleCloudPlatform/scion/pkg/ent/template" "github.com/GoogleCloudPlatform/scion/pkg/ent/user" + "github.com/GoogleCloudPlatform/scion/pkg/ent/useraccesstoken" "github.com/google/uuid" ) @@ -32,13 +55,36 @@ const ( OpUpdateOne = ent.OpUpdateOne // Node types. - TypeAccessPolicy = "AccessPolicy" - TypeAgent = "Agent" - TypeGroup = "Group" - TypeGroupMembership = "GroupMembership" - TypePolicyBinding = "PolicyBinding" - TypeProject = "Project" - TypeUser = "User" + TypeAccessPolicy = "AccessPolicy" + TypeAgent = "Agent" + TypeAllowListEntry = "AllowListEntry" + TypeApiKey = "ApiKey" + TypeBrokerJoinToken = "BrokerJoinToken" + TypeBrokerSecret = "BrokerSecret" + TypeEnvVar = "EnvVar" + TypeGCPServiceAccount = "GCPServiceAccount" + TypeGithubInstallation = "GithubInstallation" + TypeGroup = "Group" + TypeGroupMembership = "GroupMembership" + TypeHarnessConfig = "HarnessConfig" + TypeInviteCode = "InviteCode" + TypeMaintenanceOperation = "MaintenanceOperation" + TypeMaintenanceOperationRun = "MaintenanceOperationRun" + TypeMessage = "Message" + TypeNotification = "Notification" + TypeNotificationSubscription = "NotificationSubscription" + TypePolicyBinding = "PolicyBinding" + TypeProject = "Project" + TypeProjectContributor = "ProjectContributor" + TypeProjectSyncState = "ProjectSyncState" + TypeRuntimeBroker = "RuntimeBroker" + TypeSchedule = "Schedule" + TypeScheduledEvent = "ScheduledEvent" + TypeSecret = "Secret" + TypeSubscriptionTemplate = "SubscriptionTemplate" + TypeTemplate = "Template" + TypeUser = "User" + TypeUserAccessToken = "UserAccessToken" ) // AccessPolicyMutation represents an operation that mutates the AccessPolicy nodes in the graph. @@ -1442,8 +1488,37 @@ type AgentMutation struct { status *agent.Status delegation_enabled *bool visibility *string + labels *map[string]string + annotations *map[string]string + phase *string + activity *string + tool_name *string + connection_state *string + container_status *string + runtime_state *string + stalled_from_activity *string + current_turns *int + addcurrent_turns *int + current_model_calls *int + addcurrent_model_calls *int + image *string + detached *bool + runtime *string + runtime_broker_id *string + web_pty_enabled *bool + task_summary *string + message *string + applied_config *string + ancestry *[]string + appendancestry []string created *time.Time updated *time.Time + last_seen *time.Time + last_activity_event *time.Time + started_at *time.Time + deleted_at *time.Time + state_version *int64 + addstate_version *int64 clearedFields map[string]struct{} project *uuid.UUID clearedproject bool @@ -1929,289 +2004,24111 @@ func (m *AgentMutation) ResetVisibility() { m.visibility = nil } -// SetCreated sets the "created" field. -func (m *AgentMutation) SetCreated(t time.Time) { - m.created = &t +// SetLabels sets the "labels" field. +func (m *AgentMutation) SetLabels(value map[string]string) { + m.labels = &value } -// Created returns the value of the "created" field in the mutation. -func (m *AgentMutation) Created() (r time.Time, exists bool) { - v := m.created +// Labels returns the value of the "labels" field in the mutation. +func (m *AgentMutation) Labels() (r map[string]string, exists bool) { + v := m.labels if v == nil { return } return *v, true } -// OldCreated returns the old "created" field's value of the Agent entity. +// OldLabels returns the old "labels" field's value of the Agent entity. // If the Agent object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *AgentMutation) OldCreated(ctx context.Context) (v time.Time, err error) { +func (m *AgentMutation) OldLabels(ctx context.Context) (v map[string]string, err error) { if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldCreated is only allowed on UpdateOne operations") + return v, errors.New("OldLabels is only allowed on UpdateOne operations") } if m.id == nil || m.oldValue == nil { - return v, errors.New("OldCreated requires an ID field in the mutation") + return v, errors.New("OldLabels requires an ID field in the mutation") } oldValue, err := m.oldValue(ctx) if err != nil { - return v, fmt.Errorf("querying old value for OldCreated: %w", err) + return v, fmt.Errorf("querying old value for OldLabels: %w", err) } - return oldValue.Created, nil + return oldValue.Labels, nil } -// ResetCreated resets all changes to the "created" field. -func (m *AgentMutation) ResetCreated() { - m.created = nil +// ClearLabels clears the value of the "labels" field. +func (m *AgentMutation) ClearLabels() { + m.labels = nil + m.clearedFields[agent.FieldLabels] = struct{}{} } -// SetUpdated sets the "updated" field. -func (m *AgentMutation) SetUpdated(t time.Time) { - m.updated = &t +// LabelsCleared returns if the "labels" field was cleared in this mutation. +func (m *AgentMutation) LabelsCleared() bool { + _, ok := m.clearedFields[agent.FieldLabels] + return ok } -// Updated returns the value of the "updated" field in the mutation. -func (m *AgentMutation) Updated() (r time.Time, exists bool) { - v := m.updated +// ResetLabels resets all changes to the "labels" field. +func (m *AgentMutation) ResetLabels() { + m.labels = nil + delete(m.clearedFields, agent.FieldLabels) +} + +// SetAnnotations sets the "annotations" field. +func (m *AgentMutation) SetAnnotations(value map[string]string) { + m.annotations = &value +} + +// Annotations returns the value of the "annotations" field in the mutation. +func (m *AgentMutation) Annotations() (r map[string]string, exists bool) { + v := m.annotations if v == nil { return } return *v, true } -// OldUpdated returns the old "updated" field's value of the Agent entity. +// OldAnnotations returns the old "annotations" field's value of the Agent entity. // If the Agent object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *AgentMutation) OldUpdated(ctx context.Context) (v time.Time, err error) { +func (m *AgentMutation) OldAnnotations(ctx context.Context) (v map[string]string, err error) { if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldUpdated is only allowed on UpdateOne operations") + return v, errors.New("OldAnnotations is only allowed on UpdateOne operations") } if m.id == nil || m.oldValue == nil { - return v, errors.New("OldUpdated requires an ID field in the mutation") + return v, errors.New("OldAnnotations requires an ID field in the mutation") } oldValue, err := m.oldValue(ctx) if err != nil { - return v, fmt.Errorf("querying old value for OldUpdated: %w", err) + return v, fmt.Errorf("querying old value for OldAnnotations: %w", err) } - return oldValue.Updated, nil + return oldValue.Annotations, nil } -// ResetUpdated resets all changes to the "updated" field. -func (m *AgentMutation) ResetUpdated() { - m.updated = nil +// ClearAnnotations clears the value of the "annotations" field. +func (m *AgentMutation) ClearAnnotations() { + m.annotations = nil + m.clearedFields[agent.FieldAnnotations] = struct{}{} } -// ClearProject clears the "project" edge to the Project entity. -func (m *AgentMutation) ClearProject() { - m.clearedproject = true - m.clearedFields[agent.FieldProjectID] = struct{}{} +// AnnotationsCleared returns if the "annotations" field was cleared in this mutation. +func (m *AgentMutation) AnnotationsCleared() bool { + _, ok := m.clearedFields[agent.FieldAnnotations] + return ok } -// ProjectCleared reports if the "project" edge to the Project entity was cleared. -func (m *AgentMutation) ProjectCleared() bool { - return m.clearedproject +// ResetAnnotations resets all changes to the "annotations" field. +func (m *AgentMutation) ResetAnnotations() { + m.annotations = nil + delete(m.clearedFields, agent.FieldAnnotations) } -// ProjectIDs returns the "project" edge IDs in the mutation. -// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use -// ProjectID instead. It exists only for internal usage by the builders. -func (m *AgentMutation) ProjectIDs() (ids []uuid.UUID) { - if id := m.project; id != nil { - ids = append(ids, *id) +// SetPhase sets the "phase" field. +func (m *AgentMutation) SetPhase(s string) { + m.phase = &s +} + +// Phase returns the value of the "phase" field in the mutation. +func (m *AgentMutation) Phase() (r string, exists bool) { + v := m.phase + if v == nil { + return } - return + return *v, true } -// ResetProject resets all changes to the "project" edge. -func (m *AgentMutation) ResetProject() { - m.project = nil - m.clearedproject = false +// OldPhase returns the old "phase" field's value of the Agent entity. +// If the Agent object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *AgentMutation) OldPhase(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldPhase is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldPhase requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldPhase: %w", err) + } + return oldValue.Phase, nil } -// SetCreatorID sets the "creator" edge to the User entity by id. -func (m *AgentMutation) SetCreatorID(id uuid.UUID) { - m.creator = &id +// ClearPhase clears the value of the "phase" field. +func (m *AgentMutation) ClearPhase() { + m.phase = nil + m.clearedFields[agent.FieldPhase] = struct{}{} } -// ClearCreator clears the "creator" edge to the User entity. -func (m *AgentMutation) ClearCreator() { - m.clearedcreator = true - m.clearedFields[agent.FieldCreatedBy] = struct{}{} +// PhaseCleared returns if the "phase" field was cleared in this mutation. +func (m *AgentMutation) PhaseCleared() bool { + _, ok := m.clearedFields[agent.FieldPhase] + return ok } -// CreatorCleared reports if the "creator" edge to the User entity was cleared. -func (m *AgentMutation) CreatorCleared() bool { - return m.CreatedByCleared() || m.clearedcreator +// ResetPhase resets all changes to the "phase" field. +func (m *AgentMutation) ResetPhase() { + m.phase = nil + delete(m.clearedFields, agent.FieldPhase) } -// CreatorID returns the "creator" edge ID in the mutation. -func (m *AgentMutation) CreatorID() (id uuid.UUID, exists bool) { - if m.creator != nil { - return *m.creator, true +// SetActivity sets the "activity" field. +func (m *AgentMutation) SetActivity(s string) { + m.activity = &s +} + +// Activity returns the value of the "activity" field in the mutation. +func (m *AgentMutation) Activity() (r string, exists bool) { + v := m.activity + if v == nil { + return } - return + return *v, true } -// CreatorIDs returns the "creator" edge IDs in the mutation. -// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use -// CreatorID instead. It exists only for internal usage by the builders. -func (m *AgentMutation) CreatorIDs() (ids []uuid.UUID) { - if id := m.creator; id != nil { - ids = append(ids, *id) +// OldActivity returns the old "activity" field's value of the Agent entity. +// If the Agent object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *AgentMutation) OldActivity(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldActivity is only allowed on UpdateOne operations") } - return + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldActivity requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldActivity: %w", err) + } + return oldValue.Activity, nil } -// ResetCreator resets all changes to the "creator" edge. -func (m *AgentMutation) ResetCreator() { - m.creator = nil - m.clearedcreator = false +// ClearActivity clears the value of the "activity" field. +func (m *AgentMutation) ClearActivity() { + m.activity = nil + m.clearedFields[agent.FieldActivity] = struct{}{} } -// ClearOwner clears the "owner" edge to the User entity. -func (m *AgentMutation) ClearOwner() { - m.clearedowner = true - m.clearedFields[agent.FieldOwnerID] = struct{}{} +// ActivityCleared returns if the "activity" field was cleared in this mutation. +func (m *AgentMutation) ActivityCleared() bool { + _, ok := m.clearedFields[agent.FieldActivity] + return ok } -// OwnerCleared reports if the "owner" edge to the User entity was cleared. -func (m *AgentMutation) OwnerCleared() bool { - return m.OwnerIDCleared() || m.clearedowner +// ResetActivity resets all changes to the "activity" field. +func (m *AgentMutation) ResetActivity() { + m.activity = nil + delete(m.clearedFields, agent.FieldActivity) } -// OwnerIDs returns the "owner" edge IDs in the mutation. -// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use -// OwnerID instead. It exists only for internal usage by the builders. -func (m *AgentMutation) OwnerIDs() (ids []uuid.UUID) { - if id := m.owner; id != nil { - ids = append(ids, *id) - } - return +// SetToolName sets the "tool_name" field. +func (m *AgentMutation) SetToolName(s string) { + m.tool_name = &s } -// ResetOwner resets all changes to the "owner" edge. -func (m *AgentMutation) ResetOwner() { - m.owner = nil - m.clearedowner = false +// ToolName returns the value of the "tool_name" field in the mutation. +func (m *AgentMutation) ToolName() (r string, exists bool) { + v := m.tool_name + if v == nil { + return + } + return *v, true } -// AddMembershipIDs adds the "memberships" edge to the GroupMembership entity by ids. -func (m *AgentMutation) AddMembershipIDs(ids ...uuid.UUID) { - if m.memberships == nil { - m.memberships = make(map[uuid.UUID]struct{}) +// OldToolName returns the old "tool_name" field's value of the Agent entity. +// If the Agent object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *AgentMutation) OldToolName(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldToolName is only allowed on UpdateOne operations") } - for i := range ids { - m.memberships[ids[i]] = struct{}{} + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldToolName requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldToolName: %w", err) } + return oldValue.ToolName, nil } -// ClearMemberships clears the "memberships" edge to the GroupMembership entity. -func (m *AgentMutation) ClearMemberships() { - m.clearedmemberships = true +// ClearToolName clears the value of the "tool_name" field. +func (m *AgentMutation) ClearToolName() { + m.tool_name = nil + m.clearedFields[agent.FieldToolName] = struct{}{} } -// MembershipsCleared reports if the "memberships" edge to the GroupMembership entity was cleared. -func (m *AgentMutation) MembershipsCleared() bool { - return m.clearedmemberships +// ToolNameCleared returns if the "tool_name" field was cleared in this mutation. +func (m *AgentMutation) ToolNameCleared() bool { + _, ok := m.clearedFields[agent.FieldToolName] + return ok } -// RemoveMembershipIDs removes the "memberships" edge to the GroupMembership entity by IDs. -func (m *AgentMutation) RemoveMembershipIDs(ids ...uuid.UUID) { - if m.removedmemberships == nil { - m.removedmemberships = make(map[uuid.UUID]struct{}) - } - for i := range ids { - delete(m.memberships, ids[i]) - m.removedmemberships[ids[i]] = struct{}{} - } +// ResetToolName resets all changes to the "tool_name" field. +func (m *AgentMutation) ResetToolName() { + m.tool_name = nil + delete(m.clearedFields, agent.FieldToolName) } -// RemovedMemberships returns the removed IDs of the "memberships" edge to the GroupMembership entity. -func (m *AgentMutation) RemovedMembershipsIDs() (ids []uuid.UUID) { - for id := range m.removedmemberships { - ids = append(ids, id) - } - return +// SetConnectionState sets the "connection_state" field. +func (m *AgentMutation) SetConnectionState(s string) { + m.connection_state = &s } -// MembershipsIDs returns the "memberships" edge IDs in the mutation. -func (m *AgentMutation) MembershipsIDs() (ids []uuid.UUID) { - for id := range m.memberships { - ids = append(ids, id) +// ConnectionState returns the value of the "connection_state" field in the mutation. +func (m *AgentMutation) ConnectionState() (r string, exists bool) { + v := m.connection_state + if v == nil { + return } - return -} - -// ResetMemberships resets all changes to the "memberships" edge. -func (m *AgentMutation) ResetMemberships() { - m.memberships = nil - m.clearedmemberships = false - m.removedmemberships = nil + return *v, true } -// AddPolicyBindingIDs adds the "policy_bindings" edge to the PolicyBinding entity by ids. -func (m *AgentMutation) AddPolicyBindingIDs(ids ...uuid.UUID) { - if m.policy_bindings == nil { - m.policy_bindings = make(map[uuid.UUID]struct{}) +// OldConnectionState returns the old "connection_state" field's value of the Agent entity. +// If the Agent object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *AgentMutation) OldConnectionState(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldConnectionState is only allowed on UpdateOne operations") } - for i := range ids { - m.policy_bindings[ids[i]] = struct{}{} + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldConnectionState requires an ID field in the mutation") } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldConnectionState: %w", err) + } + return oldValue.ConnectionState, nil } -// ClearPolicyBindings clears the "policy_bindings" edge to the PolicyBinding entity. -func (m *AgentMutation) ClearPolicyBindings() { - m.clearedpolicy_bindings = true +// ClearConnectionState clears the value of the "connection_state" field. +func (m *AgentMutation) ClearConnectionState() { + m.connection_state = nil + m.clearedFields[agent.FieldConnectionState] = struct{}{} } -// PolicyBindingsCleared reports if the "policy_bindings" edge to the PolicyBinding entity was cleared. -func (m *AgentMutation) PolicyBindingsCleared() bool { - return m.clearedpolicy_bindings +// ConnectionStateCleared returns if the "connection_state" field was cleared in this mutation. +func (m *AgentMutation) ConnectionStateCleared() bool { + _, ok := m.clearedFields[agent.FieldConnectionState] + return ok } -// RemovePolicyBindingIDs removes the "policy_bindings" edge to the PolicyBinding entity by IDs. -func (m *AgentMutation) RemovePolicyBindingIDs(ids ...uuid.UUID) { - if m.removedpolicy_bindings == nil { - m.removedpolicy_bindings = make(map[uuid.UUID]struct{}) - } - for i := range ids { - delete(m.policy_bindings, ids[i]) - m.removedpolicy_bindings[ids[i]] = struct{}{} - } +// ResetConnectionState resets all changes to the "connection_state" field. +func (m *AgentMutation) ResetConnectionState() { + m.connection_state = nil + delete(m.clearedFields, agent.FieldConnectionState) } -// RemovedPolicyBindings returns the removed IDs of the "policy_bindings" edge to the PolicyBinding entity. -func (m *AgentMutation) RemovedPolicyBindingsIDs() (ids []uuid.UUID) { - for id := range m.removedpolicy_bindings { - ids = append(ids, id) - } - return +// SetContainerStatus sets the "container_status" field. +func (m *AgentMutation) SetContainerStatus(s string) { + m.container_status = &s } -// PolicyBindingsIDs returns the "policy_bindings" edge IDs in the mutation. -func (m *AgentMutation) PolicyBindingsIDs() (ids []uuid.UUID) { - for id := range m.policy_bindings { - ids = append(ids, id) +// ContainerStatus returns the value of the "container_status" field in the mutation. +func (m *AgentMutation) ContainerStatus() (r string, exists bool) { + v := m.container_status + if v == nil { + return } - return + return *v, true } -// ResetPolicyBindings resets all changes to the "policy_bindings" edge. -func (m *AgentMutation) ResetPolicyBindings() { - m.policy_bindings = nil +// OldContainerStatus returns the old "container_status" field's value of the Agent entity. +// If the Agent object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *AgentMutation) OldContainerStatus(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldContainerStatus is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldContainerStatus requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldContainerStatus: %w", err) + } + return oldValue.ContainerStatus, nil +} + +// ClearContainerStatus clears the value of the "container_status" field. +func (m *AgentMutation) ClearContainerStatus() { + m.container_status = nil + m.clearedFields[agent.FieldContainerStatus] = struct{}{} +} + +// ContainerStatusCleared returns if the "container_status" field was cleared in this mutation. +func (m *AgentMutation) ContainerStatusCleared() bool { + _, ok := m.clearedFields[agent.FieldContainerStatus] + return ok +} + +// ResetContainerStatus resets all changes to the "container_status" field. +func (m *AgentMutation) ResetContainerStatus() { + m.container_status = nil + delete(m.clearedFields, agent.FieldContainerStatus) +} + +// SetRuntimeState sets the "runtime_state" field. +func (m *AgentMutation) SetRuntimeState(s string) { + m.runtime_state = &s +} + +// RuntimeState returns the value of the "runtime_state" field in the mutation. +func (m *AgentMutation) RuntimeState() (r string, exists bool) { + v := m.runtime_state + if v == nil { + return + } + return *v, true +} + +// OldRuntimeState returns the old "runtime_state" field's value of the Agent entity. +// If the Agent object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *AgentMutation) OldRuntimeState(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldRuntimeState is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldRuntimeState requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldRuntimeState: %w", err) + } + return oldValue.RuntimeState, nil +} + +// ClearRuntimeState clears the value of the "runtime_state" field. +func (m *AgentMutation) ClearRuntimeState() { + m.runtime_state = nil + m.clearedFields[agent.FieldRuntimeState] = struct{}{} +} + +// RuntimeStateCleared returns if the "runtime_state" field was cleared in this mutation. +func (m *AgentMutation) RuntimeStateCleared() bool { + _, ok := m.clearedFields[agent.FieldRuntimeState] + return ok +} + +// ResetRuntimeState resets all changes to the "runtime_state" field. +func (m *AgentMutation) ResetRuntimeState() { + m.runtime_state = nil + delete(m.clearedFields, agent.FieldRuntimeState) +} + +// SetStalledFromActivity sets the "stalled_from_activity" field. +func (m *AgentMutation) SetStalledFromActivity(s string) { + m.stalled_from_activity = &s +} + +// StalledFromActivity returns the value of the "stalled_from_activity" field in the mutation. +func (m *AgentMutation) StalledFromActivity() (r string, exists bool) { + v := m.stalled_from_activity + if v == nil { + return + } + return *v, true +} + +// OldStalledFromActivity returns the old "stalled_from_activity" field's value of the Agent entity. +// If the Agent object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *AgentMutation) OldStalledFromActivity(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldStalledFromActivity is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldStalledFromActivity requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldStalledFromActivity: %w", err) + } + return oldValue.StalledFromActivity, nil +} + +// ClearStalledFromActivity clears the value of the "stalled_from_activity" field. +func (m *AgentMutation) ClearStalledFromActivity() { + m.stalled_from_activity = nil + m.clearedFields[agent.FieldStalledFromActivity] = struct{}{} +} + +// StalledFromActivityCleared returns if the "stalled_from_activity" field was cleared in this mutation. +func (m *AgentMutation) StalledFromActivityCleared() bool { + _, ok := m.clearedFields[agent.FieldStalledFromActivity] + return ok +} + +// ResetStalledFromActivity resets all changes to the "stalled_from_activity" field. +func (m *AgentMutation) ResetStalledFromActivity() { + m.stalled_from_activity = nil + delete(m.clearedFields, agent.FieldStalledFromActivity) +} + +// SetCurrentTurns sets the "current_turns" field. +func (m *AgentMutation) SetCurrentTurns(i int) { + m.current_turns = &i + m.addcurrent_turns = nil +} + +// CurrentTurns returns the value of the "current_turns" field in the mutation. +func (m *AgentMutation) CurrentTurns() (r int, exists bool) { + v := m.current_turns + if v == nil { + return + } + return *v, true +} + +// OldCurrentTurns returns the old "current_turns" field's value of the Agent entity. +// If the Agent object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *AgentMutation) OldCurrentTurns(ctx context.Context) (v int, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCurrentTurns is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCurrentTurns requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCurrentTurns: %w", err) + } + return oldValue.CurrentTurns, nil +} + +// AddCurrentTurns adds i to the "current_turns" field. +func (m *AgentMutation) AddCurrentTurns(i int) { + if m.addcurrent_turns != nil { + *m.addcurrent_turns += i + } else { + m.addcurrent_turns = &i + } +} + +// AddedCurrentTurns returns the value that was added to the "current_turns" field in this mutation. +func (m *AgentMutation) AddedCurrentTurns() (r int, exists bool) { + v := m.addcurrent_turns + if v == nil { + return + } + return *v, true +} + +// ResetCurrentTurns resets all changes to the "current_turns" field. +func (m *AgentMutation) ResetCurrentTurns() { + m.current_turns = nil + m.addcurrent_turns = nil +} + +// SetCurrentModelCalls sets the "current_model_calls" field. +func (m *AgentMutation) SetCurrentModelCalls(i int) { + m.current_model_calls = &i + m.addcurrent_model_calls = nil +} + +// CurrentModelCalls returns the value of the "current_model_calls" field in the mutation. +func (m *AgentMutation) CurrentModelCalls() (r int, exists bool) { + v := m.current_model_calls + if v == nil { + return + } + return *v, true +} + +// OldCurrentModelCalls returns the old "current_model_calls" field's value of the Agent entity. +// If the Agent object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *AgentMutation) OldCurrentModelCalls(ctx context.Context) (v int, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCurrentModelCalls is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCurrentModelCalls requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCurrentModelCalls: %w", err) + } + return oldValue.CurrentModelCalls, nil +} + +// AddCurrentModelCalls adds i to the "current_model_calls" field. +func (m *AgentMutation) AddCurrentModelCalls(i int) { + if m.addcurrent_model_calls != nil { + *m.addcurrent_model_calls += i + } else { + m.addcurrent_model_calls = &i + } +} + +// AddedCurrentModelCalls returns the value that was added to the "current_model_calls" field in this mutation. +func (m *AgentMutation) AddedCurrentModelCalls() (r int, exists bool) { + v := m.addcurrent_model_calls + if v == nil { + return + } + return *v, true +} + +// ResetCurrentModelCalls resets all changes to the "current_model_calls" field. +func (m *AgentMutation) ResetCurrentModelCalls() { + m.current_model_calls = nil + m.addcurrent_model_calls = nil +} + +// SetImage sets the "image" field. +func (m *AgentMutation) SetImage(s string) { + m.image = &s +} + +// Image returns the value of the "image" field in the mutation. +func (m *AgentMutation) Image() (r string, exists bool) { + v := m.image + if v == nil { + return + } + return *v, true +} + +// OldImage returns the old "image" field's value of the Agent entity. +// If the Agent object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *AgentMutation) OldImage(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldImage is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldImage requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldImage: %w", err) + } + return oldValue.Image, nil +} + +// ClearImage clears the value of the "image" field. +func (m *AgentMutation) ClearImage() { + m.image = nil + m.clearedFields[agent.FieldImage] = struct{}{} +} + +// ImageCleared returns if the "image" field was cleared in this mutation. +func (m *AgentMutation) ImageCleared() bool { + _, ok := m.clearedFields[agent.FieldImage] + return ok +} + +// ResetImage resets all changes to the "image" field. +func (m *AgentMutation) ResetImage() { + m.image = nil + delete(m.clearedFields, agent.FieldImage) +} + +// SetDetached sets the "detached" field. +func (m *AgentMutation) SetDetached(b bool) { + m.detached = &b +} + +// Detached returns the value of the "detached" field in the mutation. +func (m *AgentMutation) Detached() (r bool, exists bool) { + v := m.detached + if v == nil { + return + } + return *v, true +} + +// OldDetached returns the old "detached" field's value of the Agent entity. +// If the Agent object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *AgentMutation) OldDetached(ctx context.Context) (v bool, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldDetached is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldDetached requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldDetached: %w", err) + } + return oldValue.Detached, nil +} + +// ResetDetached resets all changes to the "detached" field. +func (m *AgentMutation) ResetDetached() { + m.detached = nil +} + +// SetRuntime sets the "runtime" field. +func (m *AgentMutation) SetRuntime(s string) { + m.runtime = &s +} + +// Runtime returns the value of the "runtime" field in the mutation. +func (m *AgentMutation) Runtime() (r string, exists bool) { + v := m.runtime + if v == nil { + return + } + return *v, true +} + +// OldRuntime returns the old "runtime" field's value of the Agent entity. +// If the Agent object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *AgentMutation) OldRuntime(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldRuntime is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldRuntime requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldRuntime: %w", err) + } + return oldValue.Runtime, nil +} + +// ClearRuntime clears the value of the "runtime" field. +func (m *AgentMutation) ClearRuntime() { + m.runtime = nil + m.clearedFields[agent.FieldRuntime] = struct{}{} +} + +// RuntimeCleared returns if the "runtime" field was cleared in this mutation. +func (m *AgentMutation) RuntimeCleared() bool { + _, ok := m.clearedFields[agent.FieldRuntime] + return ok +} + +// ResetRuntime resets all changes to the "runtime" field. +func (m *AgentMutation) ResetRuntime() { + m.runtime = nil + delete(m.clearedFields, agent.FieldRuntime) +} + +// SetRuntimeBrokerID sets the "runtime_broker_id" field. +func (m *AgentMutation) SetRuntimeBrokerID(s string) { + m.runtime_broker_id = &s +} + +// RuntimeBrokerID returns the value of the "runtime_broker_id" field in the mutation. +func (m *AgentMutation) RuntimeBrokerID() (r string, exists bool) { + v := m.runtime_broker_id + if v == nil { + return + } + return *v, true +} + +// OldRuntimeBrokerID returns the old "runtime_broker_id" field's value of the Agent entity. +// If the Agent object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *AgentMutation) OldRuntimeBrokerID(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldRuntimeBrokerID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldRuntimeBrokerID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldRuntimeBrokerID: %w", err) + } + return oldValue.RuntimeBrokerID, nil +} + +// ClearRuntimeBrokerID clears the value of the "runtime_broker_id" field. +func (m *AgentMutation) ClearRuntimeBrokerID() { + m.runtime_broker_id = nil + m.clearedFields[agent.FieldRuntimeBrokerID] = struct{}{} +} + +// RuntimeBrokerIDCleared returns if the "runtime_broker_id" field was cleared in this mutation. +func (m *AgentMutation) RuntimeBrokerIDCleared() bool { + _, ok := m.clearedFields[agent.FieldRuntimeBrokerID] + return ok +} + +// ResetRuntimeBrokerID resets all changes to the "runtime_broker_id" field. +func (m *AgentMutation) ResetRuntimeBrokerID() { + m.runtime_broker_id = nil + delete(m.clearedFields, agent.FieldRuntimeBrokerID) +} + +// SetWebPtyEnabled sets the "web_pty_enabled" field. +func (m *AgentMutation) SetWebPtyEnabled(b bool) { + m.web_pty_enabled = &b +} + +// WebPtyEnabled returns the value of the "web_pty_enabled" field in the mutation. +func (m *AgentMutation) WebPtyEnabled() (r bool, exists bool) { + v := m.web_pty_enabled + if v == nil { + return + } + return *v, true +} + +// OldWebPtyEnabled returns the old "web_pty_enabled" field's value of the Agent entity. +// If the Agent object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *AgentMutation) OldWebPtyEnabled(ctx context.Context) (v bool, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldWebPtyEnabled is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldWebPtyEnabled requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldWebPtyEnabled: %w", err) + } + return oldValue.WebPtyEnabled, nil +} + +// ResetWebPtyEnabled resets all changes to the "web_pty_enabled" field. +func (m *AgentMutation) ResetWebPtyEnabled() { + m.web_pty_enabled = nil +} + +// SetTaskSummary sets the "task_summary" field. +func (m *AgentMutation) SetTaskSummary(s string) { + m.task_summary = &s +} + +// TaskSummary returns the value of the "task_summary" field in the mutation. +func (m *AgentMutation) TaskSummary() (r string, exists bool) { + v := m.task_summary + if v == nil { + return + } + return *v, true +} + +// OldTaskSummary returns the old "task_summary" field's value of the Agent entity. +// If the Agent object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *AgentMutation) OldTaskSummary(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldTaskSummary is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldTaskSummary requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldTaskSummary: %w", err) + } + return oldValue.TaskSummary, nil +} + +// ClearTaskSummary clears the value of the "task_summary" field. +func (m *AgentMutation) ClearTaskSummary() { + m.task_summary = nil + m.clearedFields[agent.FieldTaskSummary] = struct{}{} +} + +// TaskSummaryCleared returns if the "task_summary" field was cleared in this mutation. +func (m *AgentMutation) TaskSummaryCleared() bool { + _, ok := m.clearedFields[agent.FieldTaskSummary] + return ok +} + +// ResetTaskSummary resets all changes to the "task_summary" field. +func (m *AgentMutation) ResetTaskSummary() { + m.task_summary = nil + delete(m.clearedFields, agent.FieldTaskSummary) +} + +// SetMessage sets the "message" field. +func (m *AgentMutation) SetMessage(s string) { + m.message = &s +} + +// Message returns the value of the "message" field in the mutation. +func (m *AgentMutation) Message() (r string, exists bool) { + v := m.message + if v == nil { + return + } + return *v, true +} + +// OldMessage returns the old "message" field's value of the Agent entity. +// If the Agent object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *AgentMutation) OldMessage(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldMessage is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldMessage requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldMessage: %w", err) + } + return oldValue.Message, nil +} + +// ClearMessage clears the value of the "message" field. +func (m *AgentMutation) ClearMessage() { + m.message = nil + m.clearedFields[agent.FieldMessage] = struct{}{} +} + +// MessageCleared returns if the "message" field was cleared in this mutation. +func (m *AgentMutation) MessageCleared() bool { + _, ok := m.clearedFields[agent.FieldMessage] + return ok +} + +// ResetMessage resets all changes to the "message" field. +func (m *AgentMutation) ResetMessage() { + m.message = nil + delete(m.clearedFields, agent.FieldMessage) +} + +// SetAppliedConfig sets the "applied_config" field. +func (m *AgentMutation) SetAppliedConfig(s string) { + m.applied_config = &s +} + +// AppliedConfig returns the value of the "applied_config" field in the mutation. +func (m *AgentMutation) AppliedConfig() (r string, exists bool) { + v := m.applied_config + if v == nil { + return + } + return *v, true +} + +// OldAppliedConfig returns the old "applied_config" field's value of the Agent entity. +// If the Agent object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *AgentMutation) OldAppliedConfig(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldAppliedConfig is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldAppliedConfig requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldAppliedConfig: %w", err) + } + return oldValue.AppliedConfig, nil +} + +// ClearAppliedConfig clears the value of the "applied_config" field. +func (m *AgentMutation) ClearAppliedConfig() { + m.applied_config = nil + m.clearedFields[agent.FieldAppliedConfig] = struct{}{} +} + +// AppliedConfigCleared returns if the "applied_config" field was cleared in this mutation. +func (m *AgentMutation) AppliedConfigCleared() bool { + _, ok := m.clearedFields[agent.FieldAppliedConfig] + return ok +} + +// ResetAppliedConfig resets all changes to the "applied_config" field. +func (m *AgentMutation) ResetAppliedConfig() { + m.applied_config = nil + delete(m.clearedFields, agent.FieldAppliedConfig) +} + +// SetAncestry sets the "ancestry" field. +func (m *AgentMutation) SetAncestry(s []string) { + m.ancestry = &s + m.appendancestry = nil +} + +// Ancestry returns the value of the "ancestry" field in the mutation. +func (m *AgentMutation) Ancestry() (r []string, exists bool) { + v := m.ancestry + if v == nil { + return + } + return *v, true +} + +// OldAncestry returns the old "ancestry" field's value of the Agent entity. +// If the Agent object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *AgentMutation) OldAncestry(ctx context.Context) (v []string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldAncestry is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldAncestry requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldAncestry: %w", err) + } + return oldValue.Ancestry, nil +} + +// AppendAncestry adds s to the "ancestry" field. +func (m *AgentMutation) AppendAncestry(s []string) { + m.appendancestry = append(m.appendancestry, s...) +} + +// AppendedAncestry returns the list of values that were appended to the "ancestry" field in this mutation. +func (m *AgentMutation) AppendedAncestry() ([]string, bool) { + if len(m.appendancestry) == 0 { + return nil, false + } + return m.appendancestry, true +} + +// ClearAncestry clears the value of the "ancestry" field. +func (m *AgentMutation) ClearAncestry() { + m.ancestry = nil + m.appendancestry = nil + m.clearedFields[agent.FieldAncestry] = struct{}{} +} + +// AncestryCleared returns if the "ancestry" field was cleared in this mutation. +func (m *AgentMutation) AncestryCleared() bool { + _, ok := m.clearedFields[agent.FieldAncestry] + return ok +} + +// ResetAncestry resets all changes to the "ancestry" field. +func (m *AgentMutation) ResetAncestry() { + m.ancestry = nil + m.appendancestry = nil + delete(m.clearedFields, agent.FieldAncestry) +} + +// SetCreated sets the "created" field. +func (m *AgentMutation) SetCreated(t time.Time) { + m.created = &t +} + +// Created returns the value of the "created" field in the mutation. +func (m *AgentMutation) Created() (r time.Time, exists bool) { + v := m.created + if v == nil { + return + } + return *v, true +} + +// OldCreated returns the old "created" field's value of the Agent entity. +// If the Agent object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *AgentMutation) OldCreated(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreated is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreated requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreated: %w", err) + } + return oldValue.Created, nil +} + +// ResetCreated resets all changes to the "created" field. +func (m *AgentMutation) ResetCreated() { + m.created = nil +} + +// SetUpdated sets the "updated" field. +func (m *AgentMutation) SetUpdated(t time.Time) { + m.updated = &t +} + +// Updated returns the value of the "updated" field in the mutation. +func (m *AgentMutation) Updated() (r time.Time, exists bool) { + v := m.updated + if v == nil { + return + } + return *v, true +} + +// OldUpdated returns the old "updated" field's value of the Agent entity. +// If the Agent object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *AgentMutation) OldUpdated(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldUpdated is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldUpdated requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldUpdated: %w", err) + } + return oldValue.Updated, nil +} + +// ResetUpdated resets all changes to the "updated" field. +func (m *AgentMutation) ResetUpdated() { + m.updated = nil +} + +// SetLastSeen sets the "last_seen" field. +func (m *AgentMutation) SetLastSeen(t time.Time) { + m.last_seen = &t +} + +// LastSeen returns the value of the "last_seen" field in the mutation. +func (m *AgentMutation) LastSeen() (r time.Time, exists bool) { + v := m.last_seen + if v == nil { + return + } + return *v, true +} + +// OldLastSeen returns the old "last_seen" field's value of the Agent entity. +// If the Agent object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *AgentMutation) OldLastSeen(ctx context.Context) (v *time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldLastSeen is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldLastSeen requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldLastSeen: %w", err) + } + return oldValue.LastSeen, nil +} + +// ClearLastSeen clears the value of the "last_seen" field. +func (m *AgentMutation) ClearLastSeen() { + m.last_seen = nil + m.clearedFields[agent.FieldLastSeen] = struct{}{} +} + +// LastSeenCleared returns if the "last_seen" field was cleared in this mutation. +func (m *AgentMutation) LastSeenCleared() bool { + _, ok := m.clearedFields[agent.FieldLastSeen] + return ok +} + +// ResetLastSeen resets all changes to the "last_seen" field. +func (m *AgentMutation) ResetLastSeen() { + m.last_seen = nil + delete(m.clearedFields, agent.FieldLastSeen) +} + +// SetLastActivityEvent sets the "last_activity_event" field. +func (m *AgentMutation) SetLastActivityEvent(t time.Time) { + m.last_activity_event = &t +} + +// LastActivityEvent returns the value of the "last_activity_event" field in the mutation. +func (m *AgentMutation) LastActivityEvent() (r time.Time, exists bool) { + v := m.last_activity_event + if v == nil { + return + } + return *v, true +} + +// OldLastActivityEvent returns the old "last_activity_event" field's value of the Agent entity. +// If the Agent object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *AgentMutation) OldLastActivityEvent(ctx context.Context) (v *time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldLastActivityEvent is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldLastActivityEvent requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldLastActivityEvent: %w", err) + } + return oldValue.LastActivityEvent, nil +} + +// ClearLastActivityEvent clears the value of the "last_activity_event" field. +func (m *AgentMutation) ClearLastActivityEvent() { + m.last_activity_event = nil + m.clearedFields[agent.FieldLastActivityEvent] = struct{}{} +} + +// LastActivityEventCleared returns if the "last_activity_event" field was cleared in this mutation. +func (m *AgentMutation) LastActivityEventCleared() bool { + _, ok := m.clearedFields[agent.FieldLastActivityEvent] + return ok +} + +// ResetLastActivityEvent resets all changes to the "last_activity_event" field. +func (m *AgentMutation) ResetLastActivityEvent() { + m.last_activity_event = nil + delete(m.clearedFields, agent.FieldLastActivityEvent) +} + +// SetStartedAt sets the "started_at" field. +func (m *AgentMutation) SetStartedAt(t time.Time) { + m.started_at = &t +} + +// StartedAt returns the value of the "started_at" field in the mutation. +func (m *AgentMutation) StartedAt() (r time.Time, exists bool) { + v := m.started_at + if v == nil { + return + } + return *v, true +} + +// OldStartedAt returns the old "started_at" field's value of the Agent entity. +// If the Agent object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *AgentMutation) OldStartedAt(ctx context.Context) (v *time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldStartedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldStartedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldStartedAt: %w", err) + } + return oldValue.StartedAt, nil +} + +// ClearStartedAt clears the value of the "started_at" field. +func (m *AgentMutation) ClearStartedAt() { + m.started_at = nil + m.clearedFields[agent.FieldStartedAt] = struct{}{} +} + +// StartedAtCleared returns if the "started_at" field was cleared in this mutation. +func (m *AgentMutation) StartedAtCleared() bool { + _, ok := m.clearedFields[agent.FieldStartedAt] + return ok +} + +// ResetStartedAt resets all changes to the "started_at" field. +func (m *AgentMutation) ResetStartedAt() { + m.started_at = nil + delete(m.clearedFields, agent.FieldStartedAt) +} + +// SetDeletedAt sets the "deleted_at" field. +func (m *AgentMutation) SetDeletedAt(t time.Time) { + m.deleted_at = &t +} + +// DeletedAt returns the value of the "deleted_at" field in the mutation. +func (m *AgentMutation) DeletedAt() (r time.Time, exists bool) { + v := m.deleted_at + if v == nil { + return + } + return *v, true +} + +// OldDeletedAt returns the old "deleted_at" field's value of the Agent entity. +// If the Agent object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *AgentMutation) OldDeletedAt(ctx context.Context) (v *time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldDeletedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldDeletedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldDeletedAt: %w", err) + } + return oldValue.DeletedAt, nil +} + +// ClearDeletedAt clears the value of the "deleted_at" field. +func (m *AgentMutation) ClearDeletedAt() { + m.deleted_at = nil + m.clearedFields[agent.FieldDeletedAt] = struct{}{} +} + +// DeletedAtCleared returns if the "deleted_at" field was cleared in this mutation. +func (m *AgentMutation) DeletedAtCleared() bool { + _, ok := m.clearedFields[agent.FieldDeletedAt] + return ok +} + +// ResetDeletedAt resets all changes to the "deleted_at" field. +func (m *AgentMutation) ResetDeletedAt() { + m.deleted_at = nil + delete(m.clearedFields, agent.FieldDeletedAt) +} + +// SetStateVersion sets the "state_version" field. +func (m *AgentMutation) SetStateVersion(i int64) { + m.state_version = &i + m.addstate_version = nil +} + +// StateVersion returns the value of the "state_version" field in the mutation. +func (m *AgentMutation) StateVersion() (r int64, exists bool) { + v := m.state_version + if v == nil { + return + } + return *v, true +} + +// OldStateVersion returns the old "state_version" field's value of the Agent entity. +// If the Agent object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *AgentMutation) OldStateVersion(ctx context.Context) (v int64, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldStateVersion is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldStateVersion requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldStateVersion: %w", err) + } + return oldValue.StateVersion, nil +} + +// AddStateVersion adds i to the "state_version" field. +func (m *AgentMutation) AddStateVersion(i int64) { + if m.addstate_version != nil { + *m.addstate_version += i + } else { + m.addstate_version = &i + } +} + +// AddedStateVersion returns the value that was added to the "state_version" field in this mutation. +func (m *AgentMutation) AddedStateVersion() (r int64, exists bool) { + v := m.addstate_version + if v == nil { + return + } + return *v, true +} + +// ResetStateVersion resets all changes to the "state_version" field. +func (m *AgentMutation) ResetStateVersion() { + m.state_version = nil + m.addstate_version = nil +} + +// ClearProject clears the "project" edge to the Project entity. +func (m *AgentMutation) ClearProject() { + m.clearedproject = true + m.clearedFields[agent.FieldProjectID] = struct{}{} +} + +// ProjectCleared reports if the "project" edge to the Project entity was cleared. +func (m *AgentMutation) ProjectCleared() bool { + return m.clearedproject +} + +// ProjectIDs returns the "project" edge IDs in the mutation. +// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use +// ProjectID instead. It exists only for internal usage by the builders. +func (m *AgentMutation) ProjectIDs() (ids []uuid.UUID) { + if id := m.project; id != nil { + ids = append(ids, *id) + } + return +} + +// ResetProject resets all changes to the "project" edge. +func (m *AgentMutation) ResetProject() { + m.project = nil + m.clearedproject = false +} + +// SetCreatorID sets the "creator" edge to the User entity by id. +func (m *AgentMutation) SetCreatorID(id uuid.UUID) { + m.creator = &id +} + +// ClearCreator clears the "creator" edge to the User entity. +func (m *AgentMutation) ClearCreator() { + m.clearedcreator = true + m.clearedFields[agent.FieldCreatedBy] = struct{}{} +} + +// CreatorCleared reports if the "creator" edge to the User entity was cleared. +func (m *AgentMutation) CreatorCleared() bool { + return m.CreatedByCleared() || m.clearedcreator +} + +// CreatorID returns the "creator" edge ID in the mutation. +func (m *AgentMutation) CreatorID() (id uuid.UUID, exists bool) { + if m.creator != nil { + return *m.creator, true + } + return +} + +// CreatorIDs returns the "creator" edge IDs in the mutation. +// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use +// CreatorID instead. It exists only for internal usage by the builders. +func (m *AgentMutation) CreatorIDs() (ids []uuid.UUID) { + if id := m.creator; id != nil { + ids = append(ids, *id) + } + return +} + +// ResetCreator resets all changes to the "creator" edge. +func (m *AgentMutation) ResetCreator() { + m.creator = nil + m.clearedcreator = false +} + +// ClearOwner clears the "owner" edge to the User entity. +func (m *AgentMutation) ClearOwner() { + m.clearedowner = true + m.clearedFields[agent.FieldOwnerID] = struct{}{} +} + +// OwnerCleared reports if the "owner" edge to the User entity was cleared. +func (m *AgentMutation) OwnerCleared() bool { + return m.OwnerIDCleared() || m.clearedowner +} + +// OwnerIDs returns the "owner" edge IDs in the mutation. +// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use +// OwnerID instead. It exists only for internal usage by the builders. +func (m *AgentMutation) OwnerIDs() (ids []uuid.UUID) { + if id := m.owner; id != nil { + ids = append(ids, *id) + } + return +} + +// ResetOwner resets all changes to the "owner" edge. +func (m *AgentMutation) ResetOwner() { + m.owner = nil + m.clearedowner = false +} + +// AddMembershipIDs adds the "memberships" edge to the GroupMembership entity by ids. +func (m *AgentMutation) AddMembershipIDs(ids ...uuid.UUID) { + if m.memberships == nil { + m.memberships = make(map[uuid.UUID]struct{}) + } + for i := range ids { + m.memberships[ids[i]] = struct{}{} + } +} + +// ClearMemberships clears the "memberships" edge to the GroupMembership entity. +func (m *AgentMutation) ClearMemberships() { + m.clearedmemberships = true +} + +// MembershipsCleared reports if the "memberships" edge to the GroupMembership entity was cleared. +func (m *AgentMutation) MembershipsCleared() bool { + return m.clearedmemberships +} + +// RemoveMembershipIDs removes the "memberships" edge to the GroupMembership entity by IDs. +func (m *AgentMutation) RemoveMembershipIDs(ids ...uuid.UUID) { + if m.removedmemberships == nil { + m.removedmemberships = make(map[uuid.UUID]struct{}) + } + for i := range ids { + delete(m.memberships, ids[i]) + m.removedmemberships[ids[i]] = struct{}{} + } +} + +// RemovedMemberships returns the removed IDs of the "memberships" edge to the GroupMembership entity. +func (m *AgentMutation) RemovedMembershipsIDs() (ids []uuid.UUID) { + for id := range m.removedmemberships { + ids = append(ids, id) + } + return +} + +// MembershipsIDs returns the "memberships" edge IDs in the mutation. +func (m *AgentMutation) MembershipsIDs() (ids []uuid.UUID) { + for id := range m.memberships { + ids = append(ids, id) + } + return +} + +// ResetMemberships resets all changes to the "memberships" edge. +func (m *AgentMutation) ResetMemberships() { + m.memberships = nil + m.clearedmemberships = false + m.removedmemberships = nil +} + +// AddPolicyBindingIDs adds the "policy_bindings" edge to the PolicyBinding entity by ids. +func (m *AgentMutation) AddPolicyBindingIDs(ids ...uuid.UUID) { + if m.policy_bindings == nil { + m.policy_bindings = make(map[uuid.UUID]struct{}) + } + for i := range ids { + m.policy_bindings[ids[i]] = struct{}{} + } +} + +// ClearPolicyBindings clears the "policy_bindings" edge to the PolicyBinding entity. +func (m *AgentMutation) ClearPolicyBindings() { + m.clearedpolicy_bindings = true +} + +// PolicyBindingsCleared reports if the "policy_bindings" edge to the PolicyBinding entity was cleared. +func (m *AgentMutation) PolicyBindingsCleared() bool { + return m.clearedpolicy_bindings +} + +// RemovePolicyBindingIDs removes the "policy_bindings" edge to the PolicyBinding entity by IDs. +func (m *AgentMutation) RemovePolicyBindingIDs(ids ...uuid.UUID) { + if m.removedpolicy_bindings == nil { + m.removedpolicy_bindings = make(map[uuid.UUID]struct{}) + } + for i := range ids { + delete(m.policy_bindings, ids[i]) + m.removedpolicy_bindings[ids[i]] = struct{}{} + } +} + +// RemovedPolicyBindings returns the removed IDs of the "policy_bindings" edge to the PolicyBinding entity. +func (m *AgentMutation) RemovedPolicyBindingsIDs() (ids []uuid.UUID) { + for id := range m.removedpolicy_bindings { + ids = append(ids, id) + } + return +} + +// PolicyBindingsIDs returns the "policy_bindings" edge IDs in the mutation. +func (m *AgentMutation) PolicyBindingsIDs() (ids []uuid.UUID) { + for id := range m.policy_bindings { + ids = append(ids, id) + } + return +} + +// ResetPolicyBindings resets all changes to the "policy_bindings" edge. +func (m *AgentMutation) ResetPolicyBindings() { + m.policy_bindings = nil + m.clearedpolicy_bindings = false + m.removedpolicy_bindings = nil +} + +// Where appends a list predicates to the AgentMutation builder. +func (m *AgentMutation) Where(ps ...predicate.Agent) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the AgentMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *AgentMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.Agent, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *AgentMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *AgentMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (Agent). +func (m *AgentMutation) Type() string { + return m.typ +} + +// Fields returns all fields that were changed during this mutation. Note that in +// order to get all numeric fields that were incremented/decremented, call +// AddedFields(). +func (m *AgentMutation) Fields() []string { + fields := make([]string, 0, 36) + if m.slug != nil { + fields = append(fields, agent.FieldSlug) + } + if m.name != nil { + fields = append(fields, agent.FieldName) + } + if m.template != nil { + fields = append(fields, agent.FieldTemplate) + } + if m.project != nil { + fields = append(fields, agent.FieldProjectID) + } + if m.status != nil { + fields = append(fields, agent.FieldStatus) + } + if m.creator != nil { + fields = append(fields, agent.FieldCreatedBy) + } + if m.owner != nil { + fields = append(fields, agent.FieldOwnerID) + } + if m.delegation_enabled != nil { + fields = append(fields, agent.FieldDelegationEnabled) + } + if m.visibility != nil { + fields = append(fields, agent.FieldVisibility) + } + if m.labels != nil { + fields = append(fields, agent.FieldLabels) + } + if m.annotations != nil { + fields = append(fields, agent.FieldAnnotations) + } + if m.phase != nil { + fields = append(fields, agent.FieldPhase) + } + if m.activity != nil { + fields = append(fields, agent.FieldActivity) + } + if m.tool_name != nil { + fields = append(fields, agent.FieldToolName) + } + if m.connection_state != nil { + fields = append(fields, agent.FieldConnectionState) + } + if m.container_status != nil { + fields = append(fields, agent.FieldContainerStatus) + } + if m.runtime_state != nil { + fields = append(fields, agent.FieldRuntimeState) + } + if m.stalled_from_activity != nil { + fields = append(fields, agent.FieldStalledFromActivity) + } + if m.current_turns != nil { + fields = append(fields, agent.FieldCurrentTurns) + } + if m.current_model_calls != nil { + fields = append(fields, agent.FieldCurrentModelCalls) + } + if m.image != nil { + fields = append(fields, agent.FieldImage) + } + if m.detached != nil { + fields = append(fields, agent.FieldDetached) + } + if m.runtime != nil { + fields = append(fields, agent.FieldRuntime) + } + if m.runtime_broker_id != nil { + fields = append(fields, agent.FieldRuntimeBrokerID) + } + if m.web_pty_enabled != nil { + fields = append(fields, agent.FieldWebPtyEnabled) + } + if m.task_summary != nil { + fields = append(fields, agent.FieldTaskSummary) + } + if m.message != nil { + fields = append(fields, agent.FieldMessage) + } + if m.applied_config != nil { + fields = append(fields, agent.FieldAppliedConfig) + } + if m.ancestry != nil { + fields = append(fields, agent.FieldAncestry) + } + if m.created != nil { + fields = append(fields, agent.FieldCreated) + } + if m.updated != nil { + fields = append(fields, agent.FieldUpdated) + } + if m.last_seen != nil { + fields = append(fields, agent.FieldLastSeen) + } + if m.last_activity_event != nil { + fields = append(fields, agent.FieldLastActivityEvent) + } + if m.started_at != nil { + fields = append(fields, agent.FieldStartedAt) + } + if m.deleted_at != nil { + fields = append(fields, agent.FieldDeletedAt) + } + if m.state_version != nil { + fields = append(fields, agent.FieldStateVersion) + } + return fields +} + +// Field returns the value of a field with the given name. The second boolean +// return value indicates that this field was not set, or was not defined in the +// schema. +func (m *AgentMutation) Field(name string) (ent.Value, bool) { + switch name { + case agent.FieldSlug: + return m.Slug() + case agent.FieldName: + return m.Name() + case agent.FieldTemplate: + return m.Template() + case agent.FieldProjectID: + return m.ProjectID() + case agent.FieldStatus: + return m.Status() + case agent.FieldCreatedBy: + return m.CreatedBy() + case agent.FieldOwnerID: + return m.OwnerID() + case agent.FieldDelegationEnabled: + return m.DelegationEnabled() + case agent.FieldVisibility: + return m.Visibility() + case agent.FieldLabels: + return m.Labels() + case agent.FieldAnnotations: + return m.Annotations() + case agent.FieldPhase: + return m.Phase() + case agent.FieldActivity: + return m.Activity() + case agent.FieldToolName: + return m.ToolName() + case agent.FieldConnectionState: + return m.ConnectionState() + case agent.FieldContainerStatus: + return m.ContainerStatus() + case agent.FieldRuntimeState: + return m.RuntimeState() + case agent.FieldStalledFromActivity: + return m.StalledFromActivity() + case agent.FieldCurrentTurns: + return m.CurrentTurns() + case agent.FieldCurrentModelCalls: + return m.CurrentModelCalls() + case agent.FieldImage: + return m.Image() + case agent.FieldDetached: + return m.Detached() + case agent.FieldRuntime: + return m.Runtime() + case agent.FieldRuntimeBrokerID: + return m.RuntimeBrokerID() + case agent.FieldWebPtyEnabled: + return m.WebPtyEnabled() + case agent.FieldTaskSummary: + return m.TaskSummary() + case agent.FieldMessage: + return m.Message() + case agent.FieldAppliedConfig: + return m.AppliedConfig() + case agent.FieldAncestry: + return m.Ancestry() + case agent.FieldCreated: + return m.Created() + case agent.FieldUpdated: + return m.Updated() + case agent.FieldLastSeen: + return m.LastSeen() + case agent.FieldLastActivityEvent: + return m.LastActivityEvent() + case agent.FieldStartedAt: + return m.StartedAt() + case agent.FieldDeletedAt: + return m.DeletedAt() + case agent.FieldStateVersion: + return m.StateVersion() + } + return nil, false +} + +// OldField returns the old value of the field from the database. An error is +// returned if the mutation operation is not UpdateOne, or the query to the +// database failed. +func (m *AgentMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + switch name { + case agent.FieldSlug: + return m.OldSlug(ctx) + case agent.FieldName: + return m.OldName(ctx) + case agent.FieldTemplate: + return m.OldTemplate(ctx) + case agent.FieldProjectID: + return m.OldProjectID(ctx) + case agent.FieldStatus: + return m.OldStatus(ctx) + case agent.FieldCreatedBy: + return m.OldCreatedBy(ctx) + case agent.FieldOwnerID: + return m.OldOwnerID(ctx) + case agent.FieldDelegationEnabled: + return m.OldDelegationEnabled(ctx) + case agent.FieldVisibility: + return m.OldVisibility(ctx) + case agent.FieldLabels: + return m.OldLabels(ctx) + case agent.FieldAnnotations: + return m.OldAnnotations(ctx) + case agent.FieldPhase: + return m.OldPhase(ctx) + case agent.FieldActivity: + return m.OldActivity(ctx) + case agent.FieldToolName: + return m.OldToolName(ctx) + case agent.FieldConnectionState: + return m.OldConnectionState(ctx) + case agent.FieldContainerStatus: + return m.OldContainerStatus(ctx) + case agent.FieldRuntimeState: + return m.OldRuntimeState(ctx) + case agent.FieldStalledFromActivity: + return m.OldStalledFromActivity(ctx) + case agent.FieldCurrentTurns: + return m.OldCurrentTurns(ctx) + case agent.FieldCurrentModelCalls: + return m.OldCurrentModelCalls(ctx) + case agent.FieldImage: + return m.OldImage(ctx) + case agent.FieldDetached: + return m.OldDetached(ctx) + case agent.FieldRuntime: + return m.OldRuntime(ctx) + case agent.FieldRuntimeBrokerID: + return m.OldRuntimeBrokerID(ctx) + case agent.FieldWebPtyEnabled: + return m.OldWebPtyEnabled(ctx) + case agent.FieldTaskSummary: + return m.OldTaskSummary(ctx) + case agent.FieldMessage: + return m.OldMessage(ctx) + case agent.FieldAppliedConfig: + return m.OldAppliedConfig(ctx) + case agent.FieldAncestry: + return m.OldAncestry(ctx) + case agent.FieldCreated: + return m.OldCreated(ctx) + case agent.FieldUpdated: + return m.OldUpdated(ctx) + case agent.FieldLastSeen: + return m.OldLastSeen(ctx) + case agent.FieldLastActivityEvent: + return m.OldLastActivityEvent(ctx) + case agent.FieldStartedAt: + return m.OldStartedAt(ctx) + case agent.FieldDeletedAt: + return m.OldDeletedAt(ctx) + case agent.FieldStateVersion: + return m.OldStateVersion(ctx) + } + return nil, fmt.Errorf("unknown Agent field %s", name) +} + +// SetField sets the value of a field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *AgentMutation) SetField(name string, value ent.Value) error { + switch name { + case agent.FieldSlug: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetSlug(v) + return nil + case agent.FieldName: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetName(v) + return nil + case agent.FieldTemplate: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetTemplate(v) + return nil + case agent.FieldProjectID: + v, ok := value.(uuid.UUID) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetProjectID(v) + return nil + case agent.FieldStatus: + v, ok := value.(agent.Status) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetStatus(v) + return nil + case agent.FieldCreatedBy: + v, ok := value.(uuid.UUID) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreatedBy(v) + return nil + case agent.FieldOwnerID: + v, ok := value.(uuid.UUID) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetOwnerID(v) + return nil + case agent.FieldDelegationEnabled: + v, ok := value.(bool) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetDelegationEnabled(v) + return nil + case agent.FieldVisibility: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetVisibility(v) + return nil + case agent.FieldLabels: + v, ok := value.(map[string]string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetLabels(v) + return nil + case agent.FieldAnnotations: + v, ok := value.(map[string]string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetAnnotations(v) + return nil + case agent.FieldPhase: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetPhase(v) + return nil + case agent.FieldActivity: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetActivity(v) + return nil + case agent.FieldToolName: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetToolName(v) + return nil + case agent.FieldConnectionState: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetConnectionState(v) + return nil + case agent.FieldContainerStatus: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetContainerStatus(v) + return nil + case agent.FieldRuntimeState: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetRuntimeState(v) + return nil + case agent.FieldStalledFromActivity: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetStalledFromActivity(v) + return nil + case agent.FieldCurrentTurns: + v, ok := value.(int) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCurrentTurns(v) + return nil + case agent.FieldCurrentModelCalls: + v, ok := value.(int) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCurrentModelCalls(v) + return nil + case agent.FieldImage: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetImage(v) + return nil + case agent.FieldDetached: + v, ok := value.(bool) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetDetached(v) + return nil + case agent.FieldRuntime: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetRuntime(v) + return nil + case agent.FieldRuntimeBrokerID: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetRuntimeBrokerID(v) + return nil + case agent.FieldWebPtyEnabled: + v, ok := value.(bool) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetWebPtyEnabled(v) + return nil + case agent.FieldTaskSummary: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetTaskSummary(v) + return nil + case agent.FieldMessage: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetMessage(v) + return nil + case agent.FieldAppliedConfig: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetAppliedConfig(v) + return nil + case agent.FieldAncestry: + v, ok := value.([]string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetAncestry(v) + return nil + case agent.FieldCreated: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreated(v) + return nil + case agent.FieldUpdated: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetUpdated(v) + return nil + case agent.FieldLastSeen: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetLastSeen(v) + return nil + case agent.FieldLastActivityEvent: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetLastActivityEvent(v) + return nil + case agent.FieldStartedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetStartedAt(v) + return nil + case agent.FieldDeletedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetDeletedAt(v) + return nil + case agent.FieldStateVersion: + v, ok := value.(int64) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetStateVersion(v) + return nil + } + return fmt.Errorf("unknown Agent field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *AgentMutation) AddedFields() []string { + var fields []string + if m.addcurrent_turns != nil { + fields = append(fields, agent.FieldCurrentTurns) + } + if m.addcurrent_model_calls != nil { + fields = append(fields, agent.FieldCurrentModelCalls) + } + if m.addstate_version != nil { + fields = append(fields, agent.FieldStateVersion) + } + return fields +} + +// AddedField returns the numeric value that was incremented/decremented on a field +// with the given name. The second boolean return value indicates that this field +// was not set, or was not defined in the schema. +func (m *AgentMutation) AddedField(name string) (ent.Value, bool) { + switch name { + case agent.FieldCurrentTurns: + return m.AddedCurrentTurns() + case agent.FieldCurrentModelCalls: + return m.AddedCurrentModelCalls() + case agent.FieldStateVersion: + return m.AddedStateVersion() + } + return nil, false +} + +// AddField adds the value to the field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *AgentMutation) AddField(name string, value ent.Value) error { + switch name { + case agent.FieldCurrentTurns: + v, ok := value.(int) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.AddCurrentTurns(v) + return nil + case agent.FieldCurrentModelCalls: + v, ok := value.(int) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.AddCurrentModelCalls(v) + return nil + case agent.FieldStateVersion: + v, ok := value.(int64) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.AddStateVersion(v) + return nil + } + return fmt.Errorf("unknown Agent numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *AgentMutation) ClearedFields() []string { + var fields []string + if m.FieldCleared(agent.FieldTemplate) { + fields = append(fields, agent.FieldTemplate) + } + if m.FieldCleared(agent.FieldCreatedBy) { + fields = append(fields, agent.FieldCreatedBy) + } + if m.FieldCleared(agent.FieldOwnerID) { + fields = append(fields, agent.FieldOwnerID) + } + if m.FieldCleared(agent.FieldLabels) { + fields = append(fields, agent.FieldLabels) + } + if m.FieldCleared(agent.FieldAnnotations) { + fields = append(fields, agent.FieldAnnotations) + } + if m.FieldCleared(agent.FieldPhase) { + fields = append(fields, agent.FieldPhase) + } + if m.FieldCleared(agent.FieldActivity) { + fields = append(fields, agent.FieldActivity) + } + if m.FieldCleared(agent.FieldToolName) { + fields = append(fields, agent.FieldToolName) + } + if m.FieldCleared(agent.FieldConnectionState) { + fields = append(fields, agent.FieldConnectionState) + } + if m.FieldCleared(agent.FieldContainerStatus) { + fields = append(fields, agent.FieldContainerStatus) + } + if m.FieldCleared(agent.FieldRuntimeState) { + fields = append(fields, agent.FieldRuntimeState) + } + if m.FieldCleared(agent.FieldStalledFromActivity) { + fields = append(fields, agent.FieldStalledFromActivity) + } + if m.FieldCleared(agent.FieldImage) { + fields = append(fields, agent.FieldImage) + } + if m.FieldCleared(agent.FieldRuntime) { + fields = append(fields, agent.FieldRuntime) + } + if m.FieldCleared(agent.FieldRuntimeBrokerID) { + fields = append(fields, agent.FieldRuntimeBrokerID) + } + if m.FieldCleared(agent.FieldTaskSummary) { + fields = append(fields, agent.FieldTaskSummary) + } + if m.FieldCleared(agent.FieldMessage) { + fields = append(fields, agent.FieldMessage) + } + if m.FieldCleared(agent.FieldAppliedConfig) { + fields = append(fields, agent.FieldAppliedConfig) + } + if m.FieldCleared(agent.FieldAncestry) { + fields = append(fields, agent.FieldAncestry) + } + if m.FieldCleared(agent.FieldLastSeen) { + fields = append(fields, agent.FieldLastSeen) + } + if m.FieldCleared(agent.FieldLastActivityEvent) { + fields = append(fields, agent.FieldLastActivityEvent) + } + if m.FieldCleared(agent.FieldStartedAt) { + fields = append(fields, agent.FieldStartedAt) + } + if m.FieldCleared(agent.FieldDeletedAt) { + fields = append(fields, agent.FieldDeletedAt) + } + return fields +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *AgentMutation) FieldCleared(name string) bool { + _, ok := m.clearedFields[name] + return ok +} + +// ClearField clears the value of the field with the given name. It returns an +// error if the field is not defined in the schema. +func (m *AgentMutation) ClearField(name string) error { + switch name { + case agent.FieldTemplate: + m.ClearTemplate() + return nil + case agent.FieldCreatedBy: + m.ClearCreatedBy() + return nil + case agent.FieldOwnerID: + m.ClearOwnerID() + return nil + case agent.FieldLabels: + m.ClearLabels() + return nil + case agent.FieldAnnotations: + m.ClearAnnotations() + return nil + case agent.FieldPhase: + m.ClearPhase() + return nil + case agent.FieldActivity: + m.ClearActivity() + return nil + case agent.FieldToolName: + m.ClearToolName() + return nil + case agent.FieldConnectionState: + m.ClearConnectionState() + return nil + case agent.FieldContainerStatus: + m.ClearContainerStatus() + return nil + case agent.FieldRuntimeState: + m.ClearRuntimeState() + return nil + case agent.FieldStalledFromActivity: + m.ClearStalledFromActivity() + return nil + case agent.FieldImage: + m.ClearImage() + return nil + case agent.FieldRuntime: + m.ClearRuntime() + return nil + case agent.FieldRuntimeBrokerID: + m.ClearRuntimeBrokerID() + return nil + case agent.FieldTaskSummary: + m.ClearTaskSummary() + return nil + case agent.FieldMessage: + m.ClearMessage() + return nil + case agent.FieldAppliedConfig: + m.ClearAppliedConfig() + return nil + case agent.FieldAncestry: + m.ClearAncestry() + return nil + case agent.FieldLastSeen: + m.ClearLastSeen() + return nil + case agent.FieldLastActivityEvent: + m.ClearLastActivityEvent() + return nil + case agent.FieldStartedAt: + m.ClearStartedAt() + return nil + case agent.FieldDeletedAt: + m.ClearDeletedAt() + return nil + } + return fmt.Errorf("unknown Agent nullable field %s", name) +} + +// ResetField resets all changes in the mutation for the field with the given name. +// It returns an error if the field is not defined in the schema. +func (m *AgentMutation) ResetField(name string) error { + switch name { + case agent.FieldSlug: + m.ResetSlug() + return nil + case agent.FieldName: + m.ResetName() + return nil + case agent.FieldTemplate: + m.ResetTemplate() + return nil + case agent.FieldProjectID: + m.ResetProjectID() + return nil + case agent.FieldStatus: + m.ResetStatus() + return nil + case agent.FieldCreatedBy: + m.ResetCreatedBy() + return nil + case agent.FieldOwnerID: + m.ResetOwnerID() + return nil + case agent.FieldDelegationEnabled: + m.ResetDelegationEnabled() + return nil + case agent.FieldVisibility: + m.ResetVisibility() + return nil + case agent.FieldLabels: + m.ResetLabels() + return nil + case agent.FieldAnnotations: + m.ResetAnnotations() + return nil + case agent.FieldPhase: + m.ResetPhase() + return nil + case agent.FieldActivity: + m.ResetActivity() + return nil + case agent.FieldToolName: + m.ResetToolName() + return nil + case agent.FieldConnectionState: + m.ResetConnectionState() + return nil + case agent.FieldContainerStatus: + m.ResetContainerStatus() + return nil + case agent.FieldRuntimeState: + m.ResetRuntimeState() + return nil + case agent.FieldStalledFromActivity: + m.ResetStalledFromActivity() + return nil + case agent.FieldCurrentTurns: + m.ResetCurrentTurns() + return nil + case agent.FieldCurrentModelCalls: + m.ResetCurrentModelCalls() + return nil + case agent.FieldImage: + m.ResetImage() + return nil + case agent.FieldDetached: + m.ResetDetached() + return nil + case agent.FieldRuntime: + m.ResetRuntime() + return nil + case agent.FieldRuntimeBrokerID: + m.ResetRuntimeBrokerID() + return nil + case agent.FieldWebPtyEnabled: + m.ResetWebPtyEnabled() + return nil + case agent.FieldTaskSummary: + m.ResetTaskSummary() + return nil + case agent.FieldMessage: + m.ResetMessage() + return nil + case agent.FieldAppliedConfig: + m.ResetAppliedConfig() + return nil + case agent.FieldAncestry: + m.ResetAncestry() + return nil + case agent.FieldCreated: + m.ResetCreated() + return nil + case agent.FieldUpdated: + m.ResetUpdated() + return nil + case agent.FieldLastSeen: + m.ResetLastSeen() + return nil + case agent.FieldLastActivityEvent: + m.ResetLastActivityEvent() + return nil + case agent.FieldStartedAt: + m.ResetStartedAt() + return nil + case agent.FieldDeletedAt: + m.ResetDeletedAt() + return nil + case agent.FieldStateVersion: + m.ResetStateVersion() + return nil + } + return fmt.Errorf("unknown Agent field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *AgentMutation) AddedEdges() []string { + edges := make([]string, 0, 5) + if m.project != nil { + edges = append(edges, agent.EdgeProject) + } + if m.creator != nil { + edges = append(edges, agent.EdgeCreator) + } + if m.owner != nil { + edges = append(edges, agent.EdgeOwner) + } + if m.memberships != nil { + edges = append(edges, agent.EdgeMemberships) + } + if m.policy_bindings != nil { + edges = append(edges, agent.EdgePolicyBindings) + } + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *AgentMutation) AddedIDs(name string) []ent.Value { + switch name { + case agent.EdgeProject: + if id := m.project; id != nil { + return []ent.Value{*id} + } + case agent.EdgeCreator: + if id := m.creator; id != nil { + return []ent.Value{*id} + } + case agent.EdgeOwner: + if id := m.owner; id != nil { + return []ent.Value{*id} + } + case agent.EdgeMemberships: + ids := make([]ent.Value, 0, len(m.memberships)) + for id := range m.memberships { + ids = append(ids, id) + } + return ids + case agent.EdgePolicyBindings: + ids := make([]ent.Value, 0, len(m.policy_bindings)) + for id := range m.policy_bindings { + ids = append(ids, id) + } + return ids + } + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *AgentMutation) RemovedEdges() []string { + edges := make([]string, 0, 5) + if m.removedmemberships != nil { + edges = append(edges, agent.EdgeMemberships) + } + if m.removedpolicy_bindings != nil { + edges = append(edges, agent.EdgePolicyBindings) + } + return edges +} + +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *AgentMutation) RemovedIDs(name string) []ent.Value { + switch name { + case agent.EdgeMemberships: + ids := make([]ent.Value, 0, len(m.removedmemberships)) + for id := range m.removedmemberships { + ids = append(ids, id) + } + return ids + case agent.EdgePolicyBindings: + ids := make([]ent.Value, 0, len(m.removedpolicy_bindings)) + for id := range m.removedpolicy_bindings { + ids = append(ids, id) + } + return ids + } + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *AgentMutation) ClearedEdges() []string { + edges := make([]string, 0, 5) + if m.clearedproject { + edges = append(edges, agent.EdgeProject) + } + if m.clearedcreator { + edges = append(edges, agent.EdgeCreator) + } + if m.clearedowner { + edges = append(edges, agent.EdgeOwner) + } + if m.clearedmemberships { + edges = append(edges, agent.EdgeMemberships) + } + if m.clearedpolicy_bindings { + edges = append(edges, agent.EdgePolicyBindings) + } + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *AgentMutation) EdgeCleared(name string) bool { + switch name { + case agent.EdgeProject: + return m.clearedproject + case agent.EdgeCreator: + return m.clearedcreator + case agent.EdgeOwner: + return m.clearedowner + case agent.EdgeMemberships: + return m.clearedmemberships + case agent.EdgePolicyBindings: + return m.clearedpolicy_bindings + } + return false +} + +// ClearEdge clears the value of the edge with the given name. It returns an error +// if that edge is not defined in the schema. +func (m *AgentMutation) ClearEdge(name string) error { + switch name { + case agent.EdgeProject: + m.ClearProject() + return nil + case agent.EdgeCreator: + m.ClearCreator() + return nil + case agent.EdgeOwner: + m.ClearOwner() + return nil + } + return fmt.Errorf("unknown Agent unique edge %s", name) +} + +// ResetEdge resets all changes to the edge with the given name in this mutation. +// It returns an error if the edge is not defined in the schema. +func (m *AgentMutation) ResetEdge(name string) error { + switch name { + case agent.EdgeProject: + m.ResetProject() + return nil + case agent.EdgeCreator: + m.ResetCreator() + return nil + case agent.EdgeOwner: + m.ResetOwner() + return nil + case agent.EdgeMemberships: + m.ResetMemberships() + return nil + case agent.EdgePolicyBindings: + m.ResetPolicyBindings() + return nil + } + return fmt.Errorf("unknown Agent edge %s", name) +} + +// AllowListEntryMutation represents an operation that mutates the AllowListEntry nodes in the graph. +type AllowListEntryMutation struct { + config + op Op + typ string + id *uuid.UUID + email *string + note *string + added_by *string + invite_id *string + created *time.Time + clearedFields map[string]struct{} + done bool + oldValue func(context.Context) (*AllowListEntry, error) + predicates []predicate.AllowListEntry +} + +var _ ent.Mutation = (*AllowListEntryMutation)(nil) + +// allowlistentryOption allows management of the mutation configuration using functional options. +type allowlistentryOption func(*AllowListEntryMutation) + +// newAllowListEntryMutation creates new mutation for the AllowListEntry entity. +func newAllowListEntryMutation(c config, op Op, opts ...allowlistentryOption) *AllowListEntryMutation { + m := &AllowListEntryMutation{ + config: c, + op: op, + typ: TypeAllowListEntry, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// withAllowListEntryID sets the ID field of the mutation. +func withAllowListEntryID(id uuid.UUID) allowlistentryOption { + return func(m *AllowListEntryMutation) { + var ( + err error + once sync.Once + value *AllowListEntry + ) + m.oldValue = func(ctx context.Context) (*AllowListEntry, error) { + once.Do(func() { + if m.done { + err = errors.New("querying old values post mutation is not allowed") + } else { + value, err = m.Client().AllowListEntry.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } +} + +// withAllowListEntry sets the old AllowListEntry of the mutation. +func withAllowListEntry(node *AllowListEntry) allowlistentryOption { + return func(m *AllowListEntryMutation) { + m.oldValue = func(context.Context) (*AllowListEntry, error) { + return node, nil + } + m.id = &node.ID + } +} + +// Client returns a new `ent.Client` from the mutation. If the mutation was +// executed in a transaction (ent.Tx), a transactional client is returned. +func (m AllowListEntryMutation) Client() *Client { + client := &Client{config: m.config} + client.init() + return client +} + +// Tx returns an `ent.Tx` for mutations that were executed in transactions; +// it returns an error otherwise. +func (m AllowListEntryMutation) Tx() (*Tx, error) { + if _, ok := m.driver.(*txDriver); !ok { + return nil, errors.New("ent: mutation is not running in a transaction") + } + tx := &Tx{config: m.config} + tx.init() + return tx, nil +} + +// SetID sets the value of the id field. Note that this +// operation is only accepted on creation of AllowListEntry entities. +func (m *AllowListEntryMutation) SetID(id uuid.UUID) { + m.id = &id +} + +// ID returns the ID value in the mutation. Note that the ID is only available +// if it was provided to the builder or after it was returned from the database. +func (m *AllowListEntryMutation) ID() (id uuid.UUID, exists bool) { + if m.id == nil { + return + } + return *m.id, true +} + +// IDs queries the database and returns the entity ids that match the mutation's predicate. +// That means, if the mutation is applied within a transaction with an isolation level such +// as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated +// or updated by the mutation. +func (m *AllowListEntryMutation) IDs(ctx context.Context) ([]uuid.UUID, error) { + switch { + case m.op.Is(OpUpdateOne | OpDeleteOne): + id, exists := m.ID() + if exists { + return []uuid.UUID{id}, nil + } + fallthrough + case m.op.Is(OpUpdate | OpDelete): + return m.Client().AllowListEntry.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } +} + +// SetEmail sets the "email" field. +func (m *AllowListEntryMutation) SetEmail(s string) { + m.email = &s +} + +// Email returns the value of the "email" field in the mutation. +func (m *AllowListEntryMutation) Email() (r string, exists bool) { + v := m.email + if v == nil { + return + } + return *v, true +} + +// OldEmail returns the old "email" field's value of the AllowListEntry entity. +// If the AllowListEntry object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *AllowListEntryMutation) OldEmail(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldEmail is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldEmail requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldEmail: %w", err) + } + return oldValue.Email, nil +} + +// ResetEmail resets all changes to the "email" field. +func (m *AllowListEntryMutation) ResetEmail() { + m.email = nil +} + +// SetNote sets the "note" field. +func (m *AllowListEntryMutation) SetNote(s string) { + m.note = &s +} + +// Note returns the value of the "note" field in the mutation. +func (m *AllowListEntryMutation) Note() (r string, exists bool) { + v := m.note + if v == nil { + return + } + return *v, true +} + +// OldNote returns the old "note" field's value of the AllowListEntry entity. +// If the AllowListEntry object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *AllowListEntryMutation) OldNote(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldNote is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldNote requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldNote: %w", err) + } + return oldValue.Note, nil +} + +// ResetNote resets all changes to the "note" field. +func (m *AllowListEntryMutation) ResetNote() { + m.note = nil +} + +// SetAddedBy sets the "added_by" field. +func (m *AllowListEntryMutation) SetAddedBy(s string) { + m.added_by = &s +} + +// AddedBy returns the value of the "added_by" field in the mutation. +func (m *AllowListEntryMutation) AddedBy() (r string, exists bool) { + v := m.added_by + if v == nil { + return + } + return *v, true +} + +// OldAddedBy returns the old "added_by" field's value of the AllowListEntry entity. +// If the AllowListEntry object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *AllowListEntryMutation) OldAddedBy(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldAddedBy is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldAddedBy requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldAddedBy: %w", err) + } + return oldValue.AddedBy, nil +} + +// ResetAddedBy resets all changes to the "added_by" field. +func (m *AllowListEntryMutation) ResetAddedBy() { + m.added_by = nil +} + +// SetInviteID sets the "invite_id" field. +func (m *AllowListEntryMutation) SetInviteID(s string) { + m.invite_id = &s +} + +// InviteID returns the value of the "invite_id" field in the mutation. +func (m *AllowListEntryMutation) InviteID() (r string, exists bool) { + v := m.invite_id + if v == nil { + return + } + return *v, true +} + +// OldInviteID returns the old "invite_id" field's value of the AllowListEntry entity. +// If the AllowListEntry object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *AllowListEntryMutation) OldInviteID(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldInviteID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldInviteID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldInviteID: %w", err) + } + return oldValue.InviteID, nil +} + +// ClearInviteID clears the value of the "invite_id" field. +func (m *AllowListEntryMutation) ClearInviteID() { + m.invite_id = nil + m.clearedFields[allowlistentry.FieldInviteID] = struct{}{} +} + +// InviteIDCleared returns if the "invite_id" field was cleared in this mutation. +func (m *AllowListEntryMutation) InviteIDCleared() bool { + _, ok := m.clearedFields[allowlistentry.FieldInviteID] + return ok +} + +// ResetInviteID resets all changes to the "invite_id" field. +func (m *AllowListEntryMutation) ResetInviteID() { + m.invite_id = nil + delete(m.clearedFields, allowlistentry.FieldInviteID) +} + +// SetCreated sets the "created" field. +func (m *AllowListEntryMutation) SetCreated(t time.Time) { + m.created = &t +} + +// Created returns the value of the "created" field in the mutation. +func (m *AllowListEntryMutation) Created() (r time.Time, exists bool) { + v := m.created + if v == nil { + return + } + return *v, true +} + +// OldCreated returns the old "created" field's value of the AllowListEntry entity. +// If the AllowListEntry object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *AllowListEntryMutation) OldCreated(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreated is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreated requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreated: %w", err) + } + return oldValue.Created, nil +} + +// ResetCreated resets all changes to the "created" field. +func (m *AllowListEntryMutation) ResetCreated() { + m.created = nil +} + +// Where appends a list predicates to the AllowListEntryMutation builder. +func (m *AllowListEntryMutation) Where(ps ...predicate.AllowListEntry) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the AllowListEntryMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *AllowListEntryMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.AllowListEntry, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *AllowListEntryMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *AllowListEntryMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (AllowListEntry). +func (m *AllowListEntryMutation) Type() string { + return m.typ +} + +// Fields returns all fields that were changed during this mutation. Note that in +// order to get all numeric fields that were incremented/decremented, call +// AddedFields(). +func (m *AllowListEntryMutation) Fields() []string { + fields := make([]string, 0, 5) + if m.email != nil { + fields = append(fields, allowlistentry.FieldEmail) + } + if m.note != nil { + fields = append(fields, allowlistentry.FieldNote) + } + if m.added_by != nil { + fields = append(fields, allowlistentry.FieldAddedBy) + } + if m.invite_id != nil { + fields = append(fields, allowlistentry.FieldInviteID) + } + if m.created != nil { + fields = append(fields, allowlistentry.FieldCreated) + } + return fields +} + +// Field returns the value of a field with the given name. The second boolean +// return value indicates that this field was not set, or was not defined in the +// schema. +func (m *AllowListEntryMutation) Field(name string) (ent.Value, bool) { + switch name { + case allowlistentry.FieldEmail: + return m.Email() + case allowlistentry.FieldNote: + return m.Note() + case allowlistentry.FieldAddedBy: + return m.AddedBy() + case allowlistentry.FieldInviteID: + return m.InviteID() + case allowlistentry.FieldCreated: + return m.Created() + } + return nil, false +} + +// OldField returns the old value of the field from the database. An error is +// returned if the mutation operation is not UpdateOne, or the query to the +// database failed. +func (m *AllowListEntryMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + switch name { + case allowlistentry.FieldEmail: + return m.OldEmail(ctx) + case allowlistentry.FieldNote: + return m.OldNote(ctx) + case allowlistentry.FieldAddedBy: + return m.OldAddedBy(ctx) + case allowlistentry.FieldInviteID: + return m.OldInviteID(ctx) + case allowlistentry.FieldCreated: + return m.OldCreated(ctx) + } + return nil, fmt.Errorf("unknown AllowListEntry field %s", name) +} + +// SetField sets the value of a field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *AllowListEntryMutation) SetField(name string, value ent.Value) error { + switch name { + case allowlistentry.FieldEmail: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetEmail(v) + return nil + case allowlistentry.FieldNote: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetNote(v) + return nil + case allowlistentry.FieldAddedBy: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetAddedBy(v) + return nil + case allowlistentry.FieldInviteID: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetInviteID(v) + return nil + case allowlistentry.FieldCreated: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreated(v) + return nil + } + return fmt.Errorf("unknown AllowListEntry field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *AllowListEntryMutation) AddedFields() []string { + return nil +} + +// AddedField returns the numeric value that was incremented/decremented on a field +// with the given name. The second boolean return value indicates that this field +// was not set, or was not defined in the schema. +func (m *AllowListEntryMutation) AddedField(name string) (ent.Value, bool) { + return nil, false +} + +// AddField adds the value to the field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *AllowListEntryMutation) AddField(name string, value ent.Value) error { + switch name { + } + return fmt.Errorf("unknown AllowListEntry numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *AllowListEntryMutation) ClearedFields() []string { + var fields []string + if m.FieldCleared(allowlistentry.FieldInviteID) { + fields = append(fields, allowlistentry.FieldInviteID) + } + return fields +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *AllowListEntryMutation) FieldCleared(name string) bool { + _, ok := m.clearedFields[name] + return ok +} + +// ClearField clears the value of the field with the given name. It returns an +// error if the field is not defined in the schema. +func (m *AllowListEntryMutation) ClearField(name string) error { + switch name { + case allowlistentry.FieldInviteID: + m.ClearInviteID() + return nil + } + return fmt.Errorf("unknown AllowListEntry nullable field %s", name) +} + +// ResetField resets all changes in the mutation for the field with the given name. +// It returns an error if the field is not defined in the schema. +func (m *AllowListEntryMutation) ResetField(name string) error { + switch name { + case allowlistentry.FieldEmail: + m.ResetEmail() + return nil + case allowlistentry.FieldNote: + m.ResetNote() + return nil + case allowlistentry.FieldAddedBy: + m.ResetAddedBy() + return nil + case allowlistentry.FieldInviteID: + m.ResetInviteID() + return nil + case allowlistentry.FieldCreated: + m.ResetCreated() + return nil + } + return fmt.Errorf("unknown AllowListEntry field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *AllowListEntryMutation) AddedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *AllowListEntryMutation) AddedIDs(name string) []ent.Value { + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *AllowListEntryMutation) RemovedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *AllowListEntryMutation) RemovedIDs(name string) []ent.Value { + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *AllowListEntryMutation) ClearedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *AllowListEntryMutation) EdgeCleared(name string) bool { + return false +} + +// ClearEdge clears the value of the edge with the given name. It returns an error +// if that edge is not defined in the schema. +func (m *AllowListEntryMutation) ClearEdge(name string) error { + return fmt.Errorf("unknown AllowListEntry unique edge %s", name) +} + +// ResetEdge resets all changes to the edge with the given name in this mutation. +// It returns an error if the edge is not defined in the schema. +func (m *AllowListEntryMutation) ResetEdge(name string) error { + return fmt.Errorf("unknown AllowListEntry edge %s", name) +} + +// ApiKeyMutation represents an operation that mutates the ApiKey nodes in the graph. +type ApiKeyMutation struct { + config + op Op + typ string + id *uuid.UUID + user_id *uuid.UUID + name *string + prefix *string + key_hash *string + scopes *string + revoked *bool + expires_at *time.Time + last_used *time.Time + created *time.Time + clearedFields map[string]struct{} + done bool + oldValue func(context.Context) (*ApiKey, error) + predicates []predicate.ApiKey +} + +var _ ent.Mutation = (*ApiKeyMutation)(nil) + +// apikeyOption allows management of the mutation configuration using functional options. +type apikeyOption func(*ApiKeyMutation) + +// newApiKeyMutation creates new mutation for the ApiKey entity. +func newApiKeyMutation(c config, op Op, opts ...apikeyOption) *ApiKeyMutation { + m := &ApiKeyMutation{ + config: c, + op: op, + typ: TypeApiKey, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// withApiKeyID sets the ID field of the mutation. +func withApiKeyID(id uuid.UUID) apikeyOption { + return func(m *ApiKeyMutation) { + var ( + err error + once sync.Once + value *ApiKey + ) + m.oldValue = func(ctx context.Context) (*ApiKey, error) { + once.Do(func() { + if m.done { + err = errors.New("querying old values post mutation is not allowed") + } else { + value, err = m.Client().ApiKey.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } +} + +// withApiKey sets the old ApiKey of the mutation. +func withApiKey(node *ApiKey) apikeyOption { + return func(m *ApiKeyMutation) { + m.oldValue = func(context.Context) (*ApiKey, error) { + return node, nil + } + m.id = &node.ID + } +} + +// Client returns a new `ent.Client` from the mutation. If the mutation was +// executed in a transaction (ent.Tx), a transactional client is returned. +func (m ApiKeyMutation) Client() *Client { + client := &Client{config: m.config} + client.init() + return client +} + +// Tx returns an `ent.Tx` for mutations that were executed in transactions; +// it returns an error otherwise. +func (m ApiKeyMutation) Tx() (*Tx, error) { + if _, ok := m.driver.(*txDriver); !ok { + return nil, errors.New("ent: mutation is not running in a transaction") + } + tx := &Tx{config: m.config} + tx.init() + return tx, nil +} + +// SetID sets the value of the id field. Note that this +// operation is only accepted on creation of ApiKey entities. +func (m *ApiKeyMutation) SetID(id uuid.UUID) { + m.id = &id +} + +// ID returns the ID value in the mutation. Note that the ID is only available +// if it was provided to the builder or after it was returned from the database. +func (m *ApiKeyMutation) ID() (id uuid.UUID, exists bool) { + if m.id == nil { + return + } + return *m.id, true +} + +// IDs queries the database and returns the entity ids that match the mutation's predicate. +// That means, if the mutation is applied within a transaction with an isolation level such +// as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated +// or updated by the mutation. +func (m *ApiKeyMutation) IDs(ctx context.Context) ([]uuid.UUID, error) { + switch { + case m.op.Is(OpUpdateOne | OpDeleteOne): + id, exists := m.ID() + if exists { + return []uuid.UUID{id}, nil + } + fallthrough + case m.op.Is(OpUpdate | OpDelete): + return m.Client().ApiKey.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } +} + +// SetUserID sets the "user_id" field. +func (m *ApiKeyMutation) SetUserID(u uuid.UUID) { + m.user_id = &u +} + +// UserID returns the value of the "user_id" field in the mutation. +func (m *ApiKeyMutation) UserID() (r uuid.UUID, exists bool) { + v := m.user_id + if v == nil { + return + } + return *v, true +} + +// OldUserID returns the old "user_id" field's value of the ApiKey entity. +// If the ApiKey object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ApiKeyMutation) OldUserID(ctx context.Context) (v uuid.UUID, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldUserID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldUserID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldUserID: %w", err) + } + return oldValue.UserID, nil +} + +// ResetUserID resets all changes to the "user_id" field. +func (m *ApiKeyMutation) ResetUserID() { + m.user_id = nil +} + +// SetName sets the "name" field. +func (m *ApiKeyMutation) SetName(s string) { + m.name = &s +} + +// Name returns the value of the "name" field in the mutation. +func (m *ApiKeyMutation) Name() (r string, exists bool) { + v := m.name + if v == nil { + return + } + return *v, true +} + +// OldName returns the old "name" field's value of the ApiKey entity. +// If the ApiKey object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ApiKeyMutation) OldName(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldName is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldName requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldName: %w", err) + } + return oldValue.Name, nil +} + +// ClearName clears the value of the "name" field. +func (m *ApiKeyMutation) ClearName() { + m.name = nil + m.clearedFields[apikey.FieldName] = struct{}{} +} + +// NameCleared returns if the "name" field was cleared in this mutation. +func (m *ApiKeyMutation) NameCleared() bool { + _, ok := m.clearedFields[apikey.FieldName] + return ok +} + +// ResetName resets all changes to the "name" field. +func (m *ApiKeyMutation) ResetName() { + m.name = nil + delete(m.clearedFields, apikey.FieldName) +} + +// SetPrefix sets the "prefix" field. +func (m *ApiKeyMutation) SetPrefix(s string) { + m.prefix = &s +} + +// Prefix returns the value of the "prefix" field in the mutation. +func (m *ApiKeyMutation) Prefix() (r string, exists bool) { + v := m.prefix + if v == nil { + return + } + return *v, true +} + +// OldPrefix returns the old "prefix" field's value of the ApiKey entity. +// If the ApiKey object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ApiKeyMutation) OldPrefix(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldPrefix is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldPrefix requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldPrefix: %w", err) + } + return oldValue.Prefix, nil +} + +// ClearPrefix clears the value of the "prefix" field. +func (m *ApiKeyMutation) ClearPrefix() { + m.prefix = nil + m.clearedFields[apikey.FieldPrefix] = struct{}{} +} + +// PrefixCleared returns if the "prefix" field was cleared in this mutation. +func (m *ApiKeyMutation) PrefixCleared() bool { + _, ok := m.clearedFields[apikey.FieldPrefix] + return ok +} + +// ResetPrefix resets all changes to the "prefix" field. +func (m *ApiKeyMutation) ResetPrefix() { + m.prefix = nil + delete(m.clearedFields, apikey.FieldPrefix) +} + +// SetKeyHash sets the "key_hash" field. +func (m *ApiKeyMutation) SetKeyHash(s string) { + m.key_hash = &s +} + +// KeyHash returns the value of the "key_hash" field in the mutation. +func (m *ApiKeyMutation) KeyHash() (r string, exists bool) { + v := m.key_hash + if v == nil { + return + } + return *v, true +} + +// OldKeyHash returns the old "key_hash" field's value of the ApiKey entity. +// If the ApiKey object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ApiKeyMutation) OldKeyHash(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldKeyHash is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldKeyHash requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldKeyHash: %w", err) + } + return oldValue.KeyHash, nil +} + +// ResetKeyHash resets all changes to the "key_hash" field. +func (m *ApiKeyMutation) ResetKeyHash() { + m.key_hash = nil +} + +// SetScopes sets the "scopes" field. +func (m *ApiKeyMutation) SetScopes(s string) { + m.scopes = &s +} + +// Scopes returns the value of the "scopes" field in the mutation. +func (m *ApiKeyMutation) Scopes() (r string, exists bool) { + v := m.scopes + if v == nil { + return + } + return *v, true +} + +// OldScopes returns the old "scopes" field's value of the ApiKey entity. +// If the ApiKey object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ApiKeyMutation) OldScopes(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldScopes is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldScopes requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldScopes: %w", err) + } + return oldValue.Scopes, nil +} + +// ClearScopes clears the value of the "scopes" field. +func (m *ApiKeyMutation) ClearScopes() { + m.scopes = nil + m.clearedFields[apikey.FieldScopes] = struct{}{} +} + +// ScopesCleared returns if the "scopes" field was cleared in this mutation. +func (m *ApiKeyMutation) ScopesCleared() bool { + _, ok := m.clearedFields[apikey.FieldScopes] + return ok +} + +// ResetScopes resets all changes to the "scopes" field. +func (m *ApiKeyMutation) ResetScopes() { + m.scopes = nil + delete(m.clearedFields, apikey.FieldScopes) +} + +// SetRevoked sets the "revoked" field. +func (m *ApiKeyMutation) SetRevoked(b bool) { + m.revoked = &b +} + +// Revoked returns the value of the "revoked" field in the mutation. +func (m *ApiKeyMutation) Revoked() (r bool, exists bool) { + v := m.revoked + if v == nil { + return + } + return *v, true +} + +// OldRevoked returns the old "revoked" field's value of the ApiKey entity. +// If the ApiKey object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ApiKeyMutation) OldRevoked(ctx context.Context) (v bool, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldRevoked is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldRevoked requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldRevoked: %w", err) + } + return oldValue.Revoked, nil +} + +// ResetRevoked resets all changes to the "revoked" field. +func (m *ApiKeyMutation) ResetRevoked() { + m.revoked = nil +} + +// SetExpiresAt sets the "expires_at" field. +func (m *ApiKeyMutation) SetExpiresAt(t time.Time) { + m.expires_at = &t +} + +// ExpiresAt returns the value of the "expires_at" field in the mutation. +func (m *ApiKeyMutation) ExpiresAt() (r time.Time, exists bool) { + v := m.expires_at + if v == nil { + return + } + return *v, true +} + +// OldExpiresAt returns the old "expires_at" field's value of the ApiKey entity. +// If the ApiKey object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ApiKeyMutation) OldExpiresAt(ctx context.Context) (v *time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldExpiresAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldExpiresAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldExpiresAt: %w", err) + } + return oldValue.ExpiresAt, nil +} + +// ClearExpiresAt clears the value of the "expires_at" field. +func (m *ApiKeyMutation) ClearExpiresAt() { + m.expires_at = nil + m.clearedFields[apikey.FieldExpiresAt] = struct{}{} +} + +// ExpiresAtCleared returns if the "expires_at" field was cleared in this mutation. +func (m *ApiKeyMutation) ExpiresAtCleared() bool { + _, ok := m.clearedFields[apikey.FieldExpiresAt] + return ok +} + +// ResetExpiresAt resets all changes to the "expires_at" field. +func (m *ApiKeyMutation) ResetExpiresAt() { + m.expires_at = nil + delete(m.clearedFields, apikey.FieldExpiresAt) +} + +// SetLastUsed sets the "last_used" field. +func (m *ApiKeyMutation) SetLastUsed(t time.Time) { + m.last_used = &t +} + +// LastUsed returns the value of the "last_used" field in the mutation. +func (m *ApiKeyMutation) LastUsed() (r time.Time, exists bool) { + v := m.last_used + if v == nil { + return + } + return *v, true +} + +// OldLastUsed returns the old "last_used" field's value of the ApiKey entity. +// If the ApiKey object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ApiKeyMutation) OldLastUsed(ctx context.Context) (v *time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldLastUsed is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldLastUsed requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldLastUsed: %w", err) + } + return oldValue.LastUsed, nil +} + +// ClearLastUsed clears the value of the "last_used" field. +func (m *ApiKeyMutation) ClearLastUsed() { + m.last_used = nil + m.clearedFields[apikey.FieldLastUsed] = struct{}{} +} + +// LastUsedCleared returns if the "last_used" field was cleared in this mutation. +func (m *ApiKeyMutation) LastUsedCleared() bool { + _, ok := m.clearedFields[apikey.FieldLastUsed] + return ok +} + +// ResetLastUsed resets all changes to the "last_used" field. +func (m *ApiKeyMutation) ResetLastUsed() { + m.last_used = nil + delete(m.clearedFields, apikey.FieldLastUsed) +} + +// SetCreated sets the "created" field. +func (m *ApiKeyMutation) SetCreated(t time.Time) { + m.created = &t +} + +// Created returns the value of the "created" field in the mutation. +func (m *ApiKeyMutation) Created() (r time.Time, exists bool) { + v := m.created + if v == nil { + return + } + return *v, true +} + +// OldCreated returns the old "created" field's value of the ApiKey entity. +// If the ApiKey object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ApiKeyMutation) OldCreated(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreated is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreated requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreated: %w", err) + } + return oldValue.Created, nil +} + +// ResetCreated resets all changes to the "created" field. +func (m *ApiKeyMutation) ResetCreated() { + m.created = nil +} + +// Where appends a list predicates to the ApiKeyMutation builder. +func (m *ApiKeyMutation) Where(ps ...predicate.ApiKey) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the ApiKeyMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *ApiKeyMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.ApiKey, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *ApiKeyMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *ApiKeyMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (ApiKey). +func (m *ApiKeyMutation) Type() string { + return m.typ +} + +// Fields returns all fields that were changed during this mutation. Note that in +// order to get all numeric fields that were incremented/decremented, call +// AddedFields(). +func (m *ApiKeyMutation) Fields() []string { + fields := make([]string, 0, 9) + if m.user_id != nil { + fields = append(fields, apikey.FieldUserID) + } + if m.name != nil { + fields = append(fields, apikey.FieldName) + } + if m.prefix != nil { + fields = append(fields, apikey.FieldPrefix) + } + if m.key_hash != nil { + fields = append(fields, apikey.FieldKeyHash) + } + if m.scopes != nil { + fields = append(fields, apikey.FieldScopes) + } + if m.revoked != nil { + fields = append(fields, apikey.FieldRevoked) + } + if m.expires_at != nil { + fields = append(fields, apikey.FieldExpiresAt) + } + if m.last_used != nil { + fields = append(fields, apikey.FieldLastUsed) + } + if m.created != nil { + fields = append(fields, apikey.FieldCreated) + } + return fields +} + +// Field returns the value of a field with the given name. The second boolean +// return value indicates that this field was not set, or was not defined in the +// schema. +func (m *ApiKeyMutation) Field(name string) (ent.Value, bool) { + switch name { + case apikey.FieldUserID: + return m.UserID() + case apikey.FieldName: + return m.Name() + case apikey.FieldPrefix: + return m.Prefix() + case apikey.FieldKeyHash: + return m.KeyHash() + case apikey.FieldScopes: + return m.Scopes() + case apikey.FieldRevoked: + return m.Revoked() + case apikey.FieldExpiresAt: + return m.ExpiresAt() + case apikey.FieldLastUsed: + return m.LastUsed() + case apikey.FieldCreated: + return m.Created() + } + return nil, false +} + +// OldField returns the old value of the field from the database. An error is +// returned if the mutation operation is not UpdateOne, or the query to the +// database failed. +func (m *ApiKeyMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + switch name { + case apikey.FieldUserID: + return m.OldUserID(ctx) + case apikey.FieldName: + return m.OldName(ctx) + case apikey.FieldPrefix: + return m.OldPrefix(ctx) + case apikey.FieldKeyHash: + return m.OldKeyHash(ctx) + case apikey.FieldScopes: + return m.OldScopes(ctx) + case apikey.FieldRevoked: + return m.OldRevoked(ctx) + case apikey.FieldExpiresAt: + return m.OldExpiresAt(ctx) + case apikey.FieldLastUsed: + return m.OldLastUsed(ctx) + case apikey.FieldCreated: + return m.OldCreated(ctx) + } + return nil, fmt.Errorf("unknown ApiKey field %s", name) +} + +// SetField sets the value of a field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *ApiKeyMutation) SetField(name string, value ent.Value) error { + switch name { + case apikey.FieldUserID: + v, ok := value.(uuid.UUID) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetUserID(v) + return nil + case apikey.FieldName: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetName(v) + return nil + case apikey.FieldPrefix: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetPrefix(v) + return nil + case apikey.FieldKeyHash: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetKeyHash(v) + return nil + case apikey.FieldScopes: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetScopes(v) + return nil + case apikey.FieldRevoked: + v, ok := value.(bool) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetRevoked(v) + return nil + case apikey.FieldExpiresAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetExpiresAt(v) + return nil + case apikey.FieldLastUsed: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetLastUsed(v) + return nil + case apikey.FieldCreated: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreated(v) + return nil + } + return fmt.Errorf("unknown ApiKey field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *ApiKeyMutation) AddedFields() []string { + return nil +} + +// AddedField returns the numeric value that was incremented/decremented on a field +// with the given name. The second boolean return value indicates that this field +// was not set, or was not defined in the schema. +func (m *ApiKeyMutation) AddedField(name string) (ent.Value, bool) { + return nil, false +} + +// AddField adds the value to the field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *ApiKeyMutation) AddField(name string, value ent.Value) error { + switch name { + } + return fmt.Errorf("unknown ApiKey numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *ApiKeyMutation) ClearedFields() []string { + var fields []string + if m.FieldCleared(apikey.FieldName) { + fields = append(fields, apikey.FieldName) + } + if m.FieldCleared(apikey.FieldPrefix) { + fields = append(fields, apikey.FieldPrefix) + } + if m.FieldCleared(apikey.FieldScopes) { + fields = append(fields, apikey.FieldScopes) + } + if m.FieldCleared(apikey.FieldExpiresAt) { + fields = append(fields, apikey.FieldExpiresAt) + } + if m.FieldCleared(apikey.FieldLastUsed) { + fields = append(fields, apikey.FieldLastUsed) + } + return fields +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *ApiKeyMutation) FieldCleared(name string) bool { + _, ok := m.clearedFields[name] + return ok +} + +// ClearField clears the value of the field with the given name. It returns an +// error if the field is not defined in the schema. +func (m *ApiKeyMutation) ClearField(name string) error { + switch name { + case apikey.FieldName: + m.ClearName() + return nil + case apikey.FieldPrefix: + m.ClearPrefix() + return nil + case apikey.FieldScopes: + m.ClearScopes() + return nil + case apikey.FieldExpiresAt: + m.ClearExpiresAt() + return nil + case apikey.FieldLastUsed: + m.ClearLastUsed() + return nil + } + return fmt.Errorf("unknown ApiKey nullable field %s", name) +} + +// ResetField resets all changes in the mutation for the field with the given name. +// It returns an error if the field is not defined in the schema. +func (m *ApiKeyMutation) ResetField(name string) error { + switch name { + case apikey.FieldUserID: + m.ResetUserID() + return nil + case apikey.FieldName: + m.ResetName() + return nil + case apikey.FieldPrefix: + m.ResetPrefix() + return nil + case apikey.FieldKeyHash: + m.ResetKeyHash() + return nil + case apikey.FieldScopes: + m.ResetScopes() + return nil + case apikey.FieldRevoked: + m.ResetRevoked() + return nil + case apikey.FieldExpiresAt: + m.ResetExpiresAt() + return nil + case apikey.FieldLastUsed: + m.ResetLastUsed() + return nil + case apikey.FieldCreated: + m.ResetCreated() + return nil + } + return fmt.Errorf("unknown ApiKey field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *ApiKeyMutation) AddedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *ApiKeyMutation) AddedIDs(name string) []ent.Value { + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *ApiKeyMutation) RemovedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *ApiKeyMutation) RemovedIDs(name string) []ent.Value { + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *ApiKeyMutation) ClearedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *ApiKeyMutation) EdgeCleared(name string) bool { + return false +} + +// ClearEdge clears the value of the edge with the given name. It returns an error +// if that edge is not defined in the schema. +func (m *ApiKeyMutation) ClearEdge(name string) error { + return fmt.Errorf("unknown ApiKey unique edge %s", name) +} + +// ResetEdge resets all changes to the edge with the given name in this mutation. +// It returns an error if the edge is not defined in the schema. +func (m *ApiKeyMutation) ResetEdge(name string) error { + return fmt.Errorf("unknown ApiKey edge %s", name) +} + +// BrokerJoinTokenMutation represents an operation that mutates the BrokerJoinToken nodes in the graph. +type BrokerJoinTokenMutation struct { + config + op Op + typ string + id *uuid.UUID + token_hash *string + expires_at *time.Time + created_by *string + created *time.Time + clearedFields map[string]struct{} + done bool + oldValue func(context.Context) (*BrokerJoinToken, error) + predicates []predicate.BrokerJoinToken +} + +var _ ent.Mutation = (*BrokerJoinTokenMutation)(nil) + +// brokerjointokenOption allows management of the mutation configuration using functional options. +type brokerjointokenOption func(*BrokerJoinTokenMutation) + +// newBrokerJoinTokenMutation creates new mutation for the BrokerJoinToken entity. +func newBrokerJoinTokenMutation(c config, op Op, opts ...brokerjointokenOption) *BrokerJoinTokenMutation { + m := &BrokerJoinTokenMutation{ + config: c, + op: op, + typ: TypeBrokerJoinToken, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// withBrokerJoinTokenID sets the ID field of the mutation. +func withBrokerJoinTokenID(id uuid.UUID) brokerjointokenOption { + return func(m *BrokerJoinTokenMutation) { + var ( + err error + once sync.Once + value *BrokerJoinToken + ) + m.oldValue = func(ctx context.Context) (*BrokerJoinToken, error) { + once.Do(func() { + if m.done { + err = errors.New("querying old values post mutation is not allowed") + } else { + value, err = m.Client().BrokerJoinToken.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } +} + +// withBrokerJoinToken sets the old BrokerJoinToken of the mutation. +func withBrokerJoinToken(node *BrokerJoinToken) brokerjointokenOption { + return func(m *BrokerJoinTokenMutation) { + m.oldValue = func(context.Context) (*BrokerJoinToken, error) { + return node, nil + } + m.id = &node.ID + } +} + +// Client returns a new `ent.Client` from the mutation. If the mutation was +// executed in a transaction (ent.Tx), a transactional client is returned. +func (m BrokerJoinTokenMutation) Client() *Client { + client := &Client{config: m.config} + client.init() + return client +} + +// Tx returns an `ent.Tx` for mutations that were executed in transactions; +// it returns an error otherwise. +func (m BrokerJoinTokenMutation) Tx() (*Tx, error) { + if _, ok := m.driver.(*txDriver); !ok { + return nil, errors.New("ent: mutation is not running in a transaction") + } + tx := &Tx{config: m.config} + tx.init() + return tx, nil +} + +// SetID sets the value of the id field. Note that this +// operation is only accepted on creation of BrokerJoinToken entities. +func (m *BrokerJoinTokenMutation) SetID(id uuid.UUID) { + m.id = &id +} + +// ID returns the ID value in the mutation. Note that the ID is only available +// if it was provided to the builder or after it was returned from the database. +func (m *BrokerJoinTokenMutation) ID() (id uuid.UUID, exists bool) { + if m.id == nil { + return + } + return *m.id, true +} + +// IDs queries the database and returns the entity ids that match the mutation's predicate. +// That means, if the mutation is applied within a transaction with an isolation level such +// as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated +// or updated by the mutation. +func (m *BrokerJoinTokenMutation) IDs(ctx context.Context) ([]uuid.UUID, error) { + switch { + case m.op.Is(OpUpdateOne | OpDeleteOne): + id, exists := m.ID() + if exists { + return []uuid.UUID{id}, nil + } + fallthrough + case m.op.Is(OpUpdate | OpDelete): + return m.Client().BrokerJoinToken.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } +} + +// SetTokenHash sets the "token_hash" field. +func (m *BrokerJoinTokenMutation) SetTokenHash(s string) { + m.token_hash = &s +} + +// TokenHash returns the value of the "token_hash" field in the mutation. +func (m *BrokerJoinTokenMutation) TokenHash() (r string, exists bool) { + v := m.token_hash + if v == nil { + return + } + return *v, true +} + +// OldTokenHash returns the old "token_hash" field's value of the BrokerJoinToken entity. +// If the BrokerJoinToken object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *BrokerJoinTokenMutation) OldTokenHash(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldTokenHash is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldTokenHash requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldTokenHash: %w", err) + } + return oldValue.TokenHash, nil +} + +// ResetTokenHash resets all changes to the "token_hash" field. +func (m *BrokerJoinTokenMutation) ResetTokenHash() { + m.token_hash = nil +} + +// SetExpiresAt sets the "expires_at" field. +func (m *BrokerJoinTokenMutation) SetExpiresAt(t time.Time) { + m.expires_at = &t +} + +// ExpiresAt returns the value of the "expires_at" field in the mutation. +func (m *BrokerJoinTokenMutation) ExpiresAt() (r time.Time, exists bool) { + v := m.expires_at + if v == nil { + return + } + return *v, true +} + +// OldExpiresAt returns the old "expires_at" field's value of the BrokerJoinToken entity. +// If the BrokerJoinToken object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *BrokerJoinTokenMutation) OldExpiresAt(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldExpiresAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldExpiresAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldExpiresAt: %w", err) + } + return oldValue.ExpiresAt, nil +} + +// ResetExpiresAt resets all changes to the "expires_at" field. +func (m *BrokerJoinTokenMutation) ResetExpiresAt() { + m.expires_at = nil +} + +// SetCreatedBy sets the "created_by" field. +func (m *BrokerJoinTokenMutation) SetCreatedBy(s string) { + m.created_by = &s +} + +// CreatedBy returns the value of the "created_by" field in the mutation. +func (m *BrokerJoinTokenMutation) CreatedBy() (r string, exists bool) { + v := m.created_by + if v == nil { + return + } + return *v, true +} + +// OldCreatedBy returns the old "created_by" field's value of the BrokerJoinToken entity. +// If the BrokerJoinToken object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *BrokerJoinTokenMutation) OldCreatedBy(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreatedBy is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreatedBy requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreatedBy: %w", err) + } + return oldValue.CreatedBy, nil +} + +// ResetCreatedBy resets all changes to the "created_by" field. +func (m *BrokerJoinTokenMutation) ResetCreatedBy() { + m.created_by = nil +} + +// SetCreated sets the "created" field. +func (m *BrokerJoinTokenMutation) SetCreated(t time.Time) { + m.created = &t +} + +// Created returns the value of the "created" field in the mutation. +func (m *BrokerJoinTokenMutation) Created() (r time.Time, exists bool) { + v := m.created + if v == nil { + return + } + return *v, true +} + +// OldCreated returns the old "created" field's value of the BrokerJoinToken entity. +// If the BrokerJoinToken object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *BrokerJoinTokenMutation) OldCreated(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreated is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreated requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreated: %w", err) + } + return oldValue.Created, nil +} + +// ResetCreated resets all changes to the "created" field. +func (m *BrokerJoinTokenMutation) ResetCreated() { + m.created = nil +} + +// Where appends a list predicates to the BrokerJoinTokenMutation builder. +func (m *BrokerJoinTokenMutation) Where(ps ...predicate.BrokerJoinToken) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the BrokerJoinTokenMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *BrokerJoinTokenMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.BrokerJoinToken, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *BrokerJoinTokenMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *BrokerJoinTokenMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (BrokerJoinToken). +func (m *BrokerJoinTokenMutation) Type() string { + return m.typ +} + +// Fields returns all fields that were changed during this mutation. Note that in +// order to get all numeric fields that were incremented/decremented, call +// AddedFields(). +func (m *BrokerJoinTokenMutation) Fields() []string { + fields := make([]string, 0, 4) + if m.token_hash != nil { + fields = append(fields, brokerjointoken.FieldTokenHash) + } + if m.expires_at != nil { + fields = append(fields, brokerjointoken.FieldExpiresAt) + } + if m.created_by != nil { + fields = append(fields, brokerjointoken.FieldCreatedBy) + } + if m.created != nil { + fields = append(fields, brokerjointoken.FieldCreated) + } + return fields +} + +// Field returns the value of a field with the given name. The second boolean +// return value indicates that this field was not set, or was not defined in the +// schema. +func (m *BrokerJoinTokenMutation) Field(name string) (ent.Value, bool) { + switch name { + case brokerjointoken.FieldTokenHash: + return m.TokenHash() + case brokerjointoken.FieldExpiresAt: + return m.ExpiresAt() + case brokerjointoken.FieldCreatedBy: + return m.CreatedBy() + case brokerjointoken.FieldCreated: + return m.Created() + } + return nil, false +} + +// OldField returns the old value of the field from the database. An error is +// returned if the mutation operation is not UpdateOne, or the query to the +// database failed. +func (m *BrokerJoinTokenMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + switch name { + case brokerjointoken.FieldTokenHash: + return m.OldTokenHash(ctx) + case brokerjointoken.FieldExpiresAt: + return m.OldExpiresAt(ctx) + case brokerjointoken.FieldCreatedBy: + return m.OldCreatedBy(ctx) + case brokerjointoken.FieldCreated: + return m.OldCreated(ctx) + } + return nil, fmt.Errorf("unknown BrokerJoinToken field %s", name) +} + +// SetField sets the value of a field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *BrokerJoinTokenMutation) SetField(name string, value ent.Value) error { + switch name { + case brokerjointoken.FieldTokenHash: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetTokenHash(v) + return nil + case brokerjointoken.FieldExpiresAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetExpiresAt(v) + return nil + case brokerjointoken.FieldCreatedBy: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreatedBy(v) + return nil + case brokerjointoken.FieldCreated: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreated(v) + return nil + } + return fmt.Errorf("unknown BrokerJoinToken field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *BrokerJoinTokenMutation) AddedFields() []string { + return nil +} + +// AddedField returns the numeric value that was incremented/decremented on a field +// with the given name. The second boolean return value indicates that this field +// was not set, or was not defined in the schema. +func (m *BrokerJoinTokenMutation) AddedField(name string) (ent.Value, bool) { + return nil, false +} + +// AddField adds the value to the field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *BrokerJoinTokenMutation) AddField(name string, value ent.Value) error { + switch name { + } + return fmt.Errorf("unknown BrokerJoinToken numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *BrokerJoinTokenMutation) ClearedFields() []string { + return nil +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *BrokerJoinTokenMutation) FieldCleared(name string) bool { + _, ok := m.clearedFields[name] + return ok +} + +// ClearField clears the value of the field with the given name. It returns an +// error if the field is not defined in the schema. +func (m *BrokerJoinTokenMutation) ClearField(name string) error { + return fmt.Errorf("unknown BrokerJoinToken nullable field %s", name) +} + +// ResetField resets all changes in the mutation for the field with the given name. +// It returns an error if the field is not defined in the schema. +func (m *BrokerJoinTokenMutation) ResetField(name string) error { + switch name { + case brokerjointoken.FieldTokenHash: + m.ResetTokenHash() + return nil + case brokerjointoken.FieldExpiresAt: + m.ResetExpiresAt() + return nil + case brokerjointoken.FieldCreatedBy: + m.ResetCreatedBy() + return nil + case brokerjointoken.FieldCreated: + m.ResetCreated() + return nil + } + return fmt.Errorf("unknown BrokerJoinToken field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *BrokerJoinTokenMutation) AddedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *BrokerJoinTokenMutation) AddedIDs(name string) []ent.Value { + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *BrokerJoinTokenMutation) RemovedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *BrokerJoinTokenMutation) RemovedIDs(name string) []ent.Value { + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *BrokerJoinTokenMutation) ClearedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *BrokerJoinTokenMutation) EdgeCleared(name string) bool { + return false +} + +// ClearEdge clears the value of the edge with the given name. It returns an error +// if that edge is not defined in the schema. +func (m *BrokerJoinTokenMutation) ClearEdge(name string) error { + return fmt.Errorf("unknown BrokerJoinToken unique edge %s", name) +} + +// ResetEdge resets all changes to the edge with the given name in this mutation. +// It returns an error if the edge is not defined in the schema. +func (m *BrokerJoinTokenMutation) ResetEdge(name string) error { + return fmt.Errorf("unknown BrokerJoinToken edge %s", name) +} + +// BrokerSecretMutation represents an operation that mutates the BrokerSecret nodes in the graph. +type BrokerSecretMutation struct { + config + op Op + typ string + id *uuid.UUID + secret_key *[]byte + algorithm *string + rotated_at *time.Time + expires_at *time.Time + status *string + created *time.Time + clearedFields map[string]struct{} + done bool + oldValue func(context.Context) (*BrokerSecret, error) + predicates []predicate.BrokerSecret +} + +var _ ent.Mutation = (*BrokerSecretMutation)(nil) + +// brokersecretOption allows management of the mutation configuration using functional options. +type brokersecretOption func(*BrokerSecretMutation) + +// newBrokerSecretMutation creates new mutation for the BrokerSecret entity. +func newBrokerSecretMutation(c config, op Op, opts ...brokersecretOption) *BrokerSecretMutation { + m := &BrokerSecretMutation{ + config: c, + op: op, + typ: TypeBrokerSecret, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// withBrokerSecretID sets the ID field of the mutation. +func withBrokerSecretID(id uuid.UUID) brokersecretOption { + return func(m *BrokerSecretMutation) { + var ( + err error + once sync.Once + value *BrokerSecret + ) + m.oldValue = func(ctx context.Context) (*BrokerSecret, error) { + once.Do(func() { + if m.done { + err = errors.New("querying old values post mutation is not allowed") + } else { + value, err = m.Client().BrokerSecret.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } +} + +// withBrokerSecret sets the old BrokerSecret of the mutation. +func withBrokerSecret(node *BrokerSecret) brokersecretOption { + return func(m *BrokerSecretMutation) { + m.oldValue = func(context.Context) (*BrokerSecret, error) { + return node, nil + } + m.id = &node.ID + } +} + +// Client returns a new `ent.Client` from the mutation. If the mutation was +// executed in a transaction (ent.Tx), a transactional client is returned. +func (m BrokerSecretMutation) Client() *Client { + client := &Client{config: m.config} + client.init() + return client +} + +// Tx returns an `ent.Tx` for mutations that were executed in transactions; +// it returns an error otherwise. +func (m BrokerSecretMutation) Tx() (*Tx, error) { + if _, ok := m.driver.(*txDriver); !ok { + return nil, errors.New("ent: mutation is not running in a transaction") + } + tx := &Tx{config: m.config} + tx.init() + return tx, nil +} + +// SetID sets the value of the id field. Note that this +// operation is only accepted on creation of BrokerSecret entities. +func (m *BrokerSecretMutation) SetID(id uuid.UUID) { + m.id = &id +} + +// ID returns the ID value in the mutation. Note that the ID is only available +// if it was provided to the builder or after it was returned from the database. +func (m *BrokerSecretMutation) ID() (id uuid.UUID, exists bool) { + if m.id == nil { + return + } + return *m.id, true +} + +// IDs queries the database and returns the entity ids that match the mutation's predicate. +// That means, if the mutation is applied within a transaction with an isolation level such +// as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated +// or updated by the mutation. +func (m *BrokerSecretMutation) IDs(ctx context.Context) ([]uuid.UUID, error) { + switch { + case m.op.Is(OpUpdateOne | OpDeleteOne): + id, exists := m.ID() + if exists { + return []uuid.UUID{id}, nil + } + fallthrough + case m.op.Is(OpUpdate | OpDelete): + return m.Client().BrokerSecret.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } +} + +// SetSecretKey sets the "secret_key" field. +func (m *BrokerSecretMutation) SetSecretKey(b []byte) { + m.secret_key = &b +} + +// SecretKey returns the value of the "secret_key" field in the mutation. +func (m *BrokerSecretMutation) SecretKey() (r []byte, exists bool) { + v := m.secret_key + if v == nil { + return + } + return *v, true +} + +// OldSecretKey returns the old "secret_key" field's value of the BrokerSecret entity. +// If the BrokerSecret object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *BrokerSecretMutation) OldSecretKey(ctx context.Context) (v []byte, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldSecretKey is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldSecretKey requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldSecretKey: %w", err) + } + return oldValue.SecretKey, nil +} + +// ResetSecretKey resets all changes to the "secret_key" field. +func (m *BrokerSecretMutation) ResetSecretKey() { + m.secret_key = nil +} + +// SetAlgorithm sets the "algorithm" field. +func (m *BrokerSecretMutation) SetAlgorithm(s string) { + m.algorithm = &s +} + +// Algorithm returns the value of the "algorithm" field in the mutation. +func (m *BrokerSecretMutation) Algorithm() (r string, exists bool) { + v := m.algorithm + if v == nil { + return + } + return *v, true +} + +// OldAlgorithm returns the old "algorithm" field's value of the BrokerSecret entity. +// If the BrokerSecret object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *BrokerSecretMutation) OldAlgorithm(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldAlgorithm is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldAlgorithm requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldAlgorithm: %w", err) + } + return oldValue.Algorithm, nil +} + +// ResetAlgorithm resets all changes to the "algorithm" field. +func (m *BrokerSecretMutation) ResetAlgorithm() { + m.algorithm = nil +} + +// SetRotatedAt sets the "rotated_at" field. +func (m *BrokerSecretMutation) SetRotatedAt(t time.Time) { + m.rotated_at = &t +} + +// RotatedAt returns the value of the "rotated_at" field in the mutation. +func (m *BrokerSecretMutation) RotatedAt() (r time.Time, exists bool) { + v := m.rotated_at + if v == nil { + return + } + return *v, true +} + +// OldRotatedAt returns the old "rotated_at" field's value of the BrokerSecret entity. +// If the BrokerSecret object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *BrokerSecretMutation) OldRotatedAt(ctx context.Context) (v *time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldRotatedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldRotatedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldRotatedAt: %w", err) + } + return oldValue.RotatedAt, nil +} + +// ClearRotatedAt clears the value of the "rotated_at" field. +func (m *BrokerSecretMutation) ClearRotatedAt() { + m.rotated_at = nil + m.clearedFields[brokersecret.FieldRotatedAt] = struct{}{} +} + +// RotatedAtCleared returns if the "rotated_at" field was cleared in this mutation. +func (m *BrokerSecretMutation) RotatedAtCleared() bool { + _, ok := m.clearedFields[brokersecret.FieldRotatedAt] + return ok +} + +// ResetRotatedAt resets all changes to the "rotated_at" field. +func (m *BrokerSecretMutation) ResetRotatedAt() { + m.rotated_at = nil + delete(m.clearedFields, brokersecret.FieldRotatedAt) +} + +// SetExpiresAt sets the "expires_at" field. +func (m *BrokerSecretMutation) SetExpiresAt(t time.Time) { + m.expires_at = &t +} + +// ExpiresAt returns the value of the "expires_at" field in the mutation. +func (m *BrokerSecretMutation) ExpiresAt() (r time.Time, exists bool) { + v := m.expires_at + if v == nil { + return + } + return *v, true +} + +// OldExpiresAt returns the old "expires_at" field's value of the BrokerSecret entity. +// If the BrokerSecret object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *BrokerSecretMutation) OldExpiresAt(ctx context.Context) (v *time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldExpiresAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldExpiresAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldExpiresAt: %w", err) + } + return oldValue.ExpiresAt, nil +} + +// ClearExpiresAt clears the value of the "expires_at" field. +func (m *BrokerSecretMutation) ClearExpiresAt() { + m.expires_at = nil + m.clearedFields[brokersecret.FieldExpiresAt] = struct{}{} +} + +// ExpiresAtCleared returns if the "expires_at" field was cleared in this mutation. +func (m *BrokerSecretMutation) ExpiresAtCleared() bool { + _, ok := m.clearedFields[brokersecret.FieldExpiresAt] + return ok +} + +// ResetExpiresAt resets all changes to the "expires_at" field. +func (m *BrokerSecretMutation) ResetExpiresAt() { + m.expires_at = nil + delete(m.clearedFields, brokersecret.FieldExpiresAt) +} + +// SetStatus sets the "status" field. +func (m *BrokerSecretMutation) SetStatus(s string) { + m.status = &s +} + +// Status returns the value of the "status" field in the mutation. +func (m *BrokerSecretMutation) Status() (r string, exists bool) { + v := m.status + if v == nil { + return + } + return *v, true +} + +// OldStatus returns the old "status" field's value of the BrokerSecret entity. +// If the BrokerSecret object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *BrokerSecretMutation) OldStatus(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldStatus is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldStatus requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldStatus: %w", err) + } + return oldValue.Status, nil +} + +// ResetStatus resets all changes to the "status" field. +func (m *BrokerSecretMutation) ResetStatus() { + m.status = nil +} + +// SetCreated sets the "created" field. +func (m *BrokerSecretMutation) SetCreated(t time.Time) { + m.created = &t +} + +// Created returns the value of the "created" field in the mutation. +func (m *BrokerSecretMutation) Created() (r time.Time, exists bool) { + v := m.created + if v == nil { + return + } + return *v, true +} + +// OldCreated returns the old "created" field's value of the BrokerSecret entity. +// If the BrokerSecret object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *BrokerSecretMutation) OldCreated(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreated is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreated requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreated: %w", err) + } + return oldValue.Created, nil +} + +// ResetCreated resets all changes to the "created" field. +func (m *BrokerSecretMutation) ResetCreated() { + m.created = nil +} + +// Where appends a list predicates to the BrokerSecretMutation builder. +func (m *BrokerSecretMutation) Where(ps ...predicate.BrokerSecret) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the BrokerSecretMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *BrokerSecretMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.BrokerSecret, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *BrokerSecretMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *BrokerSecretMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (BrokerSecret). +func (m *BrokerSecretMutation) Type() string { + return m.typ +} + +// Fields returns all fields that were changed during this mutation. Note that in +// order to get all numeric fields that were incremented/decremented, call +// AddedFields(). +func (m *BrokerSecretMutation) Fields() []string { + fields := make([]string, 0, 6) + if m.secret_key != nil { + fields = append(fields, brokersecret.FieldSecretKey) + } + if m.algorithm != nil { + fields = append(fields, brokersecret.FieldAlgorithm) + } + if m.rotated_at != nil { + fields = append(fields, brokersecret.FieldRotatedAt) + } + if m.expires_at != nil { + fields = append(fields, brokersecret.FieldExpiresAt) + } + if m.status != nil { + fields = append(fields, brokersecret.FieldStatus) + } + if m.created != nil { + fields = append(fields, brokersecret.FieldCreated) + } + return fields +} + +// Field returns the value of a field with the given name. The second boolean +// return value indicates that this field was not set, or was not defined in the +// schema. +func (m *BrokerSecretMutation) Field(name string) (ent.Value, bool) { + switch name { + case brokersecret.FieldSecretKey: + return m.SecretKey() + case brokersecret.FieldAlgorithm: + return m.Algorithm() + case brokersecret.FieldRotatedAt: + return m.RotatedAt() + case brokersecret.FieldExpiresAt: + return m.ExpiresAt() + case brokersecret.FieldStatus: + return m.Status() + case brokersecret.FieldCreated: + return m.Created() + } + return nil, false +} + +// OldField returns the old value of the field from the database. An error is +// returned if the mutation operation is not UpdateOne, or the query to the +// database failed. +func (m *BrokerSecretMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + switch name { + case brokersecret.FieldSecretKey: + return m.OldSecretKey(ctx) + case brokersecret.FieldAlgorithm: + return m.OldAlgorithm(ctx) + case brokersecret.FieldRotatedAt: + return m.OldRotatedAt(ctx) + case brokersecret.FieldExpiresAt: + return m.OldExpiresAt(ctx) + case brokersecret.FieldStatus: + return m.OldStatus(ctx) + case brokersecret.FieldCreated: + return m.OldCreated(ctx) + } + return nil, fmt.Errorf("unknown BrokerSecret field %s", name) +} + +// SetField sets the value of a field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *BrokerSecretMutation) SetField(name string, value ent.Value) error { + switch name { + case brokersecret.FieldSecretKey: + v, ok := value.([]byte) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetSecretKey(v) + return nil + case brokersecret.FieldAlgorithm: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetAlgorithm(v) + return nil + case brokersecret.FieldRotatedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetRotatedAt(v) + return nil + case brokersecret.FieldExpiresAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetExpiresAt(v) + return nil + case brokersecret.FieldStatus: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetStatus(v) + return nil + case brokersecret.FieldCreated: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreated(v) + return nil + } + return fmt.Errorf("unknown BrokerSecret field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *BrokerSecretMutation) AddedFields() []string { + return nil +} + +// AddedField returns the numeric value that was incremented/decremented on a field +// with the given name. The second boolean return value indicates that this field +// was not set, or was not defined in the schema. +func (m *BrokerSecretMutation) AddedField(name string) (ent.Value, bool) { + return nil, false +} + +// AddField adds the value to the field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *BrokerSecretMutation) AddField(name string, value ent.Value) error { + switch name { + } + return fmt.Errorf("unknown BrokerSecret numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *BrokerSecretMutation) ClearedFields() []string { + var fields []string + if m.FieldCleared(brokersecret.FieldRotatedAt) { + fields = append(fields, brokersecret.FieldRotatedAt) + } + if m.FieldCleared(brokersecret.FieldExpiresAt) { + fields = append(fields, brokersecret.FieldExpiresAt) + } + return fields +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *BrokerSecretMutation) FieldCleared(name string) bool { + _, ok := m.clearedFields[name] + return ok +} + +// ClearField clears the value of the field with the given name. It returns an +// error if the field is not defined in the schema. +func (m *BrokerSecretMutation) ClearField(name string) error { + switch name { + case brokersecret.FieldRotatedAt: + m.ClearRotatedAt() + return nil + case brokersecret.FieldExpiresAt: + m.ClearExpiresAt() + return nil + } + return fmt.Errorf("unknown BrokerSecret nullable field %s", name) +} + +// ResetField resets all changes in the mutation for the field with the given name. +// It returns an error if the field is not defined in the schema. +func (m *BrokerSecretMutation) ResetField(name string) error { + switch name { + case brokersecret.FieldSecretKey: + m.ResetSecretKey() + return nil + case brokersecret.FieldAlgorithm: + m.ResetAlgorithm() + return nil + case brokersecret.FieldRotatedAt: + m.ResetRotatedAt() + return nil + case brokersecret.FieldExpiresAt: + m.ResetExpiresAt() + return nil + case brokersecret.FieldStatus: + m.ResetStatus() + return nil + case brokersecret.FieldCreated: + m.ResetCreated() + return nil + } + return fmt.Errorf("unknown BrokerSecret field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *BrokerSecretMutation) AddedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *BrokerSecretMutation) AddedIDs(name string) []ent.Value { + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *BrokerSecretMutation) RemovedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *BrokerSecretMutation) RemovedIDs(name string) []ent.Value { + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *BrokerSecretMutation) ClearedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *BrokerSecretMutation) EdgeCleared(name string) bool { + return false +} + +// ClearEdge clears the value of the edge with the given name. It returns an error +// if that edge is not defined in the schema. +func (m *BrokerSecretMutation) ClearEdge(name string) error { + return fmt.Errorf("unknown BrokerSecret unique edge %s", name) +} + +// ResetEdge resets all changes to the edge with the given name in this mutation. +// It returns an error if the edge is not defined in the schema. +func (m *BrokerSecretMutation) ResetEdge(name string) error { + return fmt.Errorf("unknown BrokerSecret edge %s", name) +} + +// EnvVarMutation represents an operation that mutates the EnvVar nodes in the graph. +type EnvVarMutation struct { + config + op Op + typ string + id *uuid.UUID + key *string + value *string + scope *string + scope_id *string + description *string + sensitive *bool + injection_mode *envvar.InjectionMode + secret *bool + created_by *string + created *time.Time + updated *time.Time + clearedFields map[string]struct{} + done bool + oldValue func(context.Context) (*EnvVar, error) + predicates []predicate.EnvVar +} + +var _ ent.Mutation = (*EnvVarMutation)(nil) + +// envvarOption allows management of the mutation configuration using functional options. +type envvarOption func(*EnvVarMutation) + +// newEnvVarMutation creates new mutation for the EnvVar entity. +func newEnvVarMutation(c config, op Op, opts ...envvarOption) *EnvVarMutation { + m := &EnvVarMutation{ + config: c, + op: op, + typ: TypeEnvVar, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// withEnvVarID sets the ID field of the mutation. +func withEnvVarID(id uuid.UUID) envvarOption { + return func(m *EnvVarMutation) { + var ( + err error + once sync.Once + value *EnvVar + ) + m.oldValue = func(ctx context.Context) (*EnvVar, error) { + once.Do(func() { + if m.done { + err = errors.New("querying old values post mutation is not allowed") + } else { + value, err = m.Client().EnvVar.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } +} + +// withEnvVar sets the old EnvVar of the mutation. +func withEnvVar(node *EnvVar) envvarOption { + return func(m *EnvVarMutation) { + m.oldValue = func(context.Context) (*EnvVar, error) { + return node, nil + } + m.id = &node.ID + } +} + +// Client returns a new `ent.Client` from the mutation. If the mutation was +// executed in a transaction (ent.Tx), a transactional client is returned. +func (m EnvVarMutation) Client() *Client { + client := &Client{config: m.config} + client.init() + return client +} + +// Tx returns an `ent.Tx` for mutations that were executed in transactions; +// it returns an error otherwise. +func (m EnvVarMutation) Tx() (*Tx, error) { + if _, ok := m.driver.(*txDriver); !ok { + return nil, errors.New("ent: mutation is not running in a transaction") + } + tx := &Tx{config: m.config} + tx.init() + return tx, nil +} + +// SetID sets the value of the id field. Note that this +// operation is only accepted on creation of EnvVar entities. +func (m *EnvVarMutation) SetID(id uuid.UUID) { + m.id = &id +} + +// ID returns the ID value in the mutation. Note that the ID is only available +// if it was provided to the builder or after it was returned from the database. +func (m *EnvVarMutation) ID() (id uuid.UUID, exists bool) { + if m.id == nil { + return + } + return *m.id, true +} + +// IDs queries the database and returns the entity ids that match the mutation's predicate. +// That means, if the mutation is applied within a transaction with an isolation level such +// as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated +// or updated by the mutation. +func (m *EnvVarMutation) IDs(ctx context.Context) ([]uuid.UUID, error) { + switch { + case m.op.Is(OpUpdateOne | OpDeleteOne): + id, exists := m.ID() + if exists { + return []uuid.UUID{id}, nil + } + fallthrough + case m.op.Is(OpUpdate | OpDelete): + return m.Client().EnvVar.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } +} + +// SetKey sets the "key" field. +func (m *EnvVarMutation) SetKey(s string) { + m.key = &s +} + +// Key returns the value of the "key" field in the mutation. +func (m *EnvVarMutation) Key() (r string, exists bool) { + v := m.key + if v == nil { + return + } + return *v, true +} + +// OldKey returns the old "key" field's value of the EnvVar entity. +// If the EnvVar object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *EnvVarMutation) OldKey(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldKey is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldKey requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldKey: %w", err) + } + return oldValue.Key, nil +} + +// ResetKey resets all changes to the "key" field. +func (m *EnvVarMutation) ResetKey() { + m.key = nil +} + +// SetValue sets the "value" field. +func (m *EnvVarMutation) SetValue(s string) { + m.value = &s +} + +// Value returns the value of the "value" field in the mutation. +func (m *EnvVarMutation) Value() (r string, exists bool) { + v := m.value + if v == nil { + return + } + return *v, true +} + +// OldValue returns the old "value" field's value of the EnvVar entity. +// If the EnvVar object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *EnvVarMutation) OldValue(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldValue is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldValue requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldValue: %w", err) + } + return oldValue.Value, nil +} + +// ResetValue resets all changes to the "value" field. +func (m *EnvVarMutation) ResetValue() { + m.value = nil +} + +// SetScope sets the "scope" field. +func (m *EnvVarMutation) SetScope(s string) { + m.scope = &s +} + +// Scope returns the value of the "scope" field in the mutation. +func (m *EnvVarMutation) Scope() (r string, exists bool) { + v := m.scope + if v == nil { + return + } + return *v, true +} + +// OldScope returns the old "scope" field's value of the EnvVar entity. +// If the EnvVar object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *EnvVarMutation) OldScope(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldScope is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldScope requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldScope: %w", err) + } + return oldValue.Scope, nil +} + +// ResetScope resets all changes to the "scope" field. +func (m *EnvVarMutation) ResetScope() { + m.scope = nil +} + +// SetScopeID sets the "scope_id" field. +func (m *EnvVarMutation) SetScopeID(s string) { + m.scope_id = &s +} + +// ScopeID returns the value of the "scope_id" field in the mutation. +func (m *EnvVarMutation) ScopeID() (r string, exists bool) { + v := m.scope_id + if v == nil { + return + } + return *v, true +} + +// OldScopeID returns the old "scope_id" field's value of the EnvVar entity. +// If the EnvVar object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *EnvVarMutation) OldScopeID(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldScopeID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldScopeID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldScopeID: %w", err) + } + return oldValue.ScopeID, nil +} + +// ResetScopeID resets all changes to the "scope_id" field. +func (m *EnvVarMutation) ResetScopeID() { + m.scope_id = nil +} + +// SetDescription sets the "description" field. +func (m *EnvVarMutation) SetDescription(s string) { + m.description = &s +} + +// Description returns the value of the "description" field in the mutation. +func (m *EnvVarMutation) Description() (r string, exists bool) { + v := m.description + if v == nil { + return + } + return *v, true +} + +// OldDescription returns the old "description" field's value of the EnvVar entity. +// If the EnvVar object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *EnvVarMutation) OldDescription(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldDescription is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldDescription requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldDescription: %w", err) + } + return oldValue.Description, nil +} + +// ClearDescription clears the value of the "description" field. +func (m *EnvVarMutation) ClearDescription() { + m.description = nil + m.clearedFields[envvar.FieldDescription] = struct{}{} +} + +// DescriptionCleared returns if the "description" field was cleared in this mutation. +func (m *EnvVarMutation) DescriptionCleared() bool { + _, ok := m.clearedFields[envvar.FieldDescription] + return ok +} + +// ResetDescription resets all changes to the "description" field. +func (m *EnvVarMutation) ResetDescription() { + m.description = nil + delete(m.clearedFields, envvar.FieldDescription) +} + +// SetSensitive sets the "sensitive" field. +func (m *EnvVarMutation) SetSensitive(b bool) { + m.sensitive = &b +} + +// Sensitive returns the value of the "sensitive" field in the mutation. +func (m *EnvVarMutation) Sensitive() (r bool, exists bool) { + v := m.sensitive + if v == nil { + return + } + return *v, true +} + +// OldSensitive returns the old "sensitive" field's value of the EnvVar entity. +// If the EnvVar object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *EnvVarMutation) OldSensitive(ctx context.Context) (v bool, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldSensitive is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldSensitive requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldSensitive: %w", err) + } + return oldValue.Sensitive, nil +} + +// ResetSensitive resets all changes to the "sensitive" field. +func (m *EnvVarMutation) ResetSensitive() { + m.sensitive = nil +} + +// SetInjectionMode sets the "injection_mode" field. +func (m *EnvVarMutation) SetInjectionMode(em envvar.InjectionMode) { + m.injection_mode = &em +} + +// InjectionMode returns the value of the "injection_mode" field in the mutation. +func (m *EnvVarMutation) InjectionMode() (r envvar.InjectionMode, exists bool) { + v := m.injection_mode + if v == nil { + return + } + return *v, true +} + +// OldInjectionMode returns the old "injection_mode" field's value of the EnvVar entity. +// If the EnvVar object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *EnvVarMutation) OldInjectionMode(ctx context.Context) (v envvar.InjectionMode, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldInjectionMode is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldInjectionMode requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldInjectionMode: %w", err) + } + return oldValue.InjectionMode, nil +} + +// ResetInjectionMode resets all changes to the "injection_mode" field. +func (m *EnvVarMutation) ResetInjectionMode() { + m.injection_mode = nil +} + +// SetSecret sets the "secret" field. +func (m *EnvVarMutation) SetSecret(b bool) { + m.secret = &b +} + +// Secret returns the value of the "secret" field in the mutation. +func (m *EnvVarMutation) Secret() (r bool, exists bool) { + v := m.secret + if v == nil { + return + } + return *v, true +} + +// OldSecret returns the old "secret" field's value of the EnvVar entity. +// If the EnvVar object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *EnvVarMutation) OldSecret(ctx context.Context) (v bool, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldSecret is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldSecret requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldSecret: %w", err) + } + return oldValue.Secret, nil +} + +// ResetSecret resets all changes to the "secret" field. +func (m *EnvVarMutation) ResetSecret() { + m.secret = nil +} + +// SetCreatedBy sets the "created_by" field. +func (m *EnvVarMutation) SetCreatedBy(s string) { + m.created_by = &s +} + +// CreatedBy returns the value of the "created_by" field in the mutation. +func (m *EnvVarMutation) CreatedBy() (r string, exists bool) { + v := m.created_by + if v == nil { + return + } + return *v, true +} + +// OldCreatedBy returns the old "created_by" field's value of the EnvVar entity. +// If the EnvVar object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *EnvVarMutation) OldCreatedBy(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreatedBy is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreatedBy requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreatedBy: %w", err) + } + return oldValue.CreatedBy, nil +} + +// ClearCreatedBy clears the value of the "created_by" field. +func (m *EnvVarMutation) ClearCreatedBy() { + m.created_by = nil + m.clearedFields[envvar.FieldCreatedBy] = struct{}{} +} + +// CreatedByCleared returns if the "created_by" field was cleared in this mutation. +func (m *EnvVarMutation) CreatedByCleared() bool { + _, ok := m.clearedFields[envvar.FieldCreatedBy] + return ok +} + +// ResetCreatedBy resets all changes to the "created_by" field. +func (m *EnvVarMutation) ResetCreatedBy() { + m.created_by = nil + delete(m.clearedFields, envvar.FieldCreatedBy) +} + +// SetCreated sets the "created" field. +func (m *EnvVarMutation) SetCreated(t time.Time) { + m.created = &t +} + +// Created returns the value of the "created" field in the mutation. +func (m *EnvVarMutation) Created() (r time.Time, exists bool) { + v := m.created + if v == nil { + return + } + return *v, true +} + +// OldCreated returns the old "created" field's value of the EnvVar entity. +// If the EnvVar object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *EnvVarMutation) OldCreated(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreated is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreated requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreated: %w", err) + } + return oldValue.Created, nil +} + +// ResetCreated resets all changes to the "created" field. +func (m *EnvVarMutation) ResetCreated() { + m.created = nil +} + +// SetUpdated sets the "updated" field. +func (m *EnvVarMutation) SetUpdated(t time.Time) { + m.updated = &t +} + +// Updated returns the value of the "updated" field in the mutation. +func (m *EnvVarMutation) Updated() (r time.Time, exists bool) { + v := m.updated + if v == nil { + return + } + return *v, true +} + +// OldUpdated returns the old "updated" field's value of the EnvVar entity. +// If the EnvVar object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *EnvVarMutation) OldUpdated(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldUpdated is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldUpdated requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldUpdated: %w", err) + } + return oldValue.Updated, nil +} + +// ResetUpdated resets all changes to the "updated" field. +func (m *EnvVarMutation) ResetUpdated() { + m.updated = nil +} + +// Where appends a list predicates to the EnvVarMutation builder. +func (m *EnvVarMutation) Where(ps ...predicate.EnvVar) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the EnvVarMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *EnvVarMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.EnvVar, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *EnvVarMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *EnvVarMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (EnvVar). +func (m *EnvVarMutation) Type() string { + return m.typ +} + +// Fields returns all fields that were changed during this mutation. Note that in +// order to get all numeric fields that were incremented/decremented, call +// AddedFields(). +func (m *EnvVarMutation) Fields() []string { + fields := make([]string, 0, 11) + if m.key != nil { + fields = append(fields, envvar.FieldKey) + } + if m.value != nil { + fields = append(fields, envvar.FieldValue) + } + if m.scope != nil { + fields = append(fields, envvar.FieldScope) + } + if m.scope_id != nil { + fields = append(fields, envvar.FieldScopeID) + } + if m.description != nil { + fields = append(fields, envvar.FieldDescription) + } + if m.sensitive != nil { + fields = append(fields, envvar.FieldSensitive) + } + if m.injection_mode != nil { + fields = append(fields, envvar.FieldInjectionMode) + } + if m.secret != nil { + fields = append(fields, envvar.FieldSecret) + } + if m.created_by != nil { + fields = append(fields, envvar.FieldCreatedBy) + } + if m.created != nil { + fields = append(fields, envvar.FieldCreated) + } + if m.updated != nil { + fields = append(fields, envvar.FieldUpdated) + } + return fields +} + +// Field returns the value of a field with the given name. The second boolean +// return value indicates that this field was not set, or was not defined in the +// schema. +func (m *EnvVarMutation) Field(name string) (ent.Value, bool) { + switch name { + case envvar.FieldKey: + return m.Key() + case envvar.FieldValue: + return m.Value() + case envvar.FieldScope: + return m.Scope() + case envvar.FieldScopeID: + return m.ScopeID() + case envvar.FieldDescription: + return m.Description() + case envvar.FieldSensitive: + return m.Sensitive() + case envvar.FieldInjectionMode: + return m.InjectionMode() + case envvar.FieldSecret: + return m.Secret() + case envvar.FieldCreatedBy: + return m.CreatedBy() + case envvar.FieldCreated: + return m.Created() + case envvar.FieldUpdated: + return m.Updated() + } + return nil, false +} + +// OldField returns the old value of the field from the database. An error is +// returned if the mutation operation is not UpdateOne, or the query to the +// database failed. +func (m *EnvVarMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + switch name { + case envvar.FieldKey: + return m.OldKey(ctx) + case envvar.FieldValue: + return m.OldValue(ctx) + case envvar.FieldScope: + return m.OldScope(ctx) + case envvar.FieldScopeID: + return m.OldScopeID(ctx) + case envvar.FieldDescription: + return m.OldDescription(ctx) + case envvar.FieldSensitive: + return m.OldSensitive(ctx) + case envvar.FieldInjectionMode: + return m.OldInjectionMode(ctx) + case envvar.FieldSecret: + return m.OldSecret(ctx) + case envvar.FieldCreatedBy: + return m.OldCreatedBy(ctx) + case envvar.FieldCreated: + return m.OldCreated(ctx) + case envvar.FieldUpdated: + return m.OldUpdated(ctx) + } + return nil, fmt.Errorf("unknown EnvVar field %s", name) +} + +// SetField sets the value of a field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *EnvVarMutation) SetField(name string, value ent.Value) error { + switch name { + case envvar.FieldKey: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetKey(v) + return nil + case envvar.FieldValue: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetValue(v) + return nil + case envvar.FieldScope: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetScope(v) + return nil + case envvar.FieldScopeID: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetScopeID(v) + return nil + case envvar.FieldDescription: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetDescription(v) + return nil + case envvar.FieldSensitive: + v, ok := value.(bool) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetSensitive(v) + return nil + case envvar.FieldInjectionMode: + v, ok := value.(envvar.InjectionMode) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetInjectionMode(v) + return nil + case envvar.FieldSecret: + v, ok := value.(bool) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetSecret(v) + return nil + case envvar.FieldCreatedBy: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreatedBy(v) + return nil + case envvar.FieldCreated: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreated(v) + return nil + case envvar.FieldUpdated: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetUpdated(v) + return nil + } + return fmt.Errorf("unknown EnvVar field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *EnvVarMutation) AddedFields() []string { + return nil +} + +// AddedField returns the numeric value that was incremented/decremented on a field +// with the given name. The second boolean return value indicates that this field +// was not set, or was not defined in the schema. +func (m *EnvVarMutation) AddedField(name string) (ent.Value, bool) { + return nil, false +} + +// AddField adds the value to the field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *EnvVarMutation) AddField(name string, value ent.Value) error { + switch name { + } + return fmt.Errorf("unknown EnvVar numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *EnvVarMutation) ClearedFields() []string { + var fields []string + if m.FieldCleared(envvar.FieldDescription) { + fields = append(fields, envvar.FieldDescription) + } + if m.FieldCleared(envvar.FieldCreatedBy) { + fields = append(fields, envvar.FieldCreatedBy) + } + return fields +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *EnvVarMutation) FieldCleared(name string) bool { + _, ok := m.clearedFields[name] + return ok +} + +// ClearField clears the value of the field with the given name. It returns an +// error if the field is not defined in the schema. +func (m *EnvVarMutation) ClearField(name string) error { + switch name { + case envvar.FieldDescription: + m.ClearDescription() + return nil + case envvar.FieldCreatedBy: + m.ClearCreatedBy() + return nil + } + return fmt.Errorf("unknown EnvVar nullable field %s", name) +} + +// ResetField resets all changes in the mutation for the field with the given name. +// It returns an error if the field is not defined in the schema. +func (m *EnvVarMutation) ResetField(name string) error { + switch name { + case envvar.FieldKey: + m.ResetKey() + return nil + case envvar.FieldValue: + m.ResetValue() + return nil + case envvar.FieldScope: + m.ResetScope() + return nil + case envvar.FieldScopeID: + m.ResetScopeID() + return nil + case envvar.FieldDescription: + m.ResetDescription() + return nil + case envvar.FieldSensitive: + m.ResetSensitive() + return nil + case envvar.FieldInjectionMode: + m.ResetInjectionMode() + return nil + case envvar.FieldSecret: + m.ResetSecret() + return nil + case envvar.FieldCreatedBy: + m.ResetCreatedBy() + return nil + case envvar.FieldCreated: + m.ResetCreated() + return nil + case envvar.FieldUpdated: + m.ResetUpdated() + return nil + } + return fmt.Errorf("unknown EnvVar field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *EnvVarMutation) AddedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *EnvVarMutation) AddedIDs(name string) []ent.Value { + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *EnvVarMutation) RemovedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *EnvVarMutation) RemovedIDs(name string) []ent.Value { + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *EnvVarMutation) ClearedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *EnvVarMutation) EdgeCleared(name string) bool { + return false +} + +// ClearEdge clears the value of the edge with the given name. It returns an error +// if that edge is not defined in the schema. +func (m *EnvVarMutation) ClearEdge(name string) error { + return fmt.Errorf("unknown EnvVar unique edge %s", name) +} + +// ResetEdge resets all changes to the edge with the given name in this mutation. +// It returns an error if the edge is not defined in the schema. +func (m *EnvVarMutation) ResetEdge(name string) error { + return fmt.Errorf("unknown EnvVar edge %s", name) +} + +// GCPServiceAccountMutation represents an operation that mutates the GCPServiceAccount nodes in the graph. +type GCPServiceAccountMutation struct { + config + op Op + typ string + id *uuid.UUID + scope *string + scope_id *string + email *string + project_id *string + display_name *string + default_scopes *string + verified *bool + verified_at *time.Time + created_by *string + managed *bool + managed_by *string + created *time.Time + clearedFields map[string]struct{} + done bool + oldValue func(context.Context) (*GCPServiceAccount, error) + predicates []predicate.GCPServiceAccount +} + +var _ ent.Mutation = (*GCPServiceAccountMutation)(nil) + +// gcpserviceaccountOption allows management of the mutation configuration using functional options. +type gcpserviceaccountOption func(*GCPServiceAccountMutation) + +// newGCPServiceAccountMutation creates new mutation for the GCPServiceAccount entity. +func newGCPServiceAccountMutation(c config, op Op, opts ...gcpserviceaccountOption) *GCPServiceAccountMutation { + m := &GCPServiceAccountMutation{ + config: c, + op: op, + typ: TypeGCPServiceAccount, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// withGCPServiceAccountID sets the ID field of the mutation. +func withGCPServiceAccountID(id uuid.UUID) gcpserviceaccountOption { + return func(m *GCPServiceAccountMutation) { + var ( + err error + once sync.Once + value *GCPServiceAccount + ) + m.oldValue = func(ctx context.Context) (*GCPServiceAccount, error) { + once.Do(func() { + if m.done { + err = errors.New("querying old values post mutation is not allowed") + } else { + value, err = m.Client().GCPServiceAccount.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } +} + +// withGCPServiceAccount sets the old GCPServiceAccount of the mutation. +func withGCPServiceAccount(node *GCPServiceAccount) gcpserviceaccountOption { + return func(m *GCPServiceAccountMutation) { + m.oldValue = func(context.Context) (*GCPServiceAccount, error) { + return node, nil + } + m.id = &node.ID + } +} + +// Client returns a new `ent.Client` from the mutation. If the mutation was +// executed in a transaction (ent.Tx), a transactional client is returned. +func (m GCPServiceAccountMutation) Client() *Client { + client := &Client{config: m.config} + client.init() + return client +} + +// Tx returns an `ent.Tx` for mutations that were executed in transactions; +// it returns an error otherwise. +func (m GCPServiceAccountMutation) Tx() (*Tx, error) { + if _, ok := m.driver.(*txDriver); !ok { + return nil, errors.New("ent: mutation is not running in a transaction") + } + tx := &Tx{config: m.config} + tx.init() + return tx, nil +} + +// SetID sets the value of the id field. Note that this +// operation is only accepted on creation of GCPServiceAccount entities. +func (m *GCPServiceAccountMutation) SetID(id uuid.UUID) { + m.id = &id +} + +// ID returns the ID value in the mutation. Note that the ID is only available +// if it was provided to the builder or after it was returned from the database. +func (m *GCPServiceAccountMutation) ID() (id uuid.UUID, exists bool) { + if m.id == nil { + return + } + return *m.id, true +} + +// IDs queries the database and returns the entity ids that match the mutation's predicate. +// That means, if the mutation is applied within a transaction with an isolation level such +// as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated +// or updated by the mutation. +func (m *GCPServiceAccountMutation) IDs(ctx context.Context) ([]uuid.UUID, error) { + switch { + case m.op.Is(OpUpdateOne | OpDeleteOne): + id, exists := m.ID() + if exists { + return []uuid.UUID{id}, nil + } + fallthrough + case m.op.Is(OpUpdate | OpDelete): + return m.Client().GCPServiceAccount.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } +} + +// SetScope sets the "scope" field. +func (m *GCPServiceAccountMutation) SetScope(s string) { + m.scope = &s +} + +// Scope returns the value of the "scope" field in the mutation. +func (m *GCPServiceAccountMutation) Scope() (r string, exists bool) { + v := m.scope + if v == nil { + return + } + return *v, true +} + +// OldScope returns the old "scope" field's value of the GCPServiceAccount entity. +// If the GCPServiceAccount object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *GCPServiceAccountMutation) OldScope(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldScope is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldScope requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldScope: %w", err) + } + return oldValue.Scope, nil +} + +// ResetScope resets all changes to the "scope" field. +func (m *GCPServiceAccountMutation) ResetScope() { + m.scope = nil +} + +// SetScopeID sets the "scope_id" field. +func (m *GCPServiceAccountMutation) SetScopeID(s string) { + m.scope_id = &s +} + +// ScopeID returns the value of the "scope_id" field in the mutation. +func (m *GCPServiceAccountMutation) ScopeID() (r string, exists bool) { + v := m.scope_id + if v == nil { + return + } + return *v, true +} + +// OldScopeID returns the old "scope_id" field's value of the GCPServiceAccount entity. +// If the GCPServiceAccount object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *GCPServiceAccountMutation) OldScopeID(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldScopeID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldScopeID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldScopeID: %w", err) + } + return oldValue.ScopeID, nil +} + +// ResetScopeID resets all changes to the "scope_id" field. +func (m *GCPServiceAccountMutation) ResetScopeID() { + m.scope_id = nil +} + +// SetEmail sets the "email" field. +func (m *GCPServiceAccountMutation) SetEmail(s string) { + m.email = &s +} + +// Email returns the value of the "email" field in the mutation. +func (m *GCPServiceAccountMutation) Email() (r string, exists bool) { + v := m.email + if v == nil { + return + } + return *v, true +} + +// OldEmail returns the old "email" field's value of the GCPServiceAccount entity. +// If the GCPServiceAccount object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *GCPServiceAccountMutation) OldEmail(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldEmail is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldEmail requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldEmail: %w", err) + } + return oldValue.Email, nil +} + +// ResetEmail resets all changes to the "email" field. +func (m *GCPServiceAccountMutation) ResetEmail() { + m.email = nil +} + +// SetProjectID sets the "project_id" field. +func (m *GCPServiceAccountMutation) SetProjectID(s string) { + m.project_id = &s +} + +// ProjectID returns the value of the "project_id" field in the mutation. +func (m *GCPServiceAccountMutation) ProjectID() (r string, exists bool) { + v := m.project_id + if v == nil { + return + } + return *v, true +} + +// OldProjectID returns the old "project_id" field's value of the GCPServiceAccount entity. +// If the GCPServiceAccount object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *GCPServiceAccountMutation) OldProjectID(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldProjectID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldProjectID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldProjectID: %w", err) + } + return oldValue.ProjectID, nil +} + +// ResetProjectID resets all changes to the "project_id" field. +func (m *GCPServiceAccountMutation) ResetProjectID() { + m.project_id = nil +} + +// SetDisplayName sets the "display_name" field. +func (m *GCPServiceAccountMutation) SetDisplayName(s string) { + m.display_name = &s +} + +// DisplayName returns the value of the "display_name" field in the mutation. +func (m *GCPServiceAccountMutation) DisplayName() (r string, exists bool) { + v := m.display_name + if v == nil { + return + } + return *v, true +} + +// OldDisplayName returns the old "display_name" field's value of the GCPServiceAccount entity. +// If the GCPServiceAccount object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *GCPServiceAccountMutation) OldDisplayName(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldDisplayName is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldDisplayName requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldDisplayName: %w", err) + } + return oldValue.DisplayName, nil +} + +// ResetDisplayName resets all changes to the "display_name" field. +func (m *GCPServiceAccountMutation) ResetDisplayName() { + m.display_name = nil +} + +// SetDefaultScopes sets the "default_scopes" field. +func (m *GCPServiceAccountMutation) SetDefaultScopes(s string) { + m.default_scopes = &s +} + +// DefaultScopes returns the value of the "default_scopes" field in the mutation. +func (m *GCPServiceAccountMutation) DefaultScopes() (r string, exists bool) { + v := m.default_scopes + if v == nil { + return + } + return *v, true +} + +// OldDefaultScopes returns the old "default_scopes" field's value of the GCPServiceAccount entity. +// If the GCPServiceAccount object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *GCPServiceAccountMutation) OldDefaultScopes(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldDefaultScopes is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldDefaultScopes requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldDefaultScopes: %w", err) + } + return oldValue.DefaultScopes, nil +} + +// ResetDefaultScopes resets all changes to the "default_scopes" field. +func (m *GCPServiceAccountMutation) ResetDefaultScopes() { + m.default_scopes = nil +} + +// SetVerified sets the "verified" field. +func (m *GCPServiceAccountMutation) SetVerified(b bool) { + m.verified = &b +} + +// Verified returns the value of the "verified" field in the mutation. +func (m *GCPServiceAccountMutation) Verified() (r bool, exists bool) { + v := m.verified + if v == nil { + return + } + return *v, true +} + +// OldVerified returns the old "verified" field's value of the GCPServiceAccount entity. +// If the GCPServiceAccount object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *GCPServiceAccountMutation) OldVerified(ctx context.Context) (v bool, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldVerified is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldVerified requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldVerified: %w", err) + } + return oldValue.Verified, nil +} + +// ResetVerified resets all changes to the "verified" field. +func (m *GCPServiceAccountMutation) ResetVerified() { + m.verified = nil +} + +// SetVerifiedAt sets the "verified_at" field. +func (m *GCPServiceAccountMutation) SetVerifiedAt(t time.Time) { + m.verified_at = &t +} + +// VerifiedAt returns the value of the "verified_at" field in the mutation. +func (m *GCPServiceAccountMutation) VerifiedAt() (r time.Time, exists bool) { + v := m.verified_at + if v == nil { + return + } + return *v, true +} + +// OldVerifiedAt returns the old "verified_at" field's value of the GCPServiceAccount entity. +// If the GCPServiceAccount object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *GCPServiceAccountMutation) OldVerifiedAt(ctx context.Context) (v *time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldVerifiedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldVerifiedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldVerifiedAt: %w", err) + } + return oldValue.VerifiedAt, nil +} + +// ClearVerifiedAt clears the value of the "verified_at" field. +func (m *GCPServiceAccountMutation) ClearVerifiedAt() { + m.verified_at = nil + m.clearedFields[gcpserviceaccount.FieldVerifiedAt] = struct{}{} +} + +// VerifiedAtCleared returns if the "verified_at" field was cleared in this mutation. +func (m *GCPServiceAccountMutation) VerifiedAtCleared() bool { + _, ok := m.clearedFields[gcpserviceaccount.FieldVerifiedAt] + return ok +} + +// ResetVerifiedAt resets all changes to the "verified_at" field. +func (m *GCPServiceAccountMutation) ResetVerifiedAt() { + m.verified_at = nil + delete(m.clearedFields, gcpserviceaccount.FieldVerifiedAt) +} + +// SetCreatedBy sets the "created_by" field. +func (m *GCPServiceAccountMutation) SetCreatedBy(s string) { + m.created_by = &s +} + +// CreatedBy returns the value of the "created_by" field in the mutation. +func (m *GCPServiceAccountMutation) CreatedBy() (r string, exists bool) { + v := m.created_by + if v == nil { + return + } + return *v, true +} + +// OldCreatedBy returns the old "created_by" field's value of the GCPServiceAccount entity. +// If the GCPServiceAccount object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *GCPServiceAccountMutation) OldCreatedBy(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreatedBy is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreatedBy requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreatedBy: %w", err) + } + return oldValue.CreatedBy, nil +} + +// ResetCreatedBy resets all changes to the "created_by" field. +func (m *GCPServiceAccountMutation) ResetCreatedBy() { + m.created_by = nil +} + +// SetManaged sets the "managed" field. +func (m *GCPServiceAccountMutation) SetManaged(b bool) { + m.managed = &b +} + +// Managed returns the value of the "managed" field in the mutation. +func (m *GCPServiceAccountMutation) Managed() (r bool, exists bool) { + v := m.managed + if v == nil { + return + } + return *v, true +} + +// OldManaged returns the old "managed" field's value of the GCPServiceAccount entity. +// If the GCPServiceAccount object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *GCPServiceAccountMutation) OldManaged(ctx context.Context) (v bool, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldManaged is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldManaged requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldManaged: %w", err) + } + return oldValue.Managed, nil +} + +// ResetManaged resets all changes to the "managed" field. +func (m *GCPServiceAccountMutation) ResetManaged() { + m.managed = nil +} + +// SetManagedBy sets the "managed_by" field. +func (m *GCPServiceAccountMutation) SetManagedBy(s string) { + m.managed_by = &s +} + +// ManagedBy returns the value of the "managed_by" field in the mutation. +func (m *GCPServiceAccountMutation) ManagedBy() (r string, exists bool) { + v := m.managed_by + if v == nil { + return + } + return *v, true +} + +// OldManagedBy returns the old "managed_by" field's value of the GCPServiceAccount entity. +// If the GCPServiceAccount object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *GCPServiceAccountMutation) OldManagedBy(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldManagedBy is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldManagedBy requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldManagedBy: %w", err) + } + return oldValue.ManagedBy, nil +} + +// ResetManagedBy resets all changes to the "managed_by" field. +func (m *GCPServiceAccountMutation) ResetManagedBy() { + m.managed_by = nil +} + +// SetCreated sets the "created" field. +func (m *GCPServiceAccountMutation) SetCreated(t time.Time) { + m.created = &t +} + +// Created returns the value of the "created" field in the mutation. +func (m *GCPServiceAccountMutation) Created() (r time.Time, exists bool) { + v := m.created + if v == nil { + return + } + return *v, true +} + +// OldCreated returns the old "created" field's value of the GCPServiceAccount entity. +// If the GCPServiceAccount object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *GCPServiceAccountMutation) OldCreated(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreated is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreated requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreated: %w", err) + } + return oldValue.Created, nil +} + +// ResetCreated resets all changes to the "created" field. +func (m *GCPServiceAccountMutation) ResetCreated() { + m.created = nil +} + +// Where appends a list predicates to the GCPServiceAccountMutation builder. +func (m *GCPServiceAccountMutation) Where(ps ...predicate.GCPServiceAccount) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the GCPServiceAccountMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *GCPServiceAccountMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.GCPServiceAccount, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *GCPServiceAccountMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *GCPServiceAccountMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (GCPServiceAccount). +func (m *GCPServiceAccountMutation) Type() string { + return m.typ +} + +// Fields returns all fields that were changed during this mutation. Note that in +// order to get all numeric fields that were incremented/decremented, call +// AddedFields(). +func (m *GCPServiceAccountMutation) Fields() []string { + fields := make([]string, 0, 12) + if m.scope != nil { + fields = append(fields, gcpserviceaccount.FieldScope) + } + if m.scope_id != nil { + fields = append(fields, gcpserviceaccount.FieldScopeID) + } + if m.email != nil { + fields = append(fields, gcpserviceaccount.FieldEmail) + } + if m.project_id != nil { + fields = append(fields, gcpserviceaccount.FieldProjectID) + } + if m.display_name != nil { + fields = append(fields, gcpserviceaccount.FieldDisplayName) + } + if m.default_scopes != nil { + fields = append(fields, gcpserviceaccount.FieldDefaultScopes) + } + if m.verified != nil { + fields = append(fields, gcpserviceaccount.FieldVerified) + } + if m.verified_at != nil { + fields = append(fields, gcpserviceaccount.FieldVerifiedAt) + } + if m.created_by != nil { + fields = append(fields, gcpserviceaccount.FieldCreatedBy) + } + if m.managed != nil { + fields = append(fields, gcpserviceaccount.FieldManaged) + } + if m.managed_by != nil { + fields = append(fields, gcpserviceaccount.FieldManagedBy) + } + if m.created != nil { + fields = append(fields, gcpserviceaccount.FieldCreated) + } + return fields +} + +// Field returns the value of a field with the given name. The second boolean +// return value indicates that this field was not set, or was not defined in the +// schema. +func (m *GCPServiceAccountMutation) Field(name string) (ent.Value, bool) { + switch name { + case gcpserviceaccount.FieldScope: + return m.Scope() + case gcpserviceaccount.FieldScopeID: + return m.ScopeID() + case gcpserviceaccount.FieldEmail: + return m.Email() + case gcpserviceaccount.FieldProjectID: + return m.ProjectID() + case gcpserviceaccount.FieldDisplayName: + return m.DisplayName() + case gcpserviceaccount.FieldDefaultScopes: + return m.DefaultScopes() + case gcpserviceaccount.FieldVerified: + return m.Verified() + case gcpserviceaccount.FieldVerifiedAt: + return m.VerifiedAt() + case gcpserviceaccount.FieldCreatedBy: + return m.CreatedBy() + case gcpserviceaccount.FieldManaged: + return m.Managed() + case gcpserviceaccount.FieldManagedBy: + return m.ManagedBy() + case gcpserviceaccount.FieldCreated: + return m.Created() + } + return nil, false +} + +// OldField returns the old value of the field from the database. An error is +// returned if the mutation operation is not UpdateOne, or the query to the +// database failed. +func (m *GCPServiceAccountMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + switch name { + case gcpserviceaccount.FieldScope: + return m.OldScope(ctx) + case gcpserviceaccount.FieldScopeID: + return m.OldScopeID(ctx) + case gcpserviceaccount.FieldEmail: + return m.OldEmail(ctx) + case gcpserviceaccount.FieldProjectID: + return m.OldProjectID(ctx) + case gcpserviceaccount.FieldDisplayName: + return m.OldDisplayName(ctx) + case gcpserviceaccount.FieldDefaultScopes: + return m.OldDefaultScopes(ctx) + case gcpserviceaccount.FieldVerified: + return m.OldVerified(ctx) + case gcpserviceaccount.FieldVerifiedAt: + return m.OldVerifiedAt(ctx) + case gcpserviceaccount.FieldCreatedBy: + return m.OldCreatedBy(ctx) + case gcpserviceaccount.FieldManaged: + return m.OldManaged(ctx) + case gcpserviceaccount.FieldManagedBy: + return m.OldManagedBy(ctx) + case gcpserviceaccount.FieldCreated: + return m.OldCreated(ctx) + } + return nil, fmt.Errorf("unknown GCPServiceAccount field %s", name) +} + +// SetField sets the value of a field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *GCPServiceAccountMutation) SetField(name string, value ent.Value) error { + switch name { + case gcpserviceaccount.FieldScope: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetScope(v) + return nil + case gcpserviceaccount.FieldScopeID: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetScopeID(v) + return nil + case gcpserviceaccount.FieldEmail: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetEmail(v) + return nil + case gcpserviceaccount.FieldProjectID: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetProjectID(v) + return nil + case gcpserviceaccount.FieldDisplayName: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetDisplayName(v) + return nil + case gcpserviceaccount.FieldDefaultScopes: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetDefaultScopes(v) + return nil + case gcpserviceaccount.FieldVerified: + v, ok := value.(bool) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetVerified(v) + return nil + case gcpserviceaccount.FieldVerifiedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetVerifiedAt(v) + return nil + case gcpserviceaccount.FieldCreatedBy: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreatedBy(v) + return nil + case gcpserviceaccount.FieldManaged: + v, ok := value.(bool) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetManaged(v) + return nil + case gcpserviceaccount.FieldManagedBy: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetManagedBy(v) + return nil + case gcpserviceaccount.FieldCreated: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreated(v) + return nil + } + return fmt.Errorf("unknown GCPServiceAccount field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *GCPServiceAccountMutation) AddedFields() []string { + return nil +} + +// AddedField returns the numeric value that was incremented/decremented on a field +// with the given name. The second boolean return value indicates that this field +// was not set, or was not defined in the schema. +func (m *GCPServiceAccountMutation) AddedField(name string) (ent.Value, bool) { + return nil, false +} + +// AddField adds the value to the field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *GCPServiceAccountMutation) AddField(name string, value ent.Value) error { + switch name { + } + return fmt.Errorf("unknown GCPServiceAccount numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *GCPServiceAccountMutation) ClearedFields() []string { + var fields []string + if m.FieldCleared(gcpserviceaccount.FieldVerifiedAt) { + fields = append(fields, gcpserviceaccount.FieldVerifiedAt) + } + return fields +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *GCPServiceAccountMutation) FieldCleared(name string) bool { + _, ok := m.clearedFields[name] + return ok +} + +// ClearField clears the value of the field with the given name. It returns an +// error if the field is not defined in the schema. +func (m *GCPServiceAccountMutation) ClearField(name string) error { + switch name { + case gcpserviceaccount.FieldVerifiedAt: + m.ClearVerifiedAt() + return nil + } + return fmt.Errorf("unknown GCPServiceAccount nullable field %s", name) +} + +// ResetField resets all changes in the mutation for the field with the given name. +// It returns an error if the field is not defined in the schema. +func (m *GCPServiceAccountMutation) ResetField(name string) error { + switch name { + case gcpserviceaccount.FieldScope: + m.ResetScope() + return nil + case gcpserviceaccount.FieldScopeID: + m.ResetScopeID() + return nil + case gcpserviceaccount.FieldEmail: + m.ResetEmail() + return nil + case gcpserviceaccount.FieldProjectID: + m.ResetProjectID() + return nil + case gcpserviceaccount.FieldDisplayName: + m.ResetDisplayName() + return nil + case gcpserviceaccount.FieldDefaultScopes: + m.ResetDefaultScopes() + return nil + case gcpserviceaccount.FieldVerified: + m.ResetVerified() + return nil + case gcpserviceaccount.FieldVerifiedAt: + m.ResetVerifiedAt() + return nil + case gcpserviceaccount.FieldCreatedBy: + m.ResetCreatedBy() + return nil + case gcpserviceaccount.FieldManaged: + m.ResetManaged() + return nil + case gcpserviceaccount.FieldManagedBy: + m.ResetManagedBy() + return nil + case gcpserviceaccount.FieldCreated: + m.ResetCreated() + return nil + } + return fmt.Errorf("unknown GCPServiceAccount field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *GCPServiceAccountMutation) AddedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *GCPServiceAccountMutation) AddedIDs(name string) []ent.Value { + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *GCPServiceAccountMutation) RemovedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *GCPServiceAccountMutation) RemovedIDs(name string) []ent.Value { + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *GCPServiceAccountMutation) ClearedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *GCPServiceAccountMutation) EdgeCleared(name string) bool { + return false +} + +// ClearEdge clears the value of the edge with the given name. It returns an error +// if that edge is not defined in the schema. +func (m *GCPServiceAccountMutation) ClearEdge(name string) error { + return fmt.Errorf("unknown GCPServiceAccount unique edge %s", name) +} + +// ResetEdge resets all changes to the edge with the given name in this mutation. +// It returns an error if the edge is not defined in the schema. +func (m *GCPServiceAccountMutation) ResetEdge(name string) error { + return fmt.Errorf("unknown GCPServiceAccount edge %s", name) +} + +// GithubInstallationMutation represents an operation that mutates the GithubInstallation nodes in the graph. +type GithubInstallationMutation struct { + config + op Op + typ string + id *int64 + account_login *string + account_type *string + app_id *int64 + addapp_id *int64 + repositories *string + status *string + created *time.Time + updated *time.Time + clearedFields map[string]struct{} + done bool + oldValue func(context.Context) (*GithubInstallation, error) + predicates []predicate.GithubInstallation +} + +var _ ent.Mutation = (*GithubInstallationMutation)(nil) + +// githubinstallationOption allows management of the mutation configuration using functional options. +type githubinstallationOption func(*GithubInstallationMutation) + +// newGithubInstallationMutation creates new mutation for the GithubInstallation entity. +func newGithubInstallationMutation(c config, op Op, opts ...githubinstallationOption) *GithubInstallationMutation { + m := &GithubInstallationMutation{ + config: c, + op: op, + typ: TypeGithubInstallation, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// withGithubInstallationID sets the ID field of the mutation. +func withGithubInstallationID(id int64) githubinstallationOption { + return func(m *GithubInstallationMutation) { + var ( + err error + once sync.Once + value *GithubInstallation + ) + m.oldValue = func(ctx context.Context) (*GithubInstallation, error) { + once.Do(func() { + if m.done { + err = errors.New("querying old values post mutation is not allowed") + } else { + value, err = m.Client().GithubInstallation.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } +} + +// withGithubInstallation sets the old GithubInstallation of the mutation. +func withGithubInstallation(node *GithubInstallation) githubinstallationOption { + return func(m *GithubInstallationMutation) { + m.oldValue = func(context.Context) (*GithubInstallation, error) { + return node, nil + } + m.id = &node.ID + } +} + +// Client returns a new `ent.Client` from the mutation. If the mutation was +// executed in a transaction (ent.Tx), a transactional client is returned. +func (m GithubInstallationMutation) Client() *Client { + client := &Client{config: m.config} + client.init() + return client +} + +// Tx returns an `ent.Tx` for mutations that were executed in transactions; +// it returns an error otherwise. +func (m GithubInstallationMutation) Tx() (*Tx, error) { + if _, ok := m.driver.(*txDriver); !ok { + return nil, errors.New("ent: mutation is not running in a transaction") + } + tx := &Tx{config: m.config} + tx.init() + return tx, nil +} + +// SetID sets the value of the id field. Note that this +// operation is only accepted on creation of GithubInstallation entities. +func (m *GithubInstallationMutation) SetID(id int64) { + m.id = &id +} + +// ID returns the ID value in the mutation. Note that the ID is only available +// if it was provided to the builder or after it was returned from the database. +func (m *GithubInstallationMutation) ID() (id int64, exists bool) { + if m.id == nil { + return + } + return *m.id, true +} + +// IDs queries the database and returns the entity ids that match the mutation's predicate. +// That means, if the mutation is applied within a transaction with an isolation level such +// as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated +// or updated by the mutation. +func (m *GithubInstallationMutation) IDs(ctx context.Context) ([]int64, error) { + switch { + case m.op.Is(OpUpdateOne | OpDeleteOne): + id, exists := m.ID() + if exists { + return []int64{id}, nil + } + fallthrough + case m.op.Is(OpUpdate | OpDelete): + return m.Client().GithubInstallation.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } +} + +// SetAccountLogin sets the "account_login" field. +func (m *GithubInstallationMutation) SetAccountLogin(s string) { + m.account_login = &s +} + +// AccountLogin returns the value of the "account_login" field in the mutation. +func (m *GithubInstallationMutation) AccountLogin() (r string, exists bool) { + v := m.account_login + if v == nil { + return + } + return *v, true +} + +// OldAccountLogin returns the old "account_login" field's value of the GithubInstallation entity. +// If the GithubInstallation object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *GithubInstallationMutation) OldAccountLogin(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldAccountLogin is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldAccountLogin requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldAccountLogin: %w", err) + } + return oldValue.AccountLogin, nil +} + +// ResetAccountLogin resets all changes to the "account_login" field. +func (m *GithubInstallationMutation) ResetAccountLogin() { + m.account_login = nil +} + +// SetAccountType sets the "account_type" field. +func (m *GithubInstallationMutation) SetAccountType(s string) { + m.account_type = &s +} + +// AccountType returns the value of the "account_type" field in the mutation. +func (m *GithubInstallationMutation) AccountType() (r string, exists bool) { + v := m.account_type + if v == nil { + return + } + return *v, true +} + +// OldAccountType returns the old "account_type" field's value of the GithubInstallation entity. +// If the GithubInstallation object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *GithubInstallationMutation) OldAccountType(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldAccountType is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldAccountType requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldAccountType: %w", err) + } + return oldValue.AccountType, nil +} + +// ResetAccountType resets all changes to the "account_type" field. +func (m *GithubInstallationMutation) ResetAccountType() { + m.account_type = nil +} + +// SetAppID sets the "app_id" field. +func (m *GithubInstallationMutation) SetAppID(i int64) { + m.app_id = &i + m.addapp_id = nil +} + +// AppID returns the value of the "app_id" field in the mutation. +func (m *GithubInstallationMutation) AppID() (r int64, exists bool) { + v := m.app_id + if v == nil { + return + } + return *v, true +} + +// OldAppID returns the old "app_id" field's value of the GithubInstallation entity. +// If the GithubInstallation object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *GithubInstallationMutation) OldAppID(ctx context.Context) (v int64, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldAppID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldAppID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldAppID: %w", err) + } + return oldValue.AppID, nil +} + +// AddAppID adds i to the "app_id" field. +func (m *GithubInstallationMutation) AddAppID(i int64) { + if m.addapp_id != nil { + *m.addapp_id += i + } else { + m.addapp_id = &i + } +} + +// AddedAppID returns the value that was added to the "app_id" field in this mutation. +func (m *GithubInstallationMutation) AddedAppID() (r int64, exists bool) { + v := m.addapp_id + if v == nil { + return + } + return *v, true +} + +// ResetAppID resets all changes to the "app_id" field. +func (m *GithubInstallationMutation) ResetAppID() { + m.app_id = nil + m.addapp_id = nil +} + +// SetRepositories sets the "repositories" field. +func (m *GithubInstallationMutation) SetRepositories(s string) { + m.repositories = &s +} + +// Repositories returns the value of the "repositories" field in the mutation. +func (m *GithubInstallationMutation) Repositories() (r string, exists bool) { + v := m.repositories + if v == nil { + return + } + return *v, true +} + +// OldRepositories returns the old "repositories" field's value of the GithubInstallation entity. +// If the GithubInstallation object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *GithubInstallationMutation) OldRepositories(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldRepositories is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldRepositories requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldRepositories: %w", err) + } + return oldValue.Repositories, nil +} + +// ResetRepositories resets all changes to the "repositories" field. +func (m *GithubInstallationMutation) ResetRepositories() { + m.repositories = nil +} + +// SetStatus sets the "status" field. +func (m *GithubInstallationMutation) SetStatus(s string) { + m.status = &s +} + +// Status returns the value of the "status" field in the mutation. +func (m *GithubInstallationMutation) Status() (r string, exists bool) { + v := m.status + if v == nil { + return + } + return *v, true +} + +// OldStatus returns the old "status" field's value of the GithubInstallation entity. +// If the GithubInstallation object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *GithubInstallationMutation) OldStatus(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldStatus is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldStatus requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldStatus: %w", err) + } + return oldValue.Status, nil +} + +// ResetStatus resets all changes to the "status" field. +func (m *GithubInstallationMutation) ResetStatus() { + m.status = nil +} + +// SetCreated sets the "created" field. +func (m *GithubInstallationMutation) SetCreated(t time.Time) { + m.created = &t +} + +// Created returns the value of the "created" field in the mutation. +func (m *GithubInstallationMutation) Created() (r time.Time, exists bool) { + v := m.created + if v == nil { + return + } + return *v, true +} + +// OldCreated returns the old "created" field's value of the GithubInstallation entity. +// If the GithubInstallation object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *GithubInstallationMutation) OldCreated(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreated is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreated requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreated: %w", err) + } + return oldValue.Created, nil +} + +// ResetCreated resets all changes to the "created" field. +func (m *GithubInstallationMutation) ResetCreated() { + m.created = nil +} + +// SetUpdated sets the "updated" field. +func (m *GithubInstallationMutation) SetUpdated(t time.Time) { + m.updated = &t +} + +// Updated returns the value of the "updated" field in the mutation. +func (m *GithubInstallationMutation) Updated() (r time.Time, exists bool) { + v := m.updated + if v == nil { + return + } + return *v, true +} + +// OldUpdated returns the old "updated" field's value of the GithubInstallation entity. +// If the GithubInstallation object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *GithubInstallationMutation) OldUpdated(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldUpdated is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldUpdated requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldUpdated: %w", err) + } + return oldValue.Updated, nil +} + +// ResetUpdated resets all changes to the "updated" field. +func (m *GithubInstallationMutation) ResetUpdated() { + m.updated = nil +} + +// Where appends a list predicates to the GithubInstallationMutation builder. +func (m *GithubInstallationMutation) Where(ps ...predicate.GithubInstallation) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the GithubInstallationMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *GithubInstallationMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.GithubInstallation, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *GithubInstallationMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *GithubInstallationMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (GithubInstallation). +func (m *GithubInstallationMutation) Type() string { + return m.typ +} + +// Fields returns all fields that were changed during this mutation. Note that in +// order to get all numeric fields that were incremented/decremented, call +// AddedFields(). +func (m *GithubInstallationMutation) Fields() []string { + fields := make([]string, 0, 7) + if m.account_login != nil { + fields = append(fields, githubinstallation.FieldAccountLogin) + } + if m.account_type != nil { + fields = append(fields, githubinstallation.FieldAccountType) + } + if m.app_id != nil { + fields = append(fields, githubinstallation.FieldAppID) + } + if m.repositories != nil { + fields = append(fields, githubinstallation.FieldRepositories) + } + if m.status != nil { + fields = append(fields, githubinstallation.FieldStatus) + } + if m.created != nil { + fields = append(fields, githubinstallation.FieldCreated) + } + if m.updated != nil { + fields = append(fields, githubinstallation.FieldUpdated) + } + return fields +} + +// Field returns the value of a field with the given name. The second boolean +// return value indicates that this field was not set, or was not defined in the +// schema. +func (m *GithubInstallationMutation) Field(name string) (ent.Value, bool) { + switch name { + case githubinstallation.FieldAccountLogin: + return m.AccountLogin() + case githubinstallation.FieldAccountType: + return m.AccountType() + case githubinstallation.FieldAppID: + return m.AppID() + case githubinstallation.FieldRepositories: + return m.Repositories() + case githubinstallation.FieldStatus: + return m.Status() + case githubinstallation.FieldCreated: + return m.Created() + case githubinstallation.FieldUpdated: + return m.Updated() + } + return nil, false +} + +// OldField returns the old value of the field from the database. An error is +// returned if the mutation operation is not UpdateOne, or the query to the +// database failed. +func (m *GithubInstallationMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + switch name { + case githubinstallation.FieldAccountLogin: + return m.OldAccountLogin(ctx) + case githubinstallation.FieldAccountType: + return m.OldAccountType(ctx) + case githubinstallation.FieldAppID: + return m.OldAppID(ctx) + case githubinstallation.FieldRepositories: + return m.OldRepositories(ctx) + case githubinstallation.FieldStatus: + return m.OldStatus(ctx) + case githubinstallation.FieldCreated: + return m.OldCreated(ctx) + case githubinstallation.FieldUpdated: + return m.OldUpdated(ctx) + } + return nil, fmt.Errorf("unknown GithubInstallation field %s", name) +} + +// SetField sets the value of a field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *GithubInstallationMutation) SetField(name string, value ent.Value) error { + switch name { + case githubinstallation.FieldAccountLogin: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetAccountLogin(v) + return nil + case githubinstallation.FieldAccountType: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetAccountType(v) + return nil + case githubinstallation.FieldAppID: + v, ok := value.(int64) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetAppID(v) + return nil + case githubinstallation.FieldRepositories: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetRepositories(v) + return nil + case githubinstallation.FieldStatus: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetStatus(v) + return nil + case githubinstallation.FieldCreated: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreated(v) + return nil + case githubinstallation.FieldUpdated: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetUpdated(v) + return nil + } + return fmt.Errorf("unknown GithubInstallation field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *GithubInstallationMutation) AddedFields() []string { + var fields []string + if m.addapp_id != nil { + fields = append(fields, githubinstallation.FieldAppID) + } + return fields +} + +// AddedField returns the numeric value that was incremented/decremented on a field +// with the given name. The second boolean return value indicates that this field +// was not set, or was not defined in the schema. +func (m *GithubInstallationMutation) AddedField(name string) (ent.Value, bool) { + switch name { + case githubinstallation.FieldAppID: + return m.AddedAppID() + } + return nil, false +} + +// AddField adds the value to the field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *GithubInstallationMutation) AddField(name string, value ent.Value) error { + switch name { + case githubinstallation.FieldAppID: + v, ok := value.(int64) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.AddAppID(v) + return nil + } + return fmt.Errorf("unknown GithubInstallation numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *GithubInstallationMutation) ClearedFields() []string { + return nil +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *GithubInstallationMutation) FieldCleared(name string) bool { + _, ok := m.clearedFields[name] + return ok +} + +// ClearField clears the value of the field with the given name. It returns an +// error if the field is not defined in the schema. +func (m *GithubInstallationMutation) ClearField(name string) error { + return fmt.Errorf("unknown GithubInstallation nullable field %s", name) +} + +// ResetField resets all changes in the mutation for the field with the given name. +// It returns an error if the field is not defined in the schema. +func (m *GithubInstallationMutation) ResetField(name string) error { + switch name { + case githubinstallation.FieldAccountLogin: + m.ResetAccountLogin() + return nil + case githubinstallation.FieldAccountType: + m.ResetAccountType() + return nil + case githubinstallation.FieldAppID: + m.ResetAppID() + return nil + case githubinstallation.FieldRepositories: + m.ResetRepositories() + return nil + case githubinstallation.FieldStatus: + m.ResetStatus() + return nil + case githubinstallation.FieldCreated: + m.ResetCreated() + return nil + case githubinstallation.FieldUpdated: + m.ResetUpdated() + return nil + } + return fmt.Errorf("unknown GithubInstallation field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *GithubInstallationMutation) AddedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *GithubInstallationMutation) AddedIDs(name string) []ent.Value { + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *GithubInstallationMutation) RemovedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *GithubInstallationMutation) RemovedIDs(name string) []ent.Value { + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *GithubInstallationMutation) ClearedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *GithubInstallationMutation) EdgeCleared(name string) bool { + return false +} + +// ClearEdge clears the value of the edge with the given name. It returns an error +// if that edge is not defined in the schema. +func (m *GithubInstallationMutation) ClearEdge(name string) error { + return fmt.Errorf("unknown GithubInstallation unique edge %s", name) +} + +// ResetEdge resets all changes to the edge with the given name in this mutation. +// It returns an error if the edge is not defined in the schema. +func (m *GithubInstallationMutation) ResetEdge(name string) error { + return fmt.Errorf("unknown GithubInstallation edge %s", name) +} + +// GroupMutation represents an operation that mutates the Group nodes in the graph. +type GroupMutation struct { + config + op Op + typ string + id *uuid.UUID + name *string + slug *string + description *string + group_type *group.GroupType + project_id *uuid.UUID + labels *map[string]string + annotations *map[string]string + created *time.Time + updated *time.Time + created_by *string + clearedFields map[string]struct{} + memberships map[uuid.UUID]struct{} + removedmemberships map[uuid.UUID]struct{} + clearedmemberships bool + parent_groups map[uuid.UUID]struct{} + removedparent_groups map[uuid.UUID]struct{} + clearedparent_groups bool + child_groups map[uuid.UUID]struct{} + removedchild_groups map[uuid.UUID]struct{} + clearedchild_groups bool + owner *uuid.UUID + clearedowner bool + policy_bindings map[uuid.UUID]struct{} + removedpolicy_bindings map[uuid.UUID]struct{} + clearedpolicy_bindings bool + done bool + oldValue func(context.Context) (*Group, error) + predicates []predicate.Group +} + +var _ ent.Mutation = (*GroupMutation)(nil) + +// groupOption allows management of the mutation configuration using functional options. +type groupOption func(*GroupMutation) + +// newGroupMutation creates new mutation for the Group entity. +func newGroupMutation(c config, op Op, opts ...groupOption) *GroupMutation { + m := &GroupMutation{ + config: c, + op: op, + typ: TypeGroup, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// withGroupID sets the ID field of the mutation. +func withGroupID(id uuid.UUID) groupOption { + return func(m *GroupMutation) { + var ( + err error + once sync.Once + value *Group + ) + m.oldValue = func(ctx context.Context) (*Group, error) { + once.Do(func() { + if m.done { + err = errors.New("querying old values post mutation is not allowed") + } else { + value, err = m.Client().Group.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } +} + +// withGroup sets the old Group of the mutation. +func withGroup(node *Group) groupOption { + return func(m *GroupMutation) { + m.oldValue = func(context.Context) (*Group, error) { + return node, nil + } + m.id = &node.ID + } +} + +// Client returns a new `ent.Client` from the mutation. If the mutation was +// executed in a transaction (ent.Tx), a transactional client is returned. +func (m GroupMutation) Client() *Client { + client := &Client{config: m.config} + client.init() + return client +} + +// Tx returns an `ent.Tx` for mutations that were executed in transactions; +// it returns an error otherwise. +func (m GroupMutation) Tx() (*Tx, error) { + if _, ok := m.driver.(*txDriver); !ok { + return nil, errors.New("ent: mutation is not running in a transaction") + } + tx := &Tx{config: m.config} + tx.init() + return tx, nil +} + +// SetID sets the value of the id field. Note that this +// operation is only accepted on creation of Group entities. +func (m *GroupMutation) SetID(id uuid.UUID) { + m.id = &id +} + +// ID returns the ID value in the mutation. Note that the ID is only available +// if it was provided to the builder or after it was returned from the database. +func (m *GroupMutation) ID() (id uuid.UUID, exists bool) { + if m.id == nil { + return + } + return *m.id, true +} + +// IDs queries the database and returns the entity ids that match the mutation's predicate. +// That means, if the mutation is applied within a transaction with an isolation level such +// as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated +// or updated by the mutation. +func (m *GroupMutation) IDs(ctx context.Context) ([]uuid.UUID, error) { + switch { + case m.op.Is(OpUpdateOne | OpDeleteOne): + id, exists := m.ID() + if exists { + return []uuid.UUID{id}, nil + } + fallthrough + case m.op.Is(OpUpdate | OpDelete): + return m.Client().Group.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } +} + +// SetName sets the "name" field. +func (m *GroupMutation) SetName(s string) { + m.name = &s +} + +// Name returns the value of the "name" field in the mutation. +func (m *GroupMutation) Name() (r string, exists bool) { + v := m.name + if v == nil { + return + } + return *v, true +} + +// OldName returns the old "name" field's value of the Group entity. +// If the Group object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *GroupMutation) OldName(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldName is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldName requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldName: %w", err) + } + return oldValue.Name, nil +} + +// ResetName resets all changes to the "name" field. +func (m *GroupMutation) ResetName() { + m.name = nil +} + +// SetSlug sets the "slug" field. +func (m *GroupMutation) SetSlug(s string) { + m.slug = &s +} + +// Slug returns the value of the "slug" field in the mutation. +func (m *GroupMutation) Slug() (r string, exists bool) { + v := m.slug + if v == nil { + return + } + return *v, true +} + +// OldSlug returns the old "slug" field's value of the Group entity. +// If the Group object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *GroupMutation) OldSlug(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldSlug is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldSlug requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldSlug: %w", err) + } + return oldValue.Slug, nil +} + +// ResetSlug resets all changes to the "slug" field. +func (m *GroupMutation) ResetSlug() { + m.slug = nil +} + +// SetDescription sets the "description" field. +func (m *GroupMutation) SetDescription(s string) { + m.description = &s +} + +// Description returns the value of the "description" field in the mutation. +func (m *GroupMutation) Description() (r string, exists bool) { + v := m.description + if v == nil { + return + } + return *v, true +} + +// OldDescription returns the old "description" field's value of the Group entity. +// If the Group object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *GroupMutation) OldDescription(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldDescription is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldDescription requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldDescription: %w", err) + } + return oldValue.Description, nil +} + +// ClearDescription clears the value of the "description" field. +func (m *GroupMutation) ClearDescription() { + m.description = nil + m.clearedFields[group.FieldDescription] = struct{}{} +} + +// DescriptionCleared returns if the "description" field was cleared in this mutation. +func (m *GroupMutation) DescriptionCleared() bool { + _, ok := m.clearedFields[group.FieldDescription] + return ok +} + +// ResetDescription resets all changes to the "description" field. +func (m *GroupMutation) ResetDescription() { + m.description = nil + delete(m.clearedFields, group.FieldDescription) +} + +// SetGroupType sets the "group_type" field. +func (m *GroupMutation) SetGroupType(gt group.GroupType) { + m.group_type = > +} + +// GroupType returns the value of the "group_type" field in the mutation. +func (m *GroupMutation) GroupType() (r group.GroupType, exists bool) { + v := m.group_type + if v == nil { + return + } + return *v, true +} + +// OldGroupType returns the old "group_type" field's value of the Group entity. +// If the Group object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *GroupMutation) OldGroupType(ctx context.Context) (v group.GroupType, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldGroupType is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldGroupType requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldGroupType: %w", err) + } + return oldValue.GroupType, nil +} + +// ResetGroupType resets all changes to the "group_type" field. +func (m *GroupMutation) ResetGroupType() { + m.group_type = nil +} + +// SetProjectID sets the "project_id" field. +func (m *GroupMutation) SetProjectID(u uuid.UUID) { + m.project_id = &u +} + +// ProjectID returns the value of the "project_id" field in the mutation. +func (m *GroupMutation) ProjectID() (r uuid.UUID, exists bool) { + v := m.project_id + if v == nil { + return + } + return *v, true +} + +// OldProjectID returns the old "project_id" field's value of the Group entity. +// If the Group object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *GroupMutation) OldProjectID(ctx context.Context) (v *uuid.UUID, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldProjectID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldProjectID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldProjectID: %w", err) + } + return oldValue.ProjectID, nil +} + +// ClearProjectID clears the value of the "project_id" field. +func (m *GroupMutation) ClearProjectID() { + m.project_id = nil + m.clearedFields[group.FieldProjectID] = struct{}{} +} + +// ProjectIDCleared returns if the "project_id" field was cleared in this mutation. +func (m *GroupMutation) ProjectIDCleared() bool { + _, ok := m.clearedFields[group.FieldProjectID] + return ok +} + +// ResetProjectID resets all changes to the "project_id" field. +func (m *GroupMutation) ResetProjectID() { + m.project_id = nil + delete(m.clearedFields, group.FieldProjectID) +} + +// SetLabels sets the "labels" field. +func (m *GroupMutation) SetLabels(value map[string]string) { + m.labels = &value +} + +// Labels returns the value of the "labels" field in the mutation. +func (m *GroupMutation) Labels() (r map[string]string, exists bool) { + v := m.labels + if v == nil { + return + } + return *v, true +} + +// OldLabels returns the old "labels" field's value of the Group entity. +// If the Group object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *GroupMutation) OldLabels(ctx context.Context) (v map[string]string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldLabels is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldLabels requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldLabels: %w", err) + } + return oldValue.Labels, nil +} + +// ClearLabels clears the value of the "labels" field. +func (m *GroupMutation) ClearLabels() { + m.labels = nil + m.clearedFields[group.FieldLabels] = struct{}{} +} + +// LabelsCleared returns if the "labels" field was cleared in this mutation. +func (m *GroupMutation) LabelsCleared() bool { + _, ok := m.clearedFields[group.FieldLabels] + return ok +} + +// ResetLabels resets all changes to the "labels" field. +func (m *GroupMutation) ResetLabels() { + m.labels = nil + delete(m.clearedFields, group.FieldLabels) +} + +// SetAnnotations sets the "annotations" field. +func (m *GroupMutation) SetAnnotations(value map[string]string) { + m.annotations = &value +} + +// Annotations returns the value of the "annotations" field in the mutation. +func (m *GroupMutation) Annotations() (r map[string]string, exists bool) { + v := m.annotations + if v == nil { + return + } + return *v, true +} + +// OldAnnotations returns the old "annotations" field's value of the Group entity. +// If the Group object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *GroupMutation) OldAnnotations(ctx context.Context) (v map[string]string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldAnnotations is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldAnnotations requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldAnnotations: %w", err) + } + return oldValue.Annotations, nil +} + +// ClearAnnotations clears the value of the "annotations" field. +func (m *GroupMutation) ClearAnnotations() { + m.annotations = nil + m.clearedFields[group.FieldAnnotations] = struct{}{} +} + +// AnnotationsCleared returns if the "annotations" field was cleared in this mutation. +func (m *GroupMutation) AnnotationsCleared() bool { + _, ok := m.clearedFields[group.FieldAnnotations] + return ok +} + +// ResetAnnotations resets all changes to the "annotations" field. +func (m *GroupMutation) ResetAnnotations() { + m.annotations = nil + delete(m.clearedFields, group.FieldAnnotations) +} + +// SetCreated sets the "created" field. +func (m *GroupMutation) SetCreated(t time.Time) { + m.created = &t +} + +// Created returns the value of the "created" field in the mutation. +func (m *GroupMutation) Created() (r time.Time, exists bool) { + v := m.created + if v == nil { + return + } + return *v, true +} + +// OldCreated returns the old "created" field's value of the Group entity. +// If the Group object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *GroupMutation) OldCreated(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreated is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreated requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreated: %w", err) + } + return oldValue.Created, nil +} + +// ResetCreated resets all changes to the "created" field. +func (m *GroupMutation) ResetCreated() { + m.created = nil +} + +// SetUpdated sets the "updated" field. +func (m *GroupMutation) SetUpdated(t time.Time) { + m.updated = &t +} + +// Updated returns the value of the "updated" field in the mutation. +func (m *GroupMutation) Updated() (r time.Time, exists bool) { + v := m.updated + if v == nil { + return + } + return *v, true +} + +// OldUpdated returns the old "updated" field's value of the Group entity. +// If the Group object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *GroupMutation) OldUpdated(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldUpdated is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldUpdated requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldUpdated: %w", err) + } + return oldValue.Updated, nil +} + +// ResetUpdated resets all changes to the "updated" field. +func (m *GroupMutation) ResetUpdated() { + m.updated = nil +} + +// SetCreatedBy sets the "created_by" field. +func (m *GroupMutation) SetCreatedBy(s string) { + m.created_by = &s +} + +// CreatedBy returns the value of the "created_by" field in the mutation. +func (m *GroupMutation) CreatedBy() (r string, exists bool) { + v := m.created_by + if v == nil { + return + } + return *v, true +} + +// OldCreatedBy returns the old "created_by" field's value of the Group entity. +// If the Group object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *GroupMutation) OldCreatedBy(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreatedBy is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreatedBy requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreatedBy: %w", err) + } + return oldValue.CreatedBy, nil +} + +// ClearCreatedBy clears the value of the "created_by" field. +func (m *GroupMutation) ClearCreatedBy() { + m.created_by = nil + m.clearedFields[group.FieldCreatedBy] = struct{}{} +} + +// CreatedByCleared returns if the "created_by" field was cleared in this mutation. +func (m *GroupMutation) CreatedByCleared() bool { + _, ok := m.clearedFields[group.FieldCreatedBy] + return ok +} + +// ResetCreatedBy resets all changes to the "created_by" field. +func (m *GroupMutation) ResetCreatedBy() { + m.created_by = nil + delete(m.clearedFields, group.FieldCreatedBy) +} + +// SetOwnerID sets the "owner_id" field. +func (m *GroupMutation) SetOwnerID(u uuid.UUID) { + m.owner = &u +} + +// OwnerID returns the value of the "owner_id" field in the mutation. +func (m *GroupMutation) OwnerID() (r uuid.UUID, exists bool) { + v := m.owner + if v == nil { + return + } + return *v, true +} + +// OldOwnerID returns the old "owner_id" field's value of the Group entity. +// If the Group object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *GroupMutation) OldOwnerID(ctx context.Context) (v *uuid.UUID, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldOwnerID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldOwnerID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldOwnerID: %w", err) + } + return oldValue.OwnerID, nil +} + +// ClearOwnerID clears the value of the "owner_id" field. +func (m *GroupMutation) ClearOwnerID() { + m.owner = nil + m.clearedFields[group.FieldOwnerID] = struct{}{} +} + +// OwnerIDCleared returns if the "owner_id" field was cleared in this mutation. +func (m *GroupMutation) OwnerIDCleared() bool { + _, ok := m.clearedFields[group.FieldOwnerID] + return ok +} + +// ResetOwnerID resets all changes to the "owner_id" field. +func (m *GroupMutation) ResetOwnerID() { + m.owner = nil + delete(m.clearedFields, group.FieldOwnerID) +} + +// AddMembershipIDs adds the "memberships" edge to the GroupMembership entity by ids. +func (m *GroupMutation) AddMembershipIDs(ids ...uuid.UUID) { + if m.memberships == nil { + m.memberships = make(map[uuid.UUID]struct{}) + } + for i := range ids { + m.memberships[ids[i]] = struct{}{} + } +} + +// ClearMemberships clears the "memberships" edge to the GroupMembership entity. +func (m *GroupMutation) ClearMemberships() { + m.clearedmemberships = true +} + +// MembershipsCleared reports if the "memberships" edge to the GroupMembership entity was cleared. +func (m *GroupMutation) MembershipsCleared() bool { + return m.clearedmemberships +} + +// RemoveMembershipIDs removes the "memberships" edge to the GroupMembership entity by IDs. +func (m *GroupMutation) RemoveMembershipIDs(ids ...uuid.UUID) { + if m.removedmemberships == nil { + m.removedmemberships = make(map[uuid.UUID]struct{}) + } + for i := range ids { + delete(m.memberships, ids[i]) + m.removedmemberships[ids[i]] = struct{}{} + } +} + +// RemovedMemberships returns the removed IDs of the "memberships" edge to the GroupMembership entity. +func (m *GroupMutation) RemovedMembershipsIDs() (ids []uuid.UUID) { + for id := range m.removedmemberships { + ids = append(ids, id) + } + return +} + +// MembershipsIDs returns the "memberships" edge IDs in the mutation. +func (m *GroupMutation) MembershipsIDs() (ids []uuid.UUID) { + for id := range m.memberships { + ids = append(ids, id) + } + return +} + +// ResetMemberships resets all changes to the "memberships" edge. +func (m *GroupMutation) ResetMemberships() { + m.memberships = nil + m.clearedmemberships = false + m.removedmemberships = nil +} + +// AddParentGroupIDs adds the "parent_groups" edge to the Group entity by ids. +func (m *GroupMutation) AddParentGroupIDs(ids ...uuid.UUID) { + if m.parent_groups == nil { + m.parent_groups = make(map[uuid.UUID]struct{}) + } + for i := range ids { + m.parent_groups[ids[i]] = struct{}{} + } +} + +// ClearParentGroups clears the "parent_groups" edge to the Group entity. +func (m *GroupMutation) ClearParentGroups() { + m.clearedparent_groups = true +} + +// ParentGroupsCleared reports if the "parent_groups" edge to the Group entity was cleared. +func (m *GroupMutation) ParentGroupsCleared() bool { + return m.clearedparent_groups +} + +// RemoveParentGroupIDs removes the "parent_groups" edge to the Group entity by IDs. +func (m *GroupMutation) RemoveParentGroupIDs(ids ...uuid.UUID) { + if m.removedparent_groups == nil { + m.removedparent_groups = make(map[uuid.UUID]struct{}) + } + for i := range ids { + delete(m.parent_groups, ids[i]) + m.removedparent_groups[ids[i]] = struct{}{} + } +} + +// RemovedParentGroups returns the removed IDs of the "parent_groups" edge to the Group entity. +func (m *GroupMutation) RemovedParentGroupsIDs() (ids []uuid.UUID) { + for id := range m.removedparent_groups { + ids = append(ids, id) + } + return +} + +// ParentGroupsIDs returns the "parent_groups" edge IDs in the mutation. +func (m *GroupMutation) ParentGroupsIDs() (ids []uuid.UUID) { + for id := range m.parent_groups { + ids = append(ids, id) + } + return +} + +// ResetParentGroups resets all changes to the "parent_groups" edge. +func (m *GroupMutation) ResetParentGroups() { + m.parent_groups = nil + m.clearedparent_groups = false + m.removedparent_groups = nil +} + +// AddChildGroupIDs adds the "child_groups" edge to the Group entity by ids. +func (m *GroupMutation) AddChildGroupIDs(ids ...uuid.UUID) { + if m.child_groups == nil { + m.child_groups = make(map[uuid.UUID]struct{}) + } + for i := range ids { + m.child_groups[ids[i]] = struct{}{} + } +} + +// ClearChildGroups clears the "child_groups" edge to the Group entity. +func (m *GroupMutation) ClearChildGroups() { + m.clearedchild_groups = true +} + +// ChildGroupsCleared reports if the "child_groups" edge to the Group entity was cleared. +func (m *GroupMutation) ChildGroupsCleared() bool { + return m.clearedchild_groups +} + +// RemoveChildGroupIDs removes the "child_groups" edge to the Group entity by IDs. +func (m *GroupMutation) RemoveChildGroupIDs(ids ...uuid.UUID) { + if m.removedchild_groups == nil { + m.removedchild_groups = make(map[uuid.UUID]struct{}) + } + for i := range ids { + delete(m.child_groups, ids[i]) + m.removedchild_groups[ids[i]] = struct{}{} + } +} + +// RemovedChildGroups returns the removed IDs of the "child_groups" edge to the Group entity. +func (m *GroupMutation) RemovedChildGroupsIDs() (ids []uuid.UUID) { + for id := range m.removedchild_groups { + ids = append(ids, id) + } + return +} + +// ChildGroupsIDs returns the "child_groups" edge IDs in the mutation. +func (m *GroupMutation) ChildGroupsIDs() (ids []uuid.UUID) { + for id := range m.child_groups { + ids = append(ids, id) + } + return +} + +// ResetChildGroups resets all changes to the "child_groups" edge. +func (m *GroupMutation) ResetChildGroups() { + m.child_groups = nil + m.clearedchild_groups = false + m.removedchild_groups = nil +} + +// ClearOwner clears the "owner" edge to the User entity. +func (m *GroupMutation) ClearOwner() { + m.clearedowner = true + m.clearedFields[group.FieldOwnerID] = struct{}{} +} + +// OwnerCleared reports if the "owner" edge to the User entity was cleared. +func (m *GroupMutation) OwnerCleared() bool { + return m.OwnerIDCleared() || m.clearedowner +} + +// OwnerIDs returns the "owner" edge IDs in the mutation. +// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use +// OwnerID instead. It exists only for internal usage by the builders. +func (m *GroupMutation) OwnerIDs() (ids []uuid.UUID) { + if id := m.owner; id != nil { + ids = append(ids, *id) + } + return +} + +// ResetOwner resets all changes to the "owner" edge. +func (m *GroupMutation) ResetOwner() { + m.owner = nil + m.clearedowner = false +} + +// AddPolicyBindingIDs adds the "policy_bindings" edge to the PolicyBinding entity by ids. +func (m *GroupMutation) AddPolicyBindingIDs(ids ...uuid.UUID) { + if m.policy_bindings == nil { + m.policy_bindings = make(map[uuid.UUID]struct{}) + } + for i := range ids { + m.policy_bindings[ids[i]] = struct{}{} + } +} + +// ClearPolicyBindings clears the "policy_bindings" edge to the PolicyBinding entity. +func (m *GroupMutation) ClearPolicyBindings() { + m.clearedpolicy_bindings = true +} + +// PolicyBindingsCleared reports if the "policy_bindings" edge to the PolicyBinding entity was cleared. +func (m *GroupMutation) PolicyBindingsCleared() bool { + return m.clearedpolicy_bindings +} + +// RemovePolicyBindingIDs removes the "policy_bindings" edge to the PolicyBinding entity by IDs. +func (m *GroupMutation) RemovePolicyBindingIDs(ids ...uuid.UUID) { + if m.removedpolicy_bindings == nil { + m.removedpolicy_bindings = make(map[uuid.UUID]struct{}) + } + for i := range ids { + delete(m.policy_bindings, ids[i]) + m.removedpolicy_bindings[ids[i]] = struct{}{} + } +} + +// RemovedPolicyBindings returns the removed IDs of the "policy_bindings" edge to the PolicyBinding entity. +func (m *GroupMutation) RemovedPolicyBindingsIDs() (ids []uuid.UUID) { + for id := range m.removedpolicy_bindings { + ids = append(ids, id) + } + return +} + +// PolicyBindingsIDs returns the "policy_bindings" edge IDs in the mutation. +func (m *GroupMutation) PolicyBindingsIDs() (ids []uuid.UUID) { + for id := range m.policy_bindings { + ids = append(ids, id) + } + return +} + +// ResetPolicyBindings resets all changes to the "policy_bindings" edge. +func (m *GroupMutation) ResetPolicyBindings() { + m.policy_bindings = nil m.clearedpolicy_bindings = false m.removedpolicy_bindings = nil } -// Where appends a list predicates to the AgentMutation builder. -func (m *AgentMutation) Where(ps ...predicate.Agent) { +// Where appends a list predicates to the GroupMutation builder. +func (m *GroupMutation) Where(ps ...predicate.Group) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the GroupMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *GroupMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.Group, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *GroupMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *GroupMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (Group). +func (m *GroupMutation) Type() string { + return m.typ +} + +// Fields returns all fields that were changed during this mutation. Note that in +// order to get all numeric fields that were incremented/decremented, call +// AddedFields(). +func (m *GroupMutation) Fields() []string { + fields := make([]string, 0, 11) + if m.name != nil { + fields = append(fields, group.FieldName) + } + if m.slug != nil { + fields = append(fields, group.FieldSlug) + } + if m.description != nil { + fields = append(fields, group.FieldDescription) + } + if m.group_type != nil { + fields = append(fields, group.FieldGroupType) + } + if m.project_id != nil { + fields = append(fields, group.FieldProjectID) + } + if m.labels != nil { + fields = append(fields, group.FieldLabels) + } + if m.annotations != nil { + fields = append(fields, group.FieldAnnotations) + } + if m.created != nil { + fields = append(fields, group.FieldCreated) + } + if m.updated != nil { + fields = append(fields, group.FieldUpdated) + } + if m.created_by != nil { + fields = append(fields, group.FieldCreatedBy) + } + if m.owner != nil { + fields = append(fields, group.FieldOwnerID) + } + return fields +} + +// Field returns the value of a field with the given name. The second boolean +// return value indicates that this field was not set, or was not defined in the +// schema. +func (m *GroupMutation) Field(name string) (ent.Value, bool) { + switch name { + case group.FieldName: + return m.Name() + case group.FieldSlug: + return m.Slug() + case group.FieldDescription: + return m.Description() + case group.FieldGroupType: + return m.GroupType() + case group.FieldProjectID: + return m.ProjectID() + case group.FieldLabels: + return m.Labels() + case group.FieldAnnotations: + return m.Annotations() + case group.FieldCreated: + return m.Created() + case group.FieldUpdated: + return m.Updated() + case group.FieldCreatedBy: + return m.CreatedBy() + case group.FieldOwnerID: + return m.OwnerID() + } + return nil, false +} + +// OldField returns the old value of the field from the database. An error is +// returned if the mutation operation is not UpdateOne, or the query to the +// database failed. +func (m *GroupMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + switch name { + case group.FieldName: + return m.OldName(ctx) + case group.FieldSlug: + return m.OldSlug(ctx) + case group.FieldDescription: + return m.OldDescription(ctx) + case group.FieldGroupType: + return m.OldGroupType(ctx) + case group.FieldProjectID: + return m.OldProjectID(ctx) + case group.FieldLabels: + return m.OldLabels(ctx) + case group.FieldAnnotations: + return m.OldAnnotations(ctx) + case group.FieldCreated: + return m.OldCreated(ctx) + case group.FieldUpdated: + return m.OldUpdated(ctx) + case group.FieldCreatedBy: + return m.OldCreatedBy(ctx) + case group.FieldOwnerID: + return m.OldOwnerID(ctx) + } + return nil, fmt.Errorf("unknown Group field %s", name) +} + +// SetField sets the value of a field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *GroupMutation) SetField(name string, value ent.Value) error { + switch name { + case group.FieldName: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetName(v) + return nil + case group.FieldSlug: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetSlug(v) + return nil + case group.FieldDescription: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetDescription(v) + return nil + case group.FieldGroupType: + v, ok := value.(group.GroupType) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetGroupType(v) + return nil + case group.FieldProjectID: + v, ok := value.(uuid.UUID) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetProjectID(v) + return nil + case group.FieldLabels: + v, ok := value.(map[string]string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetLabels(v) + return nil + case group.FieldAnnotations: + v, ok := value.(map[string]string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetAnnotations(v) + return nil + case group.FieldCreated: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreated(v) + return nil + case group.FieldUpdated: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetUpdated(v) + return nil + case group.FieldCreatedBy: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreatedBy(v) + return nil + case group.FieldOwnerID: + v, ok := value.(uuid.UUID) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetOwnerID(v) + return nil + } + return fmt.Errorf("unknown Group field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *GroupMutation) AddedFields() []string { + return nil +} + +// AddedField returns the numeric value that was incremented/decremented on a field +// with the given name. The second boolean return value indicates that this field +// was not set, or was not defined in the schema. +func (m *GroupMutation) AddedField(name string) (ent.Value, bool) { + return nil, false +} + +// AddField adds the value to the field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *GroupMutation) AddField(name string, value ent.Value) error { + switch name { + } + return fmt.Errorf("unknown Group numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *GroupMutation) ClearedFields() []string { + var fields []string + if m.FieldCleared(group.FieldDescription) { + fields = append(fields, group.FieldDescription) + } + if m.FieldCleared(group.FieldProjectID) { + fields = append(fields, group.FieldProjectID) + } + if m.FieldCleared(group.FieldLabels) { + fields = append(fields, group.FieldLabels) + } + if m.FieldCleared(group.FieldAnnotations) { + fields = append(fields, group.FieldAnnotations) + } + if m.FieldCleared(group.FieldCreatedBy) { + fields = append(fields, group.FieldCreatedBy) + } + if m.FieldCleared(group.FieldOwnerID) { + fields = append(fields, group.FieldOwnerID) + } + return fields +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *GroupMutation) FieldCleared(name string) bool { + _, ok := m.clearedFields[name] + return ok +} + +// ClearField clears the value of the field with the given name. It returns an +// error if the field is not defined in the schema. +func (m *GroupMutation) ClearField(name string) error { + switch name { + case group.FieldDescription: + m.ClearDescription() + return nil + case group.FieldProjectID: + m.ClearProjectID() + return nil + case group.FieldLabels: + m.ClearLabels() + return nil + case group.FieldAnnotations: + m.ClearAnnotations() + return nil + case group.FieldCreatedBy: + m.ClearCreatedBy() + return nil + case group.FieldOwnerID: + m.ClearOwnerID() + return nil + } + return fmt.Errorf("unknown Group nullable field %s", name) +} + +// ResetField resets all changes in the mutation for the field with the given name. +// It returns an error if the field is not defined in the schema. +func (m *GroupMutation) ResetField(name string) error { + switch name { + case group.FieldName: + m.ResetName() + return nil + case group.FieldSlug: + m.ResetSlug() + return nil + case group.FieldDescription: + m.ResetDescription() + return nil + case group.FieldGroupType: + m.ResetGroupType() + return nil + case group.FieldProjectID: + m.ResetProjectID() + return nil + case group.FieldLabels: + m.ResetLabels() + return nil + case group.FieldAnnotations: + m.ResetAnnotations() + return nil + case group.FieldCreated: + m.ResetCreated() + return nil + case group.FieldUpdated: + m.ResetUpdated() + return nil + case group.FieldCreatedBy: + m.ResetCreatedBy() + return nil + case group.FieldOwnerID: + m.ResetOwnerID() + return nil + } + return fmt.Errorf("unknown Group field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *GroupMutation) AddedEdges() []string { + edges := make([]string, 0, 5) + if m.memberships != nil { + edges = append(edges, group.EdgeMemberships) + } + if m.parent_groups != nil { + edges = append(edges, group.EdgeParentGroups) + } + if m.child_groups != nil { + edges = append(edges, group.EdgeChildGroups) + } + if m.owner != nil { + edges = append(edges, group.EdgeOwner) + } + if m.policy_bindings != nil { + edges = append(edges, group.EdgePolicyBindings) + } + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *GroupMutation) AddedIDs(name string) []ent.Value { + switch name { + case group.EdgeMemberships: + ids := make([]ent.Value, 0, len(m.memberships)) + for id := range m.memberships { + ids = append(ids, id) + } + return ids + case group.EdgeParentGroups: + ids := make([]ent.Value, 0, len(m.parent_groups)) + for id := range m.parent_groups { + ids = append(ids, id) + } + return ids + case group.EdgeChildGroups: + ids := make([]ent.Value, 0, len(m.child_groups)) + for id := range m.child_groups { + ids = append(ids, id) + } + return ids + case group.EdgeOwner: + if id := m.owner; id != nil { + return []ent.Value{*id} + } + case group.EdgePolicyBindings: + ids := make([]ent.Value, 0, len(m.policy_bindings)) + for id := range m.policy_bindings { + ids = append(ids, id) + } + return ids + } + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *GroupMutation) RemovedEdges() []string { + edges := make([]string, 0, 5) + if m.removedmemberships != nil { + edges = append(edges, group.EdgeMemberships) + } + if m.removedparent_groups != nil { + edges = append(edges, group.EdgeParentGroups) + } + if m.removedchild_groups != nil { + edges = append(edges, group.EdgeChildGroups) + } + if m.removedpolicy_bindings != nil { + edges = append(edges, group.EdgePolicyBindings) + } + return edges +} + +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *GroupMutation) RemovedIDs(name string) []ent.Value { + switch name { + case group.EdgeMemberships: + ids := make([]ent.Value, 0, len(m.removedmemberships)) + for id := range m.removedmemberships { + ids = append(ids, id) + } + return ids + case group.EdgeParentGroups: + ids := make([]ent.Value, 0, len(m.removedparent_groups)) + for id := range m.removedparent_groups { + ids = append(ids, id) + } + return ids + case group.EdgeChildGroups: + ids := make([]ent.Value, 0, len(m.removedchild_groups)) + for id := range m.removedchild_groups { + ids = append(ids, id) + } + return ids + case group.EdgePolicyBindings: + ids := make([]ent.Value, 0, len(m.removedpolicy_bindings)) + for id := range m.removedpolicy_bindings { + ids = append(ids, id) + } + return ids + } + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *GroupMutation) ClearedEdges() []string { + edges := make([]string, 0, 5) + if m.clearedmemberships { + edges = append(edges, group.EdgeMemberships) + } + if m.clearedparent_groups { + edges = append(edges, group.EdgeParentGroups) + } + if m.clearedchild_groups { + edges = append(edges, group.EdgeChildGroups) + } + if m.clearedowner { + edges = append(edges, group.EdgeOwner) + } + if m.clearedpolicy_bindings { + edges = append(edges, group.EdgePolicyBindings) + } + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *GroupMutation) EdgeCleared(name string) bool { + switch name { + case group.EdgeMemberships: + return m.clearedmemberships + case group.EdgeParentGroups: + return m.clearedparent_groups + case group.EdgeChildGroups: + return m.clearedchild_groups + case group.EdgeOwner: + return m.clearedowner + case group.EdgePolicyBindings: + return m.clearedpolicy_bindings + } + return false +} + +// ClearEdge clears the value of the edge with the given name. It returns an error +// if that edge is not defined in the schema. +func (m *GroupMutation) ClearEdge(name string) error { + switch name { + case group.EdgeOwner: + m.ClearOwner() + return nil + } + return fmt.Errorf("unknown Group unique edge %s", name) +} + +// ResetEdge resets all changes to the edge with the given name in this mutation. +// It returns an error if the edge is not defined in the schema. +func (m *GroupMutation) ResetEdge(name string) error { + switch name { + case group.EdgeMemberships: + m.ResetMemberships() + return nil + case group.EdgeParentGroups: + m.ResetParentGroups() + return nil + case group.EdgeChildGroups: + m.ResetChildGroups() + return nil + case group.EdgeOwner: + m.ResetOwner() + return nil + case group.EdgePolicyBindings: + m.ResetPolicyBindings() + return nil + } + return fmt.Errorf("unknown Group edge %s", name) +} + +// GroupMembershipMutation represents an operation that mutates the GroupMembership nodes in the graph. +type GroupMembershipMutation struct { + config + op Op + typ string + id *uuid.UUID + role *groupmembership.Role + added_by *string + added_at *time.Time + clearedFields map[string]struct{} + group *uuid.UUID + clearedgroup bool + user *uuid.UUID + cleareduser bool + agent *uuid.UUID + clearedagent bool + done bool + oldValue func(context.Context) (*GroupMembership, error) + predicates []predicate.GroupMembership +} + +var _ ent.Mutation = (*GroupMembershipMutation)(nil) + +// groupmembershipOption allows management of the mutation configuration using functional options. +type groupmembershipOption func(*GroupMembershipMutation) + +// newGroupMembershipMutation creates new mutation for the GroupMembership entity. +func newGroupMembershipMutation(c config, op Op, opts ...groupmembershipOption) *GroupMembershipMutation { + m := &GroupMembershipMutation{ + config: c, + op: op, + typ: TypeGroupMembership, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// withGroupMembershipID sets the ID field of the mutation. +func withGroupMembershipID(id uuid.UUID) groupmembershipOption { + return func(m *GroupMembershipMutation) { + var ( + err error + once sync.Once + value *GroupMembership + ) + m.oldValue = func(ctx context.Context) (*GroupMembership, error) { + once.Do(func() { + if m.done { + err = errors.New("querying old values post mutation is not allowed") + } else { + value, err = m.Client().GroupMembership.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } +} + +// withGroupMembership sets the old GroupMembership of the mutation. +func withGroupMembership(node *GroupMembership) groupmembershipOption { + return func(m *GroupMembershipMutation) { + m.oldValue = func(context.Context) (*GroupMembership, error) { + return node, nil + } + m.id = &node.ID + } +} + +// Client returns a new `ent.Client` from the mutation. If the mutation was +// executed in a transaction (ent.Tx), a transactional client is returned. +func (m GroupMembershipMutation) Client() *Client { + client := &Client{config: m.config} + client.init() + return client +} + +// Tx returns an `ent.Tx` for mutations that were executed in transactions; +// it returns an error otherwise. +func (m GroupMembershipMutation) Tx() (*Tx, error) { + if _, ok := m.driver.(*txDriver); !ok { + return nil, errors.New("ent: mutation is not running in a transaction") + } + tx := &Tx{config: m.config} + tx.init() + return tx, nil +} + +// SetID sets the value of the id field. Note that this +// operation is only accepted on creation of GroupMembership entities. +func (m *GroupMembershipMutation) SetID(id uuid.UUID) { + m.id = &id +} + +// ID returns the ID value in the mutation. Note that the ID is only available +// if it was provided to the builder or after it was returned from the database. +func (m *GroupMembershipMutation) ID() (id uuid.UUID, exists bool) { + if m.id == nil { + return + } + return *m.id, true +} + +// IDs queries the database and returns the entity ids that match the mutation's predicate. +// That means, if the mutation is applied within a transaction with an isolation level such +// as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated +// or updated by the mutation. +func (m *GroupMembershipMutation) IDs(ctx context.Context) ([]uuid.UUID, error) { + switch { + case m.op.Is(OpUpdateOne | OpDeleteOne): + id, exists := m.ID() + if exists { + return []uuid.UUID{id}, nil + } + fallthrough + case m.op.Is(OpUpdate | OpDelete): + return m.Client().GroupMembership.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } +} + +// SetRole sets the "role" field. +func (m *GroupMembershipMutation) SetRole(gr groupmembership.Role) { + m.role = &gr +} + +// Role returns the value of the "role" field in the mutation. +func (m *GroupMembershipMutation) Role() (r groupmembership.Role, exists bool) { + v := m.role + if v == nil { + return + } + return *v, true +} + +// OldRole returns the old "role" field's value of the GroupMembership entity. +// If the GroupMembership object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *GroupMembershipMutation) OldRole(ctx context.Context) (v groupmembership.Role, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldRole is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldRole requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldRole: %w", err) + } + return oldValue.Role, nil +} + +// ResetRole resets all changes to the "role" field. +func (m *GroupMembershipMutation) ResetRole() { + m.role = nil +} + +// SetAddedBy sets the "added_by" field. +func (m *GroupMembershipMutation) SetAddedBy(s string) { + m.added_by = &s +} + +// AddedBy returns the value of the "added_by" field in the mutation. +func (m *GroupMembershipMutation) AddedBy() (r string, exists bool) { + v := m.added_by + if v == nil { + return + } + return *v, true +} + +// OldAddedBy returns the old "added_by" field's value of the GroupMembership entity. +// If the GroupMembership object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *GroupMembershipMutation) OldAddedBy(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldAddedBy is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldAddedBy requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldAddedBy: %w", err) + } + return oldValue.AddedBy, nil +} + +// ClearAddedBy clears the value of the "added_by" field. +func (m *GroupMembershipMutation) ClearAddedBy() { + m.added_by = nil + m.clearedFields[groupmembership.FieldAddedBy] = struct{}{} +} + +// AddedByCleared returns if the "added_by" field was cleared in this mutation. +func (m *GroupMembershipMutation) AddedByCleared() bool { + _, ok := m.clearedFields[groupmembership.FieldAddedBy] + return ok +} + +// ResetAddedBy resets all changes to the "added_by" field. +func (m *GroupMembershipMutation) ResetAddedBy() { + m.added_by = nil + delete(m.clearedFields, groupmembership.FieldAddedBy) +} + +// SetAddedAt sets the "added_at" field. +func (m *GroupMembershipMutation) SetAddedAt(t time.Time) { + m.added_at = &t +} + +// AddedAt returns the value of the "added_at" field in the mutation. +func (m *GroupMembershipMutation) AddedAt() (r time.Time, exists bool) { + v := m.added_at + if v == nil { + return + } + return *v, true +} + +// OldAddedAt returns the old "added_at" field's value of the GroupMembership entity. +// If the GroupMembership object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *GroupMembershipMutation) OldAddedAt(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldAddedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldAddedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldAddedAt: %w", err) + } + return oldValue.AddedAt, nil +} + +// ResetAddedAt resets all changes to the "added_at" field. +func (m *GroupMembershipMutation) ResetAddedAt() { + m.added_at = nil +} + +// SetGroupID sets the "group_id" field. +func (m *GroupMembershipMutation) SetGroupID(u uuid.UUID) { + m.group = &u +} + +// GroupID returns the value of the "group_id" field in the mutation. +func (m *GroupMembershipMutation) GroupID() (r uuid.UUID, exists bool) { + v := m.group + if v == nil { + return + } + return *v, true +} + +// OldGroupID returns the old "group_id" field's value of the GroupMembership entity. +// If the GroupMembership object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *GroupMembershipMutation) OldGroupID(ctx context.Context) (v uuid.UUID, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldGroupID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldGroupID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldGroupID: %w", err) + } + return oldValue.GroupID, nil +} + +// ResetGroupID resets all changes to the "group_id" field. +func (m *GroupMembershipMutation) ResetGroupID() { + m.group = nil +} + +// SetUserID sets the "user_id" field. +func (m *GroupMembershipMutation) SetUserID(u uuid.UUID) { + m.user = &u +} + +// UserID returns the value of the "user_id" field in the mutation. +func (m *GroupMembershipMutation) UserID() (r uuid.UUID, exists bool) { + v := m.user + if v == nil { + return + } + return *v, true +} + +// OldUserID returns the old "user_id" field's value of the GroupMembership entity. +// If the GroupMembership object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *GroupMembershipMutation) OldUserID(ctx context.Context) (v *uuid.UUID, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldUserID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldUserID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldUserID: %w", err) + } + return oldValue.UserID, nil +} + +// ClearUserID clears the value of the "user_id" field. +func (m *GroupMembershipMutation) ClearUserID() { + m.user = nil + m.clearedFields[groupmembership.FieldUserID] = struct{}{} +} + +// UserIDCleared returns if the "user_id" field was cleared in this mutation. +func (m *GroupMembershipMutation) UserIDCleared() bool { + _, ok := m.clearedFields[groupmembership.FieldUserID] + return ok +} + +// ResetUserID resets all changes to the "user_id" field. +func (m *GroupMembershipMutation) ResetUserID() { + m.user = nil + delete(m.clearedFields, groupmembership.FieldUserID) +} + +// SetAgentID sets the "agent_id" field. +func (m *GroupMembershipMutation) SetAgentID(u uuid.UUID) { + m.agent = &u +} + +// AgentID returns the value of the "agent_id" field in the mutation. +func (m *GroupMembershipMutation) AgentID() (r uuid.UUID, exists bool) { + v := m.agent + if v == nil { + return + } + return *v, true +} + +// OldAgentID returns the old "agent_id" field's value of the GroupMembership entity. +// If the GroupMembership object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *GroupMembershipMutation) OldAgentID(ctx context.Context) (v *uuid.UUID, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldAgentID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldAgentID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldAgentID: %w", err) + } + return oldValue.AgentID, nil +} + +// ClearAgentID clears the value of the "agent_id" field. +func (m *GroupMembershipMutation) ClearAgentID() { + m.agent = nil + m.clearedFields[groupmembership.FieldAgentID] = struct{}{} +} + +// AgentIDCleared returns if the "agent_id" field was cleared in this mutation. +func (m *GroupMembershipMutation) AgentIDCleared() bool { + _, ok := m.clearedFields[groupmembership.FieldAgentID] + return ok +} + +// ResetAgentID resets all changes to the "agent_id" field. +func (m *GroupMembershipMutation) ResetAgentID() { + m.agent = nil + delete(m.clearedFields, groupmembership.FieldAgentID) +} + +// ClearGroup clears the "group" edge to the Group entity. +func (m *GroupMembershipMutation) ClearGroup() { + m.clearedgroup = true + m.clearedFields[groupmembership.FieldGroupID] = struct{}{} +} + +// GroupCleared reports if the "group" edge to the Group entity was cleared. +func (m *GroupMembershipMutation) GroupCleared() bool { + return m.clearedgroup +} + +// GroupIDs returns the "group" edge IDs in the mutation. +// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use +// GroupID instead. It exists only for internal usage by the builders. +func (m *GroupMembershipMutation) GroupIDs() (ids []uuid.UUID) { + if id := m.group; id != nil { + ids = append(ids, *id) + } + return +} + +// ResetGroup resets all changes to the "group" edge. +func (m *GroupMembershipMutation) ResetGroup() { + m.group = nil + m.clearedgroup = false +} + +// ClearUser clears the "user" edge to the User entity. +func (m *GroupMembershipMutation) ClearUser() { + m.cleareduser = true + m.clearedFields[groupmembership.FieldUserID] = struct{}{} +} + +// UserCleared reports if the "user" edge to the User entity was cleared. +func (m *GroupMembershipMutation) UserCleared() bool { + return m.UserIDCleared() || m.cleareduser +} + +// UserIDs returns the "user" edge IDs in the mutation. +// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use +// UserID instead. It exists only for internal usage by the builders. +func (m *GroupMembershipMutation) UserIDs() (ids []uuid.UUID) { + if id := m.user; id != nil { + ids = append(ids, *id) + } + return +} + +// ResetUser resets all changes to the "user" edge. +func (m *GroupMembershipMutation) ResetUser() { + m.user = nil + m.cleareduser = false +} + +// ClearAgent clears the "agent" edge to the Agent entity. +func (m *GroupMembershipMutation) ClearAgent() { + m.clearedagent = true + m.clearedFields[groupmembership.FieldAgentID] = struct{}{} +} + +// AgentCleared reports if the "agent" edge to the Agent entity was cleared. +func (m *GroupMembershipMutation) AgentCleared() bool { + return m.AgentIDCleared() || m.clearedagent +} + +// AgentIDs returns the "agent" edge IDs in the mutation. +// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use +// AgentID instead. It exists only for internal usage by the builders. +func (m *GroupMembershipMutation) AgentIDs() (ids []uuid.UUID) { + if id := m.agent; id != nil { + ids = append(ids, *id) + } + return +} + +// ResetAgent resets all changes to the "agent" edge. +func (m *GroupMembershipMutation) ResetAgent() { + m.agent = nil + m.clearedagent = false +} + +// Where appends a list predicates to the GroupMembershipMutation builder. +func (m *GroupMembershipMutation) Where(ps ...predicate.GroupMembership) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the GroupMembershipMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *GroupMembershipMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.GroupMembership, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *GroupMembershipMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *GroupMembershipMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (GroupMembership). +func (m *GroupMembershipMutation) Type() string { + return m.typ +} + +// Fields returns all fields that were changed during this mutation. Note that in +// order to get all numeric fields that were incremented/decremented, call +// AddedFields(). +func (m *GroupMembershipMutation) Fields() []string { + fields := make([]string, 0, 6) + if m.role != nil { + fields = append(fields, groupmembership.FieldRole) + } + if m.added_by != nil { + fields = append(fields, groupmembership.FieldAddedBy) + } + if m.added_at != nil { + fields = append(fields, groupmembership.FieldAddedAt) + } + if m.group != nil { + fields = append(fields, groupmembership.FieldGroupID) + } + if m.user != nil { + fields = append(fields, groupmembership.FieldUserID) + } + if m.agent != nil { + fields = append(fields, groupmembership.FieldAgentID) + } + return fields +} + +// Field returns the value of a field with the given name. The second boolean +// return value indicates that this field was not set, or was not defined in the +// schema. +func (m *GroupMembershipMutation) Field(name string) (ent.Value, bool) { + switch name { + case groupmembership.FieldRole: + return m.Role() + case groupmembership.FieldAddedBy: + return m.AddedBy() + case groupmembership.FieldAddedAt: + return m.AddedAt() + case groupmembership.FieldGroupID: + return m.GroupID() + case groupmembership.FieldUserID: + return m.UserID() + case groupmembership.FieldAgentID: + return m.AgentID() + } + return nil, false +} + +// OldField returns the old value of the field from the database. An error is +// returned if the mutation operation is not UpdateOne, or the query to the +// database failed. +func (m *GroupMembershipMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + switch name { + case groupmembership.FieldRole: + return m.OldRole(ctx) + case groupmembership.FieldAddedBy: + return m.OldAddedBy(ctx) + case groupmembership.FieldAddedAt: + return m.OldAddedAt(ctx) + case groupmembership.FieldGroupID: + return m.OldGroupID(ctx) + case groupmembership.FieldUserID: + return m.OldUserID(ctx) + case groupmembership.FieldAgentID: + return m.OldAgentID(ctx) + } + return nil, fmt.Errorf("unknown GroupMembership field %s", name) +} + +// SetField sets the value of a field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *GroupMembershipMutation) SetField(name string, value ent.Value) error { + switch name { + case groupmembership.FieldRole: + v, ok := value.(groupmembership.Role) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetRole(v) + return nil + case groupmembership.FieldAddedBy: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetAddedBy(v) + return nil + case groupmembership.FieldAddedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetAddedAt(v) + return nil + case groupmembership.FieldGroupID: + v, ok := value.(uuid.UUID) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetGroupID(v) + return nil + case groupmembership.FieldUserID: + v, ok := value.(uuid.UUID) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetUserID(v) + return nil + case groupmembership.FieldAgentID: + v, ok := value.(uuid.UUID) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetAgentID(v) + return nil + } + return fmt.Errorf("unknown GroupMembership field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *GroupMembershipMutation) AddedFields() []string { + return nil +} + +// AddedField returns the numeric value that was incremented/decremented on a field +// with the given name. The second boolean return value indicates that this field +// was not set, or was not defined in the schema. +func (m *GroupMembershipMutation) AddedField(name string) (ent.Value, bool) { + return nil, false +} + +// AddField adds the value to the field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *GroupMembershipMutation) AddField(name string, value ent.Value) error { + switch name { + } + return fmt.Errorf("unknown GroupMembership numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *GroupMembershipMutation) ClearedFields() []string { + var fields []string + if m.FieldCleared(groupmembership.FieldAddedBy) { + fields = append(fields, groupmembership.FieldAddedBy) + } + if m.FieldCleared(groupmembership.FieldUserID) { + fields = append(fields, groupmembership.FieldUserID) + } + if m.FieldCleared(groupmembership.FieldAgentID) { + fields = append(fields, groupmembership.FieldAgentID) + } + return fields +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *GroupMembershipMutation) FieldCleared(name string) bool { + _, ok := m.clearedFields[name] + return ok +} + +// ClearField clears the value of the field with the given name. It returns an +// error if the field is not defined in the schema. +func (m *GroupMembershipMutation) ClearField(name string) error { + switch name { + case groupmembership.FieldAddedBy: + m.ClearAddedBy() + return nil + case groupmembership.FieldUserID: + m.ClearUserID() + return nil + case groupmembership.FieldAgentID: + m.ClearAgentID() + return nil + } + return fmt.Errorf("unknown GroupMembership nullable field %s", name) +} + +// ResetField resets all changes in the mutation for the field with the given name. +// It returns an error if the field is not defined in the schema. +func (m *GroupMembershipMutation) ResetField(name string) error { + switch name { + case groupmembership.FieldRole: + m.ResetRole() + return nil + case groupmembership.FieldAddedBy: + m.ResetAddedBy() + return nil + case groupmembership.FieldAddedAt: + m.ResetAddedAt() + return nil + case groupmembership.FieldGroupID: + m.ResetGroupID() + return nil + case groupmembership.FieldUserID: + m.ResetUserID() + return nil + case groupmembership.FieldAgentID: + m.ResetAgentID() + return nil + } + return fmt.Errorf("unknown GroupMembership field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *GroupMembershipMutation) AddedEdges() []string { + edges := make([]string, 0, 3) + if m.group != nil { + edges = append(edges, groupmembership.EdgeGroup) + } + if m.user != nil { + edges = append(edges, groupmembership.EdgeUser) + } + if m.agent != nil { + edges = append(edges, groupmembership.EdgeAgent) + } + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *GroupMembershipMutation) AddedIDs(name string) []ent.Value { + switch name { + case groupmembership.EdgeGroup: + if id := m.group; id != nil { + return []ent.Value{*id} + } + case groupmembership.EdgeUser: + if id := m.user; id != nil { + return []ent.Value{*id} + } + case groupmembership.EdgeAgent: + if id := m.agent; id != nil { + return []ent.Value{*id} + } + } + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *GroupMembershipMutation) RemovedEdges() []string { + edges := make([]string, 0, 3) + return edges +} + +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *GroupMembershipMutation) RemovedIDs(name string) []ent.Value { + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *GroupMembershipMutation) ClearedEdges() []string { + edges := make([]string, 0, 3) + if m.clearedgroup { + edges = append(edges, groupmembership.EdgeGroup) + } + if m.cleareduser { + edges = append(edges, groupmembership.EdgeUser) + } + if m.clearedagent { + edges = append(edges, groupmembership.EdgeAgent) + } + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *GroupMembershipMutation) EdgeCleared(name string) bool { + switch name { + case groupmembership.EdgeGroup: + return m.clearedgroup + case groupmembership.EdgeUser: + return m.cleareduser + case groupmembership.EdgeAgent: + return m.clearedagent + } + return false +} + +// ClearEdge clears the value of the edge with the given name. It returns an error +// if that edge is not defined in the schema. +func (m *GroupMembershipMutation) ClearEdge(name string) error { + switch name { + case groupmembership.EdgeGroup: + m.ClearGroup() + return nil + case groupmembership.EdgeUser: + m.ClearUser() + return nil + case groupmembership.EdgeAgent: + m.ClearAgent() + return nil + } + return fmt.Errorf("unknown GroupMembership unique edge %s", name) +} + +// ResetEdge resets all changes to the edge with the given name in this mutation. +// It returns an error if the edge is not defined in the schema. +func (m *GroupMembershipMutation) ResetEdge(name string) error { + switch name { + case groupmembership.EdgeGroup: + m.ResetGroup() + return nil + case groupmembership.EdgeUser: + m.ResetUser() + return nil + case groupmembership.EdgeAgent: + m.ResetAgent() + return nil + } + return fmt.Errorf("unknown GroupMembership edge %s", name) +} + +// HarnessConfigMutation represents an operation that mutates the HarnessConfig nodes in the graph. +type HarnessConfigMutation struct { + config + op Op + typ string + id *uuid.UUID + name *string + slug *string + display_name *string + description *string + harness *string + _config *string + content_hash *string + scope *string + scope_id *string + storage_uri *string + storage_bucket *string + storage_path *string + files *string + locked *bool + status *harnessconfig.Status + owner_id *string + created_by *string + updated_by *string + visibility *string + created *time.Time + updated *time.Time + clearedFields map[string]struct{} + done bool + oldValue func(context.Context) (*HarnessConfig, error) + predicates []predicate.HarnessConfig +} + +var _ ent.Mutation = (*HarnessConfigMutation)(nil) + +// harnessconfigOption allows management of the mutation configuration using functional options. +type harnessconfigOption func(*HarnessConfigMutation) + +// newHarnessConfigMutation creates new mutation for the HarnessConfig entity. +func newHarnessConfigMutation(c config, op Op, opts ...harnessconfigOption) *HarnessConfigMutation { + m := &HarnessConfigMutation{ + config: c, + op: op, + typ: TypeHarnessConfig, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// withHarnessConfigID sets the ID field of the mutation. +func withHarnessConfigID(id uuid.UUID) harnessconfigOption { + return func(m *HarnessConfigMutation) { + var ( + err error + once sync.Once + value *HarnessConfig + ) + m.oldValue = func(ctx context.Context) (*HarnessConfig, error) { + once.Do(func() { + if m.done { + err = errors.New("querying old values post mutation is not allowed") + } else { + value, err = m.Client().HarnessConfig.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } +} + +// withHarnessConfig sets the old HarnessConfig of the mutation. +func withHarnessConfig(node *HarnessConfig) harnessconfigOption { + return func(m *HarnessConfigMutation) { + m.oldValue = func(context.Context) (*HarnessConfig, error) { + return node, nil + } + m.id = &node.ID + } +} + +// Client returns a new `ent.Client` from the mutation. If the mutation was +// executed in a transaction (ent.Tx), a transactional client is returned. +func (m HarnessConfigMutation) Client() *Client { + client := &Client{config: m.config} + client.init() + return client +} + +// Tx returns an `ent.Tx` for mutations that were executed in transactions; +// it returns an error otherwise. +func (m HarnessConfigMutation) Tx() (*Tx, error) { + if _, ok := m.driver.(*txDriver); !ok { + return nil, errors.New("ent: mutation is not running in a transaction") + } + tx := &Tx{config: m.config} + tx.init() + return tx, nil +} + +// SetID sets the value of the id field. Note that this +// operation is only accepted on creation of HarnessConfig entities. +func (m *HarnessConfigMutation) SetID(id uuid.UUID) { + m.id = &id +} + +// ID returns the ID value in the mutation. Note that the ID is only available +// if it was provided to the builder or after it was returned from the database. +func (m *HarnessConfigMutation) ID() (id uuid.UUID, exists bool) { + if m.id == nil { + return + } + return *m.id, true +} + +// IDs queries the database and returns the entity ids that match the mutation's predicate. +// That means, if the mutation is applied within a transaction with an isolation level such +// as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated +// or updated by the mutation. +func (m *HarnessConfigMutation) IDs(ctx context.Context) ([]uuid.UUID, error) { + switch { + case m.op.Is(OpUpdateOne | OpDeleteOne): + id, exists := m.ID() + if exists { + return []uuid.UUID{id}, nil + } + fallthrough + case m.op.Is(OpUpdate | OpDelete): + return m.Client().HarnessConfig.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } +} + +// SetName sets the "name" field. +func (m *HarnessConfigMutation) SetName(s string) { + m.name = &s +} + +// Name returns the value of the "name" field in the mutation. +func (m *HarnessConfigMutation) Name() (r string, exists bool) { + v := m.name + if v == nil { + return + } + return *v, true +} + +// OldName returns the old "name" field's value of the HarnessConfig entity. +// If the HarnessConfig object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *HarnessConfigMutation) OldName(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldName is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldName requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldName: %w", err) + } + return oldValue.Name, nil +} + +// ResetName resets all changes to the "name" field. +func (m *HarnessConfigMutation) ResetName() { + m.name = nil +} + +// SetSlug sets the "slug" field. +func (m *HarnessConfigMutation) SetSlug(s string) { + m.slug = &s +} + +// Slug returns the value of the "slug" field in the mutation. +func (m *HarnessConfigMutation) Slug() (r string, exists bool) { + v := m.slug + if v == nil { + return + } + return *v, true +} + +// OldSlug returns the old "slug" field's value of the HarnessConfig entity. +// If the HarnessConfig object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *HarnessConfigMutation) OldSlug(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldSlug is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldSlug requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldSlug: %w", err) + } + return oldValue.Slug, nil +} + +// ResetSlug resets all changes to the "slug" field. +func (m *HarnessConfigMutation) ResetSlug() { + m.slug = nil +} + +// SetDisplayName sets the "display_name" field. +func (m *HarnessConfigMutation) SetDisplayName(s string) { + m.display_name = &s +} + +// DisplayName returns the value of the "display_name" field in the mutation. +func (m *HarnessConfigMutation) DisplayName() (r string, exists bool) { + v := m.display_name + if v == nil { + return + } + return *v, true +} + +// OldDisplayName returns the old "display_name" field's value of the HarnessConfig entity. +// If the HarnessConfig object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *HarnessConfigMutation) OldDisplayName(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldDisplayName is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldDisplayName requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldDisplayName: %w", err) + } + return oldValue.DisplayName, nil +} + +// ClearDisplayName clears the value of the "display_name" field. +func (m *HarnessConfigMutation) ClearDisplayName() { + m.display_name = nil + m.clearedFields[harnessconfig.FieldDisplayName] = struct{}{} +} + +// DisplayNameCleared returns if the "display_name" field was cleared in this mutation. +func (m *HarnessConfigMutation) DisplayNameCleared() bool { + _, ok := m.clearedFields[harnessconfig.FieldDisplayName] + return ok +} + +// ResetDisplayName resets all changes to the "display_name" field. +func (m *HarnessConfigMutation) ResetDisplayName() { + m.display_name = nil + delete(m.clearedFields, harnessconfig.FieldDisplayName) +} + +// SetDescription sets the "description" field. +func (m *HarnessConfigMutation) SetDescription(s string) { + m.description = &s +} + +// Description returns the value of the "description" field in the mutation. +func (m *HarnessConfigMutation) Description() (r string, exists bool) { + v := m.description + if v == nil { + return + } + return *v, true +} + +// OldDescription returns the old "description" field's value of the HarnessConfig entity. +// If the HarnessConfig object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *HarnessConfigMutation) OldDescription(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldDescription is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldDescription requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldDescription: %w", err) + } + return oldValue.Description, nil +} + +// ClearDescription clears the value of the "description" field. +func (m *HarnessConfigMutation) ClearDescription() { + m.description = nil + m.clearedFields[harnessconfig.FieldDescription] = struct{}{} +} + +// DescriptionCleared returns if the "description" field was cleared in this mutation. +func (m *HarnessConfigMutation) DescriptionCleared() bool { + _, ok := m.clearedFields[harnessconfig.FieldDescription] + return ok +} + +// ResetDescription resets all changes to the "description" field. +func (m *HarnessConfigMutation) ResetDescription() { + m.description = nil + delete(m.clearedFields, harnessconfig.FieldDescription) +} + +// SetHarness sets the "harness" field. +func (m *HarnessConfigMutation) SetHarness(s string) { + m.harness = &s +} + +// Harness returns the value of the "harness" field in the mutation. +func (m *HarnessConfigMutation) Harness() (r string, exists bool) { + v := m.harness + if v == nil { + return + } + return *v, true +} + +// OldHarness returns the old "harness" field's value of the HarnessConfig entity. +// If the HarnessConfig object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *HarnessConfigMutation) OldHarness(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldHarness is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldHarness requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldHarness: %w", err) + } + return oldValue.Harness, nil +} + +// ResetHarness resets all changes to the "harness" field. +func (m *HarnessConfigMutation) ResetHarness() { + m.harness = nil +} + +// SetConfig sets the "config" field. +func (m *HarnessConfigMutation) SetConfig(s string) { + m._config = &s +} + +// Config returns the value of the "config" field in the mutation. +func (m *HarnessConfigMutation) Config() (r string, exists bool) { + v := m._config + if v == nil { + return + } + return *v, true +} + +// OldConfig returns the old "config" field's value of the HarnessConfig entity. +// If the HarnessConfig object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *HarnessConfigMutation) OldConfig(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldConfig is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldConfig requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldConfig: %w", err) + } + return oldValue.Config, nil +} + +// ClearConfig clears the value of the "config" field. +func (m *HarnessConfigMutation) ClearConfig() { + m._config = nil + m.clearedFields[harnessconfig.FieldConfig] = struct{}{} +} + +// ConfigCleared returns if the "config" field was cleared in this mutation. +func (m *HarnessConfigMutation) ConfigCleared() bool { + _, ok := m.clearedFields[harnessconfig.FieldConfig] + return ok +} + +// ResetConfig resets all changes to the "config" field. +func (m *HarnessConfigMutation) ResetConfig() { + m._config = nil + delete(m.clearedFields, harnessconfig.FieldConfig) +} + +// SetContentHash sets the "content_hash" field. +func (m *HarnessConfigMutation) SetContentHash(s string) { + m.content_hash = &s +} + +// ContentHash returns the value of the "content_hash" field in the mutation. +func (m *HarnessConfigMutation) ContentHash() (r string, exists bool) { + v := m.content_hash + if v == nil { + return + } + return *v, true +} + +// OldContentHash returns the old "content_hash" field's value of the HarnessConfig entity. +// If the HarnessConfig object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *HarnessConfigMutation) OldContentHash(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldContentHash is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldContentHash requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldContentHash: %w", err) + } + return oldValue.ContentHash, nil +} + +// ClearContentHash clears the value of the "content_hash" field. +func (m *HarnessConfigMutation) ClearContentHash() { + m.content_hash = nil + m.clearedFields[harnessconfig.FieldContentHash] = struct{}{} +} + +// ContentHashCleared returns if the "content_hash" field was cleared in this mutation. +func (m *HarnessConfigMutation) ContentHashCleared() bool { + _, ok := m.clearedFields[harnessconfig.FieldContentHash] + return ok +} + +// ResetContentHash resets all changes to the "content_hash" field. +func (m *HarnessConfigMutation) ResetContentHash() { + m.content_hash = nil + delete(m.clearedFields, harnessconfig.FieldContentHash) +} + +// SetScope sets the "scope" field. +func (m *HarnessConfigMutation) SetScope(s string) { + m.scope = &s +} + +// Scope returns the value of the "scope" field in the mutation. +func (m *HarnessConfigMutation) Scope() (r string, exists bool) { + v := m.scope + if v == nil { + return + } + return *v, true +} + +// OldScope returns the old "scope" field's value of the HarnessConfig entity. +// If the HarnessConfig object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *HarnessConfigMutation) OldScope(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldScope is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldScope requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldScope: %w", err) + } + return oldValue.Scope, nil +} + +// ResetScope resets all changes to the "scope" field. +func (m *HarnessConfigMutation) ResetScope() { + m.scope = nil +} + +// SetScopeID sets the "scope_id" field. +func (m *HarnessConfigMutation) SetScopeID(s string) { + m.scope_id = &s +} + +// ScopeID returns the value of the "scope_id" field in the mutation. +func (m *HarnessConfigMutation) ScopeID() (r string, exists bool) { + v := m.scope_id + if v == nil { + return + } + return *v, true +} + +// OldScopeID returns the old "scope_id" field's value of the HarnessConfig entity. +// If the HarnessConfig object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *HarnessConfigMutation) OldScopeID(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldScopeID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldScopeID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldScopeID: %w", err) + } + return oldValue.ScopeID, nil +} + +// ClearScopeID clears the value of the "scope_id" field. +func (m *HarnessConfigMutation) ClearScopeID() { + m.scope_id = nil + m.clearedFields[harnessconfig.FieldScopeID] = struct{}{} +} + +// ScopeIDCleared returns if the "scope_id" field was cleared in this mutation. +func (m *HarnessConfigMutation) ScopeIDCleared() bool { + _, ok := m.clearedFields[harnessconfig.FieldScopeID] + return ok +} + +// ResetScopeID resets all changes to the "scope_id" field. +func (m *HarnessConfigMutation) ResetScopeID() { + m.scope_id = nil + delete(m.clearedFields, harnessconfig.FieldScopeID) +} + +// SetStorageURI sets the "storage_uri" field. +func (m *HarnessConfigMutation) SetStorageURI(s string) { + m.storage_uri = &s +} + +// StorageURI returns the value of the "storage_uri" field in the mutation. +func (m *HarnessConfigMutation) StorageURI() (r string, exists bool) { + v := m.storage_uri + if v == nil { + return + } + return *v, true +} + +// OldStorageURI returns the old "storage_uri" field's value of the HarnessConfig entity. +// If the HarnessConfig object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *HarnessConfigMutation) OldStorageURI(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldStorageURI is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldStorageURI requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldStorageURI: %w", err) + } + return oldValue.StorageURI, nil +} + +// ClearStorageURI clears the value of the "storage_uri" field. +func (m *HarnessConfigMutation) ClearStorageURI() { + m.storage_uri = nil + m.clearedFields[harnessconfig.FieldStorageURI] = struct{}{} +} + +// StorageURICleared returns if the "storage_uri" field was cleared in this mutation. +func (m *HarnessConfigMutation) StorageURICleared() bool { + _, ok := m.clearedFields[harnessconfig.FieldStorageURI] + return ok +} + +// ResetStorageURI resets all changes to the "storage_uri" field. +func (m *HarnessConfigMutation) ResetStorageURI() { + m.storage_uri = nil + delete(m.clearedFields, harnessconfig.FieldStorageURI) +} + +// SetStorageBucket sets the "storage_bucket" field. +func (m *HarnessConfigMutation) SetStorageBucket(s string) { + m.storage_bucket = &s +} + +// StorageBucket returns the value of the "storage_bucket" field in the mutation. +func (m *HarnessConfigMutation) StorageBucket() (r string, exists bool) { + v := m.storage_bucket + if v == nil { + return + } + return *v, true +} + +// OldStorageBucket returns the old "storage_bucket" field's value of the HarnessConfig entity. +// If the HarnessConfig object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *HarnessConfigMutation) OldStorageBucket(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldStorageBucket is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldStorageBucket requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldStorageBucket: %w", err) + } + return oldValue.StorageBucket, nil +} + +// ClearStorageBucket clears the value of the "storage_bucket" field. +func (m *HarnessConfigMutation) ClearStorageBucket() { + m.storage_bucket = nil + m.clearedFields[harnessconfig.FieldStorageBucket] = struct{}{} +} + +// StorageBucketCleared returns if the "storage_bucket" field was cleared in this mutation. +func (m *HarnessConfigMutation) StorageBucketCleared() bool { + _, ok := m.clearedFields[harnessconfig.FieldStorageBucket] + return ok +} + +// ResetStorageBucket resets all changes to the "storage_bucket" field. +func (m *HarnessConfigMutation) ResetStorageBucket() { + m.storage_bucket = nil + delete(m.clearedFields, harnessconfig.FieldStorageBucket) +} + +// SetStoragePath sets the "storage_path" field. +func (m *HarnessConfigMutation) SetStoragePath(s string) { + m.storage_path = &s +} + +// StoragePath returns the value of the "storage_path" field in the mutation. +func (m *HarnessConfigMutation) StoragePath() (r string, exists bool) { + v := m.storage_path + if v == nil { + return + } + return *v, true +} + +// OldStoragePath returns the old "storage_path" field's value of the HarnessConfig entity. +// If the HarnessConfig object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *HarnessConfigMutation) OldStoragePath(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldStoragePath is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldStoragePath requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldStoragePath: %w", err) + } + return oldValue.StoragePath, nil +} + +// ClearStoragePath clears the value of the "storage_path" field. +func (m *HarnessConfigMutation) ClearStoragePath() { + m.storage_path = nil + m.clearedFields[harnessconfig.FieldStoragePath] = struct{}{} +} + +// StoragePathCleared returns if the "storage_path" field was cleared in this mutation. +func (m *HarnessConfigMutation) StoragePathCleared() bool { + _, ok := m.clearedFields[harnessconfig.FieldStoragePath] + return ok +} + +// ResetStoragePath resets all changes to the "storage_path" field. +func (m *HarnessConfigMutation) ResetStoragePath() { + m.storage_path = nil + delete(m.clearedFields, harnessconfig.FieldStoragePath) +} + +// SetFiles sets the "files" field. +func (m *HarnessConfigMutation) SetFiles(s string) { + m.files = &s +} + +// Files returns the value of the "files" field in the mutation. +func (m *HarnessConfigMutation) Files() (r string, exists bool) { + v := m.files + if v == nil { + return + } + return *v, true +} + +// OldFiles returns the old "files" field's value of the HarnessConfig entity. +// If the HarnessConfig object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *HarnessConfigMutation) OldFiles(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldFiles is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldFiles requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldFiles: %w", err) + } + return oldValue.Files, nil +} + +// ClearFiles clears the value of the "files" field. +func (m *HarnessConfigMutation) ClearFiles() { + m.files = nil + m.clearedFields[harnessconfig.FieldFiles] = struct{}{} +} + +// FilesCleared returns if the "files" field was cleared in this mutation. +func (m *HarnessConfigMutation) FilesCleared() bool { + _, ok := m.clearedFields[harnessconfig.FieldFiles] + return ok +} + +// ResetFiles resets all changes to the "files" field. +func (m *HarnessConfigMutation) ResetFiles() { + m.files = nil + delete(m.clearedFields, harnessconfig.FieldFiles) +} + +// SetLocked sets the "locked" field. +func (m *HarnessConfigMutation) SetLocked(b bool) { + m.locked = &b +} + +// Locked returns the value of the "locked" field in the mutation. +func (m *HarnessConfigMutation) Locked() (r bool, exists bool) { + v := m.locked + if v == nil { + return + } + return *v, true +} + +// OldLocked returns the old "locked" field's value of the HarnessConfig entity. +// If the HarnessConfig object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *HarnessConfigMutation) OldLocked(ctx context.Context) (v bool, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldLocked is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldLocked requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldLocked: %w", err) + } + return oldValue.Locked, nil +} + +// ResetLocked resets all changes to the "locked" field. +func (m *HarnessConfigMutation) ResetLocked() { + m.locked = nil +} + +// SetStatus sets the "status" field. +func (m *HarnessConfigMutation) SetStatus(h harnessconfig.Status) { + m.status = &h +} + +// Status returns the value of the "status" field in the mutation. +func (m *HarnessConfigMutation) Status() (r harnessconfig.Status, exists bool) { + v := m.status + if v == nil { + return + } + return *v, true +} + +// OldStatus returns the old "status" field's value of the HarnessConfig entity. +// If the HarnessConfig object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *HarnessConfigMutation) OldStatus(ctx context.Context) (v harnessconfig.Status, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldStatus is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldStatus requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldStatus: %w", err) + } + return oldValue.Status, nil +} + +// ResetStatus resets all changes to the "status" field. +func (m *HarnessConfigMutation) ResetStatus() { + m.status = nil +} + +// SetOwnerID sets the "owner_id" field. +func (m *HarnessConfigMutation) SetOwnerID(s string) { + m.owner_id = &s +} + +// OwnerID returns the value of the "owner_id" field in the mutation. +func (m *HarnessConfigMutation) OwnerID() (r string, exists bool) { + v := m.owner_id + if v == nil { + return + } + return *v, true +} + +// OldOwnerID returns the old "owner_id" field's value of the HarnessConfig entity. +// If the HarnessConfig object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *HarnessConfigMutation) OldOwnerID(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldOwnerID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldOwnerID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldOwnerID: %w", err) + } + return oldValue.OwnerID, nil +} + +// ClearOwnerID clears the value of the "owner_id" field. +func (m *HarnessConfigMutation) ClearOwnerID() { + m.owner_id = nil + m.clearedFields[harnessconfig.FieldOwnerID] = struct{}{} +} + +// OwnerIDCleared returns if the "owner_id" field was cleared in this mutation. +func (m *HarnessConfigMutation) OwnerIDCleared() bool { + _, ok := m.clearedFields[harnessconfig.FieldOwnerID] + return ok +} + +// ResetOwnerID resets all changes to the "owner_id" field. +func (m *HarnessConfigMutation) ResetOwnerID() { + m.owner_id = nil + delete(m.clearedFields, harnessconfig.FieldOwnerID) +} + +// SetCreatedBy sets the "created_by" field. +func (m *HarnessConfigMutation) SetCreatedBy(s string) { + m.created_by = &s +} + +// CreatedBy returns the value of the "created_by" field in the mutation. +func (m *HarnessConfigMutation) CreatedBy() (r string, exists bool) { + v := m.created_by + if v == nil { + return + } + return *v, true +} + +// OldCreatedBy returns the old "created_by" field's value of the HarnessConfig entity. +// If the HarnessConfig object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *HarnessConfigMutation) OldCreatedBy(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreatedBy is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreatedBy requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreatedBy: %w", err) + } + return oldValue.CreatedBy, nil +} + +// ClearCreatedBy clears the value of the "created_by" field. +func (m *HarnessConfigMutation) ClearCreatedBy() { + m.created_by = nil + m.clearedFields[harnessconfig.FieldCreatedBy] = struct{}{} +} + +// CreatedByCleared returns if the "created_by" field was cleared in this mutation. +func (m *HarnessConfigMutation) CreatedByCleared() bool { + _, ok := m.clearedFields[harnessconfig.FieldCreatedBy] + return ok +} + +// ResetCreatedBy resets all changes to the "created_by" field. +func (m *HarnessConfigMutation) ResetCreatedBy() { + m.created_by = nil + delete(m.clearedFields, harnessconfig.FieldCreatedBy) +} + +// SetUpdatedBy sets the "updated_by" field. +func (m *HarnessConfigMutation) SetUpdatedBy(s string) { + m.updated_by = &s +} + +// UpdatedBy returns the value of the "updated_by" field in the mutation. +func (m *HarnessConfigMutation) UpdatedBy() (r string, exists bool) { + v := m.updated_by + if v == nil { + return + } + return *v, true +} + +// OldUpdatedBy returns the old "updated_by" field's value of the HarnessConfig entity. +// If the HarnessConfig object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *HarnessConfigMutation) OldUpdatedBy(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldUpdatedBy is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldUpdatedBy requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldUpdatedBy: %w", err) + } + return oldValue.UpdatedBy, nil +} + +// ClearUpdatedBy clears the value of the "updated_by" field. +func (m *HarnessConfigMutation) ClearUpdatedBy() { + m.updated_by = nil + m.clearedFields[harnessconfig.FieldUpdatedBy] = struct{}{} +} + +// UpdatedByCleared returns if the "updated_by" field was cleared in this mutation. +func (m *HarnessConfigMutation) UpdatedByCleared() bool { + _, ok := m.clearedFields[harnessconfig.FieldUpdatedBy] + return ok +} + +// ResetUpdatedBy resets all changes to the "updated_by" field. +func (m *HarnessConfigMutation) ResetUpdatedBy() { + m.updated_by = nil + delete(m.clearedFields, harnessconfig.FieldUpdatedBy) +} + +// SetVisibility sets the "visibility" field. +func (m *HarnessConfigMutation) SetVisibility(s string) { + m.visibility = &s +} + +// Visibility returns the value of the "visibility" field in the mutation. +func (m *HarnessConfigMutation) Visibility() (r string, exists bool) { + v := m.visibility + if v == nil { + return + } + return *v, true +} + +// OldVisibility returns the old "visibility" field's value of the HarnessConfig entity. +// If the HarnessConfig object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *HarnessConfigMutation) OldVisibility(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldVisibility is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldVisibility requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldVisibility: %w", err) + } + return oldValue.Visibility, nil +} + +// ResetVisibility resets all changes to the "visibility" field. +func (m *HarnessConfigMutation) ResetVisibility() { + m.visibility = nil +} + +// SetCreated sets the "created" field. +func (m *HarnessConfigMutation) SetCreated(t time.Time) { + m.created = &t +} + +// Created returns the value of the "created" field in the mutation. +func (m *HarnessConfigMutation) Created() (r time.Time, exists bool) { + v := m.created + if v == nil { + return + } + return *v, true +} + +// OldCreated returns the old "created" field's value of the HarnessConfig entity. +// If the HarnessConfig object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *HarnessConfigMutation) OldCreated(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreated is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreated requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreated: %w", err) + } + return oldValue.Created, nil +} + +// ResetCreated resets all changes to the "created" field. +func (m *HarnessConfigMutation) ResetCreated() { + m.created = nil +} + +// SetUpdated sets the "updated" field. +func (m *HarnessConfigMutation) SetUpdated(t time.Time) { + m.updated = &t +} + +// Updated returns the value of the "updated" field in the mutation. +func (m *HarnessConfigMutation) Updated() (r time.Time, exists bool) { + v := m.updated + if v == nil { + return + } + return *v, true +} + +// OldUpdated returns the old "updated" field's value of the HarnessConfig entity. +// If the HarnessConfig object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *HarnessConfigMutation) OldUpdated(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldUpdated is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldUpdated requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldUpdated: %w", err) + } + return oldValue.Updated, nil +} + +// ResetUpdated resets all changes to the "updated" field. +func (m *HarnessConfigMutation) ResetUpdated() { + m.updated = nil +} + +// Where appends a list predicates to the HarnessConfigMutation builder. +func (m *HarnessConfigMutation) Where(ps ...predicate.HarnessConfig) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the HarnessConfigMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *HarnessConfigMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.HarnessConfig, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *HarnessConfigMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *HarnessConfigMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (HarnessConfig). +func (m *HarnessConfigMutation) Type() string { + return m.typ +} + +// Fields returns all fields that were changed during this mutation. Note that in +// order to get all numeric fields that were incremented/decremented, call +// AddedFields(). +func (m *HarnessConfigMutation) Fields() []string { + fields := make([]string, 0, 21) + if m.name != nil { + fields = append(fields, harnessconfig.FieldName) + } + if m.slug != nil { + fields = append(fields, harnessconfig.FieldSlug) + } + if m.display_name != nil { + fields = append(fields, harnessconfig.FieldDisplayName) + } + if m.description != nil { + fields = append(fields, harnessconfig.FieldDescription) + } + if m.harness != nil { + fields = append(fields, harnessconfig.FieldHarness) + } + if m._config != nil { + fields = append(fields, harnessconfig.FieldConfig) + } + if m.content_hash != nil { + fields = append(fields, harnessconfig.FieldContentHash) + } + if m.scope != nil { + fields = append(fields, harnessconfig.FieldScope) + } + if m.scope_id != nil { + fields = append(fields, harnessconfig.FieldScopeID) + } + if m.storage_uri != nil { + fields = append(fields, harnessconfig.FieldStorageURI) + } + if m.storage_bucket != nil { + fields = append(fields, harnessconfig.FieldStorageBucket) + } + if m.storage_path != nil { + fields = append(fields, harnessconfig.FieldStoragePath) + } + if m.files != nil { + fields = append(fields, harnessconfig.FieldFiles) + } + if m.locked != nil { + fields = append(fields, harnessconfig.FieldLocked) + } + if m.status != nil { + fields = append(fields, harnessconfig.FieldStatus) + } + if m.owner_id != nil { + fields = append(fields, harnessconfig.FieldOwnerID) + } + if m.created_by != nil { + fields = append(fields, harnessconfig.FieldCreatedBy) + } + if m.updated_by != nil { + fields = append(fields, harnessconfig.FieldUpdatedBy) + } + if m.visibility != nil { + fields = append(fields, harnessconfig.FieldVisibility) + } + if m.created != nil { + fields = append(fields, harnessconfig.FieldCreated) + } + if m.updated != nil { + fields = append(fields, harnessconfig.FieldUpdated) + } + return fields +} + +// Field returns the value of a field with the given name. The second boolean +// return value indicates that this field was not set, or was not defined in the +// schema. +func (m *HarnessConfigMutation) Field(name string) (ent.Value, bool) { + switch name { + case harnessconfig.FieldName: + return m.Name() + case harnessconfig.FieldSlug: + return m.Slug() + case harnessconfig.FieldDisplayName: + return m.DisplayName() + case harnessconfig.FieldDescription: + return m.Description() + case harnessconfig.FieldHarness: + return m.Harness() + case harnessconfig.FieldConfig: + return m.Config() + case harnessconfig.FieldContentHash: + return m.ContentHash() + case harnessconfig.FieldScope: + return m.Scope() + case harnessconfig.FieldScopeID: + return m.ScopeID() + case harnessconfig.FieldStorageURI: + return m.StorageURI() + case harnessconfig.FieldStorageBucket: + return m.StorageBucket() + case harnessconfig.FieldStoragePath: + return m.StoragePath() + case harnessconfig.FieldFiles: + return m.Files() + case harnessconfig.FieldLocked: + return m.Locked() + case harnessconfig.FieldStatus: + return m.Status() + case harnessconfig.FieldOwnerID: + return m.OwnerID() + case harnessconfig.FieldCreatedBy: + return m.CreatedBy() + case harnessconfig.FieldUpdatedBy: + return m.UpdatedBy() + case harnessconfig.FieldVisibility: + return m.Visibility() + case harnessconfig.FieldCreated: + return m.Created() + case harnessconfig.FieldUpdated: + return m.Updated() + } + return nil, false +} + +// OldField returns the old value of the field from the database. An error is +// returned if the mutation operation is not UpdateOne, or the query to the +// database failed. +func (m *HarnessConfigMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + switch name { + case harnessconfig.FieldName: + return m.OldName(ctx) + case harnessconfig.FieldSlug: + return m.OldSlug(ctx) + case harnessconfig.FieldDisplayName: + return m.OldDisplayName(ctx) + case harnessconfig.FieldDescription: + return m.OldDescription(ctx) + case harnessconfig.FieldHarness: + return m.OldHarness(ctx) + case harnessconfig.FieldConfig: + return m.OldConfig(ctx) + case harnessconfig.FieldContentHash: + return m.OldContentHash(ctx) + case harnessconfig.FieldScope: + return m.OldScope(ctx) + case harnessconfig.FieldScopeID: + return m.OldScopeID(ctx) + case harnessconfig.FieldStorageURI: + return m.OldStorageURI(ctx) + case harnessconfig.FieldStorageBucket: + return m.OldStorageBucket(ctx) + case harnessconfig.FieldStoragePath: + return m.OldStoragePath(ctx) + case harnessconfig.FieldFiles: + return m.OldFiles(ctx) + case harnessconfig.FieldLocked: + return m.OldLocked(ctx) + case harnessconfig.FieldStatus: + return m.OldStatus(ctx) + case harnessconfig.FieldOwnerID: + return m.OldOwnerID(ctx) + case harnessconfig.FieldCreatedBy: + return m.OldCreatedBy(ctx) + case harnessconfig.FieldUpdatedBy: + return m.OldUpdatedBy(ctx) + case harnessconfig.FieldVisibility: + return m.OldVisibility(ctx) + case harnessconfig.FieldCreated: + return m.OldCreated(ctx) + case harnessconfig.FieldUpdated: + return m.OldUpdated(ctx) + } + return nil, fmt.Errorf("unknown HarnessConfig field %s", name) +} + +// SetField sets the value of a field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *HarnessConfigMutation) SetField(name string, value ent.Value) error { + switch name { + case harnessconfig.FieldName: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetName(v) + return nil + case harnessconfig.FieldSlug: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetSlug(v) + return nil + case harnessconfig.FieldDisplayName: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetDisplayName(v) + return nil + case harnessconfig.FieldDescription: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetDescription(v) + return nil + case harnessconfig.FieldHarness: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetHarness(v) + return nil + case harnessconfig.FieldConfig: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetConfig(v) + return nil + case harnessconfig.FieldContentHash: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetContentHash(v) + return nil + case harnessconfig.FieldScope: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetScope(v) + return nil + case harnessconfig.FieldScopeID: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetScopeID(v) + return nil + case harnessconfig.FieldStorageURI: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetStorageURI(v) + return nil + case harnessconfig.FieldStorageBucket: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetStorageBucket(v) + return nil + case harnessconfig.FieldStoragePath: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetStoragePath(v) + return nil + case harnessconfig.FieldFiles: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetFiles(v) + return nil + case harnessconfig.FieldLocked: + v, ok := value.(bool) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetLocked(v) + return nil + case harnessconfig.FieldStatus: + v, ok := value.(harnessconfig.Status) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetStatus(v) + return nil + case harnessconfig.FieldOwnerID: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetOwnerID(v) + return nil + case harnessconfig.FieldCreatedBy: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreatedBy(v) + return nil + case harnessconfig.FieldUpdatedBy: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetUpdatedBy(v) + return nil + case harnessconfig.FieldVisibility: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetVisibility(v) + return nil + case harnessconfig.FieldCreated: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreated(v) + return nil + case harnessconfig.FieldUpdated: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetUpdated(v) + return nil + } + return fmt.Errorf("unknown HarnessConfig field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *HarnessConfigMutation) AddedFields() []string { + return nil +} + +// AddedField returns the numeric value that was incremented/decremented on a field +// with the given name. The second boolean return value indicates that this field +// was not set, or was not defined in the schema. +func (m *HarnessConfigMutation) AddedField(name string) (ent.Value, bool) { + return nil, false +} + +// AddField adds the value to the field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *HarnessConfigMutation) AddField(name string, value ent.Value) error { + switch name { + } + return fmt.Errorf("unknown HarnessConfig numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *HarnessConfigMutation) ClearedFields() []string { + var fields []string + if m.FieldCleared(harnessconfig.FieldDisplayName) { + fields = append(fields, harnessconfig.FieldDisplayName) + } + if m.FieldCleared(harnessconfig.FieldDescription) { + fields = append(fields, harnessconfig.FieldDescription) + } + if m.FieldCleared(harnessconfig.FieldConfig) { + fields = append(fields, harnessconfig.FieldConfig) + } + if m.FieldCleared(harnessconfig.FieldContentHash) { + fields = append(fields, harnessconfig.FieldContentHash) + } + if m.FieldCleared(harnessconfig.FieldScopeID) { + fields = append(fields, harnessconfig.FieldScopeID) + } + if m.FieldCleared(harnessconfig.FieldStorageURI) { + fields = append(fields, harnessconfig.FieldStorageURI) + } + if m.FieldCleared(harnessconfig.FieldStorageBucket) { + fields = append(fields, harnessconfig.FieldStorageBucket) + } + if m.FieldCleared(harnessconfig.FieldStoragePath) { + fields = append(fields, harnessconfig.FieldStoragePath) + } + if m.FieldCleared(harnessconfig.FieldFiles) { + fields = append(fields, harnessconfig.FieldFiles) + } + if m.FieldCleared(harnessconfig.FieldOwnerID) { + fields = append(fields, harnessconfig.FieldOwnerID) + } + if m.FieldCleared(harnessconfig.FieldCreatedBy) { + fields = append(fields, harnessconfig.FieldCreatedBy) + } + if m.FieldCleared(harnessconfig.FieldUpdatedBy) { + fields = append(fields, harnessconfig.FieldUpdatedBy) + } + return fields +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *HarnessConfigMutation) FieldCleared(name string) bool { + _, ok := m.clearedFields[name] + return ok +} + +// ClearField clears the value of the field with the given name. It returns an +// error if the field is not defined in the schema. +func (m *HarnessConfigMutation) ClearField(name string) error { + switch name { + case harnessconfig.FieldDisplayName: + m.ClearDisplayName() + return nil + case harnessconfig.FieldDescription: + m.ClearDescription() + return nil + case harnessconfig.FieldConfig: + m.ClearConfig() + return nil + case harnessconfig.FieldContentHash: + m.ClearContentHash() + return nil + case harnessconfig.FieldScopeID: + m.ClearScopeID() + return nil + case harnessconfig.FieldStorageURI: + m.ClearStorageURI() + return nil + case harnessconfig.FieldStorageBucket: + m.ClearStorageBucket() + return nil + case harnessconfig.FieldStoragePath: + m.ClearStoragePath() + return nil + case harnessconfig.FieldFiles: + m.ClearFiles() + return nil + case harnessconfig.FieldOwnerID: + m.ClearOwnerID() + return nil + case harnessconfig.FieldCreatedBy: + m.ClearCreatedBy() + return nil + case harnessconfig.FieldUpdatedBy: + m.ClearUpdatedBy() + return nil + } + return fmt.Errorf("unknown HarnessConfig nullable field %s", name) +} + +// ResetField resets all changes in the mutation for the field with the given name. +// It returns an error if the field is not defined in the schema. +func (m *HarnessConfigMutation) ResetField(name string) error { + switch name { + case harnessconfig.FieldName: + m.ResetName() + return nil + case harnessconfig.FieldSlug: + m.ResetSlug() + return nil + case harnessconfig.FieldDisplayName: + m.ResetDisplayName() + return nil + case harnessconfig.FieldDescription: + m.ResetDescription() + return nil + case harnessconfig.FieldHarness: + m.ResetHarness() + return nil + case harnessconfig.FieldConfig: + m.ResetConfig() + return nil + case harnessconfig.FieldContentHash: + m.ResetContentHash() + return nil + case harnessconfig.FieldScope: + m.ResetScope() + return nil + case harnessconfig.FieldScopeID: + m.ResetScopeID() + return nil + case harnessconfig.FieldStorageURI: + m.ResetStorageURI() + return nil + case harnessconfig.FieldStorageBucket: + m.ResetStorageBucket() + return nil + case harnessconfig.FieldStoragePath: + m.ResetStoragePath() + return nil + case harnessconfig.FieldFiles: + m.ResetFiles() + return nil + case harnessconfig.FieldLocked: + m.ResetLocked() + return nil + case harnessconfig.FieldStatus: + m.ResetStatus() + return nil + case harnessconfig.FieldOwnerID: + m.ResetOwnerID() + return nil + case harnessconfig.FieldCreatedBy: + m.ResetCreatedBy() + return nil + case harnessconfig.FieldUpdatedBy: + m.ResetUpdatedBy() + return nil + case harnessconfig.FieldVisibility: + m.ResetVisibility() + return nil + case harnessconfig.FieldCreated: + m.ResetCreated() + return nil + case harnessconfig.FieldUpdated: + m.ResetUpdated() + return nil + } + return fmt.Errorf("unknown HarnessConfig field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *HarnessConfigMutation) AddedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *HarnessConfigMutation) AddedIDs(name string) []ent.Value { + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *HarnessConfigMutation) RemovedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *HarnessConfigMutation) RemovedIDs(name string) []ent.Value { + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *HarnessConfigMutation) ClearedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *HarnessConfigMutation) EdgeCleared(name string) bool { + return false +} + +// ClearEdge clears the value of the edge with the given name. It returns an error +// if that edge is not defined in the schema. +func (m *HarnessConfigMutation) ClearEdge(name string) error { + return fmt.Errorf("unknown HarnessConfig unique edge %s", name) +} + +// ResetEdge resets all changes to the edge with the given name in this mutation. +// It returns an error if the edge is not defined in the schema. +func (m *HarnessConfigMutation) ResetEdge(name string) error { + return fmt.Errorf("unknown HarnessConfig edge %s", name) +} + +// InviteCodeMutation represents an operation that mutates the InviteCode nodes in the graph. +type InviteCodeMutation struct { + config + op Op + typ string + id *uuid.UUID + code_hash *string + code_prefix *string + max_uses *int + addmax_uses *int + use_count *int + adduse_count *int + expires_at *time.Time + revoked *bool + created_by *string + note *string + created *time.Time + clearedFields map[string]struct{} + done bool + oldValue func(context.Context) (*InviteCode, error) + predicates []predicate.InviteCode +} + +var _ ent.Mutation = (*InviteCodeMutation)(nil) + +// invitecodeOption allows management of the mutation configuration using functional options. +type invitecodeOption func(*InviteCodeMutation) + +// newInviteCodeMutation creates new mutation for the InviteCode entity. +func newInviteCodeMutation(c config, op Op, opts ...invitecodeOption) *InviteCodeMutation { + m := &InviteCodeMutation{ + config: c, + op: op, + typ: TypeInviteCode, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// withInviteCodeID sets the ID field of the mutation. +func withInviteCodeID(id uuid.UUID) invitecodeOption { + return func(m *InviteCodeMutation) { + var ( + err error + once sync.Once + value *InviteCode + ) + m.oldValue = func(ctx context.Context) (*InviteCode, error) { + once.Do(func() { + if m.done { + err = errors.New("querying old values post mutation is not allowed") + } else { + value, err = m.Client().InviteCode.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } +} + +// withInviteCode sets the old InviteCode of the mutation. +func withInviteCode(node *InviteCode) invitecodeOption { + return func(m *InviteCodeMutation) { + m.oldValue = func(context.Context) (*InviteCode, error) { + return node, nil + } + m.id = &node.ID + } +} + +// Client returns a new `ent.Client` from the mutation. If the mutation was +// executed in a transaction (ent.Tx), a transactional client is returned. +func (m InviteCodeMutation) Client() *Client { + client := &Client{config: m.config} + client.init() + return client +} + +// Tx returns an `ent.Tx` for mutations that were executed in transactions; +// it returns an error otherwise. +func (m InviteCodeMutation) Tx() (*Tx, error) { + if _, ok := m.driver.(*txDriver); !ok { + return nil, errors.New("ent: mutation is not running in a transaction") + } + tx := &Tx{config: m.config} + tx.init() + return tx, nil +} + +// SetID sets the value of the id field. Note that this +// operation is only accepted on creation of InviteCode entities. +func (m *InviteCodeMutation) SetID(id uuid.UUID) { + m.id = &id +} + +// ID returns the ID value in the mutation. Note that the ID is only available +// if it was provided to the builder or after it was returned from the database. +func (m *InviteCodeMutation) ID() (id uuid.UUID, exists bool) { + if m.id == nil { + return + } + return *m.id, true +} + +// IDs queries the database and returns the entity ids that match the mutation's predicate. +// That means, if the mutation is applied within a transaction with an isolation level such +// as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated +// or updated by the mutation. +func (m *InviteCodeMutation) IDs(ctx context.Context) ([]uuid.UUID, error) { + switch { + case m.op.Is(OpUpdateOne | OpDeleteOne): + id, exists := m.ID() + if exists { + return []uuid.UUID{id}, nil + } + fallthrough + case m.op.Is(OpUpdate | OpDelete): + return m.Client().InviteCode.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } +} + +// SetCodeHash sets the "code_hash" field. +func (m *InviteCodeMutation) SetCodeHash(s string) { + m.code_hash = &s +} + +// CodeHash returns the value of the "code_hash" field in the mutation. +func (m *InviteCodeMutation) CodeHash() (r string, exists bool) { + v := m.code_hash + if v == nil { + return + } + return *v, true +} + +// OldCodeHash returns the old "code_hash" field's value of the InviteCode entity. +// If the InviteCode object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *InviteCodeMutation) OldCodeHash(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCodeHash is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCodeHash requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCodeHash: %w", err) + } + return oldValue.CodeHash, nil +} + +// ResetCodeHash resets all changes to the "code_hash" field. +func (m *InviteCodeMutation) ResetCodeHash() { + m.code_hash = nil +} + +// SetCodePrefix sets the "code_prefix" field. +func (m *InviteCodeMutation) SetCodePrefix(s string) { + m.code_prefix = &s +} + +// CodePrefix returns the value of the "code_prefix" field in the mutation. +func (m *InviteCodeMutation) CodePrefix() (r string, exists bool) { + v := m.code_prefix + if v == nil { + return + } + return *v, true +} + +// OldCodePrefix returns the old "code_prefix" field's value of the InviteCode entity. +// If the InviteCode object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *InviteCodeMutation) OldCodePrefix(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCodePrefix is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCodePrefix requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCodePrefix: %w", err) + } + return oldValue.CodePrefix, nil +} + +// ResetCodePrefix resets all changes to the "code_prefix" field. +func (m *InviteCodeMutation) ResetCodePrefix() { + m.code_prefix = nil +} + +// SetMaxUses sets the "max_uses" field. +func (m *InviteCodeMutation) SetMaxUses(i int) { + m.max_uses = &i + m.addmax_uses = nil +} + +// MaxUses returns the value of the "max_uses" field in the mutation. +func (m *InviteCodeMutation) MaxUses() (r int, exists bool) { + v := m.max_uses + if v == nil { + return + } + return *v, true +} + +// OldMaxUses returns the old "max_uses" field's value of the InviteCode entity. +// If the InviteCode object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *InviteCodeMutation) OldMaxUses(ctx context.Context) (v int, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldMaxUses is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldMaxUses requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldMaxUses: %w", err) + } + return oldValue.MaxUses, nil +} + +// AddMaxUses adds i to the "max_uses" field. +func (m *InviteCodeMutation) AddMaxUses(i int) { + if m.addmax_uses != nil { + *m.addmax_uses += i + } else { + m.addmax_uses = &i + } +} + +// AddedMaxUses returns the value that was added to the "max_uses" field in this mutation. +func (m *InviteCodeMutation) AddedMaxUses() (r int, exists bool) { + v := m.addmax_uses + if v == nil { + return + } + return *v, true +} + +// ResetMaxUses resets all changes to the "max_uses" field. +func (m *InviteCodeMutation) ResetMaxUses() { + m.max_uses = nil + m.addmax_uses = nil +} + +// SetUseCount sets the "use_count" field. +func (m *InviteCodeMutation) SetUseCount(i int) { + m.use_count = &i + m.adduse_count = nil +} + +// UseCount returns the value of the "use_count" field in the mutation. +func (m *InviteCodeMutation) UseCount() (r int, exists bool) { + v := m.use_count + if v == nil { + return + } + return *v, true +} + +// OldUseCount returns the old "use_count" field's value of the InviteCode entity. +// If the InviteCode object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *InviteCodeMutation) OldUseCount(ctx context.Context) (v int, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldUseCount is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldUseCount requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldUseCount: %w", err) + } + return oldValue.UseCount, nil +} + +// AddUseCount adds i to the "use_count" field. +func (m *InviteCodeMutation) AddUseCount(i int) { + if m.adduse_count != nil { + *m.adduse_count += i + } else { + m.adduse_count = &i + } +} + +// AddedUseCount returns the value that was added to the "use_count" field in this mutation. +func (m *InviteCodeMutation) AddedUseCount() (r int, exists bool) { + v := m.adduse_count + if v == nil { + return + } + return *v, true +} + +// ResetUseCount resets all changes to the "use_count" field. +func (m *InviteCodeMutation) ResetUseCount() { + m.use_count = nil + m.adduse_count = nil +} + +// SetExpiresAt sets the "expires_at" field. +func (m *InviteCodeMutation) SetExpiresAt(t time.Time) { + m.expires_at = &t +} + +// ExpiresAt returns the value of the "expires_at" field in the mutation. +func (m *InviteCodeMutation) ExpiresAt() (r time.Time, exists bool) { + v := m.expires_at + if v == nil { + return + } + return *v, true +} + +// OldExpiresAt returns the old "expires_at" field's value of the InviteCode entity. +// If the InviteCode object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *InviteCodeMutation) OldExpiresAt(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldExpiresAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldExpiresAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldExpiresAt: %w", err) + } + return oldValue.ExpiresAt, nil +} + +// ResetExpiresAt resets all changes to the "expires_at" field. +func (m *InviteCodeMutation) ResetExpiresAt() { + m.expires_at = nil +} + +// SetRevoked sets the "revoked" field. +func (m *InviteCodeMutation) SetRevoked(b bool) { + m.revoked = &b +} + +// Revoked returns the value of the "revoked" field in the mutation. +func (m *InviteCodeMutation) Revoked() (r bool, exists bool) { + v := m.revoked + if v == nil { + return + } + return *v, true +} + +// OldRevoked returns the old "revoked" field's value of the InviteCode entity. +// If the InviteCode object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *InviteCodeMutation) OldRevoked(ctx context.Context) (v bool, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldRevoked is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldRevoked requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldRevoked: %w", err) + } + return oldValue.Revoked, nil +} + +// ResetRevoked resets all changes to the "revoked" field. +func (m *InviteCodeMutation) ResetRevoked() { + m.revoked = nil +} + +// SetCreatedBy sets the "created_by" field. +func (m *InviteCodeMutation) SetCreatedBy(s string) { + m.created_by = &s +} + +// CreatedBy returns the value of the "created_by" field in the mutation. +func (m *InviteCodeMutation) CreatedBy() (r string, exists bool) { + v := m.created_by + if v == nil { + return + } + return *v, true +} + +// OldCreatedBy returns the old "created_by" field's value of the InviteCode entity. +// If the InviteCode object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *InviteCodeMutation) OldCreatedBy(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreatedBy is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreatedBy requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreatedBy: %w", err) + } + return oldValue.CreatedBy, nil +} + +// ResetCreatedBy resets all changes to the "created_by" field. +func (m *InviteCodeMutation) ResetCreatedBy() { + m.created_by = nil +} + +// SetNote sets the "note" field. +func (m *InviteCodeMutation) SetNote(s string) { + m.note = &s +} + +// Note returns the value of the "note" field in the mutation. +func (m *InviteCodeMutation) Note() (r string, exists bool) { + v := m.note + if v == nil { + return + } + return *v, true +} + +// OldNote returns the old "note" field's value of the InviteCode entity. +// If the InviteCode object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *InviteCodeMutation) OldNote(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldNote is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldNote requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldNote: %w", err) + } + return oldValue.Note, nil +} + +// ResetNote resets all changes to the "note" field. +func (m *InviteCodeMutation) ResetNote() { + m.note = nil +} + +// SetCreated sets the "created" field. +func (m *InviteCodeMutation) SetCreated(t time.Time) { + m.created = &t +} + +// Created returns the value of the "created" field in the mutation. +func (m *InviteCodeMutation) Created() (r time.Time, exists bool) { + v := m.created + if v == nil { + return + } + return *v, true +} + +// OldCreated returns the old "created" field's value of the InviteCode entity. +// If the InviteCode object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *InviteCodeMutation) OldCreated(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreated is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreated requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreated: %w", err) + } + return oldValue.Created, nil +} + +// ResetCreated resets all changes to the "created" field. +func (m *InviteCodeMutation) ResetCreated() { + m.created = nil +} + +// Where appends a list predicates to the InviteCodeMutation builder. +func (m *InviteCodeMutation) Where(ps ...predicate.InviteCode) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the InviteCodeMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *InviteCodeMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.InviteCode, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *InviteCodeMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *InviteCodeMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (InviteCode). +func (m *InviteCodeMutation) Type() string { + return m.typ +} + +// Fields returns all fields that were changed during this mutation. Note that in +// order to get all numeric fields that were incremented/decremented, call +// AddedFields(). +func (m *InviteCodeMutation) Fields() []string { + fields := make([]string, 0, 9) + if m.code_hash != nil { + fields = append(fields, invitecode.FieldCodeHash) + } + if m.code_prefix != nil { + fields = append(fields, invitecode.FieldCodePrefix) + } + if m.max_uses != nil { + fields = append(fields, invitecode.FieldMaxUses) + } + if m.use_count != nil { + fields = append(fields, invitecode.FieldUseCount) + } + if m.expires_at != nil { + fields = append(fields, invitecode.FieldExpiresAt) + } + if m.revoked != nil { + fields = append(fields, invitecode.FieldRevoked) + } + if m.created_by != nil { + fields = append(fields, invitecode.FieldCreatedBy) + } + if m.note != nil { + fields = append(fields, invitecode.FieldNote) + } + if m.created != nil { + fields = append(fields, invitecode.FieldCreated) + } + return fields +} + +// Field returns the value of a field with the given name. The second boolean +// return value indicates that this field was not set, or was not defined in the +// schema. +func (m *InviteCodeMutation) Field(name string) (ent.Value, bool) { + switch name { + case invitecode.FieldCodeHash: + return m.CodeHash() + case invitecode.FieldCodePrefix: + return m.CodePrefix() + case invitecode.FieldMaxUses: + return m.MaxUses() + case invitecode.FieldUseCount: + return m.UseCount() + case invitecode.FieldExpiresAt: + return m.ExpiresAt() + case invitecode.FieldRevoked: + return m.Revoked() + case invitecode.FieldCreatedBy: + return m.CreatedBy() + case invitecode.FieldNote: + return m.Note() + case invitecode.FieldCreated: + return m.Created() + } + return nil, false +} + +// OldField returns the old value of the field from the database. An error is +// returned if the mutation operation is not UpdateOne, or the query to the +// database failed. +func (m *InviteCodeMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + switch name { + case invitecode.FieldCodeHash: + return m.OldCodeHash(ctx) + case invitecode.FieldCodePrefix: + return m.OldCodePrefix(ctx) + case invitecode.FieldMaxUses: + return m.OldMaxUses(ctx) + case invitecode.FieldUseCount: + return m.OldUseCount(ctx) + case invitecode.FieldExpiresAt: + return m.OldExpiresAt(ctx) + case invitecode.FieldRevoked: + return m.OldRevoked(ctx) + case invitecode.FieldCreatedBy: + return m.OldCreatedBy(ctx) + case invitecode.FieldNote: + return m.OldNote(ctx) + case invitecode.FieldCreated: + return m.OldCreated(ctx) + } + return nil, fmt.Errorf("unknown InviteCode field %s", name) +} + +// SetField sets the value of a field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *InviteCodeMutation) SetField(name string, value ent.Value) error { + switch name { + case invitecode.FieldCodeHash: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCodeHash(v) + return nil + case invitecode.FieldCodePrefix: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCodePrefix(v) + return nil + case invitecode.FieldMaxUses: + v, ok := value.(int) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetMaxUses(v) + return nil + case invitecode.FieldUseCount: + v, ok := value.(int) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetUseCount(v) + return nil + case invitecode.FieldExpiresAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetExpiresAt(v) + return nil + case invitecode.FieldRevoked: + v, ok := value.(bool) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetRevoked(v) + return nil + case invitecode.FieldCreatedBy: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreatedBy(v) + return nil + case invitecode.FieldNote: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetNote(v) + return nil + case invitecode.FieldCreated: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreated(v) + return nil + } + return fmt.Errorf("unknown InviteCode field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *InviteCodeMutation) AddedFields() []string { + var fields []string + if m.addmax_uses != nil { + fields = append(fields, invitecode.FieldMaxUses) + } + if m.adduse_count != nil { + fields = append(fields, invitecode.FieldUseCount) + } + return fields +} + +// AddedField returns the numeric value that was incremented/decremented on a field +// with the given name. The second boolean return value indicates that this field +// was not set, or was not defined in the schema. +func (m *InviteCodeMutation) AddedField(name string) (ent.Value, bool) { + switch name { + case invitecode.FieldMaxUses: + return m.AddedMaxUses() + case invitecode.FieldUseCount: + return m.AddedUseCount() + } + return nil, false +} + +// AddField adds the value to the field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *InviteCodeMutation) AddField(name string, value ent.Value) error { + switch name { + case invitecode.FieldMaxUses: + v, ok := value.(int) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.AddMaxUses(v) + return nil + case invitecode.FieldUseCount: + v, ok := value.(int) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.AddUseCount(v) + return nil + } + return fmt.Errorf("unknown InviteCode numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *InviteCodeMutation) ClearedFields() []string { + return nil +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *InviteCodeMutation) FieldCleared(name string) bool { + _, ok := m.clearedFields[name] + return ok +} + +// ClearField clears the value of the field with the given name. It returns an +// error if the field is not defined in the schema. +func (m *InviteCodeMutation) ClearField(name string) error { + return fmt.Errorf("unknown InviteCode nullable field %s", name) +} + +// ResetField resets all changes in the mutation for the field with the given name. +// It returns an error if the field is not defined in the schema. +func (m *InviteCodeMutation) ResetField(name string) error { + switch name { + case invitecode.FieldCodeHash: + m.ResetCodeHash() + return nil + case invitecode.FieldCodePrefix: + m.ResetCodePrefix() + return nil + case invitecode.FieldMaxUses: + m.ResetMaxUses() + return nil + case invitecode.FieldUseCount: + m.ResetUseCount() + return nil + case invitecode.FieldExpiresAt: + m.ResetExpiresAt() + return nil + case invitecode.FieldRevoked: + m.ResetRevoked() + return nil + case invitecode.FieldCreatedBy: + m.ResetCreatedBy() + return nil + case invitecode.FieldNote: + m.ResetNote() + return nil + case invitecode.FieldCreated: + m.ResetCreated() + return nil + } + return fmt.Errorf("unknown InviteCode field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *InviteCodeMutation) AddedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *InviteCodeMutation) AddedIDs(name string) []ent.Value { + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *InviteCodeMutation) RemovedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *InviteCodeMutation) RemovedIDs(name string) []ent.Value { + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *InviteCodeMutation) ClearedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *InviteCodeMutation) EdgeCleared(name string) bool { + return false +} + +// ClearEdge clears the value of the edge with the given name. It returns an error +// if that edge is not defined in the schema. +func (m *InviteCodeMutation) ClearEdge(name string) error { + return fmt.Errorf("unknown InviteCode unique edge %s", name) +} + +// ResetEdge resets all changes to the edge with the given name in this mutation. +// It returns an error if the edge is not defined in the schema. +func (m *InviteCodeMutation) ResetEdge(name string) error { + return fmt.Errorf("unknown InviteCode edge %s", name) +} + +// MaintenanceOperationMutation represents an operation that mutates the MaintenanceOperation nodes in the graph. +type MaintenanceOperationMutation struct { + config + op Op + typ string + id *uuid.UUID + key *string + title *string + description *string + category *string + status *string + started_at *time.Time + completed_at *time.Time + started_by *string + result *string + metadata *string + created *time.Time + clearedFields map[string]struct{} + done bool + oldValue func(context.Context) (*MaintenanceOperation, error) + predicates []predicate.MaintenanceOperation +} + +var _ ent.Mutation = (*MaintenanceOperationMutation)(nil) + +// maintenanceoperationOption allows management of the mutation configuration using functional options. +type maintenanceoperationOption func(*MaintenanceOperationMutation) + +// newMaintenanceOperationMutation creates new mutation for the MaintenanceOperation entity. +func newMaintenanceOperationMutation(c config, op Op, opts ...maintenanceoperationOption) *MaintenanceOperationMutation { + m := &MaintenanceOperationMutation{ + config: c, + op: op, + typ: TypeMaintenanceOperation, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// withMaintenanceOperationID sets the ID field of the mutation. +func withMaintenanceOperationID(id uuid.UUID) maintenanceoperationOption { + return func(m *MaintenanceOperationMutation) { + var ( + err error + once sync.Once + value *MaintenanceOperation + ) + m.oldValue = func(ctx context.Context) (*MaintenanceOperation, error) { + once.Do(func() { + if m.done { + err = errors.New("querying old values post mutation is not allowed") + } else { + value, err = m.Client().MaintenanceOperation.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } +} + +// withMaintenanceOperation sets the old MaintenanceOperation of the mutation. +func withMaintenanceOperation(node *MaintenanceOperation) maintenanceoperationOption { + return func(m *MaintenanceOperationMutation) { + m.oldValue = func(context.Context) (*MaintenanceOperation, error) { + return node, nil + } + m.id = &node.ID + } +} + +// Client returns a new `ent.Client` from the mutation. If the mutation was +// executed in a transaction (ent.Tx), a transactional client is returned. +func (m MaintenanceOperationMutation) Client() *Client { + client := &Client{config: m.config} + client.init() + return client +} + +// Tx returns an `ent.Tx` for mutations that were executed in transactions; +// it returns an error otherwise. +func (m MaintenanceOperationMutation) Tx() (*Tx, error) { + if _, ok := m.driver.(*txDriver); !ok { + return nil, errors.New("ent: mutation is not running in a transaction") + } + tx := &Tx{config: m.config} + tx.init() + return tx, nil +} + +// SetID sets the value of the id field. Note that this +// operation is only accepted on creation of MaintenanceOperation entities. +func (m *MaintenanceOperationMutation) SetID(id uuid.UUID) { + m.id = &id +} + +// ID returns the ID value in the mutation. Note that the ID is only available +// if it was provided to the builder or after it was returned from the database. +func (m *MaintenanceOperationMutation) ID() (id uuid.UUID, exists bool) { + if m.id == nil { + return + } + return *m.id, true +} + +// IDs queries the database and returns the entity ids that match the mutation's predicate. +// That means, if the mutation is applied within a transaction with an isolation level such +// as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated +// or updated by the mutation. +func (m *MaintenanceOperationMutation) IDs(ctx context.Context) ([]uuid.UUID, error) { + switch { + case m.op.Is(OpUpdateOne | OpDeleteOne): + id, exists := m.ID() + if exists { + return []uuid.UUID{id}, nil + } + fallthrough + case m.op.Is(OpUpdate | OpDelete): + return m.Client().MaintenanceOperation.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } +} + +// SetKey sets the "key" field. +func (m *MaintenanceOperationMutation) SetKey(s string) { + m.key = &s +} + +// Key returns the value of the "key" field in the mutation. +func (m *MaintenanceOperationMutation) Key() (r string, exists bool) { + v := m.key + if v == nil { + return + } + return *v, true +} + +// OldKey returns the old "key" field's value of the MaintenanceOperation entity. +// If the MaintenanceOperation object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *MaintenanceOperationMutation) OldKey(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldKey is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldKey requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldKey: %w", err) + } + return oldValue.Key, nil +} + +// ResetKey resets all changes to the "key" field. +func (m *MaintenanceOperationMutation) ResetKey() { + m.key = nil +} + +// SetTitle sets the "title" field. +func (m *MaintenanceOperationMutation) SetTitle(s string) { + m.title = &s +} + +// Title returns the value of the "title" field in the mutation. +func (m *MaintenanceOperationMutation) Title() (r string, exists bool) { + v := m.title + if v == nil { + return + } + return *v, true +} + +// OldTitle returns the old "title" field's value of the MaintenanceOperation entity. +// If the MaintenanceOperation object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *MaintenanceOperationMutation) OldTitle(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldTitle is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldTitle requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldTitle: %w", err) + } + return oldValue.Title, nil +} + +// ResetTitle resets all changes to the "title" field. +func (m *MaintenanceOperationMutation) ResetTitle() { + m.title = nil +} + +// SetDescription sets the "description" field. +func (m *MaintenanceOperationMutation) SetDescription(s string) { + m.description = &s +} + +// Description returns the value of the "description" field in the mutation. +func (m *MaintenanceOperationMutation) Description() (r string, exists bool) { + v := m.description + if v == nil { + return + } + return *v, true +} + +// OldDescription returns the old "description" field's value of the MaintenanceOperation entity. +// If the MaintenanceOperation object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *MaintenanceOperationMutation) OldDescription(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldDescription is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldDescription requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldDescription: %w", err) + } + return oldValue.Description, nil +} + +// ResetDescription resets all changes to the "description" field. +func (m *MaintenanceOperationMutation) ResetDescription() { + m.description = nil +} + +// SetCategory sets the "category" field. +func (m *MaintenanceOperationMutation) SetCategory(s string) { + m.category = &s +} + +// Category returns the value of the "category" field in the mutation. +func (m *MaintenanceOperationMutation) Category() (r string, exists bool) { + v := m.category + if v == nil { + return + } + return *v, true +} + +// OldCategory returns the old "category" field's value of the MaintenanceOperation entity. +// If the MaintenanceOperation object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *MaintenanceOperationMutation) OldCategory(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCategory is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCategory requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCategory: %w", err) + } + return oldValue.Category, nil +} + +// ResetCategory resets all changes to the "category" field. +func (m *MaintenanceOperationMutation) ResetCategory() { + m.category = nil +} + +// SetStatus sets the "status" field. +func (m *MaintenanceOperationMutation) SetStatus(s string) { + m.status = &s +} + +// Status returns the value of the "status" field in the mutation. +func (m *MaintenanceOperationMutation) Status() (r string, exists bool) { + v := m.status + if v == nil { + return + } + return *v, true +} + +// OldStatus returns the old "status" field's value of the MaintenanceOperation entity. +// If the MaintenanceOperation object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *MaintenanceOperationMutation) OldStatus(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldStatus is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldStatus requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldStatus: %w", err) + } + return oldValue.Status, nil +} + +// ResetStatus resets all changes to the "status" field. +func (m *MaintenanceOperationMutation) ResetStatus() { + m.status = nil +} + +// SetStartedAt sets the "started_at" field. +func (m *MaintenanceOperationMutation) SetStartedAt(t time.Time) { + m.started_at = &t +} + +// StartedAt returns the value of the "started_at" field in the mutation. +func (m *MaintenanceOperationMutation) StartedAt() (r time.Time, exists bool) { + v := m.started_at + if v == nil { + return + } + return *v, true +} + +// OldStartedAt returns the old "started_at" field's value of the MaintenanceOperation entity. +// If the MaintenanceOperation object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *MaintenanceOperationMutation) OldStartedAt(ctx context.Context) (v *time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldStartedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldStartedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldStartedAt: %w", err) + } + return oldValue.StartedAt, nil +} + +// ClearStartedAt clears the value of the "started_at" field. +func (m *MaintenanceOperationMutation) ClearStartedAt() { + m.started_at = nil + m.clearedFields[maintenanceoperation.FieldStartedAt] = struct{}{} +} + +// StartedAtCleared returns if the "started_at" field was cleared in this mutation. +func (m *MaintenanceOperationMutation) StartedAtCleared() bool { + _, ok := m.clearedFields[maintenanceoperation.FieldStartedAt] + return ok +} + +// ResetStartedAt resets all changes to the "started_at" field. +func (m *MaintenanceOperationMutation) ResetStartedAt() { + m.started_at = nil + delete(m.clearedFields, maintenanceoperation.FieldStartedAt) +} + +// SetCompletedAt sets the "completed_at" field. +func (m *MaintenanceOperationMutation) SetCompletedAt(t time.Time) { + m.completed_at = &t +} + +// CompletedAt returns the value of the "completed_at" field in the mutation. +func (m *MaintenanceOperationMutation) CompletedAt() (r time.Time, exists bool) { + v := m.completed_at + if v == nil { + return + } + return *v, true +} + +// OldCompletedAt returns the old "completed_at" field's value of the MaintenanceOperation entity. +// If the MaintenanceOperation object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *MaintenanceOperationMutation) OldCompletedAt(ctx context.Context) (v *time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCompletedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCompletedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCompletedAt: %w", err) + } + return oldValue.CompletedAt, nil +} + +// ClearCompletedAt clears the value of the "completed_at" field. +func (m *MaintenanceOperationMutation) ClearCompletedAt() { + m.completed_at = nil + m.clearedFields[maintenanceoperation.FieldCompletedAt] = struct{}{} +} + +// CompletedAtCleared returns if the "completed_at" field was cleared in this mutation. +func (m *MaintenanceOperationMutation) CompletedAtCleared() bool { + _, ok := m.clearedFields[maintenanceoperation.FieldCompletedAt] + return ok +} + +// ResetCompletedAt resets all changes to the "completed_at" field. +func (m *MaintenanceOperationMutation) ResetCompletedAt() { + m.completed_at = nil + delete(m.clearedFields, maintenanceoperation.FieldCompletedAt) +} + +// SetStartedBy sets the "started_by" field. +func (m *MaintenanceOperationMutation) SetStartedBy(s string) { + m.started_by = &s +} + +// StartedBy returns the value of the "started_by" field in the mutation. +func (m *MaintenanceOperationMutation) StartedBy() (r string, exists bool) { + v := m.started_by + if v == nil { + return + } + return *v, true +} + +// OldStartedBy returns the old "started_by" field's value of the MaintenanceOperation entity. +// If the MaintenanceOperation object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *MaintenanceOperationMutation) OldStartedBy(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldStartedBy is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldStartedBy requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldStartedBy: %w", err) + } + return oldValue.StartedBy, nil +} + +// ClearStartedBy clears the value of the "started_by" field. +func (m *MaintenanceOperationMutation) ClearStartedBy() { + m.started_by = nil + m.clearedFields[maintenanceoperation.FieldStartedBy] = struct{}{} +} + +// StartedByCleared returns if the "started_by" field was cleared in this mutation. +func (m *MaintenanceOperationMutation) StartedByCleared() bool { + _, ok := m.clearedFields[maintenanceoperation.FieldStartedBy] + return ok +} + +// ResetStartedBy resets all changes to the "started_by" field. +func (m *MaintenanceOperationMutation) ResetStartedBy() { + m.started_by = nil + delete(m.clearedFields, maintenanceoperation.FieldStartedBy) +} + +// SetResult sets the "result" field. +func (m *MaintenanceOperationMutation) SetResult(s string) { + m.result = &s +} + +// Result returns the value of the "result" field in the mutation. +func (m *MaintenanceOperationMutation) Result() (r string, exists bool) { + v := m.result + if v == nil { + return + } + return *v, true +} + +// OldResult returns the old "result" field's value of the MaintenanceOperation entity. +// If the MaintenanceOperation object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *MaintenanceOperationMutation) OldResult(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldResult is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldResult requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldResult: %w", err) + } + return oldValue.Result, nil +} + +// ClearResult clears the value of the "result" field. +func (m *MaintenanceOperationMutation) ClearResult() { + m.result = nil + m.clearedFields[maintenanceoperation.FieldResult] = struct{}{} +} + +// ResultCleared returns if the "result" field was cleared in this mutation. +func (m *MaintenanceOperationMutation) ResultCleared() bool { + _, ok := m.clearedFields[maintenanceoperation.FieldResult] + return ok +} + +// ResetResult resets all changes to the "result" field. +func (m *MaintenanceOperationMutation) ResetResult() { + m.result = nil + delete(m.clearedFields, maintenanceoperation.FieldResult) +} + +// SetMetadata sets the "metadata" field. +func (m *MaintenanceOperationMutation) SetMetadata(s string) { + m.metadata = &s +} + +// Metadata returns the value of the "metadata" field in the mutation. +func (m *MaintenanceOperationMutation) Metadata() (r string, exists bool) { + v := m.metadata + if v == nil { + return + } + return *v, true +} + +// OldMetadata returns the old "metadata" field's value of the MaintenanceOperation entity. +// If the MaintenanceOperation object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *MaintenanceOperationMutation) OldMetadata(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldMetadata is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldMetadata requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldMetadata: %w", err) + } + return oldValue.Metadata, nil +} + +// ResetMetadata resets all changes to the "metadata" field. +func (m *MaintenanceOperationMutation) ResetMetadata() { + m.metadata = nil +} + +// SetCreated sets the "created" field. +func (m *MaintenanceOperationMutation) SetCreated(t time.Time) { + m.created = &t +} + +// Created returns the value of the "created" field in the mutation. +func (m *MaintenanceOperationMutation) Created() (r time.Time, exists bool) { + v := m.created + if v == nil { + return + } + return *v, true +} + +// OldCreated returns the old "created" field's value of the MaintenanceOperation entity. +// If the MaintenanceOperation object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *MaintenanceOperationMutation) OldCreated(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreated is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreated requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreated: %w", err) + } + return oldValue.Created, nil +} + +// ResetCreated resets all changes to the "created" field. +func (m *MaintenanceOperationMutation) ResetCreated() { + m.created = nil +} + +// Where appends a list predicates to the MaintenanceOperationMutation builder. +func (m *MaintenanceOperationMutation) Where(ps ...predicate.MaintenanceOperation) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the MaintenanceOperationMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *MaintenanceOperationMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.MaintenanceOperation, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *MaintenanceOperationMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *MaintenanceOperationMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (MaintenanceOperation). +func (m *MaintenanceOperationMutation) Type() string { + return m.typ +} + +// Fields returns all fields that were changed during this mutation. Note that in +// order to get all numeric fields that were incremented/decremented, call +// AddedFields(). +func (m *MaintenanceOperationMutation) Fields() []string { + fields := make([]string, 0, 11) + if m.key != nil { + fields = append(fields, maintenanceoperation.FieldKey) + } + if m.title != nil { + fields = append(fields, maintenanceoperation.FieldTitle) + } + if m.description != nil { + fields = append(fields, maintenanceoperation.FieldDescription) + } + if m.category != nil { + fields = append(fields, maintenanceoperation.FieldCategory) + } + if m.status != nil { + fields = append(fields, maintenanceoperation.FieldStatus) + } + if m.started_at != nil { + fields = append(fields, maintenanceoperation.FieldStartedAt) + } + if m.completed_at != nil { + fields = append(fields, maintenanceoperation.FieldCompletedAt) + } + if m.started_by != nil { + fields = append(fields, maintenanceoperation.FieldStartedBy) + } + if m.result != nil { + fields = append(fields, maintenanceoperation.FieldResult) + } + if m.metadata != nil { + fields = append(fields, maintenanceoperation.FieldMetadata) + } + if m.created != nil { + fields = append(fields, maintenanceoperation.FieldCreated) + } + return fields +} + +// Field returns the value of a field with the given name. The second boolean +// return value indicates that this field was not set, or was not defined in the +// schema. +func (m *MaintenanceOperationMutation) Field(name string) (ent.Value, bool) { + switch name { + case maintenanceoperation.FieldKey: + return m.Key() + case maintenanceoperation.FieldTitle: + return m.Title() + case maintenanceoperation.FieldDescription: + return m.Description() + case maintenanceoperation.FieldCategory: + return m.Category() + case maintenanceoperation.FieldStatus: + return m.Status() + case maintenanceoperation.FieldStartedAt: + return m.StartedAt() + case maintenanceoperation.FieldCompletedAt: + return m.CompletedAt() + case maintenanceoperation.FieldStartedBy: + return m.StartedBy() + case maintenanceoperation.FieldResult: + return m.Result() + case maintenanceoperation.FieldMetadata: + return m.Metadata() + case maintenanceoperation.FieldCreated: + return m.Created() + } + return nil, false +} + +// OldField returns the old value of the field from the database. An error is +// returned if the mutation operation is not UpdateOne, or the query to the +// database failed. +func (m *MaintenanceOperationMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + switch name { + case maintenanceoperation.FieldKey: + return m.OldKey(ctx) + case maintenanceoperation.FieldTitle: + return m.OldTitle(ctx) + case maintenanceoperation.FieldDescription: + return m.OldDescription(ctx) + case maintenanceoperation.FieldCategory: + return m.OldCategory(ctx) + case maintenanceoperation.FieldStatus: + return m.OldStatus(ctx) + case maintenanceoperation.FieldStartedAt: + return m.OldStartedAt(ctx) + case maintenanceoperation.FieldCompletedAt: + return m.OldCompletedAt(ctx) + case maintenanceoperation.FieldStartedBy: + return m.OldStartedBy(ctx) + case maintenanceoperation.FieldResult: + return m.OldResult(ctx) + case maintenanceoperation.FieldMetadata: + return m.OldMetadata(ctx) + case maintenanceoperation.FieldCreated: + return m.OldCreated(ctx) + } + return nil, fmt.Errorf("unknown MaintenanceOperation field %s", name) +} + +// SetField sets the value of a field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *MaintenanceOperationMutation) SetField(name string, value ent.Value) error { + switch name { + case maintenanceoperation.FieldKey: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetKey(v) + return nil + case maintenanceoperation.FieldTitle: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetTitle(v) + return nil + case maintenanceoperation.FieldDescription: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetDescription(v) + return nil + case maintenanceoperation.FieldCategory: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCategory(v) + return nil + case maintenanceoperation.FieldStatus: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetStatus(v) + return nil + case maintenanceoperation.FieldStartedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetStartedAt(v) + return nil + case maintenanceoperation.FieldCompletedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCompletedAt(v) + return nil + case maintenanceoperation.FieldStartedBy: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetStartedBy(v) + return nil + case maintenanceoperation.FieldResult: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetResult(v) + return nil + case maintenanceoperation.FieldMetadata: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetMetadata(v) + return nil + case maintenanceoperation.FieldCreated: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreated(v) + return nil + } + return fmt.Errorf("unknown MaintenanceOperation field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *MaintenanceOperationMutation) AddedFields() []string { + return nil +} + +// AddedField returns the numeric value that was incremented/decremented on a field +// with the given name. The second boolean return value indicates that this field +// was not set, or was not defined in the schema. +func (m *MaintenanceOperationMutation) AddedField(name string) (ent.Value, bool) { + return nil, false +} + +// AddField adds the value to the field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *MaintenanceOperationMutation) AddField(name string, value ent.Value) error { + switch name { + } + return fmt.Errorf("unknown MaintenanceOperation numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *MaintenanceOperationMutation) ClearedFields() []string { + var fields []string + if m.FieldCleared(maintenanceoperation.FieldStartedAt) { + fields = append(fields, maintenanceoperation.FieldStartedAt) + } + if m.FieldCleared(maintenanceoperation.FieldCompletedAt) { + fields = append(fields, maintenanceoperation.FieldCompletedAt) + } + if m.FieldCleared(maintenanceoperation.FieldStartedBy) { + fields = append(fields, maintenanceoperation.FieldStartedBy) + } + if m.FieldCleared(maintenanceoperation.FieldResult) { + fields = append(fields, maintenanceoperation.FieldResult) + } + return fields +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *MaintenanceOperationMutation) FieldCleared(name string) bool { + _, ok := m.clearedFields[name] + return ok +} + +// ClearField clears the value of the field with the given name. It returns an +// error if the field is not defined in the schema. +func (m *MaintenanceOperationMutation) ClearField(name string) error { + switch name { + case maintenanceoperation.FieldStartedAt: + m.ClearStartedAt() + return nil + case maintenanceoperation.FieldCompletedAt: + m.ClearCompletedAt() + return nil + case maintenanceoperation.FieldStartedBy: + m.ClearStartedBy() + return nil + case maintenanceoperation.FieldResult: + m.ClearResult() + return nil + } + return fmt.Errorf("unknown MaintenanceOperation nullable field %s", name) +} + +// ResetField resets all changes in the mutation for the field with the given name. +// It returns an error if the field is not defined in the schema. +func (m *MaintenanceOperationMutation) ResetField(name string) error { + switch name { + case maintenanceoperation.FieldKey: + m.ResetKey() + return nil + case maintenanceoperation.FieldTitle: + m.ResetTitle() + return nil + case maintenanceoperation.FieldDescription: + m.ResetDescription() + return nil + case maintenanceoperation.FieldCategory: + m.ResetCategory() + return nil + case maintenanceoperation.FieldStatus: + m.ResetStatus() + return nil + case maintenanceoperation.FieldStartedAt: + m.ResetStartedAt() + return nil + case maintenanceoperation.FieldCompletedAt: + m.ResetCompletedAt() + return nil + case maintenanceoperation.FieldStartedBy: + m.ResetStartedBy() + return nil + case maintenanceoperation.FieldResult: + m.ResetResult() + return nil + case maintenanceoperation.FieldMetadata: + m.ResetMetadata() + return nil + case maintenanceoperation.FieldCreated: + m.ResetCreated() + return nil + } + return fmt.Errorf("unknown MaintenanceOperation field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *MaintenanceOperationMutation) AddedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *MaintenanceOperationMutation) AddedIDs(name string) []ent.Value { + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *MaintenanceOperationMutation) RemovedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *MaintenanceOperationMutation) RemovedIDs(name string) []ent.Value { + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *MaintenanceOperationMutation) ClearedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *MaintenanceOperationMutation) EdgeCleared(name string) bool { + return false +} + +// ClearEdge clears the value of the edge with the given name. It returns an error +// if that edge is not defined in the schema. +func (m *MaintenanceOperationMutation) ClearEdge(name string) error { + return fmt.Errorf("unknown MaintenanceOperation unique edge %s", name) +} + +// ResetEdge resets all changes to the edge with the given name in this mutation. +// It returns an error if the edge is not defined in the schema. +func (m *MaintenanceOperationMutation) ResetEdge(name string) error { + return fmt.Errorf("unknown MaintenanceOperation edge %s", name) +} + +// MaintenanceOperationRunMutation represents an operation that mutates the MaintenanceOperationRun nodes in the graph. +type MaintenanceOperationRunMutation struct { + config + op Op + typ string + id *uuid.UUID + operation_key *string + status *string + started_at *time.Time + completed_at *time.Time + started_by *string + result *string + log *string + clearedFields map[string]struct{} + done bool + oldValue func(context.Context) (*MaintenanceOperationRun, error) + predicates []predicate.MaintenanceOperationRun +} + +var _ ent.Mutation = (*MaintenanceOperationRunMutation)(nil) + +// maintenanceoperationrunOption allows management of the mutation configuration using functional options. +type maintenanceoperationrunOption func(*MaintenanceOperationRunMutation) + +// newMaintenanceOperationRunMutation creates new mutation for the MaintenanceOperationRun entity. +func newMaintenanceOperationRunMutation(c config, op Op, opts ...maintenanceoperationrunOption) *MaintenanceOperationRunMutation { + m := &MaintenanceOperationRunMutation{ + config: c, + op: op, + typ: TypeMaintenanceOperationRun, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// withMaintenanceOperationRunID sets the ID field of the mutation. +func withMaintenanceOperationRunID(id uuid.UUID) maintenanceoperationrunOption { + return func(m *MaintenanceOperationRunMutation) { + var ( + err error + once sync.Once + value *MaintenanceOperationRun + ) + m.oldValue = func(ctx context.Context) (*MaintenanceOperationRun, error) { + once.Do(func() { + if m.done { + err = errors.New("querying old values post mutation is not allowed") + } else { + value, err = m.Client().MaintenanceOperationRun.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } +} + +// withMaintenanceOperationRun sets the old MaintenanceOperationRun of the mutation. +func withMaintenanceOperationRun(node *MaintenanceOperationRun) maintenanceoperationrunOption { + return func(m *MaintenanceOperationRunMutation) { + m.oldValue = func(context.Context) (*MaintenanceOperationRun, error) { + return node, nil + } + m.id = &node.ID + } +} + +// Client returns a new `ent.Client` from the mutation. If the mutation was +// executed in a transaction (ent.Tx), a transactional client is returned. +func (m MaintenanceOperationRunMutation) Client() *Client { + client := &Client{config: m.config} + client.init() + return client +} + +// Tx returns an `ent.Tx` for mutations that were executed in transactions; +// it returns an error otherwise. +func (m MaintenanceOperationRunMutation) Tx() (*Tx, error) { + if _, ok := m.driver.(*txDriver); !ok { + return nil, errors.New("ent: mutation is not running in a transaction") + } + tx := &Tx{config: m.config} + tx.init() + return tx, nil +} + +// SetID sets the value of the id field. Note that this +// operation is only accepted on creation of MaintenanceOperationRun entities. +func (m *MaintenanceOperationRunMutation) SetID(id uuid.UUID) { + m.id = &id +} + +// ID returns the ID value in the mutation. Note that the ID is only available +// if it was provided to the builder or after it was returned from the database. +func (m *MaintenanceOperationRunMutation) ID() (id uuid.UUID, exists bool) { + if m.id == nil { + return + } + return *m.id, true +} + +// IDs queries the database and returns the entity ids that match the mutation's predicate. +// That means, if the mutation is applied within a transaction with an isolation level such +// as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated +// or updated by the mutation. +func (m *MaintenanceOperationRunMutation) IDs(ctx context.Context) ([]uuid.UUID, error) { + switch { + case m.op.Is(OpUpdateOne | OpDeleteOne): + id, exists := m.ID() + if exists { + return []uuid.UUID{id}, nil + } + fallthrough + case m.op.Is(OpUpdate | OpDelete): + return m.Client().MaintenanceOperationRun.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } +} + +// SetOperationKey sets the "operation_key" field. +func (m *MaintenanceOperationRunMutation) SetOperationKey(s string) { + m.operation_key = &s +} + +// OperationKey returns the value of the "operation_key" field in the mutation. +func (m *MaintenanceOperationRunMutation) OperationKey() (r string, exists bool) { + v := m.operation_key + if v == nil { + return + } + return *v, true +} + +// OldOperationKey returns the old "operation_key" field's value of the MaintenanceOperationRun entity. +// If the MaintenanceOperationRun object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *MaintenanceOperationRunMutation) OldOperationKey(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldOperationKey is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldOperationKey requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldOperationKey: %w", err) + } + return oldValue.OperationKey, nil +} + +// ResetOperationKey resets all changes to the "operation_key" field. +func (m *MaintenanceOperationRunMutation) ResetOperationKey() { + m.operation_key = nil +} + +// SetStatus sets the "status" field. +func (m *MaintenanceOperationRunMutation) SetStatus(s string) { + m.status = &s +} + +// Status returns the value of the "status" field in the mutation. +func (m *MaintenanceOperationRunMutation) Status() (r string, exists bool) { + v := m.status + if v == nil { + return + } + return *v, true +} + +// OldStatus returns the old "status" field's value of the MaintenanceOperationRun entity. +// If the MaintenanceOperationRun object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *MaintenanceOperationRunMutation) OldStatus(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldStatus is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldStatus requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldStatus: %w", err) + } + return oldValue.Status, nil +} + +// ResetStatus resets all changes to the "status" field. +func (m *MaintenanceOperationRunMutation) ResetStatus() { + m.status = nil +} + +// SetStartedAt sets the "started_at" field. +func (m *MaintenanceOperationRunMutation) SetStartedAt(t time.Time) { + m.started_at = &t +} + +// StartedAt returns the value of the "started_at" field in the mutation. +func (m *MaintenanceOperationRunMutation) StartedAt() (r time.Time, exists bool) { + v := m.started_at + if v == nil { + return + } + return *v, true +} + +// OldStartedAt returns the old "started_at" field's value of the MaintenanceOperationRun entity. +// If the MaintenanceOperationRun object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *MaintenanceOperationRunMutation) OldStartedAt(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldStartedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldStartedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldStartedAt: %w", err) + } + return oldValue.StartedAt, nil +} + +// ResetStartedAt resets all changes to the "started_at" field. +func (m *MaintenanceOperationRunMutation) ResetStartedAt() { + m.started_at = nil +} + +// SetCompletedAt sets the "completed_at" field. +func (m *MaintenanceOperationRunMutation) SetCompletedAt(t time.Time) { + m.completed_at = &t +} + +// CompletedAt returns the value of the "completed_at" field in the mutation. +func (m *MaintenanceOperationRunMutation) CompletedAt() (r time.Time, exists bool) { + v := m.completed_at + if v == nil { + return + } + return *v, true +} + +// OldCompletedAt returns the old "completed_at" field's value of the MaintenanceOperationRun entity. +// If the MaintenanceOperationRun object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *MaintenanceOperationRunMutation) OldCompletedAt(ctx context.Context) (v *time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCompletedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCompletedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCompletedAt: %w", err) + } + return oldValue.CompletedAt, nil +} + +// ClearCompletedAt clears the value of the "completed_at" field. +func (m *MaintenanceOperationRunMutation) ClearCompletedAt() { + m.completed_at = nil + m.clearedFields[maintenanceoperationrun.FieldCompletedAt] = struct{}{} +} + +// CompletedAtCleared returns if the "completed_at" field was cleared in this mutation. +func (m *MaintenanceOperationRunMutation) CompletedAtCleared() bool { + _, ok := m.clearedFields[maintenanceoperationrun.FieldCompletedAt] + return ok +} + +// ResetCompletedAt resets all changes to the "completed_at" field. +func (m *MaintenanceOperationRunMutation) ResetCompletedAt() { + m.completed_at = nil + delete(m.clearedFields, maintenanceoperationrun.FieldCompletedAt) +} + +// SetStartedBy sets the "started_by" field. +func (m *MaintenanceOperationRunMutation) SetStartedBy(s string) { + m.started_by = &s +} + +// StartedBy returns the value of the "started_by" field in the mutation. +func (m *MaintenanceOperationRunMutation) StartedBy() (r string, exists bool) { + v := m.started_by + if v == nil { + return + } + return *v, true +} + +// OldStartedBy returns the old "started_by" field's value of the MaintenanceOperationRun entity. +// If the MaintenanceOperationRun object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *MaintenanceOperationRunMutation) OldStartedBy(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldStartedBy is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldStartedBy requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldStartedBy: %w", err) + } + return oldValue.StartedBy, nil +} + +// ClearStartedBy clears the value of the "started_by" field. +func (m *MaintenanceOperationRunMutation) ClearStartedBy() { + m.started_by = nil + m.clearedFields[maintenanceoperationrun.FieldStartedBy] = struct{}{} +} + +// StartedByCleared returns if the "started_by" field was cleared in this mutation. +func (m *MaintenanceOperationRunMutation) StartedByCleared() bool { + _, ok := m.clearedFields[maintenanceoperationrun.FieldStartedBy] + return ok +} + +// ResetStartedBy resets all changes to the "started_by" field. +func (m *MaintenanceOperationRunMutation) ResetStartedBy() { + m.started_by = nil + delete(m.clearedFields, maintenanceoperationrun.FieldStartedBy) +} + +// SetResult sets the "result" field. +func (m *MaintenanceOperationRunMutation) SetResult(s string) { + m.result = &s +} + +// Result returns the value of the "result" field in the mutation. +func (m *MaintenanceOperationRunMutation) Result() (r string, exists bool) { + v := m.result + if v == nil { + return + } + return *v, true +} + +// OldResult returns the old "result" field's value of the MaintenanceOperationRun entity. +// If the MaintenanceOperationRun object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *MaintenanceOperationRunMutation) OldResult(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldResult is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldResult requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldResult: %w", err) + } + return oldValue.Result, nil +} + +// ClearResult clears the value of the "result" field. +func (m *MaintenanceOperationRunMutation) ClearResult() { + m.result = nil + m.clearedFields[maintenanceoperationrun.FieldResult] = struct{}{} +} + +// ResultCleared returns if the "result" field was cleared in this mutation. +func (m *MaintenanceOperationRunMutation) ResultCleared() bool { + _, ok := m.clearedFields[maintenanceoperationrun.FieldResult] + return ok +} + +// ResetResult resets all changes to the "result" field. +func (m *MaintenanceOperationRunMutation) ResetResult() { + m.result = nil + delete(m.clearedFields, maintenanceoperationrun.FieldResult) +} + +// SetLog sets the "log" field. +func (m *MaintenanceOperationRunMutation) SetLog(s string) { + m.log = &s +} + +// Log returns the value of the "log" field in the mutation. +func (m *MaintenanceOperationRunMutation) Log() (r string, exists bool) { + v := m.log + if v == nil { + return + } + return *v, true +} + +// OldLog returns the old "log" field's value of the MaintenanceOperationRun entity. +// If the MaintenanceOperationRun object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *MaintenanceOperationRunMutation) OldLog(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldLog is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldLog requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldLog: %w", err) + } + return oldValue.Log, nil +} + +// ResetLog resets all changes to the "log" field. +func (m *MaintenanceOperationRunMutation) ResetLog() { + m.log = nil +} + +// Where appends a list predicates to the MaintenanceOperationRunMutation builder. +func (m *MaintenanceOperationRunMutation) Where(ps ...predicate.MaintenanceOperationRun) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the MaintenanceOperationRunMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *MaintenanceOperationRunMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.MaintenanceOperationRun, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *MaintenanceOperationRunMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *MaintenanceOperationRunMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (MaintenanceOperationRun). +func (m *MaintenanceOperationRunMutation) Type() string { + return m.typ +} + +// Fields returns all fields that were changed during this mutation. Note that in +// order to get all numeric fields that were incremented/decremented, call +// AddedFields(). +func (m *MaintenanceOperationRunMutation) Fields() []string { + fields := make([]string, 0, 7) + if m.operation_key != nil { + fields = append(fields, maintenanceoperationrun.FieldOperationKey) + } + if m.status != nil { + fields = append(fields, maintenanceoperationrun.FieldStatus) + } + if m.started_at != nil { + fields = append(fields, maintenanceoperationrun.FieldStartedAt) + } + if m.completed_at != nil { + fields = append(fields, maintenanceoperationrun.FieldCompletedAt) + } + if m.started_by != nil { + fields = append(fields, maintenanceoperationrun.FieldStartedBy) + } + if m.result != nil { + fields = append(fields, maintenanceoperationrun.FieldResult) + } + if m.log != nil { + fields = append(fields, maintenanceoperationrun.FieldLog) + } + return fields +} + +// Field returns the value of a field with the given name. The second boolean +// return value indicates that this field was not set, or was not defined in the +// schema. +func (m *MaintenanceOperationRunMutation) Field(name string) (ent.Value, bool) { + switch name { + case maintenanceoperationrun.FieldOperationKey: + return m.OperationKey() + case maintenanceoperationrun.FieldStatus: + return m.Status() + case maintenanceoperationrun.FieldStartedAt: + return m.StartedAt() + case maintenanceoperationrun.FieldCompletedAt: + return m.CompletedAt() + case maintenanceoperationrun.FieldStartedBy: + return m.StartedBy() + case maintenanceoperationrun.FieldResult: + return m.Result() + case maintenanceoperationrun.FieldLog: + return m.Log() + } + return nil, false +} + +// OldField returns the old value of the field from the database. An error is +// returned if the mutation operation is not UpdateOne, or the query to the +// database failed. +func (m *MaintenanceOperationRunMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + switch name { + case maintenanceoperationrun.FieldOperationKey: + return m.OldOperationKey(ctx) + case maintenanceoperationrun.FieldStatus: + return m.OldStatus(ctx) + case maintenanceoperationrun.FieldStartedAt: + return m.OldStartedAt(ctx) + case maintenanceoperationrun.FieldCompletedAt: + return m.OldCompletedAt(ctx) + case maintenanceoperationrun.FieldStartedBy: + return m.OldStartedBy(ctx) + case maintenanceoperationrun.FieldResult: + return m.OldResult(ctx) + case maintenanceoperationrun.FieldLog: + return m.OldLog(ctx) + } + return nil, fmt.Errorf("unknown MaintenanceOperationRun field %s", name) +} + +// SetField sets the value of a field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *MaintenanceOperationRunMutation) SetField(name string, value ent.Value) error { + switch name { + case maintenanceoperationrun.FieldOperationKey: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetOperationKey(v) + return nil + case maintenanceoperationrun.FieldStatus: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetStatus(v) + return nil + case maintenanceoperationrun.FieldStartedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetStartedAt(v) + return nil + case maintenanceoperationrun.FieldCompletedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCompletedAt(v) + return nil + case maintenanceoperationrun.FieldStartedBy: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetStartedBy(v) + return nil + case maintenanceoperationrun.FieldResult: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetResult(v) + return nil + case maintenanceoperationrun.FieldLog: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetLog(v) + return nil + } + return fmt.Errorf("unknown MaintenanceOperationRun field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *MaintenanceOperationRunMutation) AddedFields() []string { + return nil +} + +// AddedField returns the numeric value that was incremented/decremented on a field +// with the given name. The second boolean return value indicates that this field +// was not set, or was not defined in the schema. +func (m *MaintenanceOperationRunMutation) AddedField(name string) (ent.Value, bool) { + return nil, false +} + +// AddField adds the value to the field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *MaintenanceOperationRunMutation) AddField(name string, value ent.Value) error { + switch name { + } + return fmt.Errorf("unknown MaintenanceOperationRun numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *MaintenanceOperationRunMutation) ClearedFields() []string { + var fields []string + if m.FieldCleared(maintenanceoperationrun.FieldCompletedAt) { + fields = append(fields, maintenanceoperationrun.FieldCompletedAt) + } + if m.FieldCleared(maintenanceoperationrun.FieldStartedBy) { + fields = append(fields, maintenanceoperationrun.FieldStartedBy) + } + if m.FieldCleared(maintenanceoperationrun.FieldResult) { + fields = append(fields, maintenanceoperationrun.FieldResult) + } + return fields +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *MaintenanceOperationRunMutation) FieldCleared(name string) bool { + _, ok := m.clearedFields[name] + return ok +} + +// ClearField clears the value of the field with the given name. It returns an +// error if the field is not defined in the schema. +func (m *MaintenanceOperationRunMutation) ClearField(name string) error { + switch name { + case maintenanceoperationrun.FieldCompletedAt: + m.ClearCompletedAt() + return nil + case maintenanceoperationrun.FieldStartedBy: + m.ClearStartedBy() + return nil + case maintenanceoperationrun.FieldResult: + m.ClearResult() + return nil + } + return fmt.Errorf("unknown MaintenanceOperationRun nullable field %s", name) +} + +// ResetField resets all changes in the mutation for the field with the given name. +// It returns an error if the field is not defined in the schema. +func (m *MaintenanceOperationRunMutation) ResetField(name string) error { + switch name { + case maintenanceoperationrun.FieldOperationKey: + m.ResetOperationKey() + return nil + case maintenanceoperationrun.FieldStatus: + m.ResetStatus() + return nil + case maintenanceoperationrun.FieldStartedAt: + m.ResetStartedAt() + return nil + case maintenanceoperationrun.FieldCompletedAt: + m.ResetCompletedAt() + return nil + case maintenanceoperationrun.FieldStartedBy: + m.ResetStartedBy() + return nil + case maintenanceoperationrun.FieldResult: + m.ResetResult() + return nil + case maintenanceoperationrun.FieldLog: + m.ResetLog() + return nil + } + return fmt.Errorf("unknown MaintenanceOperationRun field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *MaintenanceOperationRunMutation) AddedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *MaintenanceOperationRunMutation) AddedIDs(name string) []ent.Value { + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *MaintenanceOperationRunMutation) RemovedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *MaintenanceOperationRunMutation) RemovedIDs(name string) []ent.Value { + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *MaintenanceOperationRunMutation) ClearedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *MaintenanceOperationRunMutation) EdgeCleared(name string) bool { + return false +} + +// ClearEdge clears the value of the edge with the given name. It returns an error +// if that edge is not defined in the schema. +func (m *MaintenanceOperationRunMutation) ClearEdge(name string) error { + return fmt.Errorf("unknown MaintenanceOperationRun unique edge %s", name) +} + +// ResetEdge resets all changes to the edge with the given name in this mutation. +// It returns an error if the edge is not defined in the schema. +func (m *MaintenanceOperationRunMutation) ResetEdge(name string) error { + return fmt.Errorf("unknown MaintenanceOperationRun edge %s", name) +} + +// MessageMutation represents an operation that mutates the Message nodes in the graph. +type MessageMutation struct { + config + op Op + typ string + id *uuid.UUID + project_id *uuid.UUID + sender *string + sender_id *string + recipient *string + recipient_id *string + msg *string + _type *string + urgent *bool + broadcasted *bool + read *bool + agent_id *string + group_id *string + created *time.Time + clearedFields map[string]struct{} + done bool + oldValue func(context.Context) (*Message, error) + predicates []predicate.Message +} + +var _ ent.Mutation = (*MessageMutation)(nil) + +// messageOption allows management of the mutation configuration using functional options. +type messageOption func(*MessageMutation) + +// newMessageMutation creates new mutation for the Message entity. +func newMessageMutation(c config, op Op, opts ...messageOption) *MessageMutation { + m := &MessageMutation{ + config: c, + op: op, + typ: TypeMessage, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// withMessageID sets the ID field of the mutation. +func withMessageID(id uuid.UUID) messageOption { + return func(m *MessageMutation) { + var ( + err error + once sync.Once + value *Message + ) + m.oldValue = func(ctx context.Context) (*Message, error) { + once.Do(func() { + if m.done { + err = errors.New("querying old values post mutation is not allowed") + } else { + value, err = m.Client().Message.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } +} + +// withMessage sets the old Message of the mutation. +func withMessage(node *Message) messageOption { + return func(m *MessageMutation) { + m.oldValue = func(context.Context) (*Message, error) { + return node, nil + } + m.id = &node.ID + } +} + +// Client returns a new `ent.Client` from the mutation. If the mutation was +// executed in a transaction (ent.Tx), a transactional client is returned. +func (m MessageMutation) Client() *Client { + client := &Client{config: m.config} + client.init() + return client +} + +// Tx returns an `ent.Tx` for mutations that were executed in transactions; +// it returns an error otherwise. +func (m MessageMutation) Tx() (*Tx, error) { + if _, ok := m.driver.(*txDriver); !ok { + return nil, errors.New("ent: mutation is not running in a transaction") + } + tx := &Tx{config: m.config} + tx.init() + return tx, nil +} + +// SetID sets the value of the id field. Note that this +// operation is only accepted on creation of Message entities. +func (m *MessageMutation) SetID(id uuid.UUID) { + m.id = &id +} + +// ID returns the ID value in the mutation. Note that the ID is only available +// if it was provided to the builder or after it was returned from the database. +func (m *MessageMutation) ID() (id uuid.UUID, exists bool) { + if m.id == nil { + return + } + return *m.id, true +} + +// IDs queries the database and returns the entity ids that match the mutation's predicate. +// That means, if the mutation is applied within a transaction with an isolation level such +// as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated +// or updated by the mutation. +func (m *MessageMutation) IDs(ctx context.Context) ([]uuid.UUID, error) { + switch { + case m.op.Is(OpUpdateOne | OpDeleteOne): + id, exists := m.ID() + if exists { + return []uuid.UUID{id}, nil + } + fallthrough + case m.op.Is(OpUpdate | OpDelete): + return m.Client().Message.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } +} + +// SetProjectID sets the "project_id" field. +func (m *MessageMutation) SetProjectID(u uuid.UUID) { + m.project_id = &u +} + +// ProjectID returns the value of the "project_id" field in the mutation. +func (m *MessageMutation) ProjectID() (r uuid.UUID, exists bool) { + v := m.project_id + if v == nil { + return + } + return *v, true +} + +// OldProjectID returns the old "project_id" field's value of the Message entity. +// If the Message object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *MessageMutation) OldProjectID(ctx context.Context) (v uuid.UUID, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldProjectID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldProjectID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldProjectID: %w", err) + } + return oldValue.ProjectID, nil +} + +// ResetProjectID resets all changes to the "project_id" field. +func (m *MessageMutation) ResetProjectID() { + m.project_id = nil +} + +// SetSender sets the "sender" field. +func (m *MessageMutation) SetSender(s string) { + m.sender = &s +} + +// Sender returns the value of the "sender" field in the mutation. +func (m *MessageMutation) Sender() (r string, exists bool) { + v := m.sender + if v == nil { + return + } + return *v, true +} + +// OldSender returns the old "sender" field's value of the Message entity. +// If the Message object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *MessageMutation) OldSender(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldSender is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldSender requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldSender: %w", err) + } + return oldValue.Sender, nil +} + +// ResetSender resets all changes to the "sender" field. +func (m *MessageMutation) ResetSender() { + m.sender = nil +} + +// SetSenderID sets the "sender_id" field. +func (m *MessageMutation) SetSenderID(s string) { + m.sender_id = &s +} + +// SenderID returns the value of the "sender_id" field in the mutation. +func (m *MessageMutation) SenderID() (r string, exists bool) { + v := m.sender_id + if v == nil { + return + } + return *v, true +} + +// OldSenderID returns the old "sender_id" field's value of the Message entity. +// If the Message object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *MessageMutation) OldSenderID(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldSenderID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldSenderID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldSenderID: %w", err) + } + return oldValue.SenderID, nil +} + +// ClearSenderID clears the value of the "sender_id" field. +func (m *MessageMutation) ClearSenderID() { + m.sender_id = nil + m.clearedFields[message.FieldSenderID] = struct{}{} +} + +// SenderIDCleared returns if the "sender_id" field was cleared in this mutation. +func (m *MessageMutation) SenderIDCleared() bool { + _, ok := m.clearedFields[message.FieldSenderID] + return ok +} + +// ResetSenderID resets all changes to the "sender_id" field. +func (m *MessageMutation) ResetSenderID() { + m.sender_id = nil + delete(m.clearedFields, message.FieldSenderID) +} + +// SetRecipient sets the "recipient" field. +func (m *MessageMutation) SetRecipient(s string) { + m.recipient = &s +} + +// Recipient returns the value of the "recipient" field in the mutation. +func (m *MessageMutation) Recipient() (r string, exists bool) { + v := m.recipient + if v == nil { + return + } + return *v, true +} + +// OldRecipient returns the old "recipient" field's value of the Message entity. +// If the Message object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *MessageMutation) OldRecipient(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldRecipient is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldRecipient requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldRecipient: %w", err) + } + return oldValue.Recipient, nil +} + +// ResetRecipient resets all changes to the "recipient" field. +func (m *MessageMutation) ResetRecipient() { + m.recipient = nil +} + +// SetRecipientID sets the "recipient_id" field. +func (m *MessageMutation) SetRecipientID(s string) { + m.recipient_id = &s +} + +// RecipientID returns the value of the "recipient_id" field in the mutation. +func (m *MessageMutation) RecipientID() (r string, exists bool) { + v := m.recipient_id + if v == nil { + return + } + return *v, true +} + +// OldRecipientID returns the old "recipient_id" field's value of the Message entity. +// If the Message object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *MessageMutation) OldRecipientID(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldRecipientID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldRecipientID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldRecipientID: %w", err) + } + return oldValue.RecipientID, nil +} + +// ClearRecipientID clears the value of the "recipient_id" field. +func (m *MessageMutation) ClearRecipientID() { + m.recipient_id = nil + m.clearedFields[message.FieldRecipientID] = struct{}{} +} + +// RecipientIDCleared returns if the "recipient_id" field was cleared in this mutation. +func (m *MessageMutation) RecipientIDCleared() bool { + _, ok := m.clearedFields[message.FieldRecipientID] + return ok +} + +// ResetRecipientID resets all changes to the "recipient_id" field. +func (m *MessageMutation) ResetRecipientID() { + m.recipient_id = nil + delete(m.clearedFields, message.FieldRecipientID) +} + +// SetMsg sets the "msg" field. +func (m *MessageMutation) SetMsg(s string) { + m.msg = &s +} + +// Msg returns the value of the "msg" field in the mutation. +func (m *MessageMutation) Msg() (r string, exists bool) { + v := m.msg + if v == nil { + return + } + return *v, true +} + +// OldMsg returns the old "msg" field's value of the Message entity. +// If the Message object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *MessageMutation) OldMsg(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldMsg is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldMsg requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldMsg: %w", err) + } + return oldValue.Msg, nil +} + +// ResetMsg resets all changes to the "msg" field. +func (m *MessageMutation) ResetMsg() { + m.msg = nil +} + +// SetType sets the "type" field. +func (m *MessageMutation) SetType(s string) { + m._type = &s +} + +// GetType returns the value of the "type" field in the mutation. +func (m *MessageMutation) GetType() (r string, exists bool) { + v := m._type + if v == nil { + return + } + return *v, true +} + +// OldType returns the old "type" field's value of the Message entity. +// If the Message object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *MessageMutation) OldType(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldType is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldType requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldType: %w", err) + } + return oldValue.Type, nil +} + +// ResetType resets all changes to the "type" field. +func (m *MessageMutation) ResetType() { + m._type = nil +} + +// SetUrgent sets the "urgent" field. +func (m *MessageMutation) SetUrgent(b bool) { + m.urgent = &b +} + +// Urgent returns the value of the "urgent" field in the mutation. +func (m *MessageMutation) Urgent() (r bool, exists bool) { + v := m.urgent + if v == nil { + return + } + return *v, true +} + +// OldUrgent returns the old "urgent" field's value of the Message entity. +// If the Message object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *MessageMutation) OldUrgent(ctx context.Context) (v bool, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldUrgent is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldUrgent requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldUrgent: %w", err) + } + return oldValue.Urgent, nil +} + +// ResetUrgent resets all changes to the "urgent" field. +func (m *MessageMutation) ResetUrgent() { + m.urgent = nil +} + +// SetBroadcasted sets the "broadcasted" field. +func (m *MessageMutation) SetBroadcasted(b bool) { + m.broadcasted = &b +} + +// Broadcasted returns the value of the "broadcasted" field in the mutation. +func (m *MessageMutation) Broadcasted() (r bool, exists bool) { + v := m.broadcasted + if v == nil { + return + } + return *v, true +} + +// OldBroadcasted returns the old "broadcasted" field's value of the Message entity. +// If the Message object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *MessageMutation) OldBroadcasted(ctx context.Context) (v bool, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldBroadcasted is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldBroadcasted requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldBroadcasted: %w", err) + } + return oldValue.Broadcasted, nil +} + +// ResetBroadcasted resets all changes to the "broadcasted" field. +func (m *MessageMutation) ResetBroadcasted() { + m.broadcasted = nil +} + +// SetRead sets the "read" field. +func (m *MessageMutation) SetRead(b bool) { + m.read = &b +} + +// Read returns the value of the "read" field in the mutation. +func (m *MessageMutation) Read() (r bool, exists bool) { + v := m.read + if v == nil { + return + } + return *v, true +} + +// OldRead returns the old "read" field's value of the Message entity. +// If the Message object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *MessageMutation) OldRead(ctx context.Context) (v bool, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldRead is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldRead requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldRead: %w", err) + } + return oldValue.Read, nil +} + +// ResetRead resets all changes to the "read" field. +func (m *MessageMutation) ResetRead() { + m.read = nil +} + +// SetAgentID sets the "agent_id" field. +func (m *MessageMutation) SetAgentID(s string) { + m.agent_id = &s +} + +// AgentID returns the value of the "agent_id" field in the mutation. +func (m *MessageMutation) AgentID() (r string, exists bool) { + v := m.agent_id + if v == nil { + return + } + return *v, true +} + +// OldAgentID returns the old "agent_id" field's value of the Message entity. +// If the Message object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *MessageMutation) OldAgentID(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldAgentID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldAgentID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldAgentID: %w", err) + } + return oldValue.AgentID, nil +} + +// ClearAgentID clears the value of the "agent_id" field. +func (m *MessageMutation) ClearAgentID() { + m.agent_id = nil + m.clearedFields[message.FieldAgentID] = struct{}{} +} + +// AgentIDCleared returns if the "agent_id" field was cleared in this mutation. +func (m *MessageMutation) AgentIDCleared() bool { + _, ok := m.clearedFields[message.FieldAgentID] + return ok +} + +// ResetAgentID resets all changes to the "agent_id" field. +func (m *MessageMutation) ResetAgentID() { + m.agent_id = nil + delete(m.clearedFields, message.FieldAgentID) +} + +// SetGroupID sets the "group_id" field. +func (m *MessageMutation) SetGroupID(s string) { + m.group_id = &s +} + +// GroupID returns the value of the "group_id" field in the mutation. +func (m *MessageMutation) GroupID() (r string, exists bool) { + v := m.group_id + if v == nil { + return + } + return *v, true +} + +// OldGroupID returns the old "group_id" field's value of the Message entity. +// If the Message object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *MessageMutation) OldGroupID(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldGroupID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldGroupID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldGroupID: %w", err) + } + return oldValue.GroupID, nil +} + +// ClearGroupID clears the value of the "group_id" field. +func (m *MessageMutation) ClearGroupID() { + m.group_id = nil + m.clearedFields[message.FieldGroupID] = struct{}{} +} + +// GroupIDCleared returns if the "group_id" field was cleared in this mutation. +func (m *MessageMutation) GroupIDCleared() bool { + _, ok := m.clearedFields[message.FieldGroupID] + return ok +} + +// ResetGroupID resets all changes to the "group_id" field. +func (m *MessageMutation) ResetGroupID() { + m.group_id = nil + delete(m.clearedFields, message.FieldGroupID) +} + +// SetCreated sets the "created" field. +func (m *MessageMutation) SetCreated(t time.Time) { + m.created = &t +} + +// Created returns the value of the "created" field in the mutation. +func (m *MessageMutation) Created() (r time.Time, exists bool) { + v := m.created + if v == nil { + return + } + return *v, true +} + +// OldCreated returns the old "created" field's value of the Message entity. +// If the Message object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *MessageMutation) OldCreated(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreated is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreated requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreated: %w", err) + } + return oldValue.Created, nil +} + +// ResetCreated resets all changes to the "created" field. +func (m *MessageMutation) ResetCreated() { + m.created = nil +} + +// Where appends a list predicates to the MessageMutation builder. +func (m *MessageMutation) Where(ps ...predicate.Message) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the MessageMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *MessageMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.Message, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *MessageMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *MessageMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (Message). +func (m *MessageMutation) Type() string { + return m.typ +} + +// Fields returns all fields that were changed during this mutation. Note that in +// order to get all numeric fields that were incremented/decremented, call +// AddedFields(). +func (m *MessageMutation) Fields() []string { + fields := make([]string, 0, 13) + if m.project_id != nil { + fields = append(fields, message.FieldProjectID) + } + if m.sender != nil { + fields = append(fields, message.FieldSender) + } + if m.sender_id != nil { + fields = append(fields, message.FieldSenderID) + } + if m.recipient != nil { + fields = append(fields, message.FieldRecipient) + } + if m.recipient_id != nil { + fields = append(fields, message.FieldRecipientID) + } + if m.msg != nil { + fields = append(fields, message.FieldMsg) + } + if m._type != nil { + fields = append(fields, message.FieldType) + } + if m.urgent != nil { + fields = append(fields, message.FieldUrgent) + } + if m.broadcasted != nil { + fields = append(fields, message.FieldBroadcasted) + } + if m.read != nil { + fields = append(fields, message.FieldRead) + } + if m.agent_id != nil { + fields = append(fields, message.FieldAgentID) + } + if m.group_id != nil { + fields = append(fields, message.FieldGroupID) + } + if m.created != nil { + fields = append(fields, message.FieldCreated) + } + return fields +} + +// Field returns the value of a field with the given name. The second boolean +// return value indicates that this field was not set, or was not defined in the +// schema. +func (m *MessageMutation) Field(name string) (ent.Value, bool) { + switch name { + case message.FieldProjectID: + return m.ProjectID() + case message.FieldSender: + return m.Sender() + case message.FieldSenderID: + return m.SenderID() + case message.FieldRecipient: + return m.Recipient() + case message.FieldRecipientID: + return m.RecipientID() + case message.FieldMsg: + return m.Msg() + case message.FieldType: + return m.GetType() + case message.FieldUrgent: + return m.Urgent() + case message.FieldBroadcasted: + return m.Broadcasted() + case message.FieldRead: + return m.Read() + case message.FieldAgentID: + return m.AgentID() + case message.FieldGroupID: + return m.GroupID() + case message.FieldCreated: + return m.Created() + } + return nil, false +} + +// OldField returns the old value of the field from the database. An error is +// returned if the mutation operation is not UpdateOne, or the query to the +// database failed. +func (m *MessageMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + switch name { + case message.FieldProjectID: + return m.OldProjectID(ctx) + case message.FieldSender: + return m.OldSender(ctx) + case message.FieldSenderID: + return m.OldSenderID(ctx) + case message.FieldRecipient: + return m.OldRecipient(ctx) + case message.FieldRecipientID: + return m.OldRecipientID(ctx) + case message.FieldMsg: + return m.OldMsg(ctx) + case message.FieldType: + return m.OldType(ctx) + case message.FieldUrgent: + return m.OldUrgent(ctx) + case message.FieldBroadcasted: + return m.OldBroadcasted(ctx) + case message.FieldRead: + return m.OldRead(ctx) + case message.FieldAgentID: + return m.OldAgentID(ctx) + case message.FieldGroupID: + return m.OldGroupID(ctx) + case message.FieldCreated: + return m.OldCreated(ctx) + } + return nil, fmt.Errorf("unknown Message field %s", name) +} + +// SetField sets the value of a field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *MessageMutation) SetField(name string, value ent.Value) error { + switch name { + case message.FieldProjectID: + v, ok := value.(uuid.UUID) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetProjectID(v) + return nil + case message.FieldSender: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetSender(v) + return nil + case message.FieldSenderID: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetSenderID(v) + return nil + case message.FieldRecipient: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetRecipient(v) + return nil + case message.FieldRecipientID: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetRecipientID(v) + return nil + case message.FieldMsg: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetMsg(v) + return nil + case message.FieldType: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetType(v) + return nil + case message.FieldUrgent: + v, ok := value.(bool) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetUrgent(v) + return nil + case message.FieldBroadcasted: + v, ok := value.(bool) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetBroadcasted(v) + return nil + case message.FieldRead: + v, ok := value.(bool) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetRead(v) + return nil + case message.FieldAgentID: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetAgentID(v) + return nil + case message.FieldGroupID: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetGroupID(v) + return nil + case message.FieldCreated: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreated(v) + return nil + } + return fmt.Errorf("unknown Message field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *MessageMutation) AddedFields() []string { + return nil +} + +// AddedField returns the numeric value that was incremented/decremented on a field +// with the given name. The second boolean return value indicates that this field +// was not set, or was not defined in the schema. +func (m *MessageMutation) AddedField(name string) (ent.Value, bool) { + return nil, false +} + +// AddField adds the value to the field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *MessageMutation) AddField(name string, value ent.Value) error { + switch name { + } + return fmt.Errorf("unknown Message numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *MessageMutation) ClearedFields() []string { + var fields []string + if m.FieldCleared(message.FieldSenderID) { + fields = append(fields, message.FieldSenderID) + } + if m.FieldCleared(message.FieldRecipientID) { + fields = append(fields, message.FieldRecipientID) + } + if m.FieldCleared(message.FieldAgentID) { + fields = append(fields, message.FieldAgentID) + } + if m.FieldCleared(message.FieldGroupID) { + fields = append(fields, message.FieldGroupID) + } + return fields +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *MessageMutation) FieldCleared(name string) bool { + _, ok := m.clearedFields[name] + return ok +} + +// ClearField clears the value of the field with the given name. It returns an +// error if the field is not defined in the schema. +func (m *MessageMutation) ClearField(name string) error { + switch name { + case message.FieldSenderID: + m.ClearSenderID() + return nil + case message.FieldRecipientID: + m.ClearRecipientID() + return nil + case message.FieldAgentID: + m.ClearAgentID() + return nil + case message.FieldGroupID: + m.ClearGroupID() + return nil + } + return fmt.Errorf("unknown Message nullable field %s", name) +} + +// ResetField resets all changes in the mutation for the field with the given name. +// It returns an error if the field is not defined in the schema. +func (m *MessageMutation) ResetField(name string) error { + switch name { + case message.FieldProjectID: + m.ResetProjectID() + return nil + case message.FieldSender: + m.ResetSender() + return nil + case message.FieldSenderID: + m.ResetSenderID() + return nil + case message.FieldRecipient: + m.ResetRecipient() + return nil + case message.FieldRecipientID: + m.ResetRecipientID() + return nil + case message.FieldMsg: + m.ResetMsg() + return nil + case message.FieldType: + m.ResetType() + return nil + case message.FieldUrgent: + m.ResetUrgent() + return nil + case message.FieldBroadcasted: + m.ResetBroadcasted() + return nil + case message.FieldRead: + m.ResetRead() + return nil + case message.FieldAgentID: + m.ResetAgentID() + return nil + case message.FieldGroupID: + m.ResetGroupID() + return nil + case message.FieldCreated: + m.ResetCreated() + return nil + } + return fmt.Errorf("unknown Message field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *MessageMutation) AddedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *MessageMutation) AddedIDs(name string) []ent.Value { + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *MessageMutation) RemovedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *MessageMutation) RemovedIDs(name string) []ent.Value { + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *MessageMutation) ClearedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *MessageMutation) EdgeCleared(name string) bool { + return false +} + +// ClearEdge clears the value of the edge with the given name. It returns an error +// if that edge is not defined in the schema. +func (m *MessageMutation) ClearEdge(name string) error { + return fmt.Errorf("unknown Message unique edge %s", name) +} + +// ResetEdge resets all changes to the edge with the given name in this mutation. +// It returns an error if the edge is not defined in the schema. +func (m *MessageMutation) ResetEdge(name string) error { + return fmt.Errorf("unknown Message edge %s", name) +} + +// NotificationMutation represents an operation that mutates the Notification nodes in the graph. +type NotificationMutation struct { + config + op Op + typ string + id *uuid.UUID + subscription_id *uuid.UUID + agent_id *uuid.UUID + project_id *uuid.UUID + subscriber_type *string + subscriber_id *string + status *string + message *string + dispatched *bool + acknowledged *bool + created *time.Time + clearedFields map[string]struct{} + done bool + oldValue func(context.Context) (*Notification, error) + predicates []predicate.Notification +} + +var _ ent.Mutation = (*NotificationMutation)(nil) + +// notificationOption allows management of the mutation configuration using functional options. +type notificationOption func(*NotificationMutation) + +// newNotificationMutation creates new mutation for the Notification entity. +func newNotificationMutation(c config, op Op, opts ...notificationOption) *NotificationMutation { + m := &NotificationMutation{ + config: c, + op: op, + typ: TypeNotification, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// withNotificationID sets the ID field of the mutation. +func withNotificationID(id uuid.UUID) notificationOption { + return func(m *NotificationMutation) { + var ( + err error + once sync.Once + value *Notification + ) + m.oldValue = func(ctx context.Context) (*Notification, error) { + once.Do(func() { + if m.done { + err = errors.New("querying old values post mutation is not allowed") + } else { + value, err = m.Client().Notification.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } +} + +// withNotification sets the old Notification of the mutation. +func withNotification(node *Notification) notificationOption { + return func(m *NotificationMutation) { + m.oldValue = func(context.Context) (*Notification, error) { + return node, nil + } + m.id = &node.ID + } +} + +// Client returns a new `ent.Client` from the mutation. If the mutation was +// executed in a transaction (ent.Tx), a transactional client is returned. +func (m NotificationMutation) Client() *Client { + client := &Client{config: m.config} + client.init() + return client +} + +// Tx returns an `ent.Tx` for mutations that were executed in transactions; +// it returns an error otherwise. +func (m NotificationMutation) Tx() (*Tx, error) { + if _, ok := m.driver.(*txDriver); !ok { + return nil, errors.New("ent: mutation is not running in a transaction") + } + tx := &Tx{config: m.config} + tx.init() + return tx, nil +} + +// SetID sets the value of the id field. Note that this +// operation is only accepted on creation of Notification entities. +func (m *NotificationMutation) SetID(id uuid.UUID) { + m.id = &id +} + +// ID returns the ID value in the mutation. Note that the ID is only available +// if it was provided to the builder or after it was returned from the database. +func (m *NotificationMutation) ID() (id uuid.UUID, exists bool) { + if m.id == nil { + return + } + return *m.id, true +} + +// IDs queries the database and returns the entity ids that match the mutation's predicate. +// That means, if the mutation is applied within a transaction with an isolation level such +// as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated +// or updated by the mutation. +func (m *NotificationMutation) IDs(ctx context.Context) ([]uuid.UUID, error) { + switch { + case m.op.Is(OpUpdateOne | OpDeleteOne): + id, exists := m.ID() + if exists { + return []uuid.UUID{id}, nil + } + fallthrough + case m.op.Is(OpUpdate | OpDelete): + return m.Client().Notification.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } +} + +// SetSubscriptionID sets the "subscription_id" field. +func (m *NotificationMutation) SetSubscriptionID(u uuid.UUID) { + m.subscription_id = &u +} + +// SubscriptionID returns the value of the "subscription_id" field in the mutation. +func (m *NotificationMutation) SubscriptionID() (r uuid.UUID, exists bool) { + v := m.subscription_id + if v == nil { + return + } + return *v, true +} + +// OldSubscriptionID returns the old "subscription_id" field's value of the Notification entity. +// If the Notification object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *NotificationMutation) OldSubscriptionID(ctx context.Context) (v uuid.UUID, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldSubscriptionID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldSubscriptionID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldSubscriptionID: %w", err) + } + return oldValue.SubscriptionID, nil +} + +// ResetSubscriptionID resets all changes to the "subscription_id" field. +func (m *NotificationMutation) ResetSubscriptionID() { + m.subscription_id = nil +} + +// SetAgentID sets the "agent_id" field. +func (m *NotificationMutation) SetAgentID(u uuid.UUID) { + m.agent_id = &u +} + +// AgentID returns the value of the "agent_id" field in the mutation. +func (m *NotificationMutation) AgentID() (r uuid.UUID, exists bool) { + v := m.agent_id + if v == nil { + return + } + return *v, true +} + +// OldAgentID returns the old "agent_id" field's value of the Notification entity. +// If the Notification object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *NotificationMutation) OldAgentID(ctx context.Context) (v uuid.UUID, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldAgentID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldAgentID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldAgentID: %w", err) + } + return oldValue.AgentID, nil +} + +// ResetAgentID resets all changes to the "agent_id" field. +func (m *NotificationMutation) ResetAgentID() { + m.agent_id = nil +} + +// SetProjectID sets the "project_id" field. +func (m *NotificationMutation) SetProjectID(u uuid.UUID) { + m.project_id = &u +} + +// ProjectID returns the value of the "project_id" field in the mutation. +func (m *NotificationMutation) ProjectID() (r uuid.UUID, exists bool) { + v := m.project_id + if v == nil { + return + } + return *v, true +} + +// OldProjectID returns the old "project_id" field's value of the Notification entity. +// If the Notification object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *NotificationMutation) OldProjectID(ctx context.Context) (v uuid.UUID, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldProjectID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldProjectID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldProjectID: %w", err) + } + return oldValue.ProjectID, nil +} + +// ResetProjectID resets all changes to the "project_id" field. +func (m *NotificationMutation) ResetProjectID() { + m.project_id = nil +} + +// SetSubscriberType sets the "subscriber_type" field. +func (m *NotificationMutation) SetSubscriberType(s string) { + m.subscriber_type = &s +} + +// SubscriberType returns the value of the "subscriber_type" field in the mutation. +func (m *NotificationMutation) SubscriberType() (r string, exists bool) { + v := m.subscriber_type + if v == nil { + return + } + return *v, true +} + +// OldSubscriberType returns the old "subscriber_type" field's value of the Notification entity. +// If the Notification object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *NotificationMutation) OldSubscriberType(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldSubscriberType is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldSubscriberType requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldSubscriberType: %w", err) + } + return oldValue.SubscriberType, nil +} + +// ResetSubscriberType resets all changes to the "subscriber_type" field. +func (m *NotificationMutation) ResetSubscriberType() { + m.subscriber_type = nil +} + +// SetSubscriberID sets the "subscriber_id" field. +func (m *NotificationMutation) SetSubscriberID(s string) { + m.subscriber_id = &s +} + +// SubscriberID returns the value of the "subscriber_id" field in the mutation. +func (m *NotificationMutation) SubscriberID() (r string, exists bool) { + v := m.subscriber_id + if v == nil { + return + } + return *v, true +} + +// OldSubscriberID returns the old "subscriber_id" field's value of the Notification entity. +// If the Notification object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *NotificationMutation) OldSubscriberID(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldSubscriberID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldSubscriberID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldSubscriberID: %w", err) + } + return oldValue.SubscriberID, nil +} + +// ResetSubscriberID resets all changes to the "subscriber_id" field. +func (m *NotificationMutation) ResetSubscriberID() { + m.subscriber_id = nil +} + +// SetStatus sets the "status" field. +func (m *NotificationMutation) SetStatus(s string) { + m.status = &s +} + +// Status returns the value of the "status" field in the mutation. +func (m *NotificationMutation) Status() (r string, exists bool) { + v := m.status + if v == nil { + return + } + return *v, true +} + +// OldStatus returns the old "status" field's value of the Notification entity. +// If the Notification object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *NotificationMutation) OldStatus(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldStatus is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldStatus requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldStatus: %w", err) + } + return oldValue.Status, nil +} + +// ResetStatus resets all changes to the "status" field. +func (m *NotificationMutation) ResetStatus() { + m.status = nil +} + +// SetMessage sets the "message" field. +func (m *NotificationMutation) SetMessage(s string) { + m.message = &s +} + +// Message returns the value of the "message" field in the mutation. +func (m *NotificationMutation) Message() (r string, exists bool) { + v := m.message + if v == nil { + return + } + return *v, true +} + +// OldMessage returns the old "message" field's value of the Notification entity. +// If the Notification object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *NotificationMutation) OldMessage(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldMessage is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldMessage requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldMessage: %w", err) + } + return oldValue.Message, nil +} + +// ResetMessage resets all changes to the "message" field. +func (m *NotificationMutation) ResetMessage() { + m.message = nil +} + +// SetDispatched sets the "dispatched" field. +func (m *NotificationMutation) SetDispatched(b bool) { + m.dispatched = &b +} + +// Dispatched returns the value of the "dispatched" field in the mutation. +func (m *NotificationMutation) Dispatched() (r bool, exists bool) { + v := m.dispatched + if v == nil { + return + } + return *v, true +} + +// OldDispatched returns the old "dispatched" field's value of the Notification entity. +// If the Notification object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *NotificationMutation) OldDispatched(ctx context.Context) (v bool, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldDispatched is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldDispatched requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldDispatched: %w", err) + } + return oldValue.Dispatched, nil +} + +// ResetDispatched resets all changes to the "dispatched" field. +func (m *NotificationMutation) ResetDispatched() { + m.dispatched = nil +} + +// SetAcknowledged sets the "acknowledged" field. +func (m *NotificationMutation) SetAcknowledged(b bool) { + m.acknowledged = &b +} + +// Acknowledged returns the value of the "acknowledged" field in the mutation. +func (m *NotificationMutation) Acknowledged() (r bool, exists bool) { + v := m.acknowledged + if v == nil { + return + } + return *v, true +} + +// OldAcknowledged returns the old "acknowledged" field's value of the Notification entity. +// If the Notification object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *NotificationMutation) OldAcknowledged(ctx context.Context) (v bool, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldAcknowledged is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldAcknowledged requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldAcknowledged: %w", err) + } + return oldValue.Acknowledged, nil +} + +// ResetAcknowledged resets all changes to the "acknowledged" field. +func (m *NotificationMutation) ResetAcknowledged() { + m.acknowledged = nil +} + +// SetCreated sets the "created" field. +func (m *NotificationMutation) SetCreated(t time.Time) { + m.created = &t +} + +// Created returns the value of the "created" field in the mutation. +func (m *NotificationMutation) Created() (r time.Time, exists bool) { + v := m.created + if v == nil { + return + } + return *v, true +} + +// OldCreated returns the old "created" field's value of the Notification entity. +// If the Notification object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *NotificationMutation) OldCreated(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreated is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreated requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreated: %w", err) + } + return oldValue.Created, nil +} + +// ResetCreated resets all changes to the "created" field. +func (m *NotificationMutation) ResetCreated() { + m.created = nil +} + +// Where appends a list predicates to the NotificationMutation builder. +func (m *NotificationMutation) Where(ps ...predicate.Notification) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the NotificationMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *NotificationMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.Notification, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *NotificationMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *NotificationMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (Notification). +func (m *NotificationMutation) Type() string { + return m.typ +} + +// Fields returns all fields that were changed during this mutation. Note that in +// order to get all numeric fields that were incremented/decremented, call +// AddedFields(). +func (m *NotificationMutation) Fields() []string { + fields := make([]string, 0, 10) + if m.subscription_id != nil { + fields = append(fields, notification.FieldSubscriptionID) + } + if m.agent_id != nil { + fields = append(fields, notification.FieldAgentID) + } + if m.project_id != nil { + fields = append(fields, notification.FieldProjectID) + } + if m.subscriber_type != nil { + fields = append(fields, notification.FieldSubscriberType) + } + if m.subscriber_id != nil { + fields = append(fields, notification.FieldSubscriberID) + } + if m.status != nil { + fields = append(fields, notification.FieldStatus) + } + if m.message != nil { + fields = append(fields, notification.FieldMessage) + } + if m.dispatched != nil { + fields = append(fields, notification.FieldDispatched) + } + if m.acknowledged != nil { + fields = append(fields, notification.FieldAcknowledged) + } + if m.created != nil { + fields = append(fields, notification.FieldCreated) + } + return fields +} + +// Field returns the value of a field with the given name. The second boolean +// return value indicates that this field was not set, or was not defined in the +// schema. +func (m *NotificationMutation) Field(name string) (ent.Value, bool) { + switch name { + case notification.FieldSubscriptionID: + return m.SubscriptionID() + case notification.FieldAgentID: + return m.AgentID() + case notification.FieldProjectID: + return m.ProjectID() + case notification.FieldSubscriberType: + return m.SubscriberType() + case notification.FieldSubscriberID: + return m.SubscriberID() + case notification.FieldStatus: + return m.Status() + case notification.FieldMessage: + return m.Message() + case notification.FieldDispatched: + return m.Dispatched() + case notification.FieldAcknowledged: + return m.Acknowledged() + case notification.FieldCreated: + return m.Created() + } + return nil, false +} + +// OldField returns the old value of the field from the database. An error is +// returned if the mutation operation is not UpdateOne, or the query to the +// database failed. +func (m *NotificationMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + switch name { + case notification.FieldSubscriptionID: + return m.OldSubscriptionID(ctx) + case notification.FieldAgentID: + return m.OldAgentID(ctx) + case notification.FieldProjectID: + return m.OldProjectID(ctx) + case notification.FieldSubscriberType: + return m.OldSubscriberType(ctx) + case notification.FieldSubscriberID: + return m.OldSubscriberID(ctx) + case notification.FieldStatus: + return m.OldStatus(ctx) + case notification.FieldMessage: + return m.OldMessage(ctx) + case notification.FieldDispatched: + return m.OldDispatched(ctx) + case notification.FieldAcknowledged: + return m.OldAcknowledged(ctx) + case notification.FieldCreated: + return m.OldCreated(ctx) + } + return nil, fmt.Errorf("unknown Notification field %s", name) +} + +// SetField sets the value of a field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *NotificationMutation) SetField(name string, value ent.Value) error { + switch name { + case notification.FieldSubscriptionID: + v, ok := value.(uuid.UUID) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetSubscriptionID(v) + return nil + case notification.FieldAgentID: + v, ok := value.(uuid.UUID) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetAgentID(v) + return nil + case notification.FieldProjectID: + v, ok := value.(uuid.UUID) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetProjectID(v) + return nil + case notification.FieldSubscriberType: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetSubscriberType(v) + return nil + case notification.FieldSubscriberID: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetSubscriberID(v) + return nil + case notification.FieldStatus: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetStatus(v) + return nil + case notification.FieldMessage: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetMessage(v) + return nil + case notification.FieldDispatched: + v, ok := value.(bool) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetDispatched(v) + return nil + case notification.FieldAcknowledged: + v, ok := value.(bool) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetAcknowledged(v) + return nil + case notification.FieldCreated: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreated(v) + return nil + } + return fmt.Errorf("unknown Notification field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *NotificationMutation) AddedFields() []string { + return nil +} + +// AddedField returns the numeric value that was incremented/decremented on a field +// with the given name. The second boolean return value indicates that this field +// was not set, or was not defined in the schema. +func (m *NotificationMutation) AddedField(name string) (ent.Value, bool) { + return nil, false +} + +// AddField adds the value to the field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *NotificationMutation) AddField(name string, value ent.Value) error { + switch name { + } + return fmt.Errorf("unknown Notification numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *NotificationMutation) ClearedFields() []string { + return nil +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *NotificationMutation) FieldCleared(name string) bool { + _, ok := m.clearedFields[name] + return ok +} + +// ClearField clears the value of the field with the given name. It returns an +// error if the field is not defined in the schema. +func (m *NotificationMutation) ClearField(name string) error { + return fmt.Errorf("unknown Notification nullable field %s", name) +} + +// ResetField resets all changes in the mutation for the field with the given name. +// It returns an error if the field is not defined in the schema. +func (m *NotificationMutation) ResetField(name string) error { + switch name { + case notification.FieldSubscriptionID: + m.ResetSubscriptionID() + return nil + case notification.FieldAgentID: + m.ResetAgentID() + return nil + case notification.FieldProjectID: + m.ResetProjectID() + return nil + case notification.FieldSubscriberType: + m.ResetSubscriberType() + return nil + case notification.FieldSubscriberID: + m.ResetSubscriberID() + return nil + case notification.FieldStatus: + m.ResetStatus() + return nil + case notification.FieldMessage: + m.ResetMessage() + return nil + case notification.FieldDispatched: + m.ResetDispatched() + return nil + case notification.FieldAcknowledged: + m.ResetAcknowledged() + return nil + case notification.FieldCreated: + m.ResetCreated() + return nil + } + return fmt.Errorf("unknown Notification field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *NotificationMutation) AddedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *NotificationMutation) AddedIDs(name string) []ent.Value { + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *NotificationMutation) RemovedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *NotificationMutation) RemovedIDs(name string) []ent.Value { + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *NotificationMutation) ClearedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *NotificationMutation) EdgeCleared(name string) bool { + return false +} + +// ClearEdge clears the value of the edge with the given name. It returns an error +// if that edge is not defined in the schema. +func (m *NotificationMutation) ClearEdge(name string) error { + return fmt.Errorf("unknown Notification unique edge %s", name) +} + +// ResetEdge resets all changes to the edge with the given name in this mutation. +// It returns an error if the edge is not defined in the schema. +func (m *NotificationMutation) ResetEdge(name string) error { + return fmt.Errorf("unknown Notification edge %s", name) +} + +// NotificationSubscriptionMutation represents an operation that mutates the NotificationSubscription nodes in the graph. +type NotificationSubscriptionMutation struct { + config + op Op + typ string + id *uuid.UUID + scope *string + agent_id *uuid.UUID + subscriber_type *string + subscriber_id *string + project_id *uuid.UUID + trigger_activities *string + created_by *string + created *time.Time + clearedFields map[string]struct{} + done bool + oldValue func(context.Context) (*NotificationSubscription, error) + predicates []predicate.NotificationSubscription +} + +var _ ent.Mutation = (*NotificationSubscriptionMutation)(nil) + +// notificationsubscriptionOption allows management of the mutation configuration using functional options. +type notificationsubscriptionOption func(*NotificationSubscriptionMutation) + +// newNotificationSubscriptionMutation creates new mutation for the NotificationSubscription entity. +func newNotificationSubscriptionMutation(c config, op Op, opts ...notificationsubscriptionOption) *NotificationSubscriptionMutation { + m := &NotificationSubscriptionMutation{ + config: c, + op: op, + typ: TypeNotificationSubscription, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// withNotificationSubscriptionID sets the ID field of the mutation. +func withNotificationSubscriptionID(id uuid.UUID) notificationsubscriptionOption { + return func(m *NotificationSubscriptionMutation) { + var ( + err error + once sync.Once + value *NotificationSubscription + ) + m.oldValue = func(ctx context.Context) (*NotificationSubscription, error) { + once.Do(func() { + if m.done { + err = errors.New("querying old values post mutation is not allowed") + } else { + value, err = m.Client().NotificationSubscription.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } +} + +// withNotificationSubscription sets the old NotificationSubscription of the mutation. +func withNotificationSubscription(node *NotificationSubscription) notificationsubscriptionOption { + return func(m *NotificationSubscriptionMutation) { + m.oldValue = func(context.Context) (*NotificationSubscription, error) { + return node, nil + } + m.id = &node.ID + } +} + +// Client returns a new `ent.Client` from the mutation. If the mutation was +// executed in a transaction (ent.Tx), a transactional client is returned. +func (m NotificationSubscriptionMutation) Client() *Client { + client := &Client{config: m.config} + client.init() + return client +} + +// Tx returns an `ent.Tx` for mutations that were executed in transactions; +// it returns an error otherwise. +func (m NotificationSubscriptionMutation) Tx() (*Tx, error) { + if _, ok := m.driver.(*txDriver); !ok { + return nil, errors.New("ent: mutation is not running in a transaction") + } + tx := &Tx{config: m.config} + tx.init() + return tx, nil +} + +// SetID sets the value of the id field. Note that this +// operation is only accepted on creation of NotificationSubscription entities. +func (m *NotificationSubscriptionMutation) SetID(id uuid.UUID) { + m.id = &id +} + +// ID returns the ID value in the mutation. Note that the ID is only available +// if it was provided to the builder or after it was returned from the database. +func (m *NotificationSubscriptionMutation) ID() (id uuid.UUID, exists bool) { + if m.id == nil { + return + } + return *m.id, true +} + +// IDs queries the database and returns the entity ids that match the mutation's predicate. +// That means, if the mutation is applied within a transaction with an isolation level such +// as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated +// or updated by the mutation. +func (m *NotificationSubscriptionMutation) IDs(ctx context.Context) ([]uuid.UUID, error) { + switch { + case m.op.Is(OpUpdateOne | OpDeleteOne): + id, exists := m.ID() + if exists { + return []uuid.UUID{id}, nil + } + fallthrough + case m.op.Is(OpUpdate | OpDelete): + return m.Client().NotificationSubscription.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } +} + +// SetScope sets the "scope" field. +func (m *NotificationSubscriptionMutation) SetScope(s string) { + m.scope = &s +} + +// Scope returns the value of the "scope" field in the mutation. +func (m *NotificationSubscriptionMutation) Scope() (r string, exists bool) { + v := m.scope + if v == nil { + return + } + return *v, true +} + +// OldScope returns the old "scope" field's value of the NotificationSubscription entity. +// If the NotificationSubscription object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *NotificationSubscriptionMutation) OldScope(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldScope is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldScope requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldScope: %w", err) + } + return oldValue.Scope, nil +} + +// ResetScope resets all changes to the "scope" field. +func (m *NotificationSubscriptionMutation) ResetScope() { + m.scope = nil +} + +// SetAgentID sets the "agent_id" field. +func (m *NotificationSubscriptionMutation) SetAgentID(u uuid.UUID) { + m.agent_id = &u +} + +// AgentID returns the value of the "agent_id" field in the mutation. +func (m *NotificationSubscriptionMutation) AgentID() (r uuid.UUID, exists bool) { + v := m.agent_id + if v == nil { + return + } + return *v, true +} + +// OldAgentID returns the old "agent_id" field's value of the NotificationSubscription entity. +// If the NotificationSubscription object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *NotificationSubscriptionMutation) OldAgentID(ctx context.Context) (v *uuid.UUID, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldAgentID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldAgentID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldAgentID: %w", err) + } + return oldValue.AgentID, nil +} + +// ClearAgentID clears the value of the "agent_id" field. +func (m *NotificationSubscriptionMutation) ClearAgentID() { + m.agent_id = nil + m.clearedFields[notificationsubscription.FieldAgentID] = struct{}{} +} + +// AgentIDCleared returns if the "agent_id" field was cleared in this mutation. +func (m *NotificationSubscriptionMutation) AgentIDCleared() bool { + _, ok := m.clearedFields[notificationsubscription.FieldAgentID] + return ok +} + +// ResetAgentID resets all changes to the "agent_id" field. +func (m *NotificationSubscriptionMutation) ResetAgentID() { + m.agent_id = nil + delete(m.clearedFields, notificationsubscription.FieldAgentID) +} + +// SetSubscriberType sets the "subscriber_type" field. +func (m *NotificationSubscriptionMutation) SetSubscriberType(s string) { + m.subscriber_type = &s +} + +// SubscriberType returns the value of the "subscriber_type" field in the mutation. +func (m *NotificationSubscriptionMutation) SubscriberType() (r string, exists bool) { + v := m.subscriber_type + if v == nil { + return + } + return *v, true +} + +// OldSubscriberType returns the old "subscriber_type" field's value of the NotificationSubscription entity. +// If the NotificationSubscription object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *NotificationSubscriptionMutation) OldSubscriberType(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldSubscriberType is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldSubscriberType requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldSubscriberType: %w", err) + } + return oldValue.SubscriberType, nil +} + +// ResetSubscriberType resets all changes to the "subscriber_type" field. +func (m *NotificationSubscriptionMutation) ResetSubscriberType() { + m.subscriber_type = nil +} + +// SetSubscriberID sets the "subscriber_id" field. +func (m *NotificationSubscriptionMutation) SetSubscriberID(s string) { + m.subscriber_id = &s +} + +// SubscriberID returns the value of the "subscriber_id" field in the mutation. +func (m *NotificationSubscriptionMutation) SubscriberID() (r string, exists bool) { + v := m.subscriber_id + if v == nil { + return + } + return *v, true +} + +// OldSubscriberID returns the old "subscriber_id" field's value of the NotificationSubscription entity. +// If the NotificationSubscription object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *NotificationSubscriptionMutation) OldSubscriberID(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldSubscriberID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldSubscriberID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldSubscriberID: %w", err) + } + return oldValue.SubscriberID, nil +} + +// ResetSubscriberID resets all changes to the "subscriber_id" field. +func (m *NotificationSubscriptionMutation) ResetSubscriberID() { + m.subscriber_id = nil +} + +// SetProjectID sets the "project_id" field. +func (m *NotificationSubscriptionMutation) SetProjectID(u uuid.UUID) { + m.project_id = &u +} + +// ProjectID returns the value of the "project_id" field in the mutation. +func (m *NotificationSubscriptionMutation) ProjectID() (r uuid.UUID, exists bool) { + v := m.project_id + if v == nil { + return + } + return *v, true +} + +// OldProjectID returns the old "project_id" field's value of the NotificationSubscription entity. +// If the NotificationSubscription object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *NotificationSubscriptionMutation) OldProjectID(ctx context.Context) (v uuid.UUID, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldProjectID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldProjectID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldProjectID: %w", err) + } + return oldValue.ProjectID, nil +} + +// ResetProjectID resets all changes to the "project_id" field. +func (m *NotificationSubscriptionMutation) ResetProjectID() { + m.project_id = nil +} + +// SetTriggerActivities sets the "trigger_activities" field. +func (m *NotificationSubscriptionMutation) SetTriggerActivities(s string) { + m.trigger_activities = &s +} + +// TriggerActivities returns the value of the "trigger_activities" field in the mutation. +func (m *NotificationSubscriptionMutation) TriggerActivities() (r string, exists bool) { + v := m.trigger_activities + if v == nil { + return + } + return *v, true +} + +// OldTriggerActivities returns the old "trigger_activities" field's value of the NotificationSubscription entity. +// If the NotificationSubscription object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *NotificationSubscriptionMutation) OldTriggerActivities(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldTriggerActivities is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldTriggerActivities requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldTriggerActivities: %w", err) + } + return oldValue.TriggerActivities, nil +} + +// ResetTriggerActivities resets all changes to the "trigger_activities" field. +func (m *NotificationSubscriptionMutation) ResetTriggerActivities() { + m.trigger_activities = nil +} + +// SetCreatedBy sets the "created_by" field. +func (m *NotificationSubscriptionMutation) SetCreatedBy(s string) { + m.created_by = &s +} + +// CreatedBy returns the value of the "created_by" field in the mutation. +func (m *NotificationSubscriptionMutation) CreatedBy() (r string, exists bool) { + v := m.created_by + if v == nil { + return + } + return *v, true +} + +// OldCreatedBy returns the old "created_by" field's value of the NotificationSubscription entity. +// If the NotificationSubscription object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *NotificationSubscriptionMutation) OldCreatedBy(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreatedBy is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreatedBy requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreatedBy: %w", err) + } + return oldValue.CreatedBy, nil +} + +// ResetCreatedBy resets all changes to the "created_by" field. +func (m *NotificationSubscriptionMutation) ResetCreatedBy() { + m.created_by = nil +} + +// SetCreated sets the "created" field. +func (m *NotificationSubscriptionMutation) SetCreated(t time.Time) { + m.created = &t +} + +// Created returns the value of the "created" field in the mutation. +func (m *NotificationSubscriptionMutation) Created() (r time.Time, exists bool) { + v := m.created + if v == nil { + return + } + return *v, true +} + +// OldCreated returns the old "created" field's value of the NotificationSubscription entity. +// If the NotificationSubscription object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *NotificationSubscriptionMutation) OldCreated(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreated is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreated requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreated: %w", err) + } + return oldValue.Created, nil +} + +// ResetCreated resets all changes to the "created" field. +func (m *NotificationSubscriptionMutation) ResetCreated() { + m.created = nil +} + +// Where appends a list predicates to the NotificationSubscriptionMutation builder. +func (m *NotificationSubscriptionMutation) Where(ps ...predicate.NotificationSubscription) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the NotificationSubscriptionMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *NotificationSubscriptionMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.NotificationSubscription, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *NotificationSubscriptionMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *NotificationSubscriptionMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (NotificationSubscription). +func (m *NotificationSubscriptionMutation) Type() string { + return m.typ +} + +// Fields returns all fields that were changed during this mutation. Note that in +// order to get all numeric fields that were incremented/decremented, call +// AddedFields(). +func (m *NotificationSubscriptionMutation) Fields() []string { + fields := make([]string, 0, 8) + if m.scope != nil { + fields = append(fields, notificationsubscription.FieldScope) + } + if m.agent_id != nil { + fields = append(fields, notificationsubscription.FieldAgentID) + } + if m.subscriber_type != nil { + fields = append(fields, notificationsubscription.FieldSubscriberType) + } + if m.subscriber_id != nil { + fields = append(fields, notificationsubscription.FieldSubscriberID) + } + if m.project_id != nil { + fields = append(fields, notificationsubscription.FieldProjectID) + } + if m.trigger_activities != nil { + fields = append(fields, notificationsubscription.FieldTriggerActivities) + } + if m.created_by != nil { + fields = append(fields, notificationsubscription.FieldCreatedBy) + } + if m.created != nil { + fields = append(fields, notificationsubscription.FieldCreated) + } + return fields +} + +// Field returns the value of a field with the given name. The second boolean +// return value indicates that this field was not set, or was not defined in the +// schema. +func (m *NotificationSubscriptionMutation) Field(name string) (ent.Value, bool) { + switch name { + case notificationsubscription.FieldScope: + return m.Scope() + case notificationsubscription.FieldAgentID: + return m.AgentID() + case notificationsubscription.FieldSubscriberType: + return m.SubscriberType() + case notificationsubscription.FieldSubscriberID: + return m.SubscriberID() + case notificationsubscription.FieldProjectID: + return m.ProjectID() + case notificationsubscription.FieldTriggerActivities: + return m.TriggerActivities() + case notificationsubscription.FieldCreatedBy: + return m.CreatedBy() + case notificationsubscription.FieldCreated: + return m.Created() + } + return nil, false +} + +// OldField returns the old value of the field from the database. An error is +// returned if the mutation operation is not UpdateOne, or the query to the +// database failed. +func (m *NotificationSubscriptionMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + switch name { + case notificationsubscription.FieldScope: + return m.OldScope(ctx) + case notificationsubscription.FieldAgentID: + return m.OldAgentID(ctx) + case notificationsubscription.FieldSubscriberType: + return m.OldSubscriberType(ctx) + case notificationsubscription.FieldSubscriberID: + return m.OldSubscriberID(ctx) + case notificationsubscription.FieldProjectID: + return m.OldProjectID(ctx) + case notificationsubscription.FieldTriggerActivities: + return m.OldTriggerActivities(ctx) + case notificationsubscription.FieldCreatedBy: + return m.OldCreatedBy(ctx) + case notificationsubscription.FieldCreated: + return m.OldCreated(ctx) + } + return nil, fmt.Errorf("unknown NotificationSubscription field %s", name) +} + +// SetField sets the value of a field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *NotificationSubscriptionMutation) SetField(name string, value ent.Value) error { + switch name { + case notificationsubscription.FieldScope: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetScope(v) + return nil + case notificationsubscription.FieldAgentID: + v, ok := value.(uuid.UUID) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetAgentID(v) + return nil + case notificationsubscription.FieldSubscriberType: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetSubscriberType(v) + return nil + case notificationsubscription.FieldSubscriberID: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetSubscriberID(v) + return nil + case notificationsubscription.FieldProjectID: + v, ok := value.(uuid.UUID) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetProjectID(v) + return nil + case notificationsubscription.FieldTriggerActivities: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetTriggerActivities(v) + return nil + case notificationsubscription.FieldCreatedBy: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreatedBy(v) + return nil + case notificationsubscription.FieldCreated: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreated(v) + return nil + } + return fmt.Errorf("unknown NotificationSubscription field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *NotificationSubscriptionMutation) AddedFields() []string { + return nil +} + +// AddedField returns the numeric value that was incremented/decremented on a field +// with the given name. The second boolean return value indicates that this field +// was not set, or was not defined in the schema. +func (m *NotificationSubscriptionMutation) AddedField(name string) (ent.Value, bool) { + return nil, false +} + +// AddField adds the value to the field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *NotificationSubscriptionMutation) AddField(name string, value ent.Value) error { + switch name { + } + return fmt.Errorf("unknown NotificationSubscription numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *NotificationSubscriptionMutation) ClearedFields() []string { + var fields []string + if m.FieldCleared(notificationsubscription.FieldAgentID) { + fields = append(fields, notificationsubscription.FieldAgentID) + } + return fields +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *NotificationSubscriptionMutation) FieldCleared(name string) bool { + _, ok := m.clearedFields[name] + return ok +} + +// ClearField clears the value of the field with the given name. It returns an +// error if the field is not defined in the schema. +func (m *NotificationSubscriptionMutation) ClearField(name string) error { + switch name { + case notificationsubscription.FieldAgentID: + m.ClearAgentID() + return nil + } + return fmt.Errorf("unknown NotificationSubscription nullable field %s", name) +} + +// ResetField resets all changes in the mutation for the field with the given name. +// It returns an error if the field is not defined in the schema. +func (m *NotificationSubscriptionMutation) ResetField(name string) error { + switch name { + case notificationsubscription.FieldScope: + m.ResetScope() + return nil + case notificationsubscription.FieldAgentID: + m.ResetAgentID() + return nil + case notificationsubscription.FieldSubscriberType: + m.ResetSubscriberType() + return nil + case notificationsubscription.FieldSubscriberID: + m.ResetSubscriberID() + return nil + case notificationsubscription.FieldProjectID: + m.ResetProjectID() + return nil + case notificationsubscription.FieldTriggerActivities: + m.ResetTriggerActivities() + return nil + case notificationsubscription.FieldCreatedBy: + m.ResetCreatedBy() + return nil + case notificationsubscription.FieldCreated: + m.ResetCreated() + return nil + } + return fmt.Errorf("unknown NotificationSubscription field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *NotificationSubscriptionMutation) AddedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *NotificationSubscriptionMutation) AddedIDs(name string) []ent.Value { + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *NotificationSubscriptionMutation) RemovedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *NotificationSubscriptionMutation) RemovedIDs(name string) []ent.Value { + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *NotificationSubscriptionMutation) ClearedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *NotificationSubscriptionMutation) EdgeCleared(name string) bool { + return false +} + +// ClearEdge clears the value of the edge with the given name. It returns an error +// if that edge is not defined in the schema. +func (m *NotificationSubscriptionMutation) ClearEdge(name string) error { + return fmt.Errorf("unknown NotificationSubscription unique edge %s", name) +} + +// ResetEdge resets all changes to the edge with the given name in this mutation. +// It returns an error if the edge is not defined in the schema. +func (m *NotificationSubscriptionMutation) ResetEdge(name string) error { + return fmt.Errorf("unknown NotificationSubscription edge %s", name) +} + +// PolicyBindingMutation represents an operation that mutates the PolicyBinding nodes in the graph. +type PolicyBindingMutation struct { + config + op Op + typ string + id *uuid.UUID + principal_type *policybinding.PrincipalType + created *time.Time + created_by *string + clearedFields map[string]struct{} + policy *uuid.UUID + clearedpolicy bool + user *uuid.UUID + cleareduser bool + group *uuid.UUID + clearedgroup bool + agent *uuid.UUID + clearedagent bool + done bool + oldValue func(context.Context) (*PolicyBinding, error) + predicates []predicate.PolicyBinding +} + +var _ ent.Mutation = (*PolicyBindingMutation)(nil) + +// policybindingOption allows management of the mutation configuration using functional options. +type policybindingOption func(*PolicyBindingMutation) + +// newPolicyBindingMutation creates new mutation for the PolicyBinding entity. +func newPolicyBindingMutation(c config, op Op, opts ...policybindingOption) *PolicyBindingMutation { + m := &PolicyBindingMutation{ + config: c, + op: op, + typ: TypePolicyBinding, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// withPolicyBindingID sets the ID field of the mutation. +func withPolicyBindingID(id uuid.UUID) policybindingOption { + return func(m *PolicyBindingMutation) { + var ( + err error + once sync.Once + value *PolicyBinding + ) + m.oldValue = func(ctx context.Context) (*PolicyBinding, error) { + once.Do(func() { + if m.done { + err = errors.New("querying old values post mutation is not allowed") + } else { + value, err = m.Client().PolicyBinding.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } +} + +// withPolicyBinding sets the old PolicyBinding of the mutation. +func withPolicyBinding(node *PolicyBinding) policybindingOption { + return func(m *PolicyBindingMutation) { + m.oldValue = func(context.Context) (*PolicyBinding, error) { + return node, nil + } + m.id = &node.ID + } +} + +// Client returns a new `ent.Client` from the mutation. If the mutation was +// executed in a transaction (ent.Tx), a transactional client is returned. +func (m PolicyBindingMutation) Client() *Client { + client := &Client{config: m.config} + client.init() + return client +} + +// Tx returns an `ent.Tx` for mutations that were executed in transactions; +// it returns an error otherwise. +func (m PolicyBindingMutation) Tx() (*Tx, error) { + if _, ok := m.driver.(*txDriver); !ok { + return nil, errors.New("ent: mutation is not running in a transaction") + } + tx := &Tx{config: m.config} + tx.init() + return tx, nil +} + +// SetID sets the value of the id field. Note that this +// operation is only accepted on creation of PolicyBinding entities. +func (m *PolicyBindingMutation) SetID(id uuid.UUID) { + m.id = &id +} + +// ID returns the ID value in the mutation. Note that the ID is only available +// if it was provided to the builder or after it was returned from the database. +func (m *PolicyBindingMutation) ID() (id uuid.UUID, exists bool) { + if m.id == nil { + return + } + return *m.id, true +} + +// IDs queries the database and returns the entity ids that match the mutation's predicate. +// That means, if the mutation is applied within a transaction with an isolation level such +// as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated +// or updated by the mutation. +func (m *PolicyBindingMutation) IDs(ctx context.Context) ([]uuid.UUID, error) { + switch { + case m.op.Is(OpUpdateOne | OpDeleteOne): + id, exists := m.ID() + if exists { + return []uuid.UUID{id}, nil + } + fallthrough + case m.op.Is(OpUpdate | OpDelete): + return m.Client().PolicyBinding.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } +} + +// SetPrincipalType sets the "principal_type" field. +func (m *PolicyBindingMutation) SetPrincipalType(pt policybinding.PrincipalType) { + m.principal_type = &pt +} + +// PrincipalType returns the value of the "principal_type" field in the mutation. +func (m *PolicyBindingMutation) PrincipalType() (r policybinding.PrincipalType, exists bool) { + v := m.principal_type + if v == nil { + return + } + return *v, true +} + +// OldPrincipalType returns the old "principal_type" field's value of the PolicyBinding entity. +// If the PolicyBinding object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *PolicyBindingMutation) OldPrincipalType(ctx context.Context) (v policybinding.PrincipalType, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldPrincipalType is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldPrincipalType requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldPrincipalType: %w", err) + } + return oldValue.PrincipalType, nil +} + +// ResetPrincipalType resets all changes to the "principal_type" field. +func (m *PolicyBindingMutation) ResetPrincipalType() { + m.principal_type = nil +} + +// SetCreated sets the "created" field. +func (m *PolicyBindingMutation) SetCreated(t time.Time) { + m.created = &t +} + +// Created returns the value of the "created" field in the mutation. +func (m *PolicyBindingMutation) Created() (r time.Time, exists bool) { + v := m.created + if v == nil { + return + } + return *v, true +} + +// OldCreated returns the old "created" field's value of the PolicyBinding entity. +// If the PolicyBinding object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *PolicyBindingMutation) OldCreated(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreated is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreated requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreated: %w", err) + } + return oldValue.Created, nil +} + +// ResetCreated resets all changes to the "created" field. +func (m *PolicyBindingMutation) ResetCreated() { + m.created = nil +} + +// SetCreatedBy sets the "created_by" field. +func (m *PolicyBindingMutation) SetCreatedBy(s string) { + m.created_by = &s +} + +// CreatedBy returns the value of the "created_by" field in the mutation. +func (m *PolicyBindingMutation) CreatedBy() (r string, exists bool) { + v := m.created_by + if v == nil { + return + } + return *v, true +} + +// OldCreatedBy returns the old "created_by" field's value of the PolicyBinding entity. +// If the PolicyBinding object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *PolicyBindingMutation) OldCreatedBy(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreatedBy is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreatedBy requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreatedBy: %w", err) + } + return oldValue.CreatedBy, nil +} + +// ClearCreatedBy clears the value of the "created_by" field. +func (m *PolicyBindingMutation) ClearCreatedBy() { + m.created_by = nil + m.clearedFields[policybinding.FieldCreatedBy] = struct{}{} +} + +// CreatedByCleared returns if the "created_by" field was cleared in this mutation. +func (m *PolicyBindingMutation) CreatedByCleared() bool { + _, ok := m.clearedFields[policybinding.FieldCreatedBy] + return ok +} + +// ResetCreatedBy resets all changes to the "created_by" field. +func (m *PolicyBindingMutation) ResetCreatedBy() { + m.created_by = nil + delete(m.clearedFields, policybinding.FieldCreatedBy) +} + +// SetPolicyID sets the "policy_id" field. +func (m *PolicyBindingMutation) SetPolicyID(u uuid.UUID) { + m.policy = &u +} + +// PolicyID returns the value of the "policy_id" field in the mutation. +func (m *PolicyBindingMutation) PolicyID() (r uuid.UUID, exists bool) { + v := m.policy + if v == nil { + return + } + return *v, true +} + +// OldPolicyID returns the old "policy_id" field's value of the PolicyBinding entity. +// If the PolicyBinding object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *PolicyBindingMutation) OldPolicyID(ctx context.Context) (v *uuid.UUID, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldPolicyID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldPolicyID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldPolicyID: %w", err) + } + return oldValue.PolicyID, nil +} + +// ClearPolicyID clears the value of the "policy_id" field. +func (m *PolicyBindingMutation) ClearPolicyID() { + m.policy = nil + m.clearedFields[policybinding.FieldPolicyID] = struct{}{} +} + +// PolicyIDCleared returns if the "policy_id" field was cleared in this mutation. +func (m *PolicyBindingMutation) PolicyIDCleared() bool { + _, ok := m.clearedFields[policybinding.FieldPolicyID] + return ok +} + +// ResetPolicyID resets all changes to the "policy_id" field. +func (m *PolicyBindingMutation) ResetPolicyID() { + m.policy = nil + delete(m.clearedFields, policybinding.FieldPolicyID) +} + +// SetUserID sets the "user_id" field. +func (m *PolicyBindingMutation) SetUserID(u uuid.UUID) { + m.user = &u +} + +// UserID returns the value of the "user_id" field in the mutation. +func (m *PolicyBindingMutation) UserID() (r uuid.UUID, exists bool) { + v := m.user + if v == nil { + return + } + return *v, true +} + +// OldUserID returns the old "user_id" field's value of the PolicyBinding entity. +// If the PolicyBinding object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *PolicyBindingMutation) OldUserID(ctx context.Context) (v *uuid.UUID, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldUserID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldUserID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldUserID: %w", err) + } + return oldValue.UserID, nil +} + +// ClearUserID clears the value of the "user_id" field. +func (m *PolicyBindingMutation) ClearUserID() { + m.user = nil + m.clearedFields[policybinding.FieldUserID] = struct{}{} +} + +// UserIDCleared returns if the "user_id" field was cleared in this mutation. +func (m *PolicyBindingMutation) UserIDCleared() bool { + _, ok := m.clearedFields[policybinding.FieldUserID] + return ok +} + +// ResetUserID resets all changes to the "user_id" field. +func (m *PolicyBindingMutation) ResetUserID() { + m.user = nil + delete(m.clearedFields, policybinding.FieldUserID) +} + +// SetGroupID sets the "group_id" field. +func (m *PolicyBindingMutation) SetGroupID(u uuid.UUID) { + m.group = &u +} + +// GroupID returns the value of the "group_id" field in the mutation. +func (m *PolicyBindingMutation) GroupID() (r uuid.UUID, exists bool) { + v := m.group + if v == nil { + return + } + return *v, true +} + +// OldGroupID returns the old "group_id" field's value of the PolicyBinding entity. +// If the PolicyBinding object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *PolicyBindingMutation) OldGroupID(ctx context.Context) (v *uuid.UUID, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldGroupID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldGroupID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldGroupID: %w", err) + } + return oldValue.GroupID, nil +} + +// ClearGroupID clears the value of the "group_id" field. +func (m *PolicyBindingMutation) ClearGroupID() { + m.group = nil + m.clearedFields[policybinding.FieldGroupID] = struct{}{} +} + +// GroupIDCleared returns if the "group_id" field was cleared in this mutation. +func (m *PolicyBindingMutation) GroupIDCleared() bool { + _, ok := m.clearedFields[policybinding.FieldGroupID] + return ok +} + +// ResetGroupID resets all changes to the "group_id" field. +func (m *PolicyBindingMutation) ResetGroupID() { + m.group = nil + delete(m.clearedFields, policybinding.FieldGroupID) +} + +// SetAgentID sets the "agent_id" field. +func (m *PolicyBindingMutation) SetAgentID(u uuid.UUID) { + m.agent = &u +} + +// AgentID returns the value of the "agent_id" field in the mutation. +func (m *PolicyBindingMutation) AgentID() (r uuid.UUID, exists bool) { + v := m.agent + if v == nil { + return + } + return *v, true +} + +// OldAgentID returns the old "agent_id" field's value of the PolicyBinding entity. +// If the PolicyBinding object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *PolicyBindingMutation) OldAgentID(ctx context.Context) (v *uuid.UUID, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldAgentID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldAgentID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldAgentID: %w", err) + } + return oldValue.AgentID, nil +} + +// ClearAgentID clears the value of the "agent_id" field. +func (m *PolicyBindingMutation) ClearAgentID() { + m.agent = nil + m.clearedFields[policybinding.FieldAgentID] = struct{}{} +} + +// AgentIDCleared returns if the "agent_id" field was cleared in this mutation. +func (m *PolicyBindingMutation) AgentIDCleared() bool { + _, ok := m.clearedFields[policybinding.FieldAgentID] + return ok +} + +// ResetAgentID resets all changes to the "agent_id" field. +func (m *PolicyBindingMutation) ResetAgentID() { + m.agent = nil + delete(m.clearedFields, policybinding.FieldAgentID) +} + +// ClearPolicy clears the "policy" edge to the AccessPolicy entity. +func (m *PolicyBindingMutation) ClearPolicy() { + m.clearedpolicy = true + m.clearedFields[policybinding.FieldPolicyID] = struct{}{} +} + +// PolicyCleared reports if the "policy" edge to the AccessPolicy entity was cleared. +func (m *PolicyBindingMutation) PolicyCleared() bool { + return m.PolicyIDCleared() || m.clearedpolicy +} + +// PolicyIDs returns the "policy" edge IDs in the mutation. +// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use +// PolicyID instead. It exists only for internal usage by the builders. +func (m *PolicyBindingMutation) PolicyIDs() (ids []uuid.UUID) { + if id := m.policy; id != nil { + ids = append(ids, *id) + } + return +} + +// ResetPolicy resets all changes to the "policy" edge. +func (m *PolicyBindingMutation) ResetPolicy() { + m.policy = nil + m.clearedpolicy = false +} + +// ClearUser clears the "user" edge to the User entity. +func (m *PolicyBindingMutation) ClearUser() { + m.cleareduser = true + m.clearedFields[policybinding.FieldUserID] = struct{}{} +} + +// UserCleared reports if the "user" edge to the User entity was cleared. +func (m *PolicyBindingMutation) UserCleared() bool { + return m.UserIDCleared() || m.cleareduser +} + +// UserIDs returns the "user" edge IDs in the mutation. +// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use +// UserID instead. It exists only for internal usage by the builders. +func (m *PolicyBindingMutation) UserIDs() (ids []uuid.UUID) { + if id := m.user; id != nil { + ids = append(ids, *id) + } + return +} + +// ResetUser resets all changes to the "user" edge. +func (m *PolicyBindingMutation) ResetUser() { + m.user = nil + m.cleareduser = false +} + +// ClearGroup clears the "group" edge to the Group entity. +func (m *PolicyBindingMutation) ClearGroup() { + m.clearedgroup = true + m.clearedFields[policybinding.FieldGroupID] = struct{}{} +} + +// GroupCleared reports if the "group" edge to the Group entity was cleared. +func (m *PolicyBindingMutation) GroupCleared() bool { + return m.GroupIDCleared() || m.clearedgroup +} + +// GroupIDs returns the "group" edge IDs in the mutation. +// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use +// GroupID instead. It exists only for internal usage by the builders. +func (m *PolicyBindingMutation) GroupIDs() (ids []uuid.UUID) { + if id := m.group; id != nil { + ids = append(ids, *id) + } + return +} + +// ResetGroup resets all changes to the "group" edge. +func (m *PolicyBindingMutation) ResetGroup() { + m.group = nil + m.clearedgroup = false +} + +// ClearAgent clears the "agent" edge to the Agent entity. +func (m *PolicyBindingMutation) ClearAgent() { + m.clearedagent = true + m.clearedFields[policybinding.FieldAgentID] = struct{}{} +} + +// AgentCleared reports if the "agent" edge to the Agent entity was cleared. +func (m *PolicyBindingMutation) AgentCleared() bool { + return m.AgentIDCleared() || m.clearedagent +} + +// AgentIDs returns the "agent" edge IDs in the mutation. +// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use +// AgentID instead. It exists only for internal usage by the builders. +func (m *PolicyBindingMutation) AgentIDs() (ids []uuid.UUID) { + if id := m.agent; id != nil { + ids = append(ids, *id) + } + return +} + +// ResetAgent resets all changes to the "agent" edge. +func (m *PolicyBindingMutation) ResetAgent() { + m.agent = nil + m.clearedagent = false +} + +// Where appends a list predicates to the PolicyBindingMutation builder. +func (m *PolicyBindingMutation) Where(ps ...predicate.PolicyBinding) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the PolicyBindingMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *PolicyBindingMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.PolicyBinding, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *PolicyBindingMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *PolicyBindingMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (PolicyBinding). +func (m *PolicyBindingMutation) Type() string { + return m.typ +} + +// Fields returns all fields that were changed during this mutation. Note that in +// order to get all numeric fields that were incremented/decremented, call +// AddedFields(). +func (m *PolicyBindingMutation) Fields() []string { + fields := make([]string, 0, 7) + if m.principal_type != nil { + fields = append(fields, policybinding.FieldPrincipalType) + } + if m.created != nil { + fields = append(fields, policybinding.FieldCreated) + } + if m.created_by != nil { + fields = append(fields, policybinding.FieldCreatedBy) + } + if m.policy != nil { + fields = append(fields, policybinding.FieldPolicyID) + } + if m.user != nil { + fields = append(fields, policybinding.FieldUserID) + } + if m.group != nil { + fields = append(fields, policybinding.FieldGroupID) + } + if m.agent != nil { + fields = append(fields, policybinding.FieldAgentID) + } + return fields +} + +// Field returns the value of a field with the given name. The second boolean +// return value indicates that this field was not set, or was not defined in the +// schema. +func (m *PolicyBindingMutation) Field(name string) (ent.Value, bool) { + switch name { + case policybinding.FieldPrincipalType: + return m.PrincipalType() + case policybinding.FieldCreated: + return m.Created() + case policybinding.FieldCreatedBy: + return m.CreatedBy() + case policybinding.FieldPolicyID: + return m.PolicyID() + case policybinding.FieldUserID: + return m.UserID() + case policybinding.FieldGroupID: + return m.GroupID() + case policybinding.FieldAgentID: + return m.AgentID() + } + return nil, false +} + +// OldField returns the old value of the field from the database. An error is +// returned if the mutation operation is not UpdateOne, or the query to the +// database failed. +func (m *PolicyBindingMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + switch name { + case policybinding.FieldPrincipalType: + return m.OldPrincipalType(ctx) + case policybinding.FieldCreated: + return m.OldCreated(ctx) + case policybinding.FieldCreatedBy: + return m.OldCreatedBy(ctx) + case policybinding.FieldPolicyID: + return m.OldPolicyID(ctx) + case policybinding.FieldUserID: + return m.OldUserID(ctx) + case policybinding.FieldGroupID: + return m.OldGroupID(ctx) + case policybinding.FieldAgentID: + return m.OldAgentID(ctx) + } + return nil, fmt.Errorf("unknown PolicyBinding field %s", name) +} + +// SetField sets the value of a field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *PolicyBindingMutation) SetField(name string, value ent.Value) error { + switch name { + case policybinding.FieldPrincipalType: + v, ok := value.(policybinding.PrincipalType) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetPrincipalType(v) + return nil + case policybinding.FieldCreated: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreated(v) + return nil + case policybinding.FieldCreatedBy: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreatedBy(v) + return nil + case policybinding.FieldPolicyID: + v, ok := value.(uuid.UUID) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetPolicyID(v) + return nil + case policybinding.FieldUserID: + v, ok := value.(uuid.UUID) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetUserID(v) + return nil + case policybinding.FieldGroupID: + v, ok := value.(uuid.UUID) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetGroupID(v) + return nil + case policybinding.FieldAgentID: + v, ok := value.(uuid.UUID) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetAgentID(v) + return nil + } + return fmt.Errorf("unknown PolicyBinding field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *PolicyBindingMutation) AddedFields() []string { + return nil +} + +// AddedField returns the numeric value that was incremented/decremented on a field +// with the given name. The second boolean return value indicates that this field +// was not set, or was not defined in the schema. +func (m *PolicyBindingMutation) AddedField(name string) (ent.Value, bool) { + return nil, false +} + +// AddField adds the value to the field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *PolicyBindingMutation) AddField(name string, value ent.Value) error { + switch name { + } + return fmt.Errorf("unknown PolicyBinding numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *PolicyBindingMutation) ClearedFields() []string { + var fields []string + if m.FieldCleared(policybinding.FieldCreatedBy) { + fields = append(fields, policybinding.FieldCreatedBy) + } + if m.FieldCleared(policybinding.FieldPolicyID) { + fields = append(fields, policybinding.FieldPolicyID) + } + if m.FieldCleared(policybinding.FieldUserID) { + fields = append(fields, policybinding.FieldUserID) + } + if m.FieldCleared(policybinding.FieldGroupID) { + fields = append(fields, policybinding.FieldGroupID) + } + if m.FieldCleared(policybinding.FieldAgentID) { + fields = append(fields, policybinding.FieldAgentID) + } + return fields +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *PolicyBindingMutation) FieldCleared(name string) bool { + _, ok := m.clearedFields[name] + return ok +} + +// ClearField clears the value of the field with the given name. It returns an +// error if the field is not defined in the schema. +func (m *PolicyBindingMutation) ClearField(name string) error { + switch name { + case policybinding.FieldCreatedBy: + m.ClearCreatedBy() + return nil + case policybinding.FieldPolicyID: + m.ClearPolicyID() + return nil + case policybinding.FieldUserID: + m.ClearUserID() + return nil + case policybinding.FieldGroupID: + m.ClearGroupID() + return nil + case policybinding.FieldAgentID: + m.ClearAgentID() + return nil + } + return fmt.Errorf("unknown PolicyBinding nullable field %s", name) +} + +// ResetField resets all changes in the mutation for the field with the given name. +// It returns an error if the field is not defined in the schema. +func (m *PolicyBindingMutation) ResetField(name string) error { + switch name { + case policybinding.FieldPrincipalType: + m.ResetPrincipalType() + return nil + case policybinding.FieldCreated: + m.ResetCreated() + return nil + case policybinding.FieldCreatedBy: + m.ResetCreatedBy() + return nil + case policybinding.FieldPolicyID: + m.ResetPolicyID() + return nil + case policybinding.FieldUserID: + m.ResetUserID() + return nil + case policybinding.FieldGroupID: + m.ResetGroupID() + return nil + case policybinding.FieldAgentID: + m.ResetAgentID() + return nil + } + return fmt.Errorf("unknown PolicyBinding field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *PolicyBindingMutation) AddedEdges() []string { + edges := make([]string, 0, 4) + if m.policy != nil { + edges = append(edges, policybinding.EdgePolicy) + } + if m.user != nil { + edges = append(edges, policybinding.EdgeUser) + } + if m.group != nil { + edges = append(edges, policybinding.EdgeGroup) + } + if m.agent != nil { + edges = append(edges, policybinding.EdgeAgent) + } + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *PolicyBindingMutation) AddedIDs(name string) []ent.Value { + switch name { + case policybinding.EdgePolicy: + if id := m.policy; id != nil { + return []ent.Value{*id} + } + case policybinding.EdgeUser: + if id := m.user; id != nil { + return []ent.Value{*id} + } + case policybinding.EdgeGroup: + if id := m.group; id != nil { + return []ent.Value{*id} + } + case policybinding.EdgeAgent: + if id := m.agent; id != nil { + return []ent.Value{*id} + } + } + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *PolicyBindingMutation) RemovedEdges() []string { + edges := make([]string, 0, 4) + return edges +} + +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *PolicyBindingMutation) RemovedIDs(name string) []ent.Value { + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *PolicyBindingMutation) ClearedEdges() []string { + edges := make([]string, 0, 4) + if m.clearedpolicy { + edges = append(edges, policybinding.EdgePolicy) + } + if m.cleareduser { + edges = append(edges, policybinding.EdgeUser) + } + if m.clearedgroup { + edges = append(edges, policybinding.EdgeGroup) + } + if m.clearedagent { + edges = append(edges, policybinding.EdgeAgent) + } + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *PolicyBindingMutation) EdgeCleared(name string) bool { + switch name { + case policybinding.EdgePolicy: + return m.clearedpolicy + case policybinding.EdgeUser: + return m.cleareduser + case policybinding.EdgeGroup: + return m.clearedgroup + case policybinding.EdgeAgent: + return m.clearedagent + } + return false +} + +// ClearEdge clears the value of the edge with the given name. It returns an error +// if that edge is not defined in the schema. +func (m *PolicyBindingMutation) ClearEdge(name string) error { + switch name { + case policybinding.EdgePolicy: + m.ClearPolicy() + return nil + case policybinding.EdgeUser: + m.ClearUser() + return nil + case policybinding.EdgeGroup: + m.ClearGroup() + return nil + case policybinding.EdgeAgent: + m.ClearAgent() + return nil + } + return fmt.Errorf("unknown PolicyBinding unique edge %s", name) +} + +// ResetEdge resets all changes to the edge with the given name in this mutation. +// It returns an error if the edge is not defined in the schema. +func (m *PolicyBindingMutation) ResetEdge(name string) error { + switch name { + case policybinding.EdgePolicy: + m.ResetPolicy() + return nil + case policybinding.EdgeUser: + m.ResetUser() + return nil + case policybinding.EdgeGroup: + m.ResetGroup() + return nil + case policybinding.EdgeAgent: + m.ResetAgent() + return nil + } + return fmt.Errorf("unknown PolicyBinding edge %s", name) +} + +// ProjectMutation represents an operation that mutates the Project nodes in the graph. +type ProjectMutation struct { + config + op Op + typ string + id *uuid.UUID + name *string + slug *string + git_remote *string + default_runtime_broker_id *string + labels *map[string]string + annotations *map[string]string + shared_dirs *string + created *time.Time + updated *time.Time + created_by *string + owner_id *string + visibility *string + github_installation_id *int64 + addgithub_installation_id *int64 + github_permissions *string + github_app_status *string + git_identity *string + clearedFields map[string]struct{} + agents map[uuid.UUID]struct{} + removedagents map[uuid.UUID]struct{} + clearedagents bool + done bool + oldValue func(context.Context) (*Project, error) + predicates []predicate.Project +} + +var _ ent.Mutation = (*ProjectMutation)(nil) + +// projectOption allows management of the mutation configuration using functional options. +type projectOption func(*ProjectMutation) + +// newProjectMutation creates new mutation for the Project entity. +func newProjectMutation(c config, op Op, opts ...projectOption) *ProjectMutation { + m := &ProjectMutation{ + config: c, + op: op, + typ: TypeProject, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// withProjectID sets the ID field of the mutation. +func withProjectID(id uuid.UUID) projectOption { + return func(m *ProjectMutation) { + var ( + err error + once sync.Once + value *Project + ) + m.oldValue = func(ctx context.Context) (*Project, error) { + once.Do(func() { + if m.done { + err = errors.New("querying old values post mutation is not allowed") + } else { + value, err = m.Client().Project.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } +} + +// withProject sets the old Project of the mutation. +func withProject(node *Project) projectOption { + return func(m *ProjectMutation) { + m.oldValue = func(context.Context) (*Project, error) { + return node, nil + } + m.id = &node.ID + } +} + +// Client returns a new `ent.Client` from the mutation. If the mutation was +// executed in a transaction (ent.Tx), a transactional client is returned. +func (m ProjectMutation) Client() *Client { + client := &Client{config: m.config} + client.init() + return client +} + +// Tx returns an `ent.Tx` for mutations that were executed in transactions; +// it returns an error otherwise. +func (m ProjectMutation) Tx() (*Tx, error) { + if _, ok := m.driver.(*txDriver); !ok { + return nil, errors.New("ent: mutation is not running in a transaction") + } + tx := &Tx{config: m.config} + tx.init() + return tx, nil +} + +// SetID sets the value of the id field. Note that this +// operation is only accepted on creation of Project entities. +func (m *ProjectMutation) SetID(id uuid.UUID) { + m.id = &id +} + +// ID returns the ID value in the mutation. Note that the ID is only available +// if it was provided to the builder or after it was returned from the database. +func (m *ProjectMutation) ID() (id uuid.UUID, exists bool) { + if m.id == nil { + return + } + return *m.id, true +} + +// IDs queries the database and returns the entity ids that match the mutation's predicate. +// That means, if the mutation is applied within a transaction with an isolation level such +// as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated +// or updated by the mutation. +func (m *ProjectMutation) IDs(ctx context.Context) ([]uuid.UUID, error) { + switch { + case m.op.Is(OpUpdateOne | OpDeleteOne): + id, exists := m.ID() + if exists { + return []uuid.UUID{id}, nil + } + fallthrough + case m.op.Is(OpUpdate | OpDelete): + return m.Client().Project.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } +} + +// SetName sets the "name" field. +func (m *ProjectMutation) SetName(s string) { + m.name = &s +} + +// Name returns the value of the "name" field in the mutation. +func (m *ProjectMutation) Name() (r string, exists bool) { + v := m.name + if v == nil { + return + } + return *v, true +} + +// OldName returns the old "name" field's value of the Project entity. +// If the Project object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ProjectMutation) OldName(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldName is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldName requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldName: %w", err) + } + return oldValue.Name, nil +} + +// ResetName resets all changes to the "name" field. +func (m *ProjectMutation) ResetName() { + m.name = nil +} + +// SetSlug sets the "slug" field. +func (m *ProjectMutation) SetSlug(s string) { + m.slug = &s +} + +// Slug returns the value of the "slug" field in the mutation. +func (m *ProjectMutation) Slug() (r string, exists bool) { + v := m.slug + if v == nil { + return + } + return *v, true +} + +// OldSlug returns the old "slug" field's value of the Project entity. +// If the Project object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ProjectMutation) OldSlug(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldSlug is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldSlug requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldSlug: %w", err) + } + return oldValue.Slug, nil +} + +// ResetSlug resets all changes to the "slug" field. +func (m *ProjectMutation) ResetSlug() { + m.slug = nil +} + +// SetGitRemote sets the "git_remote" field. +func (m *ProjectMutation) SetGitRemote(s string) { + m.git_remote = &s +} + +// GitRemote returns the value of the "git_remote" field in the mutation. +func (m *ProjectMutation) GitRemote() (r string, exists bool) { + v := m.git_remote + if v == nil { + return + } + return *v, true +} + +// OldGitRemote returns the old "git_remote" field's value of the Project entity. +// If the Project object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ProjectMutation) OldGitRemote(ctx context.Context) (v *string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldGitRemote is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldGitRemote requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldGitRemote: %w", err) + } + return oldValue.GitRemote, nil +} + +// ClearGitRemote clears the value of the "git_remote" field. +func (m *ProjectMutation) ClearGitRemote() { + m.git_remote = nil + m.clearedFields[project.FieldGitRemote] = struct{}{} +} + +// GitRemoteCleared returns if the "git_remote" field was cleared in this mutation. +func (m *ProjectMutation) GitRemoteCleared() bool { + _, ok := m.clearedFields[project.FieldGitRemote] + return ok +} + +// ResetGitRemote resets all changes to the "git_remote" field. +func (m *ProjectMutation) ResetGitRemote() { + m.git_remote = nil + delete(m.clearedFields, project.FieldGitRemote) +} + +// SetDefaultRuntimeBrokerID sets the "default_runtime_broker_id" field. +func (m *ProjectMutation) SetDefaultRuntimeBrokerID(s string) { + m.default_runtime_broker_id = &s +} + +// DefaultRuntimeBrokerID returns the value of the "default_runtime_broker_id" field in the mutation. +func (m *ProjectMutation) DefaultRuntimeBrokerID() (r string, exists bool) { + v := m.default_runtime_broker_id + if v == nil { + return + } + return *v, true +} + +// OldDefaultRuntimeBrokerID returns the old "default_runtime_broker_id" field's value of the Project entity. +// If the Project object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ProjectMutation) OldDefaultRuntimeBrokerID(ctx context.Context) (v *string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldDefaultRuntimeBrokerID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldDefaultRuntimeBrokerID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldDefaultRuntimeBrokerID: %w", err) + } + return oldValue.DefaultRuntimeBrokerID, nil +} + +// ClearDefaultRuntimeBrokerID clears the value of the "default_runtime_broker_id" field. +func (m *ProjectMutation) ClearDefaultRuntimeBrokerID() { + m.default_runtime_broker_id = nil + m.clearedFields[project.FieldDefaultRuntimeBrokerID] = struct{}{} +} + +// DefaultRuntimeBrokerIDCleared returns if the "default_runtime_broker_id" field was cleared in this mutation. +func (m *ProjectMutation) DefaultRuntimeBrokerIDCleared() bool { + _, ok := m.clearedFields[project.FieldDefaultRuntimeBrokerID] + return ok +} + +// ResetDefaultRuntimeBrokerID resets all changes to the "default_runtime_broker_id" field. +func (m *ProjectMutation) ResetDefaultRuntimeBrokerID() { + m.default_runtime_broker_id = nil + delete(m.clearedFields, project.FieldDefaultRuntimeBrokerID) +} + +// SetLabels sets the "labels" field. +func (m *ProjectMutation) SetLabels(value map[string]string) { + m.labels = &value +} + +// Labels returns the value of the "labels" field in the mutation. +func (m *ProjectMutation) Labels() (r map[string]string, exists bool) { + v := m.labels + if v == nil { + return + } + return *v, true +} + +// OldLabels returns the old "labels" field's value of the Project entity. +// If the Project object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ProjectMutation) OldLabels(ctx context.Context) (v map[string]string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldLabels is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldLabels requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldLabels: %w", err) + } + return oldValue.Labels, nil +} + +// ClearLabels clears the value of the "labels" field. +func (m *ProjectMutation) ClearLabels() { + m.labels = nil + m.clearedFields[project.FieldLabels] = struct{}{} +} + +// LabelsCleared returns if the "labels" field was cleared in this mutation. +func (m *ProjectMutation) LabelsCleared() bool { + _, ok := m.clearedFields[project.FieldLabels] + return ok +} + +// ResetLabels resets all changes to the "labels" field. +func (m *ProjectMutation) ResetLabels() { + m.labels = nil + delete(m.clearedFields, project.FieldLabels) +} + +// SetAnnotations sets the "annotations" field. +func (m *ProjectMutation) SetAnnotations(value map[string]string) { + m.annotations = &value +} + +// Annotations returns the value of the "annotations" field in the mutation. +func (m *ProjectMutation) Annotations() (r map[string]string, exists bool) { + v := m.annotations + if v == nil { + return + } + return *v, true +} + +// OldAnnotations returns the old "annotations" field's value of the Project entity. +// If the Project object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ProjectMutation) OldAnnotations(ctx context.Context) (v map[string]string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldAnnotations is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldAnnotations requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldAnnotations: %w", err) + } + return oldValue.Annotations, nil +} + +// ClearAnnotations clears the value of the "annotations" field. +func (m *ProjectMutation) ClearAnnotations() { + m.annotations = nil + m.clearedFields[project.FieldAnnotations] = struct{}{} +} + +// AnnotationsCleared returns if the "annotations" field was cleared in this mutation. +func (m *ProjectMutation) AnnotationsCleared() bool { + _, ok := m.clearedFields[project.FieldAnnotations] + return ok +} + +// ResetAnnotations resets all changes to the "annotations" field. +func (m *ProjectMutation) ResetAnnotations() { + m.annotations = nil + delete(m.clearedFields, project.FieldAnnotations) +} + +// SetSharedDirs sets the "shared_dirs" field. +func (m *ProjectMutation) SetSharedDirs(s string) { + m.shared_dirs = &s +} + +// SharedDirs returns the value of the "shared_dirs" field in the mutation. +func (m *ProjectMutation) SharedDirs() (r string, exists bool) { + v := m.shared_dirs + if v == nil { + return + } + return *v, true +} + +// OldSharedDirs returns the old "shared_dirs" field's value of the Project entity. +// If the Project object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ProjectMutation) OldSharedDirs(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldSharedDirs is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldSharedDirs requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldSharedDirs: %w", err) + } + return oldValue.SharedDirs, nil +} + +// ClearSharedDirs clears the value of the "shared_dirs" field. +func (m *ProjectMutation) ClearSharedDirs() { + m.shared_dirs = nil + m.clearedFields[project.FieldSharedDirs] = struct{}{} +} + +// SharedDirsCleared returns if the "shared_dirs" field was cleared in this mutation. +func (m *ProjectMutation) SharedDirsCleared() bool { + _, ok := m.clearedFields[project.FieldSharedDirs] + return ok +} + +// ResetSharedDirs resets all changes to the "shared_dirs" field. +func (m *ProjectMutation) ResetSharedDirs() { + m.shared_dirs = nil + delete(m.clearedFields, project.FieldSharedDirs) +} + +// SetCreated sets the "created" field. +func (m *ProjectMutation) SetCreated(t time.Time) { + m.created = &t +} + +// Created returns the value of the "created" field in the mutation. +func (m *ProjectMutation) Created() (r time.Time, exists bool) { + v := m.created + if v == nil { + return + } + return *v, true +} + +// OldCreated returns the old "created" field's value of the Project entity. +// If the Project object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ProjectMutation) OldCreated(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreated is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreated requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreated: %w", err) + } + return oldValue.Created, nil +} + +// ResetCreated resets all changes to the "created" field. +func (m *ProjectMutation) ResetCreated() { + m.created = nil +} + +// SetUpdated sets the "updated" field. +func (m *ProjectMutation) SetUpdated(t time.Time) { + m.updated = &t +} + +// Updated returns the value of the "updated" field in the mutation. +func (m *ProjectMutation) Updated() (r time.Time, exists bool) { + v := m.updated + if v == nil { + return + } + return *v, true +} + +// OldUpdated returns the old "updated" field's value of the Project entity. +// If the Project object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ProjectMutation) OldUpdated(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldUpdated is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldUpdated requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldUpdated: %w", err) + } + return oldValue.Updated, nil +} + +// ResetUpdated resets all changes to the "updated" field. +func (m *ProjectMutation) ResetUpdated() { + m.updated = nil +} + +// SetCreatedBy sets the "created_by" field. +func (m *ProjectMutation) SetCreatedBy(s string) { + m.created_by = &s +} + +// CreatedBy returns the value of the "created_by" field in the mutation. +func (m *ProjectMutation) CreatedBy() (r string, exists bool) { + v := m.created_by + if v == nil { + return + } + return *v, true +} + +// OldCreatedBy returns the old "created_by" field's value of the Project entity. +// If the Project object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ProjectMutation) OldCreatedBy(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreatedBy is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreatedBy requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreatedBy: %w", err) + } + return oldValue.CreatedBy, nil +} + +// ClearCreatedBy clears the value of the "created_by" field. +func (m *ProjectMutation) ClearCreatedBy() { + m.created_by = nil + m.clearedFields[project.FieldCreatedBy] = struct{}{} +} + +// CreatedByCleared returns if the "created_by" field was cleared in this mutation. +func (m *ProjectMutation) CreatedByCleared() bool { + _, ok := m.clearedFields[project.FieldCreatedBy] + return ok +} + +// ResetCreatedBy resets all changes to the "created_by" field. +func (m *ProjectMutation) ResetCreatedBy() { + m.created_by = nil + delete(m.clearedFields, project.FieldCreatedBy) +} + +// SetOwnerID sets the "owner_id" field. +func (m *ProjectMutation) SetOwnerID(s string) { + m.owner_id = &s +} + +// OwnerID returns the value of the "owner_id" field in the mutation. +func (m *ProjectMutation) OwnerID() (r string, exists bool) { + v := m.owner_id + if v == nil { + return + } + return *v, true +} + +// OldOwnerID returns the old "owner_id" field's value of the Project entity. +// If the Project object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ProjectMutation) OldOwnerID(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldOwnerID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldOwnerID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldOwnerID: %w", err) + } + return oldValue.OwnerID, nil +} + +// ClearOwnerID clears the value of the "owner_id" field. +func (m *ProjectMutation) ClearOwnerID() { + m.owner_id = nil + m.clearedFields[project.FieldOwnerID] = struct{}{} +} + +// OwnerIDCleared returns if the "owner_id" field was cleared in this mutation. +func (m *ProjectMutation) OwnerIDCleared() bool { + _, ok := m.clearedFields[project.FieldOwnerID] + return ok +} + +// ResetOwnerID resets all changes to the "owner_id" field. +func (m *ProjectMutation) ResetOwnerID() { + m.owner_id = nil + delete(m.clearedFields, project.FieldOwnerID) +} + +// SetVisibility sets the "visibility" field. +func (m *ProjectMutation) SetVisibility(s string) { + m.visibility = &s +} + +// Visibility returns the value of the "visibility" field in the mutation. +func (m *ProjectMutation) Visibility() (r string, exists bool) { + v := m.visibility + if v == nil { + return + } + return *v, true +} + +// OldVisibility returns the old "visibility" field's value of the Project entity. +// If the Project object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ProjectMutation) OldVisibility(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldVisibility is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldVisibility requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldVisibility: %w", err) + } + return oldValue.Visibility, nil +} + +// ResetVisibility resets all changes to the "visibility" field. +func (m *ProjectMutation) ResetVisibility() { + m.visibility = nil +} + +// SetGithubInstallationID sets the "github_installation_id" field. +func (m *ProjectMutation) SetGithubInstallationID(i int64) { + m.github_installation_id = &i + m.addgithub_installation_id = nil +} + +// GithubInstallationID returns the value of the "github_installation_id" field in the mutation. +func (m *ProjectMutation) GithubInstallationID() (r int64, exists bool) { + v := m.github_installation_id + if v == nil { + return + } + return *v, true +} + +// OldGithubInstallationID returns the old "github_installation_id" field's value of the Project entity. +// If the Project object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ProjectMutation) OldGithubInstallationID(ctx context.Context) (v *int64, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldGithubInstallationID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldGithubInstallationID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldGithubInstallationID: %w", err) + } + return oldValue.GithubInstallationID, nil +} + +// AddGithubInstallationID adds i to the "github_installation_id" field. +func (m *ProjectMutation) AddGithubInstallationID(i int64) { + if m.addgithub_installation_id != nil { + *m.addgithub_installation_id += i + } else { + m.addgithub_installation_id = &i + } +} + +// AddedGithubInstallationID returns the value that was added to the "github_installation_id" field in this mutation. +func (m *ProjectMutation) AddedGithubInstallationID() (r int64, exists bool) { + v := m.addgithub_installation_id + if v == nil { + return + } + return *v, true +} + +// ClearGithubInstallationID clears the value of the "github_installation_id" field. +func (m *ProjectMutation) ClearGithubInstallationID() { + m.github_installation_id = nil + m.addgithub_installation_id = nil + m.clearedFields[project.FieldGithubInstallationID] = struct{}{} +} + +// GithubInstallationIDCleared returns if the "github_installation_id" field was cleared in this mutation. +func (m *ProjectMutation) GithubInstallationIDCleared() bool { + _, ok := m.clearedFields[project.FieldGithubInstallationID] + return ok +} + +// ResetGithubInstallationID resets all changes to the "github_installation_id" field. +func (m *ProjectMutation) ResetGithubInstallationID() { + m.github_installation_id = nil + m.addgithub_installation_id = nil + delete(m.clearedFields, project.FieldGithubInstallationID) +} + +// SetGithubPermissions sets the "github_permissions" field. +func (m *ProjectMutation) SetGithubPermissions(s string) { + m.github_permissions = &s +} + +// GithubPermissions returns the value of the "github_permissions" field in the mutation. +func (m *ProjectMutation) GithubPermissions() (r string, exists bool) { + v := m.github_permissions + if v == nil { + return + } + return *v, true +} + +// OldGithubPermissions returns the old "github_permissions" field's value of the Project entity. +// If the Project object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ProjectMutation) OldGithubPermissions(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldGithubPermissions is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldGithubPermissions requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldGithubPermissions: %w", err) + } + return oldValue.GithubPermissions, nil +} + +// ClearGithubPermissions clears the value of the "github_permissions" field. +func (m *ProjectMutation) ClearGithubPermissions() { + m.github_permissions = nil + m.clearedFields[project.FieldGithubPermissions] = struct{}{} +} + +// GithubPermissionsCleared returns if the "github_permissions" field was cleared in this mutation. +func (m *ProjectMutation) GithubPermissionsCleared() bool { + _, ok := m.clearedFields[project.FieldGithubPermissions] + return ok +} + +// ResetGithubPermissions resets all changes to the "github_permissions" field. +func (m *ProjectMutation) ResetGithubPermissions() { + m.github_permissions = nil + delete(m.clearedFields, project.FieldGithubPermissions) +} + +// SetGithubAppStatus sets the "github_app_status" field. +func (m *ProjectMutation) SetGithubAppStatus(s string) { + m.github_app_status = &s +} + +// GithubAppStatus returns the value of the "github_app_status" field in the mutation. +func (m *ProjectMutation) GithubAppStatus() (r string, exists bool) { + v := m.github_app_status + if v == nil { + return + } + return *v, true +} + +// OldGithubAppStatus returns the old "github_app_status" field's value of the Project entity. +// If the Project object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ProjectMutation) OldGithubAppStatus(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldGithubAppStatus is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldGithubAppStatus requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldGithubAppStatus: %w", err) + } + return oldValue.GithubAppStatus, nil +} + +// ClearGithubAppStatus clears the value of the "github_app_status" field. +func (m *ProjectMutation) ClearGithubAppStatus() { + m.github_app_status = nil + m.clearedFields[project.FieldGithubAppStatus] = struct{}{} +} + +// GithubAppStatusCleared returns if the "github_app_status" field was cleared in this mutation. +func (m *ProjectMutation) GithubAppStatusCleared() bool { + _, ok := m.clearedFields[project.FieldGithubAppStatus] + return ok +} + +// ResetGithubAppStatus resets all changes to the "github_app_status" field. +func (m *ProjectMutation) ResetGithubAppStatus() { + m.github_app_status = nil + delete(m.clearedFields, project.FieldGithubAppStatus) +} + +// SetGitIdentity sets the "git_identity" field. +func (m *ProjectMutation) SetGitIdentity(s string) { + m.git_identity = &s +} + +// GitIdentity returns the value of the "git_identity" field in the mutation. +func (m *ProjectMutation) GitIdentity() (r string, exists bool) { + v := m.git_identity + if v == nil { + return + } + return *v, true +} + +// OldGitIdentity returns the old "git_identity" field's value of the Project entity. +// If the Project object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ProjectMutation) OldGitIdentity(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldGitIdentity is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldGitIdentity requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldGitIdentity: %w", err) + } + return oldValue.GitIdentity, nil +} + +// ClearGitIdentity clears the value of the "git_identity" field. +func (m *ProjectMutation) ClearGitIdentity() { + m.git_identity = nil + m.clearedFields[project.FieldGitIdentity] = struct{}{} +} + +// GitIdentityCleared returns if the "git_identity" field was cleared in this mutation. +func (m *ProjectMutation) GitIdentityCleared() bool { + _, ok := m.clearedFields[project.FieldGitIdentity] + return ok +} + +// ResetGitIdentity resets all changes to the "git_identity" field. +func (m *ProjectMutation) ResetGitIdentity() { + m.git_identity = nil + delete(m.clearedFields, project.FieldGitIdentity) +} + +// AddAgentIDs adds the "agents" edge to the Agent entity by ids. +func (m *ProjectMutation) AddAgentIDs(ids ...uuid.UUID) { + if m.agents == nil { + m.agents = make(map[uuid.UUID]struct{}) + } + for i := range ids { + m.agents[ids[i]] = struct{}{} + } +} + +// ClearAgents clears the "agents" edge to the Agent entity. +func (m *ProjectMutation) ClearAgents() { + m.clearedagents = true +} + +// AgentsCleared reports if the "agents" edge to the Agent entity was cleared. +func (m *ProjectMutation) AgentsCleared() bool { + return m.clearedagents +} + +// RemoveAgentIDs removes the "agents" edge to the Agent entity by IDs. +func (m *ProjectMutation) RemoveAgentIDs(ids ...uuid.UUID) { + if m.removedagents == nil { + m.removedagents = make(map[uuid.UUID]struct{}) + } + for i := range ids { + delete(m.agents, ids[i]) + m.removedagents[ids[i]] = struct{}{} + } +} + +// RemovedAgents returns the removed IDs of the "agents" edge to the Agent entity. +func (m *ProjectMutation) RemovedAgentsIDs() (ids []uuid.UUID) { + for id := range m.removedagents { + ids = append(ids, id) + } + return +} + +// AgentsIDs returns the "agents" edge IDs in the mutation. +func (m *ProjectMutation) AgentsIDs() (ids []uuid.UUID) { + for id := range m.agents { + ids = append(ids, id) + } + return +} + +// ResetAgents resets all changes to the "agents" edge. +func (m *ProjectMutation) ResetAgents() { + m.agents = nil + m.clearedagents = false + m.removedagents = nil +} + +// Where appends a list predicates to the ProjectMutation builder. +func (m *ProjectMutation) Where(ps ...predicate.Project) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the ProjectMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *ProjectMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.Project, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *ProjectMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *ProjectMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (Project). +func (m *ProjectMutation) Type() string { + return m.typ +} + +// Fields returns all fields that were changed during this mutation. Note that in +// order to get all numeric fields that were incremented/decremented, call +// AddedFields(). +func (m *ProjectMutation) Fields() []string { + fields := make([]string, 0, 16) + if m.name != nil { + fields = append(fields, project.FieldName) + } + if m.slug != nil { + fields = append(fields, project.FieldSlug) + } + if m.git_remote != nil { + fields = append(fields, project.FieldGitRemote) + } + if m.default_runtime_broker_id != nil { + fields = append(fields, project.FieldDefaultRuntimeBrokerID) + } + if m.labels != nil { + fields = append(fields, project.FieldLabels) + } + if m.annotations != nil { + fields = append(fields, project.FieldAnnotations) + } + if m.shared_dirs != nil { + fields = append(fields, project.FieldSharedDirs) + } + if m.created != nil { + fields = append(fields, project.FieldCreated) + } + if m.updated != nil { + fields = append(fields, project.FieldUpdated) + } + if m.created_by != nil { + fields = append(fields, project.FieldCreatedBy) + } + if m.owner_id != nil { + fields = append(fields, project.FieldOwnerID) + } + if m.visibility != nil { + fields = append(fields, project.FieldVisibility) + } + if m.github_installation_id != nil { + fields = append(fields, project.FieldGithubInstallationID) + } + if m.github_permissions != nil { + fields = append(fields, project.FieldGithubPermissions) + } + if m.github_app_status != nil { + fields = append(fields, project.FieldGithubAppStatus) + } + if m.git_identity != nil { + fields = append(fields, project.FieldGitIdentity) + } + return fields +} + +// Field returns the value of a field with the given name. The second boolean +// return value indicates that this field was not set, or was not defined in the +// schema. +func (m *ProjectMutation) Field(name string) (ent.Value, bool) { + switch name { + case project.FieldName: + return m.Name() + case project.FieldSlug: + return m.Slug() + case project.FieldGitRemote: + return m.GitRemote() + case project.FieldDefaultRuntimeBrokerID: + return m.DefaultRuntimeBrokerID() + case project.FieldLabels: + return m.Labels() + case project.FieldAnnotations: + return m.Annotations() + case project.FieldSharedDirs: + return m.SharedDirs() + case project.FieldCreated: + return m.Created() + case project.FieldUpdated: + return m.Updated() + case project.FieldCreatedBy: + return m.CreatedBy() + case project.FieldOwnerID: + return m.OwnerID() + case project.FieldVisibility: + return m.Visibility() + case project.FieldGithubInstallationID: + return m.GithubInstallationID() + case project.FieldGithubPermissions: + return m.GithubPermissions() + case project.FieldGithubAppStatus: + return m.GithubAppStatus() + case project.FieldGitIdentity: + return m.GitIdentity() + } + return nil, false +} + +// OldField returns the old value of the field from the database. An error is +// returned if the mutation operation is not UpdateOne, or the query to the +// database failed. +func (m *ProjectMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + switch name { + case project.FieldName: + return m.OldName(ctx) + case project.FieldSlug: + return m.OldSlug(ctx) + case project.FieldGitRemote: + return m.OldGitRemote(ctx) + case project.FieldDefaultRuntimeBrokerID: + return m.OldDefaultRuntimeBrokerID(ctx) + case project.FieldLabels: + return m.OldLabels(ctx) + case project.FieldAnnotations: + return m.OldAnnotations(ctx) + case project.FieldSharedDirs: + return m.OldSharedDirs(ctx) + case project.FieldCreated: + return m.OldCreated(ctx) + case project.FieldUpdated: + return m.OldUpdated(ctx) + case project.FieldCreatedBy: + return m.OldCreatedBy(ctx) + case project.FieldOwnerID: + return m.OldOwnerID(ctx) + case project.FieldVisibility: + return m.OldVisibility(ctx) + case project.FieldGithubInstallationID: + return m.OldGithubInstallationID(ctx) + case project.FieldGithubPermissions: + return m.OldGithubPermissions(ctx) + case project.FieldGithubAppStatus: + return m.OldGithubAppStatus(ctx) + case project.FieldGitIdentity: + return m.OldGitIdentity(ctx) + } + return nil, fmt.Errorf("unknown Project field %s", name) +} + +// SetField sets the value of a field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *ProjectMutation) SetField(name string, value ent.Value) error { + switch name { + case project.FieldName: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetName(v) + return nil + case project.FieldSlug: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetSlug(v) + return nil + case project.FieldGitRemote: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetGitRemote(v) + return nil + case project.FieldDefaultRuntimeBrokerID: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetDefaultRuntimeBrokerID(v) + return nil + case project.FieldLabels: + v, ok := value.(map[string]string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetLabels(v) + return nil + case project.FieldAnnotations: + v, ok := value.(map[string]string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetAnnotations(v) + return nil + case project.FieldSharedDirs: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetSharedDirs(v) + return nil + case project.FieldCreated: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreated(v) + return nil + case project.FieldUpdated: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetUpdated(v) + return nil + case project.FieldCreatedBy: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreatedBy(v) + return nil + case project.FieldOwnerID: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetOwnerID(v) + return nil + case project.FieldVisibility: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetVisibility(v) + return nil + case project.FieldGithubInstallationID: + v, ok := value.(int64) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetGithubInstallationID(v) + return nil + case project.FieldGithubPermissions: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetGithubPermissions(v) + return nil + case project.FieldGithubAppStatus: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetGithubAppStatus(v) + return nil + case project.FieldGitIdentity: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetGitIdentity(v) + return nil + } + return fmt.Errorf("unknown Project field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *ProjectMutation) AddedFields() []string { + var fields []string + if m.addgithub_installation_id != nil { + fields = append(fields, project.FieldGithubInstallationID) + } + return fields +} + +// AddedField returns the numeric value that was incremented/decremented on a field +// with the given name. The second boolean return value indicates that this field +// was not set, or was not defined in the schema. +func (m *ProjectMutation) AddedField(name string) (ent.Value, bool) { + switch name { + case project.FieldGithubInstallationID: + return m.AddedGithubInstallationID() + } + return nil, false +} + +// AddField adds the value to the field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *ProjectMutation) AddField(name string, value ent.Value) error { + switch name { + case project.FieldGithubInstallationID: + v, ok := value.(int64) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.AddGithubInstallationID(v) + return nil + } + return fmt.Errorf("unknown Project numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *ProjectMutation) ClearedFields() []string { + var fields []string + if m.FieldCleared(project.FieldGitRemote) { + fields = append(fields, project.FieldGitRemote) + } + if m.FieldCleared(project.FieldDefaultRuntimeBrokerID) { + fields = append(fields, project.FieldDefaultRuntimeBrokerID) + } + if m.FieldCleared(project.FieldLabels) { + fields = append(fields, project.FieldLabels) + } + if m.FieldCleared(project.FieldAnnotations) { + fields = append(fields, project.FieldAnnotations) + } + if m.FieldCleared(project.FieldSharedDirs) { + fields = append(fields, project.FieldSharedDirs) + } + if m.FieldCleared(project.FieldCreatedBy) { + fields = append(fields, project.FieldCreatedBy) + } + if m.FieldCleared(project.FieldOwnerID) { + fields = append(fields, project.FieldOwnerID) + } + if m.FieldCleared(project.FieldGithubInstallationID) { + fields = append(fields, project.FieldGithubInstallationID) + } + if m.FieldCleared(project.FieldGithubPermissions) { + fields = append(fields, project.FieldGithubPermissions) + } + if m.FieldCleared(project.FieldGithubAppStatus) { + fields = append(fields, project.FieldGithubAppStatus) + } + if m.FieldCleared(project.FieldGitIdentity) { + fields = append(fields, project.FieldGitIdentity) + } + return fields +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *ProjectMutation) FieldCleared(name string) bool { + _, ok := m.clearedFields[name] + return ok +} + +// ClearField clears the value of the field with the given name. It returns an +// error if the field is not defined in the schema. +func (m *ProjectMutation) ClearField(name string) error { + switch name { + case project.FieldGitRemote: + m.ClearGitRemote() + return nil + case project.FieldDefaultRuntimeBrokerID: + m.ClearDefaultRuntimeBrokerID() + return nil + case project.FieldLabels: + m.ClearLabels() + return nil + case project.FieldAnnotations: + m.ClearAnnotations() + return nil + case project.FieldSharedDirs: + m.ClearSharedDirs() + return nil + case project.FieldCreatedBy: + m.ClearCreatedBy() + return nil + case project.FieldOwnerID: + m.ClearOwnerID() + return nil + case project.FieldGithubInstallationID: + m.ClearGithubInstallationID() + return nil + case project.FieldGithubPermissions: + m.ClearGithubPermissions() + return nil + case project.FieldGithubAppStatus: + m.ClearGithubAppStatus() + return nil + case project.FieldGitIdentity: + m.ClearGitIdentity() + return nil + } + return fmt.Errorf("unknown Project nullable field %s", name) +} + +// ResetField resets all changes in the mutation for the field with the given name. +// It returns an error if the field is not defined in the schema. +func (m *ProjectMutation) ResetField(name string) error { + switch name { + case project.FieldName: + m.ResetName() + return nil + case project.FieldSlug: + m.ResetSlug() + return nil + case project.FieldGitRemote: + m.ResetGitRemote() + return nil + case project.FieldDefaultRuntimeBrokerID: + m.ResetDefaultRuntimeBrokerID() + return nil + case project.FieldLabels: + m.ResetLabels() + return nil + case project.FieldAnnotations: + m.ResetAnnotations() + return nil + case project.FieldSharedDirs: + m.ResetSharedDirs() + return nil + case project.FieldCreated: + m.ResetCreated() + return nil + case project.FieldUpdated: + m.ResetUpdated() + return nil + case project.FieldCreatedBy: + m.ResetCreatedBy() + return nil + case project.FieldOwnerID: + m.ResetOwnerID() + return nil + case project.FieldVisibility: + m.ResetVisibility() + return nil + case project.FieldGithubInstallationID: + m.ResetGithubInstallationID() + return nil + case project.FieldGithubPermissions: + m.ResetGithubPermissions() + return nil + case project.FieldGithubAppStatus: + m.ResetGithubAppStatus() + return nil + case project.FieldGitIdentity: + m.ResetGitIdentity() + return nil + } + return fmt.Errorf("unknown Project field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *ProjectMutation) AddedEdges() []string { + edges := make([]string, 0, 1) + if m.agents != nil { + edges = append(edges, project.EdgeAgents) + } + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *ProjectMutation) AddedIDs(name string) []ent.Value { + switch name { + case project.EdgeAgents: + ids := make([]ent.Value, 0, len(m.agents)) + for id := range m.agents { + ids = append(ids, id) + } + return ids + } + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *ProjectMutation) RemovedEdges() []string { + edges := make([]string, 0, 1) + if m.removedagents != nil { + edges = append(edges, project.EdgeAgents) + } + return edges +} + +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *ProjectMutation) RemovedIDs(name string) []ent.Value { + switch name { + case project.EdgeAgents: + ids := make([]ent.Value, 0, len(m.removedagents)) + for id := range m.removedagents { + ids = append(ids, id) + } + return ids + } + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *ProjectMutation) ClearedEdges() []string { + edges := make([]string, 0, 1) + if m.clearedagents { + edges = append(edges, project.EdgeAgents) + } + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *ProjectMutation) EdgeCleared(name string) bool { + switch name { + case project.EdgeAgents: + return m.clearedagents + } + return false +} + +// ClearEdge clears the value of the edge with the given name. It returns an error +// if that edge is not defined in the schema. +func (m *ProjectMutation) ClearEdge(name string) error { + switch name { + } + return fmt.Errorf("unknown Project unique edge %s", name) +} + +// ResetEdge resets all changes to the edge with the given name in this mutation. +// It returns an error if the edge is not defined in the schema. +func (m *ProjectMutation) ResetEdge(name string) error { + switch name { + case project.EdgeAgents: + m.ResetAgents() + return nil + } + return fmt.Errorf("unknown Project edge %s", name) +} + +// ProjectContributorMutation represents an operation that mutates the ProjectContributor nodes in the graph. +type ProjectContributorMutation struct { + config + op Op + typ string + id *uuid.UUID + project_id *uuid.UUID + broker_id *uuid.UUID + broker_name *string + mode *string + status *string + profiles *string + last_seen *time.Time + local_path *string + linked_by *string + linked_at *time.Time + clearedFields map[string]struct{} + done bool + oldValue func(context.Context) (*ProjectContributor, error) + predicates []predicate.ProjectContributor +} + +var _ ent.Mutation = (*ProjectContributorMutation)(nil) + +// projectcontributorOption allows management of the mutation configuration using functional options. +type projectcontributorOption func(*ProjectContributorMutation) + +// newProjectContributorMutation creates new mutation for the ProjectContributor entity. +func newProjectContributorMutation(c config, op Op, opts ...projectcontributorOption) *ProjectContributorMutation { + m := &ProjectContributorMutation{ + config: c, + op: op, + typ: TypeProjectContributor, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// withProjectContributorID sets the ID field of the mutation. +func withProjectContributorID(id uuid.UUID) projectcontributorOption { + return func(m *ProjectContributorMutation) { + var ( + err error + once sync.Once + value *ProjectContributor + ) + m.oldValue = func(ctx context.Context) (*ProjectContributor, error) { + once.Do(func() { + if m.done { + err = errors.New("querying old values post mutation is not allowed") + } else { + value, err = m.Client().ProjectContributor.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } +} + +// withProjectContributor sets the old ProjectContributor of the mutation. +func withProjectContributor(node *ProjectContributor) projectcontributorOption { + return func(m *ProjectContributorMutation) { + m.oldValue = func(context.Context) (*ProjectContributor, error) { + return node, nil + } + m.id = &node.ID + } +} + +// Client returns a new `ent.Client` from the mutation. If the mutation was +// executed in a transaction (ent.Tx), a transactional client is returned. +func (m ProjectContributorMutation) Client() *Client { + client := &Client{config: m.config} + client.init() + return client +} + +// Tx returns an `ent.Tx` for mutations that were executed in transactions; +// it returns an error otherwise. +func (m ProjectContributorMutation) Tx() (*Tx, error) { + if _, ok := m.driver.(*txDriver); !ok { + return nil, errors.New("ent: mutation is not running in a transaction") + } + tx := &Tx{config: m.config} + tx.init() + return tx, nil +} + +// SetID sets the value of the id field. Note that this +// operation is only accepted on creation of ProjectContributor entities. +func (m *ProjectContributorMutation) SetID(id uuid.UUID) { + m.id = &id +} + +// ID returns the ID value in the mutation. Note that the ID is only available +// if it was provided to the builder or after it was returned from the database. +func (m *ProjectContributorMutation) ID() (id uuid.UUID, exists bool) { + if m.id == nil { + return + } + return *m.id, true +} + +// IDs queries the database and returns the entity ids that match the mutation's predicate. +// That means, if the mutation is applied within a transaction with an isolation level such +// as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated +// or updated by the mutation. +func (m *ProjectContributorMutation) IDs(ctx context.Context) ([]uuid.UUID, error) { + switch { + case m.op.Is(OpUpdateOne | OpDeleteOne): + id, exists := m.ID() + if exists { + return []uuid.UUID{id}, nil + } + fallthrough + case m.op.Is(OpUpdate | OpDelete): + return m.Client().ProjectContributor.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } +} + +// SetProjectID sets the "project_id" field. +func (m *ProjectContributorMutation) SetProjectID(u uuid.UUID) { + m.project_id = &u +} + +// ProjectID returns the value of the "project_id" field in the mutation. +func (m *ProjectContributorMutation) ProjectID() (r uuid.UUID, exists bool) { + v := m.project_id + if v == nil { + return + } + return *v, true +} + +// OldProjectID returns the old "project_id" field's value of the ProjectContributor entity. +// If the ProjectContributor object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ProjectContributorMutation) OldProjectID(ctx context.Context) (v uuid.UUID, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldProjectID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldProjectID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldProjectID: %w", err) + } + return oldValue.ProjectID, nil +} + +// ResetProjectID resets all changes to the "project_id" field. +func (m *ProjectContributorMutation) ResetProjectID() { + m.project_id = nil +} + +// SetBrokerID sets the "broker_id" field. +func (m *ProjectContributorMutation) SetBrokerID(u uuid.UUID) { + m.broker_id = &u +} + +// BrokerID returns the value of the "broker_id" field in the mutation. +func (m *ProjectContributorMutation) BrokerID() (r uuid.UUID, exists bool) { + v := m.broker_id + if v == nil { + return + } + return *v, true +} + +// OldBrokerID returns the old "broker_id" field's value of the ProjectContributor entity. +// If the ProjectContributor object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ProjectContributorMutation) OldBrokerID(ctx context.Context) (v uuid.UUID, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldBrokerID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldBrokerID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldBrokerID: %w", err) + } + return oldValue.BrokerID, nil +} + +// ResetBrokerID resets all changes to the "broker_id" field. +func (m *ProjectContributorMutation) ResetBrokerID() { + m.broker_id = nil +} + +// SetBrokerName sets the "broker_name" field. +func (m *ProjectContributorMutation) SetBrokerName(s string) { + m.broker_name = &s +} + +// BrokerName returns the value of the "broker_name" field in the mutation. +func (m *ProjectContributorMutation) BrokerName() (r string, exists bool) { + v := m.broker_name + if v == nil { + return + } + return *v, true +} + +// OldBrokerName returns the old "broker_name" field's value of the ProjectContributor entity. +// If the ProjectContributor object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ProjectContributorMutation) OldBrokerName(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldBrokerName is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldBrokerName requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldBrokerName: %w", err) + } + return oldValue.BrokerName, nil +} + +// ResetBrokerName resets all changes to the "broker_name" field. +func (m *ProjectContributorMutation) ResetBrokerName() { + m.broker_name = nil +} + +// SetMode sets the "mode" field. +func (m *ProjectContributorMutation) SetMode(s string) { + m.mode = &s +} + +// Mode returns the value of the "mode" field in the mutation. +func (m *ProjectContributorMutation) Mode() (r string, exists bool) { + v := m.mode + if v == nil { + return + } + return *v, true +} + +// OldMode returns the old "mode" field's value of the ProjectContributor entity. +// If the ProjectContributor object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ProjectContributorMutation) OldMode(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldMode is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldMode requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldMode: %w", err) + } + return oldValue.Mode, nil +} + +// ResetMode resets all changes to the "mode" field. +func (m *ProjectContributorMutation) ResetMode() { + m.mode = nil +} + +// SetStatus sets the "status" field. +func (m *ProjectContributorMutation) SetStatus(s string) { + m.status = &s +} + +// Status returns the value of the "status" field in the mutation. +func (m *ProjectContributorMutation) Status() (r string, exists bool) { + v := m.status + if v == nil { + return + } + return *v, true +} + +// OldStatus returns the old "status" field's value of the ProjectContributor entity. +// If the ProjectContributor object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ProjectContributorMutation) OldStatus(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldStatus is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldStatus requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldStatus: %w", err) + } + return oldValue.Status, nil +} + +// ResetStatus resets all changes to the "status" field. +func (m *ProjectContributorMutation) ResetStatus() { + m.status = nil +} + +// SetProfiles sets the "profiles" field. +func (m *ProjectContributorMutation) SetProfiles(s string) { + m.profiles = &s +} + +// Profiles returns the value of the "profiles" field in the mutation. +func (m *ProjectContributorMutation) Profiles() (r string, exists bool) { + v := m.profiles + if v == nil { + return + } + return *v, true +} + +// OldProfiles returns the old "profiles" field's value of the ProjectContributor entity. +// If the ProjectContributor object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ProjectContributorMutation) OldProfiles(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldProfiles is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldProfiles requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldProfiles: %w", err) + } + return oldValue.Profiles, nil +} + +// ClearProfiles clears the value of the "profiles" field. +func (m *ProjectContributorMutation) ClearProfiles() { + m.profiles = nil + m.clearedFields[projectcontributor.FieldProfiles] = struct{}{} +} + +// ProfilesCleared returns if the "profiles" field was cleared in this mutation. +func (m *ProjectContributorMutation) ProfilesCleared() bool { + _, ok := m.clearedFields[projectcontributor.FieldProfiles] + return ok +} + +// ResetProfiles resets all changes to the "profiles" field. +func (m *ProjectContributorMutation) ResetProfiles() { + m.profiles = nil + delete(m.clearedFields, projectcontributor.FieldProfiles) +} + +// SetLastSeen sets the "last_seen" field. +func (m *ProjectContributorMutation) SetLastSeen(t time.Time) { + m.last_seen = &t +} + +// LastSeen returns the value of the "last_seen" field in the mutation. +func (m *ProjectContributorMutation) LastSeen() (r time.Time, exists bool) { + v := m.last_seen + if v == nil { + return + } + return *v, true +} + +// OldLastSeen returns the old "last_seen" field's value of the ProjectContributor entity. +// If the ProjectContributor object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ProjectContributorMutation) OldLastSeen(ctx context.Context) (v *time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldLastSeen is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldLastSeen requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldLastSeen: %w", err) + } + return oldValue.LastSeen, nil +} + +// ClearLastSeen clears the value of the "last_seen" field. +func (m *ProjectContributorMutation) ClearLastSeen() { + m.last_seen = nil + m.clearedFields[projectcontributor.FieldLastSeen] = struct{}{} +} + +// LastSeenCleared returns if the "last_seen" field was cleared in this mutation. +func (m *ProjectContributorMutation) LastSeenCleared() bool { + _, ok := m.clearedFields[projectcontributor.FieldLastSeen] + return ok +} + +// ResetLastSeen resets all changes to the "last_seen" field. +func (m *ProjectContributorMutation) ResetLastSeen() { + m.last_seen = nil + delete(m.clearedFields, projectcontributor.FieldLastSeen) +} + +// SetLocalPath sets the "local_path" field. +func (m *ProjectContributorMutation) SetLocalPath(s string) { + m.local_path = &s +} + +// LocalPath returns the value of the "local_path" field in the mutation. +func (m *ProjectContributorMutation) LocalPath() (r string, exists bool) { + v := m.local_path + if v == nil { + return + } + return *v, true +} + +// OldLocalPath returns the old "local_path" field's value of the ProjectContributor entity. +// If the ProjectContributor object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ProjectContributorMutation) OldLocalPath(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldLocalPath is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldLocalPath requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldLocalPath: %w", err) + } + return oldValue.LocalPath, nil +} + +// ClearLocalPath clears the value of the "local_path" field. +func (m *ProjectContributorMutation) ClearLocalPath() { + m.local_path = nil + m.clearedFields[projectcontributor.FieldLocalPath] = struct{}{} +} + +// LocalPathCleared returns if the "local_path" field was cleared in this mutation. +func (m *ProjectContributorMutation) LocalPathCleared() bool { + _, ok := m.clearedFields[projectcontributor.FieldLocalPath] + return ok +} + +// ResetLocalPath resets all changes to the "local_path" field. +func (m *ProjectContributorMutation) ResetLocalPath() { + m.local_path = nil + delete(m.clearedFields, projectcontributor.FieldLocalPath) +} + +// SetLinkedBy sets the "linked_by" field. +func (m *ProjectContributorMutation) SetLinkedBy(s string) { + m.linked_by = &s +} + +// LinkedBy returns the value of the "linked_by" field in the mutation. +func (m *ProjectContributorMutation) LinkedBy() (r string, exists bool) { + v := m.linked_by + if v == nil { + return + } + return *v, true +} + +// OldLinkedBy returns the old "linked_by" field's value of the ProjectContributor entity. +// If the ProjectContributor object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ProjectContributorMutation) OldLinkedBy(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldLinkedBy is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldLinkedBy requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldLinkedBy: %w", err) + } + return oldValue.LinkedBy, nil +} + +// ClearLinkedBy clears the value of the "linked_by" field. +func (m *ProjectContributorMutation) ClearLinkedBy() { + m.linked_by = nil + m.clearedFields[projectcontributor.FieldLinkedBy] = struct{}{} +} + +// LinkedByCleared returns if the "linked_by" field was cleared in this mutation. +func (m *ProjectContributorMutation) LinkedByCleared() bool { + _, ok := m.clearedFields[projectcontributor.FieldLinkedBy] + return ok +} + +// ResetLinkedBy resets all changes to the "linked_by" field. +func (m *ProjectContributorMutation) ResetLinkedBy() { + m.linked_by = nil + delete(m.clearedFields, projectcontributor.FieldLinkedBy) +} + +// SetLinkedAt sets the "linked_at" field. +func (m *ProjectContributorMutation) SetLinkedAt(t time.Time) { + m.linked_at = &t +} + +// LinkedAt returns the value of the "linked_at" field in the mutation. +func (m *ProjectContributorMutation) LinkedAt() (r time.Time, exists bool) { + v := m.linked_at + if v == nil { + return + } + return *v, true +} + +// OldLinkedAt returns the old "linked_at" field's value of the ProjectContributor entity. +// If the ProjectContributor object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ProjectContributorMutation) OldLinkedAt(ctx context.Context) (v *time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldLinkedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldLinkedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldLinkedAt: %w", err) + } + return oldValue.LinkedAt, nil +} + +// ClearLinkedAt clears the value of the "linked_at" field. +func (m *ProjectContributorMutation) ClearLinkedAt() { + m.linked_at = nil + m.clearedFields[projectcontributor.FieldLinkedAt] = struct{}{} +} + +// LinkedAtCleared returns if the "linked_at" field was cleared in this mutation. +func (m *ProjectContributorMutation) LinkedAtCleared() bool { + _, ok := m.clearedFields[projectcontributor.FieldLinkedAt] + return ok +} + +// ResetLinkedAt resets all changes to the "linked_at" field. +func (m *ProjectContributorMutation) ResetLinkedAt() { + m.linked_at = nil + delete(m.clearedFields, projectcontributor.FieldLinkedAt) +} + +// Where appends a list predicates to the ProjectContributorMutation builder. +func (m *ProjectContributorMutation) Where(ps ...predicate.ProjectContributor) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the ProjectContributorMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *ProjectContributorMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.ProjectContributor, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *ProjectContributorMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *ProjectContributorMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (ProjectContributor). +func (m *ProjectContributorMutation) Type() string { + return m.typ +} + +// Fields returns all fields that were changed during this mutation. Note that in +// order to get all numeric fields that were incremented/decremented, call +// AddedFields(). +func (m *ProjectContributorMutation) Fields() []string { + fields := make([]string, 0, 10) + if m.project_id != nil { + fields = append(fields, projectcontributor.FieldProjectID) + } + if m.broker_id != nil { + fields = append(fields, projectcontributor.FieldBrokerID) + } + if m.broker_name != nil { + fields = append(fields, projectcontributor.FieldBrokerName) + } + if m.mode != nil { + fields = append(fields, projectcontributor.FieldMode) + } + if m.status != nil { + fields = append(fields, projectcontributor.FieldStatus) + } + if m.profiles != nil { + fields = append(fields, projectcontributor.FieldProfiles) + } + if m.last_seen != nil { + fields = append(fields, projectcontributor.FieldLastSeen) + } + if m.local_path != nil { + fields = append(fields, projectcontributor.FieldLocalPath) + } + if m.linked_by != nil { + fields = append(fields, projectcontributor.FieldLinkedBy) + } + if m.linked_at != nil { + fields = append(fields, projectcontributor.FieldLinkedAt) + } + return fields +} + +// Field returns the value of a field with the given name. The second boolean +// return value indicates that this field was not set, or was not defined in the +// schema. +func (m *ProjectContributorMutation) Field(name string) (ent.Value, bool) { + switch name { + case projectcontributor.FieldProjectID: + return m.ProjectID() + case projectcontributor.FieldBrokerID: + return m.BrokerID() + case projectcontributor.FieldBrokerName: + return m.BrokerName() + case projectcontributor.FieldMode: + return m.Mode() + case projectcontributor.FieldStatus: + return m.Status() + case projectcontributor.FieldProfiles: + return m.Profiles() + case projectcontributor.FieldLastSeen: + return m.LastSeen() + case projectcontributor.FieldLocalPath: + return m.LocalPath() + case projectcontributor.FieldLinkedBy: + return m.LinkedBy() + case projectcontributor.FieldLinkedAt: + return m.LinkedAt() + } + return nil, false +} + +// OldField returns the old value of the field from the database. An error is +// returned if the mutation operation is not UpdateOne, or the query to the +// database failed. +func (m *ProjectContributorMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + switch name { + case projectcontributor.FieldProjectID: + return m.OldProjectID(ctx) + case projectcontributor.FieldBrokerID: + return m.OldBrokerID(ctx) + case projectcontributor.FieldBrokerName: + return m.OldBrokerName(ctx) + case projectcontributor.FieldMode: + return m.OldMode(ctx) + case projectcontributor.FieldStatus: + return m.OldStatus(ctx) + case projectcontributor.FieldProfiles: + return m.OldProfiles(ctx) + case projectcontributor.FieldLastSeen: + return m.OldLastSeen(ctx) + case projectcontributor.FieldLocalPath: + return m.OldLocalPath(ctx) + case projectcontributor.FieldLinkedBy: + return m.OldLinkedBy(ctx) + case projectcontributor.FieldLinkedAt: + return m.OldLinkedAt(ctx) + } + return nil, fmt.Errorf("unknown ProjectContributor field %s", name) +} + +// SetField sets the value of a field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *ProjectContributorMutation) SetField(name string, value ent.Value) error { + switch name { + case projectcontributor.FieldProjectID: + v, ok := value.(uuid.UUID) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetProjectID(v) + return nil + case projectcontributor.FieldBrokerID: + v, ok := value.(uuid.UUID) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetBrokerID(v) + return nil + case projectcontributor.FieldBrokerName: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetBrokerName(v) + return nil + case projectcontributor.FieldMode: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetMode(v) + return nil + case projectcontributor.FieldStatus: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetStatus(v) + return nil + case projectcontributor.FieldProfiles: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetProfiles(v) + return nil + case projectcontributor.FieldLastSeen: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetLastSeen(v) + return nil + case projectcontributor.FieldLocalPath: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetLocalPath(v) + return nil + case projectcontributor.FieldLinkedBy: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetLinkedBy(v) + return nil + case projectcontributor.FieldLinkedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetLinkedAt(v) + return nil + } + return fmt.Errorf("unknown ProjectContributor field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *ProjectContributorMutation) AddedFields() []string { + return nil +} + +// AddedField returns the numeric value that was incremented/decremented on a field +// with the given name. The second boolean return value indicates that this field +// was not set, or was not defined in the schema. +func (m *ProjectContributorMutation) AddedField(name string) (ent.Value, bool) { + return nil, false +} + +// AddField adds the value to the field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *ProjectContributorMutation) AddField(name string, value ent.Value) error { + switch name { + } + return fmt.Errorf("unknown ProjectContributor numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *ProjectContributorMutation) ClearedFields() []string { + var fields []string + if m.FieldCleared(projectcontributor.FieldProfiles) { + fields = append(fields, projectcontributor.FieldProfiles) + } + if m.FieldCleared(projectcontributor.FieldLastSeen) { + fields = append(fields, projectcontributor.FieldLastSeen) + } + if m.FieldCleared(projectcontributor.FieldLocalPath) { + fields = append(fields, projectcontributor.FieldLocalPath) + } + if m.FieldCleared(projectcontributor.FieldLinkedBy) { + fields = append(fields, projectcontributor.FieldLinkedBy) + } + if m.FieldCleared(projectcontributor.FieldLinkedAt) { + fields = append(fields, projectcontributor.FieldLinkedAt) + } + return fields +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *ProjectContributorMutation) FieldCleared(name string) bool { + _, ok := m.clearedFields[name] + return ok +} + +// ClearField clears the value of the field with the given name. It returns an +// error if the field is not defined in the schema. +func (m *ProjectContributorMutation) ClearField(name string) error { + switch name { + case projectcontributor.FieldProfiles: + m.ClearProfiles() + return nil + case projectcontributor.FieldLastSeen: + m.ClearLastSeen() + return nil + case projectcontributor.FieldLocalPath: + m.ClearLocalPath() + return nil + case projectcontributor.FieldLinkedBy: + m.ClearLinkedBy() + return nil + case projectcontributor.FieldLinkedAt: + m.ClearLinkedAt() + return nil + } + return fmt.Errorf("unknown ProjectContributor nullable field %s", name) +} + +// ResetField resets all changes in the mutation for the field with the given name. +// It returns an error if the field is not defined in the schema. +func (m *ProjectContributorMutation) ResetField(name string) error { + switch name { + case projectcontributor.FieldProjectID: + m.ResetProjectID() + return nil + case projectcontributor.FieldBrokerID: + m.ResetBrokerID() + return nil + case projectcontributor.FieldBrokerName: + m.ResetBrokerName() + return nil + case projectcontributor.FieldMode: + m.ResetMode() + return nil + case projectcontributor.FieldStatus: + m.ResetStatus() + return nil + case projectcontributor.FieldProfiles: + m.ResetProfiles() + return nil + case projectcontributor.FieldLastSeen: + m.ResetLastSeen() + return nil + case projectcontributor.FieldLocalPath: + m.ResetLocalPath() + return nil + case projectcontributor.FieldLinkedBy: + m.ResetLinkedBy() + return nil + case projectcontributor.FieldLinkedAt: + m.ResetLinkedAt() + return nil + } + return fmt.Errorf("unknown ProjectContributor field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *ProjectContributorMutation) AddedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *ProjectContributorMutation) AddedIDs(name string) []ent.Value { + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *ProjectContributorMutation) RemovedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *ProjectContributorMutation) RemovedIDs(name string) []ent.Value { + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *ProjectContributorMutation) ClearedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *ProjectContributorMutation) EdgeCleared(name string) bool { + return false +} + +// ClearEdge clears the value of the edge with the given name. It returns an error +// if that edge is not defined in the schema. +func (m *ProjectContributorMutation) ClearEdge(name string) error { + return fmt.Errorf("unknown ProjectContributor unique edge %s", name) +} + +// ResetEdge resets all changes to the edge with the given name in this mutation. +// It returns an error if the edge is not defined in the schema. +func (m *ProjectContributorMutation) ResetEdge(name string) error { + return fmt.Errorf("unknown ProjectContributor edge %s", name) +} + +// ProjectSyncStateMutation represents an operation that mutates the ProjectSyncState nodes in the graph. +type ProjectSyncStateMutation struct { + config + op Op + typ string + id *uuid.UUID + project_id *uuid.UUID + broker_id *string + last_sync_time *time.Time + last_commit_sha *string + file_count *int + addfile_count *int + total_bytes *int64 + addtotal_bytes *int64 + clearedFields map[string]struct{} + done bool + oldValue func(context.Context) (*ProjectSyncState, error) + predicates []predicate.ProjectSyncState +} + +var _ ent.Mutation = (*ProjectSyncStateMutation)(nil) + +// projectsyncstateOption allows management of the mutation configuration using functional options. +type projectsyncstateOption func(*ProjectSyncStateMutation) + +// newProjectSyncStateMutation creates new mutation for the ProjectSyncState entity. +func newProjectSyncStateMutation(c config, op Op, opts ...projectsyncstateOption) *ProjectSyncStateMutation { + m := &ProjectSyncStateMutation{ + config: c, + op: op, + typ: TypeProjectSyncState, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// withProjectSyncStateID sets the ID field of the mutation. +func withProjectSyncStateID(id uuid.UUID) projectsyncstateOption { + return func(m *ProjectSyncStateMutation) { + var ( + err error + once sync.Once + value *ProjectSyncState + ) + m.oldValue = func(ctx context.Context) (*ProjectSyncState, error) { + once.Do(func() { + if m.done { + err = errors.New("querying old values post mutation is not allowed") + } else { + value, err = m.Client().ProjectSyncState.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } +} + +// withProjectSyncState sets the old ProjectSyncState of the mutation. +func withProjectSyncState(node *ProjectSyncState) projectsyncstateOption { + return func(m *ProjectSyncStateMutation) { + m.oldValue = func(context.Context) (*ProjectSyncState, error) { + return node, nil + } + m.id = &node.ID + } +} + +// Client returns a new `ent.Client` from the mutation. If the mutation was +// executed in a transaction (ent.Tx), a transactional client is returned. +func (m ProjectSyncStateMutation) Client() *Client { + client := &Client{config: m.config} + client.init() + return client +} + +// Tx returns an `ent.Tx` for mutations that were executed in transactions; +// it returns an error otherwise. +func (m ProjectSyncStateMutation) Tx() (*Tx, error) { + if _, ok := m.driver.(*txDriver); !ok { + return nil, errors.New("ent: mutation is not running in a transaction") + } + tx := &Tx{config: m.config} + tx.init() + return tx, nil +} + +// SetID sets the value of the id field. Note that this +// operation is only accepted on creation of ProjectSyncState entities. +func (m *ProjectSyncStateMutation) SetID(id uuid.UUID) { + m.id = &id +} + +// ID returns the ID value in the mutation. Note that the ID is only available +// if it was provided to the builder or after it was returned from the database. +func (m *ProjectSyncStateMutation) ID() (id uuid.UUID, exists bool) { + if m.id == nil { + return + } + return *m.id, true +} + +// IDs queries the database and returns the entity ids that match the mutation's predicate. +// That means, if the mutation is applied within a transaction with an isolation level such +// as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated +// or updated by the mutation. +func (m *ProjectSyncStateMutation) IDs(ctx context.Context) ([]uuid.UUID, error) { + switch { + case m.op.Is(OpUpdateOne | OpDeleteOne): + id, exists := m.ID() + if exists { + return []uuid.UUID{id}, nil + } + fallthrough + case m.op.Is(OpUpdate | OpDelete): + return m.Client().ProjectSyncState.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } +} + +// SetProjectID sets the "project_id" field. +func (m *ProjectSyncStateMutation) SetProjectID(u uuid.UUID) { + m.project_id = &u +} + +// ProjectID returns the value of the "project_id" field in the mutation. +func (m *ProjectSyncStateMutation) ProjectID() (r uuid.UUID, exists bool) { + v := m.project_id + if v == nil { + return + } + return *v, true +} + +// OldProjectID returns the old "project_id" field's value of the ProjectSyncState entity. +// If the ProjectSyncState object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ProjectSyncStateMutation) OldProjectID(ctx context.Context) (v uuid.UUID, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldProjectID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldProjectID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldProjectID: %w", err) + } + return oldValue.ProjectID, nil +} + +// ResetProjectID resets all changes to the "project_id" field. +func (m *ProjectSyncStateMutation) ResetProjectID() { + m.project_id = nil +} + +// SetBrokerID sets the "broker_id" field. +func (m *ProjectSyncStateMutation) SetBrokerID(s string) { + m.broker_id = &s +} + +// BrokerID returns the value of the "broker_id" field in the mutation. +func (m *ProjectSyncStateMutation) BrokerID() (r string, exists bool) { + v := m.broker_id + if v == nil { + return + } + return *v, true +} + +// OldBrokerID returns the old "broker_id" field's value of the ProjectSyncState entity. +// If the ProjectSyncState object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ProjectSyncStateMutation) OldBrokerID(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldBrokerID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldBrokerID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldBrokerID: %w", err) + } + return oldValue.BrokerID, nil +} + +// ResetBrokerID resets all changes to the "broker_id" field. +func (m *ProjectSyncStateMutation) ResetBrokerID() { + m.broker_id = nil +} + +// SetLastSyncTime sets the "last_sync_time" field. +func (m *ProjectSyncStateMutation) SetLastSyncTime(t time.Time) { + m.last_sync_time = &t +} + +// LastSyncTime returns the value of the "last_sync_time" field in the mutation. +func (m *ProjectSyncStateMutation) LastSyncTime() (r time.Time, exists bool) { + v := m.last_sync_time + if v == nil { + return + } + return *v, true +} + +// OldLastSyncTime returns the old "last_sync_time" field's value of the ProjectSyncState entity. +// If the ProjectSyncState object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ProjectSyncStateMutation) OldLastSyncTime(ctx context.Context) (v *time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldLastSyncTime is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldLastSyncTime requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldLastSyncTime: %w", err) + } + return oldValue.LastSyncTime, nil +} + +// ClearLastSyncTime clears the value of the "last_sync_time" field. +func (m *ProjectSyncStateMutation) ClearLastSyncTime() { + m.last_sync_time = nil + m.clearedFields[projectsyncstate.FieldLastSyncTime] = struct{}{} +} + +// LastSyncTimeCleared returns if the "last_sync_time" field was cleared in this mutation. +func (m *ProjectSyncStateMutation) LastSyncTimeCleared() bool { + _, ok := m.clearedFields[projectsyncstate.FieldLastSyncTime] + return ok +} + +// ResetLastSyncTime resets all changes to the "last_sync_time" field. +func (m *ProjectSyncStateMutation) ResetLastSyncTime() { + m.last_sync_time = nil + delete(m.clearedFields, projectsyncstate.FieldLastSyncTime) +} + +// SetLastCommitSha sets the "last_commit_sha" field. +func (m *ProjectSyncStateMutation) SetLastCommitSha(s string) { + m.last_commit_sha = &s +} + +// LastCommitSha returns the value of the "last_commit_sha" field in the mutation. +func (m *ProjectSyncStateMutation) LastCommitSha() (r string, exists bool) { + v := m.last_commit_sha + if v == nil { + return + } + return *v, true +} + +// OldLastCommitSha returns the old "last_commit_sha" field's value of the ProjectSyncState entity. +// If the ProjectSyncState object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ProjectSyncStateMutation) OldLastCommitSha(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldLastCommitSha is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldLastCommitSha requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldLastCommitSha: %w", err) + } + return oldValue.LastCommitSha, nil +} + +// ClearLastCommitSha clears the value of the "last_commit_sha" field. +func (m *ProjectSyncStateMutation) ClearLastCommitSha() { + m.last_commit_sha = nil + m.clearedFields[projectsyncstate.FieldLastCommitSha] = struct{}{} +} + +// LastCommitShaCleared returns if the "last_commit_sha" field was cleared in this mutation. +func (m *ProjectSyncStateMutation) LastCommitShaCleared() bool { + _, ok := m.clearedFields[projectsyncstate.FieldLastCommitSha] + return ok +} + +// ResetLastCommitSha resets all changes to the "last_commit_sha" field. +func (m *ProjectSyncStateMutation) ResetLastCommitSha() { + m.last_commit_sha = nil + delete(m.clearedFields, projectsyncstate.FieldLastCommitSha) +} + +// SetFileCount sets the "file_count" field. +func (m *ProjectSyncStateMutation) SetFileCount(i int) { + m.file_count = &i + m.addfile_count = nil +} + +// FileCount returns the value of the "file_count" field in the mutation. +func (m *ProjectSyncStateMutation) FileCount() (r int, exists bool) { + v := m.file_count + if v == nil { + return + } + return *v, true +} + +// OldFileCount returns the old "file_count" field's value of the ProjectSyncState entity. +// If the ProjectSyncState object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ProjectSyncStateMutation) OldFileCount(ctx context.Context) (v int, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldFileCount is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldFileCount requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldFileCount: %w", err) + } + return oldValue.FileCount, nil +} + +// AddFileCount adds i to the "file_count" field. +func (m *ProjectSyncStateMutation) AddFileCount(i int) { + if m.addfile_count != nil { + *m.addfile_count += i + } else { + m.addfile_count = &i + } +} + +// AddedFileCount returns the value that was added to the "file_count" field in this mutation. +func (m *ProjectSyncStateMutation) AddedFileCount() (r int, exists bool) { + v := m.addfile_count + if v == nil { + return + } + return *v, true +} + +// ResetFileCount resets all changes to the "file_count" field. +func (m *ProjectSyncStateMutation) ResetFileCount() { + m.file_count = nil + m.addfile_count = nil +} + +// SetTotalBytes sets the "total_bytes" field. +func (m *ProjectSyncStateMutation) SetTotalBytes(i int64) { + m.total_bytes = &i + m.addtotal_bytes = nil +} + +// TotalBytes returns the value of the "total_bytes" field in the mutation. +func (m *ProjectSyncStateMutation) TotalBytes() (r int64, exists bool) { + v := m.total_bytes + if v == nil { + return + } + return *v, true +} + +// OldTotalBytes returns the old "total_bytes" field's value of the ProjectSyncState entity. +// If the ProjectSyncState object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ProjectSyncStateMutation) OldTotalBytes(ctx context.Context) (v int64, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldTotalBytes is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldTotalBytes requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldTotalBytes: %w", err) + } + return oldValue.TotalBytes, nil +} + +// AddTotalBytes adds i to the "total_bytes" field. +func (m *ProjectSyncStateMutation) AddTotalBytes(i int64) { + if m.addtotal_bytes != nil { + *m.addtotal_bytes += i + } else { + m.addtotal_bytes = &i + } +} + +// AddedTotalBytes returns the value that was added to the "total_bytes" field in this mutation. +func (m *ProjectSyncStateMutation) AddedTotalBytes() (r int64, exists bool) { + v := m.addtotal_bytes + if v == nil { + return + } + return *v, true +} + +// ResetTotalBytes resets all changes to the "total_bytes" field. +func (m *ProjectSyncStateMutation) ResetTotalBytes() { + m.total_bytes = nil + m.addtotal_bytes = nil +} + +// Where appends a list predicates to the ProjectSyncStateMutation builder. +func (m *ProjectSyncStateMutation) Where(ps ...predicate.ProjectSyncState) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the ProjectSyncStateMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *ProjectSyncStateMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.ProjectSyncState, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *ProjectSyncStateMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *ProjectSyncStateMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (ProjectSyncState). +func (m *ProjectSyncStateMutation) Type() string { + return m.typ +} + +// Fields returns all fields that were changed during this mutation. Note that in +// order to get all numeric fields that were incremented/decremented, call +// AddedFields(). +func (m *ProjectSyncStateMutation) Fields() []string { + fields := make([]string, 0, 6) + if m.project_id != nil { + fields = append(fields, projectsyncstate.FieldProjectID) + } + if m.broker_id != nil { + fields = append(fields, projectsyncstate.FieldBrokerID) + } + if m.last_sync_time != nil { + fields = append(fields, projectsyncstate.FieldLastSyncTime) + } + if m.last_commit_sha != nil { + fields = append(fields, projectsyncstate.FieldLastCommitSha) + } + if m.file_count != nil { + fields = append(fields, projectsyncstate.FieldFileCount) + } + if m.total_bytes != nil { + fields = append(fields, projectsyncstate.FieldTotalBytes) + } + return fields +} + +// Field returns the value of a field with the given name. The second boolean +// return value indicates that this field was not set, or was not defined in the +// schema. +func (m *ProjectSyncStateMutation) Field(name string) (ent.Value, bool) { + switch name { + case projectsyncstate.FieldProjectID: + return m.ProjectID() + case projectsyncstate.FieldBrokerID: + return m.BrokerID() + case projectsyncstate.FieldLastSyncTime: + return m.LastSyncTime() + case projectsyncstate.FieldLastCommitSha: + return m.LastCommitSha() + case projectsyncstate.FieldFileCount: + return m.FileCount() + case projectsyncstate.FieldTotalBytes: + return m.TotalBytes() + } + return nil, false +} + +// OldField returns the old value of the field from the database. An error is +// returned if the mutation operation is not UpdateOne, or the query to the +// database failed. +func (m *ProjectSyncStateMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + switch name { + case projectsyncstate.FieldProjectID: + return m.OldProjectID(ctx) + case projectsyncstate.FieldBrokerID: + return m.OldBrokerID(ctx) + case projectsyncstate.FieldLastSyncTime: + return m.OldLastSyncTime(ctx) + case projectsyncstate.FieldLastCommitSha: + return m.OldLastCommitSha(ctx) + case projectsyncstate.FieldFileCount: + return m.OldFileCount(ctx) + case projectsyncstate.FieldTotalBytes: + return m.OldTotalBytes(ctx) + } + return nil, fmt.Errorf("unknown ProjectSyncState field %s", name) +} + +// SetField sets the value of a field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *ProjectSyncStateMutation) SetField(name string, value ent.Value) error { + switch name { + case projectsyncstate.FieldProjectID: + v, ok := value.(uuid.UUID) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetProjectID(v) + return nil + case projectsyncstate.FieldBrokerID: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetBrokerID(v) + return nil + case projectsyncstate.FieldLastSyncTime: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetLastSyncTime(v) + return nil + case projectsyncstate.FieldLastCommitSha: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetLastCommitSha(v) + return nil + case projectsyncstate.FieldFileCount: + v, ok := value.(int) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetFileCount(v) + return nil + case projectsyncstate.FieldTotalBytes: + v, ok := value.(int64) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetTotalBytes(v) + return nil + } + return fmt.Errorf("unknown ProjectSyncState field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *ProjectSyncStateMutation) AddedFields() []string { + var fields []string + if m.addfile_count != nil { + fields = append(fields, projectsyncstate.FieldFileCount) + } + if m.addtotal_bytes != nil { + fields = append(fields, projectsyncstate.FieldTotalBytes) + } + return fields +} + +// AddedField returns the numeric value that was incremented/decremented on a field +// with the given name. The second boolean return value indicates that this field +// was not set, or was not defined in the schema. +func (m *ProjectSyncStateMutation) AddedField(name string) (ent.Value, bool) { + switch name { + case projectsyncstate.FieldFileCount: + return m.AddedFileCount() + case projectsyncstate.FieldTotalBytes: + return m.AddedTotalBytes() + } + return nil, false +} + +// AddField adds the value to the field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *ProjectSyncStateMutation) AddField(name string, value ent.Value) error { + switch name { + case projectsyncstate.FieldFileCount: + v, ok := value.(int) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.AddFileCount(v) + return nil + case projectsyncstate.FieldTotalBytes: + v, ok := value.(int64) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.AddTotalBytes(v) + return nil + } + return fmt.Errorf("unknown ProjectSyncState numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *ProjectSyncStateMutation) ClearedFields() []string { + var fields []string + if m.FieldCleared(projectsyncstate.FieldLastSyncTime) { + fields = append(fields, projectsyncstate.FieldLastSyncTime) + } + if m.FieldCleared(projectsyncstate.FieldLastCommitSha) { + fields = append(fields, projectsyncstate.FieldLastCommitSha) + } + return fields +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *ProjectSyncStateMutation) FieldCleared(name string) bool { + _, ok := m.clearedFields[name] + return ok +} + +// ClearField clears the value of the field with the given name. It returns an +// error if the field is not defined in the schema. +func (m *ProjectSyncStateMutation) ClearField(name string) error { + switch name { + case projectsyncstate.FieldLastSyncTime: + m.ClearLastSyncTime() + return nil + case projectsyncstate.FieldLastCommitSha: + m.ClearLastCommitSha() + return nil + } + return fmt.Errorf("unknown ProjectSyncState nullable field %s", name) +} + +// ResetField resets all changes in the mutation for the field with the given name. +// It returns an error if the field is not defined in the schema. +func (m *ProjectSyncStateMutation) ResetField(name string) error { + switch name { + case projectsyncstate.FieldProjectID: + m.ResetProjectID() + return nil + case projectsyncstate.FieldBrokerID: + m.ResetBrokerID() + return nil + case projectsyncstate.FieldLastSyncTime: + m.ResetLastSyncTime() + return nil + case projectsyncstate.FieldLastCommitSha: + m.ResetLastCommitSha() + return nil + case projectsyncstate.FieldFileCount: + m.ResetFileCount() + return nil + case projectsyncstate.FieldTotalBytes: + m.ResetTotalBytes() + return nil + } + return fmt.Errorf("unknown ProjectSyncState field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *ProjectSyncStateMutation) AddedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *ProjectSyncStateMutation) AddedIDs(name string) []ent.Value { + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *ProjectSyncStateMutation) RemovedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *ProjectSyncStateMutation) RemovedIDs(name string) []ent.Value { + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *ProjectSyncStateMutation) ClearedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *ProjectSyncStateMutation) EdgeCleared(name string) bool { + return false +} + +// ClearEdge clears the value of the edge with the given name. It returns an error +// if that edge is not defined in the schema. +func (m *ProjectSyncStateMutation) ClearEdge(name string) error { + return fmt.Errorf("unknown ProjectSyncState unique edge %s", name) +} + +// ResetEdge resets all changes to the edge with the given name in this mutation. +// It returns an error if the edge is not defined in the schema. +func (m *ProjectSyncStateMutation) ResetEdge(name string) error { + return fmt.Errorf("unknown ProjectSyncState edge %s", name) +} + +// RuntimeBrokerMutation represents an operation that mutates the RuntimeBroker nodes in the graph. +type RuntimeBrokerMutation struct { + config + op Op + typ string + id *uuid.UUID + name *string + slug *string + _type *string + mode *string + version *string + lock_version *int64 + addlock_version *int64 + status *string + connection_state *string + last_heartbeat *time.Time + capabilities *string + supported_harnesses *string + resources *string + runtimes *string + labels *string + annotations *string + endpoint *string + created_by *string + auto_provide *bool + created *time.Time + updated *time.Time + clearedFields map[string]struct{} + done bool + oldValue func(context.Context) (*RuntimeBroker, error) + predicates []predicate.RuntimeBroker +} + +var _ ent.Mutation = (*RuntimeBrokerMutation)(nil) + +// runtimebrokerOption allows management of the mutation configuration using functional options. +type runtimebrokerOption func(*RuntimeBrokerMutation) + +// newRuntimeBrokerMutation creates new mutation for the RuntimeBroker entity. +func newRuntimeBrokerMutation(c config, op Op, opts ...runtimebrokerOption) *RuntimeBrokerMutation { + m := &RuntimeBrokerMutation{ + config: c, + op: op, + typ: TypeRuntimeBroker, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// withRuntimeBrokerID sets the ID field of the mutation. +func withRuntimeBrokerID(id uuid.UUID) runtimebrokerOption { + return func(m *RuntimeBrokerMutation) { + var ( + err error + once sync.Once + value *RuntimeBroker + ) + m.oldValue = func(ctx context.Context) (*RuntimeBroker, error) { + once.Do(func() { + if m.done { + err = errors.New("querying old values post mutation is not allowed") + } else { + value, err = m.Client().RuntimeBroker.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } +} + +// withRuntimeBroker sets the old RuntimeBroker of the mutation. +func withRuntimeBroker(node *RuntimeBroker) runtimebrokerOption { + return func(m *RuntimeBrokerMutation) { + m.oldValue = func(context.Context) (*RuntimeBroker, error) { + return node, nil + } + m.id = &node.ID + } +} + +// Client returns a new `ent.Client` from the mutation. If the mutation was +// executed in a transaction (ent.Tx), a transactional client is returned. +func (m RuntimeBrokerMutation) Client() *Client { + client := &Client{config: m.config} + client.init() + return client +} + +// Tx returns an `ent.Tx` for mutations that were executed in transactions; +// it returns an error otherwise. +func (m RuntimeBrokerMutation) Tx() (*Tx, error) { + if _, ok := m.driver.(*txDriver); !ok { + return nil, errors.New("ent: mutation is not running in a transaction") + } + tx := &Tx{config: m.config} + tx.init() + return tx, nil +} + +// SetID sets the value of the id field. Note that this +// operation is only accepted on creation of RuntimeBroker entities. +func (m *RuntimeBrokerMutation) SetID(id uuid.UUID) { + m.id = &id +} + +// ID returns the ID value in the mutation. Note that the ID is only available +// if it was provided to the builder or after it was returned from the database. +func (m *RuntimeBrokerMutation) ID() (id uuid.UUID, exists bool) { + if m.id == nil { + return + } + return *m.id, true +} + +// IDs queries the database and returns the entity ids that match the mutation's predicate. +// That means, if the mutation is applied within a transaction with an isolation level such +// as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated +// or updated by the mutation. +func (m *RuntimeBrokerMutation) IDs(ctx context.Context) ([]uuid.UUID, error) { + switch { + case m.op.Is(OpUpdateOne | OpDeleteOne): + id, exists := m.ID() + if exists { + return []uuid.UUID{id}, nil + } + fallthrough + case m.op.Is(OpUpdate | OpDelete): + return m.Client().RuntimeBroker.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } +} + +// SetName sets the "name" field. +func (m *RuntimeBrokerMutation) SetName(s string) { + m.name = &s +} + +// Name returns the value of the "name" field in the mutation. +func (m *RuntimeBrokerMutation) Name() (r string, exists bool) { + v := m.name + if v == nil { + return + } + return *v, true +} + +// OldName returns the old "name" field's value of the RuntimeBroker entity. +// If the RuntimeBroker object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *RuntimeBrokerMutation) OldName(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldName is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldName requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldName: %w", err) + } + return oldValue.Name, nil +} + +// ResetName resets all changes to the "name" field. +func (m *RuntimeBrokerMutation) ResetName() { + m.name = nil +} + +// SetSlug sets the "slug" field. +func (m *RuntimeBrokerMutation) SetSlug(s string) { + m.slug = &s +} + +// Slug returns the value of the "slug" field in the mutation. +func (m *RuntimeBrokerMutation) Slug() (r string, exists bool) { + v := m.slug + if v == nil { + return + } + return *v, true +} + +// OldSlug returns the old "slug" field's value of the RuntimeBroker entity. +// If the RuntimeBroker object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *RuntimeBrokerMutation) OldSlug(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldSlug is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldSlug requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldSlug: %w", err) + } + return oldValue.Slug, nil +} + +// ResetSlug resets all changes to the "slug" field. +func (m *RuntimeBrokerMutation) ResetSlug() { + m.slug = nil +} + +// SetType sets the "type" field. +func (m *RuntimeBrokerMutation) SetType(s string) { + m._type = &s +} + +// GetType returns the value of the "type" field in the mutation. +func (m *RuntimeBrokerMutation) GetType() (r string, exists bool) { + v := m._type + if v == nil { + return + } + return *v, true +} + +// OldType returns the old "type" field's value of the RuntimeBroker entity. +// If the RuntimeBroker object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *RuntimeBrokerMutation) OldType(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldType is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldType requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldType: %w", err) + } + return oldValue.Type, nil +} + +// ClearType clears the value of the "type" field. +func (m *RuntimeBrokerMutation) ClearType() { + m._type = nil + m.clearedFields[runtimebroker.FieldType] = struct{}{} +} + +// TypeCleared returns if the "type" field was cleared in this mutation. +func (m *RuntimeBrokerMutation) TypeCleared() bool { + _, ok := m.clearedFields[runtimebroker.FieldType] + return ok +} + +// ResetType resets all changes to the "type" field. +func (m *RuntimeBrokerMutation) ResetType() { + m._type = nil + delete(m.clearedFields, runtimebroker.FieldType) +} + +// SetMode sets the "mode" field. +func (m *RuntimeBrokerMutation) SetMode(s string) { + m.mode = &s +} + +// Mode returns the value of the "mode" field in the mutation. +func (m *RuntimeBrokerMutation) Mode() (r string, exists bool) { + v := m.mode + if v == nil { + return + } + return *v, true +} + +// OldMode returns the old "mode" field's value of the RuntimeBroker entity. +// If the RuntimeBroker object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *RuntimeBrokerMutation) OldMode(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldMode is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldMode requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldMode: %w", err) + } + return oldValue.Mode, nil +} + +// ResetMode resets all changes to the "mode" field. +func (m *RuntimeBrokerMutation) ResetMode() { + m.mode = nil +} + +// SetVersion sets the "version" field. +func (m *RuntimeBrokerMutation) SetVersion(s string) { + m.version = &s +} + +// Version returns the value of the "version" field in the mutation. +func (m *RuntimeBrokerMutation) Version() (r string, exists bool) { + v := m.version + if v == nil { + return + } + return *v, true +} + +// OldVersion returns the old "version" field's value of the RuntimeBroker entity. +// If the RuntimeBroker object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *RuntimeBrokerMutation) OldVersion(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldVersion is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldVersion requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldVersion: %w", err) + } + return oldValue.Version, nil +} + +// ClearVersion clears the value of the "version" field. +func (m *RuntimeBrokerMutation) ClearVersion() { + m.version = nil + m.clearedFields[runtimebroker.FieldVersion] = struct{}{} +} + +// VersionCleared returns if the "version" field was cleared in this mutation. +func (m *RuntimeBrokerMutation) VersionCleared() bool { + _, ok := m.clearedFields[runtimebroker.FieldVersion] + return ok +} + +// ResetVersion resets all changes to the "version" field. +func (m *RuntimeBrokerMutation) ResetVersion() { + m.version = nil + delete(m.clearedFields, runtimebroker.FieldVersion) +} + +// SetLockVersion sets the "lock_version" field. +func (m *RuntimeBrokerMutation) SetLockVersion(i int64) { + m.lock_version = &i + m.addlock_version = nil +} + +// LockVersion returns the value of the "lock_version" field in the mutation. +func (m *RuntimeBrokerMutation) LockVersion() (r int64, exists bool) { + v := m.lock_version + if v == nil { + return + } + return *v, true +} + +// OldLockVersion returns the old "lock_version" field's value of the RuntimeBroker entity. +// If the RuntimeBroker object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *RuntimeBrokerMutation) OldLockVersion(ctx context.Context) (v int64, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldLockVersion is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldLockVersion requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldLockVersion: %w", err) + } + return oldValue.LockVersion, nil +} + +// AddLockVersion adds i to the "lock_version" field. +func (m *RuntimeBrokerMutation) AddLockVersion(i int64) { + if m.addlock_version != nil { + *m.addlock_version += i + } else { + m.addlock_version = &i + } +} + +// AddedLockVersion returns the value that was added to the "lock_version" field in this mutation. +func (m *RuntimeBrokerMutation) AddedLockVersion() (r int64, exists bool) { + v := m.addlock_version + if v == nil { + return + } + return *v, true +} + +// ResetLockVersion resets all changes to the "lock_version" field. +func (m *RuntimeBrokerMutation) ResetLockVersion() { + m.lock_version = nil + m.addlock_version = nil +} + +// SetStatus sets the "status" field. +func (m *RuntimeBrokerMutation) SetStatus(s string) { + m.status = &s +} + +// Status returns the value of the "status" field in the mutation. +func (m *RuntimeBrokerMutation) Status() (r string, exists bool) { + v := m.status + if v == nil { + return + } + return *v, true +} + +// OldStatus returns the old "status" field's value of the RuntimeBroker entity. +// If the RuntimeBroker object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *RuntimeBrokerMutation) OldStatus(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldStatus is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldStatus requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldStatus: %w", err) + } + return oldValue.Status, nil +} + +// ResetStatus resets all changes to the "status" field. +func (m *RuntimeBrokerMutation) ResetStatus() { + m.status = nil +} + +// SetConnectionState sets the "connection_state" field. +func (m *RuntimeBrokerMutation) SetConnectionState(s string) { + m.connection_state = &s +} + +// ConnectionState returns the value of the "connection_state" field in the mutation. +func (m *RuntimeBrokerMutation) ConnectionState() (r string, exists bool) { + v := m.connection_state + if v == nil { + return + } + return *v, true +} + +// OldConnectionState returns the old "connection_state" field's value of the RuntimeBroker entity. +// If the RuntimeBroker object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *RuntimeBrokerMutation) OldConnectionState(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldConnectionState is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldConnectionState requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldConnectionState: %w", err) + } + return oldValue.ConnectionState, nil +} + +// ResetConnectionState resets all changes to the "connection_state" field. +func (m *RuntimeBrokerMutation) ResetConnectionState() { + m.connection_state = nil +} + +// SetLastHeartbeat sets the "last_heartbeat" field. +func (m *RuntimeBrokerMutation) SetLastHeartbeat(t time.Time) { + m.last_heartbeat = &t +} + +// LastHeartbeat returns the value of the "last_heartbeat" field in the mutation. +func (m *RuntimeBrokerMutation) LastHeartbeat() (r time.Time, exists bool) { + v := m.last_heartbeat + if v == nil { + return + } + return *v, true +} + +// OldLastHeartbeat returns the old "last_heartbeat" field's value of the RuntimeBroker entity. +// If the RuntimeBroker object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *RuntimeBrokerMutation) OldLastHeartbeat(ctx context.Context) (v *time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldLastHeartbeat is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldLastHeartbeat requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldLastHeartbeat: %w", err) + } + return oldValue.LastHeartbeat, nil +} + +// ClearLastHeartbeat clears the value of the "last_heartbeat" field. +func (m *RuntimeBrokerMutation) ClearLastHeartbeat() { + m.last_heartbeat = nil + m.clearedFields[runtimebroker.FieldLastHeartbeat] = struct{}{} +} + +// LastHeartbeatCleared returns if the "last_heartbeat" field was cleared in this mutation. +func (m *RuntimeBrokerMutation) LastHeartbeatCleared() bool { + _, ok := m.clearedFields[runtimebroker.FieldLastHeartbeat] + return ok +} + +// ResetLastHeartbeat resets all changes to the "last_heartbeat" field. +func (m *RuntimeBrokerMutation) ResetLastHeartbeat() { + m.last_heartbeat = nil + delete(m.clearedFields, runtimebroker.FieldLastHeartbeat) +} + +// SetCapabilities sets the "capabilities" field. +func (m *RuntimeBrokerMutation) SetCapabilities(s string) { + m.capabilities = &s +} + +// Capabilities returns the value of the "capabilities" field in the mutation. +func (m *RuntimeBrokerMutation) Capabilities() (r string, exists bool) { + v := m.capabilities + if v == nil { + return + } + return *v, true +} + +// OldCapabilities returns the old "capabilities" field's value of the RuntimeBroker entity. +// If the RuntimeBroker object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *RuntimeBrokerMutation) OldCapabilities(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCapabilities is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCapabilities requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCapabilities: %w", err) + } + return oldValue.Capabilities, nil +} + +// ClearCapabilities clears the value of the "capabilities" field. +func (m *RuntimeBrokerMutation) ClearCapabilities() { + m.capabilities = nil + m.clearedFields[runtimebroker.FieldCapabilities] = struct{}{} +} + +// CapabilitiesCleared returns if the "capabilities" field was cleared in this mutation. +func (m *RuntimeBrokerMutation) CapabilitiesCleared() bool { + _, ok := m.clearedFields[runtimebroker.FieldCapabilities] + return ok +} + +// ResetCapabilities resets all changes to the "capabilities" field. +func (m *RuntimeBrokerMutation) ResetCapabilities() { + m.capabilities = nil + delete(m.clearedFields, runtimebroker.FieldCapabilities) +} + +// SetSupportedHarnesses sets the "supported_harnesses" field. +func (m *RuntimeBrokerMutation) SetSupportedHarnesses(s string) { + m.supported_harnesses = &s +} + +// SupportedHarnesses returns the value of the "supported_harnesses" field in the mutation. +func (m *RuntimeBrokerMutation) SupportedHarnesses() (r string, exists bool) { + v := m.supported_harnesses + if v == nil { + return + } + return *v, true +} + +// OldSupportedHarnesses returns the old "supported_harnesses" field's value of the RuntimeBroker entity. +// If the RuntimeBroker object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *RuntimeBrokerMutation) OldSupportedHarnesses(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldSupportedHarnesses is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldSupportedHarnesses requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldSupportedHarnesses: %w", err) + } + return oldValue.SupportedHarnesses, nil +} + +// ClearSupportedHarnesses clears the value of the "supported_harnesses" field. +func (m *RuntimeBrokerMutation) ClearSupportedHarnesses() { + m.supported_harnesses = nil + m.clearedFields[runtimebroker.FieldSupportedHarnesses] = struct{}{} +} + +// SupportedHarnessesCleared returns if the "supported_harnesses" field was cleared in this mutation. +func (m *RuntimeBrokerMutation) SupportedHarnessesCleared() bool { + _, ok := m.clearedFields[runtimebroker.FieldSupportedHarnesses] + return ok +} + +// ResetSupportedHarnesses resets all changes to the "supported_harnesses" field. +func (m *RuntimeBrokerMutation) ResetSupportedHarnesses() { + m.supported_harnesses = nil + delete(m.clearedFields, runtimebroker.FieldSupportedHarnesses) +} + +// SetResources sets the "resources" field. +func (m *RuntimeBrokerMutation) SetResources(s string) { + m.resources = &s +} + +// Resources returns the value of the "resources" field in the mutation. +func (m *RuntimeBrokerMutation) Resources() (r string, exists bool) { + v := m.resources + if v == nil { + return + } + return *v, true +} + +// OldResources returns the old "resources" field's value of the RuntimeBroker entity. +// If the RuntimeBroker object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *RuntimeBrokerMutation) OldResources(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldResources is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldResources requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldResources: %w", err) + } + return oldValue.Resources, nil +} + +// ClearResources clears the value of the "resources" field. +func (m *RuntimeBrokerMutation) ClearResources() { + m.resources = nil + m.clearedFields[runtimebroker.FieldResources] = struct{}{} +} + +// ResourcesCleared returns if the "resources" field was cleared in this mutation. +func (m *RuntimeBrokerMutation) ResourcesCleared() bool { + _, ok := m.clearedFields[runtimebroker.FieldResources] + return ok +} + +// ResetResources resets all changes to the "resources" field. +func (m *RuntimeBrokerMutation) ResetResources() { + m.resources = nil + delete(m.clearedFields, runtimebroker.FieldResources) +} + +// SetRuntimes sets the "runtimes" field. +func (m *RuntimeBrokerMutation) SetRuntimes(s string) { + m.runtimes = &s +} + +// Runtimes returns the value of the "runtimes" field in the mutation. +func (m *RuntimeBrokerMutation) Runtimes() (r string, exists bool) { + v := m.runtimes + if v == nil { + return + } + return *v, true +} + +// OldRuntimes returns the old "runtimes" field's value of the RuntimeBroker entity. +// If the RuntimeBroker object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *RuntimeBrokerMutation) OldRuntimes(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldRuntimes is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldRuntimes requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldRuntimes: %w", err) + } + return oldValue.Runtimes, nil +} + +// ClearRuntimes clears the value of the "runtimes" field. +func (m *RuntimeBrokerMutation) ClearRuntimes() { + m.runtimes = nil + m.clearedFields[runtimebroker.FieldRuntimes] = struct{}{} +} + +// RuntimesCleared returns if the "runtimes" field was cleared in this mutation. +func (m *RuntimeBrokerMutation) RuntimesCleared() bool { + _, ok := m.clearedFields[runtimebroker.FieldRuntimes] + return ok +} + +// ResetRuntimes resets all changes to the "runtimes" field. +func (m *RuntimeBrokerMutation) ResetRuntimes() { + m.runtimes = nil + delete(m.clearedFields, runtimebroker.FieldRuntimes) +} + +// SetLabels sets the "labels" field. +func (m *RuntimeBrokerMutation) SetLabels(s string) { + m.labels = &s +} + +// Labels returns the value of the "labels" field in the mutation. +func (m *RuntimeBrokerMutation) Labels() (r string, exists bool) { + v := m.labels + if v == nil { + return + } + return *v, true +} + +// OldLabels returns the old "labels" field's value of the RuntimeBroker entity. +// If the RuntimeBroker object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *RuntimeBrokerMutation) OldLabels(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldLabels is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldLabels requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldLabels: %w", err) + } + return oldValue.Labels, nil +} + +// ClearLabels clears the value of the "labels" field. +func (m *RuntimeBrokerMutation) ClearLabels() { + m.labels = nil + m.clearedFields[runtimebroker.FieldLabels] = struct{}{} +} + +// LabelsCleared returns if the "labels" field was cleared in this mutation. +func (m *RuntimeBrokerMutation) LabelsCleared() bool { + _, ok := m.clearedFields[runtimebroker.FieldLabels] + return ok +} + +// ResetLabels resets all changes to the "labels" field. +func (m *RuntimeBrokerMutation) ResetLabels() { + m.labels = nil + delete(m.clearedFields, runtimebroker.FieldLabels) +} + +// SetAnnotations sets the "annotations" field. +func (m *RuntimeBrokerMutation) SetAnnotations(s string) { + m.annotations = &s +} + +// Annotations returns the value of the "annotations" field in the mutation. +func (m *RuntimeBrokerMutation) Annotations() (r string, exists bool) { + v := m.annotations + if v == nil { + return + } + return *v, true +} + +// OldAnnotations returns the old "annotations" field's value of the RuntimeBroker entity. +// If the RuntimeBroker object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *RuntimeBrokerMutation) OldAnnotations(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldAnnotations is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldAnnotations requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldAnnotations: %w", err) + } + return oldValue.Annotations, nil +} + +// ClearAnnotations clears the value of the "annotations" field. +func (m *RuntimeBrokerMutation) ClearAnnotations() { + m.annotations = nil + m.clearedFields[runtimebroker.FieldAnnotations] = struct{}{} +} + +// AnnotationsCleared returns if the "annotations" field was cleared in this mutation. +func (m *RuntimeBrokerMutation) AnnotationsCleared() bool { + _, ok := m.clearedFields[runtimebroker.FieldAnnotations] + return ok +} + +// ResetAnnotations resets all changes to the "annotations" field. +func (m *RuntimeBrokerMutation) ResetAnnotations() { + m.annotations = nil + delete(m.clearedFields, runtimebroker.FieldAnnotations) +} + +// SetEndpoint sets the "endpoint" field. +func (m *RuntimeBrokerMutation) SetEndpoint(s string) { + m.endpoint = &s +} + +// Endpoint returns the value of the "endpoint" field in the mutation. +func (m *RuntimeBrokerMutation) Endpoint() (r string, exists bool) { + v := m.endpoint + if v == nil { + return + } + return *v, true +} + +// OldEndpoint returns the old "endpoint" field's value of the RuntimeBroker entity. +// If the RuntimeBroker object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *RuntimeBrokerMutation) OldEndpoint(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldEndpoint is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldEndpoint requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldEndpoint: %w", err) + } + return oldValue.Endpoint, nil +} + +// ClearEndpoint clears the value of the "endpoint" field. +func (m *RuntimeBrokerMutation) ClearEndpoint() { + m.endpoint = nil + m.clearedFields[runtimebroker.FieldEndpoint] = struct{}{} +} + +// EndpointCleared returns if the "endpoint" field was cleared in this mutation. +func (m *RuntimeBrokerMutation) EndpointCleared() bool { + _, ok := m.clearedFields[runtimebroker.FieldEndpoint] + return ok +} + +// ResetEndpoint resets all changes to the "endpoint" field. +func (m *RuntimeBrokerMutation) ResetEndpoint() { + m.endpoint = nil + delete(m.clearedFields, runtimebroker.FieldEndpoint) +} + +// SetCreatedBy sets the "created_by" field. +func (m *RuntimeBrokerMutation) SetCreatedBy(s string) { + m.created_by = &s +} + +// CreatedBy returns the value of the "created_by" field in the mutation. +func (m *RuntimeBrokerMutation) CreatedBy() (r string, exists bool) { + v := m.created_by + if v == nil { + return + } + return *v, true +} + +// OldCreatedBy returns the old "created_by" field's value of the RuntimeBroker entity. +// If the RuntimeBroker object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *RuntimeBrokerMutation) OldCreatedBy(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreatedBy is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreatedBy requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreatedBy: %w", err) + } + return oldValue.CreatedBy, nil +} + +// ClearCreatedBy clears the value of the "created_by" field. +func (m *RuntimeBrokerMutation) ClearCreatedBy() { + m.created_by = nil + m.clearedFields[runtimebroker.FieldCreatedBy] = struct{}{} +} + +// CreatedByCleared returns if the "created_by" field was cleared in this mutation. +func (m *RuntimeBrokerMutation) CreatedByCleared() bool { + _, ok := m.clearedFields[runtimebroker.FieldCreatedBy] + return ok +} + +// ResetCreatedBy resets all changes to the "created_by" field. +func (m *RuntimeBrokerMutation) ResetCreatedBy() { + m.created_by = nil + delete(m.clearedFields, runtimebroker.FieldCreatedBy) +} + +// SetAutoProvide sets the "auto_provide" field. +func (m *RuntimeBrokerMutation) SetAutoProvide(b bool) { + m.auto_provide = &b +} + +// AutoProvide returns the value of the "auto_provide" field in the mutation. +func (m *RuntimeBrokerMutation) AutoProvide() (r bool, exists bool) { + v := m.auto_provide + if v == nil { + return + } + return *v, true +} + +// OldAutoProvide returns the old "auto_provide" field's value of the RuntimeBroker entity. +// If the RuntimeBroker object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *RuntimeBrokerMutation) OldAutoProvide(ctx context.Context) (v bool, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldAutoProvide is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldAutoProvide requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldAutoProvide: %w", err) + } + return oldValue.AutoProvide, nil +} + +// ResetAutoProvide resets all changes to the "auto_provide" field. +func (m *RuntimeBrokerMutation) ResetAutoProvide() { + m.auto_provide = nil +} + +// SetCreated sets the "created" field. +func (m *RuntimeBrokerMutation) SetCreated(t time.Time) { + m.created = &t +} + +// Created returns the value of the "created" field in the mutation. +func (m *RuntimeBrokerMutation) Created() (r time.Time, exists bool) { + v := m.created + if v == nil { + return + } + return *v, true +} + +// OldCreated returns the old "created" field's value of the RuntimeBroker entity. +// If the RuntimeBroker object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *RuntimeBrokerMutation) OldCreated(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreated is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreated requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreated: %w", err) + } + return oldValue.Created, nil +} + +// ResetCreated resets all changes to the "created" field. +func (m *RuntimeBrokerMutation) ResetCreated() { + m.created = nil +} + +// SetUpdated sets the "updated" field. +func (m *RuntimeBrokerMutation) SetUpdated(t time.Time) { + m.updated = &t +} + +// Updated returns the value of the "updated" field in the mutation. +func (m *RuntimeBrokerMutation) Updated() (r time.Time, exists bool) { + v := m.updated + if v == nil { + return + } + return *v, true +} + +// OldUpdated returns the old "updated" field's value of the RuntimeBroker entity. +// If the RuntimeBroker object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *RuntimeBrokerMutation) OldUpdated(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldUpdated is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldUpdated requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldUpdated: %w", err) + } + return oldValue.Updated, nil +} + +// ResetUpdated resets all changes to the "updated" field. +func (m *RuntimeBrokerMutation) ResetUpdated() { + m.updated = nil +} + +// Where appends a list predicates to the RuntimeBrokerMutation builder. +func (m *RuntimeBrokerMutation) Where(ps ...predicate.RuntimeBroker) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the RuntimeBrokerMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *RuntimeBrokerMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.RuntimeBroker, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *RuntimeBrokerMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *RuntimeBrokerMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (RuntimeBroker). +func (m *RuntimeBrokerMutation) Type() string { + return m.typ +} + +// Fields returns all fields that were changed during this mutation. Note that in +// order to get all numeric fields that were incremented/decremented, call +// AddedFields(). +func (m *RuntimeBrokerMutation) Fields() []string { + fields := make([]string, 0, 20) + if m.name != nil { + fields = append(fields, runtimebroker.FieldName) + } + if m.slug != nil { + fields = append(fields, runtimebroker.FieldSlug) + } + if m._type != nil { + fields = append(fields, runtimebroker.FieldType) + } + if m.mode != nil { + fields = append(fields, runtimebroker.FieldMode) + } + if m.version != nil { + fields = append(fields, runtimebroker.FieldVersion) + } + if m.lock_version != nil { + fields = append(fields, runtimebroker.FieldLockVersion) + } + if m.status != nil { + fields = append(fields, runtimebroker.FieldStatus) + } + if m.connection_state != nil { + fields = append(fields, runtimebroker.FieldConnectionState) + } + if m.last_heartbeat != nil { + fields = append(fields, runtimebroker.FieldLastHeartbeat) + } + if m.capabilities != nil { + fields = append(fields, runtimebroker.FieldCapabilities) + } + if m.supported_harnesses != nil { + fields = append(fields, runtimebroker.FieldSupportedHarnesses) + } + if m.resources != nil { + fields = append(fields, runtimebroker.FieldResources) + } + if m.runtimes != nil { + fields = append(fields, runtimebroker.FieldRuntimes) + } + if m.labels != nil { + fields = append(fields, runtimebroker.FieldLabels) + } + if m.annotations != nil { + fields = append(fields, runtimebroker.FieldAnnotations) + } + if m.endpoint != nil { + fields = append(fields, runtimebroker.FieldEndpoint) + } + if m.created_by != nil { + fields = append(fields, runtimebroker.FieldCreatedBy) + } + if m.auto_provide != nil { + fields = append(fields, runtimebroker.FieldAutoProvide) + } + if m.created != nil { + fields = append(fields, runtimebroker.FieldCreated) + } + if m.updated != nil { + fields = append(fields, runtimebroker.FieldUpdated) + } + return fields +} + +// Field returns the value of a field with the given name. The second boolean +// return value indicates that this field was not set, or was not defined in the +// schema. +func (m *RuntimeBrokerMutation) Field(name string) (ent.Value, bool) { + switch name { + case runtimebroker.FieldName: + return m.Name() + case runtimebroker.FieldSlug: + return m.Slug() + case runtimebroker.FieldType: + return m.GetType() + case runtimebroker.FieldMode: + return m.Mode() + case runtimebroker.FieldVersion: + return m.Version() + case runtimebroker.FieldLockVersion: + return m.LockVersion() + case runtimebroker.FieldStatus: + return m.Status() + case runtimebroker.FieldConnectionState: + return m.ConnectionState() + case runtimebroker.FieldLastHeartbeat: + return m.LastHeartbeat() + case runtimebroker.FieldCapabilities: + return m.Capabilities() + case runtimebroker.FieldSupportedHarnesses: + return m.SupportedHarnesses() + case runtimebroker.FieldResources: + return m.Resources() + case runtimebroker.FieldRuntimes: + return m.Runtimes() + case runtimebroker.FieldLabels: + return m.Labels() + case runtimebroker.FieldAnnotations: + return m.Annotations() + case runtimebroker.FieldEndpoint: + return m.Endpoint() + case runtimebroker.FieldCreatedBy: + return m.CreatedBy() + case runtimebroker.FieldAutoProvide: + return m.AutoProvide() + case runtimebroker.FieldCreated: + return m.Created() + case runtimebroker.FieldUpdated: + return m.Updated() + } + return nil, false +} + +// OldField returns the old value of the field from the database. An error is +// returned if the mutation operation is not UpdateOne, or the query to the +// database failed. +func (m *RuntimeBrokerMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + switch name { + case runtimebroker.FieldName: + return m.OldName(ctx) + case runtimebroker.FieldSlug: + return m.OldSlug(ctx) + case runtimebroker.FieldType: + return m.OldType(ctx) + case runtimebroker.FieldMode: + return m.OldMode(ctx) + case runtimebroker.FieldVersion: + return m.OldVersion(ctx) + case runtimebroker.FieldLockVersion: + return m.OldLockVersion(ctx) + case runtimebroker.FieldStatus: + return m.OldStatus(ctx) + case runtimebroker.FieldConnectionState: + return m.OldConnectionState(ctx) + case runtimebroker.FieldLastHeartbeat: + return m.OldLastHeartbeat(ctx) + case runtimebroker.FieldCapabilities: + return m.OldCapabilities(ctx) + case runtimebroker.FieldSupportedHarnesses: + return m.OldSupportedHarnesses(ctx) + case runtimebroker.FieldResources: + return m.OldResources(ctx) + case runtimebroker.FieldRuntimes: + return m.OldRuntimes(ctx) + case runtimebroker.FieldLabels: + return m.OldLabels(ctx) + case runtimebroker.FieldAnnotations: + return m.OldAnnotations(ctx) + case runtimebroker.FieldEndpoint: + return m.OldEndpoint(ctx) + case runtimebroker.FieldCreatedBy: + return m.OldCreatedBy(ctx) + case runtimebroker.FieldAutoProvide: + return m.OldAutoProvide(ctx) + case runtimebroker.FieldCreated: + return m.OldCreated(ctx) + case runtimebroker.FieldUpdated: + return m.OldUpdated(ctx) + } + return nil, fmt.Errorf("unknown RuntimeBroker field %s", name) +} + +// SetField sets the value of a field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *RuntimeBrokerMutation) SetField(name string, value ent.Value) error { + switch name { + case runtimebroker.FieldName: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetName(v) + return nil + case runtimebroker.FieldSlug: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetSlug(v) + return nil + case runtimebroker.FieldType: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetType(v) + return nil + case runtimebroker.FieldMode: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetMode(v) + return nil + case runtimebroker.FieldVersion: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetVersion(v) + return nil + case runtimebroker.FieldLockVersion: + v, ok := value.(int64) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetLockVersion(v) + return nil + case runtimebroker.FieldStatus: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetStatus(v) + return nil + case runtimebroker.FieldConnectionState: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetConnectionState(v) + return nil + case runtimebroker.FieldLastHeartbeat: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetLastHeartbeat(v) + return nil + case runtimebroker.FieldCapabilities: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCapabilities(v) + return nil + case runtimebroker.FieldSupportedHarnesses: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetSupportedHarnesses(v) + return nil + case runtimebroker.FieldResources: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetResources(v) + return nil + case runtimebroker.FieldRuntimes: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetRuntimes(v) + return nil + case runtimebroker.FieldLabels: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetLabels(v) + return nil + case runtimebroker.FieldAnnotations: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetAnnotations(v) + return nil + case runtimebroker.FieldEndpoint: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetEndpoint(v) + return nil + case runtimebroker.FieldCreatedBy: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreatedBy(v) + return nil + case runtimebroker.FieldAutoProvide: + v, ok := value.(bool) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetAutoProvide(v) + return nil + case runtimebroker.FieldCreated: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreated(v) + return nil + case runtimebroker.FieldUpdated: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetUpdated(v) + return nil + } + return fmt.Errorf("unknown RuntimeBroker field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *RuntimeBrokerMutation) AddedFields() []string { + var fields []string + if m.addlock_version != nil { + fields = append(fields, runtimebroker.FieldLockVersion) + } + return fields +} + +// AddedField returns the numeric value that was incremented/decremented on a field +// with the given name. The second boolean return value indicates that this field +// was not set, or was not defined in the schema. +func (m *RuntimeBrokerMutation) AddedField(name string) (ent.Value, bool) { + switch name { + case runtimebroker.FieldLockVersion: + return m.AddedLockVersion() + } + return nil, false +} + +// AddField adds the value to the field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *RuntimeBrokerMutation) AddField(name string, value ent.Value) error { + switch name { + case runtimebroker.FieldLockVersion: + v, ok := value.(int64) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.AddLockVersion(v) + return nil + } + return fmt.Errorf("unknown RuntimeBroker numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *RuntimeBrokerMutation) ClearedFields() []string { + var fields []string + if m.FieldCleared(runtimebroker.FieldType) { + fields = append(fields, runtimebroker.FieldType) + } + if m.FieldCleared(runtimebroker.FieldVersion) { + fields = append(fields, runtimebroker.FieldVersion) + } + if m.FieldCleared(runtimebroker.FieldLastHeartbeat) { + fields = append(fields, runtimebroker.FieldLastHeartbeat) + } + if m.FieldCleared(runtimebroker.FieldCapabilities) { + fields = append(fields, runtimebroker.FieldCapabilities) + } + if m.FieldCleared(runtimebroker.FieldSupportedHarnesses) { + fields = append(fields, runtimebroker.FieldSupportedHarnesses) + } + if m.FieldCleared(runtimebroker.FieldResources) { + fields = append(fields, runtimebroker.FieldResources) + } + if m.FieldCleared(runtimebroker.FieldRuntimes) { + fields = append(fields, runtimebroker.FieldRuntimes) + } + if m.FieldCleared(runtimebroker.FieldLabels) { + fields = append(fields, runtimebroker.FieldLabels) + } + if m.FieldCleared(runtimebroker.FieldAnnotations) { + fields = append(fields, runtimebroker.FieldAnnotations) + } + if m.FieldCleared(runtimebroker.FieldEndpoint) { + fields = append(fields, runtimebroker.FieldEndpoint) + } + if m.FieldCleared(runtimebroker.FieldCreatedBy) { + fields = append(fields, runtimebroker.FieldCreatedBy) + } + return fields +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *RuntimeBrokerMutation) FieldCleared(name string) bool { + _, ok := m.clearedFields[name] + return ok +} + +// ClearField clears the value of the field with the given name. It returns an +// error if the field is not defined in the schema. +func (m *RuntimeBrokerMutation) ClearField(name string) error { + switch name { + case runtimebroker.FieldType: + m.ClearType() + return nil + case runtimebroker.FieldVersion: + m.ClearVersion() + return nil + case runtimebroker.FieldLastHeartbeat: + m.ClearLastHeartbeat() + return nil + case runtimebroker.FieldCapabilities: + m.ClearCapabilities() + return nil + case runtimebroker.FieldSupportedHarnesses: + m.ClearSupportedHarnesses() + return nil + case runtimebroker.FieldResources: + m.ClearResources() + return nil + case runtimebroker.FieldRuntimes: + m.ClearRuntimes() + return nil + case runtimebroker.FieldLabels: + m.ClearLabels() + return nil + case runtimebroker.FieldAnnotations: + m.ClearAnnotations() + return nil + case runtimebroker.FieldEndpoint: + m.ClearEndpoint() + return nil + case runtimebroker.FieldCreatedBy: + m.ClearCreatedBy() + return nil + } + return fmt.Errorf("unknown RuntimeBroker nullable field %s", name) +} + +// ResetField resets all changes in the mutation for the field with the given name. +// It returns an error if the field is not defined in the schema. +func (m *RuntimeBrokerMutation) ResetField(name string) error { + switch name { + case runtimebroker.FieldName: + m.ResetName() + return nil + case runtimebroker.FieldSlug: + m.ResetSlug() + return nil + case runtimebroker.FieldType: + m.ResetType() + return nil + case runtimebroker.FieldMode: + m.ResetMode() + return nil + case runtimebroker.FieldVersion: + m.ResetVersion() + return nil + case runtimebroker.FieldLockVersion: + m.ResetLockVersion() + return nil + case runtimebroker.FieldStatus: + m.ResetStatus() + return nil + case runtimebroker.FieldConnectionState: + m.ResetConnectionState() + return nil + case runtimebroker.FieldLastHeartbeat: + m.ResetLastHeartbeat() + return nil + case runtimebroker.FieldCapabilities: + m.ResetCapabilities() + return nil + case runtimebroker.FieldSupportedHarnesses: + m.ResetSupportedHarnesses() + return nil + case runtimebroker.FieldResources: + m.ResetResources() + return nil + case runtimebroker.FieldRuntimes: + m.ResetRuntimes() + return nil + case runtimebroker.FieldLabels: + m.ResetLabels() + return nil + case runtimebroker.FieldAnnotations: + m.ResetAnnotations() + return nil + case runtimebroker.FieldEndpoint: + m.ResetEndpoint() + return nil + case runtimebroker.FieldCreatedBy: + m.ResetCreatedBy() + return nil + case runtimebroker.FieldAutoProvide: + m.ResetAutoProvide() + return nil + case runtimebroker.FieldCreated: + m.ResetCreated() + return nil + case runtimebroker.FieldUpdated: + m.ResetUpdated() + return nil + } + return fmt.Errorf("unknown RuntimeBroker field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *RuntimeBrokerMutation) AddedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *RuntimeBrokerMutation) AddedIDs(name string) []ent.Value { + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *RuntimeBrokerMutation) RemovedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *RuntimeBrokerMutation) RemovedIDs(name string) []ent.Value { + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *RuntimeBrokerMutation) ClearedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *RuntimeBrokerMutation) EdgeCleared(name string) bool { + return false +} + +// ClearEdge clears the value of the edge with the given name. It returns an error +// if that edge is not defined in the schema. +func (m *RuntimeBrokerMutation) ClearEdge(name string) error { + return fmt.Errorf("unknown RuntimeBroker unique edge %s", name) +} + +// ResetEdge resets all changes to the edge with the given name in this mutation. +// It returns an error if the edge is not defined in the schema. +func (m *RuntimeBrokerMutation) ResetEdge(name string) error { + return fmt.Errorf("unknown RuntimeBroker edge %s", name) +} + +// ScheduleMutation represents an operation that mutates the Schedule nodes in the graph. +type ScheduleMutation struct { + config + op Op + typ string + id *uuid.UUID + project_id *uuid.UUID + name *string + cron_expr *string + event_type *string + payload *string + status *string + next_run_at *time.Time + last_run_at *time.Time + last_run_status *string + last_run_error *string + run_count *int + addrun_count *int + error_count *int + adderror_count *int + created_by *string + created *time.Time + updated *time.Time + clearedFields map[string]struct{} + done bool + oldValue func(context.Context) (*Schedule, error) + predicates []predicate.Schedule +} + +var _ ent.Mutation = (*ScheduleMutation)(nil) + +// scheduleOption allows management of the mutation configuration using functional options. +type scheduleOption func(*ScheduleMutation) + +// newScheduleMutation creates new mutation for the Schedule entity. +func newScheduleMutation(c config, op Op, opts ...scheduleOption) *ScheduleMutation { + m := &ScheduleMutation{ + config: c, + op: op, + typ: TypeSchedule, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// withScheduleID sets the ID field of the mutation. +func withScheduleID(id uuid.UUID) scheduleOption { + return func(m *ScheduleMutation) { + var ( + err error + once sync.Once + value *Schedule + ) + m.oldValue = func(ctx context.Context) (*Schedule, error) { + once.Do(func() { + if m.done { + err = errors.New("querying old values post mutation is not allowed") + } else { + value, err = m.Client().Schedule.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } +} + +// withSchedule sets the old Schedule of the mutation. +func withSchedule(node *Schedule) scheduleOption { + return func(m *ScheduleMutation) { + m.oldValue = func(context.Context) (*Schedule, error) { + return node, nil + } + m.id = &node.ID + } +} + +// Client returns a new `ent.Client` from the mutation. If the mutation was +// executed in a transaction (ent.Tx), a transactional client is returned. +func (m ScheduleMutation) Client() *Client { + client := &Client{config: m.config} + client.init() + return client +} + +// Tx returns an `ent.Tx` for mutations that were executed in transactions; +// it returns an error otherwise. +func (m ScheduleMutation) Tx() (*Tx, error) { + if _, ok := m.driver.(*txDriver); !ok { + return nil, errors.New("ent: mutation is not running in a transaction") + } + tx := &Tx{config: m.config} + tx.init() + return tx, nil +} + +// SetID sets the value of the id field. Note that this +// operation is only accepted on creation of Schedule entities. +func (m *ScheduleMutation) SetID(id uuid.UUID) { + m.id = &id +} + +// ID returns the ID value in the mutation. Note that the ID is only available +// if it was provided to the builder or after it was returned from the database. +func (m *ScheduleMutation) ID() (id uuid.UUID, exists bool) { + if m.id == nil { + return + } + return *m.id, true +} + +// IDs queries the database and returns the entity ids that match the mutation's predicate. +// That means, if the mutation is applied within a transaction with an isolation level such +// as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated +// or updated by the mutation. +func (m *ScheduleMutation) IDs(ctx context.Context) ([]uuid.UUID, error) { + switch { + case m.op.Is(OpUpdateOne | OpDeleteOne): + id, exists := m.ID() + if exists { + return []uuid.UUID{id}, nil + } + fallthrough + case m.op.Is(OpUpdate | OpDelete): + return m.Client().Schedule.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } +} + +// SetProjectID sets the "project_id" field. +func (m *ScheduleMutation) SetProjectID(u uuid.UUID) { + m.project_id = &u +} + +// ProjectID returns the value of the "project_id" field in the mutation. +func (m *ScheduleMutation) ProjectID() (r uuid.UUID, exists bool) { + v := m.project_id + if v == nil { + return + } + return *v, true +} + +// OldProjectID returns the old "project_id" field's value of the Schedule entity. +// If the Schedule object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ScheduleMutation) OldProjectID(ctx context.Context) (v uuid.UUID, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldProjectID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldProjectID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldProjectID: %w", err) + } + return oldValue.ProjectID, nil +} + +// ResetProjectID resets all changes to the "project_id" field. +func (m *ScheduleMutation) ResetProjectID() { + m.project_id = nil +} + +// SetName sets the "name" field. +func (m *ScheduleMutation) SetName(s string) { + m.name = &s +} + +// Name returns the value of the "name" field in the mutation. +func (m *ScheduleMutation) Name() (r string, exists bool) { + v := m.name + if v == nil { + return + } + return *v, true +} + +// OldName returns the old "name" field's value of the Schedule entity. +// If the Schedule object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ScheduleMutation) OldName(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldName is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldName requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldName: %w", err) + } + return oldValue.Name, nil +} + +// ResetName resets all changes to the "name" field. +func (m *ScheduleMutation) ResetName() { + m.name = nil +} + +// SetCronExpr sets the "cron_expr" field. +func (m *ScheduleMutation) SetCronExpr(s string) { + m.cron_expr = &s +} + +// CronExpr returns the value of the "cron_expr" field in the mutation. +func (m *ScheduleMutation) CronExpr() (r string, exists bool) { + v := m.cron_expr + if v == nil { + return + } + return *v, true +} + +// OldCronExpr returns the old "cron_expr" field's value of the Schedule entity. +// If the Schedule object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ScheduleMutation) OldCronExpr(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCronExpr is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCronExpr requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCronExpr: %w", err) + } + return oldValue.CronExpr, nil +} + +// ResetCronExpr resets all changes to the "cron_expr" field. +func (m *ScheduleMutation) ResetCronExpr() { + m.cron_expr = nil +} + +// SetEventType sets the "event_type" field. +func (m *ScheduleMutation) SetEventType(s string) { + m.event_type = &s +} + +// EventType returns the value of the "event_type" field in the mutation. +func (m *ScheduleMutation) EventType() (r string, exists bool) { + v := m.event_type + if v == nil { + return + } + return *v, true +} + +// OldEventType returns the old "event_type" field's value of the Schedule entity. +// If the Schedule object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ScheduleMutation) OldEventType(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldEventType is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldEventType requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldEventType: %w", err) + } + return oldValue.EventType, nil +} + +// ResetEventType resets all changes to the "event_type" field. +func (m *ScheduleMutation) ResetEventType() { + m.event_type = nil +} + +// SetPayload sets the "payload" field. +func (m *ScheduleMutation) SetPayload(s string) { + m.payload = &s +} + +// Payload returns the value of the "payload" field in the mutation. +func (m *ScheduleMutation) Payload() (r string, exists bool) { + v := m.payload + if v == nil { + return + } + return *v, true +} + +// OldPayload returns the old "payload" field's value of the Schedule entity. +// If the Schedule object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ScheduleMutation) OldPayload(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldPayload is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldPayload requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldPayload: %w", err) + } + return oldValue.Payload, nil +} + +// ResetPayload resets all changes to the "payload" field. +func (m *ScheduleMutation) ResetPayload() { + m.payload = nil +} + +// SetStatus sets the "status" field. +func (m *ScheduleMutation) SetStatus(s string) { + m.status = &s +} + +// Status returns the value of the "status" field in the mutation. +func (m *ScheduleMutation) Status() (r string, exists bool) { + v := m.status + if v == nil { + return + } + return *v, true +} + +// OldStatus returns the old "status" field's value of the Schedule entity. +// If the Schedule object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ScheduleMutation) OldStatus(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldStatus is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldStatus requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldStatus: %w", err) + } + return oldValue.Status, nil +} + +// ResetStatus resets all changes to the "status" field. +func (m *ScheduleMutation) ResetStatus() { + m.status = nil +} + +// SetNextRunAt sets the "next_run_at" field. +func (m *ScheduleMutation) SetNextRunAt(t time.Time) { + m.next_run_at = &t +} + +// NextRunAt returns the value of the "next_run_at" field in the mutation. +func (m *ScheduleMutation) NextRunAt() (r time.Time, exists bool) { + v := m.next_run_at + if v == nil { + return + } + return *v, true +} + +// OldNextRunAt returns the old "next_run_at" field's value of the Schedule entity. +// If the Schedule object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ScheduleMutation) OldNextRunAt(ctx context.Context) (v *time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldNextRunAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldNextRunAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldNextRunAt: %w", err) + } + return oldValue.NextRunAt, nil +} + +// ClearNextRunAt clears the value of the "next_run_at" field. +func (m *ScheduleMutation) ClearNextRunAt() { + m.next_run_at = nil + m.clearedFields[schedule.FieldNextRunAt] = struct{}{} +} + +// NextRunAtCleared returns if the "next_run_at" field was cleared in this mutation. +func (m *ScheduleMutation) NextRunAtCleared() bool { + _, ok := m.clearedFields[schedule.FieldNextRunAt] + return ok +} + +// ResetNextRunAt resets all changes to the "next_run_at" field. +func (m *ScheduleMutation) ResetNextRunAt() { + m.next_run_at = nil + delete(m.clearedFields, schedule.FieldNextRunAt) +} + +// SetLastRunAt sets the "last_run_at" field. +func (m *ScheduleMutation) SetLastRunAt(t time.Time) { + m.last_run_at = &t +} + +// LastRunAt returns the value of the "last_run_at" field in the mutation. +func (m *ScheduleMutation) LastRunAt() (r time.Time, exists bool) { + v := m.last_run_at + if v == nil { + return + } + return *v, true +} + +// OldLastRunAt returns the old "last_run_at" field's value of the Schedule entity. +// If the Schedule object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ScheduleMutation) OldLastRunAt(ctx context.Context) (v *time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldLastRunAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldLastRunAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldLastRunAt: %w", err) + } + return oldValue.LastRunAt, nil +} + +// ClearLastRunAt clears the value of the "last_run_at" field. +func (m *ScheduleMutation) ClearLastRunAt() { + m.last_run_at = nil + m.clearedFields[schedule.FieldLastRunAt] = struct{}{} +} + +// LastRunAtCleared returns if the "last_run_at" field was cleared in this mutation. +func (m *ScheduleMutation) LastRunAtCleared() bool { + _, ok := m.clearedFields[schedule.FieldLastRunAt] + return ok +} + +// ResetLastRunAt resets all changes to the "last_run_at" field. +func (m *ScheduleMutation) ResetLastRunAt() { + m.last_run_at = nil + delete(m.clearedFields, schedule.FieldLastRunAt) +} + +// SetLastRunStatus sets the "last_run_status" field. +func (m *ScheduleMutation) SetLastRunStatus(s string) { + m.last_run_status = &s +} + +// LastRunStatus returns the value of the "last_run_status" field in the mutation. +func (m *ScheduleMutation) LastRunStatus() (r string, exists bool) { + v := m.last_run_status + if v == nil { + return + } + return *v, true +} + +// OldLastRunStatus returns the old "last_run_status" field's value of the Schedule entity. +// If the Schedule object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ScheduleMutation) OldLastRunStatus(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldLastRunStatus is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldLastRunStatus requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldLastRunStatus: %w", err) + } + return oldValue.LastRunStatus, nil +} + +// ClearLastRunStatus clears the value of the "last_run_status" field. +func (m *ScheduleMutation) ClearLastRunStatus() { + m.last_run_status = nil + m.clearedFields[schedule.FieldLastRunStatus] = struct{}{} +} + +// LastRunStatusCleared returns if the "last_run_status" field was cleared in this mutation. +func (m *ScheduleMutation) LastRunStatusCleared() bool { + _, ok := m.clearedFields[schedule.FieldLastRunStatus] + return ok +} + +// ResetLastRunStatus resets all changes to the "last_run_status" field. +func (m *ScheduleMutation) ResetLastRunStatus() { + m.last_run_status = nil + delete(m.clearedFields, schedule.FieldLastRunStatus) +} + +// SetLastRunError sets the "last_run_error" field. +func (m *ScheduleMutation) SetLastRunError(s string) { + m.last_run_error = &s +} + +// LastRunError returns the value of the "last_run_error" field in the mutation. +func (m *ScheduleMutation) LastRunError() (r string, exists bool) { + v := m.last_run_error + if v == nil { + return + } + return *v, true +} + +// OldLastRunError returns the old "last_run_error" field's value of the Schedule entity. +// If the Schedule object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ScheduleMutation) OldLastRunError(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldLastRunError is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldLastRunError requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldLastRunError: %w", err) + } + return oldValue.LastRunError, nil +} + +// ClearLastRunError clears the value of the "last_run_error" field. +func (m *ScheduleMutation) ClearLastRunError() { + m.last_run_error = nil + m.clearedFields[schedule.FieldLastRunError] = struct{}{} +} + +// LastRunErrorCleared returns if the "last_run_error" field was cleared in this mutation. +func (m *ScheduleMutation) LastRunErrorCleared() bool { + _, ok := m.clearedFields[schedule.FieldLastRunError] + return ok +} + +// ResetLastRunError resets all changes to the "last_run_error" field. +func (m *ScheduleMutation) ResetLastRunError() { + m.last_run_error = nil + delete(m.clearedFields, schedule.FieldLastRunError) +} + +// SetRunCount sets the "run_count" field. +func (m *ScheduleMutation) SetRunCount(i int) { + m.run_count = &i + m.addrun_count = nil +} + +// RunCount returns the value of the "run_count" field in the mutation. +func (m *ScheduleMutation) RunCount() (r int, exists bool) { + v := m.run_count + if v == nil { + return + } + return *v, true +} + +// OldRunCount returns the old "run_count" field's value of the Schedule entity. +// If the Schedule object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ScheduleMutation) OldRunCount(ctx context.Context) (v int, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldRunCount is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldRunCount requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldRunCount: %w", err) + } + return oldValue.RunCount, nil +} + +// AddRunCount adds i to the "run_count" field. +func (m *ScheduleMutation) AddRunCount(i int) { + if m.addrun_count != nil { + *m.addrun_count += i + } else { + m.addrun_count = &i + } +} + +// AddedRunCount returns the value that was added to the "run_count" field in this mutation. +func (m *ScheduleMutation) AddedRunCount() (r int, exists bool) { + v := m.addrun_count + if v == nil { + return + } + return *v, true +} + +// ResetRunCount resets all changes to the "run_count" field. +func (m *ScheduleMutation) ResetRunCount() { + m.run_count = nil + m.addrun_count = nil +} + +// SetErrorCount sets the "error_count" field. +func (m *ScheduleMutation) SetErrorCount(i int) { + m.error_count = &i + m.adderror_count = nil +} + +// ErrorCount returns the value of the "error_count" field in the mutation. +func (m *ScheduleMutation) ErrorCount() (r int, exists bool) { + v := m.error_count + if v == nil { + return + } + return *v, true +} + +// OldErrorCount returns the old "error_count" field's value of the Schedule entity. +// If the Schedule object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ScheduleMutation) OldErrorCount(ctx context.Context) (v int, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldErrorCount is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldErrorCount requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldErrorCount: %w", err) + } + return oldValue.ErrorCount, nil +} + +// AddErrorCount adds i to the "error_count" field. +func (m *ScheduleMutation) AddErrorCount(i int) { + if m.adderror_count != nil { + *m.adderror_count += i + } else { + m.adderror_count = &i + } +} + +// AddedErrorCount returns the value that was added to the "error_count" field in this mutation. +func (m *ScheduleMutation) AddedErrorCount() (r int, exists bool) { + v := m.adderror_count + if v == nil { + return + } + return *v, true +} + +// ResetErrorCount resets all changes to the "error_count" field. +func (m *ScheduleMutation) ResetErrorCount() { + m.error_count = nil + m.adderror_count = nil +} + +// SetCreatedBy sets the "created_by" field. +func (m *ScheduleMutation) SetCreatedBy(s string) { + m.created_by = &s +} + +// CreatedBy returns the value of the "created_by" field in the mutation. +func (m *ScheduleMutation) CreatedBy() (r string, exists bool) { + v := m.created_by + if v == nil { + return + } + return *v, true +} + +// OldCreatedBy returns the old "created_by" field's value of the Schedule entity. +// If the Schedule object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ScheduleMutation) OldCreatedBy(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreatedBy is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreatedBy requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreatedBy: %w", err) + } + return oldValue.CreatedBy, nil +} + +// ClearCreatedBy clears the value of the "created_by" field. +func (m *ScheduleMutation) ClearCreatedBy() { + m.created_by = nil + m.clearedFields[schedule.FieldCreatedBy] = struct{}{} +} + +// CreatedByCleared returns if the "created_by" field was cleared in this mutation. +func (m *ScheduleMutation) CreatedByCleared() bool { + _, ok := m.clearedFields[schedule.FieldCreatedBy] + return ok +} + +// ResetCreatedBy resets all changes to the "created_by" field. +func (m *ScheduleMutation) ResetCreatedBy() { + m.created_by = nil + delete(m.clearedFields, schedule.FieldCreatedBy) +} + +// SetCreated sets the "created" field. +func (m *ScheduleMutation) SetCreated(t time.Time) { + m.created = &t +} + +// Created returns the value of the "created" field in the mutation. +func (m *ScheduleMutation) Created() (r time.Time, exists bool) { + v := m.created + if v == nil { + return + } + return *v, true +} + +// OldCreated returns the old "created" field's value of the Schedule entity. +// If the Schedule object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ScheduleMutation) OldCreated(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreated is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreated requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreated: %w", err) + } + return oldValue.Created, nil +} + +// ResetCreated resets all changes to the "created" field. +func (m *ScheduleMutation) ResetCreated() { + m.created = nil +} + +// SetUpdated sets the "updated" field. +func (m *ScheduleMutation) SetUpdated(t time.Time) { + m.updated = &t +} + +// Updated returns the value of the "updated" field in the mutation. +func (m *ScheduleMutation) Updated() (r time.Time, exists bool) { + v := m.updated + if v == nil { + return + } + return *v, true +} + +// OldUpdated returns the old "updated" field's value of the Schedule entity. +// If the Schedule object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ScheduleMutation) OldUpdated(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldUpdated is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldUpdated requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldUpdated: %w", err) + } + return oldValue.Updated, nil +} + +// ResetUpdated resets all changes to the "updated" field. +func (m *ScheduleMutation) ResetUpdated() { + m.updated = nil +} + +// Where appends a list predicates to the ScheduleMutation builder. +func (m *ScheduleMutation) Where(ps ...predicate.Schedule) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the ScheduleMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *ScheduleMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.Schedule, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *ScheduleMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *ScheduleMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (Schedule). +func (m *ScheduleMutation) Type() string { + return m.typ +} + +// Fields returns all fields that were changed during this mutation. Note that in +// order to get all numeric fields that were incremented/decremented, call +// AddedFields(). +func (m *ScheduleMutation) Fields() []string { + fields := make([]string, 0, 15) + if m.project_id != nil { + fields = append(fields, schedule.FieldProjectID) + } + if m.name != nil { + fields = append(fields, schedule.FieldName) + } + if m.cron_expr != nil { + fields = append(fields, schedule.FieldCronExpr) + } + if m.event_type != nil { + fields = append(fields, schedule.FieldEventType) + } + if m.payload != nil { + fields = append(fields, schedule.FieldPayload) + } + if m.status != nil { + fields = append(fields, schedule.FieldStatus) + } + if m.next_run_at != nil { + fields = append(fields, schedule.FieldNextRunAt) + } + if m.last_run_at != nil { + fields = append(fields, schedule.FieldLastRunAt) + } + if m.last_run_status != nil { + fields = append(fields, schedule.FieldLastRunStatus) + } + if m.last_run_error != nil { + fields = append(fields, schedule.FieldLastRunError) + } + if m.run_count != nil { + fields = append(fields, schedule.FieldRunCount) + } + if m.error_count != nil { + fields = append(fields, schedule.FieldErrorCount) + } + if m.created_by != nil { + fields = append(fields, schedule.FieldCreatedBy) + } + if m.created != nil { + fields = append(fields, schedule.FieldCreated) + } + if m.updated != nil { + fields = append(fields, schedule.FieldUpdated) + } + return fields +} + +// Field returns the value of a field with the given name. The second boolean +// return value indicates that this field was not set, or was not defined in the +// schema. +func (m *ScheduleMutation) Field(name string) (ent.Value, bool) { + switch name { + case schedule.FieldProjectID: + return m.ProjectID() + case schedule.FieldName: + return m.Name() + case schedule.FieldCronExpr: + return m.CronExpr() + case schedule.FieldEventType: + return m.EventType() + case schedule.FieldPayload: + return m.Payload() + case schedule.FieldStatus: + return m.Status() + case schedule.FieldNextRunAt: + return m.NextRunAt() + case schedule.FieldLastRunAt: + return m.LastRunAt() + case schedule.FieldLastRunStatus: + return m.LastRunStatus() + case schedule.FieldLastRunError: + return m.LastRunError() + case schedule.FieldRunCount: + return m.RunCount() + case schedule.FieldErrorCount: + return m.ErrorCount() + case schedule.FieldCreatedBy: + return m.CreatedBy() + case schedule.FieldCreated: + return m.Created() + case schedule.FieldUpdated: + return m.Updated() + } + return nil, false +} + +// OldField returns the old value of the field from the database. An error is +// returned if the mutation operation is not UpdateOne, or the query to the +// database failed. +func (m *ScheduleMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + switch name { + case schedule.FieldProjectID: + return m.OldProjectID(ctx) + case schedule.FieldName: + return m.OldName(ctx) + case schedule.FieldCronExpr: + return m.OldCronExpr(ctx) + case schedule.FieldEventType: + return m.OldEventType(ctx) + case schedule.FieldPayload: + return m.OldPayload(ctx) + case schedule.FieldStatus: + return m.OldStatus(ctx) + case schedule.FieldNextRunAt: + return m.OldNextRunAt(ctx) + case schedule.FieldLastRunAt: + return m.OldLastRunAt(ctx) + case schedule.FieldLastRunStatus: + return m.OldLastRunStatus(ctx) + case schedule.FieldLastRunError: + return m.OldLastRunError(ctx) + case schedule.FieldRunCount: + return m.OldRunCount(ctx) + case schedule.FieldErrorCount: + return m.OldErrorCount(ctx) + case schedule.FieldCreatedBy: + return m.OldCreatedBy(ctx) + case schedule.FieldCreated: + return m.OldCreated(ctx) + case schedule.FieldUpdated: + return m.OldUpdated(ctx) + } + return nil, fmt.Errorf("unknown Schedule field %s", name) +} + +// SetField sets the value of a field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *ScheduleMutation) SetField(name string, value ent.Value) error { + switch name { + case schedule.FieldProjectID: + v, ok := value.(uuid.UUID) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetProjectID(v) + return nil + case schedule.FieldName: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetName(v) + return nil + case schedule.FieldCronExpr: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCronExpr(v) + return nil + case schedule.FieldEventType: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetEventType(v) + return nil + case schedule.FieldPayload: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetPayload(v) + return nil + case schedule.FieldStatus: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetStatus(v) + return nil + case schedule.FieldNextRunAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetNextRunAt(v) + return nil + case schedule.FieldLastRunAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetLastRunAt(v) + return nil + case schedule.FieldLastRunStatus: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetLastRunStatus(v) + return nil + case schedule.FieldLastRunError: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetLastRunError(v) + return nil + case schedule.FieldRunCount: + v, ok := value.(int) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetRunCount(v) + return nil + case schedule.FieldErrorCount: + v, ok := value.(int) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetErrorCount(v) + return nil + case schedule.FieldCreatedBy: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreatedBy(v) + return nil + case schedule.FieldCreated: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreated(v) + return nil + case schedule.FieldUpdated: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetUpdated(v) + return nil + } + return fmt.Errorf("unknown Schedule field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *ScheduleMutation) AddedFields() []string { + var fields []string + if m.addrun_count != nil { + fields = append(fields, schedule.FieldRunCount) + } + if m.adderror_count != nil { + fields = append(fields, schedule.FieldErrorCount) + } + return fields +} + +// AddedField returns the numeric value that was incremented/decremented on a field +// with the given name. The second boolean return value indicates that this field +// was not set, or was not defined in the schema. +func (m *ScheduleMutation) AddedField(name string) (ent.Value, bool) { + switch name { + case schedule.FieldRunCount: + return m.AddedRunCount() + case schedule.FieldErrorCount: + return m.AddedErrorCount() + } + return nil, false +} + +// AddField adds the value to the field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *ScheduleMutation) AddField(name string, value ent.Value) error { + switch name { + case schedule.FieldRunCount: + v, ok := value.(int) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.AddRunCount(v) + return nil + case schedule.FieldErrorCount: + v, ok := value.(int) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.AddErrorCount(v) + return nil + } + return fmt.Errorf("unknown Schedule numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *ScheduleMutation) ClearedFields() []string { + var fields []string + if m.FieldCleared(schedule.FieldNextRunAt) { + fields = append(fields, schedule.FieldNextRunAt) + } + if m.FieldCleared(schedule.FieldLastRunAt) { + fields = append(fields, schedule.FieldLastRunAt) + } + if m.FieldCleared(schedule.FieldLastRunStatus) { + fields = append(fields, schedule.FieldLastRunStatus) + } + if m.FieldCleared(schedule.FieldLastRunError) { + fields = append(fields, schedule.FieldLastRunError) + } + if m.FieldCleared(schedule.FieldCreatedBy) { + fields = append(fields, schedule.FieldCreatedBy) + } + return fields +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *ScheduleMutation) FieldCleared(name string) bool { + _, ok := m.clearedFields[name] + return ok +} + +// ClearField clears the value of the field with the given name. It returns an +// error if the field is not defined in the schema. +func (m *ScheduleMutation) ClearField(name string) error { + switch name { + case schedule.FieldNextRunAt: + m.ClearNextRunAt() + return nil + case schedule.FieldLastRunAt: + m.ClearLastRunAt() + return nil + case schedule.FieldLastRunStatus: + m.ClearLastRunStatus() + return nil + case schedule.FieldLastRunError: + m.ClearLastRunError() + return nil + case schedule.FieldCreatedBy: + m.ClearCreatedBy() + return nil + } + return fmt.Errorf("unknown Schedule nullable field %s", name) +} + +// ResetField resets all changes in the mutation for the field with the given name. +// It returns an error if the field is not defined in the schema. +func (m *ScheduleMutation) ResetField(name string) error { + switch name { + case schedule.FieldProjectID: + m.ResetProjectID() + return nil + case schedule.FieldName: + m.ResetName() + return nil + case schedule.FieldCronExpr: + m.ResetCronExpr() + return nil + case schedule.FieldEventType: + m.ResetEventType() + return nil + case schedule.FieldPayload: + m.ResetPayload() + return nil + case schedule.FieldStatus: + m.ResetStatus() + return nil + case schedule.FieldNextRunAt: + m.ResetNextRunAt() + return nil + case schedule.FieldLastRunAt: + m.ResetLastRunAt() + return nil + case schedule.FieldLastRunStatus: + m.ResetLastRunStatus() + return nil + case schedule.FieldLastRunError: + m.ResetLastRunError() + return nil + case schedule.FieldRunCount: + m.ResetRunCount() + return nil + case schedule.FieldErrorCount: + m.ResetErrorCount() + return nil + case schedule.FieldCreatedBy: + m.ResetCreatedBy() + return nil + case schedule.FieldCreated: + m.ResetCreated() + return nil + case schedule.FieldUpdated: + m.ResetUpdated() + return nil + } + return fmt.Errorf("unknown Schedule field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *ScheduleMutation) AddedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *ScheduleMutation) AddedIDs(name string) []ent.Value { + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *ScheduleMutation) RemovedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *ScheduleMutation) RemovedIDs(name string) []ent.Value { + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *ScheduleMutation) ClearedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *ScheduleMutation) EdgeCleared(name string) bool { + return false +} + +// ClearEdge clears the value of the edge with the given name. It returns an error +// if that edge is not defined in the schema. +func (m *ScheduleMutation) ClearEdge(name string) error { + return fmt.Errorf("unknown Schedule unique edge %s", name) +} + +// ResetEdge resets all changes to the edge with the given name in this mutation. +// It returns an error if the edge is not defined in the schema. +func (m *ScheduleMutation) ResetEdge(name string) error { + return fmt.Errorf("unknown Schedule edge %s", name) +} + +// ScheduledEventMutation represents an operation that mutates the ScheduledEvent nodes in the graph. +type ScheduledEventMutation struct { + config + op Op + typ string + id *uuid.UUID + project_id *uuid.UUID + event_type *string + fire_at *time.Time + payload *string + status *string + created_by *string + fired_at *time.Time + error *string + schedule_id *string + created *time.Time + clearedFields map[string]struct{} + done bool + oldValue func(context.Context) (*ScheduledEvent, error) + predicates []predicate.ScheduledEvent +} + +var _ ent.Mutation = (*ScheduledEventMutation)(nil) + +// scheduledeventOption allows management of the mutation configuration using functional options. +type scheduledeventOption func(*ScheduledEventMutation) + +// newScheduledEventMutation creates new mutation for the ScheduledEvent entity. +func newScheduledEventMutation(c config, op Op, opts ...scheduledeventOption) *ScheduledEventMutation { + m := &ScheduledEventMutation{ + config: c, + op: op, + typ: TypeScheduledEvent, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// withScheduledEventID sets the ID field of the mutation. +func withScheduledEventID(id uuid.UUID) scheduledeventOption { + return func(m *ScheduledEventMutation) { + var ( + err error + once sync.Once + value *ScheduledEvent + ) + m.oldValue = func(ctx context.Context) (*ScheduledEvent, error) { + once.Do(func() { + if m.done { + err = errors.New("querying old values post mutation is not allowed") + } else { + value, err = m.Client().ScheduledEvent.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } +} + +// withScheduledEvent sets the old ScheduledEvent of the mutation. +func withScheduledEvent(node *ScheduledEvent) scheduledeventOption { + return func(m *ScheduledEventMutation) { + m.oldValue = func(context.Context) (*ScheduledEvent, error) { + return node, nil + } + m.id = &node.ID + } +} + +// Client returns a new `ent.Client` from the mutation. If the mutation was +// executed in a transaction (ent.Tx), a transactional client is returned. +func (m ScheduledEventMutation) Client() *Client { + client := &Client{config: m.config} + client.init() + return client +} + +// Tx returns an `ent.Tx` for mutations that were executed in transactions; +// it returns an error otherwise. +func (m ScheduledEventMutation) Tx() (*Tx, error) { + if _, ok := m.driver.(*txDriver); !ok { + return nil, errors.New("ent: mutation is not running in a transaction") + } + tx := &Tx{config: m.config} + tx.init() + return tx, nil +} + +// SetID sets the value of the id field. Note that this +// operation is only accepted on creation of ScheduledEvent entities. +func (m *ScheduledEventMutation) SetID(id uuid.UUID) { + m.id = &id +} + +// ID returns the ID value in the mutation. Note that the ID is only available +// if it was provided to the builder or after it was returned from the database. +func (m *ScheduledEventMutation) ID() (id uuid.UUID, exists bool) { + if m.id == nil { + return + } + return *m.id, true +} + +// IDs queries the database and returns the entity ids that match the mutation's predicate. +// That means, if the mutation is applied within a transaction with an isolation level such +// as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated +// or updated by the mutation. +func (m *ScheduledEventMutation) IDs(ctx context.Context) ([]uuid.UUID, error) { + switch { + case m.op.Is(OpUpdateOne | OpDeleteOne): + id, exists := m.ID() + if exists { + return []uuid.UUID{id}, nil + } + fallthrough + case m.op.Is(OpUpdate | OpDelete): + return m.Client().ScheduledEvent.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } +} + +// SetProjectID sets the "project_id" field. +func (m *ScheduledEventMutation) SetProjectID(u uuid.UUID) { + m.project_id = &u +} + +// ProjectID returns the value of the "project_id" field in the mutation. +func (m *ScheduledEventMutation) ProjectID() (r uuid.UUID, exists bool) { + v := m.project_id + if v == nil { + return + } + return *v, true +} + +// OldProjectID returns the old "project_id" field's value of the ScheduledEvent entity. +// If the ScheduledEvent object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ScheduledEventMutation) OldProjectID(ctx context.Context) (v uuid.UUID, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldProjectID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldProjectID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldProjectID: %w", err) + } + return oldValue.ProjectID, nil +} + +// ResetProjectID resets all changes to the "project_id" field. +func (m *ScheduledEventMutation) ResetProjectID() { + m.project_id = nil +} + +// SetEventType sets the "event_type" field. +func (m *ScheduledEventMutation) SetEventType(s string) { + m.event_type = &s +} + +// EventType returns the value of the "event_type" field in the mutation. +func (m *ScheduledEventMutation) EventType() (r string, exists bool) { + v := m.event_type + if v == nil { + return + } + return *v, true +} + +// OldEventType returns the old "event_type" field's value of the ScheduledEvent entity. +// If the ScheduledEvent object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ScheduledEventMutation) OldEventType(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldEventType is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldEventType requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldEventType: %w", err) + } + return oldValue.EventType, nil +} + +// ResetEventType resets all changes to the "event_type" field. +func (m *ScheduledEventMutation) ResetEventType() { + m.event_type = nil +} + +// SetFireAt sets the "fire_at" field. +func (m *ScheduledEventMutation) SetFireAt(t time.Time) { + m.fire_at = &t +} + +// FireAt returns the value of the "fire_at" field in the mutation. +func (m *ScheduledEventMutation) FireAt() (r time.Time, exists bool) { + v := m.fire_at + if v == nil { + return + } + return *v, true +} + +// OldFireAt returns the old "fire_at" field's value of the ScheduledEvent entity. +// If the ScheduledEvent object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ScheduledEventMutation) OldFireAt(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldFireAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldFireAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldFireAt: %w", err) + } + return oldValue.FireAt, nil +} + +// ResetFireAt resets all changes to the "fire_at" field. +func (m *ScheduledEventMutation) ResetFireAt() { + m.fire_at = nil +} + +// SetPayload sets the "payload" field. +func (m *ScheduledEventMutation) SetPayload(s string) { + m.payload = &s +} + +// Payload returns the value of the "payload" field in the mutation. +func (m *ScheduledEventMutation) Payload() (r string, exists bool) { + v := m.payload + if v == nil { + return + } + return *v, true +} + +// OldPayload returns the old "payload" field's value of the ScheduledEvent entity. +// If the ScheduledEvent object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ScheduledEventMutation) OldPayload(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldPayload is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldPayload requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldPayload: %w", err) + } + return oldValue.Payload, nil +} + +// ResetPayload resets all changes to the "payload" field. +func (m *ScheduledEventMutation) ResetPayload() { + m.payload = nil +} + +// SetStatus sets the "status" field. +func (m *ScheduledEventMutation) SetStatus(s string) { + m.status = &s +} + +// Status returns the value of the "status" field in the mutation. +func (m *ScheduledEventMutation) Status() (r string, exists bool) { + v := m.status + if v == nil { + return + } + return *v, true +} + +// OldStatus returns the old "status" field's value of the ScheduledEvent entity. +// If the ScheduledEvent object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ScheduledEventMutation) OldStatus(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldStatus is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldStatus requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldStatus: %w", err) + } + return oldValue.Status, nil +} + +// ResetStatus resets all changes to the "status" field. +func (m *ScheduledEventMutation) ResetStatus() { + m.status = nil +} + +// SetCreatedBy sets the "created_by" field. +func (m *ScheduledEventMutation) SetCreatedBy(s string) { + m.created_by = &s +} + +// CreatedBy returns the value of the "created_by" field in the mutation. +func (m *ScheduledEventMutation) CreatedBy() (r string, exists bool) { + v := m.created_by + if v == nil { + return + } + return *v, true +} + +// OldCreatedBy returns the old "created_by" field's value of the ScheduledEvent entity. +// If the ScheduledEvent object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ScheduledEventMutation) OldCreatedBy(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreatedBy is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreatedBy requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreatedBy: %w", err) + } + return oldValue.CreatedBy, nil +} + +// ClearCreatedBy clears the value of the "created_by" field. +func (m *ScheduledEventMutation) ClearCreatedBy() { + m.created_by = nil + m.clearedFields[scheduledevent.FieldCreatedBy] = struct{}{} +} + +// CreatedByCleared returns if the "created_by" field was cleared in this mutation. +func (m *ScheduledEventMutation) CreatedByCleared() bool { + _, ok := m.clearedFields[scheduledevent.FieldCreatedBy] + return ok +} + +// ResetCreatedBy resets all changes to the "created_by" field. +func (m *ScheduledEventMutation) ResetCreatedBy() { + m.created_by = nil + delete(m.clearedFields, scheduledevent.FieldCreatedBy) +} + +// SetFiredAt sets the "fired_at" field. +func (m *ScheduledEventMutation) SetFiredAt(t time.Time) { + m.fired_at = &t +} + +// FiredAt returns the value of the "fired_at" field in the mutation. +func (m *ScheduledEventMutation) FiredAt() (r time.Time, exists bool) { + v := m.fired_at + if v == nil { + return + } + return *v, true +} + +// OldFiredAt returns the old "fired_at" field's value of the ScheduledEvent entity. +// If the ScheduledEvent object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ScheduledEventMutation) OldFiredAt(ctx context.Context) (v *time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldFiredAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldFiredAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldFiredAt: %w", err) + } + return oldValue.FiredAt, nil +} + +// ClearFiredAt clears the value of the "fired_at" field. +func (m *ScheduledEventMutation) ClearFiredAt() { + m.fired_at = nil + m.clearedFields[scheduledevent.FieldFiredAt] = struct{}{} +} + +// FiredAtCleared returns if the "fired_at" field was cleared in this mutation. +func (m *ScheduledEventMutation) FiredAtCleared() bool { + _, ok := m.clearedFields[scheduledevent.FieldFiredAt] + return ok +} + +// ResetFiredAt resets all changes to the "fired_at" field. +func (m *ScheduledEventMutation) ResetFiredAt() { + m.fired_at = nil + delete(m.clearedFields, scheduledevent.FieldFiredAt) +} + +// SetError sets the "error" field. +func (m *ScheduledEventMutation) SetError(s string) { + m.error = &s +} + +// Error returns the value of the "error" field in the mutation. +func (m *ScheduledEventMutation) Error() (r string, exists bool) { + v := m.error + if v == nil { + return + } + return *v, true +} + +// OldError returns the old "error" field's value of the ScheduledEvent entity. +// If the ScheduledEvent object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ScheduledEventMutation) OldError(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldError is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldError requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldError: %w", err) + } + return oldValue.Error, nil +} + +// ClearError clears the value of the "error" field. +func (m *ScheduledEventMutation) ClearError() { + m.error = nil + m.clearedFields[scheduledevent.FieldError] = struct{}{} +} + +// ErrorCleared returns if the "error" field was cleared in this mutation. +func (m *ScheduledEventMutation) ErrorCleared() bool { + _, ok := m.clearedFields[scheduledevent.FieldError] + return ok +} + +// ResetError resets all changes to the "error" field. +func (m *ScheduledEventMutation) ResetError() { + m.error = nil + delete(m.clearedFields, scheduledevent.FieldError) +} + +// SetScheduleID sets the "schedule_id" field. +func (m *ScheduledEventMutation) SetScheduleID(s string) { + m.schedule_id = &s +} + +// ScheduleID returns the value of the "schedule_id" field in the mutation. +func (m *ScheduledEventMutation) ScheduleID() (r string, exists bool) { + v := m.schedule_id + if v == nil { + return + } + return *v, true +} + +// OldScheduleID returns the old "schedule_id" field's value of the ScheduledEvent entity. +// If the ScheduledEvent object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ScheduledEventMutation) OldScheduleID(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldScheduleID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldScheduleID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldScheduleID: %w", err) + } + return oldValue.ScheduleID, nil +} + +// ClearScheduleID clears the value of the "schedule_id" field. +func (m *ScheduledEventMutation) ClearScheduleID() { + m.schedule_id = nil + m.clearedFields[scheduledevent.FieldScheduleID] = struct{}{} +} + +// ScheduleIDCleared returns if the "schedule_id" field was cleared in this mutation. +func (m *ScheduledEventMutation) ScheduleIDCleared() bool { + _, ok := m.clearedFields[scheduledevent.FieldScheduleID] + return ok +} + +// ResetScheduleID resets all changes to the "schedule_id" field. +func (m *ScheduledEventMutation) ResetScheduleID() { + m.schedule_id = nil + delete(m.clearedFields, scheduledevent.FieldScheduleID) +} + +// SetCreated sets the "created" field. +func (m *ScheduledEventMutation) SetCreated(t time.Time) { + m.created = &t +} + +// Created returns the value of the "created" field in the mutation. +func (m *ScheduledEventMutation) Created() (r time.Time, exists bool) { + v := m.created + if v == nil { + return + } + return *v, true +} + +// OldCreated returns the old "created" field's value of the ScheduledEvent entity. +// If the ScheduledEvent object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ScheduledEventMutation) OldCreated(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreated is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreated requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreated: %w", err) + } + return oldValue.Created, nil +} + +// ResetCreated resets all changes to the "created" field. +func (m *ScheduledEventMutation) ResetCreated() { + m.created = nil +} + +// Where appends a list predicates to the ScheduledEventMutation builder. +func (m *ScheduledEventMutation) Where(ps ...predicate.ScheduledEvent) { m.predicates = append(m.predicates, ps...) } -// WhereP appends storage-level predicates to the AgentMutation builder. Using this method, +// WhereP appends storage-level predicates to the ScheduledEventMutation builder. Using this method, // users can use type-assertion to append predicates that do not depend on any generated package. -func (m *AgentMutation) WhereP(ps ...func(*sql.Selector)) { - p := make([]predicate.Agent, len(ps)) +func (m *ScheduledEventMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.ScheduledEvent, len(ps)) for i := range ps { p[i] = ps[i] } @@ -2219,57 +26116,54 @@ func (m *AgentMutation) WhereP(ps ...func(*sql.Selector)) { } // Op returns the operation name. -func (m *AgentMutation) Op() Op { +func (m *ScheduledEventMutation) Op() Op { return m.op } // SetOp allows setting the mutation operation. -func (m *AgentMutation) SetOp(op Op) { +func (m *ScheduledEventMutation) SetOp(op Op) { m.op = op } -// Type returns the node type of this mutation (Agent). -func (m *AgentMutation) Type() string { +// Type returns the node type of this mutation (ScheduledEvent). +func (m *ScheduledEventMutation) Type() string { return m.typ } // Fields returns all fields that were changed during this mutation. Note that in // order to get all numeric fields that were incremented/decremented, call // AddedFields(). -func (m *AgentMutation) Fields() []string { - fields := make([]string, 0, 11) - if m.slug != nil { - fields = append(fields, agent.FieldSlug) +func (m *ScheduledEventMutation) Fields() []string { + fields := make([]string, 0, 10) + if m.project_id != nil { + fields = append(fields, scheduledevent.FieldProjectID) } - if m.name != nil { - fields = append(fields, agent.FieldName) + if m.event_type != nil { + fields = append(fields, scheduledevent.FieldEventType) } - if m.template != nil { - fields = append(fields, agent.FieldTemplate) + if m.fire_at != nil { + fields = append(fields, scheduledevent.FieldFireAt) } - if m.project != nil { - fields = append(fields, agent.FieldProjectID) + if m.payload != nil { + fields = append(fields, scheduledevent.FieldPayload) } if m.status != nil { - fields = append(fields, agent.FieldStatus) + fields = append(fields, scheduledevent.FieldStatus) } - if m.creator != nil { - fields = append(fields, agent.FieldCreatedBy) + if m.created_by != nil { + fields = append(fields, scheduledevent.FieldCreatedBy) } - if m.owner != nil { - fields = append(fields, agent.FieldOwnerID) + if m.fired_at != nil { + fields = append(fields, scheduledevent.FieldFiredAt) } - if m.delegation_enabled != nil { - fields = append(fields, agent.FieldDelegationEnabled) + if m.error != nil { + fields = append(fields, scheduledevent.FieldError) } - if m.visibility != nil { - fields = append(fields, agent.FieldVisibility) + if m.schedule_id != nil { + fields = append(fields, scheduledevent.FieldScheduleID) } if m.created != nil { - fields = append(fields, agent.FieldCreated) - } - if m.updated != nil { - fields = append(fields, agent.FieldUpdated) + fields = append(fields, scheduledevent.FieldCreated) } return fields } @@ -2277,30 +26171,28 @@ func (m *AgentMutation) Fields() []string { // Field returns the value of a field with the given name. The second boolean // return value indicates that this field was not set, or was not defined in the // schema. -func (m *AgentMutation) Field(name string) (ent.Value, bool) { +func (m *ScheduledEventMutation) Field(name string) (ent.Value, bool) { switch name { - case agent.FieldSlug: - return m.Slug() - case agent.FieldName: - return m.Name() - case agent.FieldTemplate: - return m.Template() - case agent.FieldProjectID: + case scheduledevent.FieldProjectID: return m.ProjectID() - case agent.FieldStatus: + case scheduledevent.FieldEventType: + return m.EventType() + case scheduledevent.FieldFireAt: + return m.FireAt() + case scheduledevent.FieldPayload: + return m.Payload() + case scheduledevent.FieldStatus: return m.Status() - case agent.FieldCreatedBy: + case scheduledevent.FieldCreatedBy: return m.CreatedBy() - case agent.FieldOwnerID: - return m.OwnerID() - case agent.FieldDelegationEnabled: - return m.DelegationEnabled() - case agent.FieldVisibility: - return m.Visibility() - case agent.FieldCreated: + case scheduledevent.FieldFiredAt: + return m.FiredAt() + case scheduledevent.FieldError: + return m.Error() + case scheduledevent.FieldScheduleID: + return m.ScheduleID() + case scheduledevent.FieldCreated: return m.Created() - case agent.FieldUpdated: - return m.Updated() } return nil, false } @@ -2308,434 +26200,304 @@ func (m *AgentMutation) Field(name string) (ent.Value, bool) { // OldField returns the old value of the field from the database. An error is // returned if the mutation operation is not UpdateOne, or the query to the // database failed. -func (m *AgentMutation) OldField(ctx context.Context, name string) (ent.Value, error) { +func (m *ScheduledEventMutation) OldField(ctx context.Context, name string) (ent.Value, error) { switch name { - case agent.FieldSlug: - return m.OldSlug(ctx) - case agent.FieldName: - return m.OldName(ctx) - case agent.FieldTemplate: - return m.OldTemplate(ctx) - case agent.FieldProjectID: + case scheduledevent.FieldProjectID: return m.OldProjectID(ctx) - case agent.FieldStatus: + case scheduledevent.FieldEventType: + return m.OldEventType(ctx) + case scheduledevent.FieldFireAt: + return m.OldFireAt(ctx) + case scheduledevent.FieldPayload: + return m.OldPayload(ctx) + case scheduledevent.FieldStatus: return m.OldStatus(ctx) - case agent.FieldCreatedBy: + case scheduledevent.FieldCreatedBy: return m.OldCreatedBy(ctx) - case agent.FieldOwnerID: - return m.OldOwnerID(ctx) - case agent.FieldDelegationEnabled: - return m.OldDelegationEnabled(ctx) - case agent.FieldVisibility: - return m.OldVisibility(ctx) - case agent.FieldCreated: + case scheduledevent.FieldFiredAt: + return m.OldFiredAt(ctx) + case scheduledevent.FieldError: + return m.OldError(ctx) + case scheduledevent.FieldScheduleID: + return m.OldScheduleID(ctx) + case scheduledevent.FieldCreated: return m.OldCreated(ctx) - case agent.FieldUpdated: - return m.OldUpdated(ctx) } - return nil, fmt.Errorf("unknown Agent field %s", name) + return nil, fmt.Errorf("unknown ScheduledEvent field %s", name) } // SetField sets the value of a field with the given name. It returns an error if // the field is not defined in the schema, or if the type mismatched the field // type. -func (m *AgentMutation) SetField(name string, value ent.Value) error { +func (m *ScheduledEventMutation) SetField(name string, value ent.Value) error { switch name { - case agent.FieldSlug: - v, ok := value.(string) + case scheduledevent.FieldProjectID: + v, ok := value.(uuid.UUID) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetSlug(v) + m.SetProjectID(v) return nil - case agent.FieldName: + case scheduledevent.FieldEventType: v, ok := value.(string) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetName(v) + m.SetEventType(v) return nil - case agent.FieldTemplate: - v, ok := value.(string) + case scheduledevent.FieldFireAt: + v, ok := value.(time.Time) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetTemplate(v) + m.SetFireAt(v) return nil - case agent.FieldProjectID: - v, ok := value.(uuid.UUID) + case scheduledevent.FieldPayload: + v, ok := value.(string) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetProjectID(v) + m.SetPayload(v) return nil - case agent.FieldStatus: - v, ok := value.(agent.Status) + case scheduledevent.FieldStatus: + v, ok := value.(string) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } m.SetStatus(v) return nil - case agent.FieldCreatedBy: - v, ok := value.(uuid.UUID) + case scheduledevent.FieldCreatedBy: + v, ok := value.(string) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } m.SetCreatedBy(v) return nil - case agent.FieldOwnerID: - v, ok := value.(uuid.UUID) + case scheduledevent.FieldFiredAt: + v, ok := value.(time.Time) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetOwnerID(v) + m.SetFiredAt(v) return nil - case agent.FieldDelegationEnabled: - v, ok := value.(bool) + case scheduledevent.FieldError: + v, ok := value.(string) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetDelegationEnabled(v) + m.SetError(v) return nil - case agent.FieldVisibility: + case scheduledevent.FieldScheduleID: v, ok := value.(string) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetVisibility(v) + m.SetScheduleID(v) return nil - case agent.FieldCreated: + case scheduledevent.FieldCreated: v, ok := value.(time.Time) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } m.SetCreated(v) return nil - case agent.FieldUpdated: - v, ok := value.(time.Time) - if !ok { - return fmt.Errorf("unexpected type %T for field %s", value, name) - } - m.SetUpdated(v) - return nil } - return fmt.Errorf("unknown Agent field %s", name) + return fmt.Errorf("unknown ScheduledEvent field %s", name) } // AddedFields returns all numeric fields that were incremented/decremented during // this mutation. -func (m *AgentMutation) AddedFields() []string { +func (m *ScheduledEventMutation) AddedFields() []string { return nil } // AddedField returns the numeric value that was incremented/decremented on a field // with the given name. The second boolean return value indicates that this field // was not set, or was not defined in the schema. -func (m *AgentMutation) AddedField(name string) (ent.Value, bool) { +func (m *ScheduledEventMutation) AddedField(name string) (ent.Value, bool) { return nil, false } // AddField adds the value to the field with the given name. It returns an error if // the field is not defined in the schema, or if the type mismatched the field // type. -func (m *AgentMutation) AddField(name string, value ent.Value) error { +func (m *ScheduledEventMutation) AddField(name string, value ent.Value) error { switch name { } - return fmt.Errorf("unknown Agent numeric field %s", name) + return fmt.Errorf("unknown ScheduledEvent numeric field %s", name) } // ClearedFields returns all nullable fields that were cleared during this // mutation. -func (m *AgentMutation) ClearedFields() []string { +func (m *ScheduledEventMutation) ClearedFields() []string { var fields []string - if m.FieldCleared(agent.FieldTemplate) { - fields = append(fields, agent.FieldTemplate) + if m.FieldCleared(scheduledevent.FieldCreatedBy) { + fields = append(fields, scheduledevent.FieldCreatedBy) } - if m.FieldCleared(agent.FieldCreatedBy) { - fields = append(fields, agent.FieldCreatedBy) + if m.FieldCleared(scheduledevent.FieldFiredAt) { + fields = append(fields, scheduledevent.FieldFiredAt) } - if m.FieldCleared(agent.FieldOwnerID) { - fields = append(fields, agent.FieldOwnerID) + if m.FieldCleared(scheduledevent.FieldError) { + fields = append(fields, scheduledevent.FieldError) + } + if m.FieldCleared(scheduledevent.FieldScheduleID) { + fields = append(fields, scheduledevent.FieldScheduleID) } return fields } // FieldCleared returns a boolean indicating if a field with the given name was // cleared in this mutation. -func (m *AgentMutation) FieldCleared(name string) bool { +func (m *ScheduledEventMutation) FieldCleared(name string) bool { _, ok := m.clearedFields[name] return ok } // ClearField clears the value of the field with the given name. It returns an // error if the field is not defined in the schema. -func (m *AgentMutation) ClearField(name string) error { +func (m *ScheduledEventMutation) ClearField(name string) error { switch name { - case agent.FieldTemplate: - m.ClearTemplate() - return nil - case agent.FieldCreatedBy: + case scheduledevent.FieldCreatedBy: m.ClearCreatedBy() return nil - case agent.FieldOwnerID: - m.ClearOwnerID() + case scheduledevent.FieldFiredAt: + m.ClearFiredAt() + return nil + case scheduledevent.FieldError: + m.ClearError() + return nil + case scheduledevent.FieldScheduleID: + m.ClearScheduleID() return nil } - return fmt.Errorf("unknown Agent nullable field %s", name) + return fmt.Errorf("unknown ScheduledEvent nullable field %s", name) } // ResetField resets all changes in the mutation for the field with the given name. // It returns an error if the field is not defined in the schema. -func (m *AgentMutation) ResetField(name string) error { +func (m *ScheduledEventMutation) ResetField(name string) error { switch name { - case agent.FieldSlug: - m.ResetSlug() + case scheduledevent.FieldProjectID: + m.ResetProjectID() return nil - case agent.FieldName: - m.ResetName() + case scheduledevent.FieldEventType: + m.ResetEventType() return nil - case agent.FieldTemplate: - m.ResetTemplate() + case scheduledevent.FieldFireAt: + m.ResetFireAt() return nil - case agent.FieldProjectID: - m.ResetProjectID() + case scheduledevent.FieldPayload: + m.ResetPayload() return nil - case agent.FieldStatus: + case scheduledevent.FieldStatus: m.ResetStatus() return nil - case agent.FieldCreatedBy: + case scheduledevent.FieldCreatedBy: m.ResetCreatedBy() return nil - case agent.FieldOwnerID: - m.ResetOwnerID() + case scheduledevent.FieldFiredAt: + m.ResetFiredAt() return nil - case agent.FieldDelegationEnabled: - m.ResetDelegationEnabled() + case scheduledevent.FieldError: + m.ResetError() return nil - case agent.FieldVisibility: - m.ResetVisibility() + case scheduledevent.FieldScheduleID: + m.ResetScheduleID() return nil - case agent.FieldCreated: + case scheduledevent.FieldCreated: m.ResetCreated() return nil - case agent.FieldUpdated: - m.ResetUpdated() - return nil } - return fmt.Errorf("unknown Agent field %s", name) + return fmt.Errorf("unknown ScheduledEvent field %s", name) } // AddedEdges returns all edge names that were set/added in this mutation. -func (m *AgentMutation) AddedEdges() []string { - edges := make([]string, 0, 5) - if m.project != nil { - edges = append(edges, agent.EdgeProject) - } - if m.creator != nil { - edges = append(edges, agent.EdgeCreator) - } - if m.owner != nil { - edges = append(edges, agent.EdgeOwner) - } - if m.memberships != nil { - edges = append(edges, agent.EdgeMemberships) - } - if m.policy_bindings != nil { - edges = append(edges, agent.EdgePolicyBindings) - } +func (m *ScheduledEventMutation) AddedEdges() []string { + edges := make([]string, 0, 0) return edges } // AddedIDs returns all IDs (to other nodes) that were added for the given edge // name in this mutation. -func (m *AgentMutation) AddedIDs(name string) []ent.Value { - switch name { - case agent.EdgeProject: - if id := m.project; id != nil { - return []ent.Value{*id} - } - case agent.EdgeCreator: - if id := m.creator; id != nil { - return []ent.Value{*id} - } - case agent.EdgeOwner: - if id := m.owner; id != nil { - return []ent.Value{*id} - } - case agent.EdgeMemberships: - ids := make([]ent.Value, 0, len(m.memberships)) - for id := range m.memberships { - ids = append(ids, id) - } - return ids - case agent.EdgePolicyBindings: - ids := make([]ent.Value, 0, len(m.policy_bindings)) - for id := range m.policy_bindings { - ids = append(ids, id) - } - return ids - } +func (m *ScheduledEventMutation) AddedIDs(name string) []ent.Value { return nil } // RemovedEdges returns all edge names that were removed in this mutation. -func (m *AgentMutation) RemovedEdges() []string { - edges := make([]string, 0, 5) - if m.removedmemberships != nil { - edges = append(edges, agent.EdgeMemberships) - } - if m.removedpolicy_bindings != nil { - edges = append(edges, agent.EdgePolicyBindings) - } +func (m *ScheduledEventMutation) RemovedEdges() []string { + edges := make([]string, 0, 0) return edges } // RemovedIDs returns all IDs (to other nodes) that were removed for the edge with // the given name in this mutation. -func (m *AgentMutation) RemovedIDs(name string) []ent.Value { - switch name { - case agent.EdgeMemberships: - ids := make([]ent.Value, 0, len(m.removedmemberships)) - for id := range m.removedmemberships { - ids = append(ids, id) - } - return ids - case agent.EdgePolicyBindings: - ids := make([]ent.Value, 0, len(m.removedpolicy_bindings)) - for id := range m.removedpolicy_bindings { - ids = append(ids, id) - } - return ids - } +func (m *ScheduledEventMutation) RemovedIDs(name string) []ent.Value { return nil } // ClearedEdges returns all edge names that were cleared in this mutation. -func (m *AgentMutation) ClearedEdges() []string { - edges := make([]string, 0, 5) - if m.clearedproject { - edges = append(edges, agent.EdgeProject) - } - if m.clearedcreator { - edges = append(edges, agent.EdgeCreator) - } - if m.clearedowner { - edges = append(edges, agent.EdgeOwner) - } - if m.clearedmemberships { - edges = append(edges, agent.EdgeMemberships) - } - if m.clearedpolicy_bindings { - edges = append(edges, agent.EdgePolicyBindings) - } +func (m *ScheduledEventMutation) ClearedEdges() []string { + edges := make([]string, 0, 0) return edges } // EdgeCleared returns a boolean which indicates if the edge with the given name // was cleared in this mutation. -func (m *AgentMutation) EdgeCleared(name string) bool { - switch name { - case agent.EdgeProject: - return m.clearedproject - case agent.EdgeCreator: - return m.clearedcreator - case agent.EdgeOwner: - return m.clearedowner - case agent.EdgeMemberships: - return m.clearedmemberships - case agent.EdgePolicyBindings: - return m.clearedpolicy_bindings - } +func (m *ScheduledEventMutation) EdgeCleared(name string) bool { return false } // ClearEdge clears the value of the edge with the given name. It returns an error // if that edge is not defined in the schema. -func (m *AgentMutation) ClearEdge(name string) error { - switch name { - case agent.EdgeProject: - m.ClearProject() - return nil - case agent.EdgeCreator: - m.ClearCreator() - return nil - case agent.EdgeOwner: - m.ClearOwner() - return nil - } - return fmt.Errorf("unknown Agent unique edge %s", name) +func (m *ScheduledEventMutation) ClearEdge(name string) error { + return fmt.Errorf("unknown ScheduledEvent unique edge %s", name) } // ResetEdge resets all changes to the edge with the given name in this mutation. // It returns an error if the edge is not defined in the schema. -func (m *AgentMutation) ResetEdge(name string) error { - switch name { - case agent.EdgeProject: - m.ResetProject() - return nil - case agent.EdgeCreator: - m.ResetCreator() - return nil - case agent.EdgeOwner: - m.ResetOwner() - return nil - case agent.EdgeMemberships: - m.ResetMemberships() - return nil - case agent.EdgePolicyBindings: - m.ResetPolicyBindings() - return nil - } - return fmt.Errorf("unknown Agent edge %s", name) +func (m *ScheduledEventMutation) ResetEdge(name string) error { + return fmt.Errorf("unknown ScheduledEvent edge %s", name) } -// GroupMutation represents an operation that mutates the Group nodes in the graph. -type GroupMutation struct { +// SecretMutation represents an operation that mutates the Secret nodes in the graph. +type SecretMutation struct { config - op Op - typ string - id *uuid.UUID - name *string - slug *string - description *string - group_type *group.GroupType - project_id *uuid.UUID - labels *map[string]string - annotations *map[string]string - created *time.Time - updated *time.Time - created_by *string - clearedFields map[string]struct{} - memberships map[uuid.UUID]struct{} - removedmemberships map[uuid.UUID]struct{} - clearedmemberships bool - parent_groups map[uuid.UUID]struct{} - removedparent_groups map[uuid.UUID]struct{} - clearedparent_groups bool - child_groups map[uuid.UUID]struct{} - removedchild_groups map[uuid.UUID]struct{} - clearedchild_groups bool - owner *uuid.UUID - clearedowner bool - policy_bindings map[uuid.UUID]struct{} - removedpolicy_bindings map[uuid.UUID]struct{} - clearedpolicy_bindings bool - done bool - oldValue func(context.Context) (*Group, error) - predicates []predicate.Group + op Op + typ string + id *uuid.UUID + key *string + encrypted_value *string + secret_ref *string + secret_type *secret.SecretType + target *string + scope *string + scope_id *string + description *string + injection_mode *secret.InjectionMode + allow_progeny *bool + version *int + addversion *int + created_by *string + updated_by *string + created *time.Time + updated *time.Time + clearedFields map[string]struct{} + done bool + oldValue func(context.Context) (*Secret, error) + predicates []predicate.Secret } -var _ ent.Mutation = (*GroupMutation)(nil) +var _ ent.Mutation = (*SecretMutation)(nil) -// groupOption allows management of the mutation configuration using functional options. -type groupOption func(*GroupMutation) +// secretOption allows management of the mutation configuration using functional options. +type secretOption func(*SecretMutation) -// newGroupMutation creates new mutation for the Group entity. -func newGroupMutation(c config, op Op, opts ...groupOption) *GroupMutation { - m := &GroupMutation{ +// newSecretMutation creates new mutation for the Secret entity. +func newSecretMutation(c config, op Op, opts ...secretOption) *SecretMutation { + m := &SecretMutation{ config: c, op: op, - typ: TypeGroup, + typ: TypeSecret, clearedFields: make(map[string]struct{}), } for _, opt := range opts { @@ -2744,20 +26506,20 @@ func newGroupMutation(c config, op Op, opts ...groupOption) *GroupMutation { return m } -// withGroupID sets the ID field of the mutation. -func withGroupID(id uuid.UUID) groupOption { - return func(m *GroupMutation) { +// withSecretID sets the ID field of the mutation. +func withSecretID(id uuid.UUID) secretOption { + return func(m *SecretMutation) { var ( err error once sync.Once - value *Group + value *Secret ) - m.oldValue = func(ctx context.Context) (*Group, error) { + m.oldValue = func(ctx context.Context) (*Secret, error) { once.Do(func() { if m.done { err = errors.New("querying old values post mutation is not allowed") } else { - value, err = m.Client().Group.Get(ctx, id) + value, err = m.Client().Secret.Get(ctx, id) } }) return value, err @@ -2766,148 +26528,354 @@ func withGroupID(id uuid.UUID) groupOption { } } -// withGroup sets the old Group of the mutation. -func withGroup(node *Group) groupOption { - return func(m *GroupMutation) { - m.oldValue = func(context.Context) (*Group, error) { +// withSecret sets the old Secret of the mutation. +func withSecret(node *Secret) secretOption { + return func(m *SecretMutation) { + m.oldValue = func(context.Context) (*Secret, error) { return node, nil } m.id = &node.ID } } -// Client returns a new `ent.Client` from the mutation. If the mutation was -// executed in a transaction (ent.Tx), a transactional client is returned. -func (m GroupMutation) Client() *Client { - client := &Client{config: m.config} - client.init() - return client +// Client returns a new `ent.Client` from the mutation. If the mutation was +// executed in a transaction (ent.Tx), a transactional client is returned. +func (m SecretMutation) Client() *Client { + client := &Client{config: m.config} + client.init() + return client +} + +// Tx returns an `ent.Tx` for mutations that were executed in transactions; +// it returns an error otherwise. +func (m SecretMutation) Tx() (*Tx, error) { + if _, ok := m.driver.(*txDriver); !ok { + return nil, errors.New("ent: mutation is not running in a transaction") + } + tx := &Tx{config: m.config} + tx.init() + return tx, nil +} + +// SetID sets the value of the id field. Note that this +// operation is only accepted on creation of Secret entities. +func (m *SecretMutation) SetID(id uuid.UUID) { + m.id = &id +} + +// ID returns the ID value in the mutation. Note that the ID is only available +// if it was provided to the builder or after it was returned from the database. +func (m *SecretMutation) ID() (id uuid.UUID, exists bool) { + if m.id == nil { + return + } + return *m.id, true +} + +// IDs queries the database and returns the entity ids that match the mutation's predicate. +// That means, if the mutation is applied within a transaction with an isolation level such +// as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated +// or updated by the mutation. +func (m *SecretMutation) IDs(ctx context.Context) ([]uuid.UUID, error) { + switch { + case m.op.Is(OpUpdateOne | OpDeleteOne): + id, exists := m.ID() + if exists { + return []uuid.UUID{id}, nil + } + fallthrough + case m.op.Is(OpUpdate | OpDelete): + return m.Client().Secret.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } +} + +// SetKey sets the "key" field. +func (m *SecretMutation) SetKey(s string) { + m.key = &s +} + +// Key returns the value of the "key" field in the mutation. +func (m *SecretMutation) Key() (r string, exists bool) { + v := m.key + if v == nil { + return + } + return *v, true +} + +// OldKey returns the old "key" field's value of the Secret entity. +// If the Secret object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *SecretMutation) OldKey(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldKey is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldKey requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldKey: %w", err) + } + return oldValue.Key, nil +} + +// ResetKey resets all changes to the "key" field. +func (m *SecretMutation) ResetKey() { + m.key = nil +} + +// SetEncryptedValue sets the "encrypted_value" field. +func (m *SecretMutation) SetEncryptedValue(s string) { + m.encrypted_value = &s +} + +// EncryptedValue returns the value of the "encrypted_value" field in the mutation. +func (m *SecretMutation) EncryptedValue() (r string, exists bool) { + v := m.encrypted_value + if v == nil { + return + } + return *v, true +} + +// OldEncryptedValue returns the old "encrypted_value" field's value of the Secret entity. +// If the Secret object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *SecretMutation) OldEncryptedValue(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldEncryptedValue is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldEncryptedValue requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldEncryptedValue: %w", err) + } + return oldValue.EncryptedValue, nil +} + +// ResetEncryptedValue resets all changes to the "encrypted_value" field. +func (m *SecretMutation) ResetEncryptedValue() { + m.encrypted_value = nil +} + +// SetSecretRef sets the "secret_ref" field. +func (m *SecretMutation) SetSecretRef(s string) { + m.secret_ref = &s +} + +// SecretRef returns the value of the "secret_ref" field in the mutation. +func (m *SecretMutation) SecretRef() (r string, exists bool) { + v := m.secret_ref + if v == nil { + return + } + return *v, true +} + +// OldSecretRef returns the old "secret_ref" field's value of the Secret entity. +// If the Secret object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *SecretMutation) OldSecretRef(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldSecretRef is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldSecretRef requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldSecretRef: %w", err) + } + return oldValue.SecretRef, nil +} + +// ClearSecretRef clears the value of the "secret_ref" field. +func (m *SecretMutation) ClearSecretRef() { + m.secret_ref = nil + m.clearedFields[secret.FieldSecretRef] = struct{}{} +} + +// SecretRefCleared returns if the "secret_ref" field was cleared in this mutation. +func (m *SecretMutation) SecretRefCleared() bool { + _, ok := m.clearedFields[secret.FieldSecretRef] + return ok +} + +// ResetSecretRef resets all changes to the "secret_ref" field. +func (m *SecretMutation) ResetSecretRef() { + m.secret_ref = nil + delete(m.clearedFields, secret.FieldSecretRef) +} + +// SetSecretType sets the "secret_type" field. +func (m *SecretMutation) SetSecretType(st secret.SecretType) { + m.secret_type = &st } -// Tx returns an `ent.Tx` for mutations that were executed in transactions; -// it returns an error otherwise. -func (m GroupMutation) Tx() (*Tx, error) { - if _, ok := m.driver.(*txDriver); !ok { - return nil, errors.New("ent: mutation is not running in a transaction") +// SecretType returns the value of the "secret_type" field in the mutation. +func (m *SecretMutation) SecretType() (r secret.SecretType, exists bool) { + v := m.secret_type + if v == nil { + return } - tx := &Tx{config: m.config} - tx.init() - return tx, nil + return *v, true } -// SetID sets the value of the id field. Note that this -// operation is only accepted on creation of Group entities. -func (m *GroupMutation) SetID(id uuid.UUID) { - m.id = &id +// OldSecretType returns the old "secret_type" field's value of the Secret entity. +// If the Secret object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *SecretMutation) OldSecretType(ctx context.Context) (v secret.SecretType, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldSecretType is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldSecretType requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldSecretType: %w", err) + } + return oldValue.SecretType, nil } -// ID returns the ID value in the mutation. Note that the ID is only available -// if it was provided to the builder or after it was returned from the database. -func (m *GroupMutation) ID() (id uuid.UUID, exists bool) { - if m.id == nil { +// ResetSecretType resets all changes to the "secret_type" field. +func (m *SecretMutation) ResetSecretType() { + m.secret_type = nil +} + +// SetTarget sets the "target" field. +func (m *SecretMutation) SetTarget(s string) { + m.target = &s +} + +// Target returns the value of the "target" field in the mutation. +func (m *SecretMutation) Target() (r string, exists bool) { + v := m.target + if v == nil { return } - return *m.id, true + return *v, true } -// IDs queries the database and returns the entity ids that match the mutation's predicate. -// That means, if the mutation is applied within a transaction with an isolation level such -// as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated -// or updated by the mutation. -func (m *GroupMutation) IDs(ctx context.Context) ([]uuid.UUID, error) { - switch { - case m.op.Is(OpUpdateOne | OpDeleteOne): - id, exists := m.ID() - if exists { - return []uuid.UUID{id}, nil - } - fallthrough - case m.op.Is(OpUpdate | OpDelete): - return m.Client().Group.Query().Where(m.predicates...).IDs(ctx) - default: - return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) +// OldTarget returns the old "target" field's value of the Secret entity. +// If the Secret object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *SecretMutation) OldTarget(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldTarget is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldTarget requires an ID field in the mutation") } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldTarget: %w", err) + } + return oldValue.Target, nil } -// SetName sets the "name" field. -func (m *GroupMutation) SetName(s string) { - m.name = &s +// ClearTarget clears the value of the "target" field. +func (m *SecretMutation) ClearTarget() { + m.target = nil + m.clearedFields[secret.FieldTarget] = struct{}{} } -// Name returns the value of the "name" field in the mutation. -func (m *GroupMutation) Name() (r string, exists bool) { - v := m.name +// TargetCleared returns if the "target" field was cleared in this mutation. +func (m *SecretMutation) TargetCleared() bool { + _, ok := m.clearedFields[secret.FieldTarget] + return ok +} + +// ResetTarget resets all changes to the "target" field. +func (m *SecretMutation) ResetTarget() { + m.target = nil + delete(m.clearedFields, secret.FieldTarget) +} + +// SetScope sets the "scope" field. +func (m *SecretMutation) SetScope(s string) { + m.scope = &s +} + +// Scope returns the value of the "scope" field in the mutation. +func (m *SecretMutation) Scope() (r string, exists bool) { + v := m.scope if v == nil { return } return *v, true } -// OldName returns the old "name" field's value of the Group entity. -// If the Group object wasn't provided to the builder, the object is fetched from the database. +// OldScope returns the old "scope" field's value of the Secret entity. +// If the Secret object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *GroupMutation) OldName(ctx context.Context) (v string, err error) { +func (m *SecretMutation) OldScope(ctx context.Context) (v string, err error) { if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldName is only allowed on UpdateOne operations") + return v, errors.New("OldScope is only allowed on UpdateOne operations") } if m.id == nil || m.oldValue == nil { - return v, errors.New("OldName requires an ID field in the mutation") + return v, errors.New("OldScope requires an ID field in the mutation") } oldValue, err := m.oldValue(ctx) if err != nil { - return v, fmt.Errorf("querying old value for OldName: %w", err) + return v, fmt.Errorf("querying old value for OldScope: %w", err) } - return oldValue.Name, nil + return oldValue.Scope, nil } -// ResetName resets all changes to the "name" field. -func (m *GroupMutation) ResetName() { - m.name = nil +// ResetScope resets all changes to the "scope" field. +func (m *SecretMutation) ResetScope() { + m.scope = nil } -// SetSlug sets the "slug" field. -func (m *GroupMutation) SetSlug(s string) { - m.slug = &s +// SetScopeID sets the "scope_id" field. +func (m *SecretMutation) SetScopeID(s string) { + m.scope_id = &s } -// Slug returns the value of the "slug" field in the mutation. -func (m *GroupMutation) Slug() (r string, exists bool) { - v := m.slug +// ScopeID returns the value of the "scope_id" field in the mutation. +func (m *SecretMutation) ScopeID() (r string, exists bool) { + v := m.scope_id if v == nil { return } return *v, true } -// OldSlug returns the old "slug" field's value of the Group entity. -// If the Group object wasn't provided to the builder, the object is fetched from the database. +// OldScopeID returns the old "scope_id" field's value of the Secret entity. +// If the Secret object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *GroupMutation) OldSlug(ctx context.Context) (v string, err error) { +func (m *SecretMutation) OldScopeID(ctx context.Context) (v string, err error) { if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldSlug is only allowed on UpdateOne operations") + return v, errors.New("OldScopeID is only allowed on UpdateOne operations") } if m.id == nil || m.oldValue == nil { - return v, errors.New("OldSlug requires an ID field in the mutation") + return v, errors.New("OldScopeID requires an ID field in the mutation") } oldValue, err := m.oldValue(ctx) if err != nil { - return v, fmt.Errorf("querying old value for OldSlug: %w", err) + return v, fmt.Errorf("querying old value for OldScopeID: %w", err) } - return oldValue.Slug, nil + return oldValue.ScopeID, nil } -// ResetSlug resets all changes to the "slug" field. -func (m *GroupMutation) ResetSlug() { - m.slug = nil +// ResetScopeID resets all changes to the "scope_id" field. +func (m *SecretMutation) ResetScopeID() { + m.scope_id = nil } // SetDescription sets the "description" field. -func (m *GroupMutation) SetDescription(s string) { +func (m *SecretMutation) SetDescription(s string) { m.description = &s } // Description returns the value of the "description" field in the mutation. -func (m *GroupMutation) Description() (r string, exists bool) { +func (m *SecretMutation) Description() (r string, exists bool) { v := m.description if v == nil { return @@ -2915,10 +26883,10 @@ func (m *GroupMutation) Description() (r string, exists bool) { return *v, true } -// OldDescription returns the old "description" field's value of the Group entity. -// If the Group object wasn't provided to the builder, the object is fetched from the database. +// OldDescription returns the old "description" field's value of the Secret entity. +// If the Secret object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *GroupMutation) OldDescription(ctx context.Context) (v string, err error) { +func (m *SecretMutation) OldDescription(ctx context.Context) (v string, err error) { if !m.op.Is(OpUpdateOne) { return v, errors.New("OldDescription is only allowed on UpdateOne operations") } @@ -2933,213 +26901,256 @@ func (m *GroupMutation) OldDescription(ctx context.Context) (v string, err error } // ClearDescription clears the value of the "description" field. -func (m *GroupMutation) ClearDescription() { +func (m *SecretMutation) ClearDescription() { m.description = nil - m.clearedFields[group.FieldDescription] = struct{}{} + m.clearedFields[secret.FieldDescription] = struct{}{} } // DescriptionCleared returns if the "description" field was cleared in this mutation. -func (m *GroupMutation) DescriptionCleared() bool { - _, ok := m.clearedFields[group.FieldDescription] +func (m *SecretMutation) DescriptionCleared() bool { + _, ok := m.clearedFields[secret.FieldDescription] return ok } // ResetDescription resets all changes to the "description" field. -func (m *GroupMutation) ResetDescription() { +func (m *SecretMutation) ResetDescription() { m.description = nil - delete(m.clearedFields, group.FieldDescription) + delete(m.clearedFields, secret.FieldDescription) } -// SetGroupType sets the "group_type" field. -func (m *GroupMutation) SetGroupType(gt group.GroupType) { - m.group_type = > +// SetInjectionMode sets the "injection_mode" field. +func (m *SecretMutation) SetInjectionMode(sm secret.InjectionMode) { + m.injection_mode = &sm } -// GroupType returns the value of the "group_type" field in the mutation. -func (m *GroupMutation) GroupType() (r group.GroupType, exists bool) { - v := m.group_type +// InjectionMode returns the value of the "injection_mode" field in the mutation. +func (m *SecretMutation) InjectionMode() (r secret.InjectionMode, exists bool) { + v := m.injection_mode if v == nil { return } return *v, true } -// OldGroupType returns the old "group_type" field's value of the Group entity. -// If the Group object wasn't provided to the builder, the object is fetched from the database. +// OldInjectionMode returns the old "injection_mode" field's value of the Secret entity. +// If the Secret object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *GroupMutation) OldGroupType(ctx context.Context) (v group.GroupType, err error) { +func (m *SecretMutation) OldInjectionMode(ctx context.Context) (v secret.InjectionMode, err error) { if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldGroupType is only allowed on UpdateOne operations") + return v, errors.New("OldInjectionMode is only allowed on UpdateOne operations") } if m.id == nil || m.oldValue == nil { - return v, errors.New("OldGroupType requires an ID field in the mutation") + return v, errors.New("OldInjectionMode requires an ID field in the mutation") } oldValue, err := m.oldValue(ctx) if err != nil { - return v, fmt.Errorf("querying old value for OldGroupType: %w", err) + return v, fmt.Errorf("querying old value for OldInjectionMode: %w", err) } - return oldValue.GroupType, nil + return oldValue.InjectionMode, nil } -// ResetGroupType resets all changes to the "group_type" field. -func (m *GroupMutation) ResetGroupType() { - m.group_type = nil +// ResetInjectionMode resets all changes to the "injection_mode" field. +func (m *SecretMutation) ResetInjectionMode() { + m.injection_mode = nil } -// SetProjectID sets the "project_id" field. -func (m *GroupMutation) SetProjectID(u uuid.UUID) { - m.project_id = &u +// SetAllowProgeny sets the "allow_progeny" field. +func (m *SecretMutation) SetAllowProgeny(b bool) { + m.allow_progeny = &b } -// ProjectID returns the value of the "project_id" field in the mutation. -func (m *GroupMutation) ProjectID() (r uuid.UUID, exists bool) { - v := m.project_id +// AllowProgeny returns the value of the "allow_progeny" field in the mutation. +func (m *SecretMutation) AllowProgeny() (r bool, exists bool) { + v := m.allow_progeny if v == nil { return } return *v, true } -// OldProjectID returns the old "project_id" field's value of the Group entity. -// If the Group object wasn't provided to the builder, the object is fetched from the database. +// OldAllowProgeny returns the old "allow_progeny" field's value of the Secret entity. +// If the Secret object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *GroupMutation) OldProjectID(ctx context.Context) (v *uuid.UUID, err error) { +func (m *SecretMutation) OldAllowProgeny(ctx context.Context) (v bool, err error) { if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldProjectID is only allowed on UpdateOne operations") + return v, errors.New("OldAllowProgeny is only allowed on UpdateOne operations") } if m.id == nil || m.oldValue == nil { - return v, errors.New("OldProjectID requires an ID field in the mutation") + return v, errors.New("OldAllowProgeny requires an ID field in the mutation") } oldValue, err := m.oldValue(ctx) if err != nil { - return v, fmt.Errorf("querying old value for OldProjectID: %w", err) + return v, fmt.Errorf("querying old value for OldAllowProgeny: %w", err) } - return oldValue.ProjectID, nil + return oldValue.AllowProgeny, nil } -// ClearProjectID clears the value of the "project_id" field. -func (m *GroupMutation) ClearProjectID() { - m.project_id = nil - m.clearedFields[group.FieldProjectID] = struct{}{} +// ResetAllowProgeny resets all changes to the "allow_progeny" field. +func (m *SecretMutation) ResetAllowProgeny() { + m.allow_progeny = nil } -// ProjectIDCleared returns if the "project_id" field was cleared in this mutation. -func (m *GroupMutation) ProjectIDCleared() bool { - _, ok := m.clearedFields[group.FieldProjectID] - return ok +// SetVersion sets the "version" field. +func (m *SecretMutation) SetVersion(i int) { + m.version = &i + m.addversion = nil } -// ResetProjectID resets all changes to the "project_id" field. -func (m *GroupMutation) ResetProjectID() { - m.project_id = nil - delete(m.clearedFields, group.FieldProjectID) +// Version returns the value of the "version" field in the mutation. +func (m *SecretMutation) Version() (r int, exists bool) { + v := m.version + if v == nil { + return + } + return *v, true } -// SetLabels sets the "labels" field. -func (m *GroupMutation) SetLabels(value map[string]string) { - m.labels = &value +// OldVersion returns the old "version" field's value of the Secret entity. +// If the Secret object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *SecretMutation) OldVersion(ctx context.Context) (v int, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldVersion is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldVersion requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldVersion: %w", err) + } + return oldValue.Version, nil } -// Labels returns the value of the "labels" field in the mutation. -func (m *GroupMutation) Labels() (r map[string]string, exists bool) { - v := m.labels +// AddVersion adds i to the "version" field. +func (m *SecretMutation) AddVersion(i int) { + if m.addversion != nil { + *m.addversion += i + } else { + m.addversion = &i + } +} + +// AddedVersion returns the value that was added to the "version" field in this mutation. +func (m *SecretMutation) AddedVersion() (r int, exists bool) { + v := m.addversion if v == nil { return } return *v, true } -// OldLabels returns the old "labels" field's value of the Group entity. -// If the Group object wasn't provided to the builder, the object is fetched from the database. +// ResetVersion resets all changes to the "version" field. +func (m *SecretMutation) ResetVersion() { + m.version = nil + m.addversion = nil +} + +// SetCreatedBy sets the "created_by" field. +func (m *SecretMutation) SetCreatedBy(s string) { + m.created_by = &s +} + +// CreatedBy returns the value of the "created_by" field in the mutation. +func (m *SecretMutation) CreatedBy() (r string, exists bool) { + v := m.created_by + if v == nil { + return + } + return *v, true +} + +// OldCreatedBy returns the old "created_by" field's value of the Secret entity. +// If the Secret object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *GroupMutation) OldLabels(ctx context.Context) (v map[string]string, err error) { +func (m *SecretMutation) OldCreatedBy(ctx context.Context) (v string, err error) { if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldLabels is only allowed on UpdateOne operations") + return v, errors.New("OldCreatedBy is only allowed on UpdateOne operations") } if m.id == nil || m.oldValue == nil { - return v, errors.New("OldLabels requires an ID field in the mutation") + return v, errors.New("OldCreatedBy requires an ID field in the mutation") } oldValue, err := m.oldValue(ctx) if err != nil { - return v, fmt.Errorf("querying old value for OldLabels: %w", err) + return v, fmt.Errorf("querying old value for OldCreatedBy: %w", err) } - return oldValue.Labels, nil + return oldValue.CreatedBy, nil } -// ClearLabels clears the value of the "labels" field. -func (m *GroupMutation) ClearLabels() { - m.labels = nil - m.clearedFields[group.FieldLabels] = struct{}{} +// ClearCreatedBy clears the value of the "created_by" field. +func (m *SecretMutation) ClearCreatedBy() { + m.created_by = nil + m.clearedFields[secret.FieldCreatedBy] = struct{}{} } -// LabelsCleared returns if the "labels" field was cleared in this mutation. -func (m *GroupMutation) LabelsCleared() bool { - _, ok := m.clearedFields[group.FieldLabels] +// CreatedByCleared returns if the "created_by" field was cleared in this mutation. +func (m *SecretMutation) CreatedByCleared() bool { + _, ok := m.clearedFields[secret.FieldCreatedBy] return ok } -// ResetLabels resets all changes to the "labels" field. -func (m *GroupMutation) ResetLabels() { - m.labels = nil - delete(m.clearedFields, group.FieldLabels) +// ResetCreatedBy resets all changes to the "created_by" field. +func (m *SecretMutation) ResetCreatedBy() { + m.created_by = nil + delete(m.clearedFields, secret.FieldCreatedBy) } -// SetAnnotations sets the "annotations" field. -func (m *GroupMutation) SetAnnotations(value map[string]string) { - m.annotations = &value +// SetUpdatedBy sets the "updated_by" field. +func (m *SecretMutation) SetUpdatedBy(s string) { + m.updated_by = &s } -// Annotations returns the value of the "annotations" field in the mutation. -func (m *GroupMutation) Annotations() (r map[string]string, exists bool) { - v := m.annotations +// UpdatedBy returns the value of the "updated_by" field in the mutation. +func (m *SecretMutation) UpdatedBy() (r string, exists bool) { + v := m.updated_by if v == nil { return } return *v, true } -// OldAnnotations returns the old "annotations" field's value of the Group entity. -// If the Group object wasn't provided to the builder, the object is fetched from the database. +// OldUpdatedBy returns the old "updated_by" field's value of the Secret entity. +// If the Secret object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *GroupMutation) OldAnnotations(ctx context.Context) (v map[string]string, err error) { +func (m *SecretMutation) OldUpdatedBy(ctx context.Context) (v string, err error) { if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldAnnotations is only allowed on UpdateOne operations") + return v, errors.New("OldUpdatedBy is only allowed on UpdateOne operations") } if m.id == nil || m.oldValue == nil { - return v, errors.New("OldAnnotations requires an ID field in the mutation") + return v, errors.New("OldUpdatedBy requires an ID field in the mutation") } oldValue, err := m.oldValue(ctx) if err != nil { - return v, fmt.Errorf("querying old value for OldAnnotations: %w", err) + return v, fmt.Errorf("querying old value for OldUpdatedBy: %w", err) } - return oldValue.Annotations, nil + return oldValue.UpdatedBy, nil } -// ClearAnnotations clears the value of the "annotations" field. -func (m *GroupMutation) ClearAnnotations() { - m.annotations = nil - m.clearedFields[group.FieldAnnotations] = struct{}{} +// ClearUpdatedBy clears the value of the "updated_by" field. +func (m *SecretMutation) ClearUpdatedBy() { + m.updated_by = nil + m.clearedFields[secret.FieldUpdatedBy] = struct{}{} } -// AnnotationsCleared returns if the "annotations" field was cleared in this mutation. -func (m *GroupMutation) AnnotationsCleared() bool { - _, ok := m.clearedFields[group.FieldAnnotations] +// UpdatedByCleared returns if the "updated_by" field was cleared in this mutation. +func (m *SecretMutation) UpdatedByCleared() bool { + _, ok := m.clearedFields[secret.FieldUpdatedBy] return ok } -// ResetAnnotations resets all changes to the "annotations" field. -func (m *GroupMutation) ResetAnnotations() { - m.annotations = nil - delete(m.clearedFields, group.FieldAnnotations) +// ResetUpdatedBy resets all changes to the "updated_by" field. +func (m *SecretMutation) ResetUpdatedBy() { + m.updated_by = nil + delete(m.clearedFields, secret.FieldUpdatedBy) } // SetCreated sets the "created" field. -func (m *GroupMutation) SetCreated(t time.Time) { +func (m *SecretMutation) SetCreated(t time.Time) { m.created = &t } // Created returns the value of the "created" field in the mutation. -func (m *GroupMutation) Created() (r time.Time, exists bool) { +func (m *SecretMutation) Created() (r time.Time, exists bool) { v := m.created if v == nil { return @@ -3147,10 +27158,10 @@ func (m *GroupMutation) Created() (r time.Time, exists bool) { return *v, true } -// OldCreated returns the old "created" field's value of the Group entity. -// If the Group object wasn't provided to the builder, the object is fetched from the database. +// OldCreated returns the old "created" field's value of the Secret entity. +// If the Secret object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *GroupMutation) OldCreated(ctx context.Context) (v time.Time, err error) { +func (m *SecretMutation) OldCreated(ctx context.Context) (v time.Time, err error) { if !m.op.Is(OpUpdateOne) { return v, errors.New("OldCreated is only allowed on UpdateOne operations") } @@ -3165,17 +27176,17 @@ func (m *GroupMutation) OldCreated(ctx context.Context) (v time.Time, err error) } // ResetCreated resets all changes to the "created" field. -func (m *GroupMutation) ResetCreated() { +func (m *SecretMutation) ResetCreated() { m.created = nil } // SetUpdated sets the "updated" field. -func (m *GroupMutation) SetUpdated(t time.Time) { +func (m *SecretMutation) SetUpdated(t time.Time) { m.updated = &t } // Updated returns the value of the "updated" field in the mutation. -func (m *GroupMutation) Updated() (r time.Time, exists bool) { +func (m *SecretMutation) Updated() (r time.Time, exists bool) { v := m.updated if v == nil { return @@ -3183,10 +27194,10 @@ func (m *GroupMutation) Updated() (r time.Time, exists bool) { return *v, true } -// OldUpdated returns the old "updated" field's value of the Group entity. -// If the Group object wasn't provided to the builder, the object is fetched from the database. +// OldUpdated returns the old "updated" field's value of the Secret entity. +// If the Secret object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *GroupMutation) OldUpdated(ctx context.Context) (v time.Time, err error) { +func (m *SecretMutation) OldUpdated(ctx context.Context) (v time.Time, err error) { if !m.op.Is(OpUpdateOne) { return v, errors.New("OldUpdated is only allowed on UpdateOne operations") } @@ -3201,360 +27212,798 @@ func (m *GroupMutation) OldUpdated(ctx context.Context) (v time.Time, err error) } // ResetUpdated resets all changes to the "updated" field. -func (m *GroupMutation) ResetUpdated() { +func (m *SecretMutation) ResetUpdated() { m.updated = nil } -// SetCreatedBy sets the "created_by" field. -func (m *GroupMutation) SetCreatedBy(s string) { - m.created_by = &s +// Where appends a list predicates to the SecretMutation builder. +func (m *SecretMutation) Where(ps ...predicate.Secret) { + m.predicates = append(m.predicates, ps...) } -// CreatedBy returns the value of the "created_by" field in the mutation. -func (m *GroupMutation) CreatedBy() (r string, exists bool) { - v := m.created_by - if v == nil { - return +// WhereP appends storage-level predicates to the SecretMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *SecretMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.Secret, len(ps)) + for i := range ps { + p[i] = ps[i] } - return *v, true + m.Where(p...) } -// OldCreatedBy returns the old "created_by" field's value of the Group entity. -// If the Group object wasn't provided to the builder, the object is fetched from the database. -// An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *GroupMutation) OldCreatedBy(ctx context.Context) (v string, err error) { - if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldCreatedBy is only allowed on UpdateOne operations") +// Op returns the operation name. +func (m *SecretMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *SecretMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (Secret). +func (m *SecretMutation) Type() string { + return m.typ +} + +// Fields returns all fields that were changed during this mutation. Note that in +// order to get all numeric fields that were incremented/decremented, call +// AddedFields(). +func (m *SecretMutation) Fields() []string { + fields := make([]string, 0, 15) + if m.key != nil { + fields = append(fields, secret.FieldKey) } - if m.id == nil || m.oldValue == nil { - return v, errors.New("OldCreatedBy requires an ID field in the mutation") + if m.encrypted_value != nil { + fields = append(fields, secret.FieldEncryptedValue) } - oldValue, err := m.oldValue(ctx) - if err != nil { - return v, fmt.Errorf("querying old value for OldCreatedBy: %w", err) + if m.secret_ref != nil { + fields = append(fields, secret.FieldSecretRef) } - return oldValue.CreatedBy, nil + if m.secret_type != nil { + fields = append(fields, secret.FieldSecretType) + } + if m.target != nil { + fields = append(fields, secret.FieldTarget) + } + if m.scope != nil { + fields = append(fields, secret.FieldScope) + } + if m.scope_id != nil { + fields = append(fields, secret.FieldScopeID) + } + if m.description != nil { + fields = append(fields, secret.FieldDescription) + } + if m.injection_mode != nil { + fields = append(fields, secret.FieldInjectionMode) + } + if m.allow_progeny != nil { + fields = append(fields, secret.FieldAllowProgeny) + } + if m.version != nil { + fields = append(fields, secret.FieldVersion) + } + if m.created_by != nil { + fields = append(fields, secret.FieldCreatedBy) + } + if m.updated_by != nil { + fields = append(fields, secret.FieldUpdatedBy) + } + if m.created != nil { + fields = append(fields, secret.FieldCreated) + } + if m.updated != nil { + fields = append(fields, secret.FieldUpdated) + } + return fields } -// ClearCreatedBy clears the value of the "created_by" field. -func (m *GroupMutation) ClearCreatedBy() { - m.created_by = nil - m.clearedFields[group.FieldCreatedBy] = struct{}{} +// Field returns the value of a field with the given name. The second boolean +// return value indicates that this field was not set, or was not defined in the +// schema. +func (m *SecretMutation) Field(name string) (ent.Value, bool) { + switch name { + case secret.FieldKey: + return m.Key() + case secret.FieldEncryptedValue: + return m.EncryptedValue() + case secret.FieldSecretRef: + return m.SecretRef() + case secret.FieldSecretType: + return m.SecretType() + case secret.FieldTarget: + return m.Target() + case secret.FieldScope: + return m.Scope() + case secret.FieldScopeID: + return m.ScopeID() + case secret.FieldDescription: + return m.Description() + case secret.FieldInjectionMode: + return m.InjectionMode() + case secret.FieldAllowProgeny: + return m.AllowProgeny() + case secret.FieldVersion: + return m.Version() + case secret.FieldCreatedBy: + return m.CreatedBy() + case secret.FieldUpdatedBy: + return m.UpdatedBy() + case secret.FieldCreated: + return m.Created() + case secret.FieldUpdated: + return m.Updated() + } + return nil, false +} + +// OldField returns the old value of the field from the database. An error is +// returned if the mutation operation is not UpdateOne, or the query to the +// database failed. +func (m *SecretMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + switch name { + case secret.FieldKey: + return m.OldKey(ctx) + case secret.FieldEncryptedValue: + return m.OldEncryptedValue(ctx) + case secret.FieldSecretRef: + return m.OldSecretRef(ctx) + case secret.FieldSecretType: + return m.OldSecretType(ctx) + case secret.FieldTarget: + return m.OldTarget(ctx) + case secret.FieldScope: + return m.OldScope(ctx) + case secret.FieldScopeID: + return m.OldScopeID(ctx) + case secret.FieldDescription: + return m.OldDescription(ctx) + case secret.FieldInjectionMode: + return m.OldInjectionMode(ctx) + case secret.FieldAllowProgeny: + return m.OldAllowProgeny(ctx) + case secret.FieldVersion: + return m.OldVersion(ctx) + case secret.FieldCreatedBy: + return m.OldCreatedBy(ctx) + case secret.FieldUpdatedBy: + return m.OldUpdatedBy(ctx) + case secret.FieldCreated: + return m.OldCreated(ctx) + case secret.FieldUpdated: + return m.OldUpdated(ctx) + } + return nil, fmt.Errorf("unknown Secret field %s", name) +} + +// SetField sets the value of a field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *SecretMutation) SetField(name string, value ent.Value) error { + switch name { + case secret.FieldKey: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetKey(v) + return nil + case secret.FieldEncryptedValue: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetEncryptedValue(v) + return nil + case secret.FieldSecretRef: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetSecretRef(v) + return nil + case secret.FieldSecretType: + v, ok := value.(secret.SecretType) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetSecretType(v) + return nil + case secret.FieldTarget: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetTarget(v) + return nil + case secret.FieldScope: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetScope(v) + return nil + case secret.FieldScopeID: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetScopeID(v) + return nil + case secret.FieldDescription: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetDescription(v) + return nil + case secret.FieldInjectionMode: + v, ok := value.(secret.InjectionMode) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetInjectionMode(v) + return nil + case secret.FieldAllowProgeny: + v, ok := value.(bool) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetAllowProgeny(v) + return nil + case secret.FieldVersion: + v, ok := value.(int) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetVersion(v) + return nil + case secret.FieldCreatedBy: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreatedBy(v) + return nil + case secret.FieldUpdatedBy: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetUpdatedBy(v) + return nil + case secret.FieldCreated: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreated(v) + return nil + case secret.FieldUpdated: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetUpdated(v) + return nil + } + return fmt.Errorf("unknown Secret field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *SecretMutation) AddedFields() []string { + var fields []string + if m.addversion != nil { + fields = append(fields, secret.FieldVersion) + } + return fields +} + +// AddedField returns the numeric value that was incremented/decremented on a field +// with the given name. The second boolean return value indicates that this field +// was not set, or was not defined in the schema. +func (m *SecretMutation) AddedField(name string) (ent.Value, bool) { + switch name { + case secret.FieldVersion: + return m.AddedVersion() + } + return nil, false +} + +// AddField adds the value to the field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *SecretMutation) AddField(name string, value ent.Value) error { + switch name { + case secret.FieldVersion: + v, ok := value.(int) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.AddVersion(v) + return nil + } + return fmt.Errorf("unknown Secret numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *SecretMutation) ClearedFields() []string { + var fields []string + if m.FieldCleared(secret.FieldSecretRef) { + fields = append(fields, secret.FieldSecretRef) + } + if m.FieldCleared(secret.FieldTarget) { + fields = append(fields, secret.FieldTarget) + } + if m.FieldCleared(secret.FieldDescription) { + fields = append(fields, secret.FieldDescription) + } + if m.FieldCleared(secret.FieldCreatedBy) { + fields = append(fields, secret.FieldCreatedBy) + } + if m.FieldCleared(secret.FieldUpdatedBy) { + fields = append(fields, secret.FieldUpdatedBy) + } + return fields +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *SecretMutation) FieldCleared(name string) bool { + _, ok := m.clearedFields[name] + return ok +} + +// ClearField clears the value of the field with the given name. It returns an +// error if the field is not defined in the schema. +func (m *SecretMutation) ClearField(name string) error { + switch name { + case secret.FieldSecretRef: + m.ClearSecretRef() + return nil + case secret.FieldTarget: + m.ClearTarget() + return nil + case secret.FieldDescription: + m.ClearDescription() + return nil + case secret.FieldCreatedBy: + m.ClearCreatedBy() + return nil + case secret.FieldUpdatedBy: + m.ClearUpdatedBy() + return nil + } + return fmt.Errorf("unknown Secret nullable field %s", name) +} + +// ResetField resets all changes in the mutation for the field with the given name. +// It returns an error if the field is not defined in the schema. +func (m *SecretMutation) ResetField(name string) error { + switch name { + case secret.FieldKey: + m.ResetKey() + return nil + case secret.FieldEncryptedValue: + m.ResetEncryptedValue() + return nil + case secret.FieldSecretRef: + m.ResetSecretRef() + return nil + case secret.FieldSecretType: + m.ResetSecretType() + return nil + case secret.FieldTarget: + m.ResetTarget() + return nil + case secret.FieldScope: + m.ResetScope() + return nil + case secret.FieldScopeID: + m.ResetScopeID() + return nil + case secret.FieldDescription: + m.ResetDescription() + return nil + case secret.FieldInjectionMode: + m.ResetInjectionMode() + return nil + case secret.FieldAllowProgeny: + m.ResetAllowProgeny() + return nil + case secret.FieldVersion: + m.ResetVersion() + return nil + case secret.FieldCreatedBy: + m.ResetCreatedBy() + return nil + case secret.FieldUpdatedBy: + m.ResetUpdatedBy() + return nil + case secret.FieldCreated: + m.ResetCreated() + return nil + case secret.FieldUpdated: + m.ResetUpdated() + return nil + } + return fmt.Errorf("unknown Secret field %s", name) } -// CreatedByCleared returns if the "created_by" field was cleared in this mutation. -func (m *GroupMutation) CreatedByCleared() bool { - _, ok := m.clearedFields[group.FieldCreatedBy] - return ok +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *SecretMutation) AddedEdges() []string { + edges := make([]string, 0, 0) + return edges } -// ResetCreatedBy resets all changes to the "created_by" field. -func (m *GroupMutation) ResetCreatedBy() { - m.created_by = nil - delete(m.clearedFields, group.FieldCreatedBy) +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *SecretMutation) AddedIDs(name string) []ent.Value { + return nil } -// SetOwnerID sets the "owner_id" field. -func (m *GroupMutation) SetOwnerID(u uuid.UUID) { - m.owner = &u +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *SecretMutation) RemovedEdges() []string { + edges := make([]string, 0, 0) + return edges } -// OwnerID returns the value of the "owner_id" field in the mutation. -func (m *GroupMutation) OwnerID() (r uuid.UUID, exists bool) { - v := m.owner - if v == nil { - return - } - return *v, true +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *SecretMutation) RemovedIDs(name string) []ent.Value { + return nil } -// OldOwnerID returns the old "owner_id" field's value of the Group entity. -// If the Group object wasn't provided to the builder, the object is fetched from the database. -// An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *GroupMutation) OldOwnerID(ctx context.Context) (v *uuid.UUID, err error) { - if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldOwnerID is only allowed on UpdateOne operations") - } - if m.id == nil || m.oldValue == nil { - return v, errors.New("OldOwnerID requires an ID field in the mutation") - } - oldValue, err := m.oldValue(ctx) - if err != nil { - return v, fmt.Errorf("querying old value for OldOwnerID: %w", err) - } - return oldValue.OwnerID, nil +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *SecretMutation) ClearedEdges() []string { + edges := make([]string, 0, 0) + return edges } -// ClearOwnerID clears the value of the "owner_id" field. -func (m *GroupMutation) ClearOwnerID() { - m.owner = nil - m.clearedFields[group.FieldOwnerID] = struct{}{} +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *SecretMutation) EdgeCleared(name string) bool { + return false } -// OwnerIDCleared returns if the "owner_id" field was cleared in this mutation. -func (m *GroupMutation) OwnerIDCleared() bool { - _, ok := m.clearedFields[group.FieldOwnerID] - return ok +// ClearEdge clears the value of the edge with the given name. It returns an error +// if that edge is not defined in the schema. +func (m *SecretMutation) ClearEdge(name string) error { + return fmt.Errorf("unknown Secret unique edge %s", name) } -// ResetOwnerID resets all changes to the "owner_id" field. -func (m *GroupMutation) ResetOwnerID() { - m.owner = nil - delete(m.clearedFields, group.FieldOwnerID) +// ResetEdge resets all changes to the edge with the given name in this mutation. +// It returns an error if the edge is not defined in the schema. +func (m *SecretMutation) ResetEdge(name string) error { + return fmt.Errorf("unknown Secret edge %s", name) } -// AddMembershipIDs adds the "memberships" edge to the GroupMembership entity by ids. -func (m *GroupMutation) AddMembershipIDs(ids ...uuid.UUID) { - if m.memberships == nil { - m.memberships = make(map[uuid.UUID]struct{}) +// SubscriptionTemplateMutation represents an operation that mutates the SubscriptionTemplate nodes in the graph. +type SubscriptionTemplateMutation struct { + config + op Op + typ string + id *uuid.UUID + name *string + scope *string + trigger_activities *string + project_id *uuid.UUID + created_by *string + clearedFields map[string]struct{} + done bool + oldValue func(context.Context) (*SubscriptionTemplate, error) + predicates []predicate.SubscriptionTemplate +} + +var _ ent.Mutation = (*SubscriptionTemplateMutation)(nil) + +// subscriptiontemplateOption allows management of the mutation configuration using functional options. +type subscriptiontemplateOption func(*SubscriptionTemplateMutation) + +// newSubscriptionTemplateMutation creates new mutation for the SubscriptionTemplate entity. +func newSubscriptionTemplateMutation(c config, op Op, opts ...subscriptiontemplateOption) *SubscriptionTemplateMutation { + m := &SubscriptionTemplateMutation{ + config: c, + op: op, + typ: TypeSubscriptionTemplate, + clearedFields: make(map[string]struct{}), } - for i := range ids { - m.memberships[ids[i]] = struct{}{} + for _, opt := range opts { + opt(m) } + return m } -// ClearMemberships clears the "memberships" edge to the GroupMembership entity. -func (m *GroupMutation) ClearMemberships() { - m.clearedmemberships = true -} - -// MembershipsCleared reports if the "memberships" edge to the GroupMembership entity was cleared. -func (m *GroupMutation) MembershipsCleared() bool { - return m.clearedmemberships +// withSubscriptionTemplateID sets the ID field of the mutation. +func withSubscriptionTemplateID(id uuid.UUID) subscriptiontemplateOption { + return func(m *SubscriptionTemplateMutation) { + var ( + err error + once sync.Once + value *SubscriptionTemplate + ) + m.oldValue = func(ctx context.Context) (*SubscriptionTemplate, error) { + once.Do(func() { + if m.done { + err = errors.New("querying old values post mutation is not allowed") + } else { + value, err = m.Client().SubscriptionTemplate.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } } -// RemoveMembershipIDs removes the "memberships" edge to the GroupMembership entity by IDs. -func (m *GroupMutation) RemoveMembershipIDs(ids ...uuid.UUID) { - if m.removedmemberships == nil { - m.removedmemberships = make(map[uuid.UUID]struct{}) - } - for i := range ids { - delete(m.memberships, ids[i]) - m.removedmemberships[ids[i]] = struct{}{} +// withSubscriptionTemplate sets the old SubscriptionTemplate of the mutation. +func withSubscriptionTemplate(node *SubscriptionTemplate) subscriptiontemplateOption { + return func(m *SubscriptionTemplateMutation) { + m.oldValue = func(context.Context) (*SubscriptionTemplate, error) { + return node, nil + } + m.id = &node.ID } } -// RemovedMemberships returns the removed IDs of the "memberships" edge to the GroupMembership entity. -func (m *GroupMutation) RemovedMembershipsIDs() (ids []uuid.UUID) { - for id := range m.removedmemberships { - ids = append(ids, id) - } - return +// Client returns a new `ent.Client` from the mutation. If the mutation was +// executed in a transaction (ent.Tx), a transactional client is returned. +func (m SubscriptionTemplateMutation) Client() *Client { + client := &Client{config: m.config} + client.init() + return client } -// MembershipsIDs returns the "memberships" edge IDs in the mutation. -func (m *GroupMutation) MembershipsIDs() (ids []uuid.UUID) { - for id := range m.memberships { - ids = append(ids, id) +// Tx returns an `ent.Tx` for mutations that were executed in transactions; +// it returns an error otherwise. +func (m SubscriptionTemplateMutation) Tx() (*Tx, error) { + if _, ok := m.driver.(*txDriver); !ok { + return nil, errors.New("ent: mutation is not running in a transaction") } - return + tx := &Tx{config: m.config} + tx.init() + return tx, nil } -// ResetMemberships resets all changes to the "memberships" edge. -func (m *GroupMutation) ResetMemberships() { - m.memberships = nil - m.clearedmemberships = false - m.removedmemberships = nil +// SetID sets the value of the id field. Note that this +// operation is only accepted on creation of SubscriptionTemplate entities. +func (m *SubscriptionTemplateMutation) SetID(id uuid.UUID) { + m.id = &id } -// AddParentGroupIDs adds the "parent_groups" edge to the Group entity by ids. -func (m *GroupMutation) AddParentGroupIDs(ids ...uuid.UUID) { - if m.parent_groups == nil { - m.parent_groups = make(map[uuid.UUID]struct{}) - } - for i := range ids { - m.parent_groups[ids[i]] = struct{}{} +// ID returns the ID value in the mutation. Note that the ID is only available +// if it was provided to the builder or after it was returned from the database. +func (m *SubscriptionTemplateMutation) ID() (id uuid.UUID, exists bool) { + if m.id == nil { + return } + return *m.id, true } -// ClearParentGroups clears the "parent_groups" edge to the Group entity. -func (m *GroupMutation) ClearParentGroups() { - m.clearedparent_groups = true +// IDs queries the database and returns the entity ids that match the mutation's predicate. +// That means, if the mutation is applied within a transaction with an isolation level such +// as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated +// or updated by the mutation. +func (m *SubscriptionTemplateMutation) IDs(ctx context.Context) ([]uuid.UUID, error) { + switch { + case m.op.Is(OpUpdateOne | OpDeleteOne): + id, exists := m.ID() + if exists { + return []uuid.UUID{id}, nil + } + fallthrough + case m.op.Is(OpUpdate | OpDelete): + return m.Client().SubscriptionTemplate.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } } -// ParentGroupsCleared reports if the "parent_groups" edge to the Group entity was cleared. -func (m *GroupMutation) ParentGroupsCleared() bool { - return m.clearedparent_groups +// SetName sets the "name" field. +func (m *SubscriptionTemplateMutation) SetName(s string) { + m.name = &s } -// RemoveParentGroupIDs removes the "parent_groups" edge to the Group entity by IDs. -func (m *GroupMutation) RemoveParentGroupIDs(ids ...uuid.UUID) { - if m.removedparent_groups == nil { - m.removedparent_groups = make(map[uuid.UUID]struct{}) - } - for i := range ids { - delete(m.parent_groups, ids[i]) - m.removedparent_groups[ids[i]] = struct{}{} +// Name returns the value of the "name" field in the mutation. +func (m *SubscriptionTemplateMutation) Name() (r string, exists bool) { + v := m.name + if v == nil { + return } + return *v, true } -// RemovedParentGroups returns the removed IDs of the "parent_groups" edge to the Group entity. -func (m *GroupMutation) RemovedParentGroupsIDs() (ids []uuid.UUID) { - for id := range m.removedparent_groups { - ids = append(ids, id) +// OldName returns the old "name" field's value of the SubscriptionTemplate entity. +// If the SubscriptionTemplate object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *SubscriptionTemplateMutation) OldName(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldName is only allowed on UpdateOne operations") } - return -} - -// ParentGroupsIDs returns the "parent_groups" edge IDs in the mutation. -func (m *GroupMutation) ParentGroupsIDs() (ids []uuid.UUID) { - for id := range m.parent_groups { - ids = append(ids, id) + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldName requires an ID field in the mutation") } - return -} - -// ResetParentGroups resets all changes to the "parent_groups" edge. -func (m *GroupMutation) ResetParentGroups() { - m.parent_groups = nil - m.clearedparent_groups = false - m.removedparent_groups = nil + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldName: %w", err) + } + return oldValue.Name, nil } -// AddChildGroupIDs adds the "child_groups" edge to the Group entity by ids. -func (m *GroupMutation) AddChildGroupIDs(ids ...uuid.UUID) { - if m.child_groups == nil { - m.child_groups = make(map[uuid.UUID]struct{}) - } - for i := range ids { - m.child_groups[ids[i]] = struct{}{} - } +// ResetName resets all changes to the "name" field. +func (m *SubscriptionTemplateMutation) ResetName() { + m.name = nil } -// ClearChildGroups clears the "child_groups" edge to the Group entity. -func (m *GroupMutation) ClearChildGroups() { - m.clearedchild_groups = true +// SetScope sets the "scope" field. +func (m *SubscriptionTemplateMutation) SetScope(s string) { + m.scope = &s } -// ChildGroupsCleared reports if the "child_groups" edge to the Group entity was cleared. -func (m *GroupMutation) ChildGroupsCleared() bool { - return m.clearedchild_groups +// Scope returns the value of the "scope" field in the mutation. +func (m *SubscriptionTemplateMutation) Scope() (r string, exists bool) { + v := m.scope + if v == nil { + return + } + return *v, true } -// RemoveChildGroupIDs removes the "child_groups" edge to the Group entity by IDs. -func (m *GroupMutation) RemoveChildGroupIDs(ids ...uuid.UUID) { - if m.removedchild_groups == nil { - m.removedchild_groups = make(map[uuid.UUID]struct{}) +// OldScope returns the old "scope" field's value of the SubscriptionTemplate entity. +// If the SubscriptionTemplate object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *SubscriptionTemplateMutation) OldScope(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldScope is only allowed on UpdateOne operations") } - for i := range ids { - delete(m.child_groups, ids[i]) - m.removedchild_groups[ids[i]] = struct{}{} + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldScope requires an ID field in the mutation") } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldScope: %w", err) + } + return oldValue.Scope, nil +} + +// ResetScope resets all changes to the "scope" field. +func (m *SubscriptionTemplateMutation) ResetScope() { + m.scope = nil } -// RemovedChildGroups returns the removed IDs of the "child_groups" edge to the Group entity. -func (m *GroupMutation) RemovedChildGroupsIDs() (ids []uuid.UUID) { - for id := range m.removedchild_groups { - ids = append(ids, id) - } - return +// SetTriggerActivities sets the "trigger_activities" field. +func (m *SubscriptionTemplateMutation) SetTriggerActivities(s string) { + m.trigger_activities = &s } -// ChildGroupsIDs returns the "child_groups" edge IDs in the mutation. -func (m *GroupMutation) ChildGroupsIDs() (ids []uuid.UUID) { - for id := range m.child_groups { - ids = append(ids, id) +// TriggerActivities returns the value of the "trigger_activities" field in the mutation. +func (m *SubscriptionTemplateMutation) TriggerActivities() (r string, exists bool) { + v := m.trigger_activities + if v == nil { + return } - return + return *v, true } -// ResetChildGroups resets all changes to the "child_groups" edge. -func (m *GroupMutation) ResetChildGroups() { - m.child_groups = nil - m.clearedchild_groups = false - m.removedchild_groups = nil +// OldTriggerActivities returns the old "trigger_activities" field's value of the SubscriptionTemplate entity. +// If the SubscriptionTemplate object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *SubscriptionTemplateMutation) OldTriggerActivities(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldTriggerActivities is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldTriggerActivities requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldTriggerActivities: %w", err) + } + return oldValue.TriggerActivities, nil } -// ClearOwner clears the "owner" edge to the User entity. -func (m *GroupMutation) ClearOwner() { - m.clearedowner = true - m.clearedFields[group.FieldOwnerID] = struct{}{} +// ResetTriggerActivities resets all changes to the "trigger_activities" field. +func (m *SubscriptionTemplateMutation) ResetTriggerActivities() { + m.trigger_activities = nil } -// OwnerCleared reports if the "owner" edge to the User entity was cleared. -func (m *GroupMutation) OwnerCleared() bool { - return m.OwnerIDCleared() || m.clearedowner +// SetProjectID sets the "project_id" field. +func (m *SubscriptionTemplateMutation) SetProjectID(u uuid.UUID) { + m.project_id = &u } -// OwnerIDs returns the "owner" edge IDs in the mutation. -// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use -// OwnerID instead. It exists only for internal usage by the builders. -func (m *GroupMutation) OwnerIDs() (ids []uuid.UUID) { - if id := m.owner; id != nil { - ids = append(ids, *id) +// ProjectID returns the value of the "project_id" field in the mutation. +func (m *SubscriptionTemplateMutation) ProjectID() (r uuid.UUID, exists bool) { + v := m.project_id + if v == nil { + return } - return -} - -// ResetOwner resets all changes to the "owner" edge. -func (m *GroupMutation) ResetOwner() { - m.owner = nil - m.clearedowner = false + return *v, true } -// AddPolicyBindingIDs adds the "policy_bindings" edge to the PolicyBinding entity by ids. -func (m *GroupMutation) AddPolicyBindingIDs(ids ...uuid.UUID) { - if m.policy_bindings == nil { - m.policy_bindings = make(map[uuid.UUID]struct{}) +// OldProjectID returns the old "project_id" field's value of the SubscriptionTemplate entity. +// If the SubscriptionTemplate object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *SubscriptionTemplateMutation) OldProjectID(ctx context.Context) (v *uuid.UUID, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldProjectID is only allowed on UpdateOne operations") } - for i := range ids { - m.policy_bindings[ids[i]] = struct{}{} + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldProjectID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldProjectID: %w", err) } + return oldValue.ProjectID, nil } -// ClearPolicyBindings clears the "policy_bindings" edge to the PolicyBinding entity. -func (m *GroupMutation) ClearPolicyBindings() { - m.clearedpolicy_bindings = true +// ClearProjectID clears the value of the "project_id" field. +func (m *SubscriptionTemplateMutation) ClearProjectID() { + m.project_id = nil + m.clearedFields[subscriptiontemplate.FieldProjectID] = struct{}{} } -// PolicyBindingsCleared reports if the "policy_bindings" edge to the PolicyBinding entity was cleared. -func (m *GroupMutation) PolicyBindingsCleared() bool { - return m.clearedpolicy_bindings +// ProjectIDCleared returns if the "project_id" field was cleared in this mutation. +func (m *SubscriptionTemplateMutation) ProjectIDCleared() bool { + _, ok := m.clearedFields[subscriptiontemplate.FieldProjectID] + return ok } -// RemovePolicyBindingIDs removes the "policy_bindings" edge to the PolicyBinding entity by IDs. -func (m *GroupMutation) RemovePolicyBindingIDs(ids ...uuid.UUID) { - if m.removedpolicy_bindings == nil { - m.removedpolicy_bindings = make(map[uuid.UUID]struct{}) - } - for i := range ids { - delete(m.policy_bindings, ids[i]) - m.removedpolicy_bindings[ids[i]] = struct{}{} - } +// ResetProjectID resets all changes to the "project_id" field. +func (m *SubscriptionTemplateMutation) ResetProjectID() { + m.project_id = nil + delete(m.clearedFields, subscriptiontemplate.FieldProjectID) } -// RemovedPolicyBindings returns the removed IDs of the "policy_bindings" edge to the PolicyBinding entity. -func (m *GroupMutation) RemovedPolicyBindingsIDs() (ids []uuid.UUID) { - for id := range m.removedpolicy_bindings { - ids = append(ids, id) +// SetCreatedBy sets the "created_by" field. +func (m *SubscriptionTemplateMutation) SetCreatedBy(s string) { + m.created_by = &s +} + +// CreatedBy returns the value of the "created_by" field in the mutation. +func (m *SubscriptionTemplateMutation) CreatedBy() (r string, exists bool) { + v := m.created_by + if v == nil { + return } - return + return *v, true } -// PolicyBindingsIDs returns the "policy_bindings" edge IDs in the mutation. -func (m *GroupMutation) PolicyBindingsIDs() (ids []uuid.UUID) { - for id := range m.policy_bindings { - ids = append(ids, id) +// OldCreatedBy returns the old "created_by" field's value of the SubscriptionTemplate entity. +// If the SubscriptionTemplate object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *SubscriptionTemplateMutation) OldCreatedBy(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreatedBy is only allowed on UpdateOne operations") } - return + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreatedBy requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreatedBy: %w", err) + } + return oldValue.CreatedBy, nil } -// ResetPolicyBindings resets all changes to the "policy_bindings" edge. -func (m *GroupMutation) ResetPolicyBindings() { - m.policy_bindings = nil - m.clearedpolicy_bindings = false - m.removedpolicy_bindings = nil +// ResetCreatedBy resets all changes to the "created_by" field. +func (m *SubscriptionTemplateMutation) ResetCreatedBy() { + m.created_by = nil } -// Where appends a list predicates to the GroupMutation builder. -func (m *GroupMutation) Where(ps ...predicate.Group) { +// Where appends a list predicates to the SubscriptionTemplateMutation builder. +func (m *SubscriptionTemplateMutation) Where(ps ...predicate.SubscriptionTemplate) { m.predicates = append(m.predicates, ps...) } -// WhereP appends storage-level predicates to the GroupMutation builder. Using this method, +// WhereP appends storage-level predicates to the SubscriptionTemplateMutation builder. Using this method, // users can use type-assertion to append predicates that do not depend on any generated package. -func (m *GroupMutation) WhereP(ps ...func(*sql.Selector)) { - p := make([]predicate.Group, len(ps)) +func (m *SubscriptionTemplateMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.SubscriptionTemplate, len(ps)) for i := range ps { p[i] = ps[i] } @@ -3562,57 +28011,39 @@ func (m *GroupMutation) WhereP(ps ...func(*sql.Selector)) { } // Op returns the operation name. -func (m *GroupMutation) Op() Op { +func (m *SubscriptionTemplateMutation) Op() Op { return m.op } // SetOp allows setting the mutation operation. -func (m *GroupMutation) SetOp(op Op) { +func (m *SubscriptionTemplateMutation) SetOp(op Op) { m.op = op } -// Type returns the node type of this mutation (Group). -func (m *GroupMutation) Type() string { +// Type returns the node type of this mutation (SubscriptionTemplate). +func (m *SubscriptionTemplateMutation) Type() string { return m.typ } // Fields returns all fields that were changed during this mutation. Note that in // order to get all numeric fields that were incremented/decremented, call // AddedFields(). -func (m *GroupMutation) Fields() []string { - fields := make([]string, 0, 11) +func (m *SubscriptionTemplateMutation) Fields() []string { + fields := make([]string, 0, 5) if m.name != nil { - fields = append(fields, group.FieldName) - } - if m.slug != nil { - fields = append(fields, group.FieldSlug) + fields = append(fields, subscriptiontemplate.FieldName) } - if m.description != nil { - fields = append(fields, group.FieldDescription) + if m.scope != nil { + fields = append(fields, subscriptiontemplate.FieldScope) } - if m.group_type != nil { - fields = append(fields, group.FieldGroupType) + if m.trigger_activities != nil { + fields = append(fields, subscriptiontemplate.FieldTriggerActivities) } if m.project_id != nil { - fields = append(fields, group.FieldProjectID) - } - if m.labels != nil { - fields = append(fields, group.FieldLabels) - } - if m.annotations != nil { - fields = append(fields, group.FieldAnnotations) - } - if m.created != nil { - fields = append(fields, group.FieldCreated) - } - if m.updated != nil { - fields = append(fields, group.FieldUpdated) + fields = append(fields, subscriptiontemplate.FieldProjectID) } if m.created_by != nil { - fields = append(fields, group.FieldCreatedBy) - } - if m.owner != nil { - fields = append(fields, group.FieldOwnerID) + fields = append(fields, subscriptiontemplate.FieldCreatedBy) } return fields } @@ -3620,30 +28051,18 @@ func (m *GroupMutation) Fields() []string { // Field returns the value of a field with the given name. The second boolean // return value indicates that this field was not set, or was not defined in the // schema. -func (m *GroupMutation) Field(name string) (ent.Value, bool) { +func (m *SubscriptionTemplateMutation) Field(name string) (ent.Value, bool) { switch name { - case group.FieldName: + case subscriptiontemplate.FieldName: return m.Name() - case group.FieldSlug: - return m.Slug() - case group.FieldDescription: - return m.Description() - case group.FieldGroupType: - return m.GroupType() - case group.FieldProjectID: + case subscriptiontemplate.FieldScope: + return m.Scope() + case subscriptiontemplate.FieldTriggerActivities: + return m.TriggerActivities() + case subscriptiontemplate.FieldProjectID: return m.ProjectID() - case group.FieldLabels: - return m.Labels() - case group.FieldAnnotations: - return m.Annotations() - case group.FieldCreated: - return m.Created() - case group.FieldUpdated: - return m.Updated() - case group.FieldCreatedBy: + case subscriptiontemplate.FieldCreatedBy: return m.CreatedBy() - case group.FieldOwnerID: - return m.OwnerID() } return nil, false } @@ -3651,453 +28070,235 @@ func (m *GroupMutation) Field(name string) (ent.Value, bool) { // OldField returns the old value of the field from the database. An error is // returned if the mutation operation is not UpdateOne, or the query to the // database failed. -func (m *GroupMutation) OldField(ctx context.Context, name string) (ent.Value, error) { +func (m *SubscriptionTemplateMutation) OldField(ctx context.Context, name string) (ent.Value, error) { switch name { - case group.FieldName: + case subscriptiontemplate.FieldName: return m.OldName(ctx) - case group.FieldSlug: - return m.OldSlug(ctx) - case group.FieldDescription: - return m.OldDescription(ctx) - case group.FieldGroupType: - return m.OldGroupType(ctx) - case group.FieldProjectID: + case subscriptiontemplate.FieldScope: + return m.OldScope(ctx) + case subscriptiontemplate.FieldTriggerActivities: + return m.OldTriggerActivities(ctx) + case subscriptiontemplate.FieldProjectID: return m.OldProjectID(ctx) - case group.FieldLabels: - return m.OldLabels(ctx) - case group.FieldAnnotations: - return m.OldAnnotations(ctx) - case group.FieldCreated: - return m.OldCreated(ctx) - case group.FieldUpdated: - return m.OldUpdated(ctx) - case group.FieldCreatedBy: + case subscriptiontemplate.FieldCreatedBy: return m.OldCreatedBy(ctx) - case group.FieldOwnerID: - return m.OldOwnerID(ctx) } - return nil, fmt.Errorf("unknown Group field %s", name) + return nil, fmt.Errorf("unknown SubscriptionTemplate field %s", name) } // SetField sets the value of a field with the given name. It returns an error if // the field is not defined in the schema, or if the type mismatched the field // type. -func (m *GroupMutation) SetField(name string, value ent.Value) error { +func (m *SubscriptionTemplateMutation) SetField(name string, value ent.Value) error { switch name { - case group.FieldName: + case subscriptiontemplate.FieldName: v, ok := value.(string) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } m.SetName(v) return nil - case group.FieldSlug: + case subscriptiontemplate.FieldScope: v, ok := value.(string) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetSlug(v) + m.SetScope(v) return nil - case group.FieldDescription: + case subscriptiontemplate.FieldTriggerActivities: v, ok := value.(string) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetDescription(v) - return nil - case group.FieldGroupType: - v, ok := value.(group.GroupType) - if !ok { - return fmt.Errorf("unexpected type %T for field %s", value, name) - } - m.SetGroupType(v) + m.SetTriggerActivities(v) return nil - case group.FieldProjectID: + case subscriptiontemplate.FieldProjectID: v, ok := value.(uuid.UUID) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } m.SetProjectID(v) return nil - case group.FieldLabels: - v, ok := value.(map[string]string) - if !ok { - return fmt.Errorf("unexpected type %T for field %s", value, name) - } - m.SetLabels(v) - return nil - case group.FieldAnnotations: - v, ok := value.(map[string]string) - if !ok { - return fmt.Errorf("unexpected type %T for field %s", value, name) - } - m.SetAnnotations(v) - return nil - case group.FieldCreated: - v, ok := value.(time.Time) - if !ok { - return fmt.Errorf("unexpected type %T for field %s", value, name) - } - m.SetCreated(v) - return nil - case group.FieldUpdated: - v, ok := value.(time.Time) - if !ok { - return fmt.Errorf("unexpected type %T for field %s", value, name) - } - m.SetUpdated(v) - return nil - case group.FieldCreatedBy: + case subscriptiontemplate.FieldCreatedBy: v, ok := value.(string) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } m.SetCreatedBy(v) return nil - case group.FieldOwnerID: - v, ok := value.(uuid.UUID) - if !ok { - return fmt.Errorf("unexpected type %T for field %s", value, name) - } - m.SetOwnerID(v) - return nil } - return fmt.Errorf("unknown Group field %s", name) + return fmt.Errorf("unknown SubscriptionTemplate field %s", name) } // AddedFields returns all numeric fields that were incremented/decremented during // this mutation. -func (m *GroupMutation) AddedFields() []string { +func (m *SubscriptionTemplateMutation) AddedFields() []string { return nil } // AddedField returns the numeric value that was incremented/decremented on a field // with the given name. The second boolean return value indicates that this field // was not set, or was not defined in the schema. -func (m *GroupMutation) AddedField(name string) (ent.Value, bool) { +func (m *SubscriptionTemplateMutation) AddedField(name string) (ent.Value, bool) { return nil, false } // AddField adds the value to the field with the given name. It returns an error if // the field is not defined in the schema, or if the type mismatched the field // type. -func (m *GroupMutation) AddField(name string, value ent.Value) error { +func (m *SubscriptionTemplateMutation) AddField(name string, value ent.Value) error { switch name { } - return fmt.Errorf("unknown Group numeric field %s", name) + return fmt.Errorf("unknown SubscriptionTemplate numeric field %s", name) } // ClearedFields returns all nullable fields that were cleared during this // mutation. -func (m *GroupMutation) ClearedFields() []string { +func (m *SubscriptionTemplateMutation) ClearedFields() []string { var fields []string - if m.FieldCleared(group.FieldDescription) { - fields = append(fields, group.FieldDescription) - } - if m.FieldCleared(group.FieldProjectID) { - fields = append(fields, group.FieldProjectID) - } - if m.FieldCleared(group.FieldLabels) { - fields = append(fields, group.FieldLabels) - } - if m.FieldCleared(group.FieldAnnotations) { - fields = append(fields, group.FieldAnnotations) - } - if m.FieldCleared(group.FieldCreatedBy) { - fields = append(fields, group.FieldCreatedBy) - } - if m.FieldCleared(group.FieldOwnerID) { - fields = append(fields, group.FieldOwnerID) + if m.FieldCleared(subscriptiontemplate.FieldProjectID) { + fields = append(fields, subscriptiontemplate.FieldProjectID) } return fields } // FieldCleared returns a boolean indicating if a field with the given name was // cleared in this mutation. -func (m *GroupMutation) FieldCleared(name string) bool { +func (m *SubscriptionTemplateMutation) FieldCleared(name string) bool { _, ok := m.clearedFields[name] return ok } // ClearField clears the value of the field with the given name. It returns an // error if the field is not defined in the schema. -func (m *GroupMutation) ClearField(name string) error { +func (m *SubscriptionTemplateMutation) ClearField(name string) error { switch name { - case group.FieldDescription: - m.ClearDescription() - return nil - case group.FieldProjectID: + case subscriptiontemplate.FieldProjectID: m.ClearProjectID() return nil - case group.FieldLabels: - m.ClearLabels() - return nil - case group.FieldAnnotations: - m.ClearAnnotations() - return nil - case group.FieldCreatedBy: - m.ClearCreatedBy() - return nil - case group.FieldOwnerID: - m.ClearOwnerID() - return nil } - return fmt.Errorf("unknown Group nullable field %s", name) + return fmt.Errorf("unknown SubscriptionTemplate nullable field %s", name) } // ResetField resets all changes in the mutation for the field with the given name. // It returns an error if the field is not defined in the schema. -func (m *GroupMutation) ResetField(name string) error { +func (m *SubscriptionTemplateMutation) ResetField(name string) error { switch name { - case group.FieldName: + case subscriptiontemplate.FieldName: m.ResetName() return nil - case group.FieldSlug: - m.ResetSlug() - return nil - case group.FieldDescription: - m.ResetDescription() + case subscriptiontemplate.FieldScope: + m.ResetScope() return nil - case group.FieldGroupType: - m.ResetGroupType() + case subscriptiontemplate.FieldTriggerActivities: + m.ResetTriggerActivities() return nil - case group.FieldProjectID: + case subscriptiontemplate.FieldProjectID: m.ResetProjectID() return nil - case group.FieldLabels: - m.ResetLabels() - return nil - case group.FieldAnnotations: - m.ResetAnnotations() - return nil - case group.FieldCreated: - m.ResetCreated() - return nil - case group.FieldUpdated: - m.ResetUpdated() - return nil - case group.FieldCreatedBy: + case subscriptiontemplate.FieldCreatedBy: m.ResetCreatedBy() return nil - case group.FieldOwnerID: - m.ResetOwnerID() - return nil } - return fmt.Errorf("unknown Group field %s", name) + return fmt.Errorf("unknown SubscriptionTemplate field %s", name) } // AddedEdges returns all edge names that were set/added in this mutation. -func (m *GroupMutation) AddedEdges() []string { - edges := make([]string, 0, 5) - if m.memberships != nil { - edges = append(edges, group.EdgeMemberships) - } - if m.parent_groups != nil { - edges = append(edges, group.EdgeParentGroups) - } - if m.child_groups != nil { - edges = append(edges, group.EdgeChildGroups) - } - if m.owner != nil { - edges = append(edges, group.EdgeOwner) - } - if m.policy_bindings != nil { - edges = append(edges, group.EdgePolicyBindings) - } +func (m *SubscriptionTemplateMutation) AddedEdges() []string { + edges := make([]string, 0, 0) return edges } // AddedIDs returns all IDs (to other nodes) that were added for the given edge // name in this mutation. -func (m *GroupMutation) AddedIDs(name string) []ent.Value { - switch name { - case group.EdgeMemberships: - ids := make([]ent.Value, 0, len(m.memberships)) - for id := range m.memberships { - ids = append(ids, id) - } - return ids - case group.EdgeParentGroups: - ids := make([]ent.Value, 0, len(m.parent_groups)) - for id := range m.parent_groups { - ids = append(ids, id) - } - return ids - case group.EdgeChildGroups: - ids := make([]ent.Value, 0, len(m.child_groups)) - for id := range m.child_groups { - ids = append(ids, id) - } - return ids - case group.EdgeOwner: - if id := m.owner; id != nil { - return []ent.Value{*id} - } - case group.EdgePolicyBindings: - ids := make([]ent.Value, 0, len(m.policy_bindings)) - for id := range m.policy_bindings { - ids = append(ids, id) - } - return ids - } +func (m *SubscriptionTemplateMutation) AddedIDs(name string) []ent.Value { return nil } // RemovedEdges returns all edge names that were removed in this mutation. -func (m *GroupMutation) RemovedEdges() []string { - edges := make([]string, 0, 5) - if m.removedmemberships != nil { - edges = append(edges, group.EdgeMemberships) - } - if m.removedparent_groups != nil { - edges = append(edges, group.EdgeParentGroups) - } - if m.removedchild_groups != nil { - edges = append(edges, group.EdgeChildGroups) - } - if m.removedpolicy_bindings != nil { - edges = append(edges, group.EdgePolicyBindings) - } +func (m *SubscriptionTemplateMutation) RemovedEdges() []string { + edges := make([]string, 0, 0) return edges } // RemovedIDs returns all IDs (to other nodes) that were removed for the edge with // the given name in this mutation. -func (m *GroupMutation) RemovedIDs(name string) []ent.Value { - switch name { - case group.EdgeMemberships: - ids := make([]ent.Value, 0, len(m.removedmemberships)) - for id := range m.removedmemberships { - ids = append(ids, id) - } - return ids - case group.EdgeParentGroups: - ids := make([]ent.Value, 0, len(m.removedparent_groups)) - for id := range m.removedparent_groups { - ids = append(ids, id) - } - return ids - case group.EdgeChildGroups: - ids := make([]ent.Value, 0, len(m.removedchild_groups)) - for id := range m.removedchild_groups { - ids = append(ids, id) - } - return ids - case group.EdgePolicyBindings: - ids := make([]ent.Value, 0, len(m.removedpolicy_bindings)) - for id := range m.removedpolicy_bindings { - ids = append(ids, id) - } - return ids - } +func (m *SubscriptionTemplateMutation) RemovedIDs(name string) []ent.Value { return nil } // ClearedEdges returns all edge names that were cleared in this mutation. -func (m *GroupMutation) ClearedEdges() []string { - edges := make([]string, 0, 5) - if m.clearedmemberships { - edges = append(edges, group.EdgeMemberships) - } - if m.clearedparent_groups { - edges = append(edges, group.EdgeParentGroups) - } - if m.clearedchild_groups { - edges = append(edges, group.EdgeChildGroups) - } - if m.clearedowner { - edges = append(edges, group.EdgeOwner) - } - if m.clearedpolicy_bindings { - edges = append(edges, group.EdgePolicyBindings) - } +func (m *SubscriptionTemplateMutation) ClearedEdges() []string { + edges := make([]string, 0, 0) return edges } // EdgeCleared returns a boolean which indicates if the edge with the given name // was cleared in this mutation. -func (m *GroupMutation) EdgeCleared(name string) bool { - switch name { - case group.EdgeMemberships: - return m.clearedmemberships - case group.EdgeParentGroups: - return m.clearedparent_groups - case group.EdgeChildGroups: - return m.clearedchild_groups - case group.EdgeOwner: - return m.clearedowner - case group.EdgePolicyBindings: - return m.clearedpolicy_bindings - } +func (m *SubscriptionTemplateMutation) EdgeCleared(name string) bool { return false } // ClearEdge clears the value of the edge with the given name. It returns an error // if that edge is not defined in the schema. -func (m *GroupMutation) ClearEdge(name string) error { - switch name { - case group.EdgeOwner: - m.ClearOwner() - return nil - } - return fmt.Errorf("unknown Group unique edge %s", name) +func (m *SubscriptionTemplateMutation) ClearEdge(name string) error { + return fmt.Errorf("unknown SubscriptionTemplate unique edge %s", name) } // ResetEdge resets all changes to the edge with the given name in this mutation. // It returns an error if the edge is not defined in the schema. -func (m *GroupMutation) ResetEdge(name string) error { - switch name { - case group.EdgeMemberships: - m.ResetMemberships() - return nil - case group.EdgeParentGroups: - m.ResetParentGroups() - return nil - case group.EdgeChildGroups: - m.ResetChildGroups() - return nil - case group.EdgeOwner: - m.ResetOwner() - return nil - case group.EdgePolicyBindings: - m.ResetPolicyBindings() - return nil - } - return fmt.Errorf("unknown Group edge %s", name) +func (m *SubscriptionTemplateMutation) ResetEdge(name string) error { + return fmt.Errorf("unknown SubscriptionTemplate edge %s", name) } -// GroupMembershipMutation represents an operation that mutates the GroupMembership nodes in the graph. -type GroupMembershipMutation struct { +// TemplateMutation represents an operation that mutates the Template nodes in the graph. +type TemplateMutation struct { config - op Op - typ string - id *uuid.UUID - role *groupmembership.Role - added_by *string - added_at *time.Time - clearedFields map[string]struct{} - group *uuid.UUID - clearedgroup bool - user *uuid.UUID - cleareduser bool - agent *uuid.UUID - clearedagent bool - done bool - oldValue func(context.Context) (*GroupMembership, error) - predicates []predicate.GroupMembership + op Op + typ string + id *uuid.UUID + name *string + slug *string + display_name *string + description *string + harness *string + default_harness_config *string + image *string + _config *string + content_hash *string + scope *string + scope_id *string + project_id *string + storage_uri *string + storage_bucket *string + storage_path *string + files *string + base_template *string + locked *bool + status *template.Status + owner_id *string + created_by *string + updated_by *string + visibility *string + created *time.Time + updated *time.Time + clearedFields map[string]struct{} + done bool + oldValue func(context.Context) (*Template, error) + predicates []predicate.Template } -var _ ent.Mutation = (*GroupMembershipMutation)(nil) +var _ ent.Mutation = (*TemplateMutation)(nil) -// groupmembershipOption allows management of the mutation configuration using functional options. -type groupmembershipOption func(*GroupMembershipMutation) +// templateOption allows management of the mutation configuration using functional options. +type templateOption func(*TemplateMutation) -// newGroupMembershipMutation creates new mutation for the GroupMembership entity. -func newGroupMembershipMutation(c config, op Op, opts ...groupmembershipOption) *GroupMembershipMutation { - m := &GroupMembershipMutation{ +// newTemplateMutation creates new mutation for the Template entity. +func newTemplateMutation(c config, op Op, opts ...templateOption) *TemplateMutation { + m := &TemplateMutation{ config: c, op: op, - typ: TypeGroupMembership, + typ: TypeTemplate, clearedFields: make(map[string]struct{}), } for _, opt := range opts { @@ -4105,21 +28306,21 @@ func newGroupMembershipMutation(c config, op Op, opts ...groupmembershipOption) } return m } - -// withGroupMembershipID sets the ID field of the mutation. -func withGroupMembershipID(id uuid.UUID) groupmembershipOption { - return func(m *GroupMembershipMutation) { + +// withTemplateID sets the ID field of the mutation. +func withTemplateID(id uuid.UUID) templateOption { + return func(m *TemplateMutation) { var ( err error once sync.Once - value *GroupMembership + value *Template ) - m.oldValue = func(ctx context.Context) (*GroupMembership, error) { + m.oldValue = func(ctx context.Context) (*Template, error) { once.Do(func() { if m.done { err = errors.New("querying old values post mutation is not allowed") } else { - value, err = m.Client().GroupMembership.Get(ctx, id) + value, err = m.Client().Template.Get(ctx, id) } }) return value, err @@ -4128,10 +28329,10 @@ func withGroupMembershipID(id uuid.UUID) groupmembershipOption { } } -// withGroupMembership sets the old GroupMembership of the mutation. -func withGroupMembership(node *GroupMembership) groupmembershipOption { - return func(m *GroupMembershipMutation) { - m.oldValue = func(context.Context) (*GroupMembership, error) { +// withTemplate sets the old Template of the mutation. +func withTemplate(node *Template) templateOption { + return func(m *TemplateMutation) { + m.oldValue = func(context.Context) (*Template, error) { return node, nil } m.id = &node.ID @@ -4140,7 +28341,7 @@ func withGroupMembership(node *GroupMembership) groupmembershipOption { // Client returns a new `ent.Client` from the mutation. If the mutation was // executed in a transaction (ent.Tx), a transactional client is returned. -func (m GroupMembershipMutation) Client() *Client { +func (m TemplateMutation) Client() *Client { client := &Client{config: m.config} client.init() return client @@ -4148,7 +28349,7 @@ func (m GroupMembershipMutation) Client() *Client { // Tx returns an `ent.Tx` for mutations that were executed in transactions; // it returns an error otherwise. -func (m GroupMembershipMutation) Tx() (*Tx, error) { +func (m TemplateMutation) Tx() (*Tx, error) { if _, ok := m.driver.(*txDriver); !ok { return nil, errors.New("ent: mutation is not running in a transaction") } @@ -4158,14 +28359,14 @@ func (m GroupMembershipMutation) Tx() (*Tx, error) { } // SetID sets the value of the id field. Note that this -// operation is only accepted on creation of GroupMembership entities. -func (m *GroupMembershipMutation) SetID(id uuid.UUID) { +// operation is only accepted on creation of Template entities. +func (m *TemplateMutation) SetID(id uuid.UUID) { m.id = &id } // ID returns the ID value in the mutation. Note that the ID is only available // if it was provided to the builder or after it was returned from the database. -func (m *GroupMembershipMutation) ID() (id uuid.UUID, exists bool) { +func (m *TemplateMutation) ID() (id uuid.UUID, exists bool) { if m.id == nil { return } @@ -4176,7 +28377,7 @@ func (m *GroupMembershipMutation) ID() (id uuid.UUID, exists bool) { // That means, if the mutation is applied within a transaction with an isolation level such // as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated // or updated by the mutation. -func (m *GroupMembershipMutation) IDs(ctx context.Context) ([]uuid.UUID, error) { +func (m *TemplateMutation) IDs(ctx context.Context) ([]uuid.UUID, error) { switch { case m.op.Is(OpUpdateOne | OpDeleteOne): id, exists := m.ID() @@ -4185,1256 +28386,1129 @@ func (m *GroupMembershipMutation) IDs(ctx context.Context) ([]uuid.UUID, error) } fallthrough case m.op.Is(OpUpdate | OpDelete): - return m.Client().GroupMembership.Query().Where(m.predicates...).IDs(ctx) + return m.Client().Template.Query().Where(m.predicates...).IDs(ctx) default: return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) } } -// SetRole sets the "role" field. -func (m *GroupMembershipMutation) SetRole(gr groupmembership.Role) { - m.role = &gr +// SetName sets the "name" field. +func (m *TemplateMutation) SetName(s string) { + m.name = &s } -// Role returns the value of the "role" field in the mutation. -func (m *GroupMembershipMutation) Role() (r groupmembership.Role, exists bool) { - v := m.role +// Name returns the value of the "name" field in the mutation. +func (m *TemplateMutation) Name() (r string, exists bool) { + v := m.name if v == nil { return } return *v, true } -// OldRole returns the old "role" field's value of the GroupMembership entity. -// If the GroupMembership object wasn't provided to the builder, the object is fetched from the database. +// OldName returns the old "name" field's value of the Template entity. +// If the Template object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *GroupMembershipMutation) OldRole(ctx context.Context) (v groupmembership.Role, err error) { +func (m *TemplateMutation) OldName(ctx context.Context) (v string, err error) { if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldRole is only allowed on UpdateOne operations") + return v, errors.New("OldName is only allowed on UpdateOne operations") } if m.id == nil || m.oldValue == nil { - return v, errors.New("OldRole requires an ID field in the mutation") + return v, errors.New("OldName requires an ID field in the mutation") } oldValue, err := m.oldValue(ctx) if err != nil { - return v, fmt.Errorf("querying old value for OldRole: %w", err) + return v, fmt.Errorf("querying old value for OldName: %w", err) } - return oldValue.Role, nil + return oldValue.Name, nil } -// ResetRole resets all changes to the "role" field. -func (m *GroupMembershipMutation) ResetRole() { - m.role = nil +// ResetName resets all changes to the "name" field. +func (m *TemplateMutation) ResetName() { + m.name = nil } -// SetAddedBy sets the "added_by" field. -func (m *GroupMembershipMutation) SetAddedBy(s string) { - m.added_by = &s +// SetSlug sets the "slug" field. +func (m *TemplateMutation) SetSlug(s string) { + m.slug = &s } -// AddedBy returns the value of the "added_by" field in the mutation. -func (m *GroupMembershipMutation) AddedBy() (r string, exists bool) { - v := m.added_by +// Slug returns the value of the "slug" field in the mutation. +func (m *TemplateMutation) Slug() (r string, exists bool) { + v := m.slug if v == nil { return } return *v, true } -// OldAddedBy returns the old "added_by" field's value of the GroupMembership entity. -// If the GroupMembership object wasn't provided to the builder, the object is fetched from the database. +// OldSlug returns the old "slug" field's value of the Template entity. +// If the Template object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *GroupMembershipMutation) OldAddedBy(ctx context.Context) (v string, err error) { +func (m *TemplateMutation) OldSlug(ctx context.Context) (v string, err error) { if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldAddedBy is only allowed on UpdateOne operations") + return v, errors.New("OldSlug is only allowed on UpdateOne operations") } if m.id == nil || m.oldValue == nil { - return v, errors.New("OldAddedBy requires an ID field in the mutation") + return v, errors.New("OldSlug requires an ID field in the mutation") } oldValue, err := m.oldValue(ctx) if err != nil { - return v, fmt.Errorf("querying old value for OldAddedBy: %w", err) + return v, fmt.Errorf("querying old value for OldSlug: %w", err) } - return oldValue.AddedBy, nil -} - -// ClearAddedBy clears the value of the "added_by" field. -func (m *GroupMembershipMutation) ClearAddedBy() { - m.added_by = nil - m.clearedFields[groupmembership.FieldAddedBy] = struct{}{} -} - -// AddedByCleared returns if the "added_by" field was cleared in this mutation. -func (m *GroupMembershipMutation) AddedByCleared() bool { - _, ok := m.clearedFields[groupmembership.FieldAddedBy] - return ok + return oldValue.Slug, nil } -// ResetAddedBy resets all changes to the "added_by" field. -func (m *GroupMembershipMutation) ResetAddedBy() { - m.added_by = nil - delete(m.clearedFields, groupmembership.FieldAddedBy) +// ResetSlug resets all changes to the "slug" field. +func (m *TemplateMutation) ResetSlug() { + m.slug = nil } -// SetAddedAt sets the "added_at" field. -func (m *GroupMembershipMutation) SetAddedAt(t time.Time) { - m.added_at = &t +// SetDisplayName sets the "display_name" field. +func (m *TemplateMutation) SetDisplayName(s string) { + m.display_name = &s } -// AddedAt returns the value of the "added_at" field in the mutation. -func (m *GroupMembershipMutation) AddedAt() (r time.Time, exists bool) { - v := m.added_at +// DisplayName returns the value of the "display_name" field in the mutation. +func (m *TemplateMutation) DisplayName() (r string, exists bool) { + v := m.display_name if v == nil { return } return *v, true } -// OldAddedAt returns the old "added_at" field's value of the GroupMembership entity. -// If the GroupMembership object wasn't provided to the builder, the object is fetched from the database. +// OldDisplayName returns the old "display_name" field's value of the Template entity. +// If the Template object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *GroupMembershipMutation) OldAddedAt(ctx context.Context) (v time.Time, err error) { +func (m *TemplateMutation) OldDisplayName(ctx context.Context) (v string, err error) { if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldAddedAt is only allowed on UpdateOne operations") + return v, errors.New("OldDisplayName is only allowed on UpdateOne operations") } if m.id == nil || m.oldValue == nil { - return v, errors.New("OldAddedAt requires an ID field in the mutation") + return v, errors.New("OldDisplayName requires an ID field in the mutation") } oldValue, err := m.oldValue(ctx) if err != nil { - return v, fmt.Errorf("querying old value for OldAddedAt: %w", err) + return v, fmt.Errorf("querying old value for OldDisplayName: %w", err) } - return oldValue.AddedAt, nil -} - -// ResetAddedAt resets all changes to the "added_at" field. -func (m *GroupMembershipMutation) ResetAddedAt() { - m.added_at = nil -} - -// SetGroupID sets the "group_id" field. -func (m *GroupMembershipMutation) SetGroupID(u uuid.UUID) { - m.group = &u + return oldValue.DisplayName, nil } -// GroupID returns the value of the "group_id" field in the mutation. -func (m *GroupMembershipMutation) GroupID() (r uuid.UUID, exists bool) { - v := m.group - if v == nil { - return - } - return *v, true +// ClearDisplayName clears the value of the "display_name" field. +func (m *TemplateMutation) ClearDisplayName() { + m.display_name = nil + m.clearedFields[template.FieldDisplayName] = struct{}{} } -// OldGroupID returns the old "group_id" field's value of the GroupMembership entity. -// If the GroupMembership object wasn't provided to the builder, the object is fetched from the database. -// An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *GroupMembershipMutation) OldGroupID(ctx context.Context) (v uuid.UUID, err error) { - if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldGroupID is only allowed on UpdateOne operations") - } - if m.id == nil || m.oldValue == nil { - return v, errors.New("OldGroupID requires an ID field in the mutation") - } - oldValue, err := m.oldValue(ctx) - if err != nil { - return v, fmt.Errorf("querying old value for OldGroupID: %w", err) - } - return oldValue.GroupID, nil +// DisplayNameCleared returns if the "display_name" field was cleared in this mutation. +func (m *TemplateMutation) DisplayNameCleared() bool { + _, ok := m.clearedFields[template.FieldDisplayName] + return ok } -// ResetGroupID resets all changes to the "group_id" field. -func (m *GroupMembershipMutation) ResetGroupID() { - m.group = nil +// ResetDisplayName resets all changes to the "display_name" field. +func (m *TemplateMutation) ResetDisplayName() { + m.display_name = nil + delete(m.clearedFields, template.FieldDisplayName) } -// SetUserID sets the "user_id" field. -func (m *GroupMembershipMutation) SetUserID(u uuid.UUID) { - m.user = &u +// SetDescription sets the "description" field. +func (m *TemplateMutation) SetDescription(s string) { + m.description = &s } -// UserID returns the value of the "user_id" field in the mutation. -func (m *GroupMembershipMutation) UserID() (r uuid.UUID, exists bool) { - v := m.user +// Description returns the value of the "description" field in the mutation. +func (m *TemplateMutation) Description() (r string, exists bool) { + v := m.description if v == nil { return } return *v, true } -// OldUserID returns the old "user_id" field's value of the GroupMembership entity. -// If the GroupMembership object wasn't provided to the builder, the object is fetched from the database. +// OldDescription returns the old "description" field's value of the Template entity. +// If the Template object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *GroupMembershipMutation) OldUserID(ctx context.Context) (v *uuid.UUID, err error) { +func (m *TemplateMutation) OldDescription(ctx context.Context) (v string, err error) { if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldUserID is only allowed on UpdateOne operations") + return v, errors.New("OldDescription is only allowed on UpdateOne operations") } if m.id == nil || m.oldValue == nil { - return v, errors.New("OldUserID requires an ID field in the mutation") + return v, errors.New("OldDescription requires an ID field in the mutation") } oldValue, err := m.oldValue(ctx) if err != nil { - return v, fmt.Errorf("querying old value for OldUserID: %w", err) + return v, fmt.Errorf("querying old value for OldDescription: %w", err) } - return oldValue.UserID, nil + return oldValue.Description, nil } -// ClearUserID clears the value of the "user_id" field. -func (m *GroupMembershipMutation) ClearUserID() { - m.user = nil - m.clearedFields[groupmembership.FieldUserID] = struct{}{} +// ClearDescription clears the value of the "description" field. +func (m *TemplateMutation) ClearDescription() { + m.description = nil + m.clearedFields[template.FieldDescription] = struct{}{} } -// UserIDCleared returns if the "user_id" field was cleared in this mutation. -func (m *GroupMembershipMutation) UserIDCleared() bool { - _, ok := m.clearedFields[groupmembership.FieldUserID] +// DescriptionCleared returns if the "description" field was cleared in this mutation. +func (m *TemplateMutation) DescriptionCleared() bool { + _, ok := m.clearedFields[template.FieldDescription] return ok } -// ResetUserID resets all changes to the "user_id" field. -func (m *GroupMembershipMutation) ResetUserID() { - m.user = nil - delete(m.clearedFields, groupmembership.FieldUserID) +// ResetDescription resets all changes to the "description" field. +func (m *TemplateMutation) ResetDescription() { + m.description = nil + delete(m.clearedFields, template.FieldDescription) } -// SetAgentID sets the "agent_id" field. -func (m *GroupMembershipMutation) SetAgentID(u uuid.UUID) { - m.agent = &u +// SetHarness sets the "harness" field. +func (m *TemplateMutation) SetHarness(s string) { + m.harness = &s } -// AgentID returns the value of the "agent_id" field in the mutation. -func (m *GroupMembershipMutation) AgentID() (r uuid.UUID, exists bool) { - v := m.agent +// Harness returns the value of the "harness" field in the mutation. +func (m *TemplateMutation) Harness() (r string, exists bool) { + v := m.harness if v == nil { return } return *v, true } -// OldAgentID returns the old "agent_id" field's value of the GroupMembership entity. -// If the GroupMembership object wasn't provided to the builder, the object is fetched from the database. +// OldHarness returns the old "harness" field's value of the Template entity. +// If the Template object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *GroupMembershipMutation) OldAgentID(ctx context.Context) (v *uuid.UUID, err error) { +func (m *TemplateMutation) OldHarness(ctx context.Context) (v string, err error) { if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldAgentID is only allowed on UpdateOne operations") + return v, errors.New("OldHarness is only allowed on UpdateOne operations") } if m.id == nil || m.oldValue == nil { - return v, errors.New("OldAgentID requires an ID field in the mutation") + return v, errors.New("OldHarness requires an ID field in the mutation") } oldValue, err := m.oldValue(ctx) - if err != nil { - return v, fmt.Errorf("querying old value for OldAgentID: %w", err) - } - return oldValue.AgentID, nil -} - -// ClearAgentID clears the value of the "agent_id" field. -func (m *GroupMembershipMutation) ClearAgentID() { - m.agent = nil - m.clearedFields[groupmembership.FieldAgentID] = struct{}{} -} - -// AgentIDCleared returns if the "agent_id" field was cleared in this mutation. -func (m *GroupMembershipMutation) AgentIDCleared() bool { - _, ok := m.clearedFields[groupmembership.FieldAgentID] - return ok -} - -// ResetAgentID resets all changes to the "agent_id" field. -func (m *GroupMembershipMutation) ResetAgentID() { - m.agent = nil - delete(m.clearedFields, groupmembership.FieldAgentID) -} - -// ClearGroup clears the "group" edge to the Group entity. -func (m *GroupMembershipMutation) ClearGroup() { - m.clearedgroup = true - m.clearedFields[groupmembership.FieldGroupID] = struct{}{} -} - -// GroupCleared reports if the "group" edge to the Group entity was cleared. -func (m *GroupMembershipMutation) GroupCleared() bool { - return m.clearedgroup -} - -// GroupIDs returns the "group" edge IDs in the mutation. -// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use -// GroupID instead. It exists only for internal usage by the builders. -func (m *GroupMembershipMutation) GroupIDs() (ids []uuid.UUID) { - if id := m.group; id != nil { - ids = append(ids, *id) - } - return -} - -// ResetGroup resets all changes to the "group" edge. -func (m *GroupMembershipMutation) ResetGroup() { - m.group = nil - m.clearedgroup = false -} - -// ClearUser clears the "user" edge to the User entity. -func (m *GroupMembershipMutation) ClearUser() { - m.cleareduser = true - m.clearedFields[groupmembership.FieldUserID] = struct{}{} -} - -// UserCleared reports if the "user" edge to the User entity was cleared. -func (m *GroupMembershipMutation) UserCleared() bool { - return m.UserIDCleared() || m.cleareduser -} - -// UserIDs returns the "user" edge IDs in the mutation. -// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use -// UserID instead. It exists only for internal usage by the builders. -func (m *GroupMembershipMutation) UserIDs() (ids []uuid.UUID) { - if id := m.user; id != nil { - ids = append(ids, *id) + if err != nil { + return v, fmt.Errorf("querying old value for OldHarness: %w", err) } - return -} - -// ResetUser resets all changes to the "user" edge. -func (m *GroupMembershipMutation) ResetUser() { - m.user = nil - m.cleareduser = false + return oldValue.Harness, nil } -// ClearAgent clears the "agent" edge to the Agent entity. -func (m *GroupMembershipMutation) ClearAgent() { - m.clearedagent = true - m.clearedFields[groupmembership.FieldAgentID] = struct{}{} +// ResetHarness resets all changes to the "harness" field. +func (m *TemplateMutation) ResetHarness() { + m.harness = nil } -// AgentCleared reports if the "agent" edge to the Agent entity was cleared. -func (m *GroupMembershipMutation) AgentCleared() bool { - return m.AgentIDCleared() || m.clearedagent +// SetDefaultHarnessConfig sets the "default_harness_config" field. +func (m *TemplateMutation) SetDefaultHarnessConfig(s string) { + m.default_harness_config = &s } -// AgentIDs returns the "agent" edge IDs in the mutation. -// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use -// AgentID instead. It exists only for internal usage by the builders. -func (m *GroupMembershipMutation) AgentIDs() (ids []uuid.UUID) { - if id := m.agent; id != nil { - ids = append(ids, *id) +// DefaultHarnessConfig returns the value of the "default_harness_config" field in the mutation. +func (m *TemplateMutation) DefaultHarnessConfig() (r string, exists bool) { + v := m.default_harness_config + if v == nil { + return } - return + return *v, true } -// ResetAgent resets all changes to the "agent" edge. -func (m *GroupMembershipMutation) ResetAgent() { - m.agent = nil - m.clearedagent = false +// OldDefaultHarnessConfig returns the old "default_harness_config" field's value of the Template entity. +// If the Template object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *TemplateMutation) OldDefaultHarnessConfig(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldDefaultHarnessConfig is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldDefaultHarnessConfig requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldDefaultHarnessConfig: %w", err) + } + return oldValue.DefaultHarnessConfig, nil } -// Where appends a list predicates to the GroupMembershipMutation builder. -func (m *GroupMembershipMutation) Where(ps ...predicate.GroupMembership) { - m.predicates = append(m.predicates, ps...) +// ClearDefaultHarnessConfig clears the value of the "default_harness_config" field. +func (m *TemplateMutation) ClearDefaultHarnessConfig() { + m.default_harness_config = nil + m.clearedFields[template.FieldDefaultHarnessConfig] = struct{}{} } -// WhereP appends storage-level predicates to the GroupMembershipMutation builder. Using this method, -// users can use type-assertion to append predicates that do not depend on any generated package. -func (m *GroupMembershipMutation) WhereP(ps ...func(*sql.Selector)) { - p := make([]predicate.GroupMembership, len(ps)) - for i := range ps { - p[i] = ps[i] - } - m.Where(p...) +// DefaultHarnessConfigCleared returns if the "default_harness_config" field was cleared in this mutation. +func (m *TemplateMutation) DefaultHarnessConfigCleared() bool { + _, ok := m.clearedFields[template.FieldDefaultHarnessConfig] + return ok } -// Op returns the operation name. -func (m *GroupMembershipMutation) Op() Op { - return m.op +// ResetDefaultHarnessConfig resets all changes to the "default_harness_config" field. +func (m *TemplateMutation) ResetDefaultHarnessConfig() { + m.default_harness_config = nil + delete(m.clearedFields, template.FieldDefaultHarnessConfig) } -// SetOp allows setting the mutation operation. -func (m *GroupMembershipMutation) SetOp(op Op) { - m.op = op +// SetImage sets the "image" field. +func (m *TemplateMutation) SetImage(s string) { + m.image = &s } -// Type returns the node type of this mutation (GroupMembership). -func (m *GroupMembershipMutation) Type() string { - return m.typ +// Image returns the value of the "image" field in the mutation. +func (m *TemplateMutation) Image() (r string, exists bool) { + v := m.image + if v == nil { + return + } + return *v, true } -// Fields returns all fields that were changed during this mutation. Note that in -// order to get all numeric fields that were incremented/decremented, call -// AddedFields(). -func (m *GroupMembershipMutation) Fields() []string { - fields := make([]string, 0, 6) - if m.role != nil { - fields = append(fields, groupmembership.FieldRole) - } - if m.added_by != nil { - fields = append(fields, groupmembership.FieldAddedBy) - } - if m.added_at != nil { - fields = append(fields, groupmembership.FieldAddedAt) - } - if m.group != nil { - fields = append(fields, groupmembership.FieldGroupID) - } - if m.user != nil { - fields = append(fields, groupmembership.FieldUserID) +// OldImage returns the old "image" field's value of the Template entity. +// If the Template object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *TemplateMutation) OldImage(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldImage is only allowed on UpdateOne operations") } - if m.agent != nil { - fields = append(fields, groupmembership.FieldAgentID) + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldImage requires an ID field in the mutation") } - return fields -} - -// Field returns the value of a field with the given name. The second boolean -// return value indicates that this field was not set, or was not defined in the -// schema. -func (m *GroupMembershipMutation) Field(name string) (ent.Value, bool) { - switch name { - case groupmembership.FieldRole: - return m.Role() - case groupmembership.FieldAddedBy: - return m.AddedBy() - case groupmembership.FieldAddedAt: - return m.AddedAt() - case groupmembership.FieldGroupID: - return m.GroupID() - case groupmembership.FieldUserID: - return m.UserID() - case groupmembership.FieldAgentID: - return m.AgentID() + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldImage: %w", err) } - return nil, false + return oldValue.Image, nil } -// OldField returns the old value of the field from the database. An error is -// returned if the mutation operation is not UpdateOne, or the query to the -// database failed. -func (m *GroupMembershipMutation) OldField(ctx context.Context, name string) (ent.Value, error) { - switch name { - case groupmembership.FieldRole: - return m.OldRole(ctx) - case groupmembership.FieldAddedBy: - return m.OldAddedBy(ctx) - case groupmembership.FieldAddedAt: - return m.OldAddedAt(ctx) - case groupmembership.FieldGroupID: - return m.OldGroupID(ctx) - case groupmembership.FieldUserID: - return m.OldUserID(ctx) - case groupmembership.FieldAgentID: - return m.OldAgentID(ctx) - } - return nil, fmt.Errorf("unknown GroupMembership field %s", name) +// ClearImage clears the value of the "image" field. +func (m *TemplateMutation) ClearImage() { + m.image = nil + m.clearedFields[template.FieldImage] = struct{}{} } -// SetField sets the value of a field with the given name. It returns an error if -// the field is not defined in the schema, or if the type mismatched the field -// type. -func (m *GroupMembershipMutation) SetField(name string, value ent.Value) error { - switch name { - case groupmembership.FieldRole: - v, ok := value.(groupmembership.Role) - if !ok { - return fmt.Errorf("unexpected type %T for field %s", value, name) - } - m.SetRole(v) - return nil - case groupmembership.FieldAddedBy: - v, ok := value.(string) - if !ok { - return fmt.Errorf("unexpected type %T for field %s", value, name) - } - m.SetAddedBy(v) - return nil - case groupmembership.FieldAddedAt: - v, ok := value.(time.Time) - if !ok { - return fmt.Errorf("unexpected type %T for field %s", value, name) - } - m.SetAddedAt(v) - return nil - case groupmembership.FieldGroupID: - v, ok := value.(uuid.UUID) - if !ok { - return fmt.Errorf("unexpected type %T for field %s", value, name) - } - m.SetGroupID(v) - return nil - case groupmembership.FieldUserID: - v, ok := value.(uuid.UUID) - if !ok { - return fmt.Errorf("unexpected type %T for field %s", value, name) - } - m.SetUserID(v) - return nil - case groupmembership.FieldAgentID: - v, ok := value.(uuid.UUID) - if !ok { - return fmt.Errorf("unexpected type %T for field %s", value, name) - } - m.SetAgentID(v) - return nil - } - return fmt.Errorf("unknown GroupMembership field %s", name) +// ImageCleared returns if the "image" field was cleared in this mutation. +func (m *TemplateMutation) ImageCleared() bool { + _, ok := m.clearedFields[template.FieldImage] + return ok } -// AddedFields returns all numeric fields that were incremented/decremented during -// this mutation. -func (m *GroupMembershipMutation) AddedFields() []string { - return nil +// ResetImage resets all changes to the "image" field. +func (m *TemplateMutation) ResetImage() { + m.image = nil + delete(m.clearedFields, template.FieldImage) } -// AddedField returns the numeric value that was incremented/decremented on a field -// with the given name. The second boolean return value indicates that this field -// was not set, or was not defined in the schema. -func (m *GroupMembershipMutation) AddedField(name string) (ent.Value, bool) { - return nil, false +// SetConfig sets the "config" field. +func (m *TemplateMutation) SetConfig(s string) { + m._config = &s } -// AddField adds the value to the field with the given name. It returns an error if -// the field is not defined in the schema, or if the type mismatched the field -// type. -func (m *GroupMembershipMutation) AddField(name string, value ent.Value) error { - switch name { +// Config returns the value of the "config" field in the mutation. +func (m *TemplateMutation) Config() (r string, exists bool) { + v := m._config + if v == nil { + return } - return fmt.Errorf("unknown GroupMembership numeric field %s", name) + return *v, true } -// ClearedFields returns all nullable fields that were cleared during this -// mutation. -func (m *GroupMembershipMutation) ClearedFields() []string { - var fields []string - if m.FieldCleared(groupmembership.FieldAddedBy) { - fields = append(fields, groupmembership.FieldAddedBy) +// OldConfig returns the old "config" field's value of the Template entity. +// If the Template object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *TemplateMutation) OldConfig(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldConfig is only allowed on UpdateOne operations") } - if m.FieldCleared(groupmembership.FieldUserID) { - fields = append(fields, groupmembership.FieldUserID) + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldConfig requires an ID field in the mutation") } - if m.FieldCleared(groupmembership.FieldAgentID) { - fields = append(fields, groupmembership.FieldAgentID) + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldConfig: %w", err) } - return fields + return oldValue.Config, nil } -// FieldCleared returns a boolean indicating if a field with the given name was -// cleared in this mutation. -func (m *GroupMembershipMutation) FieldCleared(name string) bool { - _, ok := m.clearedFields[name] - return ok +// ClearConfig clears the value of the "config" field. +func (m *TemplateMutation) ClearConfig() { + m._config = nil + m.clearedFields[template.FieldConfig] = struct{}{} } -// ClearField clears the value of the field with the given name. It returns an -// error if the field is not defined in the schema. -func (m *GroupMembershipMutation) ClearField(name string) error { - switch name { - case groupmembership.FieldAddedBy: - m.ClearAddedBy() - return nil - case groupmembership.FieldUserID: - m.ClearUserID() - return nil - case groupmembership.FieldAgentID: - m.ClearAgentID() - return nil - } - return fmt.Errorf("unknown GroupMembership nullable field %s", name) +// ConfigCleared returns if the "config" field was cleared in this mutation. +func (m *TemplateMutation) ConfigCleared() bool { + _, ok := m.clearedFields[template.FieldConfig] + return ok } - -// ResetField resets all changes in the mutation for the field with the given name. -// It returns an error if the field is not defined in the schema. -func (m *GroupMembershipMutation) ResetField(name string) error { - switch name { - case groupmembership.FieldRole: - m.ResetRole() - return nil - case groupmembership.FieldAddedBy: - m.ResetAddedBy() - return nil - case groupmembership.FieldAddedAt: - m.ResetAddedAt() - return nil - case groupmembership.FieldGroupID: - m.ResetGroupID() - return nil - case groupmembership.FieldUserID: - m.ResetUserID() - return nil - case groupmembership.FieldAgentID: - m.ResetAgentID() - return nil + +// ResetConfig resets all changes to the "config" field. +func (m *TemplateMutation) ResetConfig() { + m._config = nil + delete(m.clearedFields, template.FieldConfig) +} + +// SetContentHash sets the "content_hash" field. +func (m *TemplateMutation) SetContentHash(s string) { + m.content_hash = &s +} + +// ContentHash returns the value of the "content_hash" field in the mutation. +func (m *TemplateMutation) ContentHash() (r string, exists bool) { + v := m.content_hash + if v == nil { + return } - return fmt.Errorf("unknown GroupMembership field %s", name) + return *v, true } -// AddedEdges returns all edge names that were set/added in this mutation. -func (m *GroupMembershipMutation) AddedEdges() []string { - edges := make([]string, 0, 3) - if m.group != nil { - edges = append(edges, groupmembership.EdgeGroup) +// OldContentHash returns the old "content_hash" field's value of the Template entity. +// If the Template object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *TemplateMutation) OldContentHash(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldContentHash is only allowed on UpdateOne operations") } - if m.user != nil { - edges = append(edges, groupmembership.EdgeUser) + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldContentHash requires an ID field in the mutation") } - if m.agent != nil { - edges = append(edges, groupmembership.EdgeAgent) + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldContentHash: %w", err) } - return edges + return oldValue.ContentHash, nil } -// AddedIDs returns all IDs (to other nodes) that were added for the given edge -// name in this mutation. -func (m *GroupMembershipMutation) AddedIDs(name string) []ent.Value { - switch name { - case groupmembership.EdgeGroup: - if id := m.group; id != nil { - return []ent.Value{*id} - } - case groupmembership.EdgeUser: - if id := m.user; id != nil { - return []ent.Value{*id} - } - case groupmembership.EdgeAgent: - if id := m.agent; id != nil { - return []ent.Value{*id} - } - } - return nil +// ClearContentHash clears the value of the "content_hash" field. +func (m *TemplateMutation) ClearContentHash() { + m.content_hash = nil + m.clearedFields[template.FieldContentHash] = struct{}{} } -// RemovedEdges returns all edge names that were removed in this mutation. -func (m *GroupMembershipMutation) RemovedEdges() []string { - edges := make([]string, 0, 3) - return edges +// ContentHashCleared returns if the "content_hash" field was cleared in this mutation. +func (m *TemplateMutation) ContentHashCleared() bool { + _, ok := m.clearedFields[template.FieldContentHash] + return ok } -// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with -// the given name in this mutation. -func (m *GroupMembershipMutation) RemovedIDs(name string) []ent.Value { - return nil +// ResetContentHash resets all changes to the "content_hash" field. +func (m *TemplateMutation) ResetContentHash() { + m.content_hash = nil + delete(m.clearedFields, template.FieldContentHash) } -// ClearedEdges returns all edge names that were cleared in this mutation. -func (m *GroupMembershipMutation) ClearedEdges() []string { - edges := make([]string, 0, 3) - if m.clearedgroup { - edges = append(edges, groupmembership.EdgeGroup) +// SetScope sets the "scope" field. +func (m *TemplateMutation) SetScope(s string) { + m.scope = &s +} + +// Scope returns the value of the "scope" field in the mutation. +func (m *TemplateMutation) Scope() (r string, exists bool) { + v := m.scope + if v == nil { + return } - if m.cleareduser { - edges = append(edges, groupmembership.EdgeUser) + return *v, true +} + +// OldScope returns the old "scope" field's value of the Template entity. +// If the Template object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *TemplateMutation) OldScope(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldScope is only allowed on UpdateOne operations") } - if m.clearedagent { - edges = append(edges, groupmembership.EdgeAgent) + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldScope requires an ID field in the mutation") } - return edges + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldScope: %w", err) + } + return oldValue.Scope, nil } -// EdgeCleared returns a boolean which indicates if the edge with the given name -// was cleared in this mutation. -func (m *GroupMembershipMutation) EdgeCleared(name string) bool { - switch name { - case groupmembership.EdgeGroup: - return m.clearedgroup - case groupmembership.EdgeUser: - return m.cleareduser - case groupmembership.EdgeAgent: - return m.clearedagent - } - return false +// ResetScope resets all changes to the "scope" field. +func (m *TemplateMutation) ResetScope() { + m.scope = nil } -// ClearEdge clears the value of the edge with the given name. It returns an error -// if that edge is not defined in the schema. -func (m *GroupMembershipMutation) ClearEdge(name string) error { - switch name { - case groupmembership.EdgeGroup: - m.ClearGroup() - return nil - case groupmembership.EdgeUser: - m.ClearUser() - return nil - case groupmembership.EdgeAgent: - m.ClearAgent() - return nil +// SetScopeID sets the "scope_id" field. +func (m *TemplateMutation) SetScopeID(s string) { + m.scope_id = &s +} + +// ScopeID returns the value of the "scope_id" field in the mutation. +func (m *TemplateMutation) ScopeID() (r string, exists bool) { + v := m.scope_id + if v == nil { + return } - return fmt.Errorf("unknown GroupMembership unique edge %s", name) + return *v, true } -// ResetEdge resets all changes to the edge with the given name in this mutation. -// It returns an error if the edge is not defined in the schema. -func (m *GroupMembershipMutation) ResetEdge(name string) error { - switch name { - case groupmembership.EdgeGroup: - m.ResetGroup() - return nil - case groupmembership.EdgeUser: - m.ResetUser() - return nil - case groupmembership.EdgeAgent: - m.ResetAgent() - return nil +// OldScopeID returns the old "scope_id" field's value of the Template entity. +// If the Template object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *TemplateMutation) OldScopeID(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldScopeID is only allowed on UpdateOne operations") } - return fmt.Errorf("unknown GroupMembership edge %s", name) + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldScopeID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldScopeID: %w", err) + } + return oldValue.ScopeID, nil } -// PolicyBindingMutation represents an operation that mutates the PolicyBinding nodes in the graph. -type PolicyBindingMutation struct { - config - op Op - typ string - id *uuid.UUID - principal_type *policybinding.PrincipalType - created *time.Time - created_by *string - clearedFields map[string]struct{} - policy *uuid.UUID - clearedpolicy bool - user *uuid.UUID - cleareduser bool - group *uuid.UUID - clearedgroup bool - agent *uuid.UUID - clearedagent bool - done bool - oldValue func(context.Context) (*PolicyBinding, error) - predicates []predicate.PolicyBinding +// ClearScopeID clears the value of the "scope_id" field. +func (m *TemplateMutation) ClearScopeID() { + m.scope_id = nil + m.clearedFields[template.FieldScopeID] = struct{}{} } -var _ ent.Mutation = (*PolicyBindingMutation)(nil) +// ScopeIDCleared returns if the "scope_id" field was cleared in this mutation. +func (m *TemplateMutation) ScopeIDCleared() bool { + _, ok := m.clearedFields[template.FieldScopeID] + return ok +} -// policybindingOption allows management of the mutation configuration using functional options. -type policybindingOption func(*PolicyBindingMutation) +// ResetScopeID resets all changes to the "scope_id" field. +func (m *TemplateMutation) ResetScopeID() { + m.scope_id = nil + delete(m.clearedFields, template.FieldScopeID) +} -// newPolicyBindingMutation creates new mutation for the PolicyBinding entity. -func newPolicyBindingMutation(c config, op Op, opts ...policybindingOption) *PolicyBindingMutation { - m := &PolicyBindingMutation{ - config: c, - op: op, - typ: TypePolicyBinding, - clearedFields: make(map[string]struct{}), - } - for _, opt := range opts { - opt(m) - } - return m +// SetProjectID sets the "project_id" field. +func (m *TemplateMutation) SetProjectID(s string) { + m.project_id = &s } -// withPolicyBindingID sets the ID field of the mutation. -func withPolicyBindingID(id uuid.UUID) policybindingOption { - return func(m *PolicyBindingMutation) { - var ( - err error - once sync.Once - value *PolicyBinding - ) - m.oldValue = func(ctx context.Context) (*PolicyBinding, error) { - once.Do(func() { - if m.done { - err = errors.New("querying old values post mutation is not allowed") - } else { - value, err = m.Client().PolicyBinding.Get(ctx, id) - } - }) - return value, err - } - m.id = &id +// ProjectID returns the value of the "project_id" field in the mutation. +func (m *TemplateMutation) ProjectID() (r string, exists bool) { + v := m.project_id + if v == nil { + return } + return *v, true } -// withPolicyBinding sets the old PolicyBinding of the mutation. -func withPolicyBinding(node *PolicyBinding) policybindingOption { - return func(m *PolicyBindingMutation) { - m.oldValue = func(context.Context) (*PolicyBinding, error) { - return node, nil - } - m.id = &node.ID +// OldProjectID returns the old "project_id" field's value of the Template entity. +// If the Template object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *TemplateMutation) OldProjectID(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldProjectID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldProjectID requires an ID field in the mutation") } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldProjectID: %w", err) + } + return oldValue.ProjectID, nil } -// Client returns a new `ent.Client` from the mutation. If the mutation was -// executed in a transaction (ent.Tx), a transactional client is returned. -func (m PolicyBindingMutation) Client() *Client { - client := &Client{config: m.config} - client.init() - return client +// ClearProjectID clears the value of the "project_id" field. +func (m *TemplateMutation) ClearProjectID() { + m.project_id = nil + m.clearedFields[template.FieldProjectID] = struct{}{} } -// Tx returns an `ent.Tx` for mutations that were executed in transactions; -// it returns an error otherwise. -func (m PolicyBindingMutation) Tx() (*Tx, error) { - if _, ok := m.driver.(*txDriver); !ok { - return nil, errors.New("ent: mutation is not running in a transaction") +// ProjectIDCleared returns if the "project_id" field was cleared in this mutation. +func (m *TemplateMutation) ProjectIDCleared() bool { + _, ok := m.clearedFields[template.FieldProjectID] + return ok +} + +// ResetProjectID resets all changes to the "project_id" field. +func (m *TemplateMutation) ResetProjectID() { + m.project_id = nil + delete(m.clearedFields, template.FieldProjectID) +} + +// SetStorageURI sets the "storage_uri" field. +func (m *TemplateMutation) SetStorageURI(s string) { + m.storage_uri = &s +} + +// StorageURI returns the value of the "storage_uri" field in the mutation. +func (m *TemplateMutation) StorageURI() (r string, exists bool) { + v := m.storage_uri + if v == nil { + return + } + return *v, true +} + +// OldStorageURI returns the old "storage_uri" field's value of the Template entity. +// If the Template object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *TemplateMutation) OldStorageURI(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldStorageURI is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldStorageURI requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldStorageURI: %w", err) } - tx := &Tx{config: m.config} - tx.init() - return tx, nil + return oldValue.StorageURI, nil } -// SetID sets the value of the id field. Note that this -// operation is only accepted on creation of PolicyBinding entities. -func (m *PolicyBindingMutation) SetID(id uuid.UUID) { - m.id = &id +// ClearStorageURI clears the value of the "storage_uri" field. +func (m *TemplateMutation) ClearStorageURI() { + m.storage_uri = nil + m.clearedFields[template.FieldStorageURI] = struct{}{} } -// ID returns the ID value in the mutation. Note that the ID is only available -// if it was provided to the builder or after it was returned from the database. -func (m *PolicyBindingMutation) ID() (id uuid.UUID, exists bool) { - if m.id == nil { - return - } - return *m.id, true +// StorageURICleared returns if the "storage_uri" field was cleared in this mutation. +func (m *TemplateMutation) StorageURICleared() bool { + _, ok := m.clearedFields[template.FieldStorageURI] + return ok } -// IDs queries the database and returns the entity ids that match the mutation's predicate. -// That means, if the mutation is applied within a transaction with an isolation level such -// as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated -// or updated by the mutation. -func (m *PolicyBindingMutation) IDs(ctx context.Context) ([]uuid.UUID, error) { - switch { - case m.op.Is(OpUpdateOne | OpDeleteOne): - id, exists := m.ID() - if exists { - return []uuid.UUID{id}, nil - } - fallthrough - case m.op.Is(OpUpdate | OpDelete): - return m.Client().PolicyBinding.Query().Where(m.predicates...).IDs(ctx) - default: - return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) - } +// ResetStorageURI resets all changes to the "storage_uri" field. +func (m *TemplateMutation) ResetStorageURI() { + m.storage_uri = nil + delete(m.clearedFields, template.FieldStorageURI) } -// SetPrincipalType sets the "principal_type" field. -func (m *PolicyBindingMutation) SetPrincipalType(pt policybinding.PrincipalType) { - m.principal_type = &pt +// SetStorageBucket sets the "storage_bucket" field. +func (m *TemplateMutation) SetStorageBucket(s string) { + m.storage_bucket = &s } -// PrincipalType returns the value of the "principal_type" field in the mutation. -func (m *PolicyBindingMutation) PrincipalType() (r policybinding.PrincipalType, exists bool) { - v := m.principal_type +// StorageBucket returns the value of the "storage_bucket" field in the mutation. +func (m *TemplateMutation) StorageBucket() (r string, exists bool) { + v := m.storage_bucket if v == nil { return } return *v, true } -// OldPrincipalType returns the old "principal_type" field's value of the PolicyBinding entity. -// If the PolicyBinding object wasn't provided to the builder, the object is fetched from the database. +// OldStorageBucket returns the old "storage_bucket" field's value of the Template entity. +// If the Template object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *PolicyBindingMutation) OldPrincipalType(ctx context.Context) (v policybinding.PrincipalType, err error) { +func (m *TemplateMutation) OldStorageBucket(ctx context.Context) (v string, err error) { if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldPrincipalType is only allowed on UpdateOne operations") + return v, errors.New("OldStorageBucket is only allowed on UpdateOne operations") } if m.id == nil || m.oldValue == nil { - return v, errors.New("OldPrincipalType requires an ID field in the mutation") + return v, errors.New("OldStorageBucket requires an ID field in the mutation") } oldValue, err := m.oldValue(ctx) if err != nil { - return v, fmt.Errorf("querying old value for OldPrincipalType: %w", err) + return v, fmt.Errorf("querying old value for OldStorageBucket: %w", err) } - return oldValue.PrincipalType, nil + return oldValue.StorageBucket, nil } -// ResetPrincipalType resets all changes to the "principal_type" field. -func (m *PolicyBindingMutation) ResetPrincipalType() { - m.principal_type = nil +// ClearStorageBucket clears the value of the "storage_bucket" field. +func (m *TemplateMutation) ClearStorageBucket() { + m.storage_bucket = nil + m.clearedFields[template.FieldStorageBucket] = struct{}{} } -// SetCreated sets the "created" field. -func (m *PolicyBindingMutation) SetCreated(t time.Time) { - m.created = &t +// StorageBucketCleared returns if the "storage_bucket" field was cleared in this mutation. +func (m *TemplateMutation) StorageBucketCleared() bool { + _, ok := m.clearedFields[template.FieldStorageBucket] + return ok } -// Created returns the value of the "created" field in the mutation. -func (m *PolicyBindingMutation) Created() (r time.Time, exists bool) { - v := m.created +// ResetStorageBucket resets all changes to the "storage_bucket" field. +func (m *TemplateMutation) ResetStorageBucket() { + m.storage_bucket = nil + delete(m.clearedFields, template.FieldStorageBucket) +} + +// SetStoragePath sets the "storage_path" field. +func (m *TemplateMutation) SetStoragePath(s string) { + m.storage_path = &s +} + +// StoragePath returns the value of the "storage_path" field in the mutation. +func (m *TemplateMutation) StoragePath() (r string, exists bool) { + v := m.storage_path if v == nil { return } return *v, true } -// OldCreated returns the old "created" field's value of the PolicyBinding entity. -// If the PolicyBinding object wasn't provided to the builder, the object is fetched from the database. +// OldStoragePath returns the old "storage_path" field's value of the Template entity. +// If the Template object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *PolicyBindingMutation) OldCreated(ctx context.Context) (v time.Time, err error) { +func (m *TemplateMutation) OldStoragePath(ctx context.Context) (v string, err error) { if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldCreated is only allowed on UpdateOne operations") + return v, errors.New("OldStoragePath is only allowed on UpdateOne operations") } if m.id == nil || m.oldValue == nil { - return v, errors.New("OldCreated requires an ID field in the mutation") + return v, errors.New("OldStoragePath requires an ID field in the mutation") } oldValue, err := m.oldValue(ctx) if err != nil { - return v, fmt.Errorf("querying old value for OldCreated: %w", err) + return v, fmt.Errorf("querying old value for OldStoragePath: %w", err) } - return oldValue.Created, nil + return oldValue.StoragePath, nil } -// ResetCreated resets all changes to the "created" field. -func (m *PolicyBindingMutation) ResetCreated() { - m.created = nil +// ClearStoragePath clears the value of the "storage_path" field. +func (m *TemplateMutation) ClearStoragePath() { + m.storage_path = nil + m.clearedFields[template.FieldStoragePath] = struct{}{} } -// SetCreatedBy sets the "created_by" field. -func (m *PolicyBindingMutation) SetCreatedBy(s string) { - m.created_by = &s +// StoragePathCleared returns if the "storage_path" field was cleared in this mutation. +func (m *TemplateMutation) StoragePathCleared() bool { + _, ok := m.clearedFields[template.FieldStoragePath] + return ok } -// CreatedBy returns the value of the "created_by" field in the mutation. -func (m *PolicyBindingMutation) CreatedBy() (r string, exists bool) { - v := m.created_by +// ResetStoragePath resets all changes to the "storage_path" field. +func (m *TemplateMutation) ResetStoragePath() { + m.storage_path = nil + delete(m.clearedFields, template.FieldStoragePath) +} + +// SetFiles sets the "files" field. +func (m *TemplateMutation) SetFiles(s string) { + m.files = &s +} + +// Files returns the value of the "files" field in the mutation. +func (m *TemplateMutation) Files() (r string, exists bool) { + v := m.files if v == nil { return } return *v, true } -// OldCreatedBy returns the old "created_by" field's value of the PolicyBinding entity. -// If the PolicyBinding object wasn't provided to the builder, the object is fetched from the database. +// OldFiles returns the old "files" field's value of the Template entity. +// If the Template object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *PolicyBindingMutation) OldCreatedBy(ctx context.Context) (v string, err error) { +func (m *TemplateMutation) OldFiles(ctx context.Context) (v string, err error) { if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldCreatedBy is only allowed on UpdateOne operations") + return v, errors.New("OldFiles is only allowed on UpdateOne operations") } if m.id == nil || m.oldValue == nil { - return v, errors.New("OldCreatedBy requires an ID field in the mutation") + return v, errors.New("OldFiles requires an ID field in the mutation") } oldValue, err := m.oldValue(ctx) if err != nil { - return v, fmt.Errorf("querying old value for OldCreatedBy: %w", err) + return v, fmt.Errorf("querying old value for OldFiles: %w", err) } - return oldValue.CreatedBy, nil + return oldValue.Files, nil } -// ClearCreatedBy clears the value of the "created_by" field. -func (m *PolicyBindingMutation) ClearCreatedBy() { - m.created_by = nil - m.clearedFields[policybinding.FieldCreatedBy] = struct{}{} +// ClearFiles clears the value of the "files" field. +func (m *TemplateMutation) ClearFiles() { + m.files = nil + m.clearedFields[template.FieldFiles] = struct{}{} } -// CreatedByCleared returns if the "created_by" field was cleared in this mutation. -func (m *PolicyBindingMutation) CreatedByCleared() bool { - _, ok := m.clearedFields[policybinding.FieldCreatedBy] +// FilesCleared returns if the "files" field was cleared in this mutation. +func (m *TemplateMutation) FilesCleared() bool { + _, ok := m.clearedFields[template.FieldFiles] return ok } -// ResetCreatedBy resets all changes to the "created_by" field. -func (m *PolicyBindingMutation) ResetCreatedBy() { - m.created_by = nil - delete(m.clearedFields, policybinding.FieldCreatedBy) +// ResetFiles resets all changes to the "files" field. +func (m *TemplateMutation) ResetFiles() { + m.files = nil + delete(m.clearedFields, template.FieldFiles) } -// SetPolicyID sets the "policy_id" field. -func (m *PolicyBindingMutation) SetPolicyID(u uuid.UUID) { - m.policy = &u +// SetBaseTemplate sets the "base_template" field. +func (m *TemplateMutation) SetBaseTemplate(s string) { + m.base_template = &s } -// PolicyID returns the value of the "policy_id" field in the mutation. -func (m *PolicyBindingMutation) PolicyID() (r uuid.UUID, exists bool) { - v := m.policy +// BaseTemplate returns the value of the "base_template" field in the mutation. +func (m *TemplateMutation) BaseTemplate() (r string, exists bool) { + v := m.base_template if v == nil { return } return *v, true } -// OldPolicyID returns the old "policy_id" field's value of the PolicyBinding entity. -// If the PolicyBinding object wasn't provided to the builder, the object is fetched from the database. +// OldBaseTemplate returns the old "base_template" field's value of the Template entity. +// If the Template object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *PolicyBindingMutation) OldPolicyID(ctx context.Context) (v *uuid.UUID, err error) { +func (m *TemplateMutation) OldBaseTemplate(ctx context.Context) (v string, err error) { if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldPolicyID is only allowed on UpdateOne operations") + return v, errors.New("OldBaseTemplate is only allowed on UpdateOne operations") } if m.id == nil || m.oldValue == nil { - return v, errors.New("OldPolicyID requires an ID field in the mutation") + return v, errors.New("OldBaseTemplate requires an ID field in the mutation") } oldValue, err := m.oldValue(ctx) if err != nil { - return v, fmt.Errorf("querying old value for OldPolicyID: %w", err) + return v, fmt.Errorf("querying old value for OldBaseTemplate: %w", err) } - return oldValue.PolicyID, nil + return oldValue.BaseTemplate, nil } -// ClearPolicyID clears the value of the "policy_id" field. -func (m *PolicyBindingMutation) ClearPolicyID() { - m.policy = nil - m.clearedFields[policybinding.FieldPolicyID] = struct{}{} +// ClearBaseTemplate clears the value of the "base_template" field. +func (m *TemplateMutation) ClearBaseTemplate() { + m.base_template = nil + m.clearedFields[template.FieldBaseTemplate] = struct{}{} } -// PolicyIDCleared returns if the "policy_id" field was cleared in this mutation. -func (m *PolicyBindingMutation) PolicyIDCleared() bool { - _, ok := m.clearedFields[policybinding.FieldPolicyID] +// BaseTemplateCleared returns if the "base_template" field was cleared in this mutation. +func (m *TemplateMutation) BaseTemplateCleared() bool { + _, ok := m.clearedFields[template.FieldBaseTemplate] return ok } -// ResetPolicyID resets all changes to the "policy_id" field. -func (m *PolicyBindingMutation) ResetPolicyID() { - m.policy = nil - delete(m.clearedFields, policybinding.FieldPolicyID) +// ResetBaseTemplate resets all changes to the "base_template" field. +func (m *TemplateMutation) ResetBaseTemplate() { + m.base_template = nil + delete(m.clearedFields, template.FieldBaseTemplate) } -// SetUserID sets the "user_id" field. -func (m *PolicyBindingMutation) SetUserID(u uuid.UUID) { - m.user = &u +// SetLocked sets the "locked" field. +func (m *TemplateMutation) SetLocked(b bool) { + m.locked = &b } -// UserID returns the value of the "user_id" field in the mutation. -func (m *PolicyBindingMutation) UserID() (r uuid.UUID, exists bool) { - v := m.user +// Locked returns the value of the "locked" field in the mutation. +func (m *TemplateMutation) Locked() (r bool, exists bool) { + v := m.locked if v == nil { return } return *v, true } -// OldUserID returns the old "user_id" field's value of the PolicyBinding entity. -// If the PolicyBinding object wasn't provided to the builder, the object is fetched from the database. +// OldLocked returns the old "locked" field's value of the Template entity. +// If the Template object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *PolicyBindingMutation) OldUserID(ctx context.Context) (v *uuid.UUID, err error) { +func (m *TemplateMutation) OldLocked(ctx context.Context) (v bool, err error) { if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldUserID is only allowed on UpdateOne operations") + return v, errors.New("OldLocked is only allowed on UpdateOne operations") } if m.id == nil || m.oldValue == nil { - return v, errors.New("OldUserID requires an ID field in the mutation") + return v, errors.New("OldLocked requires an ID field in the mutation") } oldValue, err := m.oldValue(ctx) if err != nil { - return v, fmt.Errorf("querying old value for OldUserID: %w", err) + return v, fmt.Errorf("querying old value for OldLocked: %w", err) } - return oldValue.UserID, nil + return oldValue.Locked, nil } -// ClearUserID clears the value of the "user_id" field. -func (m *PolicyBindingMutation) ClearUserID() { - m.user = nil - m.clearedFields[policybinding.FieldUserID] = struct{}{} +// ResetLocked resets all changes to the "locked" field. +func (m *TemplateMutation) ResetLocked() { + m.locked = nil } -// UserIDCleared returns if the "user_id" field was cleared in this mutation. -func (m *PolicyBindingMutation) UserIDCleared() bool { - _, ok := m.clearedFields[policybinding.FieldUserID] - return ok +// SetStatus sets the "status" field. +func (m *TemplateMutation) SetStatus(t template.Status) { + m.status = &t } -// ResetUserID resets all changes to the "user_id" field. -func (m *PolicyBindingMutation) ResetUserID() { - m.user = nil - delete(m.clearedFields, policybinding.FieldUserID) +// Status returns the value of the "status" field in the mutation. +func (m *TemplateMutation) Status() (r template.Status, exists bool) { + v := m.status + if v == nil { + return + } + return *v, true } -// SetGroupID sets the "group_id" field. -func (m *PolicyBindingMutation) SetGroupID(u uuid.UUID) { - m.group = &u +// OldStatus returns the old "status" field's value of the Template entity. +// If the Template object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *TemplateMutation) OldStatus(ctx context.Context) (v template.Status, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldStatus is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldStatus requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldStatus: %w", err) + } + return oldValue.Status, nil } -// GroupID returns the value of the "group_id" field in the mutation. -func (m *PolicyBindingMutation) GroupID() (r uuid.UUID, exists bool) { - v := m.group +// ResetStatus resets all changes to the "status" field. +func (m *TemplateMutation) ResetStatus() { + m.status = nil +} + +// SetOwnerID sets the "owner_id" field. +func (m *TemplateMutation) SetOwnerID(s string) { + m.owner_id = &s +} + +// OwnerID returns the value of the "owner_id" field in the mutation. +func (m *TemplateMutation) OwnerID() (r string, exists bool) { + v := m.owner_id if v == nil { return } return *v, true } -// OldGroupID returns the old "group_id" field's value of the PolicyBinding entity. -// If the PolicyBinding object wasn't provided to the builder, the object is fetched from the database. +// OldOwnerID returns the old "owner_id" field's value of the Template entity. +// If the Template object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *PolicyBindingMutation) OldGroupID(ctx context.Context) (v *uuid.UUID, err error) { +func (m *TemplateMutation) OldOwnerID(ctx context.Context) (v string, err error) { if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldGroupID is only allowed on UpdateOne operations") + return v, errors.New("OldOwnerID is only allowed on UpdateOne operations") } if m.id == nil || m.oldValue == nil { - return v, errors.New("OldGroupID requires an ID field in the mutation") + return v, errors.New("OldOwnerID requires an ID field in the mutation") } oldValue, err := m.oldValue(ctx) if err != nil { - return v, fmt.Errorf("querying old value for OldGroupID: %w", err) + return v, fmt.Errorf("querying old value for OldOwnerID: %w", err) } - return oldValue.GroupID, nil + return oldValue.OwnerID, nil } -// ClearGroupID clears the value of the "group_id" field. -func (m *PolicyBindingMutation) ClearGroupID() { - m.group = nil - m.clearedFields[policybinding.FieldGroupID] = struct{}{} +// ClearOwnerID clears the value of the "owner_id" field. +func (m *TemplateMutation) ClearOwnerID() { + m.owner_id = nil + m.clearedFields[template.FieldOwnerID] = struct{}{} } -// GroupIDCleared returns if the "group_id" field was cleared in this mutation. -func (m *PolicyBindingMutation) GroupIDCleared() bool { - _, ok := m.clearedFields[policybinding.FieldGroupID] +// OwnerIDCleared returns if the "owner_id" field was cleared in this mutation. +func (m *TemplateMutation) OwnerIDCleared() bool { + _, ok := m.clearedFields[template.FieldOwnerID] return ok } -// ResetGroupID resets all changes to the "group_id" field. -func (m *PolicyBindingMutation) ResetGroupID() { - m.group = nil - delete(m.clearedFields, policybinding.FieldGroupID) +// ResetOwnerID resets all changes to the "owner_id" field. +func (m *TemplateMutation) ResetOwnerID() { + m.owner_id = nil + delete(m.clearedFields, template.FieldOwnerID) } -// SetAgentID sets the "agent_id" field. -func (m *PolicyBindingMutation) SetAgentID(u uuid.UUID) { - m.agent = &u +// SetCreatedBy sets the "created_by" field. +func (m *TemplateMutation) SetCreatedBy(s string) { + m.created_by = &s } -// AgentID returns the value of the "agent_id" field in the mutation. -func (m *PolicyBindingMutation) AgentID() (r uuid.UUID, exists bool) { - v := m.agent +// CreatedBy returns the value of the "created_by" field in the mutation. +func (m *TemplateMutation) CreatedBy() (r string, exists bool) { + v := m.created_by if v == nil { return } return *v, true } -// OldAgentID returns the old "agent_id" field's value of the PolicyBinding entity. -// If the PolicyBinding object wasn't provided to the builder, the object is fetched from the database. +// OldCreatedBy returns the old "created_by" field's value of the Template entity. +// If the Template object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *PolicyBindingMutation) OldAgentID(ctx context.Context) (v *uuid.UUID, err error) { +func (m *TemplateMutation) OldCreatedBy(ctx context.Context) (v string, err error) { if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldAgentID is only allowed on UpdateOne operations") + return v, errors.New("OldCreatedBy is only allowed on UpdateOne operations") } if m.id == nil || m.oldValue == nil { - return v, errors.New("OldAgentID requires an ID field in the mutation") + return v, errors.New("OldCreatedBy requires an ID field in the mutation") } oldValue, err := m.oldValue(ctx) if err != nil { - return v, fmt.Errorf("querying old value for OldAgentID: %w", err) + return v, fmt.Errorf("querying old value for OldCreatedBy: %w", err) } - return oldValue.AgentID, nil + return oldValue.CreatedBy, nil } -// ClearAgentID clears the value of the "agent_id" field. -func (m *PolicyBindingMutation) ClearAgentID() { - m.agent = nil - m.clearedFields[policybinding.FieldAgentID] = struct{}{} +// ClearCreatedBy clears the value of the "created_by" field. +func (m *TemplateMutation) ClearCreatedBy() { + m.created_by = nil + m.clearedFields[template.FieldCreatedBy] = struct{}{} } -// AgentIDCleared returns if the "agent_id" field was cleared in this mutation. -func (m *PolicyBindingMutation) AgentIDCleared() bool { - _, ok := m.clearedFields[policybinding.FieldAgentID] +// CreatedByCleared returns if the "created_by" field was cleared in this mutation. +func (m *TemplateMutation) CreatedByCleared() bool { + _, ok := m.clearedFields[template.FieldCreatedBy] return ok } -// ResetAgentID resets all changes to the "agent_id" field. -func (m *PolicyBindingMutation) ResetAgentID() { - m.agent = nil - delete(m.clearedFields, policybinding.FieldAgentID) +// ResetCreatedBy resets all changes to the "created_by" field. +func (m *TemplateMutation) ResetCreatedBy() { + m.created_by = nil + delete(m.clearedFields, template.FieldCreatedBy) } -// ClearPolicy clears the "policy" edge to the AccessPolicy entity. -func (m *PolicyBindingMutation) ClearPolicy() { - m.clearedpolicy = true - m.clearedFields[policybinding.FieldPolicyID] = struct{}{} +// SetUpdatedBy sets the "updated_by" field. +func (m *TemplateMutation) SetUpdatedBy(s string) { + m.updated_by = &s +} + +// UpdatedBy returns the value of the "updated_by" field in the mutation. +func (m *TemplateMutation) UpdatedBy() (r string, exists bool) { + v := m.updated_by + if v == nil { + return + } + return *v, true } -// PolicyCleared reports if the "policy" edge to the AccessPolicy entity was cleared. -func (m *PolicyBindingMutation) PolicyCleared() bool { - return m.PolicyIDCleared() || m.clearedpolicy +// OldUpdatedBy returns the old "updated_by" field's value of the Template entity. +// If the Template object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *TemplateMutation) OldUpdatedBy(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldUpdatedBy is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldUpdatedBy requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldUpdatedBy: %w", err) + } + return oldValue.UpdatedBy, nil } -// PolicyIDs returns the "policy" edge IDs in the mutation. -// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use -// PolicyID instead. It exists only for internal usage by the builders. -func (m *PolicyBindingMutation) PolicyIDs() (ids []uuid.UUID) { - if id := m.policy; id != nil { - ids = append(ids, *id) - } - return +// ClearUpdatedBy clears the value of the "updated_by" field. +func (m *TemplateMutation) ClearUpdatedBy() { + m.updated_by = nil + m.clearedFields[template.FieldUpdatedBy] = struct{}{} } -// ResetPolicy resets all changes to the "policy" edge. -func (m *PolicyBindingMutation) ResetPolicy() { - m.policy = nil - m.clearedpolicy = false +// UpdatedByCleared returns if the "updated_by" field was cleared in this mutation. +func (m *TemplateMutation) UpdatedByCleared() bool { + _, ok := m.clearedFields[template.FieldUpdatedBy] + return ok } -// ClearUser clears the "user" edge to the User entity. -func (m *PolicyBindingMutation) ClearUser() { - m.cleareduser = true - m.clearedFields[policybinding.FieldUserID] = struct{}{} +// ResetUpdatedBy resets all changes to the "updated_by" field. +func (m *TemplateMutation) ResetUpdatedBy() { + m.updated_by = nil + delete(m.clearedFields, template.FieldUpdatedBy) } -// UserCleared reports if the "user" edge to the User entity was cleared. -func (m *PolicyBindingMutation) UserCleared() bool { - return m.UserIDCleared() || m.cleareduser +// SetVisibility sets the "visibility" field. +func (m *TemplateMutation) SetVisibility(s string) { + m.visibility = &s } -// UserIDs returns the "user" edge IDs in the mutation. -// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use -// UserID instead. It exists only for internal usage by the builders. -func (m *PolicyBindingMutation) UserIDs() (ids []uuid.UUID) { - if id := m.user; id != nil { - ids = append(ids, *id) +// Visibility returns the value of the "visibility" field in the mutation. +func (m *TemplateMutation) Visibility() (r string, exists bool) { + v := m.visibility + if v == nil { + return } - return + return *v, true } -// ResetUser resets all changes to the "user" edge. -func (m *PolicyBindingMutation) ResetUser() { - m.user = nil - m.cleareduser = false +// OldVisibility returns the old "visibility" field's value of the Template entity. +// If the Template object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *TemplateMutation) OldVisibility(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldVisibility is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldVisibility requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldVisibility: %w", err) + } + return oldValue.Visibility, nil } -// ClearGroup clears the "group" edge to the Group entity. -func (m *PolicyBindingMutation) ClearGroup() { - m.clearedgroup = true - m.clearedFields[policybinding.FieldGroupID] = struct{}{} +// ResetVisibility resets all changes to the "visibility" field. +func (m *TemplateMutation) ResetVisibility() { + m.visibility = nil } -// GroupCleared reports if the "group" edge to the Group entity was cleared. -func (m *PolicyBindingMutation) GroupCleared() bool { - return m.GroupIDCleared() || m.clearedgroup +// SetCreated sets the "created" field. +func (m *TemplateMutation) SetCreated(t time.Time) { + m.created = &t } -// GroupIDs returns the "group" edge IDs in the mutation. -// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use -// GroupID instead. It exists only for internal usage by the builders. -func (m *PolicyBindingMutation) GroupIDs() (ids []uuid.UUID) { - if id := m.group; id != nil { - ids = append(ids, *id) +// Created returns the value of the "created" field in the mutation. +func (m *TemplateMutation) Created() (r time.Time, exists bool) { + v := m.created + if v == nil { + return } - return + return *v, true } -// ResetGroup resets all changes to the "group" edge. -func (m *PolicyBindingMutation) ResetGroup() { - m.group = nil - m.clearedgroup = false +// OldCreated returns the old "created" field's value of the Template entity. +// If the Template object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *TemplateMutation) OldCreated(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreated is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreated requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreated: %w", err) + } + return oldValue.Created, nil } -// ClearAgent clears the "agent" edge to the Agent entity. -func (m *PolicyBindingMutation) ClearAgent() { - m.clearedagent = true - m.clearedFields[policybinding.FieldAgentID] = struct{}{} +// ResetCreated resets all changes to the "created" field. +func (m *TemplateMutation) ResetCreated() { + m.created = nil } -// AgentCleared reports if the "agent" edge to the Agent entity was cleared. -func (m *PolicyBindingMutation) AgentCleared() bool { - return m.AgentIDCleared() || m.clearedagent +// SetUpdated sets the "updated" field. +func (m *TemplateMutation) SetUpdated(t time.Time) { + m.updated = &t } -// AgentIDs returns the "agent" edge IDs in the mutation. -// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use -// AgentID instead. It exists only for internal usage by the builders. -func (m *PolicyBindingMutation) AgentIDs() (ids []uuid.UUID) { - if id := m.agent; id != nil { - ids = append(ids, *id) +// Updated returns the value of the "updated" field in the mutation. +func (m *TemplateMutation) Updated() (r time.Time, exists bool) { + v := m.updated + if v == nil { + return } - return + return *v, true } -// ResetAgent resets all changes to the "agent" edge. -func (m *PolicyBindingMutation) ResetAgent() { - m.agent = nil - m.clearedagent = false +// OldUpdated returns the old "updated" field's value of the Template entity. +// If the Template object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *TemplateMutation) OldUpdated(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldUpdated is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldUpdated requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldUpdated: %w", err) + } + return oldValue.Updated, nil } -// Where appends a list predicates to the PolicyBindingMutation builder. -func (m *PolicyBindingMutation) Where(ps ...predicate.PolicyBinding) { +// ResetUpdated resets all changes to the "updated" field. +func (m *TemplateMutation) ResetUpdated() { + m.updated = nil +} + +// Where appends a list predicates to the TemplateMutation builder. +func (m *TemplateMutation) Where(ps ...predicate.Template) { m.predicates = append(m.predicates, ps...) } -// WhereP appends storage-level predicates to the PolicyBindingMutation builder. Using this method, +// WhereP appends storage-level predicates to the TemplateMutation builder. Using this method, // users can use type-assertion to append predicates that do not depend on any generated package. -func (m *PolicyBindingMutation) WhereP(ps ...func(*sql.Selector)) { - p := make([]predicate.PolicyBinding, len(ps)) +func (m *TemplateMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.Template, len(ps)) for i := range ps { p[i] = ps[i] } @@ -5442,45 +29516,99 @@ func (m *PolicyBindingMutation) WhereP(ps ...func(*sql.Selector)) { } // Op returns the operation name. -func (m *PolicyBindingMutation) Op() Op { +func (m *TemplateMutation) Op() Op { return m.op } // SetOp allows setting the mutation operation. -func (m *PolicyBindingMutation) SetOp(op Op) { +func (m *TemplateMutation) SetOp(op Op) { m.op = op } -// Type returns the node type of this mutation (PolicyBinding). -func (m *PolicyBindingMutation) Type() string { +// Type returns the node type of this mutation (Template). +func (m *TemplateMutation) Type() string { return m.typ } // Fields returns all fields that were changed during this mutation. Note that in // order to get all numeric fields that were incremented/decremented, call // AddedFields(). -func (m *PolicyBindingMutation) Fields() []string { - fields := make([]string, 0, 7) - if m.principal_type != nil { - fields = append(fields, policybinding.FieldPrincipalType) +func (m *TemplateMutation) Fields() []string { + fields := make([]string, 0, 25) + if m.name != nil { + fields = append(fields, template.FieldName) } - if m.created != nil { - fields = append(fields, policybinding.FieldCreated) + if m.slug != nil { + fields = append(fields, template.FieldSlug) + } + if m.display_name != nil { + fields = append(fields, template.FieldDisplayName) + } + if m.description != nil { + fields = append(fields, template.FieldDescription) + } + if m.harness != nil { + fields = append(fields, template.FieldHarness) + } + if m.default_harness_config != nil { + fields = append(fields, template.FieldDefaultHarnessConfig) + } + if m.image != nil { + fields = append(fields, template.FieldImage) + } + if m._config != nil { + fields = append(fields, template.FieldConfig) + } + if m.content_hash != nil { + fields = append(fields, template.FieldContentHash) + } + if m.scope != nil { + fields = append(fields, template.FieldScope) + } + if m.scope_id != nil { + fields = append(fields, template.FieldScopeID) + } + if m.project_id != nil { + fields = append(fields, template.FieldProjectID) + } + if m.storage_uri != nil { + fields = append(fields, template.FieldStorageURI) + } + if m.storage_bucket != nil { + fields = append(fields, template.FieldStorageBucket) + } + if m.storage_path != nil { + fields = append(fields, template.FieldStoragePath) + } + if m.files != nil { + fields = append(fields, template.FieldFiles) + } + if m.base_template != nil { + fields = append(fields, template.FieldBaseTemplate) + } + if m.locked != nil { + fields = append(fields, template.FieldLocked) + } + if m.status != nil { + fields = append(fields, template.FieldStatus) + } + if m.owner_id != nil { + fields = append(fields, template.FieldOwnerID) } if m.created_by != nil { - fields = append(fields, policybinding.FieldCreatedBy) + fields = append(fields, template.FieldCreatedBy) } - if m.policy != nil { - fields = append(fields, policybinding.FieldPolicyID) + if m.updated_by != nil { + fields = append(fields, template.FieldUpdatedBy) } - if m.user != nil { - fields = append(fields, policybinding.FieldUserID) + if m.visibility != nil { + fields = append(fields, template.FieldVisibility) } - if m.group != nil { - fields = append(fields, policybinding.FieldGroupID) + if m.created != nil { + fields = append(fields, template.FieldCreated) } - if m.agent != nil { - fields = append(fields, policybinding.FieldAgentID) + if m.updated != nil { + fields = append(fields, template.FieldUpdated) } return fields } @@ -5488,22 +29616,58 @@ func (m *PolicyBindingMutation) Fields() []string { // Field returns the value of a field with the given name. The second boolean // return value indicates that this field was not set, or was not defined in the // schema. -func (m *PolicyBindingMutation) Field(name string) (ent.Value, bool) { +func (m *TemplateMutation) Field(name string) (ent.Value, bool) { switch name { - case policybinding.FieldPrincipalType: - return m.PrincipalType() - case policybinding.FieldCreated: - return m.Created() - case policybinding.FieldCreatedBy: + case template.FieldName: + return m.Name() + case template.FieldSlug: + return m.Slug() + case template.FieldDisplayName: + return m.DisplayName() + case template.FieldDescription: + return m.Description() + case template.FieldHarness: + return m.Harness() + case template.FieldDefaultHarnessConfig: + return m.DefaultHarnessConfig() + case template.FieldImage: + return m.Image() + case template.FieldConfig: + return m.Config() + case template.FieldContentHash: + return m.ContentHash() + case template.FieldScope: + return m.Scope() + case template.FieldScopeID: + return m.ScopeID() + case template.FieldProjectID: + return m.ProjectID() + case template.FieldStorageURI: + return m.StorageURI() + case template.FieldStorageBucket: + return m.StorageBucket() + case template.FieldStoragePath: + return m.StoragePath() + case template.FieldFiles: + return m.Files() + case template.FieldBaseTemplate: + return m.BaseTemplate() + case template.FieldLocked: + return m.Locked() + case template.FieldStatus: + return m.Status() + case template.FieldOwnerID: + return m.OwnerID() + case template.FieldCreatedBy: return m.CreatedBy() - case policybinding.FieldPolicyID: - return m.PolicyID() - case policybinding.FieldUserID: - return m.UserID() - case policybinding.FieldGroupID: - return m.GroupID() - case policybinding.FieldAgentID: - return m.AgentID() + case template.FieldUpdatedBy: + return m.UpdatedBy() + case template.FieldVisibility: + return m.Visibility() + case template.FieldCreated: + return m.Created() + case template.FieldUpdated: + return m.Updated() } return nil, false } @@ -5511,351 +29675,564 @@ func (m *PolicyBindingMutation) Field(name string) (ent.Value, bool) { // OldField returns the old value of the field from the database. An error is // returned if the mutation operation is not UpdateOne, or the query to the // database failed. -func (m *PolicyBindingMutation) OldField(ctx context.Context, name string) (ent.Value, error) { +func (m *TemplateMutation) OldField(ctx context.Context, name string) (ent.Value, error) { switch name { - case policybinding.FieldPrincipalType: - return m.OldPrincipalType(ctx) - case policybinding.FieldCreated: - return m.OldCreated(ctx) - case policybinding.FieldCreatedBy: + case template.FieldName: + return m.OldName(ctx) + case template.FieldSlug: + return m.OldSlug(ctx) + case template.FieldDisplayName: + return m.OldDisplayName(ctx) + case template.FieldDescription: + return m.OldDescription(ctx) + case template.FieldHarness: + return m.OldHarness(ctx) + case template.FieldDefaultHarnessConfig: + return m.OldDefaultHarnessConfig(ctx) + case template.FieldImage: + return m.OldImage(ctx) + case template.FieldConfig: + return m.OldConfig(ctx) + case template.FieldContentHash: + return m.OldContentHash(ctx) + case template.FieldScope: + return m.OldScope(ctx) + case template.FieldScopeID: + return m.OldScopeID(ctx) + case template.FieldProjectID: + return m.OldProjectID(ctx) + case template.FieldStorageURI: + return m.OldStorageURI(ctx) + case template.FieldStorageBucket: + return m.OldStorageBucket(ctx) + case template.FieldStoragePath: + return m.OldStoragePath(ctx) + case template.FieldFiles: + return m.OldFiles(ctx) + case template.FieldBaseTemplate: + return m.OldBaseTemplate(ctx) + case template.FieldLocked: + return m.OldLocked(ctx) + case template.FieldStatus: + return m.OldStatus(ctx) + case template.FieldOwnerID: + return m.OldOwnerID(ctx) + case template.FieldCreatedBy: return m.OldCreatedBy(ctx) - case policybinding.FieldPolicyID: - return m.OldPolicyID(ctx) - case policybinding.FieldUserID: - return m.OldUserID(ctx) - case policybinding.FieldGroupID: - return m.OldGroupID(ctx) - case policybinding.FieldAgentID: - return m.OldAgentID(ctx) + case template.FieldUpdatedBy: + return m.OldUpdatedBy(ctx) + case template.FieldVisibility: + return m.OldVisibility(ctx) + case template.FieldCreated: + return m.OldCreated(ctx) + case template.FieldUpdated: + return m.OldUpdated(ctx) } - return nil, fmt.Errorf("unknown PolicyBinding field %s", name) + return nil, fmt.Errorf("unknown Template field %s", name) } // SetField sets the value of a field with the given name. It returns an error if // the field is not defined in the schema, or if the type mismatched the field // type. -func (m *PolicyBindingMutation) SetField(name string, value ent.Value) error { +func (m *TemplateMutation) SetField(name string, value ent.Value) error { switch name { - case policybinding.FieldPrincipalType: - v, ok := value.(policybinding.PrincipalType) + case template.FieldName: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetName(v) + return nil + case template.FieldSlug: + v, ok := value.(string) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetPrincipalType(v) + m.SetSlug(v) return nil - case policybinding.FieldCreated: - v, ok := value.(time.Time) + case template.FieldDisplayName: + v, ok := value.(string) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetCreated(v) + m.SetDisplayName(v) return nil - case policybinding.FieldCreatedBy: + case template.FieldDescription: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetDescription(v) + return nil + case template.FieldHarness: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetHarness(v) + return nil + case template.FieldDefaultHarnessConfig: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetDefaultHarnessConfig(v) + return nil + case template.FieldImage: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetImage(v) + return nil + case template.FieldConfig: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetConfig(v) + return nil + case template.FieldContentHash: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetContentHash(v) + return nil + case template.FieldScope: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetScope(v) + return nil + case template.FieldScopeID: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetScopeID(v) + return nil + case template.FieldProjectID: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetProjectID(v) + return nil + case template.FieldStorageURI: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetStorageURI(v) + return nil + case template.FieldStorageBucket: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetStorageBucket(v) + return nil + case template.FieldStoragePath: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetStoragePath(v) + return nil + case template.FieldFiles: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetFiles(v) + return nil + case template.FieldBaseTemplate: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetBaseTemplate(v) + return nil + case template.FieldLocked: + v, ok := value.(bool) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetLocked(v) + return nil + case template.FieldStatus: + v, ok := value.(template.Status) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetStatus(v) + return nil + case template.FieldOwnerID: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetOwnerID(v) + return nil + case template.FieldCreatedBy: v, ok := value.(string) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } m.SetCreatedBy(v) return nil - case policybinding.FieldPolicyID: - v, ok := value.(uuid.UUID) + case template.FieldUpdatedBy: + v, ok := value.(string) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetPolicyID(v) + m.SetUpdatedBy(v) return nil - case policybinding.FieldUserID: - v, ok := value.(uuid.UUID) + case template.FieldVisibility: + v, ok := value.(string) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetUserID(v) + m.SetVisibility(v) return nil - case policybinding.FieldGroupID: - v, ok := value.(uuid.UUID) + case template.FieldCreated: + v, ok := value.(time.Time) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetGroupID(v) + m.SetCreated(v) return nil - case policybinding.FieldAgentID: - v, ok := value.(uuid.UUID) + case template.FieldUpdated: + v, ok := value.(time.Time) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetAgentID(v) + m.SetUpdated(v) return nil } - return fmt.Errorf("unknown PolicyBinding field %s", name) + return fmt.Errorf("unknown Template field %s", name) } // AddedFields returns all numeric fields that were incremented/decremented during // this mutation. -func (m *PolicyBindingMutation) AddedFields() []string { +func (m *TemplateMutation) AddedFields() []string { return nil } // AddedField returns the numeric value that was incremented/decremented on a field // with the given name. The second boolean return value indicates that this field // was not set, or was not defined in the schema. -func (m *PolicyBindingMutation) AddedField(name string) (ent.Value, bool) { +func (m *TemplateMutation) AddedField(name string) (ent.Value, bool) { return nil, false } // AddField adds the value to the field with the given name. It returns an error if // the field is not defined in the schema, or if the type mismatched the field // type. -func (m *PolicyBindingMutation) AddField(name string, value ent.Value) error { +func (m *TemplateMutation) AddField(name string, value ent.Value) error { switch name { } - return fmt.Errorf("unknown PolicyBinding numeric field %s", name) + return fmt.Errorf("unknown Template numeric field %s", name) } // ClearedFields returns all nullable fields that were cleared during this // mutation. -func (m *PolicyBindingMutation) ClearedFields() []string { +func (m *TemplateMutation) ClearedFields() []string { var fields []string - if m.FieldCleared(policybinding.FieldCreatedBy) { - fields = append(fields, policybinding.FieldCreatedBy) + if m.FieldCleared(template.FieldDisplayName) { + fields = append(fields, template.FieldDisplayName) } - if m.FieldCleared(policybinding.FieldPolicyID) { - fields = append(fields, policybinding.FieldPolicyID) + if m.FieldCleared(template.FieldDescription) { + fields = append(fields, template.FieldDescription) } - if m.FieldCleared(policybinding.FieldUserID) { - fields = append(fields, policybinding.FieldUserID) + if m.FieldCleared(template.FieldDefaultHarnessConfig) { + fields = append(fields, template.FieldDefaultHarnessConfig) } - if m.FieldCleared(policybinding.FieldGroupID) { - fields = append(fields, policybinding.FieldGroupID) + if m.FieldCleared(template.FieldImage) { + fields = append(fields, template.FieldImage) } - if m.FieldCleared(policybinding.FieldAgentID) { - fields = append(fields, policybinding.FieldAgentID) + if m.FieldCleared(template.FieldConfig) { + fields = append(fields, template.FieldConfig) + } + if m.FieldCleared(template.FieldContentHash) { + fields = append(fields, template.FieldContentHash) + } + if m.FieldCleared(template.FieldScopeID) { + fields = append(fields, template.FieldScopeID) + } + if m.FieldCleared(template.FieldProjectID) { + fields = append(fields, template.FieldProjectID) + } + if m.FieldCleared(template.FieldStorageURI) { + fields = append(fields, template.FieldStorageURI) + } + if m.FieldCleared(template.FieldStorageBucket) { + fields = append(fields, template.FieldStorageBucket) + } + if m.FieldCleared(template.FieldStoragePath) { + fields = append(fields, template.FieldStoragePath) + } + if m.FieldCleared(template.FieldFiles) { + fields = append(fields, template.FieldFiles) + } + if m.FieldCleared(template.FieldBaseTemplate) { + fields = append(fields, template.FieldBaseTemplate) + } + if m.FieldCleared(template.FieldOwnerID) { + fields = append(fields, template.FieldOwnerID) + } + if m.FieldCleared(template.FieldCreatedBy) { + fields = append(fields, template.FieldCreatedBy) + } + if m.FieldCleared(template.FieldUpdatedBy) { + fields = append(fields, template.FieldUpdatedBy) } return fields } // FieldCleared returns a boolean indicating if a field with the given name was // cleared in this mutation. -func (m *PolicyBindingMutation) FieldCleared(name string) bool { +func (m *TemplateMutation) FieldCleared(name string) bool { _, ok := m.clearedFields[name] return ok } // ClearField clears the value of the field with the given name. It returns an // error if the field is not defined in the schema. -func (m *PolicyBindingMutation) ClearField(name string) error { +func (m *TemplateMutation) ClearField(name string) error { switch name { - case policybinding.FieldCreatedBy: - m.ClearCreatedBy() + case template.FieldDisplayName: + m.ClearDisplayName() return nil - case policybinding.FieldPolicyID: - m.ClearPolicyID() + case template.FieldDescription: + m.ClearDescription() return nil - case policybinding.FieldUserID: - m.ClearUserID() + case template.FieldDefaultHarnessConfig: + m.ClearDefaultHarnessConfig() return nil - case policybinding.FieldGroupID: - m.ClearGroupID() + case template.FieldImage: + m.ClearImage() return nil - case policybinding.FieldAgentID: - m.ClearAgentID() + case template.FieldConfig: + m.ClearConfig() + return nil + case template.FieldContentHash: + m.ClearContentHash() + return nil + case template.FieldScopeID: + m.ClearScopeID() + return nil + case template.FieldProjectID: + m.ClearProjectID() + return nil + case template.FieldStorageURI: + m.ClearStorageURI() + return nil + case template.FieldStorageBucket: + m.ClearStorageBucket() + return nil + case template.FieldStoragePath: + m.ClearStoragePath() + return nil + case template.FieldFiles: + m.ClearFiles() + return nil + case template.FieldBaseTemplate: + m.ClearBaseTemplate() + return nil + case template.FieldOwnerID: + m.ClearOwnerID() + return nil + case template.FieldCreatedBy: + m.ClearCreatedBy() + return nil + case template.FieldUpdatedBy: + m.ClearUpdatedBy() return nil } - return fmt.Errorf("unknown PolicyBinding nullable field %s", name) + return fmt.Errorf("unknown Template nullable field %s", name) } // ResetField resets all changes in the mutation for the field with the given name. // It returns an error if the field is not defined in the schema. -func (m *PolicyBindingMutation) ResetField(name string) error { +func (m *TemplateMutation) ResetField(name string) error { switch name { - case policybinding.FieldPrincipalType: - m.ResetPrincipalType() + case template.FieldName: + m.ResetName() return nil - case policybinding.FieldCreated: - m.ResetCreated() + case template.FieldSlug: + m.ResetSlug() return nil - case policybinding.FieldCreatedBy: - m.ResetCreatedBy() + case template.FieldDisplayName: + m.ResetDisplayName() return nil - case policybinding.FieldPolicyID: - m.ResetPolicyID() + case template.FieldDescription: + m.ResetDescription() return nil - case policybinding.FieldUserID: - m.ResetUserID() + case template.FieldHarness: + m.ResetHarness() return nil - case policybinding.FieldGroupID: - m.ResetGroupID() + case template.FieldDefaultHarnessConfig: + m.ResetDefaultHarnessConfig() return nil - case policybinding.FieldAgentID: - m.ResetAgentID() + case template.FieldImage: + m.ResetImage() + return nil + case template.FieldConfig: + m.ResetConfig() + return nil + case template.FieldContentHash: + m.ResetContentHash() + return nil + case template.FieldScope: + m.ResetScope() + return nil + case template.FieldScopeID: + m.ResetScopeID() + return nil + case template.FieldProjectID: + m.ResetProjectID() + return nil + case template.FieldStorageURI: + m.ResetStorageURI() + return nil + case template.FieldStorageBucket: + m.ResetStorageBucket() + return nil + case template.FieldStoragePath: + m.ResetStoragePath() + return nil + case template.FieldFiles: + m.ResetFiles() + return nil + case template.FieldBaseTemplate: + m.ResetBaseTemplate() + return nil + case template.FieldLocked: + m.ResetLocked() + return nil + case template.FieldStatus: + m.ResetStatus() + return nil + case template.FieldOwnerID: + m.ResetOwnerID() + return nil + case template.FieldCreatedBy: + m.ResetCreatedBy() + return nil + case template.FieldUpdatedBy: + m.ResetUpdatedBy() + return nil + case template.FieldVisibility: + m.ResetVisibility() + return nil + case template.FieldCreated: + m.ResetCreated() + return nil + case template.FieldUpdated: + m.ResetUpdated() return nil } - return fmt.Errorf("unknown PolicyBinding field %s", name) -} - -// AddedEdges returns all edge names that were set/added in this mutation. -func (m *PolicyBindingMutation) AddedEdges() []string { - edges := make([]string, 0, 4) - if m.policy != nil { - edges = append(edges, policybinding.EdgePolicy) - } - if m.user != nil { - edges = append(edges, policybinding.EdgeUser) - } - if m.group != nil { - edges = append(edges, policybinding.EdgeGroup) - } - if m.agent != nil { - edges = append(edges, policybinding.EdgeAgent) - } - return edges -} - -// AddedIDs returns all IDs (to other nodes) that were added for the given edge -// name in this mutation. -func (m *PolicyBindingMutation) AddedIDs(name string) []ent.Value { - switch name { - case policybinding.EdgePolicy: - if id := m.policy; id != nil { - return []ent.Value{*id} - } - case policybinding.EdgeUser: - if id := m.user; id != nil { - return []ent.Value{*id} - } - case policybinding.EdgeGroup: - if id := m.group; id != nil { - return []ent.Value{*id} - } - case policybinding.EdgeAgent: - if id := m.agent; id != nil { - return []ent.Value{*id} - } - } + return fmt.Errorf("unknown Template field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *TemplateMutation) AddedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *TemplateMutation) AddedIDs(name string) []ent.Value { return nil } // RemovedEdges returns all edge names that were removed in this mutation. -func (m *PolicyBindingMutation) RemovedEdges() []string { - edges := make([]string, 0, 4) +func (m *TemplateMutation) RemovedEdges() []string { + edges := make([]string, 0, 0) return edges } // RemovedIDs returns all IDs (to other nodes) that were removed for the edge with // the given name in this mutation. -func (m *PolicyBindingMutation) RemovedIDs(name string) []ent.Value { +func (m *TemplateMutation) RemovedIDs(name string) []ent.Value { return nil } // ClearedEdges returns all edge names that were cleared in this mutation. -func (m *PolicyBindingMutation) ClearedEdges() []string { - edges := make([]string, 0, 4) - if m.clearedpolicy { - edges = append(edges, policybinding.EdgePolicy) - } - if m.cleareduser { - edges = append(edges, policybinding.EdgeUser) - } - if m.clearedgroup { - edges = append(edges, policybinding.EdgeGroup) - } - if m.clearedagent { - edges = append(edges, policybinding.EdgeAgent) - } +func (m *TemplateMutation) ClearedEdges() []string { + edges := make([]string, 0, 0) return edges } // EdgeCleared returns a boolean which indicates if the edge with the given name // was cleared in this mutation. -func (m *PolicyBindingMutation) EdgeCleared(name string) bool { - switch name { - case policybinding.EdgePolicy: - return m.clearedpolicy - case policybinding.EdgeUser: - return m.cleareduser - case policybinding.EdgeGroup: - return m.clearedgroup - case policybinding.EdgeAgent: - return m.clearedagent - } +func (m *TemplateMutation) EdgeCleared(name string) bool { return false } // ClearEdge clears the value of the edge with the given name. It returns an error // if that edge is not defined in the schema. -func (m *PolicyBindingMutation) ClearEdge(name string) error { - switch name { - case policybinding.EdgePolicy: - m.ClearPolicy() - return nil - case policybinding.EdgeUser: - m.ClearUser() - return nil - case policybinding.EdgeGroup: - m.ClearGroup() - return nil - case policybinding.EdgeAgent: - m.ClearAgent() - return nil - } - return fmt.Errorf("unknown PolicyBinding unique edge %s", name) +func (m *TemplateMutation) ClearEdge(name string) error { + return fmt.Errorf("unknown Template unique edge %s", name) } // ResetEdge resets all changes to the edge with the given name in this mutation. // It returns an error if the edge is not defined in the schema. -func (m *PolicyBindingMutation) ResetEdge(name string) error { - switch name { - case policybinding.EdgePolicy: - m.ResetPolicy() - return nil - case policybinding.EdgeUser: - m.ResetUser() - return nil - case policybinding.EdgeGroup: - m.ResetGroup() - return nil - case policybinding.EdgeAgent: - m.ResetAgent() - return nil - } - return fmt.Errorf("unknown PolicyBinding edge %s", name) +func (m *TemplateMutation) ResetEdge(name string) error { + return fmt.Errorf("unknown Template edge %s", name) } -// ProjectMutation represents an operation that mutates the Project nodes in the graph. -type ProjectMutation struct { +// UserMutation represents an operation that mutates the User nodes in the graph. +type UserMutation struct { config - op Op - typ string - id *uuid.UUID - name *string - slug *string - git_remote *string - labels *map[string]string - annotations *map[string]string - created *time.Time - updated *time.Time - created_by *string - owner_id *string - visibility *string - clearedFields map[string]struct{} - agents map[uuid.UUID]struct{} - removedagents map[uuid.UUID]struct{} - clearedagents bool - done bool - oldValue func(context.Context) (*Project, error) - predicates []predicate.Project + op Op + typ string + id *uuid.UUID + email *string + display_name *string + avatar_url *string + role *user.Role + status *user.Status + preferences **schema.UserPreferences + created *time.Time + last_login *time.Time + last_seen *time.Time + clearedFields map[string]struct{} + created_agents map[uuid.UUID]struct{} + removedcreated_agents map[uuid.UUID]struct{} + clearedcreated_agents bool + owned_agents map[uuid.UUID]struct{} + removedowned_agents map[uuid.UUID]struct{} + clearedowned_agents bool + owned_groups map[uuid.UUID]struct{} + removedowned_groups map[uuid.UUID]struct{} + clearedowned_groups bool + memberships map[uuid.UUID]struct{} + removedmemberships map[uuid.UUID]struct{} + clearedmemberships bool + policy_bindings map[uuid.UUID]struct{} + removedpolicy_bindings map[uuid.UUID]struct{} + clearedpolicy_bindings bool + done bool + oldValue func(context.Context) (*User, error) + predicates []predicate.User } -var _ ent.Mutation = (*ProjectMutation)(nil) +var _ ent.Mutation = (*UserMutation)(nil) -// projectOption allows management of the mutation configuration using functional options. -type projectOption func(*ProjectMutation) +// userOption allows management of the mutation configuration using functional options. +type userOption func(*UserMutation) -// newProjectMutation creates new mutation for the Project entity. -func newProjectMutation(c config, op Op, opts ...projectOption) *ProjectMutation { - m := &ProjectMutation{ +// newUserMutation creates new mutation for the User entity. +func newUserMutation(c config, op Op, opts ...userOption) *UserMutation { + m := &UserMutation{ config: c, op: op, - typ: TypeProject, + typ: TypeUser, clearedFields: make(map[string]struct{}), } for _, opt := range opts { @@ -5864,20 +30241,20 @@ func newProjectMutation(c config, op Op, opts ...projectOption) *ProjectMutation return m } -// withProjectID sets the ID field of the mutation. -func withProjectID(id uuid.UUID) projectOption { - return func(m *ProjectMutation) { +// withUserID sets the ID field of the mutation. +func withUserID(id uuid.UUID) userOption { + return func(m *UserMutation) { var ( err error once sync.Once - value *Project + value *User ) - m.oldValue = func(ctx context.Context) (*Project, error) { + m.oldValue = func(ctx context.Context) (*User, error) { once.Do(func() { if m.done { err = errors.New("querying old values post mutation is not allowed") } else { - value, err = m.Client().Project.Get(ctx, id) + value, err = m.Client().User.Get(ctx, id) } }) return value, err @@ -5886,10 +30263,10 @@ func withProjectID(id uuid.UUID) projectOption { } } -// withProject sets the old Project of the mutation. -func withProject(node *Project) projectOption { - return func(m *ProjectMutation) { - m.oldValue = func(context.Context) (*Project, error) { +// withUser sets the old User of the mutation. +func withUser(node *User) userOption { + return func(m *UserMutation) { + m.oldValue = func(context.Context) (*User, error) { return node, nil } m.id = &node.ID @@ -5898,7 +30275,7 @@ func withProject(node *Project) projectOption { // Client returns a new `ent.Client` from the mutation. If the mutation was // executed in a transaction (ent.Tx), a transactional client is returned. -func (m ProjectMutation) Client() *Client { +func (m UserMutation) Client() *Client { client := &Client{config: m.config} client.init() return client @@ -5906,7 +30283,7 @@ func (m ProjectMutation) Client() *Client { // Tx returns an `ent.Tx` for mutations that were executed in transactions; // it returns an error otherwise. -func (m ProjectMutation) Tx() (*Tx, error) { +func (m UserMutation) Tx() (*Tx, error) { if _, ok := m.driver.(*txDriver); !ok { return nil, errors.New("ent: mutation is not running in a transaction") } @@ -5915,528 +30292,695 @@ func (m ProjectMutation) Tx() (*Tx, error) { return tx, nil } -// SetID sets the value of the id field. Note that this -// operation is only accepted on creation of Project entities. -func (m *ProjectMutation) SetID(id uuid.UUID) { - m.id = &id +// SetID sets the value of the id field. Note that this +// operation is only accepted on creation of User entities. +func (m *UserMutation) SetID(id uuid.UUID) { + m.id = &id +} + +// ID returns the ID value in the mutation. Note that the ID is only available +// if it was provided to the builder or after it was returned from the database. +func (m *UserMutation) ID() (id uuid.UUID, exists bool) { + if m.id == nil { + return + } + return *m.id, true +} + +// IDs queries the database and returns the entity ids that match the mutation's predicate. +// That means, if the mutation is applied within a transaction with an isolation level such +// as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated +// or updated by the mutation. +func (m *UserMutation) IDs(ctx context.Context) ([]uuid.UUID, error) { + switch { + case m.op.Is(OpUpdateOne | OpDeleteOne): + id, exists := m.ID() + if exists { + return []uuid.UUID{id}, nil + } + fallthrough + case m.op.Is(OpUpdate | OpDelete): + return m.Client().User.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } +} + +// SetEmail sets the "email" field. +func (m *UserMutation) SetEmail(s string) { + m.email = &s +} + +// Email returns the value of the "email" field in the mutation. +func (m *UserMutation) Email() (r string, exists bool) { + v := m.email + if v == nil { + return + } + return *v, true +} + +// OldEmail returns the old "email" field's value of the User entity. +// If the User object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *UserMutation) OldEmail(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldEmail is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldEmail requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldEmail: %w", err) + } + return oldValue.Email, nil +} + +// ResetEmail resets all changes to the "email" field. +func (m *UserMutation) ResetEmail() { + m.email = nil +} + +// SetDisplayName sets the "display_name" field. +func (m *UserMutation) SetDisplayName(s string) { + m.display_name = &s +} + +// DisplayName returns the value of the "display_name" field in the mutation. +func (m *UserMutation) DisplayName() (r string, exists bool) { + v := m.display_name + if v == nil { + return + } + return *v, true +} + +// OldDisplayName returns the old "display_name" field's value of the User entity. +// If the User object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *UserMutation) OldDisplayName(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldDisplayName is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldDisplayName requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldDisplayName: %w", err) + } + return oldValue.DisplayName, nil +} + +// ResetDisplayName resets all changes to the "display_name" field. +func (m *UserMutation) ResetDisplayName() { + m.display_name = nil +} + +// SetAvatarURL sets the "avatar_url" field. +func (m *UserMutation) SetAvatarURL(s string) { + m.avatar_url = &s } -// ID returns the ID value in the mutation. Note that the ID is only available -// if it was provided to the builder or after it was returned from the database. -func (m *ProjectMutation) ID() (id uuid.UUID, exists bool) { - if m.id == nil { +// AvatarURL returns the value of the "avatar_url" field in the mutation. +func (m *UserMutation) AvatarURL() (r string, exists bool) { + v := m.avatar_url + if v == nil { return } - return *m.id, true + return *v, true } -// IDs queries the database and returns the entity ids that match the mutation's predicate. -// That means, if the mutation is applied within a transaction with an isolation level such -// as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated -// or updated by the mutation. -func (m *ProjectMutation) IDs(ctx context.Context) ([]uuid.UUID, error) { - switch { - case m.op.Is(OpUpdateOne | OpDeleteOne): - id, exists := m.ID() - if exists { - return []uuid.UUID{id}, nil - } - fallthrough - case m.op.Is(OpUpdate | OpDelete): - return m.Client().Project.Query().Where(m.predicates...).IDs(ctx) - default: - return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) +// OldAvatarURL returns the old "avatar_url" field's value of the User entity. +// If the User object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *UserMutation) OldAvatarURL(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldAvatarURL is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldAvatarURL requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldAvatarURL: %w", err) } + return oldValue.AvatarURL, nil } -// SetName sets the "name" field. -func (m *ProjectMutation) SetName(s string) { - m.name = &s +// ClearAvatarURL clears the value of the "avatar_url" field. +func (m *UserMutation) ClearAvatarURL() { + m.avatar_url = nil + m.clearedFields[user.FieldAvatarURL] = struct{}{} } -// Name returns the value of the "name" field in the mutation. -func (m *ProjectMutation) Name() (r string, exists bool) { - v := m.name +// AvatarURLCleared returns if the "avatar_url" field was cleared in this mutation. +func (m *UserMutation) AvatarURLCleared() bool { + _, ok := m.clearedFields[user.FieldAvatarURL] + return ok +} + +// ResetAvatarURL resets all changes to the "avatar_url" field. +func (m *UserMutation) ResetAvatarURL() { + m.avatar_url = nil + delete(m.clearedFields, user.FieldAvatarURL) +} + +// SetRole sets the "role" field. +func (m *UserMutation) SetRole(u user.Role) { + m.role = &u +} + +// Role returns the value of the "role" field in the mutation. +func (m *UserMutation) Role() (r user.Role, exists bool) { + v := m.role if v == nil { return } return *v, true } -// OldName returns the old "name" field's value of the Project entity. -// If the Project object wasn't provided to the builder, the object is fetched from the database. +// OldRole returns the old "role" field's value of the User entity. +// If the User object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *ProjectMutation) OldName(ctx context.Context) (v string, err error) { +func (m *UserMutation) OldRole(ctx context.Context) (v user.Role, err error) { if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldName is only allowed on UpdateOne operations") + return v, errors.New("OldRole is only allowed on UpdateOne operations") } if m.id == nil || m.oldValue == nil { - return v, errors.New("OldName requires an ID field in the mutation") + return v, errors.New("OldRole requires an ID field in the mutation") } oldValue, err := m.oldValue(ctx) if err != nil { - return v, fmt.Errorf("querying old value for OldName: %w", err) + return v, fmt.Errorf("querying old value for OldRole: %w", err) } - return oldValue.Name, nil + return oldValue.Role, nil } -// ResetName resets all changes to the "name" field. -func (m *ProjectMutation) ResetName() { - m.name = nil +// ResetRole resets all changes to the "role" field. +func (m *UserMutation) ResetRole() { + m.role = nil } -// SetSlug sets the "slug" field. -func (m *ProjectMutation) SetSlug(s string) { - m.slug = &s +// SetStatus sets the "status" field. +func (m *UserMutation) SetStatus(u user.Status) { + m.status = &u } -// Slug returns the value of the "slug" field in the mutation. -func (m *ProjectMutation) Slug() (r string, exists bool) { - v := m.slug +// Status returns the value of the "status" field in the mutation. +func (m *UserMutation) Status() (r user.Status, exists bool) { + v := m.status if v == nil { return } return *v, true } -// OldSlug returns the old "slug" field's value of the Project entity. -// If the Project object wasn't provided to the builder, the object is fetched from the database. +// OldStatus returns the old "status" field's value of the User entity. +// If the User object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *ProjectMutation) OldSlug(ctx context.Context) (v string, err error) { +func (m *UserMutation) OldStatus(ctx context.Context) (v user.Status, err error) { if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldSlug is only allowed on UpdateOne operations") + return v, errors.New("OldStatus is only allowed on UpdateOne operations") } if m.id == nil || m.oldValue == nil { - return v, errors.New("OldSlug requires an ID field in the mutation") + return v, errors.New("OldStatus requires an ID field in the mutation") } oldValue, err := m.oldValue(ctx) if err != nil { - return v, fmt.Errorf("querying old value for OldSlug: %w", err) + return v, fmt.Errorf("querying old value for OldStatus: %w", err) } - return oldValue.Slug, nil + return oldValue.Status, nil } -// ResetSlug resets all changes to the "slug" field. -func (m *ProjectMutation) ResetSlug() { - m.slug = nil +// ResetStatus resets all changes to the "status" field. +func (m *UserMutation) ResetStatus() { + m.status = nil } -// SetGitRemote sets the "git_remote" field. -func (m *ProjectMutation) SetGitRemote(s string) { - m.git_remote = &s +// SetPreferences sets the "preferences" field. +func (m *UserMutation) SetPreferences(sp *schema.UserPreferences) { + m.preferences = &sp } -// GitRemote returns the value of the "git_remote" field in the mutation. -func (m *ProjectMutation) GitRemote() (r string, exists bool) { - v := m.git_remote +// Preferences returns the value of the "preferences" field in the mutation. +func (m *UserMutation) Preferences() (r *schema.UserPreferences, exists bool) { + v := m.preferences if v == nil { return } return *v, true } -// OldGitRemote returns the old "git_remote" field's value of the Project entity. -// If the Project object wasn't provided to the builder, the object is fetched from the database. +// OldPreferences returns the old "preferences" field's value of the User entity. +// If the User object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *ProjectMutation) OldGitRemote(ctx context.Context) (v *string, err error) { +func (m *UserMutation) OldPreferences(ctx context.Context) (v *schema.UserPreferences, err error) { if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldGitRemote is only allowed on UpdateOne operations") + return v, errors.New("OldPreferences is only allowed on UpdateOne operations") } if m.id == nil || m.oldValue == nil { - return v, errors.New("OldGitRemote requires an ID field in the mutation") + return v, errors.New("OldPreferences requires an ID field in the mutation") } oldValue, err := m.oldValue(ctx) if err != nil { - return v, fmt.Errorf("querying old value for OldGitRemote: %w", err) + return v, fmt.Errorf("querying old value for OldPreferences: %w", err) } - return oldValue.GitRemote, nil + return oldValue.Preferences, nil } -// ClearGitRemote clears the value of the "git_remote" field. -func (m *ProjectMutation) ClearGitRemote() { - m.git_remote = nil - m.clearedFields[project.FieldGitRemote] = struct{}{} +// ClearPreferences clears the value of the "preferences" field. +func (m *UserMutation) ClearPreferences() { + m.preferences = nil + m.clearedFields[user.FieldPreferences] = struct{}{} } -// GitRemoteCleared returns if the "git_remote" field was cleared in this mutation. -func (m *ProjectMutation) GitRemoteCleared() bool { - _, ok := m.clearedFields[project.FieldGitRemote] +// PreferencesCleared returns if the "preferences" field was cleared in this mutation. +func (m *UserMutation) PreferencesCleared() bool { + _, ok := m.clearedFields[user.FieldPreferences] return ok } -// ResetGitRemote resets all changes to the "git_remote" field. -func (m *ProjectMutation) ResetGitRemote() { - m.git_remote = nil - delete(m.clearedFields, project.FieldGitRemote) +// ResetPreferences resets all changes to the "preferences" field. +func (m *UserMutation) ResetPreferences() { + m.preferences = nil + delete(m.clearedFields, user.FieldPreferences) } -// SetLabels sets the "labels" field. -func (m *ProjectMutation) SetLabels(value map[string]string) { - m.labels = &value +// SetCreated sets the "created" field. +func (m *UserMutation) SetCreated(t time.Time) { + m.created = &t } -// Labels returns the value of the "labels" field in the mutation. -func (m *ProjectMutation) Labels() (r map[string]string, exists bool) { - v := m.labels +// Created returns the value of the "created" field in the mutation. +func (m *UserMutation) Created() (r time.Time, exists bool) { + v := m.created if v == nil { return } return *v, true } -// OldLabels returns the old "labels" field's value of the Project entity. -// If the Project object wasn't provided to the builder, the object is fetched from the database. +// OldCreated returns the old "created" field's value of the User entity. +// If the User object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *ProjectMutation) OldLabels(ctx context.Context) (v map[string]string, err error) { +func (m *UserMutation) OldCreated(ctx context.Context) (v time.Time, err error) { if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldLabels is only allowed on UpdateOne operations") + return v, errors.New("OldCreated is only allowed on UpdateOne operations") } if m.id == nil || m.oldValue == nil { - return v, errors.New("OldLabels requires an ID field in the mutation") + return v, errors.New("OldCreated requires an ID field in the mutation") } oldValue, err := m.oldValue(ctx) if err != nil { - return v, fmt.Errorf("querying old value for OldLabels: %w", err) + return v, fmt.Errorf("querying old value for OldCreated: %w", err) } - return oldValue.Labels, nil -} - -// ClearLabels clears the value of the "labels" field. -func (m *ProjectMutation) ClearLabels() { - m.labels = nil - m.clearedFields[project.FieldLabels] = struct{}{} -} - -// LabelsCleared returns if the "labels" field was cleared in this mutation. -func (m *ProjectMutation) LabelsCleared() bool { - _, ok := m.clearedFields[project.FieldLabels] - return ok + return oldValue.Created, nil } -// ResetLabels resets all changes to the "labels" field. -func (m *ProjectMutation) ResetLabels() { - m.labels = nil - delete(m.clearedFields, project.FieldLabels) +// ResetCreated resets all changes to the "created" field. +func (m *UserMutation) ResetCreated() { + m.created = nil } -// SetAnnotations sets the "annotations" field. -func (m *ProjectMutation) SetAnnotations(value map[string]string) { - m.annotations = &value +// SetLastLogin sets the "last_login" field. +func (m *UserMutation) SetLastLogin(t time.Time) { + m.last_login = &t } -// Annotations returns the value of the "annotations" field in the mutation. -func (m *ProjectMutation) Annotations() (r map[string]string, exists bool) { - v := m.annotations +// LastLogin returns the value of the "last_login" field in the mutation. +func (m *UserMutation) LastLogin() (r time.Time, exists bool) { + v := m.last_login if v == nil { return } return *v, true } -// OldAnnotations returns the old "annotations" field's value of the Project entity. -// If the Project object wasn't provided to the builder, the object is fetched from the database. +// OldLastLogin returns the old "last_login" field's value of the User entity. +// If the User object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *ProjectMutation) OldAnnotations(ctx context.Context) (v map[string]string, err error) { +func (m *UserMutation) OldLastLogin(ctx context.Context) (v *time.Time, err error) { if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldAnnotations is only allowed on UpdateOne operations") + return v, errors.New("OldLastLogin is only allowed on UpdateOne operations") } if m.id == nil || m.oldValue == nil { - return v, errors.New("OldAnnotations requires an ID field in the mutation") + return v, errors.New("OldLastLogin requires an ID field in the mutation") } oldValue, err := m.oldValue(ctx) if err != nil { - return v, fmt.Errorf("querying old value for OldAnnotations: %w", err) + return v, fmt.Errorf("querying old value for OldLastLogin: %w", err) } - return oldValue.Annotations, nil + return oldValue.LastLogin, nil } -// ClearAnnotations clears the value of the "annotations" field. -func (m *ProjectMutation) ClearAnnotations() { - m.annotations = nil - m.clearedFields[project.FieldAnnotations] = struct{}{} +// ClearLastLogin clears the value of the "last_login" field. +func (m *UserMutation) ClearLastLogin() { + m.last_login = nil + m.clearedFields[user.FieldLastLogin] = struct{}{} } -// AnnotationsCleared returns if the "annotations" field was cleared in this mutation. -func (m *ProjectMutation) AnnotationsCleared() bool { - _, ok := m.clearedFields[project.FieldAnnotations] +// LastLoginCleared returns if the "last_login" field was cleared in this mutation. +func (m *UserMutation) LastLoginCleared() bool { + _, ok := m.clearedFields[user.FieldLastLogin] return ok } -// ResetAnnotations resets all changes to the "annotations" field. -func (m *ProjectMutation) ResetAnnotations() { - m.annotations = nil - delete(m.clearedFields, project.FieldAnnotations) +// ResetLastLogin resets all changes to the "last_login" field. +func (m *UserMutation) ResetLastLogin() { + m.last_login = nil + delete(m.clearedFields, user.FieldLastLogin) } -// SetCreated sets the "created" field. -func (m *ProjectMutation) SetCreated(t time.Time) { - m.created = &t +// SetLastSeen sets the "last_seen" field. +func (m *UserMutation) SetLastSeen(t time.Time) { + m.last_seen = &t } -// Created returns the value of the "created" field in the mutation. -func (m *ProjectMutation) Created() (r time.Time, exists bool) { - v := m.created +// LastSeen returns the value of the "last_seen" field in the mutation. +func (m *UserMutation) LastSeen() (r time.Time, exists bool) { + v := m.last_seen if v == nil { return } return *v, true } -// OldCreated returns the old "created" field's value of the Project entity. -// If the Project object wasn't provided to the builder, the object is fetched from the database. +// OldLastSeen returns the old "last_seen" field's value of the User entity. +// If the User object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *ProjectMutation) OldCreated(ctx context.Context) (v time.Time, err error) { +func (m *UserMutation) OldLastSeen(ctx context.Context) (v *time.Time, err error) { if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldCreated is only allowed on UpdateOne operations") + return v, errors.New("OldLastSeen is only allowed on UpdateOne operations") } if m.id == nil || m.oldValue == nil { - return v, errors.New("OldCreated requires an ID field in the mutation") + return v, errors.New("OldLastSeen requires an ID field in the mutation") } oldValue, err := m.oldValue(ctx) if err != nil { - return v, fmt.Errorf("querying old value for OldCreated: %w", err) + return v, fmt.Errorf("querying old value for OldLastSeen: %w", err) } - return oldValue.Created, nil + return oldValue.LastSeen, nil } -// ResetCreated resets all changes to the "created" field. -func (m *ProjectMutation) ResetCreated() { - m.created = nil +// ClearLastSeen clears the value of the "last_seen" field. +func (m *UserMutation) ClearLastSeen() { + m.last_seen = nil + m.clearedFields[user.FieldLastSeen] = struct{}{} } -// SetUpdated sets the "updated" field. -func (m *ProjectMutation) SetUpdated(t time.Time) { - m.updated = &t +// LastSeenCleared returns if the "last_seen" field was cleared in this mutation. +func (m *UserMutation) LastSeenCleared() bool { + _, ok := m.clearedFields[user.FieldLastSeen] + return ok } -// Updated returns the value of the "updated" field in the mutation. -func (m *ProjectMutation) Updated() (r time.Time, exists bool) { - v := m.updated - if v == nil { - return +// ResetLastSeen resets all changes to the "last_seen" field. +func (m *UserMutation) ResetLastSeen() { + m.last_seen = nil + delete(m.clearedFields, user.FieldLastSeen) +} + +// AddCreatedAgentIDs adds the "created_agents" edge to the Agent entity by ids. +func (m *UserMutation) AddCreatedAgentIDs(ids ...uuid.UUID) { + if m.created_agents == nil { + m.created_agents = make(map[uuid.UUID]struct{}) + } + for i := range ids { + m.created_agents[ids[i]] = struct{}{} } - return *v, true } -// OldUpdated returns the old "updated" field's value of the Project entity. -// If the Project object wasn't provided to the builder, the object is fetched from the database. -// An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *ProjectMutation) OldUpdated(ctx context.Context) (v time.Time, err error) { - if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldUpdated is only allowed on UpdateOne operations") +// ClearCreatedAgents clears the "created_agents" edge to the Agent entity. +func (m *UserMutation) ClearCreatedAgents() { + m.clearedcreated_agents = true +} + +// CreatedAgentsCleared reports if the "created_agents" edge to the Agent entity was cleared. +func (m *UserMutation) CreatedAgentsCleared() bool { + return m.clearedcreated_agents +} + +// RemoveCreatedAgentIDs removes the "created_agents" edge to the Agent entity by IDs. +func (m *UserMutation) RemoveCreatedAgentIDs(ids ...uuid.UUID) { + if m.removedcreated_agents == nil { + m.removedcreated_agents = make(map[uuid.UUID]struct{}) } - if m.id == nil || m.oldValue == nil { - return v, errors.New("OldUpdated requires an ID field in the mutation") + for i := range ids { + delete(m.created_agents, ids[i]) + m.removedcreated_agents[ids[i]] = struct{}{} } - oldValue, err := m.oldValue(ctx) - if err != nil { - return v, fmt.Errorf("querying old value for OldUpdated: %w", err) +} + +// RemovedCreatedAgents returns the removed IDs of the "created_agents" edge to the Agent entity. +func (m *UserMutation) RemovedCreatedAgentsIDs() (ids []uuid.UUID) { + for id := range m.removedcreated_agents { + ids = append(ids, id) } - return oldValue.Updated, nil + return } -// ResetUpdated resets all changes to the "updated" field. -func (m *ProjectMutation) ResetUpdated() { - m.updated = nil +// CreatedAgentsIDs returns the "created_agents" edge IDs in the mutation. +func (m *UserMutation) CreatedAgentsIDs() (ids []uuid.UUID) { + for id := range m.created_agents { + ids = append(ids, id) + } + return } -// SetCreatedBy sets the "created_by" field. -func (m *ProjectMutation) SetCreatedBy(s string) { - m.created_by = &s +// ResetCreatedAgents resets all changes to the "created_agents" edge. +func (m *UserMutation) ResetCreatedAgents() { + m.created_agents = nil + m.clearedcreated_agents = false + m.removedcreated_agents = nil } -// CreatedBy returns the value of the "created_by" field in the mutation. -func (m *ProjectMutation) CreatedBy() (r string, exists bool) { - v := m.created_by - if v == nil { - return +// AddOwnedAgentIDs adds the "owned_agents" edge to the Agent entity by ids. +func (m *UserMutation) AddOwnedAgentIDs(ids ...uuid.UUID) { + if m.owned_agents == nil { + m.owned_agents = make(map[uuid.UUID]struct{}) + } + for i := range ids { + m.owned_agents[ids[i]] = struct{}{} } - return *v, true } -// OldCreatedBy returns the old "created_by" field's value of the Project entity. -// If the Project object wasn't provided to the builder, the object is fetched from the database. -// An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *ProjectMutation) OldCreatedBy(ctx context.Context) (v string, err error) { - if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldCreatedBy is only allowed on UpdateOne operations") +// ClearOwnedAgents clears the "owned_agents" edge to the Agent entity. +func (m *UserMutation) ClearOwnedAgents() { + m.clearedowned_agents = true +} + +// OwnedAgentsCleared reports if the "owned_agents" edge to the Agent entity was cleared. +func (m *UserMutation) OwnedAgentsCleared() bool { + return m.clearedowned_agents +} + +// RemoveOwnedAgentIDs removes the "owned_agents" edge to the Agent entity by IDs. +func (m *UserMutation) RemoveOwnedAgentIDs(ids ...uuid.UUID) { + if m.removedowned_agents == nil { + m.removedowned_agents = make(map[uuid.UUID]struct{}) } - if m.id == nil || m.oldValue == nil { - return v, errors.New("OldCreatedBy requires an ID field in the mutation") + for i := range ids { + delete(m.owned_agents, ids[i]) + m.removedowned_agents[ids[i]] = struct{}{} } - oldValue, err := m.oldValue(ctx) - if err != nil { - return v, fmt.Errorf("querying old value for OldCreatedBy: %w", err) +} + +// RemovedOwnedAgents returns the removed IDs of the "owned_agents" edge to the Agent entity. +func (m *UserMutation) RemovedOwnedAgentsIDs() (ids []uuid.UUID) { + for id := range m.removedowned_agents { + ids = append(ids, id) } - return oldValue.CreatedBy, nil + return } -// ClearCreatedBy clears the value of the "created_by" field. -func (m *ProjectMutation) ClearCreatedBy() { - m.created_by = nil - m.clearedFields[project.FieldCreatedBy] = struct{}{} +// OwnedAgentsIDs returns the "owned_agents" edge IDs in the mutation. +func (m *UserMutation) OwnedAgentsIDs() (ids []uuid.UUID) { + for id := range m.owned_agents { + ids = append(ids, id) + } + return } -// CreatedByCleared returns if the "created_by" field was cleared in this mutation. -func (m *ProjectMutation) CreatedByCleared() bool { - _, ok := m.clearedFields[project.FieldCreatedBy] - return ok +// ResetOwnedAgents resets all changes to the "owned_agents" edge. +func (m *UserMutation) ResetOwnedAgents() { + m.owned_agents = nil + m.clearedowned_agents = false + m.removedowned_agents = nil } -// ResetCreatedBy resets all changes to the "created_by" field. -func (m *ProjectMutation) ResetCreatedBy() { - m.created_by = nil - delete(m.clearedFields, project.FieldCreatedBy) +// AddOwnedGroupIDs adds the "owned_groups" edge to the Group entity by ids. +func (m *UserMutation) AddOwnedGroupIDs(ids ...uuid.UUID) { + if m.owned_groups == nil { + m.owned_groups = make(map[uuid.UUID]struct{}) + } + for i := range ids { + m.owned_groups[ids[i]] = struct{}{} + } } -// SetOwnerID sets the "owner_id" field. -func (m *ProjectMutation) SetOwnerID(s string) { - m.owner_id = &s +// ClearOwnedGroups clears the "owned_groups" edge to the Group entity. +func (m *UserMutation) ClearOwnedGroups() { + m.clearedowned_groups = true } -// OwnerID returns the value of the "owner_id" field in the mutation. -func (m *ProjectMutation) OwnerID() (r string, exists bool) { - v := m.owner_id - if v == nil { - return - } - return *v, true +// OwnedGroupsCleared reports if the "owned_groups" edge to the Group entity was cleared. +func (m *UserMutation) OwnedGroupsCleared() bool { + return m.clearedowned_groups } -// OldOwnerID returns the old "owner_id" field's value of the Project entity. -// If the Project object wasn't provided to the builder, the object is fetched from the database. -// An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *ProjectMutation) OldOwnerID(ctx context.Context) (v string, err error) { - if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldOwnerID is only allowed on UpdateOne operations") +// RemoveOwnedGroupIDs removes the "owned_groups" edge to the Group entity by IDs. +func (m *UserMutation) RemoveOwnedGroupIDs(ids ...uuid.UUID) { + if m.removedowned_groups == nil { + m.removedowned_groups = make(map[uuid.UUID]struct{}) } - if m.id == nil || m.oldValue == nil { - return v, errors.New("OldOwnerID requires an ID field in the mutation") + for i := range ids { + delete(m.owned_groups, ids[i]) + m.removedowned_groups[ids[i]] = struct{}{} } - oldValue, err := m.oldValue(ctx) - if err != nil { - return v, fmt.Errorf("querying old value for OldOwnerID: %w", err) +} + +// RemovedOwnedGroups returns the removed IDs of the "owned_groups" edge to the Group entity. +func (m *UserMutation) RemovedOwnedGroupsIDs() (ids []uuid.UUID) { + for id := range m.removedowned_groups { + ids = append(ids, id) } - return oldValue.OwnerID, nil + return } -// ClearOwnerID clears the value of the "owner_id" field. -func (m *ProjectMutation) ClearOwnerID() { - m.owner_id = nil - m.clearedFields[project.FieldOwnerID] = struct{}{} +// OwnedGroupsIDs returns the "owned_groups" edge IDs in the mutation. +func (m *UserMutation) OwnedGroupsIDs() (ids []uuid.UUID) { + for id := range m.owned_groups { + ids = append(ids, id) + } + return +} + +// ResetOwnedGroups resets all changes to the "owned_groups" edge. +func (m *UserMutation) ResetOwnedGroups() { + m.owned_groups = nil + m.clearedowned_groups = false + m.removedowned_groups = nil +} + +// AddMembershipIDs adds the "memberships" edge to the GroupMembership entity by ids. +func (m *UserMutation) AddMembershipIDs(ids ...uuid.UUID) { + if m.memberships == nil { + m.memberships = make(map[uuid.UUID]struct{}) + } + for i := range ids { + m.memberships[ids[i]] = struct{}{} + } } -// OwnerIDCleared returns if the "owner_id" field was cleared in this mutation. -func (m *ProjectMutation) OwnerIDCleared() bool { - _, ok := m.clearedFields[project.FieldOwnerID] - return ok +// ClearMemberships clears the "memberships" edge to the GroupMembership entity. +func (m *UserMutation) ClearMemberships() { + m.clearedmemberships = true } -// ResetOwnerID resets all changes to the "owner_id" field. -func (m *ProjectMutation) ResetOwnerID() { - m.owner_id = nil - delete(m.clearedFields, project.FieldOwnerID) +// MembershipsCleared reports if the "memberships" edge to the GroupMembership entity was cleared. +func (m *UserMutation) MembershipsCleared() bool { + return m.clearedmemberships } -// SetVisibility sets the "visibility" field. -func (m *ProjectMutation) SetVisibility(s string) { - m.visibility = &s +// RemoveMembershipIDs removes the "memberships" edge to the GroupMembership entity by IDs. +func (m *UserMutation) RemoveMembershipIDs(ids ...uuid.UUID) { + if m.removedmemberships == nil { + m.removedmemberships = make(map[uuid.UUID]struct{}) + } + for i := range ids { + delete(m.memberships, ids[i]) + m.removedmemberships[ids[i]] = struct{}{} + } } -// Visibility returns the value of the "visibility" field in the mutation. -func (m *ProjectMutation) Visibility() (r string, exists bool) { - v := m.visibility - if v == nil { - return +// RemovedMemberships returns the removed IDs of the "memberships" edge to the GroupMembership entity. +func (m *UserMutation) RemovedMembershipsIDs() (ids []uuid.UUID) { + for id := range m.removedmemberships { + ids = append(ids, id) } - return *v, true + return } -// OldVisibility returns the old "visibility" field's value of the Project entity. -// If the Project object wasn't provided to the builder, the object is fetched from the database. -// An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *ProjectMutation) OldVisibility(ctx context.Context) (v string, err error) { - if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldVisibility is only allowed on UpdateOne operations") - } - if m.id == nil || m.oldValue == nil { - return v, errors.New("OldVisibility requires an ID field in the mutation") - } - oldValue, err := m.oldValue(ctx) - if err != nil { - return v, fmt.Errorf("querying old value for OldVisibility: %w", err) +// MembershipsIDs returns the "memberships" edge IDs in the mutation. +func (m *UserMutation) MembershipsIDs() (ids []uuid.UUID) { + for id := range m.memberships { + ids = append(ids, id) } - return oldValue.Visibility, nil + return } -// ResetVisibility resets all changes to the "visibility" field. -func (m *ProjectMutation) ResetVisibility() { - m.visibility = nil +// ResetMemberships resets all changes to the "memberships" edge. +func (m *UserMutation) ResetMemberships() { + m.memberships = nil + m.clearedmemberships = false + m.removedmemberships = nil } -// AddAgentIDs adds the "agents" edge to the Agent entity by ids. -func (m *ProjectMutation) AddAgentIDs(ids ...uuid.UUID) { - if m.agents == nil { - m.agents = make(map[uuid.UUID]struct{}) +// AddPolicyBindingIDs adds the "policy_bindings" edge to the PolicyBinding entity by ids. +func (m *UserMutation) AddPolicyBindingIDs(ids ...uuid.UUID) { + if m.policy_bindings == nil { + m.policy_bindings = make(map[uuid.UUID]struct{}) } for i := range ids { - m.agents[ids[i]] = struct{}{} + m.policy_bindings[ids[i]] = struct{}{} } } -// ClearAgents clears the "agents" edge to the Agent entity. -func (m *ProjectMutation) ClearAgents() { - m.clearedagents = true +// ClearPolicyBindings clears the "policy_bindings" edge to the PolicyBinding entity. +func (m *UserMutation) ClearPolicyBindings() { + m.clearedpolicy_bindings = true } -// AgentsCleared reports if the "agents" edge to the Agent entity was cleared. -func (m *ProjectMutation) AgentsCleared() bool { - return m.clearedagents +// PolicyBindingsCleared reports if the "policy_bindings" edge to the PolicyBinding entity was cleared. +func (m *UserMutation) PolicyBindingsCleared() bool { + return m.clearedpolicy_bindings } -// RemoveAgentIDs removes the "agents" edge to the Agent entity by IDs. -func (m *ProjectMutation) RemoveAgentIDs(ids ...uuid.UUID) { - if m.removedagents == nil { - m.removedagents = make(map[uuid.UUID]struct{}) +// RemovePolicyBindingIDs removes the "policy_bindings" edge to the PolicyBinding entity by IDs. +func (m *UserMutation) RemovePolicyBindingIDs(ids ...uuid.UUID) { + if m.removedpolicy_bindings == nil { + m.removedpolicy_bindings = make(map[uuid.UUID]struct{}) } for i := range ids { - delete(m.agents, ids[i]) - m.removedagents[ids[i]] = struct{}{} + delete(m.policy_bindings, ids[i]) + m.removedpolicy_bindings[ids[i]] = struct{}{} } } -// RemovedAgents returns the removed IDs of the "agents" edge to the Agent entity. -func (m *ProjectMutation) RemovedAgentsIDs() (ids []uuid.UUID) { - for id := range m.removedagents { +// RemovedPolicyBindings returns the removed IDs of the "policy_bindings" edge to the PolicyBinding entity. +func (m *UserMutation) RemovedPolicyBindingsIDs() (ids []uuid.UUID) { + for id := range m.removedpolicy_bindings { ids = append(ids, id) } return } -// AgentsIDs returns the "agents" edge IDs in the mutation. -func (m *ProjectMutation) AgentsIDs() (ids []uuid.UUID) { - for id := range m.agents { +// PolicyBindingsIDs returns the "policy_bindings" edge IDs in the mutation. +func (m *UserMutation) PolicyBindingsIDs() (ids []uuid.UUID) { + for id := range m.policy_bindings { ids = append(ids, id) } return } -// ResetAgents resets all changes to the "agents" edge. -func (m *ProjectMutation) ResetAgents() { - m.agents = nil - m.clearedagents = false - m.removedagents = nil +// ResetPolicyBindings resets all changes to the "policy_bindings" edge. +func (m *UserMutation) ResetPolicyBindings() { + m.policy_bindings = nil + m.clearedpolicy_bindings = false + m.removedpolicy_bindings = nil } -// Where appends a list predicates to the ProjectMutation builder. -func (m *ProjectMutation) Where(ps ...predicate.Project) { +// Where appends a list predicates to the UserMutation builder. +func (m *UserMutation) Where(ps ...predicate.User) { m.predicates = append(m.predicates, ps...) } -// WhereP appends storage-level predicates to the ProjectMutation builder. Using this method, +// WhereP appends storage-level predicates to the UserMutation builder. Using this method, // users can use type-assertion to append predicates that do not depend on any generated package. -func (m *ProjectMutation) WhereP(ps ...func(*sql.Selector)) { - p := make([]predicate.Project, len(ps)) +func (m *UserMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.User, len(ps)) for i := range ps { p[i] = ps[i] } @@ -6444,54 +30988,51 @@ func (m *ProjectMutation) WhereP(ps ...func(*sql.Selector)) { } // Op returns the operation name. -func (m *ProjectMutation) Op() Op { +func (m *UserMutation) Op() Op { return m.op } // SetOp allows setting the mutation operation. -func (m *ProjectMutation) SetOp(op Op) { +func (m *UserMutation) SetOp(op Op) { m.op = op } -// Type returns the node type of this mutation (Project). -func (m *ProjectMutation) Type() string { +// Type returns the node type of this mutation (User). +func (m *UserMutation) Type() string { return m.typ } // Fields returns all fields that were changed during this mutation. Note that in // order to get all numeric fields that were incremented/decremented, call // AddedFields(). -func (m *ProjectMutation) Fields() []string { - fields := make([]string, 0, 10) - if m.name != nil { - fields = append(fields, project.FieldName) - } - if m.slug != nil { - fields = append(fields, project.FieldSlug) +func (m *UserMutation) Fields() []string { + fields := make([]string, 0, 9) + if m.email != nil { + fields = append(fields, user.FieldEmail) } - if m.git_remote != nil { - fields = append(fields, project.FieldGitRemote) + if m.display_name != nil { + fields = append(fields, user.FieldDisplayName) } - if m.labels != nil { - fields = append(fields, project.FieldLabels) + if m.avatar_url != nil { + fields = append(fields, user.FieldAvatarURL) } - if m.annotations != nil { - fields = append(fields, project.FieldAnnotations) + if m.role != nil { + fields = append(fields, user.FieldRole) } - if m.created != nil { - fields = append(fields, project.FieldCreated) + if m.status != nil { + fields = append(fields, user.FieldStatus) } - if m.updated != nil { - fields = append(fields, project.FieldUpdated) + if m.preferences != nil { + fields = append(fields, user.FieldPreferences) } - if m.created_by != nil { - fields = append(fields, project.FieldCreatedBy) + if m.created != nil { + fields = append(fields, user.FieldCreated) } - if m.owner_id != nil { - fields = append(fields, project.FieldOwnerID) + if m.last_login != nil { + fields = append(fields, user.FieldLastLogin) } - if m.visibility != nil { - fields = append(fields, project.FieldVisibility) + if m.last_seen != nil { + fields = append(fields, user.FieldLastSeen) } return fields } @@ -6499,28 +31040,26 @@ func (m *ProjectMutation) Fields() []string { // Field returns the value of a field with the given name. The second boolean // return value indicates that this field was not set, or was not defined in the // schema. -func (m *ProjectMutation) Field(name string) (ent.Value, bool) { +func (m *UserMutation) Field(name string) (ent.Value, bool) { switch name { - case project.FieldName: - return m.Name() - case project.FieldSlug: - return m.Slug() - case project.FieldGitRemote: - return m.GitRemote() - case project.FieldLabels: - return m.Labels() - case project.FieldAnnotations: - return m.Annotations() - case project.FieldCreated: + case user.FieldEmail: + return m.Email() + case user.FieldDisplayName: + return m.DisplayName() + case user.FieldAvatarURL: + return m.AvatarURL() + case user.FieldRole: + return m.Role() + case user.FieldStatus: + return m.Status() + case user.FieldPreferences: + return m.Preferences() + case user.FieldCreated: return m.Created() - case project.FieldUpdated: - return m.Updated() - case project.FieldCreatedBy: - return m.CreatedBy() - case project.FieldOwnerID: - return m.OwnerID() - case project.FieldVisibility: - return m.Visibility() + case user.FieldLastLogin: + return m.LastLogin() + case user.FieldLastSeen: + return m.LastSeen() } return nil, false } @@ -6528,239 +31067,257 @@ func (m *ProjectMutation) Field(name string) (ent.Value, bool) { // OldField returns the old value of the field from the database. An error is // returned if the mutation operation is not UpdateOne, or the query to the // database failed. -func (m *ProjectMutation) OldField(ctx context.Context, name string) (ent.Value, error) { +func (m *UserMutation) OldField(ctx context.Context, name string) (ent.Value, error) { switch name { - case project.FieldName: - return m.OldName(ctx) - case project.FieldSlug: - return m.OldSlug(ctx) - case project.FieldGitRemote: - return m.OldGitRemote(ctx) - case project.FieldLabels: - return m.OldLabels(ctx) - case project.FieldAnnotations: - return m.OldAnnotations(ctx) - case project.FieldCreated: + case user.FieldEmail: + return m.OldEmail(ctx) + case user.FieldDisplayName: + return m.OldDisplayName(ctx) + case user.FieldAvatarURL: + return m.OldAvatarURL(ctx) + case user.FieldRole: + return m.OldRole(ctx) + case user.FieldStatus: + return m.OldStatus(ctx) + case user.FieldPreferences: + return m.OldPreferences(ctx) + case user.FieldCreated: return m.OldCreated(ctx) - case project.FieldUpdated: - return m.OldUpdated(ctx) - case project.FieldCreatedBy: - return m.OldCreatedBy(ctx) - case project.FieldOwnerID: - return m.OldOwnerID(ctx) - case project.FieldVisibility: - return m.OldVisibility(ctx) + case user.FieldLastLogin: + return m.OldLastLogin(ctx) + case user.FieldLastSeen: + return m.OldLastSeen(ctx) } - return nil, fmt.Errorf("unknown Project field %s", name) + return nil, fmt.Errorf("unknown User field %s", name) } // SetField sets the value of a field with the given name. It returns an error if // the field is not defined in the schema, or if the type mismatched the field // type. -func (m *ProjectMutation) SetField(name string, value ent.Value) error { +func (m *UserMutation) SetField(name string, value ent.Value) error { switch name { - case project.FieldName: + case user.FieldEmail: v, ok := value.(string) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetName(v) + m.SetEmail(v) return nil - case project.FieldSlug: + case user.FieldDisplayName: v, ok := value.(string) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetSlug(v) + m.SetDisplayName(v) return nil - case project.FieldGitRemote: + case user.FieldAvatarURL: v, ok := value.(string) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetGitRemote(v) + m.SetAvatarURL(v) return nil - case project.FieldLabels: - v, ok := value.(map[string]string) + case user.FieldRole: + v, ok := value.(user.Role) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetLabels(v) + m.SetRole(v) return nil - case project.FieldAnnotations: - v, ok := value.(map[string]string) + case user.FieldStatus: + v, ok := value.(user.Status) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetAnnotations(v) + m.SetStatus(v) return nil - case project.FieldCreated: - v, ok := value.(time.Time) + case user.FieldPreferences: + v, ok := value.(*schema.UserPreferences) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetCreated(v) + m.SetPreferences(v) return nil - case project.FieldUpdated: + case user.FieldCreated: v, ok := value.(time.Time) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetUpdated(v) - return nil - case project.FieldCreatedBy: - v, ok := value.(string) - if !ok { - return fmt.Errorf("unexpected type %T for field %s", value, name) - } - m.SetCreatedBy(v) + m.SetCreated(v) return nil - case project.FieldOwnerID: - v, ok := value.(string) + case user.FieldLastLogin: + v, ok := value.(time.Time) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetOwnerID(v) + m.SetLastLogin(v) return nil - case project.FieldVisibility: - v, ok := value.(string) + case user.FieldLastSeen: + v, ok := value.(time.Time) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetVisibility(v) + m.SetLastSeen(v) return nil } - return fmt.Errorf("unknown Project field %s", name) + return fmt.Errorf("unknown User field %s", name) } // AddedFields returns all numeric fields that were incremented/decremented during // this mutation. -func (m *ProjectMutation) AddedFields() []string { +func (m *UserMutation) AddedFields() []string { return nil } // AddedField returns the numeric value that was incremented/decremented on a field // with the given name. The second boolean return value indicates that this field // was not set, or was not defined in the schema. -func (m *ProjectMutation) AddedField(name string) (ent.Value, bool) { +func (m *UserMutation) AddedField(name string) (ent.Value, bool) { return nil, false } // AddField adds the value to the field with the given name. It returns an error if // the field is not defined in the schema, or if the type mismatched the field // type. -func (m *ProjectMutation) AddField(name string, value ent.Value) error { +func (m *UserMutation) AddField(name string, value ent.Value) error { switch name { } - return fmt.Errorf("unknown Project numeric field %s", name) + return fmt.Errorf("unknown User numeric field %s", name) } // ClearedFields returns all nullable fields that were cleared during this // mutation. -func (m *ProjectMutation) ClearedFields() []string { +func (m *UserMutation) ClearedFields() []string { var fields []string - if m.FieldCleared(project.FieldGitRemote) { - fields = append(fields, project.FieldGitRemote) - } - if m.FieldCleared(project.FieldLabels) { - fields = append(fields, project.FieldLabels) + if m.FieldCleared(user.FieldAvatarURL) { + fields = append(fields, user.FieldAvatarURL) } - if m.FieldCleared(project.FieldAnnotations) { - fields = append(fields, project.FieldAnnotations) + if m.FieldCleared(user.FieldPreferences) { + fields = append(fields, user.FieldPreferences) } - if m.FieldCleared(project.FieldCreatedBy) { - fields = append(fields, project.FieldCreatedBy) + if m.FieldCleared(user.FieldLastLogin) { + fields = append(fields, user.FieldLastLogin) } - if m.FieldCleared(project.FieldOwnerID) { - fields = append(fields, project.FieldOwnerID) + if m.FieldCleared(user.FieldLastSeen) { + fields = append(fields, user.FieldLastSeen) } return fields } // FieldCleared returns a boolean indicating if a field with the given name was // cleared in this mutation. -func (m *ProjectMutation) FieldCleared(name string) bool { +func (m *UserMutation) FieldCleared(name string) bool { _, ok := m.clearedFields[name] return ok } // ClearField clears the value of the field with the given name. It returns an // error if the field is not defined in the schema. -func (m *ProjectMutation) ClearField(name string) error { +func (m *UserMutation) ClearField(name string) error { switch name { - case project.FieldGitRemote: - m.ClearGitRemote() - return nil - case project.FieldLabels: - m.ClearLabels() + case user.FieldAvatarURL: + m.ClearAvatarURL() return nil - case project.FieldAnnotations: - m.ClearAnnotations() + case user.FieldPreferences: + m.ClearPreferences() return nil - case project.FieldCreatedBy: - m.ClearCreatedBy() + case user.FieldLastLogin: + m.ClearLastLogin() return nil - case project.FieldOwnerID: - m.ClearOwnerID() + case user.FieldLastSeen: + m.ClearLastSeen() return nil } - return fmt.Errorf("unknown Project nullable field %s", name) + return fmt.Errorf("unknown User nullable field %s", name) } // ResetField resets all changes in the mutation for the field with the given name. // It returns an error if the field is not defined in the schema. -func (m *ProjectMutation) ResetField(name string) error { +func (m *UserMutation) ResetField(name string) error { switch name { - case project.FieldName: - m.ResetName() - return nil - case project.FieldSlug: - m.ResetSlug() + case user.FieldEmail: + m.ResetEmail() return nil - case project.FieldGitRemote: - m.ResetGitRemote() + case user.FieldDisplayName: + m.ResetDisplayName() return nil - case project.FieldLabels: - m.ResetLabels() + case user.FieldAvatarURL: + m.ResetAvatarURL() return nil - case project.FieldAnnotations: - m.ResetAnnotations() + case user.FieldRole: + m.ResetRole() return nil - case project.FieldCreated: - m.ResetCreated() + case user.FieldStatus: + m.ResetStatus() return nil - case project.FieldUpdated: - m.ResetUpdated() + case user.FieldPreferences: + m.ResetPreferences() return nil - case project.FieldCreatedBy: - m.ResetCreatedBy() + case user.FieldCreated: + m.ResetCreated() return nil - case project.FieldOwnerID: - m.ResetOwnerID() + case user.FieldLastLogin: + m.ResetLastLogin() return nil - case project.FieldVisibility: - m.ResetVisibility() + case user.FieldLastSeen: + m.ResetLastSeen() return nil } - return fmt.Errorf("unknown Project field %s", name) + return fmt.Errorf("unknown User field %s", name) } // AddedEdges returns all edge names that were set/added in this mutation. -func (m *ProjectMutation) AddedEdges() []string { - edges := make([]string, 0, 1) - if m.agents != nil { - edges = append(edges, project.EdgeAgents) +func (m *UserMutation) AddedEdges() []string { + edges := make([]string, 0, 5) + if m.created_agents != nil { + edges = append(edges, user.EdgeCreatedAgents) + } + if m.owned_agents != nil { + edges = append(edges, user.EdgeOwnedAgents) + } + if m.owned_groups != nil { + edges = append(edges, user.EdgeOwnedGroups) + } + if m.memberships != nil { + edges = append(edges, user.EdgeMemberships) + } + if m.policy_bindings != nil { + edges = append(edges, user.EdgePolicyBindings) } return edges } // AddedIDs returns all IDs (to other nodes) that were added for the given edge // name in this mutation. -func (m *ProjectMutation) AddedIDs(name string) []ent.Value { +func (m *UserMutation) AddedIDs(name string) []ent.Value { switch name { - case project.EdgeAgents: - ids := make([]ent.Value, 0, len(m.agents)) - for id := range m.agents { + case user.EdgeCreatedAgents: + ids := make([]ent.Value, 0, len(m.created_agents)) + for id := range m.created_agents { + ids = append(ids, id) + } + return ids + case user.EdgeOwnedAgents: + ids := make([]ent.Value, 0, len(m.owned_agents)) + for id := range m.owned_agents { + ids = append(ids, id) + } + return ids + case user.EdgeOwnedGroups: + ids := make([]ent.Value, 0, len(m.owned_groups)) + for id := range m.owned_groups { + ids = append(ids, id) + } + return ids + case user.EdgeMemberships: + ids := make([]ent.Value, 0, len(m.memberships)) + for id := range m.memberships { + ids = append(ids, id) + } + return ids + case user.EdgePolicyBindings: + ids := make([]ent.Value, 0, len(m.policy_bindings)) + for id := range m.policy_bindings { ids = append(ids, id) } return ids @@ -6769,112 +31326,167 @@ func (m *ProjectMutation) AddedIDs(name string) []ent.Value { } // RemovedEdges returns all edge names that were removed in this mutation. -func (m *ProjectMutation) RemovedEdges() []string { - edges := make([]string, 0, 1) - if m.removedagents != nil { - edges = append(edges, project.EdgeAgents) +func (m *UserMutation) RemovedEdges() []string { + edges := make([]string, 0, 5) + if m.removedcreated_agents != nil { + edges = append(edges, user.EdgeCreatedAgents) + } + if m.removedowned_agents != nil { + edges = append(edges, user.EdgeOwnedAgents) + } + if m.removedowned_groups != nil { + edges = append(edges, user.EdgeOwnedGroups) + } + if m.removedmemberships != nil { + edges = append(edges, user.EdgeMemberships) + } + if m.removedpolicy_bindings != nil { + edges = append(edges, user.EdgePolicyBindings) } return edges } // RemovedIDs returns all IDs (to other nodes) that were removed for the edge with // the given name in this mutation. -func (m *ProjectMutation) RemovedIDs(name string) []ent.Value { +func (m *UserMutation) RemovedIDs(name string) []ent.Value { switch name { - case project.EdgeAgents: - ids := make([]ent.Value, 0, len(m.removedagents)) - for id := range m.removedagents { + case user.EdgeCreatedAgents: + ids := make([]ent.Value, 0, len(m.removedcreated_agents)) + for id := range m.removedcreated_agents { + ids = append(ids, id) + } + return ids + case user.EdgeOwnedAgents: + ids := make([]ent.Value, 0, len(m.removedowned_agents)) + for id := range m.removedowned_agents { + ids = append(ids, id) + } + return ids + case user.EdgeOwnedGroups: + ids := make([]ent.Value, 0, len(m.removedowned_groups)) + for id := range m.removedowned_groups { + ids = append(ids, id) + } + return ids + case user.EdgeMemberships: + ids := make([]ent.Value, 0, len(m.removedmemberships)) + for id := range m.removedmemberships { ids = append(ids, id) } return ids + case user.EdgePolicyBindings: + ids := make([]ent.Value, 0, len(m.removedpolicy_bindings)) + for id := range m.removedpolicy_bindings { + ids = append(ids, id) + } + return ids + } + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *UserMutation) ClearedEdges() []string { + edges := make([]string, 0, 5) + if m.clearedcreated_agents { + edges = append(edges, user.EdgeCreatedAgents) + } + if m.clearedowned_agents { + edges = append(edges, user.EdgeOwnedAgents) } - return nil -} - -// ClearedEdges returns all edge names that were cleared in this mutation. -func (m *ProjectMutation) ClearedEdges() []string { - edges := make([]string, 0, 1) - if m.clearedagents { - edges = append(edges, project.EdgeAgents) + if m.clearedowned_groups { + edges = append(edges, user.EdgeOwnedGroups) + } + if m.clearedmemberships { + edges = append(edges, user.EdgeMemberships) + } + if m.clearedpolicy_bindings { + edges = append(edges, user.EdgePolicyBindings) } return edges } // EdgeCleared returns a boolean which indicates if the edge with the given name // was cleared in this mutation. -func (m *ProjectMutation) EdgeCleared(name string) bool { +func (m *UserMutation) EdgeCleared(name string) bool { switch name { - case project.EdgeAgents: - return m.clearedagents + case user.EdgeCreatedAgents: + return m.clearedcreated_agents + case user.EdgeOwnedAgents: + return m.clearedowned_agents + case user.EdgeOwnedGroups: + return m.clearedowned_groups + case user.EdgeMemberships: + return m.clearedmemberships + case user.EdgePolicyBindings: + return m.clearedpolicy_bindings } return false } // ClearEdge clears the value of the edge with the given name. It returns an error // if that edge is not defined in the schema. -func (m *ProjectMutation) ClearEdge(name string) error { +func (m *UserMutation) ClearEdge(name string) error { switch name { } - return fmt.Errorf("unknown Project unique edge %s", name) + return fmt.Errorf("unknown User unique edge %s", name) } // ResetEdge resets all changes to the edge with the given name in this mutation. // It returns an error if the edge is not defined in the schema. -func (m *ProjectMutation) ResetEdge(name string) error { +func (m *UserMutation) ResetEdge(name string) error { switch name { - case project.EdgeAgents: - m.ResetAgents() + case user.EdgeCreatedAgents: + m.ResetCreatedAgents() + return nil + case user.EdgeOwnedAgents: + m.ResetOwnedAgents() + return nil + case user.EdgeOwnedGroups: + m.ResetOwnedGroups() + return nil + case user.EdgeMemberships: + m.ResetMemberships() + return nil + case user.EdgePolicyBindings: + m.ResetPolicyBindings() return nil } - return fmt.Errorf("unknown Project edge %s", name) + return fmt.Errorf("unknown User edge %s", name) } -// UserMutation represents an operation that mutates the User nodes in the graph. -type UserMutation struct { +// UserAccessTokenMutation represents an operation that mutates the UserAccessToken nodes in the graph. +type UserAccessTokenMutation struct { config - op Op - typ string - id *uuid.UUID - email *string - display_name *string - avatar_url *string - role *user.Role - status *user.Status - preferences **schema.UserPreferences - created *time.Time - last_login *time.Time - clearedFields map[string]struct{} - created_agents map[uuid.UUID]struct{} - removedcreated_agents map[uuid.UUID]struct{} - clearedcreated_agents bool - owned_agents map[uuid.UUID]struct{} - removedowned_agents map[uuid.UUID]struct{} - clearedowned_agents bool - owned_groups map[uuid.UUID]struct{} - removedowned_groups map[uuid.UUID]struct{} - clearedowned_groups bool - memberships map[uuid.UUID]struct{} - removedmemberships map[uuid.UUID]struct{} - clearedmemberships bool - policy_bindings map[uuid.UUID]struct{} - removedpolicy_bindings map[uuid.UUID]struct{} - clearedpolicy_bindings bool - done bool - oldValue func(context.Context) (*User, error) - predicates []predicate.User + op Op + typ string + id *uuid.UUID + user_id *uuid.UUID + name *string + prefix *string + key_hash *string + project_id *uuid.UUID + scopes *string + revoked *bool + expires_at *time.Time + last_used *time.Time + created *time.Time + clearedFields map[string]struct{} + done bool + oldValue func(context.Context) (*UserAccessToken, error) + predicates []predicate.UserAccessToken } -var _ ent.Mutation = (*UserMutation)(nil) +var _ ent.Mutation = (*UserAccessTokenMutation)(nil) -// userOption allows management of the mutation configuration using functional options. -type userOption func(*UserMutation) +// useraccesstokenOption allows management of the mutation configuration using functional options. +type useraccesstokenOption func(*UserAccessTokenMutation) -// newUserMutation creates new mutation for the User entity. -func newUserMutation(c config, op Op, opts ...userOption) *UserMutation { - m := &UserMutation{ +// newUserAccessTokenMutation creates new mutation for the UserAccessToken entity. +func newUserAccessTokenMutation(c config, op Op, opts ...useraccesstokenOption) *UserAccessTokenMutation { + m := &UserAccessTokenMutation{ config: c, op: op, - typ: TypeUser, + typ: TypeUserAccessToken, clearedFields: make(map[string]struct{}), } for _, opt := range opts { @@ -6883,20 +31495,20 @@ func newUserMutation(c config, op Op, opts ...userOption) *UserMutation { return m } -// withUserID sets the ID field of the mutation. -func withUserID(id uuid.UUID) userOption { - return func(m *UserMutation) { +// withUserAccessTokenID sets the ID field of the mutation. +func withUserAccessTokenID(id uuid.UUID) useraccesstokenOption { + return func(m *UserAccessTokenMutation) { var ( err error once sync.Once - value *User + value *UserAccessToken ) - m.oldValue = func(ctx context.Context) (*User, error) { + m.oldValue = func(ctx context.Context) (*UserAccessToken, error) { once.Do(func() { if m.done { err = errors.New("querying old values post mutation is not allowed") } else { - value, err = m.Client().User.Get(ctx, id) + value, err = m.Client().UserAccessToken.Get(ctx, id) } }) return value, err @@ -6905,10 +31517,10 @@ func withUserID(id uuid.UUID) userOption { } } -// withUser sets the old User of the mutation. -func withUser(node *User) userOption { - return func(m *UserMutation) { - m.oldValue = func(context.Context) (*User, error) { +// withUserAccessToken sets the old UserAccessToken of the mutation. +func withUserAccessToken(node *UserAccessToken) useraccesstokenOption { + return func(m *UserAccessTokenMutation) { + m.oldValue = func(context.Context) (*UserAccessToken, error) { return node, nil } m.id = &node.ID @@ -6917,663 +31529,452 @@ func withUser(node *User) userOption { // Client returns a new `ent.Client` from the mutation. If the mutation was // executed in a transaction (ent.Tx), a transactional client is returned. -func (m UserMutation) Client() *Client { +func (m UserAccessTokenMutation) Client() *Client { client := &Client{config: m.config} client.init() return client } // Tx returns an `ent.Tx` for mutations that were executed in transactions; -// it returns an error otherwise. -func (m UserMutation) Tx() (*Tx, error) { - if _, ok := m.driver.(*txDriver); !ok { - return nil, errors.New("ent: mutation is not running in a transaction") - } - tx := &Tx{config: m.config} - tx.init() - return tx, nil -} - -// SetID sets the value of the id field. Note that this -// operation is only accepted on creation of User entities. -func (m *UserMutation) SetID(id uuid.UUID) { - m.id = &id -} - -// ID returns the ID value in the mutation. Note that the ID is only available -// if it was provided to the builder or after it was returned from the database. -func (m *UserMutation) ID() (id uuid.UUID, exists bool) { - if m.id == nil { - return - } - return *m.id, true -} - -// IDs queries the database and returns the entity ids that match the mutation's predicate. -// That means, if the mutation is applied within a transaction with an isolation level such -// as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated -// or updated by the mutation. -func (m *UserMutation) IDs(ctx context.Context) ([]uuid.UUID, error) { - switch { - case m.op.Is(OpUpdateOne | OpDeleteOne): - id, exists := m.ID() - if exists { - return []uuid.UUID{id}, nil - } - fallthrough - case m.op.Is(OpUpdate | OpDelete): - return m.Client().User.Query().Where(m.predicates...).IDs(ctx) - default: - return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) - } -} - -// SetEmail sets the "email" field. -func (m *UserMutation) SetEmail(s string) { - m.email = &s -} - -// Email returns the value of the "email" field in the mutation. -func (m *UserMutation) Email() (r string, exists bool) { - v := m.email - if v == nil { - return - } - return *v, true -} - -// OldEmail returns the old "email" field's value of the User entity. -// If the User object wasn't provided to the builder, the object is fetched from the database. -// An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *UserMutation) OldEmail(ctx context.Context) (v string, err error) { - if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldEmail is only allowed on UpdateOne operations") - } - if m.id == nil || m.oldValue == nil { - return v, errors.New("OldEmail requires an ID field in the mutation") - } - oldValue, err := m.oldValue(ctx) - if err != nil { - return v, fmt.Errorf("querying old value for OldEmail: %w", err) - } - return oldValue.Email, nil -} - -// ResetEmail resets all changes to the "email" field. -func (m *UserMutation) ResetEmail() { - m.email = nil -} - -// SetDisplayName sets the "display_name" field. -func (m *UserMutation) SetDisplayName(s string) { - m.display_name = &s -} - -// DisplayName returns the value of the "display_name" field in the mutation. -func (m *UserMutation) DisplayName() (r string, exists bool) { - v := m.display_name - if v == nil { - return - } - return *v, true -} - -// OldDisplayName returns the old "display_name" field's value of the User entity. -// If the User object wasn't provided to the builder, the object is fetched from the database. -// An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *UserMutation) OldDisplayName(ctx context.Context) (v string, err error) { - if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldDisplayName is only allowed on UpdateOne operations") - } - if m.id == nil || m.oldValue == nil { - return v, errors.New("OldDisplayName requires an ID field in the mutation") - } - oldValue, err := m.oldValue(ctx) - if err != nil { - return v, fmt.Errorf("querying old value for OldDisplayName: %w", err) - } - return oldValue.DisplayName, nil -} - -// ResetDisplayName resets all changes to the "display_name" field. -func (m *UserMutation) ResetDisplayName() { - m.display_name = nil -} - -// SetAvatarURL sets the "avatar_url" field. -func (m *UserMutation) SetAvatarURL(s string) { - m.avatar_url = &s -} - -// AvatarURL returns the value of the "avatar_url" field in the mutation. -func (m *UserMutation) AvatarURL() (r string, exists bool) { - v := m.avatar_url - if v == nil { - return - } - return *v, true -} - -// OldAvatarURL returns the old "avatar_url" field's value of the User entity. -// If the User object wasn't provided to the builder, the object is fetched from the database. -// An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *UserMutation) OldAvatarURL(ctx context.Context) (v string, err error) { - if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldAvatarURL is only allowed on UpdateOne operations") - } - if m.id == nil || m.oldValue == nil { - return v, errors.New("OldAvatarURL requires an ID field in the mutation") - } - oldValue, err := m.oldValue(ctx) - if err != nil { - return v, fmt.Errorf("querying old value for OldAvatarURL: %w", err) +// it returns an error otherwise. +func (m UserAccessTokenMutation) Tx() (*Tx, error) { + if _, ok := m.driver.(*txDriver); !ok { + return nil, errors.New("ent: mutation is not running in a transaction") } - return oldValue.AvatarURL, nil + tx := &Tx{config: m.config} + tx.init() + return tx, nil } -// ClearAvatarURL clears the value of the "avatar_url" field. -func (m *UserMutation) ClearAvatarURL() { - m.avatar_url = nil - m.clearedFields[user.FieldAvatarURL] = struct{}{} +// SetID sets the value of the id field. Note that this +// operation is only accepted on creation of UserAccessToken entities. +func (m *UserAccessTokenMutation) SetID(id uuid.UUID) { + m.id = &id } -// AvatarURLCleared returns if the "avatar_url" field was cleared in this mutation. -func (m *UserMutation) AvatarURLCleared() bool { - _, ok := m.clearedFields[user.FieldAvatarURL] - return ok +// ID returns the ID value in the mutation. Note that the ID is only available +// if it was provided to the builder or after it was returned from the database. +func (m *UserAccessTokenMutation) ID() (id uuid.UUID, exists bool) { + if m.id == nil { + return + } + return *m.id, true } -// ResetAvatarURL resets all changes to the "avatar_url" field. -func (m *UserMutation) ResetAvatarURL() { - m.avatar_url = nil - delete(m.clearedFields, user.FieldAvatarURL) +// IDs queries the database and returns the entity ids that match the mutation's predicate. +// That means, if the mutation is applied within a transaction with an isolation level such +// as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated +// or updated by the mutation. +func (m *UserAccessTokenMutation) IDs(ctx context.Context) ([]uuid.UUID, error) { + switch { + case m.op.Is(OpUpdateOne | OpDeleteOne): + id, exists := m.ID() + if exists { + return []uuid.UUID{id}, nil + } + fallthrough + case m.op.Is(OpUpdate | OpDelete): + return m.Client().UserAccessToken.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } } -// SetRole sets the "role" field. -func (m *UserMutation) SetRole(u user.Role) { - m.role = &u +// SetUserID sets the "user_id" field. +func (m *UserAccessTokenMutation) SetUserID(u uuid.UUID) { + m.user_id = &u } -// Role returns the value of the "role" field in the mutation. -func (m *UserMutation) Role() (r user.Role, exists bool) { - v := m.role +// UserID returns the value of the "user_id" field in the mutation. +func (m *UserAccessTokenMutation) UserID() (r uuid.UUID, exists bool) { + v := m.user_id if v == nil { return } return *v, true } -// OldRole returns the old "role" field's value of the User entity. -// If the User object wasn't provided to the builder, the object is fetched from the database. +// OldUserID returns the old "user_id" field's value of the UserAccessToken entity. +// If the UserAccessToken object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *UserMutation) OldRole(ctx context.Context) (v user.Role, err error) { +func (m *UserAccessTokenMutation) OldUserID(ctx context.Context) (v uuid.UUID, err error) { if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldRole is only allowed on UpdateOne operations") + return v, errors.New("OldUserID is only allowed on UpdateOne operations") } if m.id == nil || m.oldValue == nil { - return v, errors.New("OldRole requires an ID field in the mutation") + return v, errors.New("OldUserID requires an ID field in the mutation") } oldValue, err := m.oldValue(ctx) if err != nil { - return v, fmt.Errorf("querying old value for OldRole: %w", err) + return v, fmt.Errorf("querying old value for OldUserID: %w", err) } - return oldValue.Role, nil + return oldValue.UserID, nil } -// ResetRole resets all changes to the "role" field. -func (m *UserMutation) ResetRole() { - m.role = nil +// ResetUserID resets all changes to the "user_id" field. +func (m *UserAccessTokenMutation) ResetUserID() { + m.user_id = nil } -// SetStatus sets the "status" field. -func (m *UserMutation) SetStatus(u user.Status) { - m.status = &u +// SetName sets the "name" field. +func (m *UserAccessTokenMutation) SetName(s string) { + m.name = &s } -// Status returns the value of the "status" field in the mutation. -func (m *UserMutation) Status() (r user.Status, exists bool) { - v := m.status +// Name returns the value of the "name" field in the mutation. +func (m *UserAccessTokenMutation) Name() (r string, exists bool) { + v := m.name if v == nil { return } return *v, true } -// OldStatus returns the old "status" field's value of the User entity. -// If the User object wasn't provided to the builder, the object is fetched from the database. +// OldName returns the old "name" field's value of the UserAccessToken entity. +// If the UserAccessToken object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *UserMutation) OldStatus(ctx context.Context) (v user.Status, err error) { +func (m *UserAccessTokenMutation) OldName(ctx context.Context) (v string, err error) { if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldStatus is only allowed on UpdateOne operations") + return v, errors.New("OldName is only allowed on UpdateOne operations") } if m.id == nil || m.oldValue == nil { - return v, errors.New("OldStatus requires an ID field in the mutation") + return v, errors.New("OldName requires an ID field in the mutation") } oldValue, err := m.oldValue(ctx) if err != nil { - return v, fmt.Errorf("querying old value for OldStatus: %w", err) + return v, fmt.Errorf("querying old value for OldName: %w", err) } - return oldValue.Status, nil + return oldValue.Name, nil } -// ResetStatus resets all changes to the "status" field. -func (m *UserMutation) ResetStatus() { - m.status = nil +// ResetName resets all changes to the "name" field. +func (m *UserAccessTokenMutation) ResetName() { + m.name = nil } -// SetPreferences sets the "preferences" field. -func (m *UserMutation) SetPreferences(sp *schema.UserPreferences) { - m.preferences = &sp +// SetPrefix sets the "prefix" field. +func (m *UserAccessTokenMutation) SetPrefix(s string) { + m.prefix = &s } -// Preferences returns the value of the "preferences" field in the mutation. -func (m *UserMutation) Preferences() (r *schema.UserPreferences, exists bool) { - v := m.preferences +// Prefix returns the value of the "prefix" field in the mutation. +func (m *UserAccessTokenMutation) Prefix() (r string, exists bool) { + v := m.prefix if v == nil { return } return *v, true } -// OldPreferences returns the old "preferences" field's value of the User entity. -// If the User object wasn't provided to the builder, the object is fetched from the database. +// OldPrefix returns the old "prefix" field's value of the UserAccessToken entity. +// If the UserAccessToken object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *UserMutation) OldPreferences(ctx context.Context) (v *schema.UserPreferences, err error) { +func (m *UserAccessTokenMutation) OldPrefix(ctx context.Context) (v string, err error) { if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldPreferences is only allowed on UpdateOne operations") + return v, errors.New("OldPrefix is only allowed on UpdateOne operations") } if m.id == nil || m.oldValue == nil { - return v, errors.New("OldPreferences requires an ID field in the mutation") + return v, errors.New("OldPrefix requires an ID field in the mutation") } oldValue, err := m.oldValue(ctx) if err != nil { - return v, fmt.Errorf("querying old value for OldPreferences: %w", err) + return v, fmt.Errorf("querying old value for OldPrefix: %w", err) } - return oldValue.Preferences, nil -} - -// ClearPreferences clears the value of the "preferences" field. -func (m *UserMutation) ClearPreferences() { - m.preferences = nil - m.clearedFields[user.FieldPreferences] = struct{}{} -} - -// PreferencesCleared returns if the "preferences" field was cleared in this mutation. -func (m *UserMutation) PreferencesCleared() bool { - _, ok := m.clearedFields[user.FieldPreferences] - return ok + return oldValue.Prefix, nil } -// ResetPreferences resets all changes to the "preferences" field. -func (m *UserMutation) ResetPreferences() { - m.preferences = nil - delete(m.clearedFields, user.FieldPreferences) +// ResetPrefix resets all changes to the "prefix" field. +func (m *UserAccessTokenMutation) ResetPrefix() { + m.prefix = nil } -// SetCreated sets the "created" field. -func (m *UserMutation) SetCreated(t time.Time) { - m.created = &t +// SetKeyHash sets the "key_hash" field. +func (m *UserAccessTokenMutation) SetKeyHash(s string) { + m.key_hash = &s } -// Created returns the value of the "created" field in the mutation. -func (m *UserMutation) Created() (r time.Time, exists bool) { - v := m.created +// KeyHash returns the value of the "key_hash" field in the mutation. +func (m *UserAccessTokenMutation) KeyHash() (r string, exists bool) { + v := m.key_hash if v == nil { return } return *v, true } -// OldCreated returns the old "created" field's value of the User entity. -// If the User object wasn't provided to the builder, the object is fetched from the database. +// OldKeyHash returns the old "key_hash" field's value of the UserAccessToken entity. +// If the UserAccessToken object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *UserMutation) OldCreated(ctx context.Context) (v time.Time, err error) { +func (m *UserAccessTokenMutation) OldKeyHash(ctx context.Context) (v string, err error) { if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldCreated is only allowed on UpdateOne operations") + return v, errors.New("OldKeyHash is only allowed on UpdateOne operations") } if m.id == nil || m.oldValue == nil { - return v, errors.New("OldCreated requires an ID field in the mutation") + return v, errors.New("OldKeyHash requires an ID field in the mutation") } oldValue, err := m.oldValue(ctx) if err != nil { - return v, fmt.Errorf("querying old value for OldCreated: %w", err) + return v, fmt.Errorf("querying old value for OldKeyHash: %w", err) } - return oldValue.Created, nil + return oldValue.KeyHash, nil } -// ResetCreated resets all changes to the "created" field. -func (m *UserMutation) ResetCreated() { - m.created = nil +// ResetKeyHash resets all changes to the "key_hash" field. +func (m *UserAccessTokenMutation) ResetKeyHash() { + m.key_hash = nil } -// SetLastLogin sets the "last_login" field. -func (m *UserMutation) SetLastLogin(t time.Time) { - m.last_login = &t +// SetProjectID sets the "project_id" field. +func (m *UserAccessTokenMutation) SetProjectID(u uuid.UUID) { + m.project_id = &u } -// LastLogin returns the value of the "last_login" field in the mutation. -func (m *UserMutation) LastLogin() (r time.Time, exists bool) { - v := m.last_login +// ProjectID returns the value of the "project_id" field in the mutation. +func (m *UserAccessTokenMutation) ProjectID() (r uuid.UUID, exists bool) { + v := m.project_id if v == nil { return } return *v, true } -// OldLastLogin returns the old "last_login" field's value of the User entity. -// If the User object wasn't provided to the builder, the object is fetched from the database. +// OldProjectID returns the old "project_id" field's value of the UserAccessToken entity. +// If the UserAccessToken object wasn't provided to the builder, the object is fetched from the database. // An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *UserMutation) OldLastLogin(ctx context.Context) (v *time.Time, err error) { +func (m *UserAccessTokenMutation) OldProjectID(ctx context.Context) (v uuid.UUID, err error) { if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldLastLogin is only allowed on UpdateOne operations") + return v, errors.New("OldProjectID is only allowed on UpdateOne operations") } if m.id == nil || m.oldValue == nil { - return v, errors.New("OldLastLogin requires an ID field in the mutation") - } - oldValue, err := m.oldValue(ctx) - if err != nil { - return v, fmt.Errorf("querying old value for OldLastLogin: %w", err) - } - return oldValue.LastLogin, nil -} - -// ClearLastLogin clears the value of the "last_login" field. -func (m *UserMutation) ClearLastLogin() { - m.last_login = nil - m.clearedFields[user.FieldLastLogin] = struct{}{} -} - -// LastLoginCleared returns if the "last_login" field was cleared in this mutation. -func (m *UserMutation) LastLoginCleared() bool { - _, ok := m.clearedFields[user.FieldLastLogin] - return ok -} - -// ResetLastLogin resets all changes to the "last_login" field. -func (m *UserMutation) ResetLastLogin() { - m.last_login = nil - delete(m.clearedFields, user.FieldLastLogin) -} - -// AddCreatedAgentIDs adds the "created_agents" edge to the Agent entity by ids. -func (m *UserMutation) AddCreatedAgentIDs(ids ...uuid.UUID) { - if m.created_agents == nil { - m.created_agents = make(map[uuid.UUID]struct{}) - } - for i := range ids { - m.created_agents[ids[i]] = struct{}{} - } -} - -// ClearCreatedAgents clears the "created_agents" edge to the Agent entity. -func (m *UserMutation) ClearCreatedAgents() { - m.clearedcreated_agents = true -} - -// CreatedAgentsCleared reports if the "created_agents" edge to the Agent entity was cleared. -func (m *UserMutation) CreatedAgentsCleared() bool { - return m.clearedcreated_agents -} - -// RemoveCreatedAgentIDs removes the "created_agents" edge to the Agent entity by IDs. -func (m *UserMutation) RemoveCreatedAgentIDs(ids ...uuid.UUID) { - if m.removedcreated_agents == nil { - m.removedcreated_agents = make(map[uuid.UUID]struct{}) - } - for i := range ids { - delete(m.created_agents, ids[i]) - m.removedcreated_agents[ids[i]] = struct{}{} - } -} - -// RemovedCreatedAgents returns the removed IDs of the "created_agents" edge to the Agent entity. -func (m *UserMutation) RemovedCreatedAgentsIDs() (ids []uuid.UUID) { - for id := range m.removedcreated_agents { - ids = append(ids, id) - } - return -} - -// CreatedAgentsIDs returns the "created_agents" edge IDs in the mutation. -func (m *UserMutation) CreatedAgentsIDs() (ids []uuid.UUID) { - for id := range m.created_agents { - ids = append(ids, id) - } - return -} - -// ResetCreatedAgents resets all changes to the "created_agents" edge. -func (m *UserMutation) ResetCreatedAgents() { - m.created_agents = nil - m.clearedcreated_agents = false - m.removedcreated_agents = nil -} - -// AddOwnedAgentIDs adds the "owned_agents" edge to the Agent entity by ids. -func (m *UserMutation) AddOwnedAgentIDs(ids ...uuid.UUID) { - if m.owned_agents == nil { - m.owned_agents = make(map[uuid.UUID]struct{}) - } - for i := range ids { - m.owned_agents[ids[i]] = struct{}{} - } -} - -// ClearOwnedAgents clears the "owned_agents" edge to the Agent entity. -func (m *UserMutation) ClearOwnedAgents() { - m.clearedowned_agents = true -} - -// OwnedAgentsCleared reports if the "owned_agents" edge to the Agent entity was cleared. -func (m *UserMutation) OwnedAgentsCleared() bool { - return m.clearedowned_agents -} - -// RemoveOwnedAgentIDs removes the "owned_agents" edge to the Agent entity by IDs. -func (m *UserMutation) RemoveOwnedAgentIDs(ids ...uuid.UUID) { - if m.removedowned_agents == nil { - m.removedowned_agents = make(map[uuid.UUID]struct{}) + return v, errors.New("OldProjectID requires an ID field in the mutation") } - for i := range ids { - delete(m.owned_agents, ids[i]) - m.removedowned_agents[ids[i]] = struct{}{} + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldProjectID: %w", err) } + return oldValue.ProjectID, nil } -// RemovedOwnedAgents returns the removed IDs of the "owned_agents" edge to the Agent entity. -func (m *UserMutation) RemovedOwnedAgentsIDs() (ids []uuid.UUID) { - for id := range m.removedowned_agents { - ids = append(ids, id) - } - return +// ResetProjectID resets all changes to the "project_id" field. +func (m *UserAccessTokenMutation) ResetProjectID() { + m.project_id = nil } -// OwnedAgentsIDs returns the "owned_agents" edge IDs in the mutation. -func (m *UserMutation) OwnedAgentsIDs() (ids []uuid.UUID) { - for id := range m.owned_agents { - ids = append(ids, id) - } - return +// SetScopes sets the "scopes" field. +func (m *UserAccessTokenMutation) SetScopes(s string) { + m.scopes = &s } -// ResetOwnedAgents resets all changes to the "owned_agents" edge. -func (m *UserMutation) ResetOwnedAgents() { - m.owned_agents = nil - m.clearedowned_agents = false - m.removedowned_agents = nil +// Scopes returns the value of the "scopes" field in the mutation. +func (m *UserAccessTokenMutation) Scopes() (r string, exists bool) { + v := m.scopes + if v == nil { + return + } + return *v, true } -// AddOwnedGroupIDs adds the "owned_groups" edge to the Group entity by ids. -func (m *UserMutation) AddOwnedGroupIDs(ids ...uuid.UUID) { - if m.owned_groups == nil { - m.owned_groups = make(map[uuid.UUID]struct{}) +// OldScopes returns the old "scopes" field's value of the UserAccessToken entity. +// If the UserAccessToken object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *UserAccessTokenMutation) OldScopes(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldScopes is only allowed on UpdateOne operations") } - for i := range ids { - m.owned_groups[ids[i]] = struct{}{} + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldScopes requires an ID field in the mutation") } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldScopes: %w", err) + } + return oldValue.Scopes, nil } -// ClearOwnedGroups clears the "owned_groups" edge to the Group entity. -func (m *UserMutation) ClearOwnedGroups() { - m.clearedowned_groups = true +// ResetScopes resets all changes to the "scopes" field. +func (m *UserAccessTokenMutation) ResetScopes() { + m.scopes = nil } -// OwnedGroupsCleared reports if the "owned_groups" edge to the Group entity was cleared. -func (m *UserMutation) OwnedGroupsCleared() bool { - return m.clearedowned_groups +// SetRevoked sets the "revoked" field. +func (m *UserAccessTokenMutation) SetRevoked(b bool) { + m.revoked = &b } -// RemoveOwnedGroupIDs removes the "owned_groups" edge to the Group entity by IDs. -func (m *UserMutation) RemoveOwnedGroupIDs(ids ...uuid.UUID) { - if m.removedowned_groups == nil { - m.removedowned_groups = make(map[uuid.UUID]struct{}) - } - for i := range ids { - delete(m.owned_groups, ids[i]) - m.removedowned_groups[ids[i]] = struct{}{} +// Revoked returns the value of the "revoked" field in the mutation. +func (m *UserAccessTokenMutation) Revoked() (r bool, exists bool) { + v := m.revoked + if v == nil { + return } + return *v, true } -// RemovedOwnedGroups returns the removed IDs of the "owned_groups" edge to the Group entity. -func (m *UserMutation) RemovedOwnedGroupsIDs() (ids []uuid.UUID) { - for id := range m.removedowned_groups { - ids = append(ids, id) +// OldRevoked returns the old "revoked" field's value of the UserAccessToken entity. +// If the UserAccessToken object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *UserAccessTokenMutation) OldRevoked(ctx context.Context) (v bool, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldRevoked is only allowed on UpdateOne operations") } - return + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldRevoked requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldRevoked: %w", err) + } + return oldValue.Revoked, nil } -// OwnedGroupsIDs returns the "owned_groups" edge IDs in the mutation. -func (m *UserMutation) OwnedGroupsIDs() (ids []uuid.UUID) { - for id := range m.owned_groups { - ids = append(ids, id) - } - return +// ResetRevoked resets all changes to the "revoked" field. +func (m *UserAccessTokenMutation) ResetRevoked() { + m.revoked = nil } -// ResetOwnedGroups resets all changes to the "owned_groups" edge. -func (m *UserMutation) ResetOwnedGroups() { - m.owned_groups = nil - m.clearedowned_groups = false - m.removedowned_groups = nil +// SetExpiresAt sets the "expires_at" field. +func (m *UserAccessTokenMutation) SetExpiresAt(t time.Time) { + m.expires_at = &t } -// AddMembershipIDs adds the "memberships" edge to the GroupMembership entity by ids. -func (m *UserMutation) AddMembershipIDs(ids ...uuid.UUID) { - if m.memberships == nil { - m.memberships = make(map[uuid.UUID]struct{}) - } - for i := range ids { - m.memberships[ids[i]] = struct{}{} +// ExpiresAt returns the value of the "expires_at" field in the mutation. +func (m *UserAccessTokenMutation) ExpiresAt() (r time.Time, exists bool) { + v := m.expires_at + if v == nil { + return } + return *v, true } -// ClearMemberships clears the "memberships" edge to the GroupMembership entity. -func (m *UserMutation) ClearMemberships() { - m.clearedmemberships = true +// OldExpiresAt returns the old "expires_at" field's value of the UserAccessToken entity. +// If the UserAccessToken object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *UserAccessTokenMutation) OldExpiresAt(ctx context.Context) (v *time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldExpiresAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldExpiresAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldExpiresAt: %w", err) + } + return oldValue.ExpiresAt, nil } -// MembershipsCleared reports if the "memberships" edge to the GroupMembership entity was cleared. -func (m *UserMutation) MembershipsCleared() bool { - return m.clearedmemberships +// ClearExpiresAt clears the value of the "expires_at" field. +func (m *UserAccessTokenMutation) ClearExpiresAt() { + m.expires_at = nil + m.clearedFields[useraccesstoken.FieldExpiresAt] = struct{}{} } -// RemoveMembershipIDs removes the "memberships" edge to the GroupMembership entity by IDs. -func (m *UserMutation) RemoveMembershipIDs(ids ...uuid.UUID) { - if m.removedmemberships == nil { - m.removedmemberships = make(map[uuid.UUID]struct{}) - } - for i := range ids { - delete(m.memberships, ids[i]) - m.removedmemberships[ids[i]] = struct{}{} - } +// ExpiresAtCleared returns if the "expires_at" field was cleared in this mutation. +func (m *UserAccessTokenMutation) ExpiresAtCleared() bool { + _, ok := m.clearedFields[useraccesstoken.FieldExpiresAt] + return ok } -// RemovedMemberships returns the removed IDs of the "memberships" edge to the GroupMembership entity. -func (m *UserMutation) RemovedMembershipsIDs() (ids []uuid.UUID) { - for id := range m.removedmemberships { - ids = append(ids, id) - } - return +// ResetExpiresAt resets all changes to the "expires_at" field. +func (m *UserAccessTokenMutation) ResetExpiresAt() { + m.expires_at = nil + delete(m.clearedFields, useraccesstoken.FieldExpiresAt) } -// MembershipsIDs returns the "memberships" edge IDs in the mutation. -func (m *UserMutation) MembershipsIDs() (ids []uuid.UUID) { - for id := range m.memberships { - ids = append(ids, id) - } - return +// SetLastUsed sets the "last_used" field. +func (m *UserAccessTokenMutation) SetLastUsed(t time.Time) { + m.last_used = &t } -// ResetMemberships resets all changes to the "memberships" edge. -func (m *UserMutation) ResetMemberships() { - m.memberships = nil - m.clearedmemberships = false - m.removedmemberships = nil +// LastUsed returns the value of the "last_used" field in the mutation. +func (m *UserAccessTokenMutation) LastUsed() (r time.Time, exists bool) { + v := m.last_used + if v == nil { + return + } + return *v, true } -// AddPolicyBindingIDs adds the "policy_bindings" edge to the PolicyBinding entity by ids. -func (m *UserMutation) AddPolicyBindingIDs(ids ...uuid.UUID) { - if m.policy_bindings == nil { - m.policy_bindings = make(map[uuid.UUID]struct{}) +// OldLastUsed returns the old "last_used" field's value of the UserAccessToken entity. +// If the UserAccessToken object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *UserAccessTokenMutation) OldLastUsed(ctx context.Context) (v *time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldLastUsed is only allowed on UpdateOne operations") } - for i := range ids { - m.policy_bindings[ids[i]] = struct{}{} + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldLastUsed requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldLastUsed: %w", err) } + return oldValue.LastUsed, nil } -// ClearPolicyBindings clears the "policy_bindings" edge to the PolicyBinding entity. -func (m *UserMutation) ClearPolicyBindings() { - m.clearedpolicy_bindings = true +// ClearLastUsed clears the value of the "last_used" field. +func (m *UserAccessTokenMutation) ClearLastUsed() { + m.last_used = nil + m.clearedFields[useraccesstoken.FieldLastUsed] = struct{}{} } -// PolicyBindingsCleared reports if the "policy_bindings" edge to the PolicyBinding entity was cleared. -func (m *UserMutation) PolicyBindingsCleared() bool { - return m.clearedpolicy_bindings +// LastUsedCleared returns if the "last_used" field was cleared in this mutation. +func (m *UserAccessTokenMutation) LastUsedCleared() bool { + _, ok := m.clearedFields[useraccesstoken.FieldLastUsed] + return ok } -// RemovePolicyBindingIDs removes the "policy_bindings" edge to the PolicyBinding entity by IDs. -func (m *UserMutation) RemovePolicyBindingIDs(ids ...uuid.UUID) { - if m.removedpolicy_bindings == nil { - m.removedpolicy_bindings = make(map[uuid.UUID]struct{}) - } - for i := range ids { - delete(m.policy_bindings, ids[i]) - m.removedpolicy_bindings[ids[i]] = struct{}{} - } +// ResetLastUsed resets all changes to the "last_used" field. +func (m *UserAccessTokenMutation) ResetLastUsed() { + m.last_used = nil + delete(m.clearedFields, useraccesstoken.FieldLastUsed) } -// RemovedPolicyBindings returns the removed IDs of the "policy_bindings" edge to the PolicyBinding entity. -func (m *UserMutation) RemovedPolicyBindingsIDs() (ids []uuid.UUID) { - for id := range m.removedpolicy_bindings { - ids = append(ids, id) +// SetCreated sets the "created" field. +func (m *UserAccessTokenMutation) SetCreated(t time.Time) { + m.created = &t +} + +// Created returns the value of the "created" field in the mutation. +func (m *UserAccessTokenMutation) Created() (r time.Time, exists bool) { + v := m.created + if v == nil { + return } - return + return *v, true } -// PolicyBindingsIDs returns the "policy_bindings" edge IDs in the mutation. -func (m *UserMutation) PolicyBindingsIDs() (ids []uuid.UUID) { - for id := range m.policy_bindings { - ids = append(ids, id) +// OldCreated returns the old "created" field's value of the UserAccessToken entity. +// If the UserAccessToken object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *UserAccessTokenMutation) OldCreated(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreated is only allowed on UpdateOne operations") } - return + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreated requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreated: %w", err) + } + return oldValue.Created, nil } -// ResetPolicyBindings resets all changes to the "policy_bindings" edge. -func (m *UserMutation) ResetPolicyBindings() { - m.policy_bindings = nil - m.clearedpolicy_bindings = false - m.removedpolicy_bindings = nil +// ResetCreated resets all changes to the "created" field. +func (m *UserAccessTokenMutation) ResetCreated() { + m.created = nil } -// Where appends a list predicates to the UserMutation builder. -func (m *UserMutation) Where(ps ...predicate.User) { +// Where appends a list predicates to the UserAccessTokenMutation builder. +func (m *UserAccessTokenMutation) Where(ps ...predicate.UserAccessToken) { m.predicates = append(m.predicates, ps...) } -// WhereP appends storage-level predicates to the UserMutation builder. Using this method, +// WhereP appends storage-level predicates to the UserAccessTokenMutation builder. Using this method, // users can use type-assertion to append predicates that do not depend on any generated package. -func (m *UserMutation) WhereP(ps ...func(*sql.Selector)) { - p := make([]predicate.User, len(ps)) +func (m *UserAccessTokenMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.UserAccessToken, len(ps)) for i := range ps { p[i] = ps[i] } @@ -7581,48 +31982,54 @@ func (m *UserMutation) WhereP(ps ...func(*sql.Selector)) { } // Op returns the operation name. -func (m *UserMutation) Op() Op { +func (m *UserAccessTokenMutation) Op() Op { return m.op } // SetOp allows setting the mutation operation. -func (m *UserMutation) SetOp(op Op) { +func (m *UserAccessTokenMutation) SetOp(op Op) { m.op = op } -// Type returns the node type of this mutation (User). -func (m *UserMutation) Type() string { +// Type returns the node type of this mutation (UserAccessToken). +func (m *UserAccessTokenMutation) Type() string { return m.typ } // Fields returns all fields that were changed during this mutation. Note that in // order to get all numeric fields that were incremented/decremented, call // AddedFields(). -func (m *UserMutation) Fields() []string { - fields := make([]string, 0, 8) - if m.email != nil { - fields = append(fields, user.FieldEmail) +func (m *UserAccessTokenMutation) Fields() []string { + fields := make([]string, 0, 10) + if m.user_id != nil { + fields = append(fields, useraccesstoken.FieldUserID) } - if m.display_name != nil { - fields = append(fields, user.FieldDisplayName) + if m.name != nil { + fields = append(fields, useraccesstoken.FieldName) } - if m.avatar_url != nil { - fields = append(fields, user.FieldAvatarURL) + if m.prefix != nil { + fields = append(fields, useraccesstoken.FieldPrefix) } - if m.role != nil { - fields = append(fields, user.FieldRole) + if m.key_hash != nil { + fields = append(fields, useraccesstoken.FieldKeyHash) } - if m.status != nil { - fields = append(fields, user.FieldStatus) + if m.project_id != nil { + fields = append(fields, useraccesstoken.FieldProjectID) } - if m.preferences != nil { - fields = append(fields, user.FieldPreferences) + if m.scopes != nil { + fields = append(fields, useraccesstoken.FieldScopes) } - if m.created != nil { - fields = append(fields, user.FieldCreated) + if m.revoked != nil { + fields = append(fields, useraccesstoken.FieldRevoked) } - if m.last_login != nil { - fields = append(fields, user.FieldLastLogin) + if m.expires_at != nil { + fields = append(fields, useraccesstoken.FieldExpiresAt) + } + if m.last_used != nil { + fields = append(fields, useraccesstoken.FieldLastUsed) + } + if m.created != nil { + fields = append(fields, useraccesstoken.FieldCreated) } return fields } @@ -7630,24 +32037,28 @@ func (m *UserMutation) Fields() []string { // Field returns the value of a field with the given name. The second boolean // return value indicates that this field was not set, or was not defined in the // schema. -func (m *UserMutation) Field(name string) (ent.Value, bool) { +func (m *UserAccessTokenMutation) Field(name string) (ent.Value, bool) { switch name { - case user.FieldEmail: - return m.Email() - case user.FieldDisplayName: - return m.DisplayName() - case user.FieldAvatarURL: - return m.AvatarURL() - case user.FieldRole: - return m.Role() - case user.FieldStatus: - return m.Status() - case user.FieldPreferences: - return m.Preferences() - case user.FieldCreated: + case useraccesstoken.FieldUserID: + return m.UserID() + case useraccesstoken.FieldName: + return m.Name() + case useraccesstoken.FieldPrefix: + return m.Prefix() + case useraccesstoken.FieldKeyHash: + return m.KeyHash() + case useraccesstoken.FieldProjectID: + return m.ProjectID() + case useraccesstoken.FieldScopes: + return m.Scopes() + case useraccesstoken.FieldRevoked: + return m.Revoked() + case useraccesstoken.FieldExpiresAt: + return m.ExpiresAt() + case useraccesstoken.FieldLastUsed: + return m.LastUsed() + case useraccesstoken.FieldCreated: return m.Created() - case user.FieldLastLogin: - return m.LastLogin() } return nil, false } @@ -7655,371 +32066,249 @@ func (m *UserMutation) Field(name string) (ent.Value, bool) { // OldField returns the old value of the field from the database. An error is // returned if the mutation operation is not UpdateOne, or the query to the // database failed. -func (m *UserMutation) OldField(ctx context.Context, name string) (ent.Value, error) { +func (m *UserAccessTokenMutation) OldField(ctx context.Context, name string) (ent.Value, error) { switch name { - case user.FieldEmail: - return m.OldEmail(ctx) - case user.FieldDisplayName: - return m.OldDisplayName(ctx) - case user.FieldAvatarURL: - return m.OldAvatarURL(ctx) - case user.FieldRole: - return m.OldRole(ctx) - case user.FieldStatus: - return m.OldStatus(ctx) - case user.FieldPreferences: - return m.OldPreferences(ctx) - case user.FieldCreated: + case useraccesstoken.FieldUserID: + return m.OldUserID(ctx) + case useraccesstoken.FieldName: + return m.OldName(ctx) + case useraccesstoken.FieldPrefix: + return m.OldPrefix(ctx) + case useraccesstoken.FieldKeyHash: + return m.OldKeyHash(ctx) + case useraccesstoken.FieldProjectID: + return m.OldProjectID(ctx) + case useraccesstoken.FieldScopes: + return m.OldScopes(ctx) + case useraccesstoken.FieldRevoked: + return m.OldRevoked(ctx) + case useraccesstoken.FieldExpiresAt: + return m.OldExpiresAt(ctx) + case useraccesstoken.FieldLastUsed: + return m.OldLastUsed(ctx) + case useraccesstoken.FieldCreated: return m.OldCreated(ctx) - case user.FieldLastLogin: - return m.OldLastLogin(ctx) } - return nil, fmt.Errorf("unknown User field %s", name) + return nil, fmt.Errorf("unknown UserAccessToken field %s", name) } // SetField sets the value of a field with the given name. It returns an error if // the field is not defined in the schema, or if the type mismatched the field // type. -func (m *UserMutation) SetField(name string, value ent.Value) error { +func (m *UserAccessTokenMutation) SetField(name string, value ent.Value) error { switch name { - case user.FieldEmail: + case useraccesstoken.FieldUserID: + v, ok := value.(uuid.UUID) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetUserID(v) + return nil + case useraccesstoken.FieldName: v, ok := value.(string) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetEmail(v) + m.SetName(v) return nil - case user.FieldDisplayName: + case useraccesstoken.FieldPrefix: v, ok := value.(string) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetDisplayName(v) + m.SetPrefix(v) return nil - case user.FieldAvatarURL: + case useraccesstoken.FieldKeyHash: v, ok := value.(string) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetAvatarURL(v) + m.SetKeyHash(v) return nil - case user.FieldRole: - v, ok := value.(user.Role) + case useraccesstoken.FieldProjectID: + v, ok := value.(uuid.UUID) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetRole(v) + m.SetProjectID(v) return nil - case user.FieldStatus: - v, ok := value.(user.Status) + case useraccesstoken.FieldScopes: + v, ok := value.(string) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetStatus(v) + m.SetScopes(v) return nil - case user.FieldPreferences: - v, ok := value.(*schema.UserPreferences) + case useraccesstoken.FieldRevoked: + v, ok := value.(bool) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetPreferences(v) + m.SetRevoked(v) return nil - case user.FieldCreated: + case useraccesstoken.FieldExpiresAt: v, ok := value.(time.Time) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetCreated(v) + m.SetExpiresAt(v) return nil - case user.FieldLastLogin: + case useraccesstoken.FieldLastUsed: v, ok := value.(time.Time) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetLastLogin(v) + m.SetLastUsed(v) + return nil + case useraccesstoken.FieldCreated: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreated(v) return nil } - return fmt.Errorf("unknown User field %s", name) + return fmt.Errorf("unknown UserAccessToken field %s", name) } // AddedFields returns all numeric fields that were incremented/decremented during // this mutation. -func (m *UserMutation) AddedFields() []string { +func (m *UserAccessTokenMutation) AddedFields() []string { return nil } // AddedField returns the numeric value that was incremented/decremented on a field // with the given name. The second boolean return value indicates that this field // was not set, or was not defined in the schema. -func (m *UserMutation) AddedField(name string) (ent.Value, bool) { +func (m *UserAccessTokenMutation) AddedField(name string) (ent.Value, bool) { return nil, false } // AddField adds the value to the field with the given name. It returns an error if // the field is not defined in the schema, or if the type mismatched the field // type. -func (m *UserMutation) AddField(name string, value ent.Value) error { +func (m *UserAccessTokenMutation) AddField(name string, value ent.Value) error { switch name { } - return fmt.Errorf("unknown User numeric field %s", name) + return fmt.Errorf("unknown UserAccessToken numeric field %s", name) } // ClearedFields returns all nullable fields that were cleared during this // mutation. -func (m *UserMutation) ClearedFields() []string { +func (m *UserAccessTokenMutation) ClearedFields() []string { var fields []string - if m.FieldCleared(user.FieldAvatarURL) { - fields = append(fields, user.FieldAvatarURL) + if m.FieldCleared(useraccesstoken.FieldExpiresAt) { + fields = append(fields, useraccesstoken.FieldExpiresAt) } - if m.FieldCleared(user.FieldPreferences) { - fields = append(fields, user.FieldPreferences) - } - if m.FieldCleared(user.FieldLastLogin) { - fields = append(fields, user.FieldLastLogin) + if m.FieldCleared(useraccesstoken.FieldLastUsed) { + fields = append(fields, useraccesstoken.FieldLastUsed) } return fields } // FieldCleared returns a boolean indicating if a field with the given name was // cleared in this mutation. -func (m *UserMutation) FieldCleared(name string) bool { +func (m *UserAccessTokenMutation) FieldCleared(name string) bool { _, ok := m.clearedFields[name] return ok } // ClearField clears the value of the field with the given name. It returns an // error if the field is not defined in the schema. -func (m *UserMutation) ClearField(name string) error { +func (m *UserAccessTokenMutation) ClearField(name string) error { switch name { - case user.FieldAvatarURL: - m.ClearAvatarURL() - return nil - case user.FieldPreferences: - m.ClearPreferences() + case useraccesstoken.FieldExpiresAt: + m.ClearExpiresAt() return nil - case user.FieldLastLogin: - m.ClearLastLogin() + case useraccesstoken.FieldLastUsed: + m.ClearLastUsed() return nil } - return fmt.Errorf("unknown User nullable field %s", name) + return fmt.Errorf("unknown UserAccessToken nullable field %s", name) } // ResetField resets all changes in the mutation for the field with the given name. // It returns an error if the field is not defined in the schema. -func (m *UserMutation) ResetField(name string) error { +func (m *UserAccessTokenMutation) ResetField(name string) error { switch name { - case user.FieldEmail: - m.ResetEmail() + case useraccesstoken.FieldUserID: + m.ResetUserID() return nil - case user.FieldDisplayName: - m.ResetDisplayName() + case useraccesstoken.FieldName: + m.ResetName() return nil - case user.FieldAvatarURL: - m.ResetAvatarURL() + case useraccesstoken.FieldPrefix: + m.ResetPrefix() return nil - case user.FieldRole: - m.ResetRole() + case useraccesstoken.FieldKeyHash: + m.ResetKeyHash() return nil - case user.FieldStatus: - m.ResetStatus() + case useraccesstoken.FieldProjectID: + m.ResetProjectID() return nil - case user.FieldPreferences: - m.ResetPreferences() + case useraccesstoken.FieldScopes: + m.ResetScopes() return nil - case user.FieldCreated: - m.ResetCreated() + case useraccesstoken.FieldRevoked: + m.ResetRevoked() return nil - case user.FieldLastLogin: - m.ResetLastLogin() + case useraccesstoken.FieldExpiresAt: + m.ResetExpiresAt() + return nil + case useraccesstoken.FieldLastUsed: + m.ResetLastUsed() + return nil + case useraccesstoken.FieldCreated: + m.ResetCreated() return nil } - return fmt.Errorf("unknown User field %s", name) + return fmt.Errorf("unknown UserAccessToken field %s", name) } // AddedEdges returns all edge names that were set/added in this mutation. -func (m *UserMutation) AddedEdges() []string { - edges := make([]string, 0, 5) - if m.created_agents != nil { - edges = append(edges, user.EdgeCreatedAgents) - } - if m.owned_agents != nil { - edges = append(edges, user.EdgeOwnedAgents) - } - if m.owned_groups != nil { - edges = append(edges, user.EdgeOwnedGroups) - } - if m.memberships != nil { - edges = append(edges, user.EdgeMemberships) - } - if m.policy_bindings != nil { - edges = append(edges, user.EdgePolicyBindings) - } +func (m *UserAccessTokenMutation) AddedEdges() []string { + edges := make([]string, 0, 0) return edges } // AddedIDs returns all IDs (to other nodes) that were added for the given edge // name in this mutation. -func (m *UserMutation) AddedIDs(name string) []ent.Value { - switch name { - case user.EdgeCreatedAgents: - ids := make([]ent.Value, 0, len(m.created_agents)) - for id := range m.created_agents { - ids = append(ids, id) - } - return ids - case user.EdgeOwnedAgents: - ids := make([]ent.Value, 0, len(m.owned_agents)) - for id := range m.owned_agents { - ids = append(ids, id) - } - return ids - case user.EdgeOwnedGroups: - ids := make([]ent.Value, 0, len(m.owned_groups)) - for id := range m.owned_groups { - ids = append(ids, id) - } - return ids - case user.EdgeMemberships: - ids := make([]ent.Value, 0, len(m.memberships)) - for id := range m.memberships { - ids = append(ids, id) - } - return ids - case user.EdgePolicyBindings: - ids := make([]ent.Value, 0, len(m.policy_bindings)) - for id := range m.policy_bindings { - ids = append(ids, id) - } - return ids - } +func (m *UserAccessTokenMutation) AddedIDs(name string) []ent.Value { return nil } // RemovedEdges returns all edge names that were removed in this mutation. -func (m *UserMutation) RemovedEdges() []string { - edges := make([]string, 0, 5) - if m.removedcreated_agents != nil { - edges = append(edges, user.EdgeCreatedAgents) - } - if m.removedowned_agents != nil { - edges = append(edges, user.EdgeOwnedAgents) - } - if m.removedowned_groups != nil { - edges = append(edges, user.EdgeOwnedGroups) - } - if m.removedmemberships != nil { - edges = append(edges, user.EdgeMemberships) - } - if m.removedpolicy_bindings != nil { - edges = append(edges, user.EdgePolicyBindings) - } +func (m *UserAccessTokenMutation) RemovedEdges() []string { + edges := make([]string, 0, 0) return edges } // RemovedIDs returns all IDs (to other nodes) that were removed for the edge with // the given name in this mutation. -func (m *UserMutation) RemovedIDs(name string) []ent.Value { - switch name { - case user.EdgeCreatedAgents: - ids := make([]ent.Value, 0, len(m.removedcreated_agents)) - for id := range m.removedcreated_agents { - ids = append(ids, id) - } - return ids - case user.EdgeOwnedAgents: - ids := make([]ent.Value, 0, len(m.removedowned_agents)) - for id := range m.removedowned_agents { - ids = append(ids, id) - } - return ids - case user.EdgeOwnedGroups: - ids := make([]ent.Value, 0, len(m.removedowned_groups)) - for id := range m.removedowned_groups { - ids = append(ids, id) - } - return ids - case user.EdgeMemberships: - ids := make([]ent.Value, 0, len(m.removedmemberships)) - for id := range m.removedmemberships { - ids = append(ids, id) - } - return ids - case user.EdgePolicyBindings: - ids := make([]ent.Value, 0, len(m.removedpolicy_bindings)) - for id := range m.removedpolicy_bindings { - ids = append(ids, id) - } - return ids - } +func (m *UserAccessTokenMutation) RemovedIDs(name string) []ent.Value { return nil } // ClearedEdges returns all edge names that were cleared in this mutation. -func (m *UserMutation) ClearedEdges() []string { - edges := make([]string, 0, 5) - if m.clearedcreated_agents { - edges = append(edges, user.EdgeCreatedAgents) - } - if m.clearedowned_agents { - edges = append(edges, user.EdgeOwnedAgents) - } - if m.clearedowned_groups { - edges = append(edges, user.EdgeOwnedGroups) - } - if m.clearedmemberships { - edges = append(edges, user.EdgeMemberships) - } - if m.clearedpolicy_bindings { - edges = append(edges, user.EdgePolicyBindings) - } +func (m *UserAccessTokenMutation) ClearedEdges() []string { + edges := make([]string, 0, 0) return edges } // EdgeCleared returns a boolean which indicates if the edge with the given name // was cleared in this mutation. -func (m *UserMutation) EdgeCleared(name string) bool { - switch name { - case user.EdgeCreatedAgents: - return m.clearedcreated_agents - case user.EdgeOwnedAgents: - return m.clearedowned_agents - case user.EdgeOwnedGroups: - return m.clearedowned_groups - case user.EdgeMemberships: - return m.clearedmemberships - case user.EdgePolicyBindings: - return m.clearedpolicy_bindings - } +func (m *UserAccessTokenMutation) EdgeCleared(name string) bool { return false } // ClearEdge clears the value of the edge with the given name. It returns an error // if that edge is not defined in the schema. -func (m *UserMutation) ClearEdge(name string) error { - switch name { - } - return fmt.Errorf("unknown User unique edge %s", name) +func (m *UserAccessTokenMutation) ClearEdge(name string) error { + return fmt.Errorf("unknown UserAccessToken unique edge %s", name) } // ResetEdge resets all changes to the edge with the given name in this mutation. // It returns an error if the edge is not defined in the schema. -func (m *UserMutation) ResetEdge(name string) error { - switch name { - case user.EdgeCreatedAgents: - m.ResetCreatedAgents() - return nil - case user.EdgeOwnedAgents: - m.ResetOwnedAgents() - return nil - case user.EdgeOwnedGroups: - m.ResetOwnedGroups() - return nil - case user.EdgeMemberships: - m.ResetMemberships() - return nil - case user.EdgePolicyBindings: - m.ResetPolicyBindings() - return nil - } - return fmt.Errorf("unknown User edge %s", name) +func (m *UserAccessTokenMutation) ResetEdge(name string) error { + return fmt.Errorf("unknown UserAccessToken edge %s", name) } diff --git a/pkg/ent/notification.go b/pkg/ent/notification.go new file mode 100644 index 000000000..08e322a45 --- /dev/null +++ b/pkg/ent/notification.go @@ -0,0 +1,208 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "fmt" + "strings" + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "github.com/GoogleCloudPlatform/scion/pkg/ent/notification" + "github.com/google/uuid" +) + +// Notification is the model entity for the Notification schema. +type Notification struct { + config `json:"-"` + // ID of the ent. + ID uuid.UUID `json:"id,omitempty"` + // SubscriptionID holds the value of the "subscription_id" field. + SubscriptionID uuid.UUID `json:"subscription_id,omitempty"` + // AgentID holds the value of the "agent_id" field. + AgentID uuid.UUID `json:"agent_id,omitempty"` + // ProjectID holds the value of the "project_id" field. + ProjectID uuid.UUID `json:"project_id,omitempty"` + // SubscriberType holds the value of the "subscriber_type" field. + SubscriberType string `json:"subscriber_type,omitempty"` + // SubscriberID holds the value of the "subscriber_id" field. + SubscriberID string `json:"subscriber_id,omitempty"` + // Status holds the value of the "status" field. + Status string `json:"status,omitempty"` + // Message holds the value of the "message" field. + Message string `json:"message,omitempty"` + // Dispatched holds the value of the "dispatched" field. + Dispatched bool `json:"dispatched,omitempty"` + // Acknowledged holds the value of the "acknowledged" field. + Acknowledged bool `json:"acknowledged,omitempty"` + // Created holds the value of the "created" field. + Created time.Time `json:"created,omitempty"` + selectValues sql.SelectValues +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*Notification) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case notification.FieldDispatched, notification.FieldAcknowledged: + values[i] = new(sql.NullBool) + case notification.FieldSubscriberType, notification.FieldSubscriberID, notification.FieldStatus, notification.FieldMessage: + values[i] = new(sql.NullString) + case notification.FieldCreated: + values[i] = new(sql.NullTime) + case notification.FieldID, notification.FieldSubscriptionID, notification.FieldAgentID, notification.FieldProjectID: + values[i] = new(uuid.UUID) + default: + values[i] = new(sql.UnknownType) + } + } + return values, nil +} + +// assignValues assigns the values that were returned from sql.Rows (after scanning) +// to the Notification fields. +func (_m *Notification) assignValues(columns []string, values []any) error { + if m, n := len(values), len(columns); m < n { + return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) + } + for i := range columns { + switch columns[i] { + case notification.FieldID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field id", values[i]) + } else if value != nil { + _m.ID = *value + } + case notification.FieldSubscriptionID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field subscription_id", values[i]) + } else if value != nil { + _m.SubscriptionID = *value + } + case notification.FieldAgentID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field agent_id", values[i]) + } else if value != nil { + _m.AgentID = *value + } + case notification.FieldProjectID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field project_id", values[i]) + } else if value != nil { + _m.ProjectID = *value + } + case notification.FieldSubscriberType: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field subscriber_type", values[i]) + } else if value.Valid { + _m.SubscriberType = value.String + } + case notification.FieldSubscriberID: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field subscriber_id", values[i]) + } else if value.Valid { + _m.SubscriberID = value.String + } + case notification.FieldStatus: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field status", values[i]) + } else if value.Valid { + _m.Status = value.String + } + case notification.FieldMessage: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field message", values[i]) + } else if value.Valid { + _m.Message = value.String + } + case notification.FieldDispatched: + if value, ok := values[i].(*sql.NullBool); !ok { + return fmt.Errorf("unexpected type %T for field dispatched", values[i]) + } else if value.Valid { + _m.Dispatched = value.Bool + } + case notification.FieldAcknowledged: + if value, ok := values[i].(*sql.NullBool); !ok { + return fmt.Errorf("unexpected type %T for field acknowledged", values[i]) + } else if value.Valid { + _m.Acknowledged = value.Bool + } + case notification.FieldCreated: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field created", values[i]) + } else if value.Valid { + _m.Created = value.Time + } + default: + _m.selectValues.Set(columns[i], values[i]) + } + } + return nil +} + +// Value returns the ent.Value that was dynamically selected and assigned to the Notification. +// This includes values selected through modifiers, order, etc. +func (_m *Notification) Value(name string) (ent.Value, error) { + return _m.selectValues.Get(name) +} + +// Update returns a builder for updating this Notification. +// Note that you need to call Notification.Unwrap() before calling this method if this Notification +// was returned from a transaction, and the transaction was committed or rolled back. +func (_m *Notification) Update() *NotificationUpdateOne { + return NewNotificationClient(_m.config).UpdateOne(_m) +} + +// Unwrap unwraps the Notification entity that was returned from a transaction after it was closed, +// so that all future queries will be executed through the driver which created the transaction. +func (_m *Notification) Unwrap() *Notification { + _tx, ok := _m.config.driver.(*txDriver) + if !ok { + panic("ent: Notification is not a transactional entity") + } + _m.config.driver = _tx.drv + return _m +} + +// String implements the fmt.Stringer. +func (_m *Notification) String() string { + var builder strings.Builder + builder.WriteString("Notification(") + builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID)) + builder.WriteString("subscription_id=") + builder.WriteString(fmt.Sprintf("%v", _m.SubscriptionID)) + builder.WriteString(", ") + builder.WriteString("agent_id=") + builder.WriteString(fmt.Sprintf("%v", _m.AgentID)) + builder.WriteString(", ") + builder.WriteString("project_id=") + builder.WriteString(fmt.Sprintf("%v", _m.ProjectID)) + builder.WriteString(", ") + builder.WriteString("subscriber_type=") + builder.WriteString(_m.SubscriberType) + builder.WriteString(", ") + builder.WriteString("subscriber_id=") + builder.WriteString(_m.SubscriberID) + builder.WriteString(", ") + builder.WriteString("status=") + builder.WriteString(_m.Status) + builder.WriteString(", ") + builder.WriteString("message=") + builder.WriteString(_m.Message) + builder.WriteString(", ") + builder.WriteString("dispatched=") + builder.WriteString(fmt.Sprintf("%v", _m.Dispatched)) + builder.WriteString(", ") + builder.WriteString("acknowledged=") + builder.WriteString(fmt.Sprintf("%v", _m.Acknowledged)) + builder.WriteString(", ") + builder.WriteString("created=") + builder.WriteString(_m.Created.Format(time.ANSIC)) + builder.WriteByte(')') + return builder.String() +} + +// Notifications is a parsable slice of Notification. +type Notifications []*Notification diff --git a/pkg/ent/notification/notification.go b/pkg/ent/notification/notification.go new file mode 100644 index 000000000..ccb96cc81 --- /dev/null +++ b/pkg/ent/notification/notification.go @@ -0,0 +1,141 @@ +// Code generated by ent, DO NOT EDIT. + +package notification + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "github.com/google/uuid" +) + +const ( + // Label holds the string label denoting the notification type in the database. + Label = "notification" + // FieldID holds the string denoting the id field in the database. + FieldID = "id" + // FieldSubscriptionID holds the string denoting the subscription_id field in the database. + FieldSubscriptionID = "subscription_id" + // FieldAgentID holds the string denoting the agent_id field in the database. + FieldAgentID = "agent_id" + // FieldProjectID holds the string denoting the project_id field in the database. + FieldProjectID = "project_id" + // FieldSubscriberType holds the string denoting the subscriber_type field in the database. + FieldSubscriberType = "subscriber_type" + // FieldSubscriberID holds the string denoting the subscriber_id field in the database. + FieldSubscriberID = "subscriber_id" + // FieldStatus holds the string denoting the status field in the database. + FieldStatus = "status" + // FieldMessage holds the string denoting the message field in the database. + FieldMessage = "message" + // FieldDispatched holds the string denoting the dispatched field in the database. + FieldDispatched = "dispatched" + // FieldAcknowledged holds the string denoting the acknowledged field in the database. + FieldAcknowledged = "acknowledged" + // FieldCreated holds the string denoting the created field in the database. + FieldCreated = "created" + // Table holds the table name of the notification in the database. + Table = "notifications" +) + +// Columns holds all SQL columns for notification fields. +var Columns = []string{ + FieldID, + FieldSubscriptionID, + FieldAgentID, + FieldProjectID, + FieldSubscriberType, + FieldSubscriberID, + FieldStatus, + FieldMessage, + FieldDispatched, + FieldAcknowledged, + FieldCreated, +} + +// ValidColumn reports if the column name is valid (part of the table columns). +func ValidColumn(column string) bool { + for i := range Columns { + if column == Columns[i] { + return true + } + } + return false +} + +var ( + // SubscriberTypeValidator is a validator for the "subscriber_type" field. It is called by the builders before save. + SubscriberTypeValidator func(string) error + // SubscriberIDValidator is a validator for the "subscriber_id" field. It is called by the builders before save. + SubscriberIDValidator func(string) error + // StatusValidator is a validator for the "status" field. It is called by the builders before save. + StatusValidator func(string) error + // MessageValidator is a validator for the "message" field. It is called by the builders before save. + MessageValidator func(string) error + // DefaultDispatched holds the default value on creation for the "dispatched" field. + DefaultDispatched bool + // DefaultAcknowledged holds the default value on creation for the "acknowledged" field. + DefaultAcknowledged bool + // DefaultCreated holds the default value on creation for the "created" field. + DefaultCreated func() time.Time + // DefaultID holds the default value on creation for the "id" field. + DefaultID func() uuid.UUID +) + +// OrderOption defines the ordering options for the Notification queries. +type OrderOption func(*sql.Selector) + +// ByID orders the results by the id field. +func ByID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldID, opts...).ToFunc() +} + +// BySubscriptionID orders the results by the subscription_id field. +func BySubscriptionID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldSubscriptionID, opts...).ToFunc() +} + +// ByAgentID orders the results by the agent_id field. +func ByAgentID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldAgentID, opts...).ToFunc() +} + +// ByProjectID orders the results by the project_id field. +func ByProjectID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldProjectID, opts...).ToFunc() +} + +// BySubscriberType orders the results by the subscriber_type field. +func BySubscriberType(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldSubscriberType, opts...).ToFunc() +} + +// BySubscriberID orders the results by the subscriber_id field. +func BySubscriberID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldSubscriberID, opts...).ToFunc() +} + +// ByStatus orders the results by the status field. +func ByStatus(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldStatus, opts...).ToFunc() +} + +// ByMessage orders the results by the message field. +func ByMessage(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldMessage, opts...).ToFunc() +} + +// ByDispatched orders the results by the dispatched field. +func ByDispatched(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldDispatched, opts...).ToFunc() +} + +// ByAcknowledged orders the results by the acknowledged field. +func ByAcknowledged(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldAcknowledged, opts...).ToFunc() +} + +// ByCreated orders the results by the created field. +func ByCreated(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCreated, opts...).ToFunc() +} diff --git a/pkg/ent/notification/where.go b/pkg/ent/notification/where.go new file mode 100644 index 000000000..1dd76dd11 --- /dev/null +++ b/pkg/ent/notification/where.go @@ -0,0 +1,561 @@ +// Code generated by ent, DO NOT EDIT. + +package notification + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/google/uuid" +) + +// ID filters vertices based on their ID field. +func ID(id uuid.UUID) predicate.Notification { + return predicate.Notification(sql.FieldEQ(FieldID, id)) +} + +// IDEQ applies the EQ predicate on the ID field. +func IDEQ(id uuid.UUID) predicate.Notification { + return predicate.Notification(sql.FieldEQ(FieldID, id)) +} + +// IDNEQ applies the NEQ predicate on the ID field. +func IDNEQ(id uuid.UUID) predicate.Notification { + return predicate.Notification(sql.FieldNEQ(FieldID, id)) +} + +// IDIn applies the In predicate on the ID field. +func IDIn(ids ...uuid.UUID) predicate.Notification { + return predicate.Notification(sql.FieldIn(FieldID, ids...)) +} + +// IDNotIn applies the NotIn predicate on the ID field. +func IDNotIn(ids ...uuid.UUID) predicate.Notification { + return predicate.Notification(sql.FieldNotIn(FieldID, ids...)) +} + +// IDGT applies the GT predicate on the ID field. +func IDGT(id uuid.UUID) predicate.Notification { + return predicate.Notification(sql.FieldGT(FieldID, id)) +} + +// IDGTE applies the GTE predicate on the ID field. +func IDGTE(id uuid.UUID) predicate.Notification { + return predicate.Notification(sql.FieldGTE(FieldID, id)) +} + +// IDLT applies the LT predicate on the ID field. +func IDLT(id uuid.UUID) predicate.Notification { + return predicate.Notification(sql.FieldLT(FieldID, id)) +} + +// IDLTE applies the LTE predicate on the ID field. +func IDLTE(id uuid.UUID) predicate.Notification { + return predicate.Notification(sql.FieldLTE(FieldID, id)) +} + +// SubscriptionID applies equality check predicate on the "subscription_id" field. It's identical to SubscriptionIDEQ. +func SubscriptionID(v uuid.UUID) predicate.Notification { + return predicate.Notification(sql.FieldEQ(FieldSubscriptionID, v)) +} + +// AgentID applies equality check predicate on the "agent_id" field. It's identical to AgentIDEQ. +func AgentID(v uuid.UUID) predicate.Notification { + return predicate.Notification(sql.FieldEQ(FieldAgentID, v)) +} + +// ProjectID applies equality check predicate on the "project_id" field. It's identical to ProjectIDEQ. +func ProjectID(v uuid.UUID) predicate.Notification { + return predicate.Notification(sql.FieldEQ(FieldProjectID, v)) +} + +// SubscriberType applies equality check predicate on the "subscriber_type" field. It's identical to SubscriberTypeEQ. +func SubscriberType(v string) predicate.Notification { + return predicate.Notification(sql.FieldEQ(FieldSubscriberType, v)) +} + +// SubscriberID applies equality check predicate on the "subscriber_id" field. It's identical to SubscriberIDEQ. +func SubscriberID(v string) predicate.Notification { + return predicate.Notification(sql.FieldEQ(FieldSubscriberID, v)) +} + +// Status applies equality check predicate on the "status" field. It's identical to StatusEQ. +func Status(v string) predicate.Notification { + return predicate.Notification(sql.FieldEQ(FieldStatus, v)) +} + +// Message applies equality check predicate on the "message" field. It's identical to MessageEQ. +func Message(v string) predicate.Notification { + return predicate.Notification(sql.FieldEQ(FieldMessage, v)) +} + +// Dispatched applies equality check predicate on the "dispatched" field. It's identical to DispatchedEQ. +func Dispatched(v bool) predicate.Notification { + return predicate.Notification(sql.FieldEQ(FieldDispatched, v)) +} + +// Acknowledged applies equality check predicate on the "acknowledged" field. It's identical to AcknowledgedEQ. +func Acknowledged(v bool) predicate.Notification { + return predicate.Notification(sql.FieldEQ(FieldAcknowledged, v)) +} + +// Created applies equality check predicate on the "created" field. It's identical to CreatedEQ. +func Created(v time.Time) predicate.Notification { + return predicate.Notification(sql.FieldEQ(FieldCreated, v)) +} + +// SubscriptionIDEQ applies the EQ predicate on the "subscription_id" field. +func SubscriptionIDEQ(v uuid.UUID) predicate.Notification { + return predicate.Notification(sql.FieldEQ(FieldSubscriptionID, v)) +} + +// SubscriptionIDNEQ applies the NEQ predicate on the "subscription_id" field. +func SubscriptionIDNEQ(v uuid.UUID) predicate.Notification { + return predicate.Notification(sql.FieldNEQ(FieldSubscriptionID, v)) +} + +// SubscriptionIDIn applies the In predicate on the "subscription_id" field. +func SubscriptionIDIn(vs ...uuid.UUID) predicate.Notification { + return predicate.Notification(sql.FieldIn(FieldSubscriptionID, vs...)) +} + +// SubscriptionIDNotIn applies the NotIn predicate on the "subscription_id" field. +func SubscriptionIDNotIn(vs ...uuid.UUID) predicate.Notification { + return predicate.Notification(sql.FieldNotIn(FieldSubscriptionID, vs...)) +} + +// SubscriptionIDGT applies the GT predicate on the "subscription_id" field. +func SubscriptionIDGT(v uuid.UUID) predicate.Notification { + return predicate.Notification(sql.FieldGT(FieldSubscriptionID, v)) +} + +// SubscriptionIDGTE applies the GTE predicate on the "subscription_id" field. +func SubscriptionIDGTE(v uuid.UUID) predicate.Notification { + return predicate.Notification(sql.FieldGTE(FieldSubscriptionID, v)) +} + +// SubscriptionIDLT applies the LT predicate on the "subscription_id" field. +func SubscriptionIDLT(v uuid.UUID) predicate.Notification { + return predicate.Notification(sql.FieldLT(FieldSubscriptionID, v)) +} + +// SubscriptionIDLTE applies the LTE predicate on the "subscription_id" field. +func SubscriptionIDLTE(v uuid.UUID) predicate.Notification { + return predicate.Notification(sql.FieldLTE(FieldSubscriptionID, v)) +} + +// AgentIDEQ applies the EQ predicate on the "agent_id" field. +func AgentIDEQ(v uuid.UUID) predicate.Notification { + return predicate.Notification(sql.FieldEQ(FieldAgentID, v)) +} + +// AgentIDNEQ applies the NEQ predicate on the "agent_id" field. +func AgentIDNEQ(v uuid.UUID) predicate.Notification { + return predicate.Notification(sql.FieldNEQ(FieldAgentID, v)) +} + +// AgentIDIn applies the In predicate on the "agent_id" field. +func AgentIDIn(vs ...uuid.UUID) predicate.Notification { + return predicate.Notification(sql.FieldIn(FieldAgentID, vs...)) +} + +// AgentIDNotIn applies the NotIn predicate on the "agent_id" field. +func AgentIDNotIn(vs ...uuid.UUID) predicate.Notification { + return predicate.Notification(sql.FieldNotIn(FieldAgentID, vs...)) +} + +// AgentIDGT applies the GT predicate on the "agent_id" field. +func AgentIDGT(v uuid.UUID) predicate.Notification { + return predicate.Notification(sql.FieldGT(FieldAgentID, v)) +} + +// AgentIDGTE applies the GTE predicate on the "agent_id" field. +func AgentIDGTE(v uuid.UUID) predicate.Notification { + return predicate.Notification(sql.FieldGTE(FieldAgentID, v)) +} + +// AgentIDLT applies the LT predicate on the "agent_id" field. +func AgentIDLT(v uuid.UUID) predicate.Notification { + return predicate.Notification(sql.FieldLT(FieldAgentID, v)) +} + +// AgentIDLTE applies the LTE predicate on the "agent_id" field. +func AgentIDLTE(v uuid.UUID) predicate.Notification { + return predicate.Notification(sql.FieldLTE(FieldAgentID, v)) +} + +// ProjectIDEQ applies the EQ predicate on the "project_id" field. +func ProjectIDEQ(v uuid.UUID) predicate.Notification { + return predicate.Notification(sql.FieldEQ(FieldProjectID, v)) +} + +// ProjectIDNEQ applies the NEQ predicate on the "project_id" field. +func ProjectIDNEQ(v uuid.UUID) predicate.Notification { + return predicate.Notification(sql.FieldNEQ(FieldProjectID, v)) +} + +// ProjectIDIn applies the In predicate on the "project_id" field. +func ProjectIDIn(vs ...uuid.UUID) predicate.Notification { + return predicate.Notification(sql.FieldIn(FieldProjectID, vs...)) +} + +// ProjectIDNotIn applies the NotIn predicate on the "project_id" field. +func ProjectIDNotIn(vs ...uuid.UUID) predicate.Notification { + return predicate.Notification(sql.FieldNotIn(FieldProjectID, vs...)) +} + +// ProjectIDGT applies the GT predicate on the "project_id" field. +func ProjectIDGT(v uuid.UUID) predicate.Notification { + return predicate.Notification(sql.FieldGT(FieldProjectID, v)) +} + +// ProjectIDGTE applies the GTE predicate on the "project_id" field. +func ProjectIDGTE(v uuid.UUID) predicate.Notification { + return predicate.Notification(sql.FieldGTE(FieldProjectID, v)) +} + +// ProjectIDLT applies the LT predicate on the "project_id" field. +func ProjectIDLT(v uuid.UUID) predicate.Notification { + return predicate.Notification(sql.FieldLT(FieldProjectID, v)) +} + +// ProjectIDLTE applies the LTE predicate on the "project_id" field. +func ProjectIDLTE(v uuid.UUID) predicate.Notification { + return predicate.Notification(sql.FieldLTE(FieldProjectID, v)) +} + +// SubscriberTypeEQ applies the EQ predicate on the "subscriber_type" field. +func SubscriberTypeEQ(v string) predicate.Notification { + return predicate.Notification(sql.FieldEQ(FieldSubscriberType, v)) +} + +// SubscriberTypeNEQ applies the NEQ predicate on the "subscriber_type" field. +func SubscriberTypeNEQ(v string) predicate.Notification { + return predicate.Notification(sql.FieldNEQ(FieldSubscriberType, v)) +} + +// SubscriberTypeIn applies the In predicate on the "subscriber_type" field. +func SubscriberTypeIn(vs ...string) predicate.Notification { + return predicate.Notification(sql.FieldIn(FieldSubscriberType, vs...)) +} + +// SubscriberTypeNotIn applies the NotIn predicate on the "subscriber_type" field. +func SubscriberTypeNotIn(vs ...string) predicate.Notification { + return predicate.Notification(sql.FieldNotIn(FieldSubscriberType, vs...)) +} + +// SubscriberTypeGT applies the GT predicate on the "subscriber_type" field. +func SubscriberTypeGT(v string) predicate.Notification { + return predicate.Notification(sql.FieldGT(FieldSubscriberType, v)) +} + +// SubscriberTypeGTE applies the GTE predicate on the "subscriber_type" field. +func SubscriberTypeGTE(v string) predicate.Notification { + return predicate.Notification(sql.FieldGTE(FieldSubscriberType, v)) +} + +// SubscriberTypeLT applies the LT predicate on the "subscriber_type" field. +func SubscriberTypeLT(v string) predicate.Notification { + return predicate.Notification(sql.FieldLT(FieldSubscriberType, v)) +} + +// SubscriberTypeLTE applies the LTE predicate on the "subscriber_type" field. +func SubscriberTypeLTE(v string) predicate.Notification { + return predicate.Notification(sql.FieldLTE(FieldSubscriberType, v)) +} + +// SubscriberTypeContains applies the Contains predicate on the "subscriber_type" field. +func SubscriberTypeContains(v string) predicate.Notification { + return predicate.Notification(sql.FieldContains(FieldSubscriberType, v)) +} + +// SubscriberTypeHasPrefix applies the HasPrefix predicate on the "subscriber_type" field. +func SubscriberTypeHasPrefix(v string) predicate.Notification { + return predicate.Notification(sql.FieldHasPrefix(FieldSubscriberType, v)) +} + +// SubscriberTypeHasSuffix applies the HasSuffix predicate on the "subscriber_type" field. +func SubscriberTypeHasSuffix(v string) predicate.Notification { + return predicate.Notification(sql.FieldHasSuffix(FieldSubscriberType, v)) +} + +// SubscriberTypeEqualFold applies the EqualFold predicate on the "subscriber_type" field. +func SubscriberTypeEqualFold(v string) predicate.Notification { + return predicate.Notification(sql.FieldEqualFold(FieldSubscriberType, v)) +} + +// SubscriberTypeContainsFold applies the ContainsFold predicate on the "subscriber_type" field. +func SubscriberTypeContainsFold(v string) predicate.Notification { + return predicate.Notification(sql.FieldContainsFold(FieldSubscriberType, v)) +} + +// SubscriberIDEQ applies the EQ predicate on the "subscriber_id" field. +func SubscriberIDEQ(v string) predicate.Notification { + return predicate.Notification(sql.FieldEQ(FieldSubscriberID, v)) +} + +// SubscriberIDNEQ applies the NEQ predicate on the "subscriber_id" field. +func SubscriberIDNEQ(v string) predicate.Notification { + return predicate.Notification(sql.FieldNEQ(FieldSubscriberID, v)) +} + +// SubscriberIDIn applies the In predicate on the "subscriber_id" field. +func SubscriberIDIn(vs ...string) predicate.Notification { + return predicate.Notification(sql.FieldIn(FieldSubscriberID, vs...)) +} + +// SubscriberIDNotIn applies the NotIn predicate on the "subscriber_id" field. +func SubscriberIDNotIn(vs ...string) predicate.Notification { + return predicate.Notification(sql.FieldNotIn(FieldSubscriberID, vs...)) +} + +// SubscriberIDGT applies the GT predicate on the "subscriber_id" field. +func SubscriberIDGT(v string) predicate.Notification { + return predicate.Notification(sql.FieldGT(FieldSubscriberID, v)) +} + +// SubscriberIDGTE applies the GTE predicate on the "subscriber_id" field. +func SubscriberIDGTE(v string) predicate.Notification { + return predicate.Notification(sql.FieldGTE(FieldSubscriberID, v)) +} + +// SubscriberIDLT applies the LT predicate on the "subscriber_id" field. +func SubscriberIDLT(v string) predicate.Notification { + return predicate.Notification(sql.FieldLT(FieldSubscriberID, v)) +} + +// SubscriberIDLTE applies the LTE predicate on the "subscriber_id" field. +func SubscriberIDLTE(v string) predicate.Notification { + return predicate.Notification(sql.FieldLTE(FieldSubscriberID, v)) +} + +// SubscriberIDContains applies the Contains predicate on the "subscriber_id" field. +func SubscriberIDContains(v string) predicate.Notification { + return predicate.Notification(sql.FieldContains(FieldSubscriberID, v)) +} + +// SubscriberIDHasPrefix applies the HasPrefix predicate on the "subscriber_id" field. +func SubscriberIDHasPrefix(v string) predicate.Notification { + return predicate.Notification(sql.FieldHasPrefix(FieldSubscriberID, v)) +} + +// SubscriberIDHasSuffix applies the HasSuffix predicate on the "subscriber_id" field. +func SubscriberIDHasSuffix(v string) predicate.Notification { + return predicate.Notification(sql.FieldHasSuffix(FieldSubscriberID, v)) +} + +// SubscriberIDEqualFold applies the EqualFold predicate on the "subscriber_id" field. +func SubscriberIDEqualFold(v string) predicate.Notification { + return predicate.Notification(sql.FieldEqualFold(FieldSubscriberID, v)) +} + +// SubscriberIDContainsFold applies the ContainsFold predicate on the "subscriber_id" field. +func SubscriberIDContainsFold(v string) predicate.Notification { + return predicate.Notification(sql.FieldContainsFold(FieldSubscriberID, v)) +} + +// StatusEQ applies the EQ predicate on the "status" field. +func StatusEQ(v string) predicate.Notification { + return predicate.Notification(sql.FieldEQ(FieldStatus, v)) +} + +// StatusNEQ applies the NEQ predicate on the "status" field. +func StatusNEQ(v string) predicate.Notification { + return predicate.Notification(sql.FieldNEQ(FieldStatus, v)) +} + +// StatusIn applies the In predicate on the "status" field. +func StatusIn(vs ...string) predicate.Notification { + return predicate.Notification(sql.FieldIn(FieldStatus, vs...)) +} + +// StatusNotIn applies the NotIn predicate on the "status" field. +func StatusNotIn(vs ...string) predicate.Notification { + return predicate.Notification(sql.FieldNotIn(FieldStatus, vs...)) +} + +// StatusGT applies the GT predicate on the "status" field. +func StatusGT(v string) predicate.Notification { + return predicate.Notification(sql.FieldGT(FieldStatus, v)) +} + +// StatusGTE applies the GTE predicate on the "status" field. +func StatusGTE(v string) predicate.Notification { + return predicate.Notification(sql.FieldGTE(FieldStatus, v)) +} + +// StatusLT applies the LT predicate on the "status" field. +func StatusLT(v string) predicate.Notification { + return predicate.Notification(sql.FieldLT(FieldStatus, v)) +} + +// StatusLTE applies the LTE predicate on the "status" field. +func StatusLTE(v string) predicate.Notification { + return predicate.Notification(sql.FieldLTE(FieldStatus, v)) +} + +// StatusContains applies the Contains predicate on the "status" field. +func StatusContains(v string) predicate.Notification { + return predicate.Notification(sql.FieldContains(FieldStatus, v)) +} + +// StatusHasPrefix applies the HasPrefix predicate on the "status" field. +func StatusHasPrefix(v string) predicate.Notification { + return predicate.Notification(sql.FieldHasPrefix(FieldStatus, v)) +} + +// StatusHasSuffix applies the HasSuffix predicate on the "status" field. +func StatusHasSuffix(v string) predicate.Notification { + return predicate.Notification(sql.FieldHasSuffix(FieldStatus, v)) +} + +// StatusEqualFold applies the EqualFold predicate on the "status" field. +func StatusEqualFold(v string) predicate.Notification { + return predicate.Notification(sql.FieldEqualFold(FieldStatus, v)) +} + +// StatusContainsFold applies the ContainsFold predicate on the "status" field. +func StatusContainsFold(v string) predicate.Notification { + return predicate.Notification(sql.FieldContainsFold(FieldStatus, v)) +} + +// MessageEQ applies the EQ predicate on the "message" field. +func MessageEQ(v string) predicate.Notification { + return predicate.Notification(sql.FieldEQ(FieldMessage, v)) +} + +// MessageNEQ applies the NEQ predicate on the "message" field. +func MessageNEQ(v string) predicate.Notification { + return predicate.Notification(sql.FieldNEQ(FieldMessage, v)) +} + +// MessageIn applies the In predicate on the "message" field. +func MessageIn(vs ...string) predicate.Notification { + return predicate.Notification(sql.FieldIn(FieldMessage, vs...)) +} + +// MessageNotIn applies the NotIn predicate on the "message" field. +func MessageNotIn(vs ...string) predicate.Notification { + return predicate.Notification(sql.FieldNotIn(FieldMessage, vs...)) +} + +// MessageGT applies the GT predicate on the "message" field. +func MessageGT(v string) predicate.Notification { + return predicate.Notification(sql.FieldGT(FieldMessage, v)) +} + +// MessageGTE applies the GTE predicate on the "message" field. +func MessageGTE(v string) predicate.Notification { + return predicate.Notification(sql.FieldGTE(FieldMessage, v)) +} + +// MessageLT applies the LT predicate on the "message" field. +func MessageLT(v string) predicate.Notification { + return predicate.Notification(sql.FieldLT(FieldMessage, v)) +} + +// MessageLTE applies the LTE predicate on the "message" field. +func MessageLTE(v string) predicate.Notification { + return predicate.Notification(sql.FieldLTE(FieldMessage, v)) +} + +// MessageContains applies the Contains predicate on the "message" field. +func MessageContains(v string) predicate.Notification { + return predicate.Notification(sql.FieldContains(FieldMessage, v)) +} + +// MessageHasPrefix applies the HasPrefix predicate on the "message" field. +func MessageHasPrefix(v string) predicate.Notification { + return predicate.Notification(sql.FieldHasPrefix(FieldMessage, v)) +} + +// MessageHasSuffix applies the HasSuffix predicate on the "message" field. +func MessageHasSuffix(v string) predicate.Notification { + return predicate.Notification(sql.FieldHasSuffix(FieldMessage, v)) +} + +// MessageEqualFold applies the EqualFold predicate on the "message" field. +func MessageEqualFold(v string) predicate.Notification { + return predicate.Notification(sql.FieldEqualFold(FieldMessage, v)) +} + +// MessageContainsFold applies the ContainsFold predicate on the "message" field. +func MessageContainsFold(v string) predicate.Notification { + return predicate.Notification(sql.FieldContainsFold(FieldMessage, v)) +} + +// DispatchedEQ applies the EQ predicate on the "dispatched" field. +func DispatchedEQ(v bool) predicate.Notification { + return predicate.Notification(sql.FieldEQ(FieldDispatched, v)) +} + +// DispatchedNEQ applies the NEQ predicate on the "dispatched" field. +func DispatchedNEQ(v bool) predicate.Notification { + return predicate.Notification(sql.FieldNEQ(FieldDispatched, v)) +} + +// AcknowledgedEQ applies the EQ predicate on the "acknowledged" field. +func AcknowledgedEQ(v bool) predicate.Notification { + return predicate.Notification(sql.FieldEQ(FieldAcknowledged, v)) +} + +// AcknowledgedNEQ applies the NEQ predicate on the "acknowledged" field. +func AcknowledgedNEQ(v bool) predicate.Notification { + return predicate.Notification(sql.FieldNEQ(FieldAcknowledged, v)) +} + +// CreatedEQ applies the EQ predicate on the "created" field. +func CreatedEQ(v time.Time) predicate.Notification { + return predicate.Notification(sql.FieldEQ(FieldCreated, v)) +} + +// CreatedNEQ applies the NEQ predicate on the "created" field. +func CreatedNEQ(v time.Time) predicate.Notification { + return predicate.Notification(sql.FieldNEQ(FieldCreated, v)) +} + +// CreatedIn applies the In predicate on the "created" field. +func CreatedIn(vs ...time.Time) predicate.Notification { + return predicate.Notification(sql.FieldIn(FieldCreated, vs...)) +} + +// CreatedNotIn applies the NotIn predicate on the "created" field. +func CreatedNotIn(vs ...time.Time) predicate.Notification { + return predicate.Notification(sql.FieldNotIn(FieldCreated, vs...)) +} + +// CreatedGT applies the GT predicate on the "created" field. +func CreatedGT(v time.Time) predicate.Notification { + return predicate.Notification(sql.FieldGT(FieldCreated, v)) +} + +// CreatedGTE applies the GTE predicate on the "created" field. +func CreatedGTE(v time.Time) predicate.Notification { + return predicate.Notification(sql.FieldGTE(FieldCreated, v)) +} + +// CreatedLT applies the LT predicate on the "created" field. +func CreatedLT(v time.Time) predicate.Notification { + return predicate.Notification(sql.FieldLT(FieldCreated, v)) +} + +// CreatedLTE applies the LTE predicate on the "created" field. +func CreatedLTE(v time.Time) predicate.Notification { + return predicate.Notification(sql.FieldLTE(FieldCreated, v)) +} + +// And groups predicates with the AND operator between them. +func And(predicates ...predicate.Notification) predicate.Notification { + return predicate.Notification(sql.AndPredicates(predicates...)) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.Notification) predicate.Notification { + return predicate.Notification(sql.OrPredicates(predicates...)) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.Notification) predicate.Notification { + return predicate.Notification(sql.NotPredicates(p)) +} diff --git a/pkg/ent/notification_create.go b/pkg/ent/notification_create.go new file mode 100644 index 000000000..94bb3994b --- /dev/null +++ b/pkg/ent/notification_create.go @@ -0,0 +1,1008 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/notification" + "github.com/google/uuid" +) + +// NotificationCreate is the builder for creating a Notification entity. +type NotificationCreate struct { + config + mutation *NotificationMutation + hooks []Hook + conflict []sql.ConflictOption +} + +// SetSubscriptionID sets the "subscription_id" field. +func (_c *NotificationCreate) SetSubscriptionID(v uuid.UUID) *NotificationCreate { + _c.mutation.SetSubscriptionID(v) + return _c +} + +// SetAgentID sets the "agent_id" field. +func (_c *NotificationCreate) SetAgentID(v uuid.UUID) *NotificationCreate { + _c.mutation.SetAgentID(v) + return _c +} + +// SetProjectID sets the "project_id" field. +func (_c *NotificationCreate) SetProjectID(v uuid.UUID) *NotificationCreate { + _c.mutation.SetProjectID(v) + return _c +} + +// SetSubscriberType sets the "subscriber_type" field. +func (_c *NotificationCreate) SetSubscriberType(v string) *NotificationCreate { + _c.mutation.SetSubscriberType(v) + return _c +} + +// SetSubscriberID sets the "subscriber_id" field. +func (_c *NotificationCreate) SetSubscriberID(v string) *NotificationCreate { + _c.mutation.SetSubscriberID(v) + return _c +} + +// SetStatus sets the "status" field. +func (_c *NotificationCreate) SetStatus(v string) *NotificationCreate { + _c.mutation.SetStatus(v) + return _c +} + +// SetMessage sets the "message" field. +func (_c *NotificationCreate) SetMessage(v string) *NotificationCreate { + _c.mutation.SetMessage(v) + return _c +} + +// SetDispatched sets the "dispatched" field. +func (_c *NotificationCreate) SetDispatched(v bool) *NotificationCreate { + _c.mutation.SetDispatched(v) + return _c +} + +// SetNillableDispatched sets the "dispatched" field if the given value is not nil. +func (_c *NotificationCreate) SetNillableDispatched(v *bool) *NotificationCreate { + if v != nil { + _c.SetDispatched(*v) + } + return _c +} + +// SetAcknowledged sets the "acknowledged" field. +func (_c *NotificationCreate) SetAcknowledged(v bool) *NotificationCreate { + _c.mutation.SetAcknowledged(v) + return _c +} + +// SetNillableAcknowledged sets the "acknowledged" field if the given value is not nil. +func (_c *NotificationCreate) SetNillableAcknowledged(v *bool) *NotificationCreate { + if v != nil { + _c.SetAcknowledged(*v) + } + return _c +} + +// SetCreated sets the "created" field. +func (_c *NotificationCreate) SetCreated(v time.Time) *NotificationCreate { + _c.mutation.SetCreated(v) + return _c +} + +// SetNillableCreated sets the "created" field if the given value is not nil. +func (_c *NotificationCreate) SetNillableCreated(v *time.Time) *NotificationCreate { + if v != nil { + _c.SetCreated(*v) + } + return _c +} + +// SetID sets the "id" field. +func (_c *NotificationCreate) SetID(v uuid.UUID) *NotificationCreate { + _c.mutation.SetID(v) + return _c +} + +// SetNillableID sets the "id" field if the given value is not nil. +func (_c *NotificationCreate) SetNillableID(v *uuid.UUID) *NotificationCreate { + if v != nil { + _c.SetID(*v) + } + return _c +} + +// Mutation returns the NotificationMutation object of the builder. +func (_c *NotificationCreate) Mutation() *NotificationMutation { + return _c.mutation +} + +// Save creates the Notification in the database. +func (_c *NotificationCreate) Save(ctx context.Context) (*Notification, error) { + _c.defaults() + return withHooks(ctx, _c.sqlSave, _c.mutation, _c.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (_c *NotificationCreate) SaveX(ctx context.Context) *Notification { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *NotificationCreate) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *NotificationCreate) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_c *NotificationCreate) defaults() { + if _, ok := _c.mutation.Dispatched(); !ok { + v := notification.DefaultDispatched + _c.mutation.SetDispatched(v) + } + if _, ok := _c.mutation.Acknowledged(); !ok { + v := notification.DefaultAcknowledged + _c.mutation.SetAcknowledged(v) + } + if _, ok := _c.mutation.Created(); !ok { + v := notification.DefaultCreated() + _c.mutation.SetCreated(v) + } + if _, ok := _c.mutation.ID(); !ok { + v := notification.DefaultID() + _c.mutation.SetID(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_c *NotificationCreate) check() error { + if _, ok := _c.mutation.SubscriptionID(); !ok { + return &ValidationError{Name: "subscription_id", err: errors.New(`ent: missing required field "Notification.subscription_id"`)} + } + if _, ok := _c.mutation.AgentID(); !ok { + return &ValidationError{Name: "agent_id", err: errors.New(`ent: missing required field "Notification.agent_id"`)} + } + if _, ok := _c.mutation.ProjectID(); !ok { + return &ValidationError{Name: "project_id", err: errors.New(`ent: missing required field "Notification.project_id"`)} + } + if _, ok := _c.mutation.SubscriberType(); !ok { + return &ValidationError{Name: "subscriber_type", err: errors.New(`ent: missing required field "Notification.subscriber_type"`)} + } + if v, ok := _c.mutation.SubscriberType(); ok { + if err := notification.SubscriberTypeValidator(v); err != nil { + return &ValidationError{Name: "subscriber_type", err: fmt.Errorf(`ent: validator failed for field "Notification.subscriber_type": %w`, err)} + } + } + if _, ok := _c.mutation.SubscriberID(); !ok { + return &ValidationError{Name: "subscriber_id", err: errors.New(`ent: missing required field "Notification.subscriber_id"`)} + } + if v, ok := _c.mutation.SubscriberID(); ok { + if err := notification.SubscriberIDValidator(v); err != nil { + return &ValidationError{Name: "subscriber_id", err: fmt.Errorf(`ent: validator failed for field "Notification.subscriber_id": %w`, err)} + } + } + if _, ok := _c.mutation.Status(); !ok { + return &ValidationError{Name: "status", err: errors.New(`ent: missing required field "Notification.status"`)} + } + if v, ok := _c.mutation.Status(); ok { + if err := notification.StatusValidator(v); err != nil { + return &ValidationError{Name: "status", err: fmt.Errorf(`ent: validator failed for field "Notification.status": %w`, err)} + } + } + if _, ok := _c.mutation.Message(); !ok { + return &ValidationError{Name: "message", err: errors.New(`ent: missing required field "Notification.message"`)} + } + if v, ok := _c.mutation.Message(); ok { + if err := notification.MessageValidator(v); err != nil { + return &ValidationError{Name: "message", err: fmt.Errorf(`ent: validator failed for field "Notification.message": %w`, err)} + } + } + if _, ok := _c.mutation.Dispatched(); !ok { + return &ValidationError{Name: "dispatched", err: errors.New(`ent: missing required field "Notification.dispatched"`)} + } + if _, ok := _c.mutation.Acknowledged(); !ok { + return &ValidationError{Name: "acknowledged", err: errors.New(`ent: missing required field "Notification.acknowledged"`)} + } + if _, ok := _c.mutation.Created(); !ok { + return &ValidationError{Name: "created", err: errors.New(`ent: missing required field "Notification.created"`)} + } + return nil +} + +func (_c *NotificationCreate) sqlSave(ctx context.Context) (*Notification, error) { + if err := _c.check(); err != nil { + return nil, err + } + _node, _spec := _c.createSpec() + if err := sqlgraph.CreateNode(ctx, _c.driver, _spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + if _spec.ID.Value != nil { + if id, ok := _spec.ID.Value.(*uuid.UUID); ok { + _node.ID = *id + } else if err := _node.ID.Scan(_spec.ID.Value); err != nil { + return nil, err + } + } + _c.mutation.id = &_node.ID + _c.mutation.done = true + return _node, nil +} + +func (_c *NotificationCreate) createSpec() (*Notification, *sqlgraph.CreateSpec) { + var ( + _node = &Notification{config: _c.config} + _spec = sqlgraph.NewCreateSpec(notification.Table, sqlgraph.NewFieldSpec(notification.FieldID, field.TypeUUID)) + ) + _spec.OnConflict = _c.conflict + if id, ok := _c.mutation.ID(); ok { + _node.ID = id + _spec.ID.Value = &id + } + if value, ok := _c.mutation.SubscriptionID(); ok { + _spec.SetField(notification.FieldSubscriptionID, field.TypeUUID, value) + _node.SubscriptionID = value + } + if value, ok := _c.mutation.AgentID(); ok { + _spec.SetField(notification.FieldAgentID, field.TypeUUID, value) + _node.AgentID = value + } + if value, ok := _c.mutation.ProjectID(); ok { + _spec.SetField(notification.FieldProjectID, field.TypeUUID, value) + _node.ProjectID = value + } + if value, ok := _c.mutation.SubscriberType(); ok { + _spec.SetField(notification.FieldSubscriberType, field.TypeString, value) + _node.SubscriberType = value + } + if value, ok := _c.mutation.SubscriberID(); ok { + _spec.SetField(notification.FieldSubscriberID, field.TypeString, value) + _node.SubscriberID = value + } + if value, ok := _c.mutation.Status(); ok { + _spec.SetField(notification.FieldStatus, field.TypeString, value) + _node.Status = value + } + if value, ok := _c.mutation.Message(); ok { + _spec.SetField(notification.FieldMessage, field.TypeString, value) + _node.Message = value + } + if value, ok := _c.mutation.Dispatched(); ok { + _spec.SetField(notification.FieldDispatched, field.TypeBool, value) + _node.Dispatched = value + } + if value, ok := _c.mutation.Acknowledged(); ok { + _spec.SetField(notification.FieldAcknowledged, field.TypeBool, value) + _node.Acknowledged = value + } + if value, ok := _c.mutation.Created(); ok { + _spec.SetField(notification.FieldCreated, field.TypeTime, value) + _node.Created = value + } + return _node, _spec +} + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.Notification.Create(). +// SetSubscriptionID(v). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.NotificationUpsert) { +// SetSubscriptionID(v+v). +// }). +// Exec(ctx) +func (_c *NotificationCreate) OnConflict(opts ...sql.ConflictOption) *NotificationUpsertOne { + _c.conflict = opts + return &NotificationUpsertOne{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.Notification.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *NotificationCreate) OnConflictColumns(columns ...string) *NotificationUpsertOne { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &NotificationUpsertOne{ + create: _c, + } +} + +type ( + // NotificationUpsertOne is the builder for "upsert"-ing + // one Notification node. + NotificationUpsertOne struct { + create *NotificationCreate + } + + // NotificationUpsert is the "OnConflict" setter. + NotificationUpsert struct { + *sql.UpdateSet + } +) + +// SetSubscriptionID sets the "subscription_id" field. +func (u *NotificationUpsert) SetSubscriptionID(v uuid.UUID) *NotificationUpsert { + u.Set(notification.FieldSubscriptionID, v) + return u +} + +// UpdateSubscriptionID sets the "subscription_id" field to the value that was provided on create. +func (u *NotificationUpsert) UpdateSubscriptionID() *NotificationUpsert { + u.SetExcluded(notification.FieldSubscriptionID) + return u +} + +// SetAgentID sets the "agent_id" field. +func (u *NotificationUpsert) SetAgentID(v uuid.UUID) *NotificationUpsert { + u.Set(notification.FieldAgentID, v) + return u +} + +// UpdateAgentID sets the "agent_id" field to the value that was provided on create. +func (u *NotificationUpsert) UpdateAgentID() *NotificationUpsert { + u.SetExcluded(notification.FieldAgentID) + return u +} + +// SetProjectID sets the "project_id" field. +func (u *NotificationUpsert) SetProjectID(v uuid.UUID) *NotificationUpsert { + u.Set(notification.FieldProjectID, v) + return u +} + +// UpdateProjectID sets the "project_id" field to the value that was provided on create. +func (u *NotificationUpsert) UpdateProjectID() *NotificationUpsert { + u.SetExcluded(notification.FieldProjectID) + return u +} + +// SetSubscriberType sets the "subscriber_type" field. +func (u *NotificationUpsert) SetSubscriberType(v string) *NotificationUpsert { + u.Set(notification.FieldSubscriberType, v) + return u +} + +// UpdateSubscriberType sets the "subscriber_type" field to the value that was provided on create. +func (u *NotificationUpsert) UpdateSubscriberType() *NotificationUpsert { + u.SetExcluded(notification.FieldSubscriberType) + return u +} + +// SetSubscriberID sets the "subscriber_id" field. +func (u *NotificationUpsert) SetSubscriberID(v string) *NotificationUpsert { + u.Set(notification.FieldSubscriberID, v) + return u +} + +// UpdateSubscriberID sets the "subscriber_id" field to the value that was provided on create. +func (u *NotificationUpsert) UpdateSubscriberID() *NotificationUpsert { + u.SetExcluded(notification.FieldSubscriberID) + return u +} + +// SetStatus sets the "status" field. +func (u *NotificationUpsert) SetStatus(v string) *NotificationUpsert { + u.Set(notification.FieldStatus, v) + return u +} + +// UpdateStatus sets the "status" field to the value that was provided on create. +func (u *NotificationUpsert) UpdateStatus() *NotificationUpsert { + u.SetExcluded(notification.FieldStatus) + return u +} + +// SetMessage sets the "message" field. +func (u *NotificationUpsert) SetMessage(v string) *NotificationUpsert { + u.Set(notification.FieldMessage, v) + return u +} + +// UpdateMessage sets the "message" field to the value that was provided on create. +func (u *NotificationUpsert) UpdateMessage() *NotificationUpsert { + u.SetExcluded(notification.FieldMessage) + return u +} + +// SetDispatched sets the "dispatched" field. +func (u *NotificationUpsert) SetDispatched(v bool) *NotificationUpsert { + u.Set(notification.FieldDispatched, v) + return u +} + +// UpdateDispatched sets the "dispatched" field to the value that was provided on create. +func (u *NotificationUpsert) UpdateDispatched() *NotificationUpsert { + u.SetExcluded(notification.FieldDispatched) + return u +} + +// SetAcknowledged sets the "acknowledged" field. +func (u *NotificationUpsert) SetAcknowledged(v bool) *NotificationUpsert { + u.Set(notification.FieldAcknowledged, v) + return u +} + +// UpdateAcknowledged sets the "acknowledged" field to the value that was provided on create. +func (u *NotificationUpsert) UpdateAcknowledged() *NotificationUpsert { + u.SetExcluded(notification.FieldAcknowledged) + return u +} + +// UpdateNewValues updates the mutable fields using the new values that were set on create except the ID field. +// Using this option is equivalent to using: +// +// client.Notification.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(notification.FieldID) +// }), +// ). +// Exec(ctx) +func (u *NotificationUpsertOne) UpdateNewValues() *NotificationUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + if _, exists := u.create.mutation.ID(); exists { + s.SetIgnore(notification.FieldID) + } + if _, exists := u.create.mutation.Created(); exists { + s.SetIgnore(notification.FieldCreated) + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.Notification.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *NotificationUpsertOne) Ignore() *NotificationUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *NotificationUpsertOne) DoNothing() *NotificationUpsertOne { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the NotificationCreate.OnConflict +// documentation for more info. +func (u *NotificationUpsertOne) Update(set func(*NotificationUpsert)) *NotificationUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&NotificationUpsert{UpdateSet: update}) + })) + return u +} + +// SetSubscriptionID sets the "subscription_id" field. +func (u *NotificationUpsertOne) SetSubscriptionID(v uuid.UUID) *NotificationUpsertOne { + return u.Update(func(s *NotificationUpsert) { + s.SetSubscriptionID(v) + }) +} + +// UpdateSubscriptionID sets the "subscription_id" field to the value that was provided on create. +func (u *NotificationUpsertOne) UpdateSubscriptionID() *NotificationUpsertOne { + return u.Update(func(s *NotificationUpsert) { + s.UpdateSubscriptionID() + }) +} + +// SetAgentID sets the "agent_id" field. +func (u *NotificationUpsertOne) SetAgentID(v uuid.UUID) *NotificationUpsertOne { + return u.Update(func(s *NotificationUpsert) { + s.SetAgentID(v) + }) +} + +// UpdateAgentID sets the "agent_id" field to the value that was provided on create. +func (u *NotificationUpsertOne) UpdateAgentID() *NotificationUpsertOne { + return u.Update(func(s *NotificationUpsert) { + s.UpdateAgentID() + }) +} + +// SetProjectID sets the "project_id" field. +func (u *NotificationUpsertOne) SetProjectID(v uuid.UUID) *NotificationUpsertOne { + return u.Update(func(s *NotificationUpsert) { + s.SetProjectID(v) + }) +} + +// UpdateProjectID sets the "project_id" field to the value that was provided on create. +func (u *NotificationUpsertOne) UpdateProjectID() *NotificationUpsertOne { + return u.Update(func(s *NotificationUpsert) { + s.UpdateProjectID() + }) +} + +// SetSubscriberType sets the "subscriber_type" field. +func (u *NotificationUpsertOne) SetSubscriberType(v string) *NotificationUpsertOne { + return u.Update(func(s *NotificationUpsert) { + s.SetSubscriberType(v) + }) +} + +// UpdateSubscriberType sets the "subscriber_type" field to the value that was provided on create. +func (u *NotificationUpsertOne) UpdateSubscriberType() *NotificationUpsertOne { + return u.Update(func(s *NotificationUpsert) { + s.UpdateSubscriberType() + }) +} + +// SetSubscriberID sets the "subscriber_id" field. +func (u *NotificationUpsertOne) SetSubscriberID(v string) *NotificationUpsertOne { + return u.Update(func(s *NotificationUpsert) { + s.SetSubscriberID(v) + }) +} + +// UpdateSubscriberID sets the "subscriber_id" field to the value that was provided on create. +func (u *NotificationUpsertOne) UpdateSubscriberID() *NotificationUpsertOne { + return u.Update(func(s *NotificationUpsert) { + s.UpdateSubscriberID() + }) +} + +// SetStatus sets the "status" field. +func (u *NotificationUpsertOne) SetStatus(v string) *NotificationUpsertOne { + return u.Update(func(s *NotificationUpsert) { + s.SetStatus(v) + }) +} + +// UpdateStatus sets the "status" field to the value that was provided on create. +func (u *NotificationUpsertOne) UpdateStatus() *NotificationUpsertOne { + return u.Update(func(s *NotificationUpsert) { + s.UpdateStatus() + }) +} + +// SetMessage sets the "message" field. +func (u *NotificationUpsertOne) SetMessage(v string) *NotificationUpsertOne { + return u.Update(func(s *NotificationUpsert) { + s.SetMessage(v) + }) +} + +// UpdateMessage sets the "message" field to the value that was provided on create. +func (u *NotificationUpsertOne) UpdateMessage() *NotificationUpsertOne { + return u.Update(func(s *NotificationUpsert) { + s.UpdateMessage() + }) +} + +// SetDispatched sets the "dispatched" field. +func (u *NotificationUpsertOne) SetDispatched(v bool) *NotificationUpsertOne { + return u.Update(func(s *NotificationUpsert) { + s.SetDispatched(v) + }) +} + +// UpdateDispatched sets the "dispatched" field to the value that was provided on create. +func (u *NotificationUpsertOne) UpdateDispatched() *NotificationUpsertOne { + return u.Update(func(s *NotificationUpsert) { + s.UpdateDispatched() + }) +} + +// SetAcknowledged sets the "acknowledged" field. +func (u *NotificationUpsertOne) SetAcknowledged(v bool) *NotificationUpsertOne { + return u.Update(func(s *NotificationUpsert) { + s.SetAcknowledged(v) + }) +} + +// UpdateAcknowledged sets the "acknowledged" field to the value that was provided on create. +func (u *NotificationUpsertOne) UpdateAcknowledged() *NotificationUpsertOne { + return u.Update(func(s *NotificationUpsert) { + s.UpdateAcknowledged() + }) +} + +// Exec executes the query. +func (u *NotificationUpsertOne) Exec(ctx context.Context) error { + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for NotificationCreate.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *NotificationUpsertOne) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} + +// Exec executes the UPSERT query and returns the inserted/updated ID. +func (u *NotificationUpsertOne) ID(ctx context.Context) (id uuid.UUID, err error) { + if u.create.driver.Dialect() == dialect.MySQL { + // In case of "ON CONFLICT", there is no way to get back non-numeric ID + // fields from the database since MySQL does not support the RETURNING clause. + return id, errors.New("ent: NotificationUpsertOne.ID is not supported by MySQL driver. Use NotificationUpsertOne.Exec instead") + } + node, err := u.create.Save(ctx) + if err != nil { + return id, err + } + return node.ID, nil +} + +// IDX is like ID, but panics if an error occurs. +func (u *NotificationUpsertOne) IDX(ctx context.Context) uuid.UUID { + id, err := u.ID(ctx) + if err != nil { + panic(err) + } + return id +} + +// NotificationCreateBulk is the builder for creating many Notification entities in bulk. +type NotificationCreateBulk struct { + config + err error + builders []*NotificationCreate + conflict []sql.ConflictOption +} + +// Save creates the Notification entities in the database. +func (_c *NotificationCreateBulk) Save(ctx context.Context) ([]*Notification, error) { + if _c.err != nil { + return nil, _c.err + } + specs := make([]*sqlgraph.CreateSpec, len(_c.builders)) + nodes := make([]*Notification, len(_c.builders)) + mutators := make([]Mutator, len(_c.builders)) + for i := range _c.builders { + func(i int, root context.Context) { + builder := _c.builders[i] + builder.defaults() + var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { + mutation, ok := m.(*NotificationMutation) + if !ok { + return nil, fmt.Errorf("unexpected mutation type %T", m) + } + if err := builder.check(); err != nil { + return nil, err + } + builder.mutation = mutation + var err error + nodes[i], specs[i] = builder.createSpec() + if i < len(mutators)-1 { + _, err = mutators[i+1].Mutate(root, _c.builders[i+1].mutation) + } else { + spec := &sqlgraph.BatchCreateSpec{Nodes: specs} + spec.OnConflict = _c.conflict + // Invoke the actual operation on the latest mutation in the chain. + if err = sqlgraph.BatchCreate(ctx, _c.driver, spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + } + } + if err != nil { + return nil, err + } + mutation.id = &nodes[i].ID + mutation.done = true + return nodes[i], nil + }) + for i := len(builder.hooks) - 1; i >= 0; i-- { + mut = builder.hooks[i](mut) + } + mutators[i] = mut + }(i, ctx) + } + if len(mutators) > 0 { + if _, err := mutators[0].Mutate(ctx, _c.builders[0].mutation); err != nil { + return nil, err + } + } + return nodes, nil +} + +// SaveX is like Save, but panics if an error occurs. +func (_c *NotificationCreateBulk) SaveX(ctx context.Context) []*Notification { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *NotificationCreateBulk) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *NotificationCreateBulk) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.Notification.CreateBulk(builders...). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.NotificationUpsert) { +// SetSubscriptionID(v+v). +// }). +// Exec(ctx) +func (_c *NotificationCreateBulk) OnConflict(opts ...sql.ConflictOption) *NotificationUpsertBulk { + _c.conflict = opts + return &NotificationUpsertBulk{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.Notification.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *NotificationCreateBulk) OnConflictColumns(columns ...string) *NotificationUpsertBulk { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &NotificationUpsertBulk{ + create: _c, + } +} + +// NotificationUpsertBulk is the builder for "upsert"-ing +// a bulk of Notification nodes. +type NotificationUpsertBulk struct { + create *NotificationCreateBulk +} + +// UpdateNewValues updates the mutable fields using the new values that +// were set on create. Using this option is equivalent to using: +// +// client.Notification.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(notification.FieldID) +// }), +// ). +// Exec(ctx) +func (u *NotificationUpsertBulk) UpdateNewValues() *NotificationUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + for _, b := range u.create.builders { + if _, exists := b.mutation.ID(); exists { + s.SetIgnore(notification.FieldID) + } + if _, exists := b.mutation.Created(); exists { + s.SetIgnore(notification.FieldCreated) + } + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.Notification.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *NotificationUpsertBulk) Ignore() *NotificationUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *NotificationUpsertBulk) DoNothing() *NotificationUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the NotificationCreateBulk.OnConflict +// documentation for more info. +func (u *NotificationUpsertBulk) Update(set func(*NotificationUpsert)) *NotificationUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&NotificationUpsert{UpdateSet: update}) + })) + return u +} + +// SetSubscriptionID sets the "subscription_id" field. +func (u *NotificationUpsertBulk) SetSubscriptionID(v uuid.UUID) *NotificationUpsertBulk { + return u.Update(func(s *NotificationUpsert) { + s.SetSubscriptionID(v) + }) +} + +// UpdateSubscriptionID sets the "subscription_id" field to the value that was provided on create. +func (u *NotificationUpsertBulk) UpdateSubscriptionID() *NotificationUpsertBulk { + return u.Update(func(s *NotificationUpsert) { + s.UpdateSubscriptionID() + }) +} + +// SetAgentID sets the "agent_id" field. +func (u *NotificationUpsertBulk) SetAgentID(v uuid.UUID) *NotificationUpsertBulk { + return u.Update(func(s *NotificationUpsert) { + s.SetAgentID(v) + }) +} + +// UpdateAgentID sets the "agent_id" field to the value that was provided on create. +func (u *NotificationUpsertBulk) UpdateAgentID() *NotificationUpsertBulk { + return u.Update(func(s *NotificationUpsert) { + s.UpdateAgentID() + }) +} + +// SetProjectID sets the "project_id" field. +func (u *NotificationUpsertBulk) SetProjectID(v uuid.UUID) *NotificationUpsertBulk { + return u.Update(func(s *NotificationUpsert) { + s.SetProjectID(v) + }) +} + +// UpdateProjectID sets the "project_id" field to the value that was provided on create. +func (u *NotificationUpsertBulk) UpdateProjectID() *NotificationUpsertBulk { + return u.Update(func(s *NotificationUpsert) { + s.UpdateProjectID() + }) +} + +// SetSubscriberType sets the "subscriber_type" field. +func (u *NotificationUpsertBulk) SetSubscriberType(v string) *NotificationUpsertBulk { + return u.Update(func(s *NotificationUpsert) { + s.SetSubscriberType(v) + }) +} + +// UpdateSubscriberType sets the "subscriber_type" field to the value that was provided on create. +func (u *NotificationUpsertBulk) UpdateSubscriberType() *NotificationUpsertBulk { + return u.Update(func(s *NotificationUpsert) { + s.UpdateSubscriberType() + }) +} + +// SetSubscriberID sets the "subscriber_id" field. +func (u *NotificationUpsertBulk) SetSubscriberID(v string) *NotificationUpsertBulk { + return u.Update(func(s *NotificationUpsert) { + s.SetSubscriberID(v) + }) +} + +// UpdateSubscriberID sets the "subscriber_id" field to the value that was provided on create. +func (u *NotificationUpsertBulk) UpdateSubscriberID() *NotificationUpsertBulk { + return u.Update(func(s *NotificationUpsert) { + s.UpdateSubscriberID() + }) +} + +// SetStatus sets the "status" field. +func (u *NotificationUpsertBulk) SetStatus(v string) *NotificationUpsertBulk { + return u.Update(func(s *NotificationUpsert) { + s.SetStatus(v) + }) +} + +// UpdateStatus sets the "status" field to the value that was provided on create. +func (u *NotificationUpsertBulk) UpdateStatus() *NotificationUpsertBulk { + return u.Update(func(s *NotificationUpsert) { + s.UpdateStatus() + }) +} + +// SetMessage sets the "message" field. +func (u *NotificationUpsertBulk) SetMessage(v string) *NotificationUpsertBulk { + return u.Update(func(s *NotificationUpsert) { + s.SetMessage(v) + }) +} + +// UpdateMessage sets the "message" field to the value that was provided on create. +func (u *NotificationUpsertBulk) UpdateMessage() *NotificationUpsertBulk { + return u.Update(func(s *NotificationUpsert) { + s.UpdateMessage() + }) +} + +// SetDispatched sets the "dispatched" field. +func (u *NotificationUpsertBulk) SetDispatched(v bool) *NotificationUpsertBulk { + return u.Update(func(s *NotificationUpsert) { + s.SetDispatched(v) + }) +} + +// UpdateDispatched sets the "dispatched" field to the value that was provided on create. +func (u *NotificationUpsertBulk) UpdateDispatched() *NotificationUpsertBulk { + return u.Update(func(s *NotificationUpsert) { + s.UpdateDispatched() + }) +} + +// SetAcknowledged sets the "acknowledged" field. +func (u *NotificationUpsertBulk) SetAcknowledged(v bool) *NotificationUpsertBulk { + return u.Update(func(s *NotificationUpsert) { + s.SetAcknowledged(v) + }) +} + +// UpdateAcknowledged sets the "acknowledged" field to the value that was provided on create. +func (u *NotificationUpsertBulk) UpdateAcknowledged() *NotificationUpsertBulk { + return u.Update(func(s *NotificationUpsert) { + s.UpdateAcknowledged() + }) +} + +// Exec executes the query. +func (u *NotificationUpsertBulk) Exec(ctx context.Context) error { + if u.create.err != nil { + return u.create.err + } + for i, b := range u.create.builders { + if len(b.conflict) != 0 { + return fmt.Errorf("ent: OnConflict was set for builder %d. Set it on the NotificationCreateBulk instead", i) + } + } + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for NotificationCreateBulk.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *NotificationUpsertBulk) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/pkg/ent/notification_delete.go b/pkg/ent/notification_delete.go new file mode 100644 index 000000000..0426bcd8f --- /dev/null +++ b/pkg/ent/notification_delete.go @@ -0,0 +1,88 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/notification" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" +) + +// NotificationDelete is the builder for deleting a Notification entity. +type NotificationDelete struct { + config + hooks []Hook + mutation *NotificationMutation +} + +// Where appends a list predicates to the NotificationDelete builder. +func (_d *NotificationDelete) Where(ps ...predicate.Notification) *NotificationDelete { + _d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (_d *NotificationDelete) Exec(ctx context.Context) (int, error) { + return withHooks(ctx, _d.sqlExec, _d.mutation, _d.hooks) +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *NotificationDelete) ExecX(ctx context.Context) int { + n, err := _d.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (_d *NotificationDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(notification.Table, sqlgraph.NewFieldSpec(notification.FieldID, field.TypeUUID)) + if ps := _d.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + affected, err := sqlgraph.DeleteNodes(ctx, _d.driver, _spec) + if err != nil && sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + _d.mutation.done = true + return affected, err +} + +// NotificationDeleteOne is the builder for deleting a single Notification entity. +type NotificationDeleteOne struct { + _d *NotificationDelete +} + +// Where appends a list predicates to the NotificationDelete builder. +func (_d *NotificationDeleteOne) Where(ps ...predicate.Notification) *NotificationDeleteOne { + _d._d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query. +func (_d *NotificationDeleteOne) Exec(ctx context.Context) error { + n, err := _d._d.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{notification.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *NotificationDeleteOne) ExecX(ctx context.Context) { + if err := _d.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/pkg/ent/notification_query.go b/pkg/ent/notification_query.go new file mode 100644 index 000000000..03a858392 --- /dev/null +++ b/pkg/ent/notification_query.go @@ -0,0 +1,565 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "fmt" + "math" + + "entgo.io/ent" + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/notification" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/google/uuid" +) + +// NotificationQuery is the builder for querying Notification entities. +type NotificationQuery struct { + config + ctx *QueryContext + order []notification.OrderOption + inters []Interceptor + predicates []predicate.Notification + modifiers []func(*sql.Selector) + // intermediate query (i.e. traversal path). + sql *sql.Selector + path func(context.Context) (*sql.Selector, error) +} + +// Where adds a new predicate for the NotificationQuery builder. +func (_q *NotificationQuery) Where(ps ...predicate.Notification) *NotificationQuery { + _q.predicates = append(_q.predicates, ps...) + return _q +} + +// Limit the number of records to be returned by this query. +func (_q *NotificationQuery) Limit(limit int) *NotificationQuery { + _q.ctx.Limit = &limit + return _q +} + +// Offset to start from. +func (_q *NotificationQuery) Offset(offset int) *NotificationQuery { + _q.ctx.Offset = &offset + return _q +} + +// Unique configures the query builder to filter duplicate records on query. +// By default, unique is set to true, and can be disabled using this method. +func (_q *NotificationQuery) Unique(unique bool) *NotificationQuery { + _q.ctx.Unique = &unique + return _q +} + +// Order specifies how the records should be ordered. +func (_q *NotificationQuery) Order(o ...notification.OrderOption) *NotificationQuery { + _q.order = append(_q.order, o...) + return _q +} + +// First returns the first Notification entity from the query. +// Returns a *NotFoundError when no Notification was found. +func (_q *NotificationQuery) First(ctx context.Context) (*Notification, error) { + nodes, err := _q.Limit(1).All(setContextOp(ctx, _q.ctx, ent.OpQueryFirst)) + if err != nil { + return nil, err + } + if len(nodes) == 0 { + return nil, &NotFoundError{notification.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (_q *NotificationQuery) FirstX(ctx context.Context) *Notification { + node, err := _q.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// FirstID returns the first Notification ID from the query. +// Returns a *NotFoundError when no Notification ID was found. +func (_q *NotificationQuery) FirstID(ctx context.Context) (id uuid.UUID, err error) { + var ids []uuid.UUID + if ids, err = _q.Limit(1).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryFirstID)); err != nil { + return + } + if len(ids) == 0 { + err = &NotFoundError{notification.Label} + return + } + return ids[0], nil +} + +// FirstIDX is like FirstID, but panics if an error occurs. +func (_q *NotificationQuery) FirstIDX(ctx context.Context) uuid.UUID { + id, err := _q.FirstID(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return id +} + +// Only returns a single Notification entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one Notification entity is found. +// Returns a *NotFoundError when no Notification entities are found. +func (_q *NotificationQuery) Only(ctx context.Context) (*Notification, error) { + nodes, err := _q.Limit(2).All(setContextOp(ctx, _q.ctx, ent.OpQueryOnly)) + if err != nil { + return nil, err + } + switch len(nodes) { + case 1: + return nodes[0], nil + case 0: + return nil, &NotFoundError{notification.Label} + default: + return nil, &NotSingularError{notification.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (_q *NotificationQuery) OnlyX(ctx context.Context) *Notification { + node, err := _q.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// OnlyID is like Only, but returns the only Notification ID in the query. +// Returns a *NotSingularError when more than one Notification ID is found. +// Returns a *NotFoundError when no entities are found. +func (_q *NotificationQuery) OnlyID(ctx context.Context) (id uuid.UUID, err error) { + var ids []uuid.UUID + if ids, err = _q.Limit(2).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryOnlyID)); err != nil { + return + } + switch len(ids) { + case 1: + id = ids[0] + case 0: + err = &NotFoundError{notification.Label} + default: + err = &NotSingularError{notification.Label} + } + return +} + +// OnlyIDX is like OnlyID, but panics if an error occurs. +func (_q *NotificationQuery) OnlyIDX(ctx context.Context) uuid.UUID { + id, err := _q.OnlyID(ctx) + if err != nil { + panic(err) + } + return id +} + +// All executes the query and returns a list of Notifications. +func (_q *NotificationQuery) All(ctx context.Context) ([]*Notification, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryAll) + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*Notification, *NotificationQuery]() + return withInterceptors[[]*Notification](ctx, _q, qr, _q.inters) +} + +// AllX is like All, but panics if an error occurs. +func (_q *NotificationQuery) AllX(ctx context.Context) []*Notification { + nodes, err := _q.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// IDs executes the query and returns a list of Notification IDs. +func (_q *NotificationQuery) IDs(ctx context.Context) (ids []uuid.UUID, err error) { + if _q.ctx.Unique == nil && _q.path != nil { + _q.Unique(true) + } + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryIDs) + if err = _q.Select(notification.FieldID).Scan(ctx, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// IDsX is like IDs, but panics if an error occurs. +func (_q *NotificationQuery) IDsX(ctx context.Context) []uuid.UUID { + ids, err := _q.IDs(ctx) + if err != nil { + panic(err) + } + return ids +} + +// Count returns the count of the given query. +func (_q *NotificationQuery) Count(ctx context.Context) (int, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryCount) + if err := _q.prepareQuery(ctx); err != nil { + return 0, err + } + return withInterceptors[int](ctx, _q, querierCount[*NotificationQuery](), _q.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (_q *NotificationQuery) CountX(ctx context.Context) int { + count, err := _q.Count(ctx) + if err != nil { + panic(err) + } + return count +} + +// Exist returns true if the query has elements in the graph. +func (_q *NotificationQuery) Exist(ctx context.Context) (bool, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryExist) + switch _, err := _q.FirstID(ctx); { + case IsNotFound(err): + return false, nil + case err != nil: + return false, fmt.Errorf("ent: check existence: %w", err) + default: + return true, nil + } +} + +// ExistX is like Exist, but panics if an error occurs. +func (_q *NotificationQuery) ExistX(ctx context.Context) bool { + exist, err := _q.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the NotificationQuery builder, including all associated steps. It can be +// used to prepare common query builders and use them differently after the clone is made. +func (_q *NotificationQuery) Clone() *NotificationQuery { + if _q == nil { + return nil + } + return &NotificationQuery{ + config: _q.config, + ctx: _q.ctx.Clone(), + order: append([]notification.OrderOption{}, _q.order...), + inters: append([]Interceptor{}, _q.inters...), + predicates: append([]predicate.Notification{}, _q.predicates...), + // clone intermediate query. + sql: _q.sql.Clone(), + path: _q.path, + } +} + +// GroupBy is used to group vertices by one or more fields/columns. +// It is often used with aggregate functions, like: count, max, mean, min, sum. +// +// Example: +// +// var v []struct { +// SubscriptionID uuid.UUID `json:"subscription_id,omitempty"` +// Count int `json:"count,omitempty"` +// } +// +// client.Notification.Query(). +// GroupBy(notification.FieldSubscriptionID). +// Aggregate(ent.Count()). +// Scan(ctx, &v) +func (_q *NotificationQuery) GroupBy(field string, fields ...string) *NotificationGroupBy { + _q.ctx.Fields = append([]string{field}, fields...) + grbuild := &NotificationGroupBy{build: _q} + grbuild.flds = &_q.ctx.Fields + grbuild.label = notification.Label + grbuild.scan = grbuild.Scan + return grbuild +} + +// Select allows the selection one or more fields/columns for the given query, +// instead of selecting all fields in the entity. +// +// Example: +// +// var v []struct { +// SubscriptionID uuid.UUID `json:"subscription_id,omitempty"` +// } +// +// client.Notification.Query(). +// Select(notification.FieldSubscriptionID). +// Scan(ctx, &v) +func (_q *NotificationQuery) Select(fields ...string) *NotificationSelect { + _q.ctx.Fields = append(_q.ctx.Fields, fields...) + sbuild := &NotificationSelect{NotificationQuery: _q} + sbuild.label = notification.Label + sbuild.flds, sbuild.scan = &_q.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a NotificationSelect configured with the given aggregations. +func (_q *NotificationQuery) Aggregate(fns ...AggregateFunc) *NotificationSelect { + return _q.Select().Aggregate(fns...) +} + +func (_q *NotificationQuery) prepareQuery(ctx context.Context) error { + for _, inter := range _q.inters { + if inter == nil { + return fmt.Errorf("ent: uninitialized interceptor (forgotten import ent/runtime?)") + } + if trv, ok := inter.(Traverser); ok { + if err := trv.Traverse(ctx, _q); err != nil { + return err + } + } + } + for _, f := range _q.ctx.Fields { + if !notification.ValidColumn(f) { + return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + } + if _q.path != nil { + prev, err := _q.path(ctx) + if err != nil { + return err + } + _q.sql = prev + } + return nil +} + +func (_q *NotificationQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*Notification, error) { + var ( + nodes = []*Notification{} + _spec = _q.querySpec() + ) + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*Notification).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &Notification{config: _q.config} + nodes = append(nodes, node) + return node.assignValues(columns, values) + } + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } + for i := range hooks { + hooks[i](ctx, _spec) + } + if err := sqlgraph.QueryNodes(ctx, _q.driver, _spec); err != nil { + return nil, err + } + if len(nodes) == 0 { + return nodes, nil + } + return nodes, nil +} + +func (_q *NotificationQuery) sqlCount(ctx context.Context) (int, error) { + _spec := _q.querySpec() + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } + _spec.Node.Columns = _q.ctx.Fields + if len(_q.ctx.Fields) > 0 { + _spec.Unique = _q.ctx.Unique != nil && *_q.ctx.Unique + } + return sqlgraph.CountNodes(ctx, _q.driver, _spec) +} + +func (_q *NotificationQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(notification.Table, notification.Columns, sqlgraph.NewFieldSpec(notification.FieldID, field.TypeUUID)) + _spec.From = _q.sql + if unique := _q.ctx.Unique; unique != nil { + _spec.Unique = *unique + } else if _q.path != nil { + _spec.Unique = true + } + if fields := _q.ctx.Fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, notification.FieldID) + for i := range fields { + if fields[i] != notification.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, fields[i]) + } + } + } + if ps := _q.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if limit := _q.ctx.Limit; limit != nil { + _spec.Limit = *limit + } + if offset := _q.ctx.Offset; offset != nil { + _spec.Offset = *offset + } + if ps := _q.order; len(ps) > 0 { + _spec.Order = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + return _spec +} + +func (_q *NotificationQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(_q.driver.Dialect()) + t1 := builder.Table(notification.Table) + columns := _q.ctx.Fields + if len(columns) == 0 { + columns = notification.Columns + } + selector := builder.Select(t1.Columns(columns...)...).From(t1) + if _q.sql != nil { + selector = _q.sql + selector.Select(selector.Columns(columns...)...) + } + if _q.ctx.Unique != nil && *_q.ctx.Unique { + selector.Distinct() + } + for _, m := range _q.modifiers { + m(selector) + } + for _, p := range _q.predicates { + p(selector) + } + for _, p := range _q.order { + p(selector) + } + if offset := _q.ctx.Offset; offset != nil { + // limit is mandatory for offset clause. We start + // with default value, and override it below if needed. + selector.Offset(*offset).Limit(math.MaxInt32) + } + if limit := _q.ctx.Limit; limit != nil { + selector.Limit(*limit) + } + return selector +} + +// ForUpdate locks the selected rows against concurrent updates, and prevent them from being +// updated, deleted or "selected ... for update" by other sessions, until the transaction is +// either committed or rolled-back. +func (_q *NotificationQuery) ForUpdate(opts ...sql.LockOption) *NotificationQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForUpdate(opts...) + }) + return _q +} + +// ForShare behaves similarly to ForUpdate, except that it acquires a shared mode lock +// on any rows that are read. Other sessions can read the rows, but cannot modify them +// until your transaction commits. +func (_q *NotificationQuery) ForShare(opts ...sql.LockOption) *NotificationQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForShare(opts...) + }) + return _q +} + +// NotificationGroupBy is the group-by builder for Notification entities. +type NotificationGroupBy struct { + selector + build *NotificationQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (_g *NotificationGroupBy) Aggregate(fns ...AggregateFunc) *NotificationGroupBy { + _g.fns = append(_g.fns, fns...) + return _g +} + +// Scan applies the selector query and scans the result into the given value. +func (_g *NotificationGroupBy) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, _g.build.ctx, ent.OpQueryGroupBy) + if err := _g.build.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*NotificationQuery, *NotificationGroupBy](ctx, _g.build, _g, _g.build.inters, v) +} + +func (_g *NotificationGroupBy) sqlScan(ctx context.Context, root *NotificationQuery, v any) error { + selector := root.sqlQuery(ctx).Select() + aggregation := make([]string, 0, len(_g.fns)) + for _, fn := range _g.fns { + aggregation = append(aggregation, fn(selector)) + } + if len(selector.SelectedColumns()) == 0 { + columns := make([]string, 0, len(*_g.flds)+len(_g.fns)) + for _, f := range *_g.flds { + columns = append(columns, selector.C(f)) + } + columns = append(columns, aggregation...) + selector.Select(columns...) + } + selector.GroupBy(selector.Columns(*_g.flds...)...) + if err := selector.Err(); err != nil { + return err + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := _g.build.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} + +// NotificationSelect is the builder for selecting fields of Notification entities. +type NotificationSelect struct { + *NotificationQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (_s *NotificationSelect) Aggregate(fns ...AggregateFunc) *NotificationSelect { + _s.fns = append(_s.fns, fns...) + return _s +} + +// Scan applies the selector query and scans the result into the given value. +func (_s *NotificationSelect) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, _s.ctx, ent.OpQuerySelect) + if err := _s.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*NotificationQuery, *NotificationSelect](ctx, _s.NotificationQuery, _s, _s.inters, v) +} + +func (_s *NotificationSelect) sqlScan(ctx context.Context, root *NotificationQuery, v any) error { + selector := root.sqlQuery(ctx) + aggregation := make([]string, 0, len(_s.fns)) + for _, fn := range _s.fns { + aggregation = append(aggregation, fn(selector)) + } + switch n := len(*_s.selector.flds); { + case n == 0 && len(aggregation) > 0: + selector.Select(aggregation...) + case n != 0 && len(aggregation) > 0: + selector.AppendSelect(aggregation...) + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := _s.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} diff --git a/pkg/ent/notification_update.go b/pkg/ent/notification_update.go new file mode 100644 index 000000000..65166cd0f --- /dev/null +++ b/pkg/ent/notification_update.go @@ -0,0 +1,538 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/notification" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/google/uuid" +) + +// NotificationUpdate is the builder for updating Notification entities. +type NotificationUpdate struct { + config + hooks []Hook + mutation *NotificationMutation +} + +// Where appends a list predicates to the NotificationUpdate builder. +func (_u *NotificationUpdate) Where(ps ...predicate.Notification) *NotificationUpdate { + _u.mutation.Where(ps...) + return _u +} + +// SetSubscriptionID sets the "subscription_id" field. +func (_u *NotificationUpdate) SetSubscriptionID(v uuid.UUID) *NotificationUpdate { + _u.mutation.SetSubscriptionID(v) + return _u +} + +// SetNillableSubscriptionID sets the "subscription_id" field if the given value is not nil. +func (_u *NotificationUpdate) SetNillableSubscriptionID(v *uuid.UUID) *NotificationUpdate { + if v != nil { + _u.SetSubscriptionID(*v) + } + return _u +} + +// SetAgentID sets the "agent_id" field. +func (_u *NotificationUpdate) SetAgentID(v uuid.UUID) *NotificationUpdate { + _u.mutation.SetAgentID(v) + return _u +} + +// SetNillableAgentID sets the "agent_id" field if the given value is not nil. +func (_u *NotificationUpdate) SetNillableAgentID(v *uuid.UUID) *NotificationUpdate { + if v != nil { + _u.SetAgentID(*v) + } + return _u +} + +// SetProjectID sets the "project_id" field. +func (_u *NotificationUpdate) SetProjectID(v uuid.UUID) *NotificationUpdate { + _u.mutation.SetProjectID(v) + return _u +} + +// SetNillableProjectID sets the "project_id" field if the given value is not nil. +func (_u *NotificationUpdate) SetNillableProjectID(v *uuid.UUID) *NotificationUpdate { + if v != nil { + _u.SetProjectID(*v) + } + return _u +} + +// SetSubscriberType sets the "subscriber_type" field. +func (_u *NotificationUpdate) SetSubscriberType(v string) *NotificationUpdate { + _u.mutation.SetSubscriberType(v) + return _u +} + +// SetNillableSubscriberType sets the "subscriber_type" field if the given value is not nil. +func (_u *NotificationUpdate) SetNillableSubscriberType(v *string) *NotificationUpdate { + if v != nil { + _u.SetSubscriberType(*v) + } + return _u +} + +// SetSubscriberID sets the "subscriber_id" field. +func (_u *NotificationUpdate) SetSubscriberID(v string) *NotificationUpdate { + _u.mutation.SetSubscriberID(v) + return _u +} + +// SetNillableSubscriberID sets the "subscriber_id" field if the given value is not nil. +func (_u *NotificationUpdate) SetNillableSubscriberID(v *string) *NotificationUpdate { + if v != nil { + _u.SetSubscriberID(*v) + } + return _u +} + +// SetStatus sets the "status" field. +func (_u *NotificationUpdate) SetStatus(v string) *NotificationUpdate { + _u.mutation.SetStatus(v) + return _u +} + +// SetNillableStatus sets the "status" field if the given value is not nil. +func (_u *NotificationUpdate) SetNillableStatus(v *string) *NotificationUpdate { + if v != nil { + _u.SetStatus(*v) + } + return _u +} + +// SetMessage sets the "message" field. +func (_u *NotificationUpdate) SetMessage(v string) *NotificationUpdate { + _u.mutation.SetMessage(v) + return _u +} + +// SetNillableMessage sets the "message" field if the given value is not nil. +func (_u *NotificationUpdate) SetNillableMessage(v *string) *NotificationUpdate { + if v != nil { + _u.SetMessage(*v) + } + return _u +} + +// SetDispatched sets the "dispatched" field. +func (_u *NotificationUpdate) SetDispatched(v bool) *NotificationUpdate { + _u.mutation.SetDispatched(v) + return _u +} + +// SetNillableDispatched sets the "dispatched" field if the given value is not nil. +func (_u *NotificationUpdate) SetNillableDispatched(v *bool) *NotificationUpdate { + if v != nil { + _u.SetDispatched(*v) + } + return _u +} + +// SetAcknowledged sets the "acknowledged" field. +func (_u *NotificationUpdate) SetAcknowledged(v bool) *NotificationUpdate { + _u.mutation.SetAcknowledged(v) + return _u +} + +// SetNillableAcknowledged sets the "acknowledged" field if the given value is not nil. +func (_u *NotificationUpdate) SetNillableAcknowledged(v *bool) *NotificationUpdate { + if v != nil { + _u.SetAcknowledged(*v) + } + return _u +} + +// Mutation returns the NotificationMutation object of the builder. +func (_u *NotificationUpdate) Mutation() *NotificationMutation { + return _u.mutation +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (_u *NotificationUpdate) Save(ctx context.Context) (int, error) { + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *NotificationUpdate) SaveX(ctx context.Context) int { + affected, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (_u *NotificationUpdate) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *NotificationUpdate) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_u *NotificationUpdate) check() error { + if v, ok := _u.mutation.SubscriberType(); ok { + if err := notification.SubscriberTypeValidator(v); err != nil { + return &ValidationError{Name: "subscriber_type", err: fmt.Errorf(`ent: validator failed for field "Notification.subscriber_type": %w`, err)} + } + } + if v, ok := _u.mutation.SubscriberID(); ok { + if err := notification.SubscriberIDValidator(v); err != nil { + return &ValidationError{Name: "subscriber_id", err: fmt.Errorf(`ent: validator failed for field "Notification.subscriber_id": %w`, err)} + } + } + if v, ok := _u.mutation.Status(); ok { + if err := notification.StatusValidator(v); err != nil { + return &ValidationError{Name: "status", err: fmt.Errorf(`ent: validator failed for field "Notification.status": %w`, err)} + } + } + if v, ok := _u.mutation.Message(); ok { + if err := notification.MessageValidator(v); err != nil { + return &ValidationError{Name: "message", err: fmt.Errorf(`ent: validator failed for field "Notification.message": %w`, err)} + } + } + return nil +} + +func (_u *NotificationUpdate) sqlSave(ctx context.Context) (_node int, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(notification.Table, notification.Columns, sqlgraph.NewFieldSpec(notification.FieldID, field.TypeUUID)) + if ps := _u.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := _u.mutation.SubscriptionID(); ok { + _spec.SetField(notification.FieldSubscriptionID, field.TypeUUID, value) + } + if value, ok := _u.mutation.AgentID(); ok { + _spec.SetField(notification.FieldAgentID, field.TypeUUID, value) + } + if value, ok := _u.mutation.ProjectID(); ok { + _spec.SetField(notification.FieldProjectID, field.TypeUUID, value) + } + if value, ok := _u.mutation.SubscriberType(); ok { + _spec.SetField(notification.FieldSubscriberType, field.TypeString, value) + } + if value, ok := _u.mutation.SubscriberID(); ok { + _spec.SetField(notification.FieldSubscriberID, field.TypeString, value) + } + if value, ok := _u.mutation.Status(); ok { + _spec.SetField(notification.FieldStatus, field.TypeString, value) + } + if value, ok := _u.mutation.Message(); ok { + _spec.SetField(notification.FieldMessage, field.TypeString, value) + } + if value, ok := _u.mutation.Dispatched(); ok { + _spec.SetField(notification.FieldDispatched, field.TypeBool, value) + } + if value, ok := _u.mutation.Acknowledged(); ok { + _spec.SetField(notification.FieldAcknowledged, field.TypeBool, value) + } + if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{notification.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + _u.mutation.done = true + return _node, nil +} + +// NotificationUpdateOne is the builder for updating a single Notification entity. +type NotificationUpdateOne struct { + config + fields []string + hooks []Hook + mutation *NotificationMutation +} + +// SetSubscriptionID sets the "subscription_id" field. +func (_u *NotificationUpdateOne) SetSubscriptionID(v uuid.UUID) *NotificationUpdateOne { + _u.mutation.SetSubscriptionID(v) + return _u +} + +// SetNillableSubscriptionID sets the "subscription_id" field if the given value is not nil. +func (_u *NotificationUpdateOne) SetNillableSubscriptionID(v *uuid.UUID) *NotificationUpdateOne { + if v != nil { + _u.SetSubscriptionID(*v) + } + return _u +} + +// SetAgentID sets the "agent_id" field. +func (_u *NotificationUpdateOne) SetAgentID(v uuid.UUID) *NotificationUpdateOne { + _u.mutation.SetAgentID(v) + return _u +} + +// SetNillableAgentID sets the "agent_id" field if the given value is not nil. +func (_u *NotificationUpdateOne) SetNillableAgentID(v *uuid.UUID) *NotificationUpdateOne { + if v != nil { + _u.SetAgentID(*v) + } + return _u +} + +// SetProjectID sets the "project_id" field. +func (_u *NotificationUpdateOne) SetProjectID(v uuid.UUID) *NotificationUpdateOne { + _u.mutation.SetProjectID(v) + return _u +} + +// SetNillableProjectID sets the "project_id" field if the given value is not nil. +func (_u *NotificationUpdateOne) SetNillableProjectID(v *uuid.UUID) *NotificationUpdateOne { + if v != nil { + _u.SetProjectID(*v) + } + return _u +} + +// SetSubscriberType sets the "subscriber_type" field. +func (_u *NotificationUpdateOne) SetSubscriberType(v string) *NotificationUpdateOne { + _u.mutation.SetSubscriberType(v) + return _u +} + +// SetNillableSubscriberType sets the "subscriber_type" field if the given value is not nil. +func (_u *NotificationUpdateOne) SetNillableSubscriberType(v *string) *NotificationUpdateOne { + if v != nil { + _u.SetSubscriberType(*v) + } + return _u +} + +// SetSubscriberID sets the "subscriber_id" field. +func (_u *NotificationUpdateOne) SetSubscriberID(v string) *NotificationUpdateOne { + _u.mutation.SetSubscriberID(v) + return _u +} + +// SetNillableSubscriberID sets the "subscriber_id" field if the given value is not nil. +func (_u *NotificationUpdateOne) SetNillableSubscriberID(v *string) *NotificationUpdateOne { + if v != nil { + _u.SetSubscriberID(*v) + } + return _u +} + +// SetStatus sets the "status" field. +func (_u *NotificationUpdateOne) SetStatus(v string) *NotificationUpdateOne { + _u.mutation.SetStatus(v) + return _u +} + +// SetNillableStatus sets the "status" field if the given value is not nil. +func (_u *NotificationUpdateOne) SetNillableStatus(v *string) *NotificationUpdateOne { + if v != nil { + _u.SetStatus(*v) + } + return _u +} + +// SetMessage sets the "message" field. +func (_u *NotificationUpdateOne) SetMessage(v string) *NotificationUpdateOne { + _u.mutation.SetMessage(v) + return _u +} + +// SetNillableMessage sets the "message" field if the given value is not nil. +func (_u *NotificationUpdateOne) SetNillableMessage(v *string) *NotificationUpdateOne { + if v != nil { + _u.SetMessage(*v) + } + return _u +} + +// SetDispatched sets the "dispatched" field. +func (_u *NotificationUpdateOne) SetDispatched(v bool) *NotificationUpdateOne { + _u.mutation.SetDispatched(v) + return _u +} + +// SetNillableDispatched sets the "dispatched" field if the given value is not nil. +func (_u *NotificationUpdateOne) SetNillableDispatched(v *bool) *NotificationUpdateOne { + if v != nil { + _u.SetDispatched(*v) + } + return _u +} + +// SetAcknowledged sets the "acknowledged" field. +func (_u *NotificationUpdateOne) SetAcknowledged(v bool) *NotificationUpdateOne { + _u.mutation.SetAcknowledged(v) + return _u +} + +// SetNillableAcknowledged sets the "acknowledged" field if the given value is not nil. +func (_u *NotificationUpdateOne) SetNillableAcknowledged(v *bool) *NotificationUpdateOne { + if v != nil { + _u.SetAcknowledged(*v) + } + return _u +} + +// Mutation returns the NotificationMutation object of the builder. +func (_u *NotificationUpdateOne) Mutation() *NotificationMutation { + return _u.mutation +} + +// Where appends a list predicates to the NotificationUpdate builder. +func (_u *NotificationUpdateOne) Where(ps ...predicate.Notification) *NotificationUpdateOne { + _u.mutation.Where(ps...) + return _u +} + +// Select allows selecting one or more fields (columns) of the returned entity. +// The default is selecting all fields defined in the entity schema. +func (_u *NotificationUpdateOne) Select(field string, fields ...string) *NotificationUpdateOne { + _u.fields = append([]string{field}, fields...) + return _u +} + +// Save executes the query and returns the updated Notification entity. +func (_u *NotificationUpdateOne) Save(ctx context.Context) (*Notification, error) { + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *NotificationUpdateOne) SaveX(ctx context.Context) *Notification { + node, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (_u *NotificationUpdateOne) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *NotificationUpdateOne) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_u *NotificationUpdateOne) check() error { + if v, ok := _u.mutation.SubscriberType(); ok { + if err := notification.SubscriberTypeValidator(v); err != nil { + return &ValidationError{Name: "subscriber_type", err: fmt.Errorf(`ent: validator failed for field "Notification.subscriber_type": %w`, err)} + } + } + if v, ok := _u.mutation.SubscriberID(); ok { + if err := notification.SubscriberIDValidator(v); err != nil { + return &ValidationError{Name: "subscriber_id", err: fmt.Errorf(`ent: validator failed for field "Notification.subscriber_id": %w`, err)} + } + } + if v, ok := _u.mutation.Status(); ok { + if err := notification.StatusValidator(v); err != nil { + return &ValidationError{Name: "status", err: fmt.Errorf(`ent: validator failed for field "Notification.status": %w`, err)} + } + } + if v, ok := _u.mutation.Message(); ok { + if err := notification.MessageValidator(v); err != nil { + return &ValidationError{Name: "message", err: fmt.Errorf(`ent: validator failed for field "Notification.message": %w`, err)} + } + } + return nil +} + +func (_u *NotificationUpdateOne) sqlSave(ctx context.Context) (_node *Notification, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(notification.Table, notification.Columns, sqlgraph.NewFieldSpec(notification.FieldID, field.TypeUUID)) + id, ok := _u.mutation.ID() + if !ok { + return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "Notification.id" for update`)} + } + _spec.Node.ID.Value = id + if fields := _u.fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, notification.FieldID) + for _, f := range fields { + if !notification.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + if f != notification.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, f) + } + } + } + if ps := _u.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := _u.mutation.SubscriptionID(); ok { + _spec.SetField(notification.FieldSubscriptionID, field.TypeUUID, value) + } + if value, ok := _u.mutation.AgentID(); ok { + _spec.SetField(notification.FieldAgentID, field.TypeUUID, value) + } + if value, ok := _u.mutation.ProjectID(); ok { + _spec.SetField(notification.FieldProjectID, field.TypeUUID, value) + } + if value, ok := _u.mutation.SubscriberType(); ok { + _spec.SetField(notification.FieldSubscriberType, field.TypeString, value) + } + if value, ok := _u.mutation.SubscriberID(); ok { + _spec.SetField(notification.FieldSubscriberID, field.TypeString, value) + } + if value, ok := _u.mutation.Status(); ok { + _spec.SetField(notification.FieldStatus, field.TypeString, value) + } + if value, ok := _u.mutation.Message(); ok { + _spec.SetField(notification.FieldMessage, field.TypeString, value) + } + if value, ok := _u.mutation.Dispatched(); ok { + _spec.SetField(notification.FieldDispatched, field.TypeBool, value) + } + if value, ok := _u.mutation.Acknowledged(); ok { + _spec.SetField(notification.FieldAcknowledged, field.TypeBool, value) + } + _node = &Notification{config: _u.config} + _spec.Assign = _node.assignValues + _spec.ScanValues = _node.scanValues + if err = sqlgraph.UpdateNode(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{notification.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + _u.mutation.done = true + return _node, nil +} diff --git a/pkg/ent/notificationsubscription.go b/pkg/ent/notificationsubscription.go new file mode 100644 index 000000000..fe6952d31 --- /dev/null +++ b/pkg/ent/notificationsubscription.go @@ -0,0 +1,189 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "fmt" + "strings" + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "github.com/GoogleCloudPlatform/scion/pkg/ent/notificationsubscription" + "github.com/google/uuid" +) + +// NotificationSubscription is the model entity for the NotificationSubscription schema. +type NotificationSubscription struct { + config `json:"-"` + // ID of the ent. + ID uuid.UUID `json:"id,omitempty"` + // Scope holds the value of the "scope" field. + Scope string `json:"scope,omitempty"` + // AgentID holds the value of the "agent_id" field. + AgentID *uuid.UUID `json:"agent_id,omitempty"` + // SubscriberType holds the value of the "subscriber_type" field. + SubscriberType string `json:"subscriber_type,omitempty"` + // SubscriberID holds the value of the "subscriber_id" field. + SubscriberID string `json:"subscriber_id,omitempty"` + // ProjectID holds the value of the "project_id" field. + ProjectID uuid.UUID `json:"project_id,omitempty"` + // TriggerActivities holds the value of the "trigger_activities" field. + TriggerActivities string `json:"trigger_activities,omitempty"` + // CreatedBy holds the value of the "created_by" field. + CreatedBy string `json:"created_by,omitempty"` + // Created holds the value of the "created" field. + Created time.Time `json:"created,omitempty"` + selectValues sql.SelectValues +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*NotificationSubscription) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case notificationsubscription.FieldAgentID: + values[i] = &sql.NullScanner{S: new(uuid.UUID)} + case notificationsubscription.FieldScope, notificationsubscription.FieldSubscriberType, notificationsubscription.FieldSubscriberID, notificationsubscription.FieldTriggerActivities, notificationsubscription.FieldCreatedBy: + values[i] = new(sql.NullString) + case notificationsubscription.FieldCreated: + values[i] = new(sql.NullTime) + case notificationsubscription.FieldID, notificationsubscription.FieldProjectID: + values[i] = new(uuid.UUID) + default: + values[i] = new(sql.UnknownType) + } + } + return values, nil +} + +// assignValues assigns the values that were returned from sql.Rows (after scanning) +// to the NotificationSubscription fields. +func (_m *NotificationSubscription) assignValues(columns []string, values []any) error { + if m, n := len(values), len(columns); m < n { + return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) + } + for i := range columns { + switch columns[i] { + case notificationsubscription.FieldID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field id", values[i]) + } else if value != nil { + _m.ID = *value + } + case notificationsubscription.FieldScope: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field scope", values[i]) + } else if value.Valid { + _m.Scope = value.String + } + case notificationsubscription.FieldAgentID: + if value, ok := values[i].(*sql.NullScanner); !ok { + return fmt.Errorf("unexpected type %T for field agent_id", values[i]) + } else if value.Valid { + _m.AgentID = new(uuid.UUID) + *_m.AgentID = *value.S.(*uuid.UUID) + } + case notificationsubscription.FieldSubscriberType: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field subscriber_type", values[i]) + } else if value.Valid { + _m.SubscriberType = value.String + } + case notificationsubscription.FieldSubscriberID: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field subscriber_id", values[i]) + } else if value.Valid { + _m.SubscriberID = value.String + } + case notificationsubscription.FieldProjectID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field project_id", values[i]) + } else if value != nil { + _m.ProjectID = *value + } + case notificationsubscription.FieldTriggerActivities: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field trigger_activities", values[i]) + } else if value.Valid { + _m.TriggerActivities = value.String + } + case notificationsubscription.FieldCreatedBy: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field created_by", values[i]) + } else if value.Valid { + _m.CreatedBy = value.String + } + case notificationsubscription.FieldCreated: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field created", values[i]) + } else if value.Valid { + _m.Created = value.Time + } + default: + _m.selectValues.Set(columns[i], values[i]) + } + } + return nil +} + +// Value returns the ent.Value that was dynamically selected and assigned to the NotificationSubscription. +// This includes values selected through modifiers, order, etc. +func (_m *NotificationSubscription) Value(name string) (ent.Value, error) { + return _m.selectValues.Get(name) +} + +// Update returns a builder for updating this NotificationSubscription. +// Note that you need to call NotificationSubscription.Unwrap() before calling this method if this NotificationSubscription +// was returned from a transaction, and the transaction was committed or rolled back. +func (_m *NotificationSubscription) Update() *NotificationSubscriptionUpdateOne { + return NewNotificationSubscriptionClient(_m.config).UpdateOne(_m) +} + +// Unwrap unwraps the NotificationSubscription entity that was returned from a transaction after it was closed, +// so that all future queries will be executed through the driver which created the transaction. +func (_m *NotificationSubscription) Unwrap() *NotificationSubscription { + _tx, ok := _m.config.driver.(*txDriver) + if !ok { + panic("ent: NotificationSubscription is not a transactional entity") + } + _m.config.driver = _tx.drv + return _m +} + +// String implements the fmt.Stringer. +func (_m *NotificationSubscription) String() string { + var builder strings.Builder + builder.WriteString("NotificationSubscription(") + builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID)) + builder.WriteString("scope=") + builder.WriteString(_m.Scope) + builder.WriteString(", ") + if v := _m.AgentID; v != nil { + builder.WriteString("agent_id=") + builder.WriteString(fmt.Sprintf("%v", *v)) + } + builder.WriteString(", ") + builder.WriteString("subscriber_type=") + builder.WriteString(_m.SubscriberType) + builder.WriteString(", ") + builder.WriteString("subscriber_id=") + builder.WriteString(_m.SubscriberID) + builder.WriteString(", ") + builder.WriteString("project_id=") + builder.WriteString(fmt.Sprintf("%v", _m.ProjectID)) + builder.WriteString(", ") + builder.WriteString("trigger_activities=") + builder.WriteString(_m.TriggerActivities) + builder.WriteString(", ") + builder.WriteString("created_by=") + builder.WriteString(_m.CreatedBy) + builder.WriteString(", ") + builder.WriteString("created=") + builder.WriteString(_m.Created.Format(time.ANSIC)) + builder.WriteByte(')') + return builder.String() +} + +// NotificationSubscriptions is a parsable slice of NotificationSubscription. +type NotificationSubscriptions []*NotificationSubscription diff --git a/pkg/ent/notificationsubscription/notificationsubscription.go b/pkg/ent/notificationsubscription/notificationsubscription.go new file mode 100644 index 000000000..2bc971e81 --- /dev/null +++ b/pkg/ent/notificationsubscription/notificationsubscription.go @@ -0,0 +1,123 @@ +// Code generated by ent, DO NOT EDIT. + +package notificationsubscription + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "github.com/google/uuid" +) + +const ( + // Label holds the string label denoting the notificationsubscription type in the database. + Label = "notification_subscription" + // FieldID holds the string denoting the id field in the database. + FieldID = "id" + // FieldScope holds the string denoting the scope field in the database. + FieldScope = "scope" + // FieldAgentID holds the string denoting the agent_id field in the database. + FieldAgentID = "agent_id" + // FieldSubscriberType holds the string denoting the subscriber_type field in the database. + FieldSubscriberType = "subscriber_type" + // FieldSubscriberID holds the string denoting the subscriber_id field in the database. + FieldSubscriberID = "subscriber_id" + // FieldProjectID holds the string denoting the project_id field in the database. + FieldProjectID = "project_id" + // FieldTriggerActivities holds the string denoting the trigger_activities field in the database. + FieldTriggerActivities = "trigger_activities" + // FieldCreatedBy holds the string denoting the created_by field in the database. + FieldCreatedBy = "created_by" + // FieldCreated holds the string denoting the created field in the database. + FieldCreated = "created" + // Table holds the table name of the notificationsubscription in the database. + Table = "notification_subscriptions" +) + +// Columns holds all SQL columns for notificationsubscription fields. +var Columns = []string{ + FieldID, + FieldScope, + FieldAgentID, + FieldSubscriberType, + FieldSubscriberID, + FieldProjectID, + FieldTriggerActivities, + FieldCreatedBy, + FieldCreated, +} + +// ValidColumn reports if the column name is valid (part of the table columns). +func ValidColumn(column string) bool { + for i := range Columns { + if column == Columns[i] { + return true + } + } + return false +} + +var ( + // DefaultScope holds the default value on creation for the "scope" field. + DefaultScope string + // DefaultSubscriberType holds the default value on creation for the "subscriber_type" field. + DefaultSubscriberType string + // SubscriberIDValidator is a validator for the "subscriber_id" field. It is called by the builders before save. + SubscriberIDValidator func(string) error + // TriggerActivitiesValidator is a validator for the "trigger_activities" field. It is called by the builders before save. + TriggerActivitiesValidator func(string) error + // CreatedByValidator is a validator for the "created_by" field. It is called by the builders before save. + CreatedByValidator func(string) error + // DefaultCreated holds the default value on creation for the "created" field. + DefaultCreated func() time.Time + // DefaultID holds the default value on creation for the "id" field. + DefaultID func() uuid.UUID +) + +// OrderOption defines the ordering options for the NotificationSubscription queries. +type OrderOption func(*sql.Selector) + +// ByID orders the results by the id field. +func ByID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldID, opts...).ToFunc() +} + +// ByScope orders the results by the scope field. +func ByScope(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldScope, opts...).ToFunc() +} + +// ByAgentID orders the results by the agent_id field. +func ByAgentID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldAgentID, opts...).ToFunc() +} + +// BySubscriberType orders the results by the subscriber_type field. +func BySubscriberType(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldSubscriberType, opts...).ToFunc() +} + +// BySubscriberID orders the results by the subscriber_id field. +func BySubscriberID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldSubscriberID, opts...).ToFunc() +} + +// ByProjectID orders the results by the project_id field. +func ByProjectID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldProjectID, opts...).ToFunc() +} + +// ByTriggerActivities orders the results by the trigger_activities field. +func ByTriggerActivities(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldTriggerActivities, opts...).ToFunc() +} + +// ByCreatedBy orders the results by the created_by field. +func ByCreatedBy(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCreatedBy, opts...).ToFunc() +} + +// ByCreated orders the results by the created field. +func ByCreated(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCreated, opts...).ToFunc() +} diff --git a/pkg/ent/notificationsubscription/where.go b/pkg/ent/notificationsubscription/where.go new file mode 100644 index 000000000..a783f41ae --- /dev/null +++ b/pkg/ent/notificationsubscription/where.go @@ -0,0 +1,566 @@ +// Code generated by ent, DO NOT EDIT. + +package notificationsubscription + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/google/uuid" +) + +// ID filters vertices based on their ID field. +func ID(id uuid.UUID) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldEQ(FieldID, id)) +} + +// IDEQ applies the EQ predicate on the ID field. +func IDEQ(id uuid.UUID) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldEQ(FieldID, id)) +} + +// IDNEQ applies the NEQ predicate on the ID field. +func IDNEQ(id uuid.UUID) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldNEQ(FieldID, id)) +} + +// IDIn applies the In predicate on the ID field. +func IDIn(ids ...uuid.UUID) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldIn(FieldID, ids...)) +} + +// IDNotIn applies the NotIn predicate on the ID field. +func IDNotIn(ids ...uuid.UUID) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldNotIn(FieldID, ids...)) +} + +// IDGT applies the GT predicate on the ID field. +func IDGT(id uuid.UUID) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldGT(FieldID, id)) +} + +// IDGTE applies the GTE predicate on the ID field. +func IDGTE(id uuid.UUID) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldGTE(FieldID, id)) +} + +// IDLT applies the LT predicate on the ID field. +func IDLT(id uuid.UUID) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldLT(FieldID, id)) +} + +// IDLTE applies the LTE predicate on the ID field. +func IDLTE(id uuid.UUID) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldLTE(FieldID, id)) +} + +// Scope applies equality check predicate on the "scope" field. It's identical to ScopeEQ. +func Scope(v string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldEQ(FieldScope, v)) +} + +// AgentID applies equality check predicate on the "agent_id" field. It's identical to AgentIDEQ. +func AgentID(v uuid.UUID) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldEQ(FieldAgentID, v)) +} + +// SubscriberType applies equality check predicate on the "subscriber_type" field. It's identical to SubscriberTypeEQ. +func SubscriberType(v string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldEQ(FieldSubscriberType, v)) +} + +// SubscriberID applies equality check predicate on the "subscriber_id" field. It's identical to SubscriberIDEQ. +func SubscriberID(v string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldEQ(FieldSubscriberID, v)) +} + +// ProjectID applies equality check predicate on the "project_id" field. It's identical to ProjectIDEQ. +func ProjectID(v uuid.UUID) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldEQ(FieldProjectID, v)) +} + +// TriggerActivities applies equality check predicate on the "trigger_activities" field. It's identical to TriggerActivitiesEQ. +func TriggerActivities(v string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldEQ(FieldTriggerActivities, v)) +} + +// CreatedBy applies equality check predicate on the "created_by" field. It's identical to CreatedByEQ. +func CreatedBy(v string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldEQ(FieldCreatedBy, v)) +} + +// Created applies equality check predicate on the "created" field. It's identical to CreatedEQ. +func Created(v time.Time) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldEQ(FieldCreated, v)) +} + +// ScopeEQ applies the EQ predicate on the "scope" field. +func ScopeEQ(v string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldEQ(FieldScope, v)) +} + +// ScopeNEQ applies the NEQ predicate on the "scope" field. +func ScopeNEQ(v string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldNEQ(FieldScope, v)) +} + +// ScopeIn applies the In predicate on the "scope" field. +func ScopeIn(vs ...string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldIn(FieldScope, vs...)) +} + +// ScopeNotIn applies the NotIn predicate on the "scope" field. +func ScopeNotIn(vs ...string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldNotIn(FieldScope, vs...)) +} + +// ScopeGT applies the GT predicate on the "scope" field. +func ScopeGT(v string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldGT(FieldScope, v)) +} + +// ScopeGTE applies the GTE predicate on the "scope" field. +func ScopeGTE(v string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldGTE(FieldScope, v)) +} + +// ScopeLT applies the LT predicate on the "scope" field. +func ScopeLT(v string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldLT(FieldScope, v)) +} + +// ScopeLTE applies the LTE predicate on the "scope" field. +func ScopeLTE(v string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldLTE(FieldScope, v)) +} + +// ScopeContains applies the Contains predicate on the "scope" field. +func ScopeContains(v string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldContains(FieldScope, v)) +} + +// ScopeHasPrefix applies the HasPrefix predicate on the "scope" field. +func ScopeHasPrefix(v string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldHasPrefix(FieldScope, v)) +} + +// ScopeHasSuffix applies the HasSuffix predicate on the "scope" field. +func ScopeHasSuffix(v string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldHasSuffix(FieldScope, v)) +} + +// ScopeEqualFold applies the EqualFold predicate on the "scope" field. +func ScopeEqualFold(v string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldEqualFold(FieldScope, v)) +} + +// ScopeContainsFold applies the ContainsFold predicate on the "scope" field. +func ScopeContainsFold(v string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldContainsFold(FieldScope, v)) +} + +// AgentIDEQ applies the EQ predicate on the "agent_id" field. +func AgentIDEQ(v uuid.UUID) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldEQ(FieldAgentID, v)) +} + +// AgentIDNEQ applies the NEQ predicate on the "agent_id" field. +func AgentIDNEQ(v uuid.UUID) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldNEQ(FieldAgentID, v)) +} + +// AgentIDIn applies the In predicate on the "agent_id" field. +func AgentIDIn(vs ...uuid.UUID) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldIn(FieldAgentID, vs...)) +} + +// AgentIDNotIn applies the NotIn predicate on the "agent_id" field. +func AgentIDNotIn(vs ...uuid.UUID) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldNotIn(FieldAgentID, vs...)) +} + +// AgentIDGT applies the GT predicate on the "agent_id" field. +func AgentIDGT(v uuid.UUID) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldGT(FieldAgentID, v)) +} + +// AgentIDGTE applies the GTE predicate on the "agent_id" field. +func AgentIDGTE(v uuid.UUID) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldGTE(FieldAgentID, v)) +} + +// AgentIDLT applies the LT predicate on the "agent_id" field. +func AgentIDLT(v uuid.UUID) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldLT(FieldAgentID, v)) +} + +// AgentIDLTE applies the LTE predicate on the "agent_id" field. +func AgentIDLTE(v uuid.UUID) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldLTE(FieldAgentID, v)) +} + +// AgentIDIsNil applies the IsNil predicate on the "agent_id" field. +func AgentIDIsNil() predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldIsNull(FieldAgentID)) +} + +// AgentIDNotNil applies the NotNil predicate on the "agent_id" field. +func AgentIDNotNil() predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldNotNull(FieldAgentID)) +} + +// SubscriberTypeEQ applies the EQ predicate on the "subscriber_type" field. +func SubscriberTypeEQ(v string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldEQ(FieldSubscriberType, v)) +} + +// SubscriberTypeNEQ applies the NEQ predicate on the "subscriber_type" field. +func SubscriberTypeNEQ(v string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldNEQ(FieldSubscriberType, v)) +} + +// SubscriberTypeIn applies the In predicate on the "subscriber_type" field. +func SubscriberTypeIn(vs ...string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldIn(FieldSubscriberType, vs...)) +} + +// SubscriberTypeNotIn applies the NotIn predicate on the "subscriber_type" field. +func SubscriberTypeNotIn(vs ...string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldNotIn(FieldSubscriberType, vs...)) +} + +// SubscriberTypeGT applies the GT predicate on the "subscriber_type" field. +func SubscriberTypeGT(v string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldGT(FieldSubscriberType, v)) +} + +// SubscriberTypeGTE applies the GTE predicate on the "subscriber_type" field. +func SubscriberTypeGTE(v string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldGTE(FieldSubscriberType, v)) +} + +// SubscriberTypeLT applies the LT predicate on the "subscriber_type" field. +func SubscriberTypeLT(v string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldLT(FieldSubscriberType, v)) +} + +// SubscriberTypeLTE applies the LTE predicate on the "subscriber_type" field. +func SubscriberTypeLTE(v string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldLTE(FieldSubscriberType, v)) +} + +// SubscriberTypeContains applies the Contains predicate on the "subscriber_type" field. +func SubscriberTypeContains(v string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldContains(FieldSubscriberType, v)) +} + +// SubscriberTypeHasPrefix applies the HasPrefix predicate on the "subscriber_type" field. +func SubscriberTypeHasPrefix(v string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldHasPrefix(FieldSubscriberType, v)) +} + +// SubscriberTypeHasSuffix applies the HasSuffix predicate on the "subscriber_type" field. +func SubscriberTypeHasSuffix(v string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldHasSuffix(FieldSubscriberType, v)) +} + +// SubscriberTypeEqualFold applies the EqualFold predicate on the "subscriber_type" field. +func SubscriberTypeEqualFold(v string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldEqualFold(FieldSubscriberType, v)) +} + +// SubscriberTypeContainsFold applies the ContainsFold predicate on the "subscriber_type" field. +func SubscriberTypeContainsFold(v string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldContainsFold(FieldSubscriberType, v)) +} + +// SubscriberIDEQ applies the EQ predicate on the "subscriber_id" field. +func SubscriberIDEQ(v string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldEQ(FieldSubscriberID, v)) +} + +// SubscriberIDNEQ applies the NEQ predicate on the "subscriber_id" field. +func SubscriberIDNEQ(v string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldNEQ(FieldSubscriberID, v)) +} + +// SubscriberIDIn applies the In predicate on the "subscriber_id" field. +func SubscriberIDIn(vs ...string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldIn(FieldSubscriberID, vs...)) +} + +// SubscriberIDNotIn applies the NotIn predicate on the "subscriber_id" field. +func SubscriberIDNotIn(vs ...string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldNotIn(FieldSubscriberID, vs...)) +} + +// SubscriberIDGT applies the GT predicate on the "subscriber_id" field. +func SubscriberIDGT(v string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldGT(FieldSubscriberID, v)) +} + +// SubscriberIDGTE applies the GTE predicate on the "subscriber_id" field. +func SubscriberIDGTE(v string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldGTE(FieldSubscriberID, v)) +} + +// SubscriberIDLT applies the LT predicate on the "subscriber_id" field. +func SubscriberIDLT(v string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldLT(FieldSubscriberID, v)) +} + +// SubscriberIDLTE applies the LTE predicate on the "subscriber_id" field. +func SubscriberIDLTE(v string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldLTE(FieldSubscriberID, v)) +} + +// SubscriberIDContains applies the Contains predicate on the "subscriber_id" field. +func SubscriberIDContains(v string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldContains(FieldSubscriberID, v)) +} + +// SubscriberIDHasPrefix applies the HasPrefix predicate on the "subscriber_id" field. +func SubscriberIDHasPrefix(v string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldHasPrefix(FieldSubscriberID, v)) +} + +// SubscriberIDHasSuffix applies the HasSuffix predicate on the "subscriber_id" field. +func SubscriberIDHasSuffix(v string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldHasSuffix(FieldSubscriberID, v)) +} + +// SubscriberIDEqualFold applies the EqualFold predicate on the "subscriber_id" field. +func SubscriberIDEqualFold(v string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldEqualFold(FieldSubscriberID, v)) +} + +// SubscriberIDContainsFold applies the ContainsFold predicate on the "subscriber_id" field. +func SubscriberIDContainsFold(v string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldContainsFold(FieldSubscriberID, v)) +} + +// ProjectIDEQ applies the EQ predicate on the "project_id" field. +func ProjectIDEQ(v uuid.UUID) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldEQ(FieldProjectID, v)) +} + +// ProjectIDNEQ applies the NEQ predicate on the "project_id" field. +func ProjectIDNEQ(v uuid.UUID) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldNEQ(FieldProjectID, v)) +} + +// ProjectIDIn applies the In predicate on the "project_id" field. +func ProjectIDIn(vs ...uuid.UUID) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldIn(FieldProjectID, vs...)) +} + +// ProjectIDNotIn applies the NotIn predicate on the "project_id" field. +func ProjectIDNotIn(vs ...uuid.UUID) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldNotIn(FieldProjectID, vs...)) +} + +// ProjectIDGT applies the GT predicate on the "project_id" field. +func ProjectIDGT(v uuid.UUID) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldGT(FieldProjectID, v)) +} + +// ProjectIDGTE applies the GTE predicate on the "project_id" field. +func ProjectIDGTE(v uuid.UUID) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldGTE(FieldProjectID, v)) +} + +// ProjectIDLT applies the LT predicate on the "project_id" field. +func ProjectIDLT(v uuid.UUID) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldLT(FieldProjectID, v)) +} + +// ProjectIDLTE applies the LTE predicate on the "project_id" field. +func ProjectIDLTE(v uuid.UUID) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldLTE(FieldProjectID, v)) +} + +// TriggerActivitiesEQ applies the EQ predicate on the "trigger_activities" field. +func TriggerActivitiesEQ(v string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldEQ(FieldTriggerActivities, v)) +} + +// TriggerActivitiesNEQ applies the NEQ predicate on the "trigger_activities" field. +func TriggerActivitiesNEQ(v string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldNEQ(FieldTriggerActivities, v)) +} + +// TriggerActivitiesIn applies the In predicate on the "trigger_activities" field. +func TriggerActivitiesIn(vs ...string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldIn(FieldTriggerActivities, vs...)) +} + +// TriggerActivitiesNotIn applies the NotIn predicate on the "trigger_activities" field. +func TriggerActivitiesNotIn(vs ...string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldNotIn(FieldTriggerActivities, vs...)) +} + +// TriggerActivitiesGT applies the GT predicate on the "trigger_activities" field. +func TriggerActivitiesGT(v string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldGT(FieldTriggerActivities, v)) +} + +// TriggerActivitiesGTE applies the GTE predicate on the "trigger_activities" field. +func TriggerActivitiesGTE(v string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldGTE(FieldTriggerActivities, v)) +} + +// TriggerActivitiesLT applies the LT predicate on the "trigger_activities" field. +func TriggerActivitiesLT(v string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldLT(FieldTriggerActivities, v)) +} + +// TriggerActivitiesLTE applies the LTE predicate on the "trigger_activities" field. +func TriggerActivitiesLTE(v string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldLTE(FieldTriggerActivities, v)) +} + +// TriggerActivitiesContains applies the Contains predicate on the "trigger_activities" field. +func TriggerActivitiesContains(v string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldContains(FieldTriggerActivities, v)) +} + +// TriggerActivitiesHasPrefix applies the HasPrefix predicate on the "trigger_activities" field. +func TriggerActivitiesHasPrefix(v string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldHasPrefix(FieldTriggerActivities, v)) +} + +// TriggerActivitiesHasSuffix applies the HasSuffix predicate on the "trigger_activities" field. +func TriggerActivitiesHasSuffix(v string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldHasSuffix(FieldTriggerActivities, v)) +} + +// TriggerActivitiesEqualFold applies the EqualFold predicate on the "trigger_activities" field. +func TriggerActivitiesEqualFold(v string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldEqualFold(FieldTriggerActivities, v)) +} + +// TriggerActivitiesContainsFold applies the ContainsFold predicate on the "trigger_activities" field. +func TriggerActivitiesContainsFold(v string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldContainsFold(FieldTriggerActivities, v)) +} + +// CreatedByEQ applies the EQ predicate on the "created_by" field. +func CreatedByEQ(v string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldEQ(FieldCreatedBy, v)) +} + +// CreatedByNEQ applies the NEQ predicate on the "created_by" field. +func CreatedByNEQ(v string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldNEQ(FieldCreatedBy, v)) +} + +// CreatedByIn applies the In predicate on the "created_by" field. +func CreatedByIn(vs ...string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldIn(FieldCreatedBy, vs...)) +} + +// CreatedByNotIn applies the NotIn predicate on the "created_by" field. +func CreatedByNotIn(vs ...string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldNotIn(FieldCreatedBy, vs...)) +} + +// CreatedByGT applies the GT predicate on the "created_by" field. +func CreatedByGT(v string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldGT(FieldCreatedBy, v)) +} + +// CreatedByGTE applies the GTE predicate on the "created_by" field. +func CreatedByGTE(v string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldGTE(FieldCreatedBy, v)) +} + +// CreatedByLT applies the LT predicate on the "created_by" field. +func CreatedByLT(v string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldLT(FieldCreatedBy, v)) +} + +// CreatedByLTE applies the LTE predicate on the "created_by" field. +func CreatedByLTE(v string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldLTE(FieldCreatedBy, v)) +} + +// CreatedByContains applies the Contains predicate on the "created_by" field. +func CreatedByContains(v string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldContains(FieldCreatedBy, v)) +} + +// CreatedByHasPrefix applies the HasPrefix predicate on the "created_by" field. +func CreatedByHasPrefix(v string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldHasPrefix(FieldCreatedBy, v)) +} + +// CreatedByHasSuffix applies the HasSuffix predicate on the "created_by" field. +func CreatedByHasSuffix(v string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldHasSuffix(FieldCreatedBy, v)) +} + +// CreatedByEqualFold applies the EqualFold predicate on the "created_by" field. +func CreatedByEqualFold(v string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldEqualFold(FieldCreatedBy, v)) +} + +// CreatedByContainsFold applies the ContainsFold predicate on the "created_by" field. +func CreatedByContainsFold(v string) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldContainsFold(FieldCreatedBy, v)) +} + +// CreatedEQ applies the EQ predicate on the "created" field. +func CreatedEQ(v time.Time) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldEQ(FieldCreated, v)) +} + +// CreatedNEQ applies the NEQ predicate on the "created" field. +func CreatedNEQ(v time.Time) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldNEQ(FieldCreated, v)) +} + +// CreatedIn applies the In predicate on the "created" field. +func CreatedIn(vs ...time.Time) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldIn(FieldCreated, vs...)) +} + +// CreatedNotIn applies the NotIn predicate on the "created" field. +func CreatedNotIn(vs ...time.Time) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldNotIn(FieldCreated, vs...)) +} + +// CreatedGT applies the GT predicate on the "created" field. +func CreatedGT(v time.Time) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldGT(FieldCreated, v)) +} + +// CreatedGTE applies the GTE predicate on the "created" field. +func CreatedGTE(v time.Time) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldGTE(FieldCreated, v)) +} + +// CreatedLT applies the LT predicate on the "created" field. +func CreatedLT(v time.Time) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldLT(FieldCreated, v)) +} + +// CreatedLTE applies the LTE predicate on the "created" field. +func CreatedLTE(v time.Time) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.FieldLTE(FieldCreated, v)) +} + +// And groups predicates with the AND operator between them. +func And(predicates ...predicate.NotificationSubscription) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.AndPredicates(predicates...)) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.NotificationSubscription) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.OrPredicates(predicates...)) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.NotificationSubscription) predicate.NotificationSubscription { + return predicate.NotificationSubscription(sql.NotPredicates(p)) +} diff --git a/pkg/ent/notificationsubscription_create.go b/pkg/ent/notificationsubscription_create.go new file mode 100644 index 000000000..b7b0dcbec --- /dev/null +++ b/pkg/ent/notificationsubscription_create.go @@ -0,0 +1,922 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/notificationsubscription" + "github.com/google/uuid" +) + +// NotificationSubscriptionCreate is the builder for creating a NotificationSubscription entity. +type NotificationSubscriptionCreate struct { + config + mutation *NotificationSubscriptionMutation + hooks []Hook + conflict []sql.ConflictOption +} + +// SetScope sets the "scope" field. +func (_c *NotificationSubscriptionCreate) SetScope(v string) *NotificationSubscriptionCreate { + _c.mutation.SetScope(v) + return _c +} + +// SetNillableScope sets the "scope" field if the given value is not nil. +func (_c *NotificationSubscriptionCreate) SetNillableScope(v *string) *NotificationSubscriptionCreate { + if v != nil { + _c.SetScope(*v) + } + return _c +} + +// SetAgentID sets the "agent_id" field. +func (_c *NotificationSubscriptionCreate) SetAgentID(v uuid.UUID) *NotificationSubscriptionCreate { + _c.mutation.SetAgentID(v) + return _c +} + +// SetNillableAgentID sets the "agent_id" field if the given value is not nil. +func (_c *NotificationSubscriptionCreate) SetNillableAgentID(v *uuid.UUID) *NotificationSubscriptionCreate { + if v != nil { + _c.SetAgentID(*v) + } + return _c +} + +// SetSubscriberType sets the "subscriber_type" field. +func (_c *NotificationSubscriptionCreate) SetSubscriberType(v string) *NotificationSubscriptionCreate { + _c.mutation.SetSubscriberType(v) + return _c +} + +// SetNillableSubscriberType sets the "subscriber_type" field if the given value is not nil. +func (_c *NotificationSubscriptionCreate) SetNillableSubscriberType(v *string) *NotificationSubscriptionCreate { + if v != nil { + _c.SetSubscriberType(*v) + } + return _c +} + +// SetSubscriberID sets the "subscriber_id" field. +func (_c *NotificationSubscriptionCreate) SetSubscriberID(v string) *NotificationSubscriptionCreate { + _c.mutation.SetSubscriberID(v) + return _c +} + +// SetProjectID sets the "project_id" field. +func (_c *NotificationSubscriptionCreate) SetProjectID(v uuid.UUID) *NotificationSubscriptionCreate { + _c.mutation.SetProjectID(v) + return _c +} + +// SetTriggerActivities sets the "trigger_activities" field. +func (_c *NotificationSubscriptionCreate) SetTriggerActivities(v string) *NotificationSubscriptionCreate { + _c.mutation.SetTriggerActivities(v) + return _c +} + +// SetCreatedBy sets the "created_by" field. +func (_c *NotificationSubscriptionCreate) SetCreatedBy(v string) *NotificationSubscriptionCreate { + _c.mutation.SetCreatedBy(v) + return _c +} + +// SetCreated sets the "created" field. +func (_c *NotificationSubscriptionCreate) SetCreated(v time.Time) *NotificationSubscriptionCreate { + _c.mutation.SetCreated(v) + return _c +} + +// SetNillableCreated sets the "created" field if the given value is not nil. +func (_c *NotificationSubscriptionCreate) SetNillableCreated(v *time.Time) *NotificationSubscriptionCreate { + if v != nil { + _c.SetCreated(*v) + } + return _c +} + +// SetID sets the "id" field. +func (_c *NotificationSubscriptionCreate) SetID(v uuid.UUID) *NotificationSubscriptionCreate { + _c.mutation.SetID(v) + return _c +} + +// SetNillableID sets the "id" field if the given value is not nil. +func (_c *NotificationSubscriptionCreate) SetNillableID(v *uuid.UUID) *NotificationSubscriptionCreate { + if v != nil { + _c.SetID(*v) + } + return _c +} + +// Mutation returns the NotificationSubscriptionMutation object of the builder. +func (_c *NotificationSubscriptionCreate) Mutation() *NotificationSubscriptionMutation { + return _c.mutation +} + +// Save creates the NotificationSubscription in the database. +func (_c *NotificationSubscriptionCreate) Save(ctx context.Context) (*NotificationSubscription, error) { + _c.defaults() + return withHooks(ctx, _c.sqlSave, _c.mutation, _c.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (_c *NotificationSubscriptionCreate) SaveX(ctx context.Context) *NotificationSubscription { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *NotificationSubscriptionCreate) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *NotificationSubscriptionCreate) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_c *NotificationSubscriptionCreate) defaults() { + if _, ok := _c.mutation.Scope(); !ok { + v := notificationsubscription.DefaultScope + _c.mutation.SetScope(v) + } + if _, ok := _c.mutation.SubscriberType(); !ok { + v := notificationsubscription.DefaultSubscriberType + _c.mutation.SetSubscriberType(v) + } + if _, ok := _c.mutation.Created(); !ok { + v := notificationsubscription.DefaultCreated() + _c.mutation.SetCreated(v) + } + if _, ok := _c.mutation.ID(); !ok { + v := notificationsubscription.DefaultID() + _c.mutation.SetID(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_c *NotificationSubscriptionCreate) check() error { + if _, ok := _c.mutation.Scope(); !ok { + return &ValidationError{Name: "scope", err: errors.New(`ent: missing required field "NotificationSubscription.scope"`)} + } + if _, ok := _c.mutation.SubscriberType(); !ok { + return &ValidationError{Name: "subscriber_type", err: errors.New(`ent: missing required field "NotificationSubscription.subscriber_type"`)} + } + if _, ok := _c.mutation.SubscriberID(); !ok { + return &ValidationError{Name: "subscriber_id", err: errors.New(`ent: missing required field "NotificationSubscription.subscriber_id"`)} + } + if v, ok := _c.mutation.SubscriberID(); ok { + if err := notificationsubscription.SubscriberIDValidator(v); err != nil { + return &ValidationError{Name: "subscriber_id", err: fmt.Errorf(`ent: validator failed for field "NotificationSubscription.subscriber_id": %w`, err)} + } + } + if _, ok := _c.mutation.ProjectID(); !ok { + return &ValidationError{Name: "project_id", err: errors.New(`ent: missing required field "NotificationSubscription.project_id"`)} + } + if _, ok := _c.mutation.TriggerActivities(); !ok { + return &ValidationError{Name: "trigger_activities", err: errors.New(`ent: missing required field "NotificationSubscription.trigger_activities"`)} + } + if v, ok := _c.mutation.TriggerActivities(); ok { + if err := notificationsubscription.TriggerActivitiesValidator(v); err != nil { + return &ValidationError{Name: "trigger_activities", err: fmt.Errorf(`ent: validator failed for field "NotificationSubscription.trigger_activities": %w`, err)} + } + } + if _, ok := _c.mutation.CreatedBy(); !ok { + return &ValidationError{Name: "created_by", err: errors.New(`ent: missing required field "NotificationSubscription.created_by"`)} + } + if v, ok := _c.mutation.CreatedBy(); ok { + if err := notificationsubscription.CreatedByValidator(v); err != nil { + return &ValidationError{Name: "created_by", err: fmt.Errorf(`ent: validator failed for field "NotificationSubscription.created_by": %w`, err)} + } + } + if _, ok := _c.mutation.Created(); !ok { + return &ValidationError{Name: "created", err: errors.New(`ent: missing required field "NotificationSubscription.created"`)} + } + return nil +} + +func (_c *NotificationSubscriptionCreate) sqlSave(ctx context.Context) (*NotificationSubscription, error) { + if err := _c.check(); err != nil { + return nil, err + } + _node, _spec := _c.createSpec() + if err := sqlgraph.CreateNode(ctx, _c.driver, _spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + if _spec.ID.Value != nil { + if id, ok := _spec.ID.Value.(*uuid.UUID); ok { + _node.ID = *id + } else if err := _node.ID.Scan(_spec.ID.Value); err != nil { + return nil, err + } + } + _c.mutation.id = &_node.ID + _c.mutation.done = true + return _node, nil +} + +func (_c *NotificationSubscriptionCreate) createSpec() (*NotificationSubscription, *sqlgraph.CreateSpec) { + var ( + _node = &NotificationSubscription{config: _c.config} + _spec = sqlgraph.NewCreateSpec(notificationsubscription.Table, sqlgraph.NewFieldSpec(notificationsubscription.FieldID, field.TypeUUID)) + ) + _spec.OnConflict = _c.conflict + if id, ok := _c.mutation.ID(); ok { + _node.ID = id + _spec.ID.Value = &id + } + if value, ok := _c.mutation.Scope(); ok { + _spec.SetField(notificationsubscription.FieldScope, field.TypeString, value) + _node.Scope = value + } + if value, ok := _c.mutation.AgentID(); ok { + _spec.SetField(notificationsubscription.FieldAgentID, field.TypeUUID, value) + _node.AgentID = &value + } + if value, ok := _c.mutation.SubscriberType(); ok { + _spec.SetField(notificationsubscription.FieldSubscriberType, field.TypeString, value) + _node.SubscriberType = value + } + if value, ok := _c.mutation.SubscriberID(); ok { + _spec.SetField(notificationsubscription.FieldSubscriberID, field.TypeString, value) + _node.SubscriberID = value + } + if value, ok := _c.mutation.ProjectID(); ok { + _spec.SetField(notificationsubscription.FieldProjectID, field.TypeUUID, value) + _node.ProjectID = value + } + if value, ok := _c.mutation.TriggerActivities(); ok { + _spec.SetField(notificationsubscription.FieldTriggerActivities, field.TypeString, value) + _node.TriggerActivities = value + } + if value, ok := _c.mutation.CreatedBy(); ok { + _spec.SetField(notificationsubscription.FieldCreatedBy, field.TypeString, value) + _node.CreatedBy = value + } + if value, ok := _c.mutation.Created(); ok { + _spec.SetField(notificationsubscription.FieldCreated, field.TypeTime, value) + _node.Created = value + } + return _node, _spec +} + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.NotificationSubscription.Create(). +// SetScope(v). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.NotificationSubscriptionUpsert) { +// SetScope(v+v). +// }). +// Exec(ctx) +func (_c *NotificationSubscriptionCreate) OnConflict(opts ...sql.ConflictOption) *NotificationSubscriptionUpsertOne { + _c.conflict = opts + return &NotificationSubscriptionUpsertOne{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.NotificationSubscription.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *NotificationSubscriptionCreate) OnConflictColumns(columns ...string) *NotificationSubscriptionUpsertOne { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &NotificationSubscriptionUpsertOne{ + create: _c, + } +} + +type ( + // NotificationSubscriptionUpsertOne is the builder for "upsert"-ing + // one NotificationSubscription node. + NotificationSubscriptionUpsertOne struct { + create *NotificationSubscriptionCreate + } + + // NotificationSubscriptionUpsert is the "OnConflict" setter. + NotificationSubscriptionUpsert struct { + *sql.UpdateSet + } +) + +// SetScope sets the "scope" field. +func (u *NotificationSubscriptionUpsert) SetScope(v string) *NotificationSubscriptionUpsert { + u.Set(notificationsubscription.FieldScope, v) + return u +} + +// UpdateScope sets the "scope" field to the value that was provided on create. +func (u *NotificationSubscriptionUpsert) UpdateScope() *NotificationSubscriptionUpsert { + u.SetExcluded(notificationsubscription.FieldScope) + return u +} + +// SetAgentID sets the "agent_id" field. +func (u *NotificationSubscriptionUpsert) SetAgentID(v uuid.UUID) *NotificationSubscriptionUpsert { + u.Set(notificationsubscription.FieldAgentID, v) + return u +} + +// UpdateAgentID sets the "agent_id" field to the value that was provided on create. +func (u *NotificationSubscriptionUpsert) UpdateAgentID() *NotificationSubscriptionUpsert { + u.SetExcluded(notificationsubscription.FieldAgentID) + return u +} + +// ClearAgentID clears the value of the "agent_id" field. +func (u *NotificationSubscriptionUpsert) ClearAgentID() *NotificationSubscriptionUpsert { + u.SetNull(notificationsubscription.FieldAgentID) + return u +} + +// SetSubscriberType sets the "subscriber_type" field. +func (u *NotificationSubscriptionUpsert) SetSubscriberType(v string) *NotificationSubscriptionUpsert { + u.Set(notificationsubscription.FieldSubscriberType, v) + return u +} + +// UpdateSubscriberType sets the "subscriber_type" field to the value that was provided on create. +func (u *NotificationSubscriptionUpsert) UpdateSubscriberType() *NotificationSubscriptionUpsert { + u.SetExcluded(notificationsubscription.FieldSubscriberType) + return u +} + +// SetSubscriberID sets the "subscriber_id" field. +func (u *NotificationSubscriptionUpsert) SetSubscriberID(v string) *NotificationSubscriptionUpsert { + u.Set(notificationsubscription.FieldSubscriberID, v) + return u +} + +// UpdateSubscriberID sets the "subscriber_id" field to the value that was provided on create. +func (u *NotificationSubscriptionUpsert) UpdateSubscriberID() *NotificationSubscriptionUpsert { + u.SetExcluded(notificationsubscription.FieldSubscriberID) + return u +} + +// SetProjectID sets the "project_id" field. +func (u *NotificationSubscriptionUpsert) SetProjectID(v uuid.UUID) *NotificationSubscriptionUpsert { + u.Set(notificationsubscription.FieldProjectID, v) + return u +} + +// UpdateProjectID sets the "project_id" field to the value that was provided on create. +func (u *NotificationSubscriptionUpsert) UpdateProjectID() *NotificationSubscriptionUpsert { + u.SetExcluded(notificationsubscription.FieldProjectID) + return u +} + +// SetTriggerActivities sets the "trigger_activities" field. +func (u *NotificationSubscriptionUpsert) SetTriggerActivities(v string) *NotificationSubscriptionUpsert { + u.Set(notificationsubscription.FieldTriggerActivities, v) + return u +} + +// UpdateTriggerActivities sets the "trigger_activities" field to the value that was provided on create. +func (u *NotificationSubscriptionUpsert) UpdateTriggerActivities() *NotificationSubscriptionUpsert { + u.SetExcluded(notificationsubscription.FieldTriggerActivities) + return u +} + +// SetCreatedBy sets the "created_by" field. +func (u *NotificationSubscriptionUpsert) SetCreatedBy(v string) *NotificationSubscriptionUpsert { + u.Set(notificationsubscription.FieldCreatedBy, v) + return u +} + +// UpdateCreatedBy sets the "created_by" field to the value that was provided on create. +func (u *NotificationSubscriptionUpsert) UpdateCreatedBy() *NotificationSubscriptionUpsert { + u.SetExcluded(notificationsubscription.FieldCreatedBy) + return u +} + +// UpdateNewValues updates the mutable fields using the new values that were set on create except the ID field. +// Using this option is equivalent to using: +// +// client.NotificationSubscription.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(notificationsubscription.FieldID) +// }), +// ). +// Exec(ctx) +func (u *NotificationSubscriptionUpsertOne) UpdateNewValues() *NotificationSubscriptionUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + if _, exists := u.create.mutation.ID(); exists { + s.SetIgnore(notificationsubscription.FieldID) + } + if _, exists := u.create.mutation.Created(); exists { + s.SetIgnore(notificationsubscription.FieldCreated) + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.NotificationSubscription.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *NotificationSubscriptionUpsertOne) Ignore() *NotificationSubscriptionUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *NotificationSubscriptionUpsertOne) DoNothing() *NotificationSubscriptionUpsertOne { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the NotificationSubscriptionCreate.OnConflict +// documentation for more info. +func (u *NotificationSubscriptionUpsertOne) Update(set func(*NotificationSubscriptionUpsert)) *NotificationSubscriptionUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&NotificationSubscriptionUpsert{UpdateSet: update}) + })) + return u +} + +// SetScope sets the "scope" field. +func (u *NotificationSubscriptionUpsertOne) SetScope(v string) *NotificationSubscriptionUpsertOne { + return u.Update(func(s *NotificationSubscriptionUpsert) { + s.SetScope(v) + }) +} + +// UpdateScope sets the "scope" field to the value that was provided on create. +func (u *NotificationSubscriptionUpsertOne) UpdateScope() *NotificationSubscriptionUpsertOne { + return u.Update(func(s *NotificationSubscriptionUpsert) { + s.UpdateScope() + }) +} + +// SetAgentID sets the "agent_id" field. +func (u *NotificationSubscriptionUpsertOne) SetAgentID(v uuid.UUID) *NotificationSubscriptionUpsertOne { + return u.Update(func(s *NotificationSubscriptionUpsert) { + s.SetAgentID(v) + }) +} + +// UpdateAgentID sets the "agent_id" field to the value that was provided on create. +func (u *NotificationSubscriptionUpsertOne) UpdateAgentID() *NotificationSubscriptionUpsertOne { + return u.Update(func(s *NotificationSubscriptionUpsert) { + s.UpdateAgentID() + }) +} + +// ClearAgentID clears the value of the "agent_id" field. +func (u *NotificationSubscriptionUpsertOne) ClearAgentID() *NotificationSubscriptionUpsertOne { + return u.Update(func(s *NotificationSubscriptionUpsert) { + s.ClearAgentID() + }) +} + +// SetSubscriberType sets the "subscriber_type" field. +func (u *NotificationSubscriptionUpsertOne) SetSubscriberType(v string) *NotificationSubscriptionUpsertOne { + return u.Update(func(s *NotificationSubscriptionUpsert) { + s.SetSubscriberType(v) + }) +} + +// UpdateSubscriberType sets the "subscriber_type" field to the value that was provided on create. +func (u *NotificationSubscriptionUpsertOne) UpdateSubscriberType() *NotificationSubscriptionUpsertOne { + return u.Update(func(s *NotificationSubscriptionUpsert) { + s.UpdateSubscriberType() + }) +} + +// SetSubscriberID sets the "subscriber_id" field. +func (u *NotificationSubscriptionUpsertOne) SetSubscriberID(v string) *NotificationSubscriptionUpsertOne { + return u.Update(func(s *NotificationSubscriptionUpsert) { + s.SetSubscriberID(v) + }) +} + +// UpdateSubscriberID sets the "subscriber_id" field to the value that was provided on create. +func (u *NotificationSubscriptionUpsertOne) UpdateSubscriberID() *NotificationSubscriptionUpsertOne { + return u.Update(func(s *NotificationSubscriptionUpsert) { + s.UpdateSubscriberID() + }) +} + +// SetProjectID sets the "project_id" field. +func (u *NotificationSubscriptionUpsertOne) SetProjectID(v uuid.UUID) *NotificationSubscriptionUpsertOne { + return u.Update(func(s *NotificationSubscriptionUpsert) { + s.SetProjectID(v) + }) +} + +// UpdateProjectID sets the "project_id" field to the value that was provided on create. +func (u *NotificationSubscriptionUpsertOne) UpdateProjectID() *NotificationSubscriptionUpsertOne { + return u.Update(func(s *NotificationSubscriptionUpsert) { + s.UpdateProjectID() + }) +} + +// SetTriggerActivities sets the "trigger_activities" field. +func (u *NotificationSubscriptionUpsertOne) SetTriggerActivities(v string) *NotificationSubscriptionUpsertOne { + return u.Update(func(s *NotificationSubscriptionUpsert) { + s.SetTriggerActivities(v) + }) +} + +// UpdateTriggerActivities sets the "trigger_activities" field to the value that was provided on create. +func (u *NotificationSubscriptionUpsertOne) UpdateTriggerActivities() *NotificationSubscriptionUpsertOne { + return u.Update(func(s *NotificationSubscriptionUpsert) { + s.UpdateTriggerActivities() + }) +} + +// SetCreatedBy sets the "created_by" field. +func (u *NotificationSubscriptionUpsertOne) SetCreatedBy(v string) *NotificationSubscriptionUpsertOne { + return u.Update(func(s *NotificationSubscriptionUpsert) { + s.SetCreatedBy(v) + }) +} + +// UpdateCreatedBy sets the "created_by" field to the value that was provided on create. +func (u *NotificationSubscriptionUpsertOne) UpdateCreatedBy() *NotificationSubscriptionUpsertOne { + return u.Update(func(s *NotificationSubscriptionUpsert) { + s.UpdateCreatedBy() + }) +} + +// Exec executes the query. +func (u *NotificationSubscriptionUpsertOne) Exec(ctx context.Context) error { + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for NotificationSubscriptionCreate.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *NotificationSubscriptionUpsertOne) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} + +// Exec executes the UPSERT query and returns the inserted/updated ID. +func (u *NotificationSubscriptionUpsertOne) ID(ctx context.Context) (id uuid.UUID, err error) { + if u.create.driver.Dialect() == dialect.MySQL { + // In case of "ON CONFLICT", there is no way to get back non-numeric ID + // fields from the database since MySQL does not support the RETURNING clause. + return id, errors.New("ent: NotificationSubscriptionUpsertOne.ID is not supported by MySQL driver. Use NotificationSubscriptionUpsertOne.Exec instead") + } + node, err := u.create.Save(ctx) + if err != nil { + return id, err + } + return node.ID, nil +} + +// IDX is like ID, but panics if an error occurs. +func (u *NotificationSubscriptionUpsertOne) IDX(ctx context.Context) uuid.UUID { + id, err := u.ID(ctx) + if err != nil { + panic(err) + } + return id +} + +// NotificationSubscriptionCreateBulk is the builder for creating many NotificationSubscription entities in bulk. +type NotificationSubscriptionCreateBulk struct { + config + err error + builders []*NotificationSubscriptionCreate + conflict []sql.ConflictOption +} + +// Save creates the NotificationSubscription entities in the database. +func (_c *NotificationSubscriptionCreateBulk) Save(ctx context.Context) ([]*NotificationSubscription, error) { + if _c.err != nil { + return nil, _c.err + } + specs := make([]*sqlgraph.CreateSpec, len(_c.builders)) + nodes := make([]*NotificationSubscription, len(_c.builders)) + mutators := make([]Mutator, len(_c.builders)) + for i := range _c.builders { + func(i int, root context.Context) { + builder := _c.builders[i] + builder.defaults() + var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { + mutation, ok := m.(*NotificationSubscriptionMutation) + if !ok { + return nil, fmt.Errorf("unexpected mutation type %T", m) + } + if err := builder.check(); err != nil { + return nil, err + } + builder.mutation = mutation + var err error + nodes[i], specs[i] = builder.createSpec() + if i < len(mutators)-1 { + _, err = mutators[i+1].Mutate(root, _c.builders[i+1].mutation) + } else { + spec := &sqlgraph.BatchCreateSpec{Nodes: specs} + spec.OnConflict = _c.conflict + // Invoke the actual operation on the latest mutation in the chain. + if err = sqlgraph.BatchCreate(ctx, _c.driver, spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + } + } + if err != nil { + return nil, err + } + mutation.id = &nodes[i].ID + mutation.done = true + return nodes[i], nil + }) + for i := len(builder.hooks) - 1; i >= 0; i-- { + mut = builder.hooks[i](mut) + } + mutators[i] = mut + }(i, ctx) + } + if len(mutators) > 0 { + if _, err := mutators[0].Mutate(ctx, _c.builders[0].mutation); err != nil { + return nil, err + } + } + return nodes, nil +} + +// SaveX is like Save, but panics if an error occurs. +func (_c *NotificationSubscriptionCreateBulk) SaveX(ctx context.Context) []*NotificationSubscription { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *NotificationSubscriptionCreateBulk) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *NotificationSubscriptionCreateBulk) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.NotificationSubscription.CreateBulk(builders...). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.NotificationSubscriptionUpsert) { +// SetScope(v+v). +// }). +// Exec(ctx) +func (_c *NotificationSubscriptionCreateBulk) OnConflict(opts ...sql.ConflictOption) *NotificationSubscriptionUpsertBulk { + _c.conflict = opts + return &NotificationSubscriptionUpsertBulk{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.NotificationSubscription.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *NotificationSubscriptionCreateBulk) OnConflictColumns(columns ...string) *NotificationSubscriptionUpsertBulk { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &NotificationSubscriptionUpsertBulk{ + create: _c, + } +} + +// NotificationSubscriptionUpsertBulk is the builder for "upsert"-ing +// a bulk of NotificationSubscription nodes. +type NotificationSubscriptionUpsertBulk struct { + create *NotificationSubscriptionCreateBulk +} + +// UpdateNewValues updates the mutable fields using the new values that +// were set on create. Using this option is equivalent to using: +// +// client.NotificationSubscription.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(notificationsubscription.FieldID) +// }), +// ). +// Exec(ctx) +func (u *NotificationSubscriptionUpsertBulk) UpdateNewValues() *NotificationSubscriptionUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + for _, b := range u.create.builders { + if _, exists := b.mutation.ID(); exists { + s.SetIgnore(notificationsubscription.FieldID) + } + if _, exists := b.mutation.Created(); exists { + s.SetIgnore(notificationsubscription.FieldCreated) + } + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.NotificationSubscription.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *NotificationSubscriptionUpsertBulk) Ignore() *NotificationSubscriptionUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *NotificationSubscriptionUpsertBulk) DoNothing() *NotificationSubscriptionUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the NotificationSubscriptionCreateBulk.OnConflict +// documentation for more info. +func (u *NotificationSubscriptionUpsertBulk) Update(set func(*NotificationSubscriptionUpsert)) *NotificationSubscriptionUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&NotificationSubscriptionUpsert{UpdateSet: update}) + })) + return u +} + +// SetScope sets the "scope" field. +func (u *NotificationSubscriptionUpsertBulk) SetScope(v string) *NotificationSubscriptionUpsertBulk { + return u.Update(func(s *NotificationSubscriptionUpsert) { + s.SetScope(v) + }) +} + +// UpdateScope sets the "scope" field to the value that was provided on create. +func (u *NotificationSubscriptionUpsertBulk) UpdateScope() *NotificationSubscriptionUpsertBulk { + return u.Update(func(s *NotificationSubscriptionUpsert) { + s.UpdateScope() + }) +} + +// SetAgentID sets the "agent_id" field. +func (u *NotificationSubscriptionUpsertBulk) SetAgentID(v uuid.UUID) *NotificationSubscriptionUpsertBulk { + return u.Update(func(s *NotificationSubscriptionUpsert) { + s.SetAgentID(v) + }) +} + +// UpdateAgentID sets the "agent_id" field to the value that was provided on create. +func (u *NotificationSubscriptionUpsertBulk) UpdateAgentID() *NotificationSubscriptionUpsertBulk { + return u.Update(func(s *NotificationSubscriptionUpsert) { + s.UpdateAgentID() + }) +} + +// ClearAgentID clears the value of the "agent_id" field. +func (u *NotificationSubscriptionUpsertBulk) ClearAgentID() *NotificationSubscriptionUpsertBulk { + return u.Update(func(s *NotificationSubscriptionUpsert) { + s.ClearAgentID() + }) +} + +// SetSubscriberType sets the "subscriber_type" field. +func (u *NotificationSubscriptionUpsertBulk) SetSubscriberType(v string) *NotificationSubscriptionUpsertBulk { + return u.Update(func(s *NotificationSubscriptionUpsert) { + s.SetSubscriberType(v) + }) +} + +// UpdateSubscriberType sets the "subscriber_type" field to the value that was provided on create. +func (u *NotificationSubscriptionUpsertBulk) UpdateSubscriberType() *NotificationSubscriptionUpsertBulk { + return u.Update(func(s *NotificationSubscriptionUpsert) { + s.UpdateSubscriberType() + }) +} + +// SetSubscriberID sets the "subscriber_id" field. +func (u *NotificationSubscriptionUpsertBulk) SetSubscriberID(v string) *NotificationSubscriptionUpsertBulk { + return u.Update(func(s *NotificationSubscriptionUpsert) { + s.SetSubscriberID(v) + }) +} + +// UpdateSubscriberID sets the "subscriber_id" field to the value that was provided on create. +func (u *NotificationSubscriptionUpsertBulk) UpdateSubscriberID() *NotificationSubscriptionUpsertBulk { + return u.Update(func(s *NotificationSubscriptionUpsert) { + s.UpdateSubscriberID() + }) +} + +// SetProjectID sets the "project_id" field. +func (u *NotificationSubscriptionUpsertBulk) SetProjectID(v uuid.UUID) *NotificationSubscriptionUpsertBulk { + return u.Update(func(s *NotificationSubscriptionUpsert) { + s.SetProjectID(v) + }) +} + +// UpdateProjectID sets the "project_id" field to the value that was provided on create. +func (u *NotificationSubscriptionUpsertBulk) UpdateProjectID() *NotificationSubscriptionUpsertBulk { + return u.Update(func(s *NotificationSubscriptionUpsert) { + s.UpdateProjectID() + }) +} + +// SetTriggerActivities sets the "trigger_activities" field. +func (u *NotificationSubscriptionUpsertBulk) SetTriggerActivities(v string) *NotificationSubscriptionUpsertBulk { + return u.Update(func(s *NotificationSubscriptionUpsert) { + s.SetTriggerActivities(v) + }) +} + +// UpdateTriggerActivities sets the "trigger_activities" field to the value that was provided on create. +func (u *NotificationSubscriptionUpsertBulk) UpdateTriggerActivities() *NotificationSubscriptionUpsertBulk { + return u.Update(func(s *NotificationSubscriptionUpsert) { + s.UpdateTriggerActivities() + }) +} + +// SetCreatedBy sets the "created_by" field. +func (u *NotificationSubscriptionUpsertBulk) SetCreatedBy(v string) *NotificationSubscriptionUpsertBulk { + return u.Update(func(s *NotificationSubscriptionUpsert) { + s.SetCreatedBy(v) + }) +} + +// UpdateCreatedBy sets the "created_by" field to the value that was provided on create. +func (u *NotificationSubscriptionUpsertBulk) UpdateCreatedBy() *NotificationSubscriptionUpsertBulk { + return u.Update(func(s *NotificationSubscriptionUpsert) { + s.UpdateCreatedBy() + }) +} + +// Exec executes the query. +func (u *NotificationSubscriptionUpsertBulk) Exec(ctx context.Context) error { + if u.create.err != nil { + return u.create.err + } + for i, b := range u.create.builders { + if len(b.conflict) != 0 { + return fmt.Errorf("ent: OnConflict was set for builder %d. Set it on the NotificationSubscriptionCreateBulk instead", i) + } + } + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for NotificationSubscriptionCreateBulk.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *NotificationSubscriptionUpsertBulk) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/pkg/ent/notificationsubscription_delete.go b/pkg/ent/notificationsubscription_delete.go new file mode 100644 index 000000000..98118d835 --- /dev/null +++ b/pkg/ent/notificationsubscription_delete.go @@ -0,0 +1,88 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/notificationsubscription" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" +) + +// NotificationSubscriptionDelete is the builder for deleting a NotificationSubscription entity. +type NotificationSubscriptionDelete struct { + config + hooks []Hook + mutation *NotificationSubscriptionMutation +} + +// Where appends a list predicates to the NotificationSubscriptionDelete builder. +func (_d *NotificationSubscriptionDelete) Where(ps ...predicate.NotificationSubscription) *NotificationSubscriptionDelete { + _d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (_d *NotificationSubscriptionDelete) Exec(ctx context.Context) (int, error) { + return withHooks(ctx, _d.sqlExec, _d.mutation, _d.hooks) +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *NotificationSubscriptionDelete) ExecX(ctx context.Context) int { + n, err := _d.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (_d *NotificationSubscriptionDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(notificationsubscription.Table, sqlgraph.NewFieldSpec(notificationsubscription.FieldID, field.TypeUUID)) + if ps := _d.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + affected, err := sqlgraph.DeleteNodes(ctx, _d.driver, _spec) + if err != nil && sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + _d.mutation.done = true + return affected, err +} + +// NotificationSubscriptionDeleteOne is the builder for deleting a single NotificationSubscription entity. +type NotificationSubscriptionDeleteOne struct { + _d *NotificationSubscriptionDelete +} + +// Where appends a list predicates to the NotificationSubscriptionDelete builder. +func (_d *NotificationSubscriptionDeleteOne) Where(ps ...predicate.NotificationSubscription) *NotificationSubscriptionDeleteOne { + _d._d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query. +func (_d *NotificationSubscriptionDeleteOne) Exec(ctx context.Context) error { + n, err := _d._d.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{notificationsubscription.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *NotificationSubscriptionDeleteOne) ExecX(ctx context.Context) { + if err := _d.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/pkg/ent/notificationsubscription_query.go b/pkg/ent/notificationsubscription_query.go new file mode 100644 index 000000000..0122d0b9d --- /dev/null +++ b/pkg/ent/notificationsubscription_query.go @@ -0,0 +1,565 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "fmt" + "math" + + "entgo.io/ent" + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/notificationsubscription" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/google/uuid" +) + +// NotificationSubscriptionQuery is the builder for querying NotificationSubscription entities. +type NotificationSubscriptionQuery struct { + config + ctx *QueryContext + order []notificationsubscription.OrderOption + inters []Interceptor + predicates []predicate.NotificationSubscription + modifiers []func(*sql.Selector) + // intermediate query (i.e. traversal path). + sql *sql.Selector + path func(context.Context) (*sql.Selector, error) +} + +// Where adds a new predicate for the NotificationSubscriptionQuery builder. +func (_q *NotificationSubscriptionQuery) Where(ps ...predicate.NotificationSubscription) *NotificationSubscriptionQuery { + _q.predicates = append(_q.predicates, ps...) + return _q +} + +// Limit the number of records to be returned by this query. +func (_q *NotificationSubscriptionQuery) Limit(limit int) *NotificationSubscriptionQuery { + _q.ctx.Limit = &limit + return _q +} + +// Offset to start from. +func (_q *NotificationSubscriptionQuery) Offset(offset int) *NotificationSubscriptionQuery { + _q.ctx.Offset = &offset + return _q +} + +// Unique configures the query builder to filter duplicate records on query. +// By default, unique is set to true, and can be disabled using this method. +func (_q *NotificationSubscriptionQuery) Unique(unique bool) *NotificationSubscriptionQuery { + _q.ctx.Unique = &unique + return _q +} + +// Order specifies how the records should be ordered. +func (_q *NotificationSubscriptionQuery) Order(o ...notificationsubscription.OrderOption) *NotificationSubscriptionQuery { + _q.order = append(_q.order, o...) + return _q +} + +// First returns the first NotificationSubscription entity from the query. +// Returns a *NotFoundError when no NotificationSubscription was found. +func (_q *NotificationSubscriptionQuery) First(ctx context.Context) (*NotificationSubscription, error) { + nodes, err := _q.Limit(1).All(setContextOp(ctx, _q.ctx, ent.OpQueryFirst)) + if err != nil { + return nil, err + } + if len(nodes) == 0 { + return nil, &NotFoundError{notificationsubscription.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (_q *NotificationSubscriptionQuery) FirstX(ctx context.Context) *NotificationSubscription { + node, err := _q.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// FirstID returns the first NotificationSubscription ID from the query. +// Returns a *NotFoundError when no NotificationSubscription ID was found. +func (_q *NotificationSubscriptionQuery) FirstID(ctx context.Context) (id uuid.UUID, err error) { + var ids []uuid.UUID + if ids, err = _q.Limit(1).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryFirstID)); err != nil { + return + } + if len(ids) == 0 { + err = &NotFoundError{notificationsubscription.Label} + return + } + return ids[0], nil +} + +// FirstIDX is like FirstID, but panics if an error occurs. +func (_q *NotificationSubscriptionQuery) FirstIDX(ctx context.Context) uuid.UUID { + id, err := _q.FirstID(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return id +} + +// Only returns a single NotificationSubscription entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one NotificationSubscription entity is found. +// Returns a *NotFoundError when no NotificationSubscription entities are found. +func (_q *NotificationSubscriptionQuery) Only(ctx context.Context) (*NotificationSubscription, error) { + nodes, err := _q.Limit(2).All(setContextOp(ctx, _q.ctx, ent.OpQueryOnly)) + if err != nil { + return nil, err + } + switch len(nodes) { + case 1: + return nodes[0], nil + case 0: + return nil, &NotFoundError{notificationsubscription.Label} + default: + return nil, &NotSingularError{notificationsubscription.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (_q *NotificationSubscriptionQuery) OnlyX(ctx context.Context) *NotificationSubscription { + node, err := _q.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// OnlyID is like Only, but returns the only NotificationSubscription ID in the query. +// Returns a *NotSingularError when more than one NotificationSubscription ID is found. +// Returns a *NotFoundError when no entities are found. +func (_q *NotificationSubscriptionQuery) OnlyID(ctx context.Context) (id uuid.UUID, err error) { + var ids []uuid.UUID + if ids, err = _q.Limit(2).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryOnlyID)); err != nil { + return + } + switch len(ids) { + case 1: + id = ids[0] + case 0: + err = &NotFoundError{notificationsubscription.Label} + default: + err = &NotSingularError{notificationsubscription.Label} + } + return +} + +// OnlyIDX is like OnlyID, but panics if an error occurs. +func (_q *NotificationSubscriptionQuery) OnlyIDX(ctx context.Context) uuid.UUID { + id, err := _q.OnlyID(ctx) + if err != nil { + panic(err) + } + return id +} + +// All executes the query and returns a list of NotificationSubscriptions. +func (_q *NotificationSubscriptionQuery) All(ctx context.Context) ([]*NotificationSubscription, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryAll) + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*NotificationSubscription, *NotificationSubscriptionQuery]() + return withInterceptors[[]*NotificationSubscription](ctx, _q, qr, _q.inters) +} + +// AllX is like All, but panics if an error occurs. +func (_q *NotificationSubscriptionQuery) AllX(ctx context.Context) []*NotificationSubscription { + nodes, err := _q.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// IDs executes the query and returns a list of NotificationSubscription IDs. +func (_q *NotificationSubscriptionQuery) IDs(ctx context.Context) (ids []uuid.UUID, err error) { + if _q.ctx.Unique == nil && _q.path != nil { + _q.Unique(true) + } + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryIDs) + if err = _q.Select(notificationsubscription.FieldID).Scan(ctx, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// IDsX is like IDs, but panics if an error occurs. +func (_q *NotificationSubscriptionQuery) IDsX(ctx context.Context) []uuid.UUID { + ids, err := _q.IDs(ctx) + if err != nil { + panic(err) + } + return ids +} + +// Count returns the count of the given query. +func (_q *NotificationSubscriptionQuery) Count(ctx context.Context) (int, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryCount) + if err := _q.prepareQuery(ctx); err != nil { + return 0, err + } + return withInterceptors[int](ctx, _q, querierCount[*NotificationSubscriptionQuery](), _q.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (_q *NotificationSubscriptionQuery) CountX(ctx context.Context) int { + count, err := _q.Count(ctx) + if err != nil { + panic(err) + } + return count +} + +// Exist returns true if the query has elements in the graph. +func (_q *NotificationSubscriptionQuery) Exist(ctx context.Context) (bool, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryExist) + switch _, err := _q.FirstID(ctx); { + case IsNotFound(err): + return false, nil + case err != nil: + return false, fmt.Errorf("ent: check existence: %w", err) + default: + return true, nil + } +} + +// ExistX is like Exist, but panics if an error occurs. +func (_q *NotificationSubscriptionQuery) ExistX(ctx context.Context) bool { + exist, err := _q.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the NotificationSubscriptionQuery builder, including all associated steps. It can be +// used to prepare common query builders and use them differently after the clone is made. +func (_q *NotificationSubscriptionQuery) Clone() *NotificationSubscriptionQuery { + if _q == nil { + return nil + } + return &NotificationSubscriptionQuery{ + config: _q.config, + ctx: _q.ctx.Clone(), + order: append([]notificationsubscription.OrderOption{}, _q.order...), + inters: append([]Interceptor{}, _q.inters...), + predicates: append([]predicate.NotificationSubscription{}, _q.predicates...), + // clone intermediate query. + sql: _q.sql.Clone(), + path: _q.path, + } +} + +// GroupBy is used to group vertices by one or more fields/columns. +// It is often used with aggregate functions, like: count, max, mean, min, sum. +// +// Example: +// +// var v []struct { +// Scope string `json:"scope,omitempty"` +// Count int `json:"count,omitempty"` +// } +// +// client.NotificationSubscription.Query(). +// GroupBy(notificationsubscription.FieldScope). +// Aggregate(ent.Count()). +// Scan(ctx, &v) +func (_q *NotificationSubscriptionQuery) GroupBy(field string, fields ...string) *NotificationSubscriptionGroupBy { + _q.ctx.Fields = append([]string{field}, fields...) + grbuild := &NotificationSubscriptionGroupBy{build: _q} + grbuild.flds = &_q.ctx.Fields + grbuild.label = notificationsubscription.Label + grbuild.scan = grbuild.Scan + return grbuild +} + +// Select allows the selection one or more fields/columns for the given query, +// instead of selecting all fields in the entity. +// +// Example: +// +// var v []struct { +// Scope string `json:"scope,omitempty"` +// } +// +// client.NotificationSubscription.Query(). +// Select(notificationsubscription.FieldScope). +// Scan(ctx, &v) +func (_q *NotificationSubscriptionQuery) Select(fields ...string) *NotificationSubscriptionSelect { + _q.ctx.Fields = append(_q.ctx.Fields, fields...) + sbuild := &NotificationSubscriptionSelect{NotificationSubscriptionQuery: _q} + sbuild.label = notificationsubscription.Label + sbuild.flds, sbuild.scan = &_q.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a NotificationSubscriptionSelect configured with the given aggregations. +func (_q *NotificationSubscriptionQuery) Aggregate(fns ...AggregateFunc) *NotificationSubscriptionSelect { + return _q.Select().Aggregate(fns...) +} + +func (_q *NotificationSubscriptionQuery) prepareQuery(ctx context.Context) error { + for _, inter := range _q.inters { + if inter == nil { + return fmt.Errorf("ent: uninitialized interceptor (forgotten import ent/runtime?)") + } + if trv, ok := inter.(Traverser); ok { + if err := trv.Traverse(ctx, _q); err != nil { + return err + } + } + } + for _, f := range _q.ctx.Fields { + if !notificationsubscription.ValidColumn(f) { + return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + } + if _q.path != nil { + prev, err := _q.path(ctx) + if err != nil { + return err + } + _q.sql = prev + } + return nil +} + +func (_q *NotificationSubscriptionQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*NotificationSubscription, error) { + var ( + nodes = []*NotificationSubscription{} + _spec = _q.querySpec() + ) + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*NotificationSubscription).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &NotificationSubscription{config: _q.config} + nodes = append(nodes, node) + return node.assignValues(columns, values) + } + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } + for i := range hooks { + hooks[i](ctx, _spec) + } + if err := sqlgraph.QueryNodes(ctx, _q.driver, _spec); err != nil { + return nil, err + } + if len(nodes) == 0 { + return nodes, nil + } + return nodes, nil +} + +func (_q *NotificationSubscriptionQuery) sqlCount(ctx context.Context) (int, error) { + _spec := _q.querySpec() + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } + _spec.Node.Columns = _q.ctx.Fields + if len(_q.ctx.Fields) > 0 { + _spec.Unique = _q.ctx.Unique != nil && *_q.ctx.Unique + } + return sqlgraph.CountNodes(ctx, _q.driver, _spec) +} + +func (_q *NotificationSubscriptionQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(notificationsubscription.Table, notificationsubscription.Columns, sqlgraph.NewFieldSpec(notificationsubscription.FieldID, field.TypeUUID)) + _spec.From = _q.sql + if unique := _q.ctx.Unique; unique != nil { + _spec.Unique = *unique + } else if _q.path != nil { + _spec.Unique = true + } + if fields := _q.ctx.Fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, notificationsubscription.FieldID) + for i := range fields { + if fields[i] != notificationsubscription.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, fields[i]) + } + } + } + if ps := _q.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if limit := _q.ctx.Limit; limit != nil { + _spec.Limit = *limit + } + if offset := _q.ctx.Offset; offset != nil { + _spec.Offset = *offset + } + if ps := _q.order; len(ps) > 0 { + _spec.Order = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + return _spec +} + +func (_q *NotificationSubscriptionQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(_q.driver.Dialect()) + t1 := builder.Table(notificationsubscription.Table) + columns := _q.ctx.Fields + if len(columns) == 0 { + columns = notificationsubscription.Columns + } + selector := builder.Select(t1.Columns(columns...)...).From(t1) + if _q.sql != nil { + selector = _q.sql + selector.Select(selector.Columns(columns...)...) + } + if _q.ctx.Unique != nil && *_q.ctx.Unique { + selector.Distinct() + } + for _, m := range _q.modifiers { + m(selector) + } + for _, p := range _q.predicates { + p(selector) + } + for _, p := range _q.order { + p(selector) + } + if offset := _q.ctx.Offset; offset != nil { + // limit is mandatory for offset clause. We start + // with default value, and override it below if needed. + selector.Offset(*offset).Limit(math.MaxInt32) + } + if limit := _q.ctx.Limit; limit != nil { + selector.Limit(*limit) + } + return selector +} + +// ForUpdate locks the selected rows against concurrent updates, and prevent them from being +// updated, deleted or "selected ... for update" by other sessions, until the transaction is +// either committed or rolled-back. +func (_q *NotificationSubscriptionQuery) ForUpdate(opts ...sql.LockOption) *NotificationSubscriptionQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForUpdate(opts...) + }) + return _q +} + +// ForShare behaves similarly to ForUpdate, except that it acquires a shared mode lock +// on any rows that are read. Other sessions can read the rows, but cannot modify them +// until your transaction commits. +func (_q *NotificationSubscriptionQuery) ForShare(opts ...sql.LockOption) *NotificationSubscriptionQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForShare(opts...) + }) + return _q +} + +// NotificationSubscriptionGroupBy is the group-by builder for NotificationSubscription entities. +type NotificationSubscriptionGroupBy struct { + selector + build *NotificationSubscriptionQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (_g *NotificationSubscriptionGroupBy) Aggregate(fns ...AggregateFunc) *NotificationSubscriptionGroupBy { + _g.fns = append(_g.fns, fns...) + return _g +} + +// Scan applies the selector query and scans the result into the given value. +func (_g *NotificationSubscriptionGroupBy) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, _g.build.ctx, ent.OpQueryGroupBy) + if err := _g.build.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*NotificationSubscriptionQuery, *NotificationSubscriptionGroupBy](ctx, _g.build, _g, _g.build.inters, v) +} + +func (_g *NotificationSubscriptionGroupBy) sqlScan(ctx context.Context, root *NotificationSubscriptionQuery, v any) error { + selector := root.sqlQuery(ctx).Select() + aggregation := make([]string, 0, len(_g.fns)) + for _, fn := range _g.fns { + aggregation = append(aggregation, fn(selector)) + } + if len(selector.SelectedColumns()) == 0 { + columns := make([]string, 0, len(*_g.flds)+len(_g.fns)) + for _, f := range *_g.flds { + columns = append(columns, selector.C(f)) + } + columns = append(columns, aggregation...) + selector.Select(columns...) + } + selector.GroupBy(selector.Columns(*_g.flds...)...) + if err := selector.Err(); err != nil { + return err + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := _g.build.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} + +// NotificationSubscriptionSelect is the builder for selecting fields of NotificationSubscription entities. +type NotificationSubscriptionSelect struct { + *NotificationSubscriptionQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (_s *NotificationSubscriptionSelect) Aggregate(fns ...AggregateFunc) *NotificationSubscriptionSelect { + _s.fns = append(_s.fns, fns...) + return _s +} + +// Scan applies the selector query and scans the result into the given value. +func (_s *NotificationSubscriptionSelect) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, _s.ctx, ent.OpQuerySelect) + if err := _s.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*NotificationSubscriptionQuery, *NotificationSubscriptionSelect](ctx, _s.NotificationSubscriptionQuery, _s, _s.inters, v) +} + +func (_s *NotificationSubscriptionSelect) sqlScan(ctx context.Context, root *NotificationSubscriptionQuery, v any) error { + selector := root.sqlQuery(ctx) + aggregation := make([]string, 0, len(_s.fns)) + for _, fn := range _s.fns { + aggregation = append(aggregation, fn(selector)) + } + switch n := len(*_s.selector.flds); { + case n == 0 && len(aggregation) > 0: + selector.Select(aggregation...) + case n != 0 && len(aggregation) > 0: + selector.AppendSelect(aggregation...) + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := _s.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} diff --git a/pkg/ent/notificationsubscription_update.go b/pkg/ent/notificationsubscription_update.go new file mode 100644 index 000000000..94495f7c1 --- /dev/null +++ b/pkg/ent/notificationsubscription_update.go @@ -0,0 +1,478 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/notificationsubscription" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/google/uuid" +) + +// NotificationSubscriptionUpdate is the builder for updating NotificationSubscription entities. +type NotificationSubscriptionUpdate struct { + config + hooks []Hook + mutation *NotificationSubscriptionMutation +} + +// Where appends a list predicates to the NotificationSubscriptionUpdate builder. +func (_u *NotificationSubscriptionUpdate) Where(ps ...predicate.NotificationSubscription) *NotificationSubscriptionUpdate { + _u.mutation.Where(ps...) + return _u +} + +// SetScope sets the "scope" field. +func (_u *NotificationSubscriptionUpdate) SetScope(v string) *NotificationSubscriptionUpdate { + _u.mutation.SetScope(v) + return _u +} + +// SetNillableScope sets the "scope" field if the given value is not nil. +func (_u *NotificationSubscriptionUpdate) SetNillableScope(v *string) *NotificationSubscriptionUpdate { + if v != nil { + _u.SetScope(*v) + } + return _u +} + +// SetAgentID sets the "agent_id" field. +func (_u *NotificationSubscriptionUpdate) SetAgentID(v uuid.UUID) *NotificationSubscriptionUpdate { + _u.mutation.SetAgentID(v) + return _u +} + +// SetNillableAgentID sets the "agent_id" field if the given value is not nil. +func (_u *NotificationSubscriptionUpdate) SetNillableAgentID(v *uuid.UUID) *NotificationSubscriptionUpdate { + if v != nil { + _u.SetAgentID(*v) + } + return _u +} + +// ClearAgentID clears the value of the "agent_id" field. +func (_u *NotificationSubscriptionUpdate) ClearAgentID() *NotificationSubscriptionUpdate { + _u.mutation.ClearAgentID() + return _u +} + +// SetSubscriberType sets the "subscriber_type" field. +func (_u *NotificationSubscriptionUpdate) SetSubscriberType(v string) *NotificationSubscriptionUpdate { + _u.mutation.SetSubscriberType(v) + return _u +} + +// SetNillableSubscriberType sets the "subscriber_type" field if the given value is not nil. +func (_u *NotificationSubscriptionUpdate) SetNillableSubscriberType(v *string) *NotificationSubscriptionUpdate { + if v != nil { + _u.SetSubscriberType(*v) + } + return _u +} + +// SetSubscriberID sets the "subscriber_id" field. +func (_u *NotificationSubscriptionUpdate) SetSubscriberID(v string) *NotificationSubscriptionUpdate { + _u.mutation.SetSubscriberID(v) + return _u +} + +// SetNillableSubscriberID sets the "subscriber_id" field if the given value is not nil. +func (_u *NotificationSubscriptionUpdate) SetNillableSubscriberID(v *string) *NotificationSubscriptionUpdate { + if v != nil { + _u.SetSubscriberID(*v) + } + return _u +} + +// SetProjectID sets the "project_id" field. +func (_u *NotificationSubscriptionUpdate) SetProjectID(v uuid.UUID) *NotificationSubscriptionUpdate { + _u.mutation.SetProjectID(v) + return _u +} + +// SetNillableProjectID sets the "project_id" field if the given value is not nil. +func (_u *NotificationSubscriptionUpdate) SetNillableProjectID(v *uuid.UUID) *NotificationSubscriptionUpdate { + if v != nil { + _u.SetProjectID(*v) + } + return _u +} + +// SetTriggerActivities sets the "trigger_activities" field. +func (_u *NotificationSubscriptionUpdate) SetTriggerActivities(v string) *NotificationSubscriptionUpdate { + _u.mutation.SetTriggerActivities(v) + return _u +} + +// SetNillableTriggerActivities sets the "trigger_activities" field if the given value is not nil. +func (_u *NotificationSubscriptionUpdate) SetNillableTriggerActivities(v *string) *NotificationSubscriptionUpdate { + if v != nil { + _u.SetTriggerActivities(*v) + } + return _u +} + +// SetCreatedBy sets the "created_by" field. +func (_u *NotificationSubscriptionUpdate) SetCreatedBy(v string) *NotificationSubscriptionUpdate { + _u.mutation.SetCreatedBy(v) + return _u +} + +// SetNillableCreatedBy sets the "created_by" field if the given value is not nil. +func (_u *NotificationSubscriptionUpdate) SetNillableCreatedBy(v *string) *NotificationSubscriptionUpdate { + if v != nil { + _u.SetCreatedBy(*v) + } + return _u +} + +// Mutation returns the NotificationSubscriptionMutation object of the builder. +func (_u *NotificationSubscriptionUpdate) Mutation() *NotificationSubscriptionMutation { + return _u.mutation +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (_u *NotificationSubscriptionUpdate) Save(ctx context.Context) (int, error) { + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *NotificationSubscriptionUpdate) SaveX(ctx context.Context) int { + affected, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (_u *NotificationSubscriptionUpdate) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *NotificationSubscriptionUpdate) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_u *NotificationSubscriptionUpdate) check() error { + if v, ok := _u.mutation.SubscriberID(); ok { + if err := notificationsubscription.SubscriberIDValidator(v); err != nil { + return &ValidationError{Name: "subscriber_id", err: fmt.Errorf(`ent: validator failed for field "NotificationSubscription.subscriber_id": %w`, err)} + } + } + if v, ok := _u.mutation.TriggerActivities(); ok { + if err := notificationsubscription.TriggerActivitiesValidator(v); err != nil { + return &ValidationError{Name: "trigger_activities", err: fmt.Errorf(`ent: validator failed for field "NotificationSubscription.trigger_activities": %w`, err)} + } + } + if v, ok := _u.mutation.CreatedBy(); ok { + if err := notificationsubscription.CreatedByValidator(v); err != nil { + return &ValidationError{Name: "created_by", err: fmt.Errorf(`ent: validator failed for field "NotificationSubscription.created_by": %w`, err)} + } + } + return nil +} + +func (_u *NotificationSubscriptionUpdate) sqlSave(ctx context.Context) (_node int, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(notificationsubscription.Table, notificationsubscription.Columns, sqlgraph.NewFieldSpec(notificationsubscription.FieldID, field.TypeUUID)) + if ps := _u.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := _u.mutation.Scope(); ok { + _spec.SetField(notificationsubscription.FieldScope, field.TypeString, value) + } + if value, ok := _u.mutation.AgentID(); ok { + _spec.SetField(notificationsubscription.FieldAgentID, field.TypeUUID, value) + } + if _u.mutation.AgentIDCleared() { + _spec.ClearField(notificationsubscription.FieldAgentID, field.TypeUUID) + } + if value, ok := _u.mutation.SubscriberType(); ok { + _spec.SetField(notificationsubscription.FieldSubscriberType, field.TypeString, value) + } + if value, ok := _u.mutation.SubscriberID(); ok { + _spec.SetField(notificationsubscription.FieldSubscriberID, field.TypeString, value) + } + if value, ok := _u.mutation.ProjectID(); ok { + _spec.SetField(notificationsubscription.FieldProjectID, field.TypeUUID, value) + } + if value, ok := _u.mutation.TriggerActivities(); ok { + _spec.SetField(notificationsubscription.FieldTriggerActivities, field.TypeString, value) + } + if value, ok := _u.mutation.CreatedBy(); ok { + _spec.SetField(notificationsubscription.FieldCreatedBy, field.TypeString, value) + } + if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{notificationsubscription.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + _u.mutation.done = true + return _node, nil +} + +// NotificationSubscriptionUpdateOne is the builder for updating a single NotificationSubscription entity. +type NotificationSubscriptionUpdateOne struct { + config + fields []string + hooks []Hook + mutation *NotificationSubscriptionMutation +} + +// SetScope sets the "scope" field. +func (_u *NotificationSubscriptionUpdateOne) SetScope(v string) *NotificationSubscriptionUpdateOne { + _u.mutation.SetScope(v) + return _u +} + +// SetNillableScope sets the "scope" field if the given value is not nil. +func (_u *NotificationSubscriptionUpdateOne) SetNillableScope(v *string) *NotificationSubscriptionUpdateOne { + if v != nil { + _u.SetScope(*v) + } + return _u +} + +// SetAgentID sets the "agent_id" field. +func (_u *NotificationSubscriptionUpdateOne) SetAgentID(v uuid.UUID) *NotificationSubscriptionUpdateOne { + _u.mutation.SetAgentID(v) + return _u +} + +// SetNillableAgentID sets the "agent_id" field if the given value is not nil. +func (_u *NotificationSubscriptionUpdateOne) SetNillableAgentID(v *uuid.UUID) *NotificationSubscriptionUpdateOne { + if v != nil { + _u.SetAgentID(*v) + } + return _u +} + +// ClearAgentID clears the value of the "agent_id" field. +func (_u *NotificationSubscriptionUpdateOne) ClearAgentID() *NotificationSubscriptionUpdateOne { + _u.mutation.ClearAgentID() + return _u +} + +// SetSubscriberType sets the "subscriber_type" field. +func (_u *NotificationSubscriptionUpdateOne) SetSubscriberType(v string) *NotificationSubscriptionUpdateOne { + _u.mutation.SetSubscriberType(v) + return _u +} + +// SetNillableSubscriberType sets the "subscriber_type" field if the given value is not nil. +func (_u *NotificationSubscriptionUpdateOne) SetNillableSubscriberType(v *string) *NotificationSubscriptionUpdateOne { + if v != nil { + _u.SetSubscriberType(*v) + } + return _u +} + +// SetSubscriberID sets the "subscriber_id" field. +func (_u *NotificationSubscriptionUpdateOne) SetSubscriberID(v string) *NotificationSubscriptionUpdateOne { + _u.mutation.SetSubscriberID(v) + return _u +} + +// SetNillableSubscriberID sets the "subscriber_id" field if the given value is not nil. +func (_u *NotificationSubscriptionUpdateOne) SetNillableSubscriberID(v *string) *NotificationSubscriptionUpdateOne { + if v != nil { + _u.SetSubscriberID(*v) + } + return _u +} + +// SetProjectID sets the "project_id" field. +func (_u *NotificationSubscriptionUpdateOne) SetProjectID(v uuid.UUID) *NotificationSubscriptionUpdateOne { + _u.mutation.SetProjectID(v) + return _u +} + +// SetNillableProjectID sets the "project_id" field if the given value is not nil. +func (_u *NotificationSubscriptionUpdateOne) SetNillableProjectID(v *uuid.UUID) *NotificationSubscriptionUpdateOne { + if v != nil { + _u.SetProjectID(*v) + } + return _u +} + +// SetTriggerActivities sets the "trigger_activities" field. +func (_u *NotificationSubscriptionUpdateOne) SetTriggerActivities(v string) *NotificationSubscriptionUpdateOne { + _u.mutation.SetTriggerActivities(v) + return _u +} + +// SetNillableTriggerActivities sets the "trigger_activities" field if the given value is not nil. +func (_u *NotificationSubscriptionUpdateOne) SetNillableTriggerActivities(v *string) *NotificationSubscriptionUpdateOne { + if v != nil { + _u.SetTriggerActivities(*v) + } + return _u +} + +// SetCreatedBy sets the "created_by" field. +func (_u *NotificationSubscriptionUpdateOne) SetCreatedBy(v string) *NotificationSubscriptionUpdateOne { + _u.mutation.SetCreatedBy(v) + return _u +} + +// SetNillableCreatedBy sets the "created_by" field if the given value is not nil. +func (_u *NotificationSubscriptionUpdateOne) SetNillableCreatedBy(v *string) *NotificationSubscriptionUpdateOne { + if v != nil { + _u.SetCreatedBy(*v) + } + return _u +} + +// Mutation returns the NotificationSubscriptionMutation object of the builder. +func (_u *NotificationSubscriptionUpdateOne) Mutation() *NotificationSubscriptionMutation { + return _u.mutation +} + +// Where appends a list predicates to the NotificationSubscriptionUpdate builder. +func (_u *NotificationSubscriptionUpdateOne) Where(ps ...predicate.NotificationSubscription) *NotificationSubscriptionUpdateOne { + _u.mutation.Where(ps...) + return _u +} + +// Select allows selecting one or more fields (columns) of the returned entity. +// The default is selecting all fields defined in the entity schema. +func (_u *NotificationSubscriptionUpdateOne) Select(field string, fields ...string) *NotificationSubscriptionUpdateOne { + _u.fields = append([]string{field}, fields...) + return _u +} + +// Save executes the query and returns the updated NotificationSubscription entity. +func (_u *NotificationSubscriptionUpdateOne) Save(ctx context.Context) (*NotificationSubscription, error) { + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *NotificationSubscriptionUpdateOne) SaveX(ctx context.Context) *NotificationSubscription { + node, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (_u *NotificationSubscriptionUpdateOne) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *NotificationSubscriptionUpdateOne) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_u *NotificationSubscriptionUpdateOne) check() error { + if v, ok := _u.mutation.SubscriberID(); ok { + if err := notificationsubscription.SubscriberIDValidator(v); err != nil { + return &ValidationError{Name: "subscriber_id", err: fmt.Errorf(`ent: validator failed for field "NotificationSubscription.subscriber_id": %w`, err)} + } + } + if v, ok := _u.mutation.TriggerActivities(); ok { + if err := notificationsubscription.TriggerActivitiesValidator(v); err != nil { + return &ValidationError{Name: "trigger_activities", err: fmt.Errorf(`ent: validator failed for field "NotificationSubscription.trigger_activities": %w`, err)} + } + } + if v, ok := _u.mutation.CreatedBy(); ok { + if err := notificationsubscription.CreatedByValidator(v); err != nil { + return &ValidationError{Name: "created_by", err: fmt.Errorf(`ent: validator failed for field "NotificationSubscription.created_by": %w`, err)} + } + } + return nil +} + +func (_u *NotificationSubscriptionUpdateOne) sqlSave(ctx context.Context) (_node *NotificationSubscription, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(notificationsubscription.Table, notificationsubscription.Columns, sqlgraph.NewFieldSpec(notificationsubscription.FieldID, field.TypeUUID)) + id, ok := _u.mutation.ID() + if !ok { + return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "NotificationSubscription.id" for update`)} + } + _spec.Node.ID.Value = id + if fields := _u.fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, notificationsubscription.FieldID) + for _, f := range fields { + if !notificationsubscription.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + if f != notificationsubscription.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, f) + } + } + } + if ps := _u.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := _u.mutation.Scope(); ok { + _spec.SetField(notificationsubscription.FieldScope, field.TypeString, value) + } + if value, ok := _u.mutation.AgentID(); ok { + _spec.SetField(notificationsubscription.FieldAgentID, field.TypeUUID, value) + } + if _u.mutation.AgentIDCleared() { + _spec.ClearField(notificationsubscription.FieldAgentID, field.TypeUUID) + } + if value, ok := _u.mutation.SubscriberType(); ok { + _spec.SetField(notificationsubscription.FieldSubscriberType, field.TypeString, value) + } + if value, ok := _u.mutation.SubscriberID(); ok { + _spec.SetField(notificationsubscription.FieldSubscriberID, field.TypeString, value) + } + if value, ok := _u.mutation.ProjectID(); ok { + _spec.SetField(notificationsubscription.FieldProjectID, field.TypeUUID, value) + } + if value, ok := _u.mutation.TriggerActivities(); ok { + _spec.SetField(notificationsubscription.FieldTriggerActivities, field.TypeString, value) + } + if value, ok := _u.mutation.CreatedBy(); ok { + _spec.SetField(notificationsubscription.FieldCreatedBy, field.TypeString, value) + } + _node = &NotificationSubscription{config: _u.config} + _spec.Assign = _node.assignValues + _spec.ScanValues = _node.scanValues + if err = sqlgraph.UpdateNode(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{notificationsubscription.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + _u.mutation.done = true + return _node, nil +} diff --git a/pkg/ent/policybinding_create.go b/pkg/ent/policybinding_create.go index 41f8cd3a9..d57a7ad41 100644 --- a/pkg/ent/policybinding_create.go +++ b/pkg/ent/policybinding_create.go @@ -8,6 +8,8 @@ import ( "fmt" "time" + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" "entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/schema/field" "github.com/GoogleCloudPlatform/scion/pkg/ent/accesspolicy" @@ -23,6 +25,7 @@ type PolicyBindingCreate struct { config mutation *PolicyBindingMutation hooks []Hook + conflict []sql.ConflictOption } // SetPrincipalType sets the "principal_type" field. @@ -238,6 +241,7 @@ func (_c *PolicyBindingCreate) createSpec() (*PolicyBinding, *sqlgraph.CreateSpe _node = &PolicyBinding{config: _c.config} _spec = sqlgraph.NewCreateSpec(policybinding.Table, sqlgraph.NewFieldSpec(policybinding.FieldID, field.TypeUUID)) ) + _spec.OnConflict = _c.conflict if id, ok := _c.mutation.ID(); ok { _node.ID = id _spec.ID.Value = &id @@ -325,11 +329,371 @@ func (_c *PolicyBindingCreate) createSpec() (*PolicyBinding, *sqlgraph.CreateSpe return _node, _spec } +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.PolicyBinding.Create(). +// SetPrincipalType(v). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.PolicyBindingUpsert) { +// SetPrincipalType(v+v). +// }). +// Exec(ctx) +func (_c *PolicyBindingCreate) OnConflict(opts ...sql.ConflictOption) *PolicyBindingUpsertOne { + _c.conflict = opts + return &PolicyBindingUpsertOne{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.PolicyBinding.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *PolicyBindingCreate) OnConflictColumns(columns ...string) *PolicyBindingUpsertOne { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &PolicyBindingUpsertOne{ + create: _c, + } +} + +type ( + // PolicyBindingUpsertOne is the builder for "upsert"-ing + // one PolicyBinding node. + PolicyBindingUpsertOne struct { + create *PolicyBindingCreate + } + + // PolicyBindingUpsert is the "OnConflict" setter. + PolicyBindingUpsert struct { + *sql.UpdateSet + } +) + +// SetPrincipalType sets the "principal_type" field. +func (u *PolicyBindingUpsert) SetPrincipalType(v policybinding.PrincipalType) *PolicyBindingUpsert { + u.Set(policybinding.FieldPrincipalType, v) + return u +} + +// UpdatePrincipalType sets the "principal_type" field to the value that was provided on create. +func (u *PolicyBindingUpsert) UpdatePrincipalType() *PolicyBindingUpsert { + u.SetExcluded(policybinding.FieldPrincipalType) + return u +} + +// SetCreatedBy sets the "created_by" field. +func (u *PolicyBindingUpsert) SetCreatedBy(v string) *PolicyBindingUpsert { + u.Set(policybinding.FieldCreatedBy, v) + return u +} + +// UpdateCreatedBy sets the "created_by" field to the value that was provided on create. +func (u *PolicyBindingUpsert) UpdateCreatedBy() *PolicyBindingUpsert { + u.SetExcluded(policybinding.FieldCreatedBy) + return u +} + +// ClearCreatedBy clears the value of the "created_by" field. +func (u *PolicyBindingUpsert) ClearCreatedBy() *PolicyBindingUpsert { + u.SetNull(policybinding.FieldCreatedBy) + return u +} + +// SetPolicyID sets the "policy_id" field. +func (u *PolicyBindingUpsert) SetPolicyID(v uuid.UUID) *PolicyBindingUpsert { + u.Set(policybinding.FieldPolicyID, v) + return u +} + +// UpdatePolicyID sets the "policy_id" field to the value that was provided on create. +func (u *PolicyBindingUpsert) UpdatePolicyID() *PolicyBindingUpsert { + u.SetExcluded(policybinding.FieldPolicyID) + return u +} + +// ClearPolicyID clears the value of the "policy_id" field. +func (u *PolicyBindingUpsert) ClearPolicyID() *PolicyBindingUpsert { + u.SetNull(policybinding.FieldPolicyID) + return u +} + +// SetUserID sets the "user_id" field. +func (u *PolicyBindingUpsert) SetUserID(v uuid.UUID) *PolicyBindingUpsert { + u.Set(policybinding.FieldUserID, v) + return u +} + +// UpdateUserID sets the "user_id" field to the value that was provided on create. +func (u *PolicyBindingUpsert) UpdateUserID() *PolicyBindingUpsert { + u.SetExcluded(policybinding.FieldUserID) + return u +} + +// ClearUserID clears the value of the "user_id" field. +func (u *PolicyBindingUpsert) ClearUserID() *PolicyBindingUpsert { + u.SetNull(policybinding.FieldUserID) + return u +} + +// SetGroupID sets the "group_id" field. +func (u *PolicyBindingUpsert) SetGroupID(v uuid.UUID) *PolicyBindingUpsert { + u.Set(policybinding.FieldGroupID, v) + return u +} + +// UpdateGroupID sets the "group_id" field to the value that was provided on create. +func (u *PolicyBindingUpsert) UpdateGroupID() *PolicyBindingUpsert { + u.SetExcluded(policybinding.FieldGroupID) + return u +} + +// ClearGroupID clears the value of the "group_id" field. +func (u *PolicyBindingUpsert) ClearGroupID() *PolicyBindingUpsert { + u.SetNull(policybinding.FieldGroupID) + return u +} + +// SetAgentID sets the "agent_id" field. +func (u *PolicyBindingUpsert) SetAgentID(v uuid.UUID) *PolicyBindingUpsert { + u.Set(policybinding.FieldAgentID, v) + return u +} + +// UpdateAgentID sets the "agent_id" field to the value that was provided on create. +func (u *PolicyBindingUpsert) UpdateAgentID() *PolicyBindingUpsert { + u.SetExcluded(policybinding.FieldAgentID) + return u +} + +// ClearAgentID clears the value of the "agent_id" field. +func (u *PolicyBindingUpsert) ClearAgentID() *PolicyBindingUpsert { + u.SetNull(policybinding.FieldAgentID) + return u +} + +// UpdateNewValues updates the mutable fields using the new values that were set on create except the ID field. +// Using this option is equivalent to using: +// +// client.PolicyBinding.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(policybinding.FieldID) +// }), +// ). +// Exec(ctx) +func (u *PolicyBindingUpsertOne) UpdateNewValues() *PolicyBindingUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + if _, exists := u.create.mutation.ID(); exists { + s.SetIgnore(policybinding.FieldID) + } + if _, exists := u.create.mutation.Created(); exists { + s.SetIgnore(policybinding.FieldCreated) + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.PolicyBinding.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *PolicyBindingUpsertOne) Ignore() *PolicyBindingUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *PolicyBindingUpsertOne) DoNothing() *PolicyBindingUpsertOne { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the PolicyBindingCreate.OnConflict +// documentation for more info. +func (u *PolicyBindingUpsertOne) Update(set func(*PolicyBindingUpsert)) *PolicyBindingUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&PolicyBindingUpsert{UpdateSet: update}) + })) + return u +} + +// SetPrincipalType sets the "principal_type" field. +func (u *PolicyBindingUpsertOne) SetPrincipalType(v policybinding.PrincipalType) *PolicyBindingUpsertOne { + return u.Update(func(s *PolicyBindingUpsert) { + s.SetPrincipalType(v) + }) +} + +// UpdatePrincipalType sets the "principal_type" field to the value that was provided on create. +func (u *PolicyBindingUpsertOne) UpdatePrincipalType() *PolicyBindingUpsertOne { + return u.Update(func(s *PolicyBindingUpsert) { + s.UpdatePrincipalType() + }) +} + +// SetCreatedBy sets the "created_by" field. +func (u *PolicyBindingUpsertOne) SetCreatedBy(v string) *PolicyBindingUpsertOne { + return u.Update(func(s *PolicyBindingUpsert) { + s.SetCreatedBy(v) + }) +} + +// UpdateCreatedBy sets the "created_by" field to the value that was provided on create. +func (u *PolicyBindingUpsertOne) UpdateCreatedBy() *PolicyBindingUpsertOne { + return u.Update(func(s *PolicyBindingUpsert) { + s.UpdateCreatedBy() + }) +} + +// ClearCreatedBy clears the value of the "created_by" field. +func (u *PolicyBindingUpsertOne) ClearCreatedBy() *PolicyBindingUpsertOne { + return u.Update(func(s *PolicyBindingUpsert) { + s.ClearCreatedBy() + }) +} + +// SetPolicyID sets the "policy_id" field. +func (u *PolicyBindingUpsertOne) SetPolicyID(v uuid.UUID) *PolicyBindingUpsertOne { + return u.Update(func(s *PolicyBindingUpsert) { + s.SetPolicyID(v) + }) +} + +// UpdatePolicyID sets the "policy_id" field to the value that was provided on create. +func (u *PolicyBindingUpsertOne) UpdatePolicyID() *PolicyBindingUpsertOne { + return u.Update(func(s *PolicyBindingUpsert) { + s.UpdatePolicyID() + }) +} + +// ClearPolicyID clears the value of the "policy_id" field. +func (u *PolicyBindingUpsertOne) ClearPolicyID() *PolicyBindingUpsertOne { + return u.Update(func(s *PolicyBindingUpsert) { + s.ClearPolicyID() + }) +} + +// SetUserID sets the "user_id" field. +func (u *PolicyBindingUpsertOne) SetUserID(v uuid.UUID) *PolicyBindingUpsertOne { + return u.Update(func(s *PolicyBindingUpsert) { + s.SetUserID(v) + }) +} + +// UpdateUserID sets the "user_id" field to the value that was provided on create. +func (u *PolicyBindingUpsertOne) UpdateUserID() *PolicyBindingUpsertOne { + return u.Update(func(s *PolicyBindingUpsert) { + s.UpdateUserID() + }) +} + +// ClearUserID clears the value of the "user_id" field. +func (u *PolicyBindingUpsertOne) ClearUserID() *PolicyBindingUpsertOne { + return u.Update(func(s *PolicyBindingUpsert) { + s.ClearUserID() + }) +} + +// SetGroupID sets the "group_id" field. +func (u *PolicyBindingUpsertOne) SetGroupID(v uuid.UUID) *PolicyBindingUpsertOne { + return u.Update(func(s *PolicyBindingUpsert) { + s.SetGroupID(v) + }) +} + +// UpdateGroupID sets the "group_id" field to the value that was provided on create. +func (u *PolicyBindingUpsertOne) UpdateGroupID() *PolicyBindingUpsertOne { + return u.Update(func(s *PolicyBindingUpsert) { + s.UpdateGroupID() + }) +} + +// ClearGroupID clears the value of the "group_id" field. +func (u *PolicyBindingUpsertOne) ClearGroupID() *PolicyBindingUpsertOne { + return u.Update(func(s *PolicyBindingUpsert) { + s.ClearGroupID() + }) +} + +// SetAgentID sets the "agent_id" field. +func (u *PolicyBindingUpsertOne) SetAgentID(v uuid.UUID) *PolicyBindingUpsertOne { + return u.Update(func(s *PolicyBindingUpsert) { + s.SetAgentID(v) + }) +} + +// UpdateAgentID sets the "agent_id" field to the value that was provided on create. +func (u *PolicyBindingUpsertOne) UpdateAgentID() *PolicyBindingUpsertOne { + return u.Update(func(s *PolicyBindingUpsert) { + s.UpdateAgentID() + }) +} + +// ClearAgentID clears the value of the "agent_id" field. +func (u *PolicyBindingUpsertOne) ClearAgentID() *PolicyBindingUpsertOne { + return u.Update(func(s *PolicyBindingUpsert) { + s.ClearAgentID() + }) +} + +// Exec executes the query. +func (u *PolicyBindingUpsertOne) Exec(ctx context.Context) error { + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for PolicyBindingCreate.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *PolicyBindingUpsertOne) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} + +// Exec executes the UPSERT query and returns the inserted/updated ID. +func (u *PolicyBindingUpsertOne) ID(ctx context.Context) (id uuid.UUID, err error) { + if u.create.driver.Dialect() == dialect.MySQL { + // In case of "ON CONFLICT", there is no way to get back non-numeric ID + // fields from the database since MySQL does not support the RETURNING clause. + return id, errors.New("ent: PolicyBindingUpsertOne.ID is not supported by MySQL driver. Use PolicyBindingUpsertOne.Exec instead") + } + node, err := u.create.Save(ctx) + if err != nil { + return id, err + } + return node.ID, nil +} + +// IDX is like ID, but panics if an error occurs. +func (u *PolicyBindingUpsertOne) IDX(ctx context.Context) uuid.UUID { + id, err := u.ID(ctx) + if err != nil { + panic(err) + } + return id +} + // PolicyBindingCreateBulk is the builder for creating many PolicyBinding entities in bulk. type PolicyBindingCreateBulk struct { config err error builders []*PolicyBindingCreate + conflict []sql.ConflictOption } // Save creates the PolicyBinding entities in the database. @@ -359,6 +723,7 @@ func (_c *PolicyBindingCreateBulk) Save(ctx context.Context) ([]*PolicyBinding, _, err = mutators[i+1].Mutate(root, _c.builders[i+1].mutation) } else { spec := &sqlgraph.BatchCreateSpec{Nodes: specs} + spec.OnConflict = _c.conflict // Invoke the actual operation on the latest mutation in the chain. if err = sqlgraph.BatchCreate(ctx, _c.driver, spec); err != nil { if sqlgraph.IsConstraintError(err) { @@ -408,3 +773,239 @@ func (_c *PolicyBindingCreateBulk) ExecX(ctx context.Context) { panic(err) } } + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.PolicyBinding.CreateBulk(builders...). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.PolicyBindingUpsert) { +// SetPrincipalType(v+v). +// }). +// Exec(ctx) +func (_c *PolicyBindingCreateBulk) OnConflict(opts ...sql.ConflictOption) *PolicyBindingUpsertBulk { + _c.conflict = opts + return &PolicyBindingUpsertBulk{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.PolicyBinding.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *PolicyBindingCreateBulk) OnConflictColumns(columns ...string) *PolicyBindingUpsertBulk { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &PolicyBindingUpsertBulk{ + create: _c, + } +} + +// PolicyBindingUpsertBulk is the builder for "upsert"-ing +// a bulk of PolicyBinding nodes. +type PolicyBindingUpsertBulk struct { + create *PolicyBindingCreateBulk +} + +// UpdateNewValues updates the mutable fields using the new values that +// were set on create. Using this option is equivalent to using: +// +// client.PolicyBinding.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(policybinding.FieldID) +// }), +// ). +// Exec(ctx) +func (u *PolicyBindingUpsertBulk) UpdateNewValues() *PolicyBindingUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + for _, b := range u.create.builders { + if _, exists := b.mutation.ID(); exists { + s.SetIgnore(policybinding.FieldID) + } + if _, exists := b.mutation.Created(); exists { + s.SetIgnore(policybinding.FieldCreated) + } + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.PolicyBinding.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *PolicyBindingUpsertBulk) Ignore() *PolicyBindingUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *PolicyBindingUpsertBulk) DoNothing() *PolicyBindingUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the PolicyBindingCreateBulk.OnConflict +// documentation for more info. +func (u *PolicyBindingUpsertBulk) Update(set func(*PolicyBindingUpsert)) *PolicyBindingUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&PolicyBindingUpsert{UpdateSet: update}) + })) + return u +} + +// SetPrincipalType sets the "principal_type" field. +func (u *PolicyBindingUpsertBulk) SetPrincipalType(v policybinding.PrincipalType) *PolicyBindingUpsertBulk { + return u.Update(func(s *PolicyBindingUpsert) { + s.SetPrincipalType(v) + }) +} + +// UpdatePrincipalType sets the "principal_type" field to the value that was provided on create. +func (u *PolicyBindingUpsertBulk) UpdatePrincipalType() *PolicyBindingUpsertBulk { + return u.Update(func(s *PolicyBindingUpsert) { + s.UpdatePrincipalType() + }) +} + +// SetCreatedBy sets the "created_by" field. +func (u *PolicyBindingUpsertBulk) SetCreatedBy(v string) *PolicyBindingUpsertBulk { + return u.Update(func(s *PolicyBindingUpsert) { + s.SetCreatedBy(v) + }) +} + +// UpdateCreatedBy sets the "created_by" field to the value that was provided on create. +func (u *PolicyBindingUpsertBulk) UpdateCreatedBy() *PolicyBindingUpsertBulk { + return u.Update(func(s *PolicyBindingUpsert) { + s.UpdateCreatedBy() + }) +} + +// ClearCreatedBy clears the value of the "created_by" field. +func (u *PolicyBindingUpsertBulk) ClearCreatedBy() *PolicyBindingUpsertBulk { + return u.Update(func(s *PolicyBindingUpsert) { + s.ClearCreatedBy() + }) +} + +// SetPolicyID sets the "policy_id" field. +func (u *PolicyBindingUpsertBulk) SetPolicyID(v uuid.UUID) *PolicyBindingUpsertBulk { + return u.Update(func(s *PolicyBindingUpsert) { + s.SetPolicyID(v) + }) +} + +// UpdatePolicyID sets the "policy_id" field to the value that was provided on create. +func (u *PolicyBindingUpsertBulk) UpdatePolicyID() *PolicyBindingUpsertBulk { + return u.Update(func(s *PolicyBindingUpsert) { + s.UpdatePolicyID() + }) +} + +// ClearPolicyID clears the value of the "policy_id" field. +func (u *PolicyBindingUpsertBulk) ClearPolicyID() *PolicyBindingUpsertBulk { + return u.Update(func(s *PolicyBindingUpsert) { + s.ClearPolicyID() + }) +} + +// SetUserID sets the "user_id" field. +func (u *PolicyBindingUpsertBulk) SetUserID(v uuid.UUID) *PolicyBindingUpsertBulk { + return u.Update(func(s *PolicyBindingUpsert) { + s.SetUserID(v) + }) +} + +// UpdateUserID sets the "user_id" field to the value that was provided on create. +func (u *PolicyBindingUpsertBulk) UpdateUserID() *PolicyBindingUpsertBulk { + return u.Update(func(s *PolicyBindingUpsert) { + s.UpdateUserID() + }) +} + +// ClearUserID clears the value of the "user_id" field. +func (u *PolicyBindingUpsertBulk) ClearUserID() *PolicyBindingUpsertBulk { + return u.Update(func(s *PolicyBindingUpsert) { + s.ClearUserID() + }) +} + +// SetGroupID sets the "group_id" field. +func (u *PolicyBindingUpsertBulk) SetGroupID(v uuid.UUID) *PolicyBindingUpsertBulk { + return u.Update(func(s *PolicyBindingUpsert) { + s.SetGroupID(v) + }) +} + +// UpdateGroupID sets the "group_id" field to the value that was provided on create. +func (u *PolicyBindingUpsertBulk) UpdateGroupID() *PolicyBindingUpsertBulk { + return u.Update(func(s *PolicyBindingUpsert) { + s.UpdateGroupID() + }) +} + +// ClearGroupID clears the value of the "group_id" field. +func (u *PolicyBindingUpsertBulk) ClearGroupID() *PolicyBindingUpsertBulk { + return u.Update(func(s *PolicyBindingUpsert) { + s.ClearGroupID() + }) +} + +// SetAgentID sets the "agent_id" field. +func (u *PolicyBindingUpsertBulk) SetAgentID(v uuid.UUID) *PolicyBindingUpsertBulk { + return u.Update(func(s *PolicyBindingUpsert) { + s.SetAgentID(v) + }) +} + +// UpdateAgentID sets the "agent_id" field to the value that was provided on create. +func (u *PolicyBindingUpsertBulk) UpdateAgentID() *PolicyBindingUpsertBulk { + return u.Update(func(s *PolicyBindingUpsert) { + s.UpdateAgentID() + }) +} + +// ClearAgentID clears the value of the "agent_id" field. +func (u *PolicyBindingUpsertBulk) ClearAgentID() *PolicyBindingUpsertBulk { + return u.Update(func(s *PolicyBindingUpsert) { + s.ClearAgentID() + }) +} + +// Exec executes the query. +func (u *PolicyBindingUpsertBulk) Exec(ctx context.Context) error { + if u.create.err != nil { + return u.create.err + } + for i, b := range u.create.builders { + if len(b.conflict) != 0 { + return fmt.Errorf("ent: OnConflict was set for builder %d. Set it on the PolicyBindingCreateBulk instead", i) + } + } + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for PolicyBindingCreateBulk.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *PolicyBindingUpsertBulk) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/pkg/ent/policybinding_query.go b/pkg/ent/policybinding_query.go index 2580b7618..6a2498f2c 100644 --- a/pkg/ent/policybinding_query.go +++ b/pkg/ent/policybinding_query.go @@ -8,6 +8,7 @@ import ( "math" "entgo.io/ent" + "entgo.io/ent/dialect" "entgo.io/ent/dialect/sql" "entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/schema/field" @@ -31,6 +32,7 @@ type PolicyBindingQuery struct { withUser *UserQuery withGroup *GroupQuery withAgent *AgentQuery + modifiers []func(*sql.Selector) // intermediate query (i.e. traversal path). sql *sql.Selector path func(context.Context) (*sql.Selector, error) @@ -495,6 +497,9 @@ func (_q *PolicyBindingQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([ node.Edges.loadedTypes = loadedTypes return node.assignValues(columns, values) } + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } for i := range hooks { hooks[i](ctx, _spec) } @@ -662,6 +667,9 @@ func (_q *PolicyBindingQuery) loadAgent(ctx context.Context, query *AgentQuery, func (_q *PolicyBindingQuery) sqlCount(ctx context.Context) (int, error) { _spec := _q.querySpec() + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } _spec.Node.Columns = _q.ctx.Fields if len(_q.ctx.Fields) > 0 { _spec.Unique = _q.ctx.Unique != nil && *_q.ctx.Unique @@ -736,6 +744,9 @@ func (_q *PolicyBindingQuery) sqlQuery(ctx context.Context) *sql.Selector { if _q.ctx.Unique != nil && *_q.ctx.Unique { selector.Distinct() } + for _, m := range _q.modifiers { + m(selector) + } for _, p := range _q.predicates { p(selector) } @@ -753,6 +764,32 @@ func (_q *PolicyBindingQuery) sqlQuery(ctx context.Context) *sql.Selector { return selector } +// ForUpdate locks the selected rows against concurrent updates, and prevent them from being +// updated, deleted or "selected ... for update" by other sessions, until the transaction is +// either committed or rolled-back. +func (_q *PolicyBindingQuery) ForUpdate(opts ...sql.LockOption) *PolicyBindingQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForUpdate(opts...) + }) + return _q +} + +// ForShare behaves similarly to ForUpdate, except that it acquires a shared mode lock +// on any rows that are read. Other sessions can read the rows, but cannot modify them +// until your transaction commits. +func (_q *PolicyBindingQuery) ForShare(opts ...sql.LockOption) *PolicyBindingQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForShare(opts...) + }) + return _q +} + // PolicyBindingGroupBy is the group-by builder for PolicyBinding entities. type PolicyBindingGroupBy struct { selector diff --git a/pkg/ent/predicate/predicate.go b/pkg/ent/predicate/predicate.go index 58e745e1e..894f3d7e3 100644 --- a/pkg/ent/predicate/predicate.go +++ b/pkg/ent/predicate/predicate.go @@ -12,17 +12,86 @@ type AccessPolicy func(*sql.Selector) // Agent is the predicate function for agent builders. type Agent func(*sql.Selector) +// AllowListEntry is the predicate function for allowlistentry builders. +type AllowListEntry func(*sql.Selector) + +// ApiKey is the predicate function for apikey builders. +type ApiKey func(*sql.Selector) + +// BrokerJoinToken is the predicate function for brokerjointoken builders. +type BrokerJoinToken func(*sql.Selector) + +// BrokerSecret is the predicate function for brokersecret builders. +type BrokerSecret func(*sql.Selector) + +// EnvVar is the predicate function for envvar builders. +type EnvVar func(*sql.Selector) + +// GCPServiceAccount is the predicate function for gcpserviceaccount builders. +type GCPServiceAccount func(*sql.Selector) + +// GithubInstallation is the predicate function for githubinstallation builders. +type GithubInstallation func(*sql.Selector) + // Group is the predicate function for group builders. type Group func(*sql.Selector) // GroupMembership is the predicate function for groupmembership builders. type GroupMembership func(*sql.Selector) +// HarnessConfig is the predicate function for harnessconfig builders. +type HarnessConfig func(*sql.Selector) + +// InviteCode is the predicate function for invitecode builders. +type InviteCode func(*sql.Selector) + +// MaintenanceOperation is the predicate function for maintenanceoperation builders. +type MaintenanceOperation func(*sql.Selector) + +// MaintenanceOperationRun is the predicate function for maintenanceoperationrun builders. +type MaintenanceOperationRun func(*sql.Selector) + +// Message is the predicate function for message builders. +type Message func(*sql.Selector) + +// Notification is the predicate function for notification builders. +type Notification func(*sql.Selector) + +// NotificationSubscription is the predicate function for notificationsubscription builders. +type NotificationSubscription func(*sql.Selector) + // PolicyBinding is the predicate function for policybinding builders. type PolicyBinding func(*sql.Selector) // Project is the predicate function for project builders. type Project func(*sql.Selector) +// ProjectContributor is the predicate function for projectcontributor builders. +type ProjectContributor func(*sql.Selector) + +// ProjectSyncState is the predicate function for projectsyncstate builders. +type ProjectSyncState func(*sql.Selector) + +// RuntimeBroker is the predicate function for runtimebroker builders. +type RuntimeBroker func(*sql.Selector) + +// Schedule is the predicate function for schedule builders. +type Schedule func(*sql.Selector) + +// ScheduledEvent is the predicate function for scheduledevent builders. +type ScheduledEvent func(*sql.Selector) + +// Secret is the predicate function for secret builders. +type Secret func(*sql.Selector) + +// SubscriptionTemplate is the predicate function for subscriptiontemplate builders. +type SubscriptionTemplate func(*sql.Selector) + +// Template is the predicate function for template builders. +type Template func(*sql.Selector) + // User is the predicate function for user builders. type User func(*sql.Selector) + +// UserAccessToken is the predicate function for useraccesstoken builders. +type UserAccessToken func(*sql.Selector) diff --git a/pkg/ent/project.go b/pkg/ent/project.go index 32b7418f6..9110915bc 100644 --- a/pkg/ent/project.go +++ b/pkg/ent/project.go @@ -25,10 +25,14 @@ type Project struct { Slug string `json:"slug,omitempty"` // GitRemote holds the value of the "git_remote" field. GitRemote *string `json:"git_remote,omitempty"` + // DefaultRuntimeBrokerID holds the value of the "default_runtime_broker_id" field. + DefaultRuntimeBrokerID *string `json:"default_runtime_broker_id,omitempty"` // Labels holds the value of the "labels" field. Labels map[string]string `json:"labels,omitempty"` // Annotations holds the value of the "annotations" field. Annotations map[string]string `json:"annotations,omitempty"` + // SharedDirs holds the value of the "shared_dirs" field. + SharedDirs string `json:"shared_dirs,omitempty"` // Created holds the value of the "created" field. Created time.Time `json:"created,omitempty"` // Updated holds the value of the "updated" field. @@ -39,6 +43,14 @@ type Project struct { OwnerID string `json:"owner_id,omitempty"` // Visibility holds the value of the "visibility" field. Visibility string `json:"visibility,omitempty"` + // GithubInstallationID holds the value of the "github_installation_id" field. + GithubInstallationID *int64 `json:"github_installation_id,omitempty"` + // GithubPermissions holds the value of the "github_permissions" field. + GithubPermissions string `json:"github_permissions,omitempty"` + // GithubAppStatus holds the value of the "github_app_status" field. + GithubAppStatus string `json:"github_app_status,omitempty"` + // GitIdentity holds the value of the "git_identity" field. + GitIdentity string `json:"git_identity,omitempty"` // Edges holds the relations/edges for other nodes in the graph. // The values are being populated by the ProjectQuery when eager-loading is set. Edges ProjectEdges `json:"edges"` @@ -70,7 +82,9 @@ func (*Project) scanValues(columns []string) ([]any, error) { switch columns[i] { case project.FieldLabels, project.FieldAnnotations: values[i] = new([]byte) - case project.FieldName, project.FieldSlug, project.FieldGitRemote, project.FieldCreatedBy, project.FieldOwnerID, project.FieldVisibility: + case project.FieldGithubInstallationID: + values[i] = new(sql.NullInt64) + case project.FieldName, project.FieldSlug, project.FieldGitRemote, project.FieldDefaultRuntimeBrokerID, project.FieldSharedDirs, project.FieldCreatedBy, project.FieldOwnerID, project.FieldVisibility, project.FieldGithubPermissions, project.FieldGithubAppStatus, project.FieldGitIdentity: values[i] = new(sql.NullString) case project.FieldCreated, project.FieldUpdated: values[i] = new(sql.NullTime) @@ -116,6 +130,13 @@ func (_m *Project) assignValues(columns []string, values []any) error { _m.GitRemote = new(string) *_m.GitRemote = value.String } + case project.FieldDefaultRuntimeBrokerID: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field default_runtime_broker_id", values[i]) + } else if value.Valid { + _m.DefaultRuntimeBrokerID = new(string) + *_m.DefaultRuntimeBrokerID = value.String + } case project.FieldLabels: if value, ok := values[i].(*[]byte); !ok { return fmt.Errorf("unexpected type %T for field labels", values[i]) @@ -132,6 +153,12 @@ func (_m *Project) assignValues(columns []string, values []any) error { return fmt.Errorf("unmarshal field annotations: %w", err) } } + case project.FieldSharedDirs: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field shared_dirs", values[i]) + } else if value.Valid { + _m.SharedDirs = value.String + } case project.FieldCreated: if value, ok := values[i].(*sql.NullTime); !ok { return fmt.Errorf("unexpected type %T for field created", values[i]) @@ -162,6 +189,31 @@ func (_m *Project) assignValues(columns []string, values []any) error { } else if value.Valid { _m.Visibility = value.String } + case project.FieldGithubInstallationID: + if value, ok := values[i].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for field github_installation_id", values[i]) + } else if value.Valid { + _m.GithubInstallationID = new(int64) + *_m.GithubInstallationID = value.Int64 + } + case project.FieldGithubPermissions: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field github_permissions", values[i]) + } else if value.Valid { + _m.GithubPermissions = value.String + } + case project.FieldGithubAppStatus: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field github_app_status", values[i]) + } else if value.Valid { + _m.GithubAppStatus = value.String + } + case project.FieldGitIdentity: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field git_identity", values[i]) + } else if value.Valid { + _m.GitIdentity = value.String + } default: _m.selectValues.Set(columns[i], values[i]) } @@ -214,12 +266,20 @@ func (_m *Project) String() string { builder.WriteString(*v) } builder.WriteString(", ") + if v := _m.DefaultRuntimeBrokerID; v != nil { + builder.WriteString("default_runtime_broker_id=") + builder.WriteString(*v) + } + builder.WriteString(", ") builder.WriteString("labels=") builder.WriteString(fmt.Sprintf("%v", _m.Labels)) builder.WriteString(", ") builder.WriteString("annotations=") builder.WriteString(fmt.Sprintf("%v", _m.Annotations)) builder.WriteString(", ") + builder.WriteString("shared_dirs=") + builder.WriteString(_m.SharedDirs) + builder.WriteString(", ") builder.WriteString("created=") builder.WriteString(_m.Created.Format(time.ANSIC)) builder.WriteString(", ") @@ -234,6 +294,20 @@ func (_m *Project) String() string { builder.WriteString(", ") builder.WriteString("visibility=") builder.WriteString(_m.Visibility) + builder.WriteString(", ") + if v := _m.GithubInstallationID; v != nil { + builder.WriteString("github_installation_id=") + builder.WriteString(fmt.Sprintf("%v", *v)) + } + builder.WriteString(", ") + builder.WriteString("github_permissions=") + builder.WriteString(_m.GithubPermissions) + builder.WriteString(", ") + builder.WriteString("github_app_status=") + builder.WriteString(_m.GithubAppStatus) + builder.WriteString(", ") + builder.WriteString("git_identity=") + builder.WriteString(_m.GitIdentity) builder.WriteByte(')') return builder.String() } diff --git a/pkg/ent/project/project.go b/pkg/ent/project/project.go index 8911eb1aa..a8988dcc5 100644 --- a/pkg/ent/project/project.go +++ b/pkg/ent/project/project.go @@ -21,10 +21,14 @@ const ( FieldSlug = "slug" // FieldGitRemote holds the string denoting the git_remote field in the database. FieldGitRemote = "git_remote" + // FieldDefaultRuntimeBrokerID holds the string denoting the default_runtime_broker_id field in the database. + FieldDefaultRuntimeBrokerID = "default_runtime_broker_id" // FieldLabels holds the string denoting the labels field in the database. FieldLabels = "labels" // FieldAnnotations holds the string denoting the annotations field in the database. FieldAnnotations = "annotations" + // FieldSharedDirs holds the string denoting the shared_dirs field in the database. + FieldSharedDirs = "shared_dirs" // FieldCreated holds the string denoting the created field in the database. FieldCreated = "created" // FieldUpdated holds the string denoting the updated field in the database. @@ -35,6 +39,14 @@ const ( FieldOwnerID = "owner_id" // FieldVisibility holds the string denoting the visibility field in the database. FieldVisibility = "visibility" + // FieldGithubInstallationID holds the string denoting the github_installation_id field in the database. + FieldGithubInstallationID = "github_installation_id" + // FieldGithubPermissions holds the string denoting the github_permissions field in the database. + FieldGithubPermissions = "github_permissions" + // FieldGithubAppStatus holds the string denoting the github_app_status field in the database. + FieldGithubAppStatus = "github_app_status" + // FieldGitIdentity holds the string denoting the git_identity field in the database. + FieldGitIdentity = "git_identity" // EdgeAgents holds the string denoting the agents edge name in mutations. EdgeAgents = "agents" // Table holds the table name of the project in the database. @@ -54,13 +66,19 @@ var Columns = []string{ FieldName, FieldSlug, FieldGitRemote, + FieldDefaultRuntimeBrokerID, FieldLabels, FieldAnnotations, + FieldSharedDirs, FieldCreated, FieldUpdated, FieldCreatedBy, FieldOwnerID, FieldVisibility, + FieldGithubInstallationID, + FieldGithubPermissions, + FieldGithubAppStatus, + FieldGitIdentity, } // ValidColumn reports if the column name is valid (part of the table columns). @@ -113,6 +131,16 @@ func ByGitRemote(opts ...sql.OrderTermOption) OrderOption { return sql.OrderByField(FieldGitRemote, opts...).ToFunc() } +// ByDefaultRuntimeBrokerID orders the results by the default_runtime_broker_id field. +func ByDefaultRuntimeBrokerID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldDefaultRuntimeBrokerID, opts...).ToFunc() +} + +// BySharedDirs orders the results by the shared_dirs field. +func BySharedDirs(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldSharedDirs, opts...).ToFunc() +} + // ByCreated orders the results by the created field. func ByCreated(opts ...sql.OrderTermOption) OrderOption { return sql.OrderByField(FieldCreated, opts...).ToFunc() @@ -138,6 +166,26 @@ func ByVisibility(opts ...sql.OrderTermOption) OrderOption { return sql.OrderByField(FieldVisibility, opts...).ToFunc() } +// ByGithubInstallationID orders the results by the github_installation_id field. +func ByGithubInstallationID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldGithubInstallationID, opts...).ToFunc() +} + +// ByGithubPermissions orders the results by the github_permissions field. +func ByGithubPermissions(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldGithubPermissions, opts...).ToFunc() +} + +// ByGithubAppStatus orders the results by the github_app_status field. +func ByGithubAppStatus(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldGithubAppStatus, opts...).ToFunc() +} + +// ByGitIdentity orders the results by the git_identity field. +func ByGitIdentity(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldGitIdentity, opts...).ToFunc() +} + // ByAgentsCount orders the results by agents count. func ByAgentsCount(opts ...sql.OrderTermOption) OrderOption { return func(s *sql.Selector) { diff --git a/pkg/ent/project/where.go b/pkg/ent/project/where.go index 4e0643014..d7e0f6b94 100644 --- a/pkg/ent/project/where.go +++ b/pkg/ent/project/where.go @@ -71,6 +71,16 @@ func GitRemote(v string) predicate.Project { return predicate.Project(sql.FieldEQ(FieldGitRemote, v)) } +// DefaultRuntimeBrokerID applies equality check predicate on the "default_runtime_broker_id" field. It's identical to DefaultRuntimeBrokerIDEQ. +func DefaultRuntimeBrokerID(v string) predicate.Project { + return predicate.Project(sql.FieldEQ(FieldDefaultRuntimeBrokerID, v)) +} + +// SharedDirs applies equality check predicate on the "shared_dirs" field. It's identical to SharedDirsEQ. +func SharedDirs(v string) predicate.Project { + return predicate.Project(sql.FieldEQ(FieldSharedDirs, v)) +} + // Created applies equality check predicate on the "created" field. It's identical to CreatedEQ. func Created(v time.Time) predicate.Project { return predicate.Project(sql.FieldEQ(FieldCreated, v)) @@ -96,6 +106,26 @@ func Visibility(v string) predicate.Project { return predicate.Project(sql.FieldEQ(FieldVisibility, v)) } +// GithubInstallationID applies equality check predicate on the "github_installation_id" field. It's identical to GithubInstallationIDEQ. +func GithubInstallationID(v int64) predicate.Project { + return predicate.Project(sql.FieldEQ(FieldGithubInstallationID, v)) +} + +// GithubPermissions applies equality check predicate on the "github_permissions" field. It's identical to GithubPermissionsEQ. +func GithubPermissions(v string) predicate.Project { + return predicate.Project(sql.FieldEQ(FieldGithubPermissions, v)) +} + +// GithubAppStatus applies equality check predicate on the "github_app_status" field. It's identical to GithubAppStatusEQ. +func GithubAppStatus(v string) predicate.Project { + return predicate.Project(sql.FieldEQ(FieldGithubAppStatus, v)) +} + +// GitIdentity applies equality check predicate on the "git_identity" field. It's identical to GitIdentityEQ. +func GitIdentity(v string) predicate.Project { + return predicate.Project(sql.FieldEQ(FieldGitIdentity, v)) +} + // NameEQ applies the EQ predicate on the "name" field. func NameEQ(v string) predicate.Project { return predicate.Project(sql.FieldEQ(FieldName, v)) @@ -301,6 +331,81 @@ func GitRemoteContainsFold(v string) predicate.Project { return predicate.Project(sql.FieldContainsFold(FieldGitRemote, v)) } +// DefaultRuntimeBrokerIDEQ applies the EQ predicate on the "default_runtime_broker_id" field. +func DefaultRuntimeBrokerIDEQ(v string) predicate.Project { + return predicate.Project(sql.FieldEQ(FieldDefaultRuntimeBrokerID, v)) +} + +// DefaultRuntimeBrokerIDNEQ applies the NEQ predicate on the "default_runtime_broker_id" field. +func DefaultRuntimeBrokerIDNEQ(v string) predicate.Project { + return predicate.Project(sql.FieldNEQ(FieldDefaultRuntimeBrokerID, v)) +} + +// DefaultRuntimeBrokerIDIn applies the In predicate on the "default_runtime_broker_id" field. +func DefaultRuntimeBrokerIDIn(vs ...string) predicate.Project { + return predicate.Project(sql.FieldIn(FieldDefaultRuntimeBrokerID, vs...)) +} + +// DefaultRuntimeBrokerIDNotIn applies the NotIn predicate on the "default_runtime_broker_id" field. +func DefaultRuntimeBrokerIDNotIn(vs ...string) predicate.Project { + return predicate.Project(sql.FieldNotIn(FieldDefaultRuntimeBrokerID, vs...)) +} + +// DefaultRuntimeBrokerIDGT applies the GT predicate on the "default_runtime_broker_id" field. +func DefaultRuntimeBrokerIDGT(v string) predicate.Project { + return predicate.Project(sql.FieldGT(FieldDefaultRuntimeBrokerID, v)) +} + +// DefaultRuntimeBrokerIDGTE applies the GTE predicate on the "default_runtime_broker_id" field. +func DefaultRuntimeBrokerIDGTE(v string) predicate.Project { + return predicate.Project(sql.FieldGTE(FieldDefaultRuntimeBrokerID, v)) +} + +// DefaultRuntimeBrokerIDLT applies the LT predicate on the "default_runtime_broker_id" field. +func DefaultRuntimeBrokerIDLT(v string) predicate.Project { + return predicate.Project(sql.FieldLT(FieldDefaultRuntimeBrokerID, v)) +} + +// DefaultRuntimeBrokerIDLTE applies the LTE predicate on the "default_runtime_broker_id" field. +func DefaultRuntimeBrokerIDLTE(v string) predicate.Project { + return predicate.Project(sql.FieldLTE(FieldDefaultRuntimeBrokerID, v)) +} + +// DefaultRuntimeBrokerIDContains applies the Contains predicate on the "default_runtime_broker_id" field. +func DefaultRuntimeBrokerIDContains(v string) predicate.Project { + return predicate.Project(sql.FieldContains(FieldDefaultRuntimeBrokerID, v)) +} + +// DefaultRuntimeBrokerIDHasPrefix applies the HasPrefix predicate on the "default_runtime_broker_id" field. +func DefaultRuntimeBrokerIDHasPrefix(v string) predicate.Project { + return predicate.Project(sql.FieldHasPrefix(FieldDefaultRuntimeBrokerID, v)) +} + +// DefaultRuntimeBrokerIDHasSuffix applies the HasSuffix predicate on the "default_runtime_broker_id" field. +func DefaultRuntimeBrokerIDHasSuffix(v string) predicate.Project { + return predicate.Project(sql.FieldHasSuffix(FieldDefaultRuntimeBrokerID, v)) +} + +// DefaultRuntimeBrokerIDIsNil applies the IsNil predicate on the "default_runtime_broker_id" field. +func DefaultRuntimeBrokerIDIsNil() predicate.Project { + return predicate.Project(sql.FieldIsNull(FieldDefaultRuntimeBrokerID)) +} + +// DefaultRuntimeBrokerIDNotNil applies the NotNil predicate on the "default_runtime_broker_id" field. +func DefaultRuntimeBrokerIDNotNil() predicate.Project { + return predicate.Project(sql.FieldNotNull(FieldDefaultRuntimeBrokerID)) +} + +// DefaultRuntimeBrokerIDEqualFold applies the EqualFold predicate on the "default_runtime_broker_id" field. +func DefaultRuntimeBrokerIDEqualFold(v string) predicate.Project { + return predicate.Project(sql.FieldEqualFold(FieldDefaultRuntimeBrokerID, v)) +} + +// DefaultRuntimeBrokerIDContainsFold applies the ContainsFold predicate on the "default_runtime_broker_id" field. +func DefaultRuntimeBrokerIDContainsFold(v string) predicate.Project { + return predicate.Project(sql.FieldContainsFold(FieldDefaultRuntimeBrokerID, v)) +} + // LabelsIsNil applies the IsNil predicate on the "labels" field. func LabelsIsNil() predicate.Project { return predicate.Project(sql.FieldIsNull(FieldLabels)) @@ -321,6 +426,81 @@ func AnnotationsNotNil() predicate.Project { return predicate.Project(sql.FieldNotNull(FieldAnnotations)) } +// SharedDirsEQ applies the EQ predicate on the "shared_dirs" field. +func SharedDirsEQ(v string) predicate.Project { + return predicate.Project(sql.FieldEQ(FieldSharedDirs, v)) +} + +// SharedDirsNEQ applies the NEQ predicate on the "shared_dirs" field. +func SharedDirsNEQ(v string) predicate.Project { + return predicate.Project(sql.FieldNEQ(FieldSharedDirs, v)) +} + +// SharedDirsIn applies the In predicate on the "shared_dirs" field. +func SharedDirsIn(vs ...string) predicate.Project { + return predicate.Project(sql.FieldIn(FieldSharedDirs, vs...)) +} + +// SharedDirsNotIn applies the NotIn predicate on the "shared_dirs" field. +func SharedDirsNotIn(vs ...string) predicate.Project { + return predicate.Project(sql.FieldNotIn(FieldSharedDirs, vs...)) +} + +// SharedDirsGT applies the GT predicate on the "shared_dirs" field. +func SharedDirsGT(v string) predicate.Project { + return predicate.Project(sql.FieldGT(FieldSharedDirs, v)) +} + +// SharedDirsGTE applies the GTE predicate on the "shared_dirs" field. +func SharedDirsGTE(v string) predicate.Project { + return predicate.Project(sql.FieldGTE(FieldSharedDirs, v)) +} + +// SharedDirsLT applies the LT predicate on the "shared_dirs" field. +func SharedDirsLT(v string) predicate.Project { + return predicate.Project(sql.FieldLT(FieldSharedDirs, v)) +} + +// SharedDirsLTE applies the LTE predicate on the "shared_dirs" field. +func SharedDirsLTE(v string) predicate.Project { + return predicate.Project(sql.FieldLTE(FieldSharedDirs, v)) +} + +// SharedDirsContains applies the Contains predicate on the "shared_dirs" field. +func SharedDirsContains(v string) predicate.Project { + return predicate.Project(sql.FieldContains(FieldSharedDirs, v)) +} + +// SharedDirsHasPrefix applies the HasPrefix predicate on the "shared_dirs" field. +func SharedDirsHasPrefix(v string) predicate.Project { + return predicate.Project(sql.FieldHasPrefix(FieldSharedDirs, v)) +} + +// SharedDirsHasSuffix applies the HasSuffix predicate on the "shared_dirs" field. +func SharedDirsHasSuffix(v string) predicate.Project { + return predicate.Project(sql.FieldHasSuffix(FieldSharedDirs, v)) +} + +// SharedDirsIsNil applies the IsNil predicate on the "shared_dirs" field. +func SharedDirsIsNil() predicate.Project { + return predicate.Project(sql.FieldIsNull(FieldSharedDirs)) +} + +// SharedDirsNotNil applies the NotNil predicate on the "shared_dirs" field. +func SharedDirsNotNil() predicate.Project { + return predicate.Project(sql.FieldNotNull(FieldSharedDirs)) +} + +// SharedDirsEqualFold applies the EqualFold predicate on the "shared_dirs" field. +func SharedDirsEqualFold(v string) predicate.Project { + return predicate.Project(sql.FieldEqualFold(FieldSharedDirs, v)) +} + +// SharedDirsContainsFold applies the ContainsFold predicate on the "shared_dirs" field. +func SharedDirsContainsFold(v string) predicate.Project { + return predicate.Project(sql.FieldContainsFold(FieldSharedDirs, v)) +} + // CreatedEQ applies the EQ predicate on the "created" field. func CreatedEQ(v time.Time) predicate.Project { return predicate.Project(sql.FieldEQ(FieldCreated, v)) @@ -616,6 +796,281 @@ func VisibilityContainsFold(v string) predicate.Project { return predicate.Project(sql.FieldContainsFold(FieldVisibility, v)) } +// GithubInstallationIDEQ applies the EQ predicate on the "github_installation_id" field. +func GithubInstallationIDEQ(v int64) predicate.Project { + return predicate.Project(sql.FieldEQ(FieldGithubInstallationID, v)) +} + +// GithubInstallationIDNEQ applies the NEQ predicate on the "github_installation_id" field. +func GithubInstallationIDNEQ(v int64) predicate.Project { + return predicate.Project(sql.FieldNEQ(FieldGithubInstallationID, v)) +} + +// GithubInstallationIDIn applies the In predicate on the "github_installation_id" field. +func GithubInstallationIDIn(vs ...int64) predicate.Project { + return predicate.Project(sql.FieldIn(FieldGithubInstallationID, vs...)) +} + +// GithubInstallationIDNotIn applies the NotIn predicate on the "github_installation_id" field. +func GithubInstallationIDNotIn(vs ...int64) predicate.Project { + return predicate.Project(sql.FieldNotIn(FieldGithubInstallationID, vs...)) +} + +// GithubInstallationIDGT applies the GT predicate on the "github_installation_id" field. +func GithubInstallationIDGT(v int64) predicate.Project { + return predicate.Project(sql.FieldGT(FieldGithubInstallationID, v)) +} + +// GithubInstallationIDGTE applies the GTE predicate on the "github_installation_id" field. +func GithubInstallationIDGTE(v int64) predicate.Project { + return predicate.Project(sql.FieldGTE(FieldGithubInstallationID, v)) +} + +// GithubInstallationIDLT applies the LT predicate on the "github_installation_id" field. +func GithubInstallationIDLT(v int64) predicate.Project { + return predicate.Project(sql.FieldLT(FieldGithubInstallationID, v)) +} + +// GithubInstallationIDLTE applies the LTE predicate on the "github_installation_id" field. +func GithubInstallationIDLTE(v int64) predicate.Project { + return predicate.Project(sql.FieldLTE(FieldGithubInstallationID, v)) +} + +// GithubInstallationIDIsNil applies the IsNil predicate on the "github_installation_id" field. +func GithubInstallationIDIsNil() predicate.Project { + return predicate.Project(sql.FieldIsNull(FieldGithubInstallationID)) +} + +// GithubInstallationIDNotNil applies the NotNil predicate on the "github_installation_id" field. +func GithubInstallationIDNotNil() predicate.Project { + return predicate.Project(sql.FieldNotNull(FieldGithubInstallationID)) +} + +// GithubPermissionsEQ applies the EQ predicate on the "github_permissions" field. +func GithubPermissionsEQ(v string) predicate.Project { + return predicate.Project(sql.FieldEQ(FieldGithubPermissions, v)) +} + +// GithubPermissionsNEQ applies the NEQ predicate on the "github_permissions" field. +func GithubPermissionsNEQ(v string) predicate.Project { + return predicate.Project(sql.FieldNEQ(FieldGithubPermissions, v)) +} + +// GithubPermissionsIn applies the In predicate on the "github_permissions" field. +func GithubPermissionsIn(vs ...string) predicate.Project { + return predicate.Project(sql.FieldIn(FieldGithubPermissions, vs...)) +} + +// GithubPermissionsNotIn applies the NotIn predicate on the "github_permissions" field. +func GithubPermissionsNotIn(vs ...string) predicate.Project { + return predicate.Project(sql.FieldNotIn(FieldGithubPermissions, vs...)) +} + +// GithubPermissionsGT applies the GT predicate on the "github_permissions" field. +func GithubPermissionsGT(v string) predicate.Project { + return predicate.Project(sql.FieldGT(FieldGithubPermissions, v)) +} + +// GithubPermissionsGTE applies the GTE predicate on the "github_permissions" field. +func GithubPermissionsGTE(v string) predicate.Project { + return predicate.Project(sql.FieldGTE(FieldGithubPermissions, v)) +} + +// GithubPermissionsLT applies the LT predicate on the "github_permissions" field. +func GithubPermissionsLT(v string) predicate.Project { + return predicate.Project(sql.FieldLT(FieldGithubPermissions, v)) +} + +// GithubPermissionsLTE applies the LTE predicate on the "github_permissions" field. +func GithubPermissionsLTE(v string) predicate.Project { + return predicate.Project(sql.FieldLTE(FieldGithubPermissions, v)) +} + +// GithubPermissionsContains applies the Contains predicate on the "github_permissions" field. +func GithubPermissionsContains(v string) predicate.Project { + return predicate.Project(sql.FieldContains(FieldGithubPermissions, v)) +} + +// GithubPermissionsHasPrefix applies the HasPrefix predicate on the "github_permissions" field. +func GithubPermissionsHasPrefix(v string) predicate.Project { + return predicate.Project(sql.FieldHasPrefix(FieldGithubPermissions, v)) +} + +// GithubPermissionsHasSuffix applies the HasSuffix predicate on the "github_permissions" field. +func GithubPermissionsHasSuffix(v string) predicate.Project { + return predicate.Project(sql.FieldHasSuffix(FieldGithubPermissions, v)) +} + +// GithubPermissionsIsNil applies the IsNil predicate on the "github_permissions" field. +func GithubPermissionsIsNil() predicate.Project { + return predicate.Project(sql.FieldIsNull(FieldGithubPermissions)) +} + +// GithubPermissionsNotNil applies the NotNil predicate on the "github_permissions" field. +func GithubPermissionsNotNil() predicate.Project { + return predicate.Project(sql.FieldNotNull(FieldGithubPermissions)) +} + +// GithubPermissionsEqualFold applies the EqualFold predicate on the "github_permissions" field. +func GithubPermissionsEqualFold(v string) predicate.Project { + return predicate.Project(sql.FieldEqualFold(FieldGithubPermissions, v)) +} + +// GithubPermissionsContainsFold applies the ContainsFold predicate on the "github_permissions" field. +func GithubPermissionsContainsFold(v string) predicate.Project { + return predicate.Project(sql.FieldContainsFold(FieldGithubPermissions, v)) +} + +// GithubAppStatusEQ applies the EQ predicate on the "github_app_status" field. +func GithubAppStatusEQ(v string) predicate.Project { + return predicate.Project(sql.FieldEQ(FieldGithubAppStatus, v)) +} + +// GithubAppStatusNEQ applies the NEQ predicate on the "github_app_status" field. +func GithubAppStatusNEQ(v string) predicate.Project { + return predicate.Project(sql.FieldNEQ(FieldGithubAppStatus, v)) +} + +// GithubAppStatusIn applies the In predicate on the "github_app_status" field. +func GithubAppStatusIn(vs ...string) predicate.Project { + return predicate.Project(sql.FieldIn(FieldGithubAppStatus, vs...)) +} + +// GithubAppStatusNotIn applies the NotIn predicate on the "github_app_status" field. +func GithubAppStatusNotIn(vs ...string) predicate.Project { + return predicate.Project(sql.FieldNotIn(FieldGithubAppStatus, vs...)) +} + +// GithubAppStatusGT applies the GT predicate on the "github_app_status" field. +func GithubAppStatusGT(v string) predicate.Project { + return predicate.Project(sql.FieldGT(FieldGithubAppStatus, v)) +} + +// GithubAppStatusGTE applies the GTE predicate on the "github_app_status" field. +func GithubAppStatusGTE(v string) predicate.Project { + return predicate.Project(sql.FieldGTE(FieldGithubAppStatus, v)) +} + +// GithubAppStatusLT applies the LT predicate on the "github_app_status" field. +func GithubAppStatusLT(v string) predicate.Project { + return predicate.Project(sql.FieldLT(FieldGithubAppStatus, v)) +} + +// GithubAppStatusLTE applies the LTE predicate on the "github_app_status" field. +func GithubAppStatusLTE(v string) predicate.Project { + return predicate.Project(sql.FieldLTE(FieldGithubAppStatus, v)) +} + +// GithubAppStatusContains applies the Contains predicate on the "github_app_status" field. +func GithubAppStatusContains(v string) predicate.Project { + return predicate.Project(sql.FieldContains(FieldGithubAppStatus, v)) +} + +// GithubAppStatusHasPrefix applies the HasPrefix predicate on the "github_app_status" field. +func GithubAppStatusHasPrefix(v string) predicate.Project { + return predicate.Project(sql.FieldHasPrefix(FieldGithubAppStatus, v)) +} + +// GithubAppStatusHasSuffix applies the HasSuffix predicate on the "github_app_status" field. +func GithubAppStatusHasSuffix(v string) predicate.Project { + return predicate.Project(sql.FieldHasSuffix(FieldGithubAppStatus, v)) +} + +// GithubAppStatusIsNil applies the IsNil predicate on the "github_app_status" field. +func GithubAppStatusIsNil() predicate.Project { + return predicate.Project(sql.FieldIsNull(FieldGithubAppStatus)) +} + +// GithubAppStatusNotNil applies the NotNil predicate on the "github_app_status" field. +func GithubAppStatusNotNil() predicate.Project { + return predicate.Project(sql.FieldNotNull(FieldGithubAppStatus)) +} + +// GithubAppStatusEqualFold applies the EqualFold predicate on the "github_app_status" field. +func GithubAppStatusEqualFold(v string) predicate.Project { + return predicate.Project(sql.FieldEqualFold(FieldGithubAppStatus, v)) +} + +// GithubAppStatusContainsFold applies the ContainsFold predicate on the "github_app_status" field. +func GithubAppStatusContainsFold(v string) predicate.Project { + return predicate.Project(sql.FieldContainsFold(FieldGithubAppStatus, v)) +} + +// GitIdentityEQ applies the EQ predicate on the "git_identity" field. +func GitIdentityEQ(v string) predicate.Project { + return predicate.Project(sql.FieldEQ(FieldGitIdentity, v)) +} + +// GitIdentityNEQ applies the NEQ predicate on the "git_identity" field. +func GitIdentityNEQ(v string) predicate.Project { + return predicate.Project(sql.FieldNEQ(FieldGitIdentity, v)) +} + +// GitIdentityIn applies the In predicate on the "git_identity" field. +func GitIdentityIn(vs ...string) predicate.Project { + return predicate.Project(sql.FieldIn(FieldGitIdentity, vs...)) +} + +// GitIdentityNotIn applies the NotIn predicate on the "git_identity" field. +func GitIdentityNotIn(vs ...string) predicate.Project { + return predicate.Project(sql.FieldNotIn(FieldGitIdentity, vs...)) +} + +// GitIdentityGT applies the GT predicate on the "git_identity" field. +func GitIdentityGT(v string) predicate.Project { + return predicate.Project(sql.FieldGT(FieldGitIdentity, v)) +} + +// GitIdentityGTE applies the GTE predicate on the "git_identity" field. +func GitIdentityGTE(v string) predicate.Project { + return predicate.Project(sql.FieldGTE(FieldGitIdentity, v)) +} + +// GitIdentityLT applies the LT predicate on the "git_identity" field. +func GitIdentityLT(v string) predicate.Project { + return predicate.Project(sql.FieldLT(FieldGitIdentity, v)) +} + +// GitIdentityLTE applies the LTE predicate on the "git_identity" field. +func GitIdentityLTE(v string) predicate.Project { + return predicate.Project(sql.FieldLTE(FieldGitIdentity, v)) +} + +// GitIdentityContains applies the Contains predicate on the "git_identity" field. +func GitIdentityContains(v string) predicate.Project { + return predicate.Project(sql.FieldContains(FieldGitIdentity, v)) +} + +// GitIdentityHasPrefix applies the HasPrefix predicate on the "git_identity" field. +func GitIdentityHasPrefix(v string) predicate.Project { + return predicate.Project(sql.FieldHasPrefix(FieldGitIdentity, v)) +} + +// GitIdentityHasSuffix applies the HasSuffix predicate on the "git_identity" field. +func GitIdentityHasSuffix(v string) predicate.Project { + return predicate.Project(sql.FieldHasSuffix(FieldGitIdentity, v)) +} + +// GitIdentityIsNil applies the IsNil predicate on the "git_identity" field. +func GitIdentityIsNil() predicate.Project { + return predicate.Project(sql.FieldIsNull(FieldGitIdentity)) +} + +// GitIdentityNotNil applies the NotNil predicate on the "git_identity" field. +func GitIdentityNotNil() predicate.Project { + return predicate.Project(sql.FieldNotNull(FieldGitIdentity)) +} + +// GitIdentityEqualFold applies the EqualFold predicate on the "git_identity" field. +func GitIdentityEqualFold(v string) predicate.Project { + return predicate.Project(sql.FieldEqualFold(FieldGitIdentity, v)) +} + +// GitIdentityContainsFold applies the ContainsFold predicate on the "git_identity" field. +func GitIdentityContainsFold(v string) predicate.Project { + return predicate.Project(sql.FieldContainsFold(FieldGitIdentity, v)) +} + // HasAgents applies the HasEdge predicate on the "agents" edge. func HasAgents() predicate.Project { return predicate.Project(func(s *sql.Selector) { diff --git a/pkg/ent/project_create.go b/pkg/ent/project_create.go index 315948661..376e38471 100644 --- a/pkg/ent/project_create.go +++ b/pkg/ent/project_create.go @@ -8,6 +8,8 @@ import ( "fmt" "time" + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" "entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/schema/field" "github.com/GoogleCloudPlatform/scion/pkg/ent/agent" @@ -20,6 +22,7 @@ type ProjectCreate struct { config mutation *ProjectMutation hooks []Hook + conflict []sql.ConflictOption } // SetName sets the "name" field. @@ -48,6 +51,20 @@ func (_c *ProjectCreate) SetNillableGitRemote(v *string) *ProjectCreate { return _c } +// SetDefaultRuntimeBrokerID sets the "default_runtime_broker_id" field. +func (_c *ProjectCreate) SetDefaultRuntimeBrokerID(v string) *ProjectCreate { + _c.mutation.SetDefaultRuntimeBrokerID(v) + return _c +} + +// SetNillableDefaultRuntimeBrokerID sets the "default_runtime_broker_id" field if the given value is not nil. +func (_c *ProjectCreate) SetNillableDefaultRuntimeBrokerID(v *string) *ProjectCreate { + if v != nil { + _c.SetDefaultRuntimeBrokerID(*v) + } + return _c +} + // SetLabels sets the "labels" field. func (_c *ProjectCreate) SetLabels(v map[string]string) *ProjectCreate { _c.mutation.SetLabels(v) @@ -60,6 +77,20 @@ func (_c *ProjectCreate) SetAnnotations(v map[string]string) *ProjectCreate { return _c } +// SetSharedDirs sets the "shared_dirs" field. +func (_c *ProjectCreate) SetSharedDirs(v string) *ProjectCreate { + _c.mutation.SetSharedDirs(v) + return _c +} + +// SetNillableSharedDirs sets the "shared_dirs" field if the given value is not nil. +func (_c *ProjectCreate) SetNillableSharedDirs(v *string) *ProjectCreate { + if v != nil { + _c.SetSharedDirs(*v) + } + return _c +} + // SetCreated sets the "created" field. func (_c *ProjectCreate) SetCreated(v time.Time) *ProjectCreate { _c.mutation.SetCreated(v) @@ -130,6 +161,62 @@ func (_c *ProjectCreate) SetNillableVisibility(v *string) *ProjectCreate { return _c } +// SetGithubInstallationID sets the "github_installation_id" field. +func (_c *ProjectCreate) SetGithubInstallationID(v int64) *ProjectCreate { + _c.mutation.SetGithubInstallationID(v) + return _c +} + +// SetNillableGithubInstallationID sets the "github_installation_id" field if the given value is not nil. +func (_c *ProjectCreate) SetNillableGithubInstallationID(v *int64) *ProjectCreate { + if v != nil { + _c.SetGithubInstallationID(*v) + } + return _c +} + +// SetGithubPermissions sets the "github_permissions" field. +func (_c *ProjectCreate) SetGithubPermissions(v string) *ProjectCreate { + _c.mutation.SetGithubPermissions(v) + return _c +} + +// SetNillableGithubPermissions sets the "github_permissions" field if the given value is not nil. +func (_c *ProjectCreate) SetNillableGithubPermissions(v *string) *ProjectCreate { + if v != nil { + _c.SetGithubPermissions(*v) + } + return _c +} + +// SetGithubAppStatus sets the "github_app_status" field. +func (_c *ProjectCreate) SetGithubAppStatus(v string) *ProjectCreate { + _c.mutation.SetGithubAppStatus(v) + return _c +} + +// SetNillableGithubAppStatus sets the "github_app_status" field if the given value is not nil. +func (_c *ProjectCreate) SetNillableGithubAppStatus(v *string) *ProjectCreate { + if v != nil { + _c.SetGithubAppStatus(*v) + } + return _c +} + +// SetGitIdentity sets the "git_identity" field. +func (_c *ProjectCreate) SetGitIdentity(v string) *ProjectCreate { + _c.mutation.SetGitIdentity(v) + return _c +} + +// SetNillableGitIdentity sets the "git_identity" field if the given value is not nil. +func (_c *ProjectCreate) SetNillableGitIdentity(v *string) *ProjectCreate { + if v != nil { + _c.SetGitIdentity(*v) + } + return _c +} + // SetID sets the "id" field. func (_c *ProjectCreate) SetID(v uuid.UUID) *ProjectCreate { _c.mutation.SetID(v) @@ -270,6 +357,7 @@ func (_c *ProjectCreate) createSpec() (*Project, *sqlgraph.CreateSpec) { _node = &Project{config: _c.config} _spec = sqlgraph.NewCreateSpec(project.Table, sqlgraph.NewFieldSpec(project.FieldID, field.TypeUUID)) ) + _spec.OnConflict = _c.conflict if id, ok := _c.mutation.ID(); ok { _node.ID = id _spec.ID.Value = &id @@ -286,6 +374,10 @@ func (_c *ProjectCreate) createSpec() (*Project, *sqlgraph.CreateSpec) { _spec.SetField(project.FieldGitRemote, field.TypeString, value) _node.GitRemote = &value } + if value, ok := _c.mutation.DefaultRuntimeBrokerID(); ok { + _spec.SetField(project.FieldDefaultRuntimeBrokerID, field.TypeString, value) + _node.DefaultRuntimeBrokerID = &value + } if value, ok := _c.mutation.Labels(); ok { _spec.SetField(project.FieldLabels, field.TypeJSON, value) _node.Labels = value @@ -294,6 +386,10 @@ func (_c *ProjectCreate) createSpec() (*Project, *sqlgraph.CreateSpec) { _spec.SetField(project.FieldAnnotations, field.TypeJSON, value) _node.Annotations = value } + if value, ok := _c.mutation.SharedDirs(); ok { + _spec.SetField(project.FieldSharedDirs, field.TypeString, value) + _node.SharedDirs = value + } if value, ok := _c.mutation.Created(); ok { _spec.SetField(project.FieldCreated, field.TypeTime, value) _node.Created = value @@ -314,6 +410,22 @@ func (_c *ProjectCreate) createSpec() (*Project, *sqlgraph.CreateSpec) { _spec.SetField(project.FieldVisibility, field.TypeString, value) _node.Visibility = value } + if value, ok := _c.mutation.GithubInstallationID(); ok { + _spec.SetField(project.FieldGithubInstallationID, field.TypeInt64, value) + _node.GithubInstallationID = &value + } + if value, ok := _c.mutation.GithubPermissions(); ok { + _spec.SetField(project.FieldGithubPermissions, field.TypeString, value) + _node.GithubPermissions = value + } + if value, ok := _c.mutation.GithubAppStatus(); ok { + _spec.SetField(project.FieldGithubAppStatus, field.TypeString, value) + _node.GithubAppStatus = value + } + if value, ok := _c.mutation.GitIdentity(); ok { + _spec.SetField(project.FieldGitIdentity, field.TypeString, value) + _node.GitIdentity = value + } if nodes := _c.mutation.AgentsIDs(); len(nodes) > 0 { edge := &sqlgraph.EdgeSpec{ Rel: sqlgraph.O2M, @@ -333,11 +445,696 @@ func (_c *ProjectCreate) createSpec() (*Project, *sqlgraph.CreateSpec) { return _node, _spec } +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.Project.Create(). +// SetName(v). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.ProjectUpsert) { +// SetName(v+v). +// }). +// Exec(ctx) +func (_c *ProjectCreate) OnConflict(opts ...sql.ConflictOption) *ProjectUpsertOne { + _c.conflict = opts + return &ProjectUpsertOne{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.Project.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *ProjectCreate) OnConflictColumns(columns ...string) *ProjectUpsertOne { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &ProjectUpsertOne{ + create: _c, + } +} + +type ( + // ProjectUpsertOne is the builder for "upsert"-ing + // one Project node. + ProjectUpsertOne struct { + create *ProjectCreate + } + + // ProjectUpsert is the "OnConflict" setter. + ProjectUpsert struct { + *sql.UpdateSet + } +) + +// SetName sets the "name" field. +func (u *ProjectUpsert) SetName(v string) *ProjectUpsert { + u.Set(project.FieldName, v) + return u +} + +// UpdateName sets the "name" field to the value that was provided on create. +func (u *ProjectUpsert) UpdateName() *ProjectUpsert { + u.SetExcluded(project.FieldName) + return u +} + +// SetSlug sets the "slug" field. +func (u *ProjectUpsert) SetSlug(v string) *ProjectUpsert { + u.Set(project.FieldSlug, v) + return u +} + +// UpdateSlug sets the "slug" field to the value that was provided on create. +func (u *ProjectUpsert) UpdateSlug() *ProjectUpsert { + u.SetExcluded(project.FieldSlug) + return u +} + +// SetGitRemote sets the "git_remote" field. +func (u *ProjectUpsert) SetGitRemote(v string) *ProjectUpsert { + u.Set(project.FieldGitRemote, v) + return u +} + +// UpdateGitRemote sets the "git_remote" field to the value that was provided on create. +func (u *ProjectUpsert) UpdateGitRemote() *ProjectUpsert { + u.SetExcluded(project.FieldGitRemote) + return u +} + +// ClearGitRemote clears the value of the "git_remote" field. +func (u *ProjectUpsert) ClearGitRemote() *ProjectUpsert { + u.SetNull(project.FieldGitRemote) + return u +} + +// SetDefaultRuntimeBrokerID sets the "default_runtime_broker_id" field. +func (u *ProjectUpsert) SetDefaultRuntimeBrokerID(v string) *ProjectUpsert { + u.Set(project.FieldDefaultRuntimeBrokerID, v) + return u +} + +// UpdateDefaultRuntimeBrokerID sets the "default_runtime_broker_id" field to the value that was provided on create. +func (u *ProjectUpsert) UpdateDefaultRuntimeBrokerID() *ProjectUpsert { + u.SetExcluded(project.FieldDefaultRuntimeBrokerID) + return u +} + +// ClearDefaultRuntimeBrokerID clears the value of the "default_runtime_broker_id" field. +func (u *ProjectUpsert) ClearDefaultRuntimeBrokerID() *ProjectUpsert { + u.SetNull(project.FieldDefaultRuntimeBrokerID) + return u +} + +// SetLabels sets the "labels" field. +func (u *ProjectUpsert) SetLabels(v map[string]string) *ProjectUpsert { + u.Set(project.FieldLabels, v) + return u +} + +// UpdateLabels sets the "labels" field to the value that was provided on create. +func (u *ProjectUpsert) UpdateLabels() *ProjectUpsert { + u.SetExcluded(project.FieldLabels) + return u +} + +// ClearLabels clears the value of the "labels" field. +func (u *ProjectUpsert) ClearLabels() *ProjectUpsert { + u.SetNull(project.FieldLabels) + return u +} + +// SetAnnotations sets the "annotations" field. +func (u *ProjectUpsert) SetAnnotations(v map[string]string) *ProjectUpsert { + u.Set(project.FieldAnnotations, v) + return u +} + +// UpdateAnnotations sets the "annotations" field to the value that was provided on create. +func (u *ProjectUpsert) UpdateAnnotations() *ProjectUpsert { + u.SetExcluded(project.FieldAnnotations) + return u +} + +// ClearAnnotations clears the value of the "annotations" field. +func (u *ProjectUpsert) ClearAnnotations() *ProjectUpsert { + u.SetNull(project.FieldAnnotations) + return u +} + +// SetSharedDirs sets the "shared_dirs" field. +func (u *ProjectUpsert) SetSharedDirs(v string) *ProjectUpsert { + u.Set(project.FieldSharedDirs, v) + return u +} + +// UpdateSharedDirs sets the "shared_dirs" field to the value that was provided on create. +func (u *ProjectUpsert) UpdateSharedDirs() *ProjectUpsert { + u.SetExcluded(project.FieldSharedDirs) + return u +} + +// ClearSharedDirs clears the value of the "shared_dirs" field. +func (u *ProjectUpsert) ClearSharedDirs() *ProjectUpsert { + u.SetNull(project.FieldSharedDirs) + return u +} + +// SetUpdated sets the "updated" field. +func (u *ProjectUpsert) SetUpdated(v time.Time) *ProjectUpsert { + u.Set(project.FieldUpdated, v) + return u +} + +// UpdateUpdated sets the "updated" field to the value that was provided on create. +func (u *ProjectUpsert) UpdateUpdated() *ProjectUpsert { + u.SetExcluded(project.FieldUpdated) + return u +} + +// SetCreatedBy sets the "created_by" field. +func (u *ProjectUpsert) SetCreatedBy(v string) *ProjectUpsert { + u.Set(project.FieldCreatedBy, v) + return u +} + +// UpdateCreatedBy sets the "created_by" field to the value that was provided on create. +func (u *ProjectUpsert) UpdateCreatedBy() *ProjectUpsert { + u.SetExcluded(project.FieldCreatedBy) + return u +} + +// ClearCreatedBy clears the value of the "created_by" field. +func (u *ProjectUpsert) ClearCreatedBy() *ProjectUpsert { + u.SetNull(project.FieldCreatedBy) + return u +} + +// SetOwnerID sets the "owner_id" field. +func (u *ProjectUpsert) SetOwnerID(v string) *ProjectUpsert { + u.Set(project.FieldOwnerID, v) + return u +} + +// UpdateOwnerID sets the "owner_id" field to the value that was provided on create. +func (u *ProjectUpsert) UpdateOwnerID() *ProjectUpsert { + u.SetExcluded(project.FieldOwnerID) + return u +} + +// ClearOwnerID clears the value of the "owner_id" field. +func (u *ProjectUpsert) ClearOwnerID() *ProjectUpsert { + u.SetNull(project.FieldOwnerID) + return u +} + +// SetVisibility sets the "visibility" field. +func (u *ProjectUpsert) SetVisibility(v string) *ProjectUpsert { + u.Set(project.FieldVisibility, v) + return u +} + +// UpdateVisibility sets the "visibility" field to the value that was provided on create. +func (u *ProjectUpsert) UpdateVisibility() *ProjectUpsert { + u.SetExcluded(project.FieldVisibility) + return u +} + +// SetGithubInstallationID sets the "github_installation_id" field. +func (u *ProjectUpsert) SetGithubInstallationID(v int64) *ProjectUpsert { + u.Set(project.FieldGithubInstallationID, v) + return u +} + +// UpdateGithubInstallationID sets the "github_installation_id" field to the value that was provided on create. +func (u *ProjectUpsert) UpdateGithubInstallationID() *ProjectUpsert { + u.SetExcluded(project.FieldGithubInstallationID) + return u +} + +// AddGithubInstallationID adds v to the "github_installation_id" field. +func (u *ProjectUpsert) AddGithubInstallationID(v int64) *ProjectUpsert { + u.Add(project.FieldGithubInstallationID, v) + return u +} + +// ClearGithubInstallationID clears the value of the "github_installation_id" field. +func (u *ProjectUpsert) ClearGithubInstallationID() *ProjectUpsert { + u.SetNull(project.FieldGithubInstallationID) + return u +} + +// SetGithubPermissions sets the "github_permissions" field. +func (u *ProjectUpsert) SetGithubPermissions(v string) *ProjectUpsert { + u.Set(project.FieldGithubPermissions, v) + return u +} + +// UpdateGithubPermissions sets the "github_permissions" field to the value that was provided on create. +func (u *ProjectUpsert) UpdateGithubPermissions() *ProjectUpsert { + u.SetExcluded(project.FieldGithubPermissions) + return u +} + +// ClearGithubPermissions clears the value of the "github_permissions" field. +func (u *ProjectUpsert) ClearGithubPermissions() *ProjectUpsert { + u.SetNull(project.FieldGithubPermissions) + return u +} + +// SetGithubAppStatus sets the "github_app_status" field. +func (u *ProjectUpsert) SetGithubAppStatus(v string) *ProjectUpsert { + u.Set(project.FieldGithubAppStatus, v) + return u +} + +// UpdateGithubAppStatus sets the "github_app_status" field to the value that was provided on create. +func (u *ProjectUpsert) UpdateGithubAppStatus() *ProjectUpsert { + u.SetExcluded(project.FieldGithubAppStatus) + return u +} + +// ClearGithubAppStatus clears the value of the "github_app_status" field. +func (u *ProjectUpsert) ClearGithubAppStatus() *ProjectUpsert { + u.SetNull(project.FieldGithubAppStatus) + return u +} + +// SetGitIdentity sets the "git_identity" field. +func (u *ProjectUpsert) SetGitIdentity(v string) *ProjectUpsert { + u.Set(project.FieldGitIdentity, v) + return u +} + +// UpdateGitIdentity sets the "git_identity" field to the value that was provided on create. +func (u *ProjectUpsert) UpdateGitIdentity() *ProjectUpsert { + u.SetExcluded(project.FieldGitIdentity) + return u +} + +// ClearGitIdentity clears the value of the "git_identity" field. +func (u *ProjectUpsert) ClearGitIdentity() *ProjectUpsert { + u.SetNull(project.FieldGitIdentity) + return u +} + +// UpdateNewValues updates the mutable fields using the new values that were set on create except the ID field. +// Using this option is equivalent to using: +// +// client.Project.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(project.FieldID) +// }), +// ). +// Exec(ctx) +func (u *ProjectUpsertOne) UpdateNewValues() *ProjectUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + if _, exists := u.create.mutation.ID(); exists { + s.SetIgnore(project.FieldID) + } + if _, exists := u.create.mutation.Created(); exists { + s.SetIgnore(project.FieldCreated) + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.Project.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *ProjectUpsertOne) Ignore() *ProjectUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *ProjectUpsertOne) DoNothing() *ProjectUpsertOne { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the ProjectCreate.OnConflict +// documentation for more info. +func (u *ProjectUpsertOne) Update(set func(*ProjectUpsert)) *ProjectUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&ProjectUpsert{UpdateSet: update}) + })) + return u +} + +// SetName sets the "name" field. +func (u *ProjectUpsertOne) SetName(v string) *ProjectUpsertOne { + return u.Update(func(s *ProjectUpsert) { + s.SetName(v) + }) +} + +// UpdateName sets the "name" field to the value that was provided on create. +func (u *ProjectUpsertOne) UpdateName() *ProjectUpsertOne { + return u.Update(func(s *ProjectUpsert) { + s.UpdateName() + }) +} + +// SetSlug sets the "slug" field. +func (u *ProjectUpsertOne) SetSlug(v string) *ProjectUpsertOne { + return u.Update(func(s *ProjectUpsert) { + s.SetSlug(v) + }) +} + +// UpdateSlug sets the "slug" field to the value that was provided on create. +func (u *ProjectUpsertOne) UpdateSlug() *ProjectUpsertOne { + return u.Update(func(s *ProjectUpsert) { + s.UpdateSlug() + }) +} + +// SetGitRemote sets the "git_remote" field. +func (u *ProjectUpsertOne) SetGitRemote(v string) *ProjectUpsertOne { + return u.Update(func(s *ProjectUpsert) { + s.SetGitRemote(v) + }) +} + +// UpdateGitRemote sets the "git_remote" field to the value that was provided on create. +func (u *ProjectUpsertOne) UpdateGitRemote() *ProjectUpsertOne { + return u.Update(func(s *ProjectUpsert) { + s.UpdateGitRemote() + }) +} + +// ClearGitRemote clears the value of the "git_remote" field. +func (u *ProjectUpsertOne) ClearGitRemote() *ProjectUpsertOne { + return u.Update(func(s *ProjectUpsert) { + s.ClearGitRemote() + }) +} + +// SetDefaultRuntimeBrokerID sets the "default_runtime_broker_id" field. +func (u *ProjectUpsertOne) SetDefaultRuntimeBrokerID(v string) *ProjectUpsertOne { + return u.Update(func(s *ProjectUpsert) { + s.SetDefaultRuntimeBrokerID(v) + }) +} + +// UpdateDefaultRuntimeBrokerID sets the "default_runtime_broker_id" field to the value that was provided on create. +func (u *ProjectUpsertOne) UpdateDefaultRuntimeBrokerID() *ProjectUpsertOne { + return u.Update(func(s *ProjectUpsert) { + s.UpdateDefaultRuntimeBrokerID() + }) +} + +// ClearDefaultRuntimeBrokerID clears the value of the "default_runtime_broker_id" field. +func (u *ProjectUpsertOne) ClearDefaultRuntimeBrokerID() *ProjectUpsertOne { + return u.Update(func(s *ProjectUpsert) { + s.ClearDefaultRuntimeBrokerID() + }) +} + +// SetLabels sets the "labels" field. +func (u *ProjectUpsertOne) SetLabels(v map[string]string) *ProjectUpsertOne { + return u.Update(func(s *ProjectUpsert) { + s.SetLabels(v) + }) +} + +// UpdateLabels sets the "labels" field to the value that was provided on create. +func (u *ProjectUpsertOne) UpdateLabels() *ProjectUpsertOne { + return u.Update(func(s *ProjectUpsert) { + s.UpdateLabels() + }) +} + +// ClearLabels clears the value of the "labels" field. +func (u *ProjectUpsertOne) ClearLabels() *ProjectUpsertOne { + return u.Update(func(s *ProjectUpsert) { + s.ClearLabels() + }) +} + +// SetAnnotations sets the "annotations" field. +func (u *ProjectUpsertOne) SetAnnotations(v map[string]string) *ProjectUpsertOne { + return u.Update(func(s *ProjectUpsert) { + s.SetAnnotations(v) + }) +} + +// UpdateAnnotations sets the "annotations" field to the value that was provided on create. +func (u *ProjectUpsertOne) UpdateAnnotations() *ProjectUpsertOne { + return u.Update(func(s *ProjectUpsert) { + s.UpdateAnnotations() + }) +} + +// ClearAnnotations clears the value of the "annotations" field. +func (u *ProjectUpsertOne) ClearAnnotations() *ProjectUpsertOne { + return u.Update(func(s *ProjectUpsert) { + s.ClearAnnotations() + }) +} + +// SetSharedDirs sets the "shared_dirs" field. +func (u *ProjectUpsertOne) SetSharedDirs(v string) *ProjectUpsertOne { + return u.Update(func(s *ProjectUpsert) { + s.SetSharedDirs(v) + }) +} + +// UpdateSharedDirs sets the "shared_dirs" field to the value that was provided on create. +func (u *ProjectUpsertOne) UpdateSharedDirs() *ProjectUpsertOne { + return u.Update(func(s *ProjectUpsert) { + s.UpdateSharedDirs() + }) +} + +// ClearSharedDirs clears the value of the "shared_dirs" field. +func (u *ProjectUpsertOne) ClearSharedDirs() *ProjectUpsertOne { + return u.Update(func(s *ProjectUpsert) { + s.ClearSharedDirs() + }) +} + +// SetUpdated sets the "updated" field. +func (u *ProjectUpsertOne) SetUpdated(v time.Time) *ProjectUpsertOne { + return u.Update(func(s *ProjectUpsert) { + s.SetUpdated(v) + }) +} + +// UpdateUpdated sets the "updated" field to the value that was provided on create. +func (u *ProjectUpsertOne) UpdateUpdated() *ProjectUpsertOne { + return u.Update(func(s *ProjectUpsert) { + s.UpdateUpdated() + }) +} + +// SetCreatedBy sets the "created_by" field. +func (u *ProjectUpsertOne) SetCreatedBy(v string) *ProjectUpsertOne { + return u.Update(func(s *ProjectUpsert) { + s.SetCreatedBy(v) + }) +} + +// UpdateCreatedBy sets the "created_by" field to the value that was provided on create. +func (u *ProjectUpsertOne) UpdateCreatedBy() *ProjectUpsertOne { + return u.Update(func(s *ProjectUpsert) { + s.UpdateCreatedBy() + }) +} + +// ClearCreatedBy clears the value of the "created_by" field. +func (u *ProjectUpsertOne) ClearCreatedBy() *ProjectUpsertOne { + return u.Update(func(s *ProjectUpsert) { + s.ClearCreatedBy() + }) +} + +// SetOwnerID sets the "owner_id" field. +func (u *ProjectUpsertOne) SetOwnerID(v string) *ProjectUpsertOne { + return u.Update(func(s *ProjectUpsert) { + s.SetOwnerID(v) + }) +} + +// UpdateOwnerID sets the "owner_id" field to the value that was provided on create. +func (u *ProjectUpsertOne) UpdateOwnerID() *ProjectUpsertOne { + return u.Update(func(s *ProjectUpsert) { + s.UpdateOwnerID() + }) +} + +// ClearOwnerID clears the value of the "owner_id" field. +func (u *ProjectUpsertOne) ClearOwnerID() *ProjectUpsertOne { + return u.Update(func(s *ProjectUpsert) { + s.ClearOwnerID() + }) +} + +// SetVisibility sets the "visibility" field. +func (u *ProjectUpsertOne) SetVisibility(v string) *ProjectUpsertOne { + return u.Update(func(s *ProjectUpsert) { + s.SetVisibility(v) + }) +} + +// UpdateVisibility sets the "visibility" field to the value that was provided on create. +func (u *ProjectUpsertOne) UpdateVisibility() *ProjectUpsertOne { + return u.Update(func(s *ProjectUpsert) { + s.UpdateVisibility() + }) +} + +// SetGithubInstallationID sets the "github_installation_id" field. +func (u *ProjectUpsertOne) SetGithubInstallationID(v int64) *ProjectUpsertOne { + return u.Update(func(s *ProjectUpsert) { + s.SetGithubInstallationID(v) + }) +} + +// AddGithubInstallationID adds v to the "github_installation_id" field. +func (u *ProjectUpsertOne) AddGithubInstallationID(v int64) *ProjectUpsertOne { + return u.Update(func(s *ProjectUpsert) { + s.AddGithubInstallationID(v) + }) +} + +// UpdateGithubInstallationID sets the "github_installation_id" field to the value that was provided on create. +func (u *ProjectUpsertOne) UpdateGithubInstallationID() *ProjectUpsertOne { + return u.Update(func(s *ProjectUpsert) { + s.UpdateGithubInstallationID() + }) +} + +// ClearGithubInstallationID clears the value of the "github_installation_id" field. +func (u *ProjectUpsertOne) ClearGithubInstallationID() *ProjectUpsertOne { + return u.Update(func(s *ProjectUpsert) { + s.ClearGithubInstallationID() + }) +} + +// SetGithubPermissions sets the "github_permissions" field. +func (u *ProjectUpsertOne) SetGithubPermissions(v string) *ProjectUpsertOne { + return u.Update(func(s *ProjectUpsert) { + s.SetGithubPermissions(v) + }) +} + +// UpdateGithubPermissions sets the "github_permissions" field to the value that was provided on create. +func (u *ProjectUpsertOne) UpdateGithubPermissions() *ProjectUpsertOne { + return u.Update(func(s *ProjectUpsert) { + s.UpdateGithubPermissions() + }) +} + +// ClearGithubPermissions clears the value of the "github_permissions" field. +func (u *ProjectUpsertOne) ClearGithubPermissions() *ProjectUpsertOne { + return u.Update(func(s *ProjectUpsert) { + s.ClearGithubPermissions() + }) +} + +// SetGithubAppStatus sets the "github_app_status" field. +func (u *ProjectUpsertOne) SetGithubAppStatus(v string) *ProjectUpsertOne { + return u.Update(func(s *ProjectUpsert) { + s.SetGithubAppStatus(v) + }) +} + +// UpdateGithubAppStatus sets the "github_app_status" field to the value that was provided on create. +func (u *ProjectUpsertOne) UpdateGithubAppStatus() *ProjectUpsertOne { + return u.Update(func(s *ProjectUpsert) { + s.UpdateGithubAppStatus() + }) +} + +// ClearGithubAppStatus clears the value of the "github_app_status" field. +func (u *ProjectUpsertOne) ClearGithubAppStatus() *ProjectUpsertOne { + return u.Update(func(s *ProjectUpsert) { + s.ClearGithubAppStatus() + }) +} + +// SetGitIdentity sets the "git_identity" field. +func (u *ProjectUpsertOne) SetGitIdentity(v string) *ProjectUpsertOne { + return u.Update(func(s *ProjectUpsert) { + s.SetGitIdentity(v) + }) +} + +// UpdateGitIdentity sets the "git_identity" field to the value that was provided on create. +func (u *ProjectUpsertOne) UpdateGitIdentity() *ProjectUpsertOne { + return u.Update(func(s *ProjectUpsert) { + s.UpdateGitIdentity() + }) +} + +// ClearGitIdentity clears the value of the "git_identity" field. +func (u *ProjectUpsertOne) ClearGitIdentity() *ProjectUpsertOne { + return u.Update(func(s *ProjectUpsert) { + s.ClearGitIdentity() + }) +} + +// Exec executes the query. +func (u *ProjectUpsertOne) Exec(ctx context.Context) error { + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for ProjectCreate.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *ProjectUpsertOne) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} + +// Exec executes the UPSERT query and returns the inserted/updated ID. +func (u *ProjectUpsertOne) ID(ctx context.Context) (id uuid.UUID, err error) { + if u.create.driver.Dialect() == dialect.MySQL { + // In case of "ON CONFLICT", there is no way to get back non-numeric ID + // fields from the database since MySQL does not support the RETURNING clause. + return id, errors.New("ent: ProjectUpsertOne.ID is not supported by MySQL driver. Use ProjectUpsertOne.Exec instead") + } + node, err := u.create.Save(ctx) + if err != nil { + return id, err + } + return node.ID, nil +} + +// IDX is like ID, but panics if an error occurs. +func (u *ProjectUpsertOne) IDX(ctx context.Context) uuid.UUID { + id, err := u.ID(ctx) + if err != nil { + panic(err) + } + return id +} + // ProjectCreateBulk is the builder for creating many Project entities in bulk. type ProjectCreateBulk struct { config err error builders []*ProjectCreate + conflict []sql.ConflictOption } // Save creates the Project entities in the database. @@ -367,6 +1164,7 @@ func (_c *ProjectCreateBulk) Save(ctx context.Context) ([]*Project, error) { _, err = mutators[i+1].Mutate(root, _c.builders[i+1].mutation) } else { spec := &sqlgraph.BatchCreateSpec{Nodes: specs} + spec.OnConflict = _c.conflict // Invoke the actual operation on the latest mutation in the chain. if err = sqlgraph.BatchCreate(ctx, _c.driver, spec); err != nil { if sqlgraph.IsConstraintError(err) { @@ -416,3 +1214,414 @@ func (_c *ProjectCreateBulk) ExecX(ctx context.Context) { panic(err) } } + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.Project.CreateBulk(builders...). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.ProjectUpsert) { +// SetName(v+v). +// }). +// Exec(ctx) +func (_c *ProjectCreateBulk) OnConflict(opts ...sql.ConflictOption) *ProjectUpsertBulk { + _c.conflict = opts + return &ProjectUpsertBulk{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.Project.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *ProjectCreateBulk) OnConflictColumns(columns ...string) *ProjectUpsertBulk { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &ProjectUpsertBulk{ + create: _c, + } +} + +// ProjectUpsertBulk is the builder for "upsert"-ing +// a bulk of Project nodes. +type ProjectUpsertBulk struct { + create *ProjectCreateBulk +} + +// UpdateNewValues updates the mutable fields using the new values that +// were set on create. Using this option is equivalent to using: +// +// client.Project.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(project.FieldID) +// }), +// ). +// Exec(ctx) +func (u *ProjectUpsertBulk) UpdateNewValues() *ProjectUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + for _, b := range u.create.builders { + if _, exists := b.mutation.ID(); exists { + s.SetIgnore(project.FieldID) + } + if _, exists := b.mutation.Created(); exists { + s.SetIgnore(project.FieldCreated) + } + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.Project.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *ProjectUpsertBulk) Ignore() *ProjectUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *ProjectUpsertBulk) DoNothing() *ProjectUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the ProjectCreateBulk.OnConflict +// documentation for more info. +func (u *ProjectUpsertBulk) Update(set func(*ProjectUpsert)) *ProjectUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&ProjectUpsert{UpdateSet: update}) + })) + return u +} + +// SetName sets the "name" field. +func (u *ProjectUpsertBulk) SetName(v string) *ProjectUpsertBulk { + return u.Update(func(s *ProjectUpsert) { + s.SetName(v) + }) +} + +// UpdateName sets the "name" field to the value that was provided on create. +func (u *ProjectUpsertBulk) UpdateName() *ProjectUpsertBulk { + return u.Update(func(s *ProjectUpsert) { + s.UpdateName() + }) +} + +// SetSlug sets the "slug" field. +func (u *ProjectUpsertBulk) SetSlug(v string) *ProjectUpsertBulk { + return u.Update(func(s *ProjectUpsert) { + s.SetSlug(v) + }) +} + +// UpdateSlug sets the "slug" field to the value that was provided on create. +func (u *ProjectUpsertBulk) UpdateSlug() *ProjectUpsertBulk { + return u.Update(func(s *ProjectUpsert) { + s.UpdateSlug() + }) +} + +// SetGitRemote sets the "git_remote" field. +func (u *ProjectUpsertBulk) SetGitRemote(v string) *ProjectUpsertBulk { + return u.Update(func(s *ProjectUpsert) { + s.SetGitRemote(v) + }) +} + +// UpdateGitRemote sets the "git_remote" field to the value that was provided on create. +func (u *ProjectUpsertBulk) UpdateGitRemote() *ProjectUpsertBulk { + return u.Update(func(s *ProjectUpsert) { + s.UpdateGitRemote() + }) +} + +// ClearGitRemote clears the value of the "git_remote" field. +func (u *ProjectUpsertBulk) ClearGitRemote() *ProjectUpsertBulk { + return u.Update(func(s *ProjectUpsert) { + s.ClearGitRemote() + }) +} + +// SetDefaultRuntimeBrokerID sets the "default_runtime_broker_id" field. +func (u *ProjectUpsertBulk) SetDefaultRuntimeBrokerID(v string) *ProjectUpsertBulk { + return u.Update(func(s *ProjectUpsert) { + s.SetDefaultRuntimeBrokerID(v) + }) +} + +// UpdateDefaultRuntimeBrokerID sets the "default_runtime_broker_id" field to the value that was provided on create. +func (u *ProjectUpsertBulk) UpdateDefaultRuntimeBrokerID() *ProjectUpsertBulk { + return u.Update(func(s *ProjectUpsert) { + s.UpdateDefaultRuntimeBrokerID() + }) +} + +// ClearDefaultRuntimeBrokerID clears the value of the "default_runtime_broker_id" field. +func (u *ProjectUpsertBulk) ClearDefaultRuntimeBrokerID() *ProjectUpsertBulk { + return u.Update(func(s *ProjectUpsert) { + s.ClearDefaultRuntimeBrokerID() + }) +} + +// SetLabels sets the "labels" field. +func (u *ProjectUpsertBulk) SetLabels(v map[string]string) *ProjectUpsertBulk { + return u.Update(func(s *ProjectUpsert) { + s.SetLabels(v) + }) +} + +// UpdateLabels sets the "labels" field to the value that was provided on create. +func (u *ProjectUpsertBulk) UpdateLabels() *ProjectUpsertBulk { + return u.Update(func(s *ProjectUpsert) { + s.UpdateLabels() + }) +} + +// ClearLabels clears the value of the "labels" field. +func (u *ProjectUpsertBulk) ClearLabels() *ProjectUpsertBulk { + return u.Update(func(s *ProjectUpsert) { + s.ClearLabels() + }) +} + +// SetAnnotations sets the "annotations" field. +func (u *ProjectUpsertBulk) SetAnnotations(v map[string]string) *ProjectUpsertBulk { + return u.Update(func(s *ProjectUpsert) { + s.SetAnnotations(v) + }) +} + +// UpdateAnnotations sets the "annotations" field to the value that was provided on create. +func (u *ProjectUpsertBulk) UpdateAnnotations() *ProjectUpsertBulk { + return u.Update(func(s *ProjectUpsert) { + s.UpdateAnnotations() + }) +} + +// ClearAnnotations clears the value of the "annotations" field. +func (u *ProjectUpsertBulk) ClearAnnotations() *ProjectUpsertBulk { + return u.Update(func(s *ProjectUpsert) { + s.ClearAnnotations() + }) +} + +// SetSharedDirs sets the "shared_dirs" field. +func (u *ProjectUpsertBulk) SetSharedDirs(v string) *ProjectUpsertBulk { + return u.Update(func(s *ProjectUpsert) { + s.SetSharedDirs(v) + }) +} + +// UpdateSharedDirs sets the "shared_dirs" field to the value that was provided on create. +func (u *ProjectUpsertBulk) UpdateSharedDirs() *ProjectUpsertBulk { + return u.Update(func(s *ProjectUpsert) { + s.UpdateSharedDirs() + }) +} + +// ClearSharedDirs clears the value of the "shared_dirs" field. +func (u *ProjectUpsertBulk) ClearSharedDirs() *ProjectUpsertBulk { + return u.Update(func(s *ProjectUpsert) { + s.ClearSharedDirs() + }) +} + +// SetUpdated sets the "updated" field. +func (u *ProjectUpsertBulk) SetUpdated(v time.Time) *ProjectUpsertBulk { + return u.Update(func(s *ProjectUpsert) { + s.SetUpdated(v) + }) +} + +// UpdateUpdated sets the "updated" field to the value that was provided on create. +func (u *ProjectUpsertBulk) UpdateUpdated() *ProjectUpsertBulk { + return u.Update(func(s *ProjectUpsert) { + s.UpdateUpdated() + }) +} + +// SetCreatedBy sets the "created_by" field. +func (u *ProjectUpsertBulk) SetCreatedBy(v string) *ProjectUpsertBulk { + return u.Update(func(s *ProjectUpsert) { + s.SetCreatedBy(v) + }) +} + +// UpdateCreatedBy sets the "created_by" field to the value that was provided on create. +func (u *ProjectUpsertBulk) UpdateCreatedBy() *ProjectUpsertBulk { + return u.Update(func(s *ProjectUpsert) { + s.UpdateCreatedBy() + }) +} + +// ClearCreatedBy clears the value of the "created_by" field. +func (u *ProjectUpsertBulk) ClearCreatedBy() *ProjectUpsertBulk { + return u.Update(func(s *ProjectUpsert) { + s.ClearCreatedBy() + }) +} + +// SetOwnerID sets the "owner_id" field. +func (u *ProjectUpsertBulk) SetOwnerID(v string) *ProjectUpsertBulk { + return u.Update(func(s *ProjectUpsert) { + s.SetOwnerID(v) + }) +} + +// UpdateOwnerID sets the "owner_id" field to the value that was provided on create. +func (u *ProjectUpsertBulk) UpdateOwnerID() *ProjectUpsertBulk { + return u.Update(func(s *ProjectUpsert) { + s.UpdateOwnerID() + }) +} + +// ClearOwnerID clears the value of the "owner_id" field. +func (u *ProjectUpsertBulk) ClearOwnerID() *ProjectUpsertBulk { + return u.Update(func(s *ProjectUpsert) { + s.ClearOwnerID() + }) +} + +// SetVisibility sets the "visibility" field. +func (u *ProjectUpsertBulk) SetVisibility(v string) *ProjectUpsertBulk { + return u.Update(func(s *ProjectUpsert) { + s.SetVisibility(v) + }) +} + +// UpdateVisibility sets the "visibility" field to the value that was provided on create. +func (u *ProjectUpsertBulk) UpdateVisibility() *ProjectUpsertBulk { + return u.Update(func(s *ProjectUpsert) { + s.UpdateVisibility() + }) +} + +// SetGithubInstallationID sets the "github_installation_id" field. +func (u *ProjectUpsertBulk) SetGithubInstallationID(v int64) *ProjectUpsertBulk { + return u.Update(func(s *ProjectUpsert) { + s.SetGithubInstallationID(v) + }) +} + +// AddGithubInstallationID adds v to the "github_installation_id" field. +func (u *ProjectUpsertBulk) AddGithubInstallationID(v int64) *ProjectUpsertBulk { + return u.Update(func(s *ProjectUpsert) { + s.AddGithubInstallationID(v) + }) +} + +// UpdateGithubInstallationID sets the "github_installation_id" field to the value that was provided on create. +func (u *ProjectUpsertBulk) UpdateGithubInstallationID() *ProjectUpsertBulk { + return u.Update(func(s *ProjectUpsert) { + s.UpdateGithubInstallationID() + }) +} + +// ClearGithubInstallationID clears the value of the "github_installation_id" field. +func (u *ProjectUpsertBulk) ClearGithubInstallationID() *ProjectUpsertBulk { + return u.Update(func(s *ProjectUpsert) { + s.ClearGithubInstallationID() + }) +} + +// SetGithubPermissions sets the "github_permissions" field. +func (u *ProjectUpsertBulk) SetGithubPermissions(v string) *ProjectUpsertBulk { + return u.Update(func(s *ProjectUpsert) { + s.SetGithubPermissions(v) + }) +} + +// UpdateGithubPermissions sets the "github_permissions" field to the value that was provided on create. +func (u *ProjectUpsertBulk) UpdateGithubPermissions() *ProjectUpsertBulk { + return u.Update(func(s *ProjectUpsert) { + s.UpdateGithubPermissions() + }) +} + +// ClearGithubPermissions clears the value of the "github_permissions" field. +func (u *ProjectUpsertBulk) ClearGithubPermissions() *ProjectUpsertBulk { + return u.Update(func(s *ProjectUpsert) { + s.ClearGithubPermissions() + }) +} + +// SetGithubAppStatus sets the "github_app_status" field. +func (u *ProjectUpsertBulk) SetGithubAppStatus(v string) *ProjectUpsertBulk { + return u.Update(func(s *ProjectUpsert) { + s.SetGithubAppStatus(v) + }) +} + +// UpdateGithubAppStatus sets the "github_app_status" field to the value that was provided on create. +func (u *ProjectUpsertBulk) UpdateGithubAppStatus() *ProjectUpsertBulk { + return u.Update(func(s *ProjectUpsert) { + s.UpdateGithubAppStatus() + }) +} + +// ClearGithubAppStatus clears the value of the "github_app_status" field. +func (u *ProjectUpsertBulk) ClearGithubAppStatus() *ProjectUpsertBulk { + return u.Update(func(s *ProjectUpsert) { + s.ClearGithubAppStatus() + }) +} + +// SetGitIdentity sets the "git_identity" field. +func (u *ProjectUpsertBulk) SetGitIdentity(v string) *ProjectUpsertBulk { + return u.Update(func(s *ProjectUpsert) { + s.SetGitIdentity(v) + }) +} + +// UpdateGitIdentity sets the "git_identity" field to the value that was provided on create. +func (u *ProjectUpsertBulk) UpdateGitIdentity() *ProjectUpsertBulk { + return u.Update(func(s *ProjectUpsert) { + s.UpdateGitIdentity() + }) +} + +// ClearGitIdentity clears the value of the "git_identity" field. +func (u *ProjectUpsertBulk) ClearGitIdentity() *ProjectUpsertBulk { + return u.Update(func(s *ProjectUpsert) { + s.ClearGitIdentity() + }) +} + +// Exec executes the query. +func (u *ProjectUpsertBulk) Exec(ctx context.Context) error { + if u.create.err != nil { + return u.create.err + } + for i, b := range u.create.builders { + if len(b.conflict) != 0 { + return fmt.Errorf("ent: OnConflict was set for builder %d. Set it on the ProjectCreateBulk instead", i) + } + } + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for ProjectCreateBulk.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *ProjectUpsertBulk) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/pkg/ent/project_query.go b/pkg/ent/project_query.go index 15f69e7be..d339a90eb 100644 --- a/pkg/ent/project_query.go +++ b/pkg/ent/project_query.go @@ -9,6 +9,7 @@ import ( "math" "entgo.io/ent" + "entgo.io/ent/dialect" "entgo.io/ent/dialect/sql" "entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/schema/field" @@ -26,6 +27,7 @@ type ProjectQuery struct { inters []Interceptor predicates []predicate.Project withAgents *AgentQuery + modifiers []func(*sql.Selector) // intermediate query (i.e. traversal path). sql *sql.Selector path func(context.Context) (*sql.Selector, error) @@ -385,6 +387,9 @@ func (_q *ProjectQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*Proj node.Edges.loadedTypes = loadedTypes return node.assignValues(columns, values) } + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } for i := range hooks { hooks[i](ctx, _spec) } @@ -437,6 +442,9 @@ func (_q *ProjectQuery) loadAgents(ctx context.Context, query *AgentQuery, nodes func (_q *ProjectQuery) sqlCount(ctx context.Context) (int, error) { _spec := _q.querySpec() + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } _spec.Node.Columns = _q.ctx.Fields if len(_q.ctx.Fields) > 0 { _spec.Unique = _q.ctx.Unique != nil && *_q.ctx.Unique @@ -499,6 +507,9 @@ func (_q *ProjectQuery) sqlQuery(ctx context.Context) *sql.Selector { if _q.ctx.Unique != nil && *_q.ctx.Unique { selector.Distinct() } + for _, m := range _q.modifiers { + m(selector) + } for _, p := range _q.predicates { p(selector) } @@ -516,6 +527,32 @@ func (_q *ProjectQuery) sqlQuery(ctx context.Context) *sql.Selector { return selector } +// ForUpdate locks the selected rows against concurrent updates, and prevent them from being +// updated, deleted or "selected ... for update" by other sessions, until the transaction is +// either committed or rolled-back. +func (_q *ProjectQuery) ForUpdate(opts ...sql.LockOption) *ProjectQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForUpdate(opts...) + }) + return _q +} + +// ForShare behaves similarly to ForUpdate, except that it acquires a shared mode lock +// on any rows that are read. Other sessions can read the rows, but cannot modify them +// until your transaction commits. +func (_q *ProjectQuery) ForShare(opts ...sql.LockOption) *ProjectQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForShare(opts...) + }) + return _q +} + // ProjectGroupBy is the group-by builder for Project entities. type ProjectGroupBy struct { selector diff --git a/pkg/ent/project_update.go b/pkg/ent/project_update.go index 23bc242da..81ada5256 100644 --- a/pkg/ent/project_update.go +++ b/pkg/ent/project_update.go @@ -78,6 +78,26 @@ func (_u *ProjectUpdate) ClearGitRemote() *ProjectUpdate { return _u } +// SetDefaultRuntimeBrokerID sets the "default_runtime_broker_id" field. +func (_u *ProjectUpdate) SetDefaultRuntimeBrokerID(v string) *ProjectUpdate { + _u.mutation.SetDefaultRuntimeBrokerID(v) + return _u +} + +// SetNillableDefaultRuntimeBrokerID sets the "default_runtime_broker_id" field if the given value is not nil. +func (_u *ProjectUpdate) SetNillableDefaultRuntimeBrokerID(v *string) *ProjectUpdate { + if v != nil { + _u.SetDefaultRuntimeBrokerID(*v) + } + return _u +} + +// ClearDefaultRuntimeBrokerID clears the value of the "default_runtime_broker_id" field. +func (_u *ProjectUpdate) ClearDefaultRuntimeBrokerID() *ProjectUpdate { + _u.mutation.ClearDefaultRuntimeBrokerID() + return _u +} + // SetLabels sets the "labels" field. func (_u *ProjectUpdate) SetLabels(v map[string]string) *ProjectUpdate { _u.mutation.SetLabels(v) @@ -102,6 +122,26 @@ func (_u *ProjectUpdate) ClearAnnotations() *ProjectUpdate { return _u } +// SetSharedDirs sets the "shared_dirs" field. +func (_u *ProjectUpdate) SetSharedDirs(v string) *ProjectUpdate { + _u.mutation.SetSharedDirs(v) + return _u +} + +// SetNillableSharedDirs sets the "shared_dirs" field if the given value is not nil. +func (_u *ProjectUpdate) SetNillableSharedDirs(v *string) *ProjectUpdate { + if v != nil { + _u.SetSharedDirs(*v) + } + return _u +} + +// ClearSharedDirs clears the value of the "shared_dirs" field. +func (_u *ProjectUpdate) ClearSharedDirs() *ProjectUpdate { + _u.mutation.ClearSharedDirs() + return _u +} + // SetUpdated sets the "updated" field. func (_u *ProjectUpdate) SetUpdated(v time.Time) *ProjectUpdate { _u.mutation.SetUpdated(v) @@ -162,6 +202,93 @@ func (_u *ProjectUpdate) SetNillableVisibility(v *string) *ProjectUpdate { return _u } +// SetGithubInstallationID sets the "github_installation_id" field. +func (_u *ProjectUpdate) SetGithubInstallationID(v int64) *ProjectUpdate { + _u.mutation.ResetGithubInstallationID() + _u.mutation.SetGithubInstallationID(v) + return _u +} + +// SetNillableGithubInstallationID sets the "github_installation_id" field if the given value is not nil. +func (_u *ProjectUpdate) SetNillableGithubInstallationID(v *int64) *ProjectUpdate { + if v != nil { + _u.SetGithubInstallationID(*v) + } + return _u +} + +// AddGithubInstallationID adds value to the "github_installation_id" field. +func (_u *ProjectUpdate) AddGithubInstallationID(v int64) *ProjectUpdate { + _u.mutation.AddGithubInstallationID(v) + return _u +} + +// ClearGithubInstallationID clears the value of the "github_installation_id" field. +func (_u *ProjectUpdate) ClearGithubInstallationID() *ProjectUpdate { + _u.mutation.ClearGithubInstallationID() + return _u +} + +// SetGithubPermissions sets the "github_permissions" field. +func (_u *ProjectUpdate) SetGithubPermissions(v string) *ProjectUpdate { + _u.mutation.SetGithubPermissions(v) + return _u +} + +// SetNillableGithubPermissions sets the "github_permissions" field if the given value is not nil. +func (_u *ProjectUpdate) SetNillableGithubPermissions(v *string) *ProjectUpdate { + if v != nil { + _u.SetGithubPermissions(*v) + } + return _u +} + +// ClearGithubPermissions clears the value of the "github_permissions" field. +func (_u *ProjectUpdate) ClearGithubPermissions() *ProjectUpdate { + _u.mutation.ClearGithubPermissions() + return _u +} + +// SetGithubAppStatus sets the "github_app_status" field. +func (_u *ProjectUpdate) SetGithubAppStatus(v string) *ProjectUpdate { + _u.mutation.SetGithubAppStatus(v) + return _u +} + +// SetNillableGithubAppStatus sets the "github_app_status" field if the given value is not nil. +func (_u *ProjectUpdate) SetNillableGithubAppStatus(v *string) *ProjectUpdate { + if v != nil { + _u.SetGithubAppStatus(*v) + } + return _u +} + +// ClearGithubAppStatus clears the value of the "github_app_status" field. +func (_u *ProjectUpdate) ClearGithubAppStatus() *ProjectUpdate { + _u.mutation.ClearGithubAppStatus() + return _u +} + +// SetGitIdentity sets the "git_identity" field. +func (_u *ProjectUpdate) SetGitIdentity(v string) *ProjectUpdate { + _u.mutation.SetGitIdentity(v) + return _u +} + +// SetNillableGitIdentity sets the "git_identity" field if the given value is not nil. +func (_u *ProjectUpdate) SetNillableGitIdentity(v *string) *ProjectUpdate { + if v != nil { + _u.SetGitIdentity(*v) + } + return _u +} + +// ClearGitIdentity clears the value of the "git_identity" field. +func (_u *ProjectUpdate) ClearGitIdentity() *ProjectUpdate { + _u.mutation.ClearGitIdentity() + return _u +} + // AddAgentIDs adds the "agents" edge to the Agent entity by IDs. func (_u *ProjectUpdate) AddAgentIDs(ids ...uuid.UUID) *ProjectUpdate { _u.mutation.AddAgentIDs(ids...) @@ -278,6 +405,12 @@ func (_u *ProjectUpdate) sqlSave(ctx context.Context) (_node int, err error) { if _u.mutation.GitRemoteCleared() { _spec.ClearField(project.FieldGitRemote, field.TypeString) } + if value, ok := _u.mutation.DefaultRuntimeBrokerID(); ok { + _spec.SetField(project.FieldDefaultRuntimeBrokerID, field.TypeString, value) + } + if _u.mutation.DefaultRuntimeBrokerIDCleared() { + _spec.ClearField(project.FieldDefaultRuntimeBrokerID, field.TypeString) + } if value, ok := _u.mutation.Labels(); ok { _spec.SetField(project.FieldLabels, field.TypeJSON, value) } @@ -290,6 +423,12 @@ func (_u *ProjectUpdate) sqlSave(ctx context.Context) (_node int, err error) { if _u.mutation.AnnotationsCleared() { _spec.ClearField(project.FieldAnnotations, field.TypeJSON) } + if value, ok := _u.mutation.SharedDirs(); ok { + _spec.SetField(project.FieldSharedDirs, field.TypeString, value) + } + if _u.mutation.SharedDirsCleared() { + _spec.ClearField(project.FieldSharedDirs, field.TypeString) + } if value, ok := _u.mutation.Updated(); ok { _spec.SetField(project.FieldUpdated, field.TypeTime, value) } @@ -308,6 +447,33 @@ func (_u *ProjectUpdate) sqlSave(ctx context.Context) (_node int, err error) { if value, ok := _u.mutation.Visibility(); ok { _spec.SetField(project.FieldVisibility, field.TypeString, value) } + if value, ok := _u.mutation.GithubInstallationID(); ok { + _spec.SetField(project.FieldGithubInstallationID, field.TypeInt64, value) + } + if value, ok := _u.mutation.AddedGithubInstallationID(); ok { + _spec.AddField(project.FieldGithubInstallationID, field.TypeInt64, value) + } + if _u.mutation.GithubInstallationIDCleared() { + _spec.ClearField(project.FieldGithubInstallationID, field.TypeInt64) + } + if value, ok := _u.mutation.GithubPermissions(); ok { + _spec.SetField(project.FieldGithubPermissions, field.TypeString, value) + } + if _u.mutation.GithubPermissionsCleared() { + _spec.ClearField(project.FieldGithubPermissions, field.TypeString) + } + if value, ok := _u.mutation.GithubAppStatus(); ok { + _spec.SetField(project.FieldGithubAppStatus, field.TypeString, value) + } + if _u.mutation.GithubAppStatusCleared() { + _spec.ClearField(project.FieldGithubAppStatus, field.TypeString) + } + if value, ok := _u.mutation.GitIdentity(); ok { + _spec.SetField(project.FieldGitIdentity, field.TypeString, value) + } + if _u.mutation.GitIdentityCleared() { + _spec.ClearField(project.FieldGitIdentity, field.TypeString) + } if _u.mutation.AgentsCleared() { edge := &sqlgraph.EdgeSpec{ Rel: sqlgraph.O2M, @@ -421,6 +587,26 @@ func (_u *ProjectUpdateOne) ClearGitRemote() *ProjectUpdateOne { return _u } +// SetDefaultRuntimeBrokerID sets the "default_runtime_broker_id" field. +func (_u *ProjectUpdateOne) SetDefaultRuntimeBrokerID(v string) *ProjectUpdateOne { + _u.mutation.SetDefaultRuntimeBrokerID(v) + return _u +} + +// SetNillableDefaultRuntimeBrokerID sets the "default_runtime_broker_id" field if the given value is not nil. +func (_u *ProjectUpdateOne) SetNillableDefaultRuntimeBrokerID(v *string) *ProjectUpdateOne { + if v != nil { + _u.SetDefaultRuntimeBrokerID(*v) + } + return _u +} + +// ClearDefaultRuntimeBrokerID clears the value of the "default_runtime_broker_id" field. +func (_u *ProjectUpdateOne) ClearDefaultRuntimeBrokerID() *ProjectUpdateOne { + _u.mutation.ClearDefaultRuntimeBrokerID() + return _u +} + // SetLabels sets the "labels" field. func (_u *ProjectUpdateOne) SetLabels(v map[string]string) *ProjectUpdateOne { _u.mutation.SetLabels(v) @@ -445,6 +631,26 @@ func (_u *ProjectUpdateOne) ClearAnnotations() *ProjectUpdateOne { return _u } +// SetSharedDirs sets the "shared_dirs" field. +func (_u *ProjectUpdateOne) SetSharedDirs(v string) *ProjectUpdateOne { + _u.mutation.SetSharedDirs(v) + return _u +} + +// SetNillableSharedDirs sets the "shared_dirs" field if the given value is not nil. +func (_u *ProjectUpdateOne) SetNillableSharedDirs(v *string) *ProjectUpdateOne { + if v != nil { + _u.SetSharedDirs(*v) + } + return _u +} + +// ClearSharedDirs clears the value of the "shared_dirs" field. +func (_u *ProjectUpdateOne) ClearSharedDirs() *ProjectUpdateOne { + _u.mutation.ClearSharedDirs() + return _u +} + // SetUpdated sets the "updated" field. func (_u *ProjectUpdateOne) SetUpdated(v time.Time) *ProjectUpdateOne { _u.mutation.SetUpdated(v) @@ -505,6 +711,93 @@ func (_u *ProjectUpdateOne) SetNillableVisibility(v *string) *ProjectUpdateOne { return _u } +// SetGithubInstallationID sets the "github_installation_id" field. +func (_u *ProjectUpdateOne) SetGithubInstallationID(v int64) *ProjectUpdateOne { + _u.mutation.ResetGithubInstallationID() + _u.mutation.SetGithubInstallationID(v) + return _u +} + +// SetNillableGithubInstallationID sets the "github_installation_id" field if the given value is not nil. +func (_u *ProjectUpdateOne) SetNillableGithubInstallationID(v *int64) *ProjectUpdateOne { + if v != nil { + _u.SetGithubInstallationID(*v) + } + return _u +} + +// AddGithubInstallationID adds value to the "github_installation_id" field. +func (_u *ProjectUpdateOne) AddGithubInstallationID(v int64) *ProjectUpdateOne { + _u.mutation.AddGithubInstallationID(v) + return _u +} + +// ClearGithubInstallationID clears the value of the "github_installation_id" field. +func (_u *ProjectUpdateOne) ClearGithubInstallationID() *ProjectUpdateOne { + _u.mutation.ClearGithubInstallationID() + return _u +} + +// SetGithubPermissions sets the "github_permissions" field. +func (_u *ProjectUpdateOne) SetGithubPermissions(v string) *ProjectUpdateOne { + _u.mutation.SetGithubPermissions(v) + return _u +} + +// SetNillableGithubPermissions sets the "github_permissions" field if the given value is not nil. +func (_u *ProjectUpdateOne) SetNillableGithubPermissions(v *string) *ProjectUpdateOne { + if v != nil { + _u.SetGithubPermissions(*v) + } + return _u +} + +// ClearGithubPermissions clears the value of the "github_permissions" field. +func (_u *ProjectUpdateOne) ClearGithubPermissions() *ProjectUpdateOne { + _u.mutation.ClearGithubPermissions() + return _u +} + +// SetGithubAppStatus sets the "github_app_status" field. +func (_u *ProjectUpdateOne) SetGithubAppStatus(v string) *ProjectUpdateOne { + _u.mutation.SetGithubAppStatus(v) + return _u +} + +// SetNillableGithubAppStatus sets the "github_app_status" field if the given value is not nil. +func (_u *ProjectUpdateOne) SetNillableGithubAppStatus(v *string) *ProjectUpdateOne { + if v != nil { + _u.SetGithubAppStatus(*v) + } + return _u +} + +// ClearGithubAppStatus clears the value of the "github_app_status" field. +func (_u *ProjectUpdateOne) ClearGithubAppStatus() *ProjectUpdateOne { + _u.mutation.ClearGithubAppStatus() + return _u +} + +// SetGitIdentity sets the "git_identity" field. +func (_u *ProjectUpdateOne) SetGitIdentity(v string) *ProjectUpdateOne { + _u.mutation.SetGitIdentity(v) + return _u +} + +// SetNillableGitIdentity sets the "git_identity" field if the given value is not nil. +func (_u *ProjectUpdateOne) SetNillableGitIdentity(v *string) *ProjectUpdateOne { + if v != nil { + _u.SetGitIdentity(*v) + } + return _u +} + +// ClearGitIdentity clears the value of the "git_identity" field. +func (_u *ProjectUpdateOne) ClearGitIdentity() *ProjectUpdateOne { + _u.mutation.ClearGitIdentity() + return _u +} + // AddAgentIDs adds the "agents" edge to the Agent entity by IDs. func (_u *ProjectUpdateOne) AddAgentIDs(ids ...uuid.UUID) *ProjectUpdateOne { _u.mutation.AddAgentIDs(ids...) @@ -651,6 +944,12 @@ func (_u *ProjectUpdateOne) sqlSave(ctx context.Context) (_node *Project, err er if _u.mutation.GitRemoteCleared() { _spec.ClearField(project.FieldGitRemote, field.TypeString) } + if value, ok := _u.mutation.DefaultRuntimeBrokerID(); ok { + _spec.SetField(project.FieldDefaultRuntimeBrokerID, field.TypeString, value) + } + if _u.mutation.DefaultRuntimeBrokerIDCleared() { + _spec.ClearField(project.FieldDefaultRuntimeBrokerID, field.TypeString) + } if value, ok := _u.mutation.Labels(); ok { _spec.SetField(project.FieldLabels, field.TypeJSON, value) } @@ -663,6 +962,12 @@ func (_u *ProjectUpdateOne) sqlSave(ctx context.Context) (_node *Project, err er if _u.mutation.AnnotationsCleared() { _spec.ClearField(project.FieldAnnotations, field.TypeJSON) } + if value, ok := _u.mutation.SharedDirs(); ok { + _spec.SetField(project.FieldSharedDirs, field.TypeString, value) + } + if _u.mutation.SharedDirsCleared() { + _spec.ClearField(project.FieldSharedDirs, field.TypeString) + } if value, ok := _u.mutation.Updated(); ok { _spec.SetField(project.FieldUpdated, field.TypeTime, value) } @@ -681,6 +986,33 @@ func (_u *ProjectUpdateOne) sqlSave(ctx context.Context) (_node *Project, err er if value, ok := _u.mutation.Visibility(); ok { _spec.SetField(project.FieldVisibility, field.TypeString, value) } + if value, ok := _u.mutation.GithubInstallationID(); ok { + _spec.SetField(project.FieldGithubInstallationID, field.TypeInt64, value) + } + if value, ok := _u.mutation.AddedGithubInstallationID(); ok { + _spec.AddField(project.FieldGithubInstallationID, field.TypeInt64, value) + } + if _u.mutation.GithubInstallationIDCleared() { + _spec.ClearField(project.FieldGithubInstallationID, field.TypeInt64) + } + if value, ok := _u.mutation.GithubPermissions(); ok { + _spec.SetField(project.FieldGithubPermissions, field.TypeString, value) + } + if _u.mutation.GithubPermissionsCleared() { + _spec.ClearField(project.FieldGithubPermissions, field.TypeString) + } + if value, ok := _u.mutation.GithubAppStatus(); ok { + _spec.SetField(project.FieldGithubAppStatus, field.TypeString, value) + } + if _u.mutation.GithubAppStatusCleared() { + _spec.ClearField(project.FieldGithubAppStatus, field.TypeString) + } + if value, ok := _u.mutation.GitIdentity(); ok { + _spec.SetField(project.FieldGitIdentity, field.TypeString, value) + } + if _u.mutation.GitIdentityCleared() { + _spec.ClearField(project.FieldGitIdentity, field.TypeString) + } if _u.mutation.AgentsCleared() { edge := &sqlgraph.EdgeSpec{ Rel: sqlgraph.O2M, diff --git a/pkg/ent/projectcontributor.go b/pkg/ent/projectcontributor.go new file mode 100644 index 000000000..927f1b119 --- /dev/null +++ b/pkg/ent/projectcontributor.go @@ -0,0 +1,212 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "fmt" + "strings" + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "github.com/GoogleCloudPlatform/scion/pkg/ent/projectcontributor" + "github.com/google/uuid" +) + +// ProjectContributor is the model entity for the ProjectContributor schema. +type ProjectContributor struct { + config `json:"-"` + // ID of the ent. + ID uuid.UUID `json:"id,omitempty"` + // ProjectID holds the value of the "project_id" field. + ProjectID uuid.UUID `json:"project_id,omitempty"` + // BrokerID holds the value of the "broker_id" field. + BrokerID uuid.UUID `json:"broker_id,omitempty"` + // BrokerName holds the value of the "broker_name" field. + BrokerName string `json:"broker_name,omitempty"` + // Mode holds the value of the "mode" field. + Mode string `json:"mode,omitempty"` + // Status holds the value of the "status" field. + Status string `json:"status,omitempty"` + // Profiles holds the value of the "profiles" field. + Profiles string `json:"profiles,omitempty"` + // LastSeen holds the value of the "last_seen" field. + LastSeen *time.Time `json:"last_seen,omitempty"` + // LocalPath holds the value of the "local_path" field. + LocalPath string `json:"local_path,omitempty"` + // LinkedBy holds the value of the "linked_by" field. + LinkedBy string `json:"linked_by,omitempty"` + // LinkedAt holds the value of the "linked_at" field. + LinkedAt *time.Time `json:"linked_at,omitempty"` + selectValues sql.SelectValues +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*ProjectContributor) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case projectcontributor.FieldBrokerName, projectcontributor.FieldMode, projectcontributor.FieldStatus, projectcontributor.FieldProfiles, projectcontributor.FieldLocalPath, projectcontributor.FieldLinkedBy: + values[i] = new(sql.NullString) + case projectcontributor.FieldLastSeen, projectcontributor.FieldLinkedAt: + values[i] = new(sql.NullTime) + case projectcontributor.FieldID, projectcontributor.FieldProjectID, projectcontributor.FieldBrokerID: + values[i] = new(uuid.UUID) + default: + values[i] = new(sql.UnknownType) + } + } + return values, nil +} + +// assignValues assigns the values that were returned from sql.Rows (after scanning) +// to the ProjectContributor fields. +func (_m *ProjectContributor) assignValues(columns []string, values []any) error { + if m, n := len(values), len(columns); m < n { + return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) + } + for i := range columns { + switch columns[i] { + case projectcontributor.FieldID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field id", values[i]) + } else if value != nil { + _m.ID = *value + } + case projectcontributor.FieldProjectID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field project_id", values[i]) + } else if value != nil { + _m.ProjectID = *value + } + case projectcontributor.FieldBrokerID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field broker_id", values[i]) + } else if value != nil { + _m.BrokerID = *value + } + case projectcontributor.FieldBrokerName: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field broker_name", values[i]) + } else if value.Valid { + _m.BrokerName = value.String + } + case projectcontributor.FieldMode: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field mode", values[i]) + } else if value.Valid { + _m.Mode = value.String + } + case projectcontributor.FieldStatus: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field status", values[i]) + } else if value.Valid { + _m.Status = value.String + } + case projectcontributor.FieldProfiles: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field profiles", values[i]) + } else if value.Valid { + _m.Profiles = value.String + } + case projectcontributor.FieldLastSeen: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field last_seen", values[i]) + } else if value.Valid { + _m.LastSeen = new(time.Time) + *_m.LastSeen = value.Time + } + case projectcontributor.FieldLocalPath: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field local_path", values[i]) + } else if value.Valid { + _m.LocalPath = value.String + } + case projectcontributor.FieldLinkedBy: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field linked_by", values[i]) + } else if value.Valid { + _m.LinkedBy = value.String + } + case projectcontributor.FieldLinkedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field linked_at", values[i]) + } else if value.Valid { + _m.LinkedAt = new(time.Time) + *_m.LinkedAt = value.Time + } + default: + _m.selectValues.Set(columns[i], values[i]) + } + } + return nil +} + +// Value returns the ent.Value that was dynamically selected and assigned to the ProjectContributor. +// This includes values selected through modifiers, order, etc. +func (_m *ProjectContributor) Value(name string) (ent.Value, error) { + return _m.selectValues.Get(name) +} + +// Update returns a builder for updating this ProjectContributor. +// Note that you need to call ProjectContributor.Unwrap() before calling this method if this ProjectContributor +// was returned from a transaction, and the transaction was committed or rolled back. +func (_m *ProjectContributor) Update() *ProjectContributorUpdateOne { + return NewProjectContributorClient(_m.config).UpdateOne(_m) +} + +// Unwrap unwraps the ProjectContributor entity that was returned from a transaction after it was closed, +// so that all future queries will be executed through the driver which created the transaction. +func (_m *ProjectContributor) Unwrap() *ProjectContributor { + _tx, ok := _m.config.driver.(*txDriver) + if !ok { + panic("ent: ProjectContributor is not a transactional entity") + } + _m.config.driver = _tx.drv + return _m +} + +// String implements the fmt.Stringer. +func (_m *ProjectContributor) String() string { + var builder strings.Builder + builder.WriteString("ProjectContributor(") + builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID)) + builder.WriteString("project_id=") + builder.WriteString(fmt.Sprintf("%v", _m.ProjectID)) + builder.WriteString(", ") + builder.WriteString("broker_id=") + builder.WriteString(fmt.Sprintf("%v", _m.BrokerID)) + builder.WriteString(", ") + builder.WriteString("broker_name=") + builder.WriteString(_m.BrokerName) + builder.WriteString(", ") + builder.WriteString("mode=") + builder.WriteString(_m.Mode) + builder.WriteString(", ") + builder.WriteString("status=") + builder.WriteString(_m.Status) + builder.WriteString(", ") + builder.WriteString("profiles=") + builder.WriteString(_m.Profiles) + builder.WriteString(", ") + if v := _m.LastSeen; v != nil { + builder.WriteString("last_seen=") + builder.WriteString(v.Format(time.ANSIC)) + } + builder.WriteString(", ") + builder.WriteString("local_path=") + builder.WriteString(_m.LocalPath) + builder.WriteString(", ") + builder.WriteString("linked_by=") + builder.WriteString(_m.LinkedBy) + builder.WriteString(", ") + if v := _m.LinkedAt; v != nil { + builder.WriteString("linked_at=") + builder.WriteString(v.Format(time.ANSIC)) + } + builder.WriteByte(')') + return builder.String() +} + +// ProjectContributors is a parsable slice of ProjectContributor. +type ProjectContributors []*ProjectContributor diff --git a/pkg/ent/projectcontributor/projectcontributor.go b/pkg/ent/projectcontributor/projectcontributor.go new file mode 100644 index 000000000..3e21bfd64 --- /dev/null +++ b/pkg/ent/projectcontributor/projectcontributor.go @@ -0,0 +1,131 @@ +// Code generated by ent, DO NOT EDIT. + +package projectcontributor + +import ( + "entgo.io/ent/dialect/sql" + "github.com/google/uuid" +) + +const ( + // Label holds the string label denoting the projectcontributor type in the database. + Label = "project_contributor" + // FieldID holds the string denoting the id field in the database. + FieldID = "id" + // FieldProjectID holds the string denoting the project_id field in the database. + FieldProjectID = "project_id" + // FieldBrokerID holds the string denoting the broker_id field in the database. + FieldBrokerID = "broker_id" + // FieldBrokerName holds the string denoting the broker_name field in the database. + FieldBrokerName = "broker_name" + // FieldMode holds the string denoting the mode field in the database. + FieldMode = "mode" + // FieldStatus holds the string denoting the status field in the database. + FieldStatus = "status" + // FieldProfiles holds the string denoting the profiles field in the database. + FieldProfiles = "profiles" + // FieldLastSeen holds the string denoting the last_seen field in the database. + FieldLastSeen = "last_seen" + // FieldLocalPath holds the string denoting the local_path field in the database. + FieldLocalPath = "local_path" + // FieldLinkedBy holds the string denoting the linked_by field in the database. + FieldLinkedBy = "linked_by" + // FieldLinkedAt holds the string denoting the linked_at field in the database. + FieldLinkedAt = "linked_at" + // Table holds the table name of the projectcontributor in the database. + Table = "project_contributors" +) + +// Columns holds all SQL columns for projectcontributor fields. +var Columns = []string{ + FieldID, + FieldProjectID, + FieldBrokerID, + FieldBrokerName, + FieldMode, + FieldStatus, + FieldProfiles, + FieldLastSeen, + FieldLocalPath, + FieldLinkedBy, + FieldLinkedAt, +} + +// ValidColumn reports if the column name is valid (part of the table columns). +func ValidColumn(column string) bool { + for i := range Columns { + if column == Columns[i] { + return true + } + } + return false +} + +var ( + // BrokerNameValidator is a validator for the "broker_name" field. It is called by the builders before save. + BrokerNameValidator func(string) error + // DefaultMode holds the default value on creation for the "mode" field. + DefaultMode string + // DefaultStatus holds the default value on creation for the "status" field. + DefaultStatus string + // DefaultID holds the default value on creation for the "id" field. + DefaultID func() uuid.UUID +) + +// OrderOption defines the ordering options for the ProjectContributor queries. +type OrderOption func(*sql.Selector) + +// ByID orders the results by the id field. +func ByID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldID, opts...).ToFunc() +} + +// ByProjectID orders the results by the project_id field. +func ByProjectID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldProjectID, opts...).ToFunc() +} + +// ByBrokerID orders the results by the broker_id field. +func ByBrokerID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldBrokerID, opts...).ToFunc() +} + +// ByBrokerName orders the results by the broker_name field. +func ByBrokerName(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldBrokerName, opts...).ToFunc() +} + +// ByMode orders the results by the mode field. +func ByMode(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldMode, opts...).ToFunc() +} + +// ByStatus orders the results by the status field. +func ByStatus(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldStatus, opts...).ToFunc() +} + +// ByProfiles orders the results by the profiles field. +func ByProfiles(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldProfiles, opts...).ToFunc() +} + +// ByLastSeen orders the results by the last_seen field. +func ByLastSeen(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldLastSeen, opts...).ToFunc() +} + +// ByLocalPath orders the results by the local_path field. +func ByLocalPath(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldLocalPath, opts...).ToFunc() +} + +// ByLinkedBy orders the results by the linked_by field. +func ByLinkedBy(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldLinkedBy, opts...).ToFunc() +} + +// ByLinkedAt orders the results by the linked_at field. +func ByLinkedAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldLinkedAt, opts...).ToFunc() +} diff --git a/pkg/ent/projectcontributor/where.go b/pkg/ent/projectcontributor/where.go new file mode 100644 index 000000000..d34c16ab5 --- /dev/null +++ b/pkg/ent/projectcontributor/where.go @@ -0,0 +1,721 @@ +// Code generated by ent, DO NOT EDIT. + +package projectcontributor + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/google/uuid" +) + +// ID filters vertices based on their ID field. +func ID(id uuid.UUID) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldEQ(FieldID, id)) +} + +// IDEQ applies the EQ predicate on the ID field. +func IDEQ(id uuid.UUID) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldEQ(FieldID, id)) +} + +// IDNEQ applies the NEQ predicate on the ID field. +func IDNEQ(id uuid.UUID) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldNEQ(FieldID, id)) +} + +// IDIn applies the In predicate on the ID field. +func IDIn(ids ...uuid.UUID) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldIn(FieldID, ids...)) +} + +// IDNotIn applies the NotIn predicate on the ID field. +func IDNotIn(ids ...uuid.UUID) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldNotIn(FieldID, ids...)) +} + +// IDGT applies the GT predicate on the ID field. +func IDGT(id uuid.UUID) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldGT(FieldID, id)) +} + +// IDGTE applies the GTE predicate on the ID field. +func IDGTE(id uuid.UUID) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldGTE(FieldID, id)) +} + +// IDLT applies the LT predicate on the ID field. +func IDLT(id uuid.UUID) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldLT(FieldID, id)) +} + +// IDLTE applies the LTE predicate on the ID field. +func IDLTE(id uuid.UUID) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldLTE(FieldID, id)) +} + +// ProjectID applies equality check predicate on the "project_id" field. It's identical to ProjectIDEQ. +func ProjectID(v uuid.UUID) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldEQ(FieldProjectID, v)) +} + +// BrokerID applies equality check predicate on the "broker_id" field. It's identical to BrokerIDEQ. +func BrokerID(v uuid.UUID) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldEQ(FieldBrokerID, v)) +} + +// BrokerName applies equality check predicate on the "broker_name" field. It's identical to BrokerNameEQ. +func BrokerName(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldEQ(FieldBrokerName, v)) +} + +// Mode applies equality check predicate on the "mode" field. It's identical to ModeEQ. +func Mode(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldEQ(FieldMode, v)) +} + +// Status applies equality check predicate on the "status" field. It's identical to StatusEQ. +func Status(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldEQ(FieldStatus, v)) +} + +// Profiles applies equality check predicate on the "profiles" field. It's identical to ProfilesEQ. +func Profiles(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldEQ(FieldProfiles, v)) +} + +// LastSeen applies equality check predicate on the "last_seen" field. It's identical to LastSeenEQ. +func LastSeen(v time.Time) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldEQ(FieldLastSeen, v)) +} + +// LocalPath applies equality check predicate on the "local_path" field. It's identical to LocalPathEQ. +func LocalPath(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldEQ(FieldLocalPath, v)) +} + +// LinkedBy applies equality check predicate on the "linked_by" field. It's identical to LinkedByEQ. +func LinkedBy(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldEQ(FieldLinkedBy, v)) +} + +// LinkedAt applies equality check predicate on the "linked_at" field. It's identical to LinkedAtEQ. +func LinkedAt(v time.Time) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldEQ(FieldLinkedAt, v)) +} + +// ProjectIDEQ applies the EQ predicate on the "project_id" field. +func ProjectIDEQ(v uuid.UUID) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldEQ(FieldProjectID, v)) +} + +// ProjectIDNEQ applies the NEQ predicate on the "project_id" field. +func ProjectIDNEQ(v uuid.UUID) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldNEQ(FieldProjectID, v)) +} + +// ProjectIDIn applies the In predicate on the "project_id" field. +func ProjectIDIn(vs ...uuid.UUID) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldIn(FieldProjectID, vs...)) +} + +// ProjectIDNotIn applies the NotIn predicate on the "project_id" field. +func ProjectIDNotIn(vs ...uuid.UUID) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldNotIn(FieldProjectID, vs...)) +} + +// ProjectIDGT applies the GT predicate on the "project_id" field. +func ProjectIDGT(v uuid.UUID) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldGT(FieldProjectID, v)) +} + +// ProjectIDGTE applies the GTE predicate on the "project_id" field. +func ProjectIDGTE(v uuid.UUID) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldGTE(FieldProjectID, v)) +} + +// ProjectIDLT applies the LT predicate on the "project_id" field. +func ProjectIDLT(v uuid.UUID) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldLT(FieldProjectID, v)) +} + +// ProjectIDLTE applies the LTE predicate on the "project_id" field. +func ProjectIDLTE(v uuid.UUID) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldLTE(FieldProjectID, v)) +} + +// BrokerIDEQ applies the EQ predicate on the "broker_id" field. +func BrokerIDEQ(v uuid.UUID) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldEQ(FieldBrokerID, v)) +} + +// BrokerIDNEQ applies the NEQ predicate on the "broker_id" field. +func BrokerIDNEQ(v uuid.UUID) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldNEQ(FieldBrokerID, v)) +} + +// BrokerIDIn applies the In predicate on the "broker_id" field. +func BrokerIDIn(vs ...uuid.UUID) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldIn(FieldBrokerID, vs...)) +} + +// BrokerIDNotIn applies the NotIn predicate on the "broker_id" field. +func BrokerIDNotIn(vs ...uuid.UUID) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldNotIn(FieldBrokerID, vs...)) +} + +// BrokerIDGT applies the GT predicate on the "broker_id" field. +func BrokerIDGT(v uuid.UUID) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldGT(FieldBrokerID, v)) +} + +// BrokerIDGTE applies the GTE predicate on the "broker_id" field. +func BrokerIDGTE(v uuid.UUID) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldGTE(FieldBrokerID, v)) +} + +// BrokerIDLT applies the LT predicate on the "broker_id" field. +func BrokerIDLT(v uuid.UUID) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldLT(FieldBrokerID, v)) +} + +// BrokerIDLTE applies the LTE predicate on the "broker_id" field. +func BrokerIDLTE(v uuid.UUID) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldLTE(FieldBrokerID, v)) +} + +// BrokerNameEQ applies the EQ predicate on the "broker_name" field. +func BrokerNameEQ(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldEQ(FieldBrokerName, v)) +} + +// BrokerNameNEQ applies the NEQ predicate on the "broker_name" field. +func BrokerNameNEQ(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldNEQ(FieldBrokerName, v)) +} + +// BrokerNameIn applies the In predicate on the "broker_name" field. +func BrokerNameIn(vs ...string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldIn(FieldBrokerName, vs...)) +} + +// BrokerNameNotIn applies the NotIn predicate on the "broker_name" field. +func BrokerNameNotIn(vs ...string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldNotIn(FieldBrokerName, vs...)) +} + +// BrokerNameGT applies the GT predicate on the "broker_name" field. +func BrokerNameGT(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldGT(FieldBrokerName, v)) +} + +// BrokerNameGTE applies the GTE predicate on the "broker_name" field. +func BrokerNameGTE(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldGTE(FieldBrokerName, v)) +} + +// BrokerNameLT applies the LT predicate on the "broker_name" field. +func BrokerNameLT(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldLT(FieldBrokerName, v)) +} + +// BrokerNameLTE applies the LTE predicate on the "broker_name" field. +func BrokerNameLTE(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldLTE(FieldBrokerName, v)) +} + +// BrokerNameContains applies the Contains predicate on the "broker_name" field. +func BrokerNameContains(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldContains(FieldBrokerName, v)) +} + +// BrokerNameHasPrefix applies the HasPrefix predicate on the "broker_name" field. +func BrokerNameHasPrefix(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldHasPrefix(FieldBrokerName, v)) +} + +// BrokerNameHasSuffix applies the HasSuffix predicate on the "broker_name" field. +func BrokerNameHasSuffix(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldHasSuffix(FieldBrokerName, v)) +} + +// BrokerNameEqualFold applies the EqualFold predicate on the "broker_name" field. +func BrokerNameEqualFold(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldEqualFold(FieldBrokerName, v)) +} + +// BrokerNameContainsFold applies the ContainsFold predicate on the "broker_name" field. +func BrokerNameContainsFold(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldContainsFold(FieldBrokerName, v)) +} + +// ModeEQ applies the EQ predicate on the "mode" field. +func ModeEQ(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldEQ(FieldMode, v)) +} + +// ModeNEQ applies the NEQ predicate on the "mode" field. +func ModeNEQ(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldNEQ(FieldMode, v)) +} + +// ModeIn applies the In predicate on the "mode" field. +func ModeIn(vs ...string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldIn(FieldMode, vs...)) +} + +// ModeNotIn applies the NotIn predicate on the "mode" field. +func ModeNotIn(vs ...string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldNotIn(FieldMode, vs...)) +} + +// ModeGT applies the GT predicate on the "mode" field. +func ModeGT(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldGT(FieldMode, v)) +} + +// ModeGTE applies the GTE predicate on the "mode" field. +func ModeGTE(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldGTE(FieldMode, v)) +} + +// ModeLT applies the LT predicate on the "mode" field. +func ModeLT(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldLT(FieldMode, v)) +} + +// ModeLTE applies the LTE predicate on the "mode" field. +func ModeLTE(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldLTE(FieldMode, v)) +} + +// ModeContains applies the Contains predicate on the "mode" field. +func ModeContains(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldContains(FieldMode, v)) +} + +// ModeHasPrefix applies the HasPrefix predicate on the "mode" field. +func ModeHasPrefix(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldHasPrefix(FieldMode, v)) +} + +// ModeHasSuffix applies the HasSuffix predicate on the "mode" field. +func ModeHasSuffix(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldHasSuffix(FieldMode, v)) +} + +// ModeEqualFold applies the EqualFold predicate on the "mode" field. +func ModeEqualFold(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldEqualFold(FieldMode, v)) +} + +// ModeContainsFold applies the ContainsFold predicate on the "mode" field. +func ModeContainsFold(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldContainsFold(FieldMode, v)) +} + +// StatusEQ applies the EQ predicate on the "status" field. +func StatusEQ(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldEQ(FieldStatus, v)) +} + +// StatusNEQ applies the NEQ predicate on the "status" field. +func StatusNEQ(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldNEQ(FieldStatus, v)) +} + +// StatusIn applies the In predicate on the "status" field. +func StatusIn(vs ...string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldIn(FieldStatus, vs...)) +} + +// StatusNotIn applies the NotIn predicate on the "status" field. +func StatusNotIn(vs ...string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldNotIn(FieldStatus, vs...)) +} + +// StatusGT applies the GT predicate on the "status" field. +func StatusGT(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldGT(FieldStatus, v)) +} + +// StatusGTE applies the GTE predicate on the "status" field. +func StatusGTE(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldGTE(FieldStatus, v)) +} + +// StatusLT applies the LT predicate on the "status" field. +func StatusLT(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldLT(FieldStatus, v)) +} + +// StatusLTE applies the LTE predicate on the "status" field. +func StatusLTE(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldLTE(FieldStatus, v)) +} + +// StatusContains applies the Contains predicate on the "status" field. +func StatusContains(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldContains(FieldStatus, v)) +} + +// StatusHasPrefix applies the HasPrefix predicate on the "status" field. +func StatusHasPrefix(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldHasPrefix(FieldStatus, v)) +} + +// StatusHasSuffix applies the HasSuffix predicate on the "status" field. +func StatusHasSuffix(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldHasSuffix(FieldStatus, v)) +} + +// StatusEqualFold applies the EqualFold predicate on the "status" field. +func StatusEqualFold(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldEqualFold(FieldStatus, v)) +} + +// StatusContainsFold applies the ContainsFold predicate on the "status" field. +func StatusContainsFold(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldContainsFold(FieldStatus, v)) +} + +// ProfilesEQ applies the EQ predicate on the "profiles" field. +func ProfilesEQ(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldEQ(FieldProfiles, v)) +} + +// ProfilesNEQ applies the NEQ predicate on the "profiles" field. +func ProfilesNEQ(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldNEQ(FieldProfiles, v)) +} + +// ProfilesIn applies the In predicate on the "profiles" field. +func ProfilesIn(vs ...string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldIn(FieldProfiles, vs...)) +} + +// ProfilesNotIn applies the NotIn predicate on the "profiles" field. +func ProfilesNotIn(vs ...string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldNotIn(FieldProfiles, vs...)) +} + +// ProfilesGT applies the GT predicate on the "profiles" field. +func ProfilesGT(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldGT(FieldProfiles, v)) +} + +// ProfilesGTE applies the GTE predicate on the "profiles" field. +func ProfilesGTE(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldGTE(FieldProfiles, v)) +} + +// ProfilesLT applies the LT predicate on the "profiles" field. +func ProfilesLT(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldLT(FieldProfiles, v)) +} + +// ProfilesLTE applies the LTE predicate on the "profiles" field. +func ProfilesLTE(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldLTE(FieldProfiles, v)) +} + +// ProfilesContains applies the Contains predicate on the "profiles" field. +func ProfilesContains(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldContains(FieldProfiles, v)) +} + +// ProfilesHasPrefix applies the HasPrefix predicate on the "profiles" field. +func ProfilesHasPrefix(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldHasPrefix(FieldProfiles, v)) +} + +// ProfilesHasSuffix applies the HasSuffix predicate on the "profiles" field. +func ProfilesHasSuffix(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldHasSuffix(FieldProfiles, v)) +} + +// ProfilesIsNil applies the IsNil predicate on the "profiles" field. +func ProfilesIsNil() predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldIsNull(FieldProfiles)) +} + +// ProfilesNotNil applies the NotNil predicate on the "profiles" field. +func ProfilesNotNil() predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldNotNull(FieldProfiles)) +} + +// ProfilesEqualFold applies the EqualFold predicate on the "profiles" field. +func ProfilesEqualFold(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldEqualFold(FieldProfiles, v)) +} + +// ProfilesContainsFold applies the ContainsFold predicate on the "profiles" field. +func ProfilesContainsFold(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldContainsFold(FieldProfiles, v)) +} + +// LastSeenEQ applies the EQ predicate on the "last_seen" field. +func LastSeenEQ(v time.Time) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldEQ(FieldLastSeen, v)) +} + +// LastSeenNEQ applies the NEQ predicate on the "last_seen" field. +func LastSeenNEQ(v time.Time) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldNEQ(FieldLastSeen, v)) +} + +// LastSeenIn applies the In predicate on the "last_seen" field. +func LastSeenIn(vs ...time.Time) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldIn(FieldLastSeen, vs...)) +} + +// LastSeenNotIn applies the NotIn predicate on the "last_seen" field. +func LastSeenNotIn(vs ...time.Time) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldNotIn(FieldLastSeen, vs...)) +} + +// LastSeenGT applies the GT predicate on the "last_seen" field. +func LastSeenGT(v time.Time) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldGT(FieldLastSeen, v)) +} + +// LastSeenGTE applies the GTE predicate on the "last_seen" field. +func LastSeenGTE(v time.Time) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldGTE(FieldLastSeen, v)) +} + +// LastSeenLT applies the LT predicate on the "last_seen" field. +func LastSeenLT(v time.Time) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldLT(FieldLastSeen, v)) +} + +// LastSeenLTE applies the LTE predicate on the "last_seen" field. +func LastSeenLTE(v time.Time) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldLTE(FieldLastSeen, v)) +} + +// LastSeenIsNil applies the IsNil predicate on the "last_seen" field. +func LastSeenIsNil() predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldIsNull(FieldLastSeen)) +} + +// LastSeenNotNil applies the NotNil predicate on the "last_seen" field. +func LastSeenNotNil() predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldNotNull(FieldLastSeen)) +} + +// LocalPathEQ applies the EQ predicate on the "local_path" field. +func LocalPathEQ(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldEQ(FieldLocalPath, v)) +} + +// LocalPathNEQ applies the NEQ predicate on the "local_path" field. +func LocalPathNEQ(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldNEQ(FieldLocalPath, v)) +} + +// LocalPathIn applies the In predicate on the "local_path" field. +func LocalPathIn(vs ...string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldIn(FieldLocalPath, vs...)) +} + +// LocalPathNotIn applies the NotIn predicate on the "local_path" field. +func LocalPathNotIn(vs ...string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldNotIn(FieldLocalPath, vs...)) +} + +// LocalPathGT applies the GT predicate on the "local_path" field. +func LocalPathGT(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldGT(FieldLocalPath, v)) +} + +// LocalPathGTE applies the GTE predicate on the "local_path" field. +func LocalPathGTE(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldGTE(FieldLocalPath, v)) +} + +// LocalPathLT applies the LT predicate on the "local_path" field. +func LocalPathLT(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldLT(FieldLocalPath, v)) +} + +// LocalPathLTE applies the LTE predicate on the "local_path" field. +func LocalPathLTE(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldLTE(FieldLocalPath, v)) +} + +// LocalPathContains applies the Contains predicate on the "local_path" field. +func LocalPathContains(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldContains(FieldLocalPath, v)) +} + +// LocalPathHasPrefix applies the HasPrefix predicate on the "local_path" field. +func LocalPathHasPrefix(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldHasPrefix(FieldLocalPath, v)) +} + +// LocalPathHasSuffix applies the HasSuffix predicate on the "local_path" field. +func LocalPathHasSuffix(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldHasSuffix(FieldLocalPath, v)) +} + +// LocalPathIsNil applies the IsNil predicate on the "local_path" field. +func LocalPathIsNil() predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldIsNull(FieldLocalPath)) +} + +// LocalPathNotNil applies the NotNil predicate on the "local_path" field. +func LocalPathNotNil() predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldNotNull(FieldLocalPath)) +} + +// LocalPathEqualFold applies the EqualFold predicate on the "local_path" field. +func LocalPathEqualFold(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldEqualFold(FieldLocalPath, v)) +} + +// LocalPathContainsFold applies the ContainsFold predicate on the "local_path" field. +func LocalPathContainsFold(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldContainsFold(FieldLocalPath, v)) +} + +// LinkedByEQ applies the EQ predicate on the "linked_by" field. +func LinkedByEQ(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldEQ(FieldLinkedBy, v)) +} + +// LinkedByNEQ applies the NEQ predicate on the "linked_by" field. +func LinkedByNEQ(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldNEQ(FieldLinkedBy, v)) +} + +// LinkedByIn applies the In predicate on the "linked_by" field. +func LinkedByIn(vs ...string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldIn(FieldLinkedBy, vs...)) +} + +// LinkedByNotIn applies the NotIn predicate on the "linked_by" field. +func LinkedByNotIn(vs ...string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldNotIn(FieldLinkedBy, vs...)) +} + +// LinkedByGT applies the GT predicate on the "linked_by" field. +func LinkedByGT(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldGT(FieldLinkedBy, v)) +} + +// LinkedByGTE applies the GTE predicate on the "linked_by" field. +func LinkedByGTE(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldGTE(FieldLinkedBy, v)) +} + +// LinkedByLT applies the LT predicate on the "linked_by" field. +func LinkedByLT(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldLT(FieldLinkedBy, v)) +} + +// LinkedByLTE applies the LTE predicate on the "linked_by" field. +func LinkedByLTE(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldLTE(FieldLinkedBy, v)) +} + +// LinkedByContains applies the Contains predicate on the "linked_by" field. +func LinkedByContains(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldContains(FieldLinkedBy, v)) +} + +// LinkedByHasPrefix applies the HasPrefix predicate on the "linked_by" field. +func LinkedByHasPrefix(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldHasPrefix(FieldLinkedBy, v)) +} + +// LinkedByHasSuffix applies the HasSuffix predicate on the "linked_by" field. +func LinkedByHasSuffix(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldHasSuffix(FieldLinkedBy, v)) +} + +// LinkedByIsNil applies the IsNil predicate on the "linked_by" field. +func LinkedByIsNil() predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldIsNull(FieldLinkedBy)) +} + +// LinkedByNotNil applies the NotNil predicate on the "linked_by" field. +func LinkedByNotNil() predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldNotNull(FieldLinkedBy)) +} + +// LinkedByEqualFold applies the EqualFold predicate on the "linked_by" field. +func LinkedByEqualFold(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldEqualFold(FieldLinkedBy, v)) +} + +// LinkedByContainsFold applies the ContainsFold predicate on the "linked_by" field. +func LinkedByContainsFold(v string) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldContainsFold(FieldLinkedBy, v)) +} + +// LinkedAtEQ applies the EQ predicate on the "linked_at" field. +func LinkedAtEQ(v time.Time) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldEQ(FieldLinkedAt, v)) +} + +// LinkedAtNEQ applies the NEQ predicate on the "linked_at" field. +func LinkedAtNEQ(v time.Time) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldNEQ(FieldLinkedAt, v)) +} + +// LinkedAtIn applies the In predicate on the "linked_at" field. +func LinkedAtIn(vs ...time.Time) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldIn(FieldLinkedAt, vs...)) +} + +// LinkedAtNotIn applies the NotIn predicate on the "linked_at" field. +func LinkedAtNotIn(vs ...time.Time) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldNotIn(FieldLinkedAt, vs...)) +} + +// LinkedAtGT applies the GT predicate on the "linked_at" field. +func LinkedAtGT(v time.Time) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldGT(FieldLinkedAt, v)) +} + +// LinkedAtGTE applies the GTE predicate on the "linked_at" field. +func LinkedAtGTE(v time.Time) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldGTE(FieldLinkedAt, v)) +} + +// LinkedAtLT applies the LT predicate on the "linked_at" field. +func LinkedAtLT(v time.Time) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldLT(FieldLinkedAt, v)) +} + +// LinkedAtLTE applies the LTE predicate on the "linked_at" field. +func LinkedAtLTE(v time.Time) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldLTE(FieldLinkedAt, v)) +} + +// LinkedAtIsNil applies the IsNil predicate on the "linked_at" field. +func LinkedAtIsNil() predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldIsNull(FieldLinkedAt)) +} + +// LinkedAtNotNil applies the NotNil predicate on the "linked_at" field. +func LinkedAtNotNil() predicate.ProjectContributor { + return predicate.ProjectContributor(sql.FieldNotNull(FieldLinkedAt)) +} + +// And groups predicates with the AND operator between them. +func And(predicates ...predicate.ProjectContributor) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.AndPredicates(predicates...)) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.ProjectContributor) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.OrPredicates(predicates...)) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.ProjectContributor) predicate.ProjectContributor { + return predicate.ProjectContributor(sql.NotPredicates(p)) +} diff --git a/pkg/ent/projectcontributor_create.go b/pkg/ent/projectcontributor_create.go new file mode 100644 index 000000000..d2b1620d4 --- /dev/null +++ b/pkg/ent/projectcontributor_create.go @@ -0,0 +1,1140 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/projectcontributor" + "github.com/google/uuid" +) + +// ProjectContributorCreate is the builder for creating a ProjectContributor entity. +type ProjectContributorCreate struct { + config + mutation *ProjectContributorMutation + hooks []Hook + conflict []sql.ConflictOption +} + +// SetProjectID sets the "project_id" field. +func (_c *ProjectContributorCreate) SetProjectID(v uuid.UUID) *ProjectContributorCreate { + _c.mutation.SetProjectID(v) + return _c +} + +// SetBrokerID sets the "broker_id" field. +func (_c *ProjectContributorCreate) SetBrokerID(v uuid.UUID) *ProjectContributorCreate { + _c.mutation.SetBrokerID(v) + return _c +} + +// SetBrokerName sets the "broker_name" field. +func (_c *ProjectContributorCreate) SetBrokerName(v string) *ProjectContributorCreate { + _c.mutation.SetBrokerName(v) + return _c +} + +// SetMode sets the "mode" field. +func (_c *ProjectContributorCreate) SetMode(v string) *ProjectContributorCreate { + _c.mutation.SetMode(v) + return _c +} + +// SetNillableMode sets the "mode" field if the given value is not nil. +func (_c *ProjectContributorCreate) SetNillableMode(v *string) *ProjectContributorCreate { + if v != nil { + _c.SetMode(*v) + } + return _c +} + +// SetStatus sets the "status" field. +func (_c *ProjectContributorCreate) SetStatus(v string) *ProjectContributorCreate { + _c.mutation.SetStatus(v) + return _c +} + +// SetNillableStatus sets the "status" field if the given value is not nil. +func (_c *ProjectContributorCreate) SetNillableStatus(v *string) *ProjectContributorCreate { + if v != nil { + _c.SetStatus(*v) + } + return _c +} + +// SetProfiles sets the "profiles" field. +func (_c *ProjectContributorCreate) SetProfiles(v string) *ProjectContributorCreate { + _c.mutation.SetProfiles(v) + return _c +} + +// SetNillableProfiles sets the "profiles" field if the given value is not nil. +func (_c *ProjectContributorCreate) SetNillableProfiles(v *string) *ProjectContributorCreate { + if v != nil { + _c.SetProfiles(*v) + } + return _c +} + +// SetLastSeen sets the "last_seen" field. +func (_c *ProjectContributorCreate) SetLastSeen(v time.Time) *ProjectContributorCreate { + _c.mutation.SetLastSeen(v) + return _c +} + +// SetNillableLastSeen sets the "last_seen" field if the given value is not nil. +func (_c *ProjectContributorCreate) SetNillableLastSeen(v *time.Time) *ProjectContributorCreate { + if v != nil { + _c.SetLastSeen(*v) + } + return _c +} + +// SetLocalPath sets the "local_path" field. +func (_c *ProjectContributorCreate) SetLocalPath(v string) *ProjectContributorCreate { + _c.mutation.SetLocalPath(v) + return _c +} + +// SetNillableLocalPath sets the "local_path" field if the given value is not nil. +func (_c *ProjectContributorCreate) SetNillableLocalPath(v *string) *ProjectContributorCreate { + if v != nil { + _c.SetLocalPath(*v) + } + return _c +} + +// SetLinkedBy sets the "linked_by" field. +func (_c *ProjectContributorCreate) SetLinkedBy(v string) *ProjectContributorCreate { + _c.mutation.SetLinkedBy(v) + return _c +} + +// SetNillableLinkedBy sets the "linked_by" field if the given value is not nil. +func (_c *ProjectContributorCreate) SetNillableLinkedBy(v *string) *ProjectContributorCreate { + if v != nil { + _c.SetLinkedBy(*v) + } + return _c +} + +// SetLinkedAt sets the "linked_at" field. +func (_c *ProjectContributorCreate) SetLinkedAt(v time.Time) *ProjectContributorCreate { + _c.mutation.SetLinkedAt(v) + return _c +} + +// SetNillableLinkedAt sets the "linked_at" field if the given value is not nil. +func (_c *ProjectContributorCreate) SetNillableLinkedAt(v *time.Time) *ProjectContributorCreate { + if v != nil { + _c.SetLinkedAt(*v) + } + return _c +} + +// SetID sets the "id" field. +func (_c *ProjectContributorCreate) SetID(v uuid.UUID) *ProjectContributorCreate { + _c.mutation.SetID(v) + return _c +} + +// SetNillableID sets the "id" field if the given value is not nil. +func (_c *ProjectContributorCreate) SetNillableID(v *uuid.UUID) *ProjectContributorCreate { + if v != nil { + _c.SetID(*v) + } + return _c +} + +// Mutation returns the ProjectContributorMutation object of the builder. +func (_c *ProjectContributorCreate) Mutation() *ProjectContributorMutation { + return _c.mutation +} + +// Save creates the ProjectContributor in the database. +func (_c *ProjectContributorCreate) Save(ctx context.Context) (*ProjectContributor, error) { + _c.defaults() + return withHooks(ctx, _c.sqlSave, _c.mutation, _c.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (_c *ProjectContributorCreate) SaveX(ctx context.Context) *ProjectContributor { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *ProjectContributorCreate) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *ProjectContributorCreate) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_c *ProjectContributorCreate) defaults() { + if _, ok := _c.mutation.Mode(); !ok { + v := projectcontributor.DefaultMode + _c.mutation.SetMode(v) + } + if _, ok := _c.mutation.Status(); !ok { + v := projectcontributor.DefaultStatus + _c.mutation.SetStatus(v) + } + if _, ok := _c.mutation.ID(); !ok { + v := projectcontributor.DefaultID() + _c.mutation.SetID(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_c *ProjectContributorCreate) check() error { + if _, ok := _c.mutation.ProjectID(); !ok { + return &ValidationError{Name: "project_id", err: errors.New(`ent: missing required field "ProjectContributor.project_id"`)} + } + if _, ok := _c.mutation.BrokerID(); !ok { + return &ValidationError{Name: "broker_id", err: errors.New(`ent: missing required field "ProjectContributor.broker_id"`)} + } + if _, ok := _c.mutation.BrokerName(); !ok { + return &ValidationError{Name: "broker_name", err: errors.New(`ent: missing required field "ProjectContributor.broker_name"`)} + } + if v, ok := _c.mutation.BrokerName(); ok { + if err := projectcontributor.BrokerNameValidator(v); err != nil { + return &ValidationError{Name: "broker_name", err: fmt.Errorf(`ent: validator failed for field "ProjectContributor.broker_name": %w`, err)} + } + } + if _, ok := _c.mutation.Mode(); !ok { + return &ValidationError{Name: "mode", err: errors.New(`ent: missing required field "ProjectContributor.mode"`)} + } + if _, ok := _c.mutation.Status(); !ok { + return &ValidationError{Name: "status", err: errors.New(`ent: missing required field "ProjectContributor.status"`)} + } + return nil +} + +func (_c *ProjectContributorCreate) sqlSave(ctx context.Context) (*ProjectContributor, error) { + if err := _c.check(); err != nil { + return nil, err + } + _node, _spec := _c.createSpec() + if err := sqlgraph.CreateNode(ctx, _c.driver, _spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + if _spec.ID.Value != nil { + if id, ok := _spec.ID.Value.(*uuid.UUID); ok { + _node.ID = *id + } else if err := _node.ID.Scan(_spec.ID.Value); err != nil { + return nil, err + } + } + _c.mutation.id = &_node.ID + _c.mutation.done = true + return _node, nil +} + +func (_c *ProjectContributorCreate) createSpec() (*ProjectContributor, *sqlgraph.CreateSpec) { + var ( + _node = &ProjectContributor{config: _c.config} + _spec = sqlgraph.NewCreateSpec(projectcontributor.Table, sqlgraph.NewFieldSpec(projectcontributor.FieldID, field.TypeUUID)) + ) + _spec.OnConflict = _c.conflict + if id, ok := _c.mutation.ID(); ok { + _node.ID = id + _spec.ID.Value = &id + } + if value, ok := _c.mutation.ProjectID(); ok { + _spec.SetField(projectcontributor.FieldProjectID, field.TypeUUID, value) + _node.ProjectID = value + } + if value, ok := _c.mutation.BrokerID(); ok { + _spec.SetField(projectcontributor.FieldBrokerID, field.TypeUUID, value) + _node.BrokerID = value + } + if value, ok := _c.mutation.BrokerName(); ok { + _spec.SetField(projectcontributor.FieldBrokerName, field.TypeString, value) + _node.BrokerName = value + } + if value, ok := _c.mutation.Mode(); ok { + _spec.SetField(projectcontributor.FieldMode, field.TypeString, value) + _node.Mode = value + } + if value, ok := _c.mutation.Status(); ok { + _spec.SetField(projectcontributor.FieldStatus, field.TypeString, value) + _node.Status = value + } + if value, ok := _c.mutation.Profiles(); ok { + _spec.SetField(projectcontributor.FieldProfiles, field.TypeString, value) + _node.Profiles = value + } + if value, ok := _c.mutation.LastSeen(); ok { + _spec.SetField(projectcontributor.FieldLastSeen, field.TypeTime, value) + _node.LastSeen = &value + } + if value, ok := _c.mutation.LocalPath(); ok { + _spec.SetField(projectcontributor.FieldLocalPath, field.TypeString, value) + _node.LocalPath = value + } + if value, ok := _c.mutation.LinkedBy(); ok { + _spec.SetField(projectcontributor.FieldLinkedBy, field.TypeString, value) + _node.LinkedBy = value + } + if value, ok := _c.mutation.LinkedAt(); ok { + _spec.SetField(projectcontributor.FieldLinkedAt, field.TypeTime, value) + _node.LinkedAt = &value + } + return _node, _spec +} + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.ProjectContributor.Create(). +// SetProjectID(v). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.ProjectContributorUpsert) { +// SetProjectID(v+v). +// }). +// Exec(ctx) +func (_c *ProjectContributorCreate) OnConflict(opts ...sql.ConflictOption) *ProjectContributorUpsertOne { + _c.conflict = opts + return &ProjectContributorUpsertOne{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.ProjectContributor.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *ProjectContributorCreate) OnConflictColumns(columns ...string) *ProjectContributorUpsertOne { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &ProjectContributorUpsertOne{ + create: _c, + } +} + +type ( + // ProjectContributorUpsertOne is the builder for "upsert"-ing + // one ProjectContributor node. + ProjectContributorUpsertOne struct { + create *ProjectContributorCreate + } + + // ProjectContributorUpsert is the "OnConflict" setter. + ProjectContributorUpsert struct { + *sql.UpdateSet + } +) + +// SetProjectID sets the "project_id" field. +func (u *ProjectContributorUpsert) SetProjectID(v uuid.UUID) *ProjectContributorUpsert { + u.Set(projectcontributor.FieldProjectID, v) + return u +} + +// UpdateProjectID sets the "project_id" field to the value that was provided on create. +func (u *ProjectContributorUpsert) UpdateProjectID() *ProjectContributorUpsert { + u.SetExcluded(projectcontributor.FieldProjectID) + return u +} + +// SetBrokerID sets the "broker_id" field. +func (u *ProjectContributorUpsert) SetBrokerID(v uuid.UUID) *ProjectContributorUpsert { + u.Set(projectcontributor.FieldBrokerID, v) + return u +} + +// UpdateBrokerID sets the "broker_id" field to the value that was provided on create. +func (u *ProjectContributorUpsert) UpdateBrokerID() *ProjectContributorUpsert { + u.SetExcluded(projectcontributor.FieldBrokerID) + return u +} + +// SetBrokerName sets the "broker_name" field. +func (u *ProjectContributorUpsert) SetBrokerName(v string) *ProjectContributorUpsert { + u.Set(projectcontributor.FieldBrokerName, v) + return u +} + +// UpdateBrokerName sets the "broker_name" field to the value that was provided on create. +func (u *ProjectContributorUpsert) UpdateBrokerName() *ProjectContributorUpsert { + u.SetExcluded(projectcontributor.FieldBrokerName) + return u +} + +// SetMode sets the "mode" field. +func (u *ProjectContributorUpsert) SetMode(v string) *ProjectContributorUpsert { + u.Set(projectcontributor.FieldMode, v) + return u +} + +// UpdateMode sets the "mode" field to the value that was provided on create. +func (u *ProjectContributorUpsert) UpdateMode() *ProjectContributorUpsert { + u.SetExcluded(projectcontributor.FieldMode) + return u +} + +// SetStatus sets the "status" field. +func (u *ProjectContributorUpsert) SetStatus(v string) *ProjectContributorUpsert { + u.Set(projectcontributor.FieldStatus, v) + return u +} + +// UpdateStatus sets the "status" field to the value that was provided on create. +func (u *ProjectContributorUpsert) UpdateStatus() *ProjectContributorUpsert { + u.SetExcluded(projectcontributor.FieldStatus) + return u +} + +// SetProfiles sets the "profiles" field. +func (u *ProjectContributorUpsert) SetProfiles(v string) *ProjectContributorUpsert { + u.Set(projectcontributor.FieldProfiles, v) + return u +} + +// UpdateProfiles sets the "profiles" field to the value that was provided on create. +func (u *ProjectContributorUpsert) UpdateProfiles() *ProjectContributorUpsert { + u.SetExcluded(projectcontributor.FieldProfiles) + return u +} + +// ClearProfiles clears the value of the "profiles" field. +func (u *ProjectContributorUpsert) ClearProfiles() *ProjectContributorUpsert { + u.SetNull(projectcontributor.FieldProfiles) + return u +} + +// SetLastSeen sets the "last_seen" field. +func (u *ProjectContributorUpsert) SetLastSeen(v time.Time) *ProjectContributorUpsert { + u.Set(projectcontributor.FieldLastSeen, v) + return u +} + +// UpdateLastSeen sets the "last_seen" field to the value that was provided on create. +func (u *ProjectContributorUpsert) UpdateLastSeen() *ProjectContributorUpsert { + u.SetExcluded(projectcontributor.FieldLastSeen) + return u +} + +// ClearLastSeen clears the value of the "last_seen" field. +func (u *ProjectContributorUpsert) ClearLastSeen() *ProjectContributorUpsert { + u.SetNull(projectcontributor.FieldLastSeen) + return u +} + +// SetLocalPath sets the "local_path" field. +func (u *ProjectContributorUpsert) SetLocalPath(v string) *ProjectContributorUpsert { + u.Set(projectcontributor.FieldLocalPath, v) + return u +} + +// UpdateLocalPath sets the "local_path" field to the value that was provided on create. +func (u *ProjectContributorUpsert) UpdateLocalPath() *ProjectContributorUpsert { + u.SetExcluded(projectcontributor.FieldLocalPath) + return u +} + +// ClearLocalPath clears the value of the "local_path" field. +func (u *ProjectContributorUpsert) ClearLocalPath() *ProjectContributorUpsert { + u.SetNull(projectcontributor.FieldLocalPath) + return u +} + +// SetLinkedBy sets the "linked_by" field. +func (u *ProjectContributorUpsert) SetLinkedBy(v string) *ProjectContributorUpsert { + u.Set(projectcontributor.FieldLinkedBy, v) + return u +} + +// UpdateLinkedBy sets the "linked_by" field to the value that was provided on create. +func (u *ProjectContributorUpsert) UpdateLinkedBy() *ProjectContributorUpsert { + u.SetExcluded(projectcontributor.FieldLinkedBy) + return u +} + +// ClearLinkedBy clears the value of the "linked_by" field. +func (u *ProjectContributorUpsert) ClearLinkedBy() *ProjectContributorUpsert { + u.SetNull(projectcontributor.FieldLinkedBy) + return u +} + +// SetLinkedAt sets the "linked_at" field. +func (u *ProjectContributorUpsert) SetLinkedAt(v time.Time) *ProjectContributorUpsert { + u.Set(projectcontributor.FieldLinkedAt, v) + return u +} + +// UpdateLinkedAt sets the "linked_at" field to the value that was provided on create. +func (u *ProjectContributorUpsert) UpdateLinkedAt() *ProjectContributorUpsert { + u.SetExcluded(projectcontributor.FieldLinkedAt) + return u +} + +// ClearLinkedAt clears the value of the "linked_at" field. +func (u *ProjectContributorUpsert) ClearLinkedAt() *ProjectContributorUpsert { + u.SetNull(projectcontributor.FieldLinkedAt) + return u +} + +// UpdateNewValues updates the mutable fields using the new values that were set on create except the ID field. +// Using this option is equivalent to using: +// +// client.ProjectContributor.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(projectcontributor.FieldID) +// }), +// ). +// Exec(ctx) +func (u *ProjectContributorUpsertOne) UpdateNewValues() *ProjectContributorUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + if _, exists := u.create.mutation.ID(); exists { + s.SetIgnore(projectcontributor.FieldID) + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.ProjectContributor.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *ProjectContributorUpsertOne) Ignore() *ProjectContributorUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *ProjectContributorUpsertOne) DoNothing() *ProjectContributorUpsertOne { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the ProjectContributorCreate.OnConflict +// documentation for more info. +func (u *ProjectContributorUpsertOne) Update(set func(*ProjectContributorUpsert)) *ProjectContributorUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&ProjectContributorUpsert{UpdateSet: update}) + })) + return u +} + +// SetProjectID sets the "project_id" field. +func (u *ProjectContributorUpsertOne) SetProjectID(v uuid.UUID) *ProjectContributorUpsertOne { + return u.Update(func(s *ProjectContributorUpsert) { + s.SetProjectID(v) + }) +} + +// UpdateProjectID sets the "project_id" field to the value that was provided on create. +func (u *ProjectContributorUpsertOne) UpdateProjectID() *ProjectContributorUpsertOne { + return u.Update(func(s *ProjectContributorUpsert) { + s.UpdateProjectID() + }) +} + +// SetBrokerID sets the "broker_id" field. +func (u *ProjectContributorUpsertOne) SetBrokerID(v uuid.UUID) *ProjectContributorUpsertOne { + return u.Update(func(s *ProjectContributorUpsert) { + s.SetBrokerID(v) + }) +} + +// UpdateBrokerID sets the "broker_id" field to the value that was provided on create. +func (u *ProjectContributorUpsertOne) UpdateBrokerID() *ProjectContributorUpsertOne { + return u.Update(func(s *ProjectContributorUpsert) { + s.UpdateBrokerID() + }) +} + +// SetBrokerName sets the "broker_name" field. +func (u *ProjectContributorUpsertOne) SetBrokerName(v string) *ProjectContributorUpsertOne { + return u.Update(func(s *ProjectContributorUpsert) { + s.SetBrokerName(v) + }) +} + +// UpdateBrokerName sets the "broker_name" field to the value that was provided on create. +func (u *ProjectContributorUpsertOne) UpdateBrokerName() *ProjectContributorUpsertOne { + return u.Update(func(s *ProjectContributorUpsert) { + s.UpdateBrokerName() + }) +} + +// SetMode sets the "mode" field. +func (u *ProjectContributorUpsertOne) SetMode(v string) *ProjectContributorUpsertOne { + return u.Update(func(s *ProjectContributorUpsert) { + s.SetMode(v) + }) +} + +// UpdateMode sets the "mode" field to the value that was provided on create. +func (u *ProjectContributorUpsertOne) UpdateMode() *ProjectContributorUpsertOne { + return u.Update(func(s *ProjectContributorUpsert) { + s.UpdateMode() + }) +} + +// SetStatus sets the "status" field. +func (u *ProjectContributorUpsertOne) SetStatus(v string) *ProjectContributorUpsertOne { + return u.Update(func(s *ProjectContributorUpsert) { + s.SetStatus(v) + }) +} + +// UpdateStatus sets the "status" field to the value that was provided on create. +func (u *ProjectContributorUpsertOne) UpdateStatus() *ProjectContributorUpsertOne { + return u.Update(func(s *ProjectContributorUpsert) { + s.UpdateStatus() + }) +} + +// SetProfiles sets the "profiles" field. +func (u *ProjectContributorUpsertOne) SetProfiles(v string) *ProjectContributorUpsertOne { + return u.Update(func(s *ProjectContributorUpsert) { + s.SetProfiles(v) + }) +} + +// UpdateProfiles sets the "profiles" field to the value that was provided on create. +func (u *ProjectContributorUpsertOne) UpdateProfiles() *ProjectContributorUpsertOne { + return u.Update(func(s *ProjectContributorUpsert) { + s.UpdateProfiles() + }) +} + +// ClearProfiles clears the value of the "profiles" field. +func (u *ProjectContributorUpsertOne) ClearProfiles() *ProjectContributorUpsertOne { + return u.Update(func(s *ProjectContributorUpsert) { + s.ClearProfiles() + }) +} + +// SetLastSeen sets the "last_seen" field. +func (u *ProjectContributorUpsertOne) SetLastSeen(v time.Time) *ProjectContributorUpsertOne { + return u.Update(func(s *ProjectContributorUpsert) { + s.SetLastSeen(v) + }) +} + +// UpdateLastSeen sets the "last_seen" field to the value that was provided on create. +func (u *ProjectContributorUpsertOne) UpdateLastSeen() *ProjectContributorUpsertOne { + return u.Update(func(s *ProjectContributorUpsert) { + s.UpdateLastSeen() + }) +} + +// ClearLastSeen clears the value of the "last_seen" field. +func (u *ProjectContributorUpsertOne) ClearLastSeen() *ProjectContributorUpsertOne { + return u.Update(func(s *ProjectContributorUpsert) { + s.ClearLastSeen() + }) +} + +// SetLocalPath sets the "local_path" field. +func (u *ProjectContributorUpsertOne) SetLocalPath(v string) *ProjectContributorUpsertOne { + return u.Update(func(s *ProjectContributorUpsert) { + s.SetLocalPath(v) + }) +} + +// UpdateLocalPath sets the "local_path" field to the value that was provided on create. +func (u *ProjectContributorUpsertOne) UpdateLocalPath() *ProjectContributorUpsertOne { + return u.Update(func(s *ProjectContributorUpsert) { + s.UpdateLocalPath() + }) +} + +// ClearLocalPath clears the value of the "local_path" field. +func (u *ProjectContributorUpsertOne) ClearLocalPath() *ProjectContributorUpsertOne { + return u.Update(func(s *ProjectContributorUpsert) { + s.ClearLocalPath() + }) +} + +// SetLinkedBy sets the "linked_by" field. +func (u *ProjectContributorUpsertOne) SetLinkedBy(v string) *ProjectContributorUpsertOne { + return u.Update(func(s *ProjectContributorUpsert) { + s.SetLinkedBy(v) + }) +} + +// UpdateLinkedBy sets the "linked_by" field to the value that was provided on create. +func (u *ProjectContributorUpsertOne) UpdateLinkedBy() *ProjectContributorUpsertOne { + return u.Update(func(s *ProjectContributorUpsert) { + s.UpdateLinkedBy() + }) +} + +// ClearLinkedBy clears the value of the "linked_by" field. +func (u *ProjectContributorUpsertOne) ClearLinkedBy() *ProjectContributorUpsertOne { + return u.Update(func(s *ProjectContributorUpsert) { + s.ClearLinkedBy() + }) +} + +// SetLinkedAt sets the "linked_at" field. +func (u *ProjectContributorUpsertOne) SetLinkedAt(v time.Time) *ProjectContributorUpsertOne { + return u.Update(func(s *ProjectContributorUpsert) { + s.SetLinkedAt(v) + }) +} + +// UpdateLinkedAt sets the "linked_at" field to the value that was provided on create. +func (u *ProjectContributorUpsertOne) UpdateLinkedAt() *ProjectContributorUpsertOne { + return u.Update(func(s *ProjectContributorUpsert) { + s.UpdateLinkedAt() + }) +} + +// ClearLinkedAt clears the value of the "linked_at" field. +func (u *ProjectContributorUpsertOne) ClearLinkedAt() *ProjectContributorUpsertOne { + return u.Update(func(s *ProjectContributorUpsert) { + s.ClearLinkedAt() + }) +} + +// Exec executes the query. +func (u *ProjectContributorUpsertOne) Exec(ctx context.Context) error { + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for ProjectContributorCreate.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *ProjectContributorUpsertOne) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} + +// Exec executes the UPSERT query and returns the inserted/updated ID. +func (u *ProjectContributorUpsertOne) ID(ctx context.Context) (id uuid.UUID, err error) { + if u.create.driver.Dialect() == dialect.MySQL { + // In case of "ON CONFLICT", there is no way to get back non-numeric ID + // fields from the database since MySQL does not support the RETURNING clause. + return id, errors.New("ent: ProjectContributorUpsertOne.ID is not supported by MySQL driver. Use ProjectContributorUpsertOne.Exec instead") + } + node, err := u.create.Save(ctx) + if err != nil { + return id, err + } + return node.ID, nil +} + +// IDX is like ID, but panics if an error occurs. +func (u *ProjectContributorUpsertOne) IDX(ctx context.Context) uuid.UUID { + id, err := u.ID(ctx) + if err != nil { + panic(err) + } + return id +} + +// ProjectContributorCreateBulk is the builder for creating many ProjectContributor entities in bulk. +type ProjectContributorCreateBulk struct { + config + err error + builders []*ProjectContributorCreate + conflict []sql.ConflictOption +} + +// Save creates the ProjectContributor entities in the database. +func (_c *ProjectContributorCreateBulk) Save(ctx context.Context) ([]*ProjectContributor, error) { + if _c.err != nil { + return nil, _c.err + } + specs := make([]*sqlgraph.CreateSpec, len(_c.builders)) + nodes := make([]*ProjectContributor, len(_c.builders)) + mutators := make([]Mutator, len(_c.builders)) + for i := range _c.builders { + func(i int, root context.Context) { + builder := _c.builders[i] + builder.defaults() + var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { + mutation, ok := m.(*ProjectContributorMutation) + if !ok { + return nil, fmt.Errorf("unexpected mutation type %T", m) + } + if err := builder.check(); err != nil { + return nil, err + } + builder.mutation = mutation + var err error + nodes[i], specs[i] = builder.createSpec() + if i < len(mutators)-1 { + _, err = mutators[i+1].Mutate(root, _c.builders[i+1].mutation) + } else { + spec := &sqlgraph.BatchCreateSpec{Nodes: specs} + spec.OnConflict = _c.conflict + // Invoke the actual operation on the latest mutation in the chain. + if err = sqlgraph.BatchCreate(ctx, _c.driver, spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + } + } + if err != nil { + return nil, err + } + mutation.id = &nodes[i].ID + mutation.done = true + return nodes[i], nil + }) + for i := len(builder.hooks) - 1; i >= 0; i-- { + mut = builder.hooks[i](mut) + } + mutators[i] = mut + }(i, ctx) + } + if len(mutators) > 0 { + if _, err := mutators[0].Mutate(ctx, _c.builders[0].mutation); err != nil { + return nil, err + } + } + return nodes, nil +} + +// SaveX is like Save, but panics if an error occurs. +func (_c *ProjectContributorCreateBulk) SaveX(ctx context.Context) []*ProjectContributor { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *ProjectContributorCreateBulk) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *ProjectContributorCreateBulk) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.ProjectContributor.CreateBulk(builders...). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.ProjectContributorUpsert) { +// SetProjectID(v+v). +// }). +// Exec(ctx) +func (_c *ProjectContributorCreateBulk) OnConflict(opts ...sql.ConflictOption) *ProjectContributorUpsertBulk { + _c.conflict = opts + return &ProjectContributorUpsertBulk{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.ProjectContributor.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *ProjectContributorCreateBulk) OnConflictColumns(columns ...string) *ProjectContributorUpsertBulk { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &ProjectContributorUpsertBulk{ + create: _c, + } +} + +// ProjectContributorUpsertBulk is the builder for "upsert"-ing +// a bulk of ProjectContributor nodes. +type ProjectContributorUpsertBulk struct { + create *ProjectContributorCreateBulk +} + +// UpdateNewValues updates the mutable fields using the new values that +// were set on create. Using this option is equivalent to using: +// +// client.ProjectContributor.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(projectcontributor.FieldID) +// }), +// ). +// Exec(ctx) +func (u *ProjectContributorUpsertBulk) UpdateNewValues() *ProjectContributorUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + for _, b := range u.create.builders { + if _, exists := b.mutation.ID(); exists { + s.SetIgnore(projectcontributor.FieldID) + } + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.ProjectContributor.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *ProjectContributorUpsertBulk) Ignore() *ProjectContributorUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *ProjectContributorUpsertBulk) DoNothing() *ProjectContributorUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the ProjectContributorCreateBulk.OnConflict +// documentation for more info. +func (u *ProjectContributorUpsertBulk) Update(set func(*ProjectContributorUpsert)) *ProjectContributorUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&ProjectContributorUpsert{UpdateSet: update}) + })) + return u +} + +// SetProjectID sets the "project_id" field. +func (u *ProjectContributorUpsertBulk) SetProjectID(v uuid.UUID) *ProjectContributorUpsertBulk { + return u.Update(func(s *ProjectContributorUpsert) { + s.SetProjectID(v) + }) +} + +// UpdateProjectID sets the "project_id" field to the value that was provided on create. +func (u *ProjectContributorUpsertBulk) UpdateProjectID() *ProjectContributorUpsertBulk { + return u.Update(func(s *ProjectContributorUpsert) { + s.UpdateProjectID() + }) +} + +// SetBrokerID sets the "broker_id" field. +func (u *ProjectContributorUpsertBulk) SetBrokerID(v uuid.UUID) *ProjectContributorUpsertBulk { + return u.Update(func(s *ProjectContributorUpsert) { + s.SetBrokerID(v) + }) +} + +// UpdateBrokerID sets the "broker_id" field to the value that was provided on create. +func (u *ProjectContributorUpsertBulk) UpdateBrokerID() *ProjectContributorUpsertBulk { + return u.Update(func(s *ProjectContributorUpsert) { + s.UpdateBrokerID() + }) +} + +// SetBrokerName sets the "broker_name" field. +func (u *ProjectContributorUpsertBulk) SetBrokerName(v string) *ProjectContributorUpsertBulk { + return u.Update(func(s *ProjectContributorUpsert) { + s.SetBrokerName(v) + }) +} + +// UpdateBrokerName sets the "broker_name" field to the value that was provided on create. +func (u *ProjectContributorUpsertBulk) UpdateBrokerName() *ProjectContributorUpsertBulk { + return u.Update(func(s *ProjectContributorUpsert) { + s.UpdateBrokerName() + }) +} + +// SetMode sets the "mode" field. +func (u *ProjectContributorUpsertBulk) SetMode(v string) *ProjectContributorUpsertBulk { + return u.Update(func(s *ProjectContributorUpsert) { + s.SetMode(v) + }) +} + +// UpdateMode sets the "mode" field to the value that was provided on create. +func (u *ProjectContributorUpsertBulk) UpdateMode() *ProjectContributorUpsertBulk { + return u.Update(func(s *ProjectContributorUpsert) { + s.UpdateMode() + }) +} + +// SetStatus sets the "status" field. +func (u *ProjectContributorUpsertBulk) SetStatus(v string) *ProjectContributorUpsertBulk { + return u.Update(func(s *ProjectContributorUpsert) { + s.SetStatus(v) + }) +} + +// UpdateStatus sets the "status" field to the value that was provided on create. +func (u *ProjectContributorUpsertBulk) UpdateStatus() *ProjectContributorUpsertBulk { + return u.Update(func(s *ProjectContributorUpsert) { + s.UpdateStatus() + }) +} + +// SetProfiles sets the "profiles" field. +func (u *ProjectContributorUpsertBulk) SetProfiles(v string) *ProjectContributorUpsertBulk { + return u.Update(func(s *ProjectContributorUpsert) { + s.SetProfiles(v) + }) +} + +// UpdateProfiles sets the "profiles" field to the value that was provided on create. +func (u *ProjectContributorUpsertBulk) UpdateProfiles() *ProjectContributorUpsertBulk { + return u.Update(func(s *ProjectContributorUpsert) { + s.UpdateProfiles() + }) +} + +// ClearProfiles clears the value of the "profiles" field. +func (u *ProjectContributorUpsertBulk) ClearProfiles() *ProjectContributorUpsertBulk { + return u.Update(func(s *ProjectContributorUpsert) { + s.ClearProfiles() + }) +} + +// SetLastSeen sets the "last_seen" field. +func (u *ProjectContributorUpsertBulk) SetLastSeen(v time.Time) *ProjectContributorUpsertBulk { + return u.Update(func(s *ProjectContributorUpsert) { + s.SetLastSeen(v) + }) +} + +// UpdateLastSeen sets the "last_seen" field to the value that was provided on create. +func (u *ProjectContributorUpsertBulk) UpdateLastSeen() *ProjectContributorUpsertBulk { + return u.Update(func(s *ProjectContributorUpsert) { + s.UpdateLastSeen() + }) +} + +// ClearLastSeen clears the value of the "last_seen" field. +func (u *ProjectContributorUpsertBulk) ClearLastSeen() *ProjectContributorUpsertBulk { + return u.Update(func(s *ProjectContributorUpsert) { + s.ClearLastSeen() + }) +} + +// SetLocalPath sets the "local_path" field. +func (u *ProjectContributorUpsertBulk) SetLocalPath(v string) *ProjectContributorUpsertBulk { + return u.Update(func(s *ProjectContributorUpsert) { + s.SetLocalPath(v) + }) +} + +// UpdateLocalPath sets the "local_path" field to the value that was provided on create. +func (u *ProjectContributorUpsertBulk) UpdateLocalPath() *ProjectContributorUpsertBulk { + return u.Update(func(s *ProjectContributorUpsert) { + s.UpdateLocalPath() + }) +} + +// ClearLocalPath clears the value of the "local_path" field. +func (u *ProjectContributorUpsertBulk) ClearLocalPath() *ProjectContributorUpsertBulk { + return u.Update(func(s *ProjectContributorUpsert) { + s.ClearLocalPath() + }) +} + +// SetLinkedBy sets the "linked_by" field. +func (u *ProjectContributorUpsertBulk) SetLinkedBy(v string) *ProjectContributorUpsertBulk { + return u.Update(func(s *ProjectContributorUpsert) { + s.SetLinkedBy(v) + }) +} + +// UpdateLinkedBy sets the "linked_by" field to the value that was provided on create. +func (u *ProjectContributorUpsertBulk) UpdateLinkedBy() *ProjectContributorUpsertBulk { + return u.Update(func(s *ProjectContributorUpsert) { + s.UpdateLinkedBy() + }) +} + +// ClearLinkedBy clears the value of the "linked_by" field. +func (u *ProjectContributorUpsertBulk) ClearLinkedBy() *ProjectContributorUpsertBulk { + return u.Update(func(s *ProjectContributorUpsert) { + s.ClearLinkedBy() + }) +} + +// SetLinkedAt sets the "linked_at" field. +func (u *ProjectContributorUpsertBulk) SetLinkedAt(v time.Time) *ProjectContributorUpsertBulk { + return u.Update(func(s *ProjectContributorUpsert) { + s.SetLinkedAt(v) + }) +} + +// UpdateLinkedAt sets the "linked_at" field to the value that was provided on create. +func (u *ProjectContributorUpsertBulk) UpdateLinkedAt() *ProjectContributorUpsertBulk { + return u.Update(func(s *ProjectContributorUpsert) { + s.UpdateLinkedAt() + }) +} + +// ClearLinkedAt clears the value of the "linked_at" field. +func (u *ProjectContributorUpsertBulk) ClearLinkedAt() *ProjectContributorUpsertBulk { + return u.Update(func(s *ProjectContributorUpsert) { + s.ClearLinkedAt() + }) +} + +// Exec executes the query. +func (u *ProjectContributorUpsertBulk) Exec(ctx context.Context) error { + if u.create.err != nil { + return u.create.err + } + for i, b := range u.create.builders { + if len(b.conflict) != 0 { + return fmt.Errorf("ent: OnConflict was set for builder %d. Set it on the ProjectContributorCreateBulk instead", i) + } + } + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for ProjectContributorCreateBulk.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *ProjectContributorUpsertBulk) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/pkg/ent/projectcontributor_delete.go b/pkg/ent/projectcontributor_delete.go new file mode 100644 index 000000000..974b17485 --- /dev/null +++ b/pkg/ent/projectcontributor_delete.go @@ -0,0 +1,88 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/GoogleCloudPlatform/scion/pkg/ent/projectcontributor" +) + +// ProjectContributorDelete is the builder for deleting a ProjectContributor entity. +type ProjectContributorDelete struct { + config + hooks []Hook + mutation *ProjectContributorMutation +} + +// Where appends a list predicates to the ProjectContributorDelete builder. +func (_d *ProjectContributorDelete) Where(ps ...predicate.ProjectContributor) *ProjectContributorDelete { + _d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (_d *ProjectContributorDelete) Exec(ctx context.Context) (int, error) { + return withHooks(ctx, _d.sqlExec, _d.mutation, _d.hooks) +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *ProjectContributorDelete) ExecX(ctx context.Context) int { + n, err := _d.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (_d *ProjectContributorDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(projectcontributor.Table, sqlgraph.NewFieldSpec(projectcontributor.FieldID, field.TypeUUID)) + if ps := _d.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + affected, err := sqlgraph.DeleteNodes(ctx, _d.driver, _spec) + if err != nil && sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + _d.mutation.done = true + return affected, err +} + +// ProjectContributorDeleteOne is the builder for deleting a single ProjectContributor entity. +type ProjectContributorDeleteOne struct { + _d *ProjectContributorDelete +} + +// Where appends a list predicates to the ProjectContributorDelete builder. +func (_d *ProjectContributorDeleteOne) Where(ps ...predicate.ProjectContributor) *ProjectContributorDeleteOne { + _d._d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query. +func (_d *ProjectContributorDeleteOne) Exec(ctx context.Context) error { + n, err := _d._d.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{projectcontributor.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *ProjectContributorDeleteOne) ExecX(ctx context.Context) { + if err := _d.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/pkg/ent/projectcontributor_query.go b/pkg/ent/projectcontributor_query.go new file mode 100644 index 000000000..9398e2ec2 --- /dev/null +++ b/pkg/ent/projectcontributor_query.go @@ -0,0 +1,565 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "fmt" + "math" + + "entgo.io/ent" + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/GoogleCloudPlatform/scion/pkg/ent/projectcontributor" + "github.com/google/uuid" +) + +// ProjectContributorQuery is the builder for querying ProjectContributor entities. +type ProjectContributorQuery struct { + config + ctx *QueryContext + order []projectcontributor.OrderOption + inters []Interceptor + predicates []predicate.ProjectContributor + modifiers []func(*sql.Selector) + // intermediate query (i.e. traversal path). + sql *sql.Selector + path func(context.Context) (*sql.Selector, error) +} + +// Where adds a new predicate for the ProjectContributorQuery builder. +func (_q *ProjectContributorQuery) Where(ps ...predicate.ProjectContributor) *ProjectContributorQuery { + _q.predicates = append(_q.predicates, ps...) + return _q +} + +// Limit the number of records to be returned by this query. +func (_q *ProjectContributorQuery) Limit(limit int) *ProjectContributorQuery { + _q.ctx.Limit = &limit + return _q +} + +// Offset to start from. +func (_q *ProjectContributorQuery) Offset(offset int) *ProjectContributorQuery { + _q.ctx.Offset = &offset + return _q +} + +// Unique configures the query builder to filter duplicate records on query. +// By default, unique is set to true, and can be disabled using this method. +func (_q *ProjectContributorQuery) Unique(unique bool) *ProjectContributorQuery { + _q.ctx.Unique = &unique + return _q +} + +// Order specifies how the records should be ordered. +func (_q *ProjectContributorQuery) Order(o ...projectcontributor.OrderOption) *ProjectContributorQuery { + _q.order = append(_q.order, o...) + return _q +} + +// First returns the first ProjectContributor entity from the query. +// Returns a *NotFoundError when no ProjectContributor was found. +func (_q *ProjectContributorQuery) First(ctx context.Context) (*ProjectContributor, error) { + nodes, err := _q.Limit(1).All(setContextOp(ctx, _q.ctx, ent.OpQueryFirst)) + if err != nil { + return nil, err + } + if len(nodes) == 0 { + return nil, &NotFoundError{projectcontributor.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (_q *ProjectContributorQuery) FirstX(ctx context.Context) *ProjectContributor { + node, err := _q.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// FirstID returns the first ProjectContributor ID from the query. +// Returns a *NotFoundError when no ProjectContributor ID was found. +func (_q *ProjectContributorQuery) FirstID(ctx context.Context) (id uuid.UUID, err error) { + var ids []uuid.UUID + if ids, err = _q.Limit(1).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryFirstID)); err != nil { + return + } + if len(ids) == 0 { + err = &NotFoundError{projectcontributor.Label} + return + } + return ids[0], nil +} + +// FirstIDX is like FirstID, but panics if an error occurs. +func (_q *ProjectContributorQuery) FirstIDX(ctx context.Context) uuid.UUID { + id, err := _q.FirstID(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return id +} + +// Only returns a single ProjectContributor entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one ProjectContributor entity is found. +// Returns a *NotFoundError when no ProjectContributor entities are found. +func (_q *ProjectContributorQuery) Only(ctx context.Context) (*ProjectContributor, error) { + nodes, err := _q.Limit(2).All(setContextOp(ctx, _q.ctx, ent.OpQueryOnly)) + if err != nil { + return nil, err + } + switch len(nodes) { + case 1: + return nodes[0], nil + case 0: + return nil, &NotFoundError{projectcontributor.Label} + default: + return nil, &NotSingularError{projectcontributor.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (_q *ProjectContributorQuery) OnlyX(ctx context.Context) *ProjectContributor { + node, err := _q.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// OnlyID is like Only, but returns the only ProjectContributor ID in the query. +// Returns a *NotSingularError when more than one ProjectContributor ID is found. +// Returns a *NotFoundError when no entities are found. +func (_q *ProjectContributorQuery) OnlyID(ctx context.Context) (id uuid.UUID, err error) { + var ids []uuid.UUID + if ids, err = _q.Limit(2).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryOnlyID)); err != nil { + return + } + switch len(ids) { + case 1: + id = ids[0] + case 0: + err = &NotFoundError{projectcontributor.Label} + default: + err = &NotSingularError{projectcontributor.Label} + } + return +} + +// OnlyIDX is like OnlyID, but panics if an error occurs. +func (_q *ProjectContributorQuery) OnlyIDX(ctx context.Context) uuid.UUID { + id, err := _q.OnlyID(ctx) + if err != nil { + panic(err) + } + return id +} + +// All executes the query and returns a list of ProjectContributors. +func (_q *ProjectContributorQuery) All(ctx context.Context) ([]*ProjectContributor, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryAll) + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*ProjectContributor, *ProjectContributorQuery]() + return withInterceptors[[]*ProjectContributor](ctx, _q, qr, _q.inters) +} + +// AllX is like All, but panics if an error occurs. +func (_q *ProjectContributorQuery) AllX(ctx context.Context) []*ProjectContributor { + nodes, err := _q.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// IDs executes the query and returns a list of ProjectContributor IDs. +func (_q *ProjectContributorQuery) IDs(ctx context.Context) (ids []uuid.UUID, err error) { + if _q.ctx.Unique == nil && _q.path != nil { + _q.Unique(true) + } + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryIDs) + if err = _q.Select(projectcontributor.FieldID).Scan(ctx, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// IDsX is like IDs, but panics if an error occurs. +func (_q *ProjectContributorQuery) IDsX(ctx context.Context) []uuid.UUID { + ids, err := _q.IDs(ctx) + if err != nil { + panic(err) + } + return ids +} + +// Count returns the count of the given query. +func (_q *ProjectContributorQuery) Count(ctx context.Context) (int, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryCount) + if err := _q.prepareQuery(ctx); err != nil { + return 0, err + } + return withInterceptors[int](ctx, _q, querierCount[*ProjectContributorQuery](), _q.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (_q *ProjectContributorQuery) CountX(ctx context.Context) int { + count, err := _q.Count(ctx) + if err != nil { + panic(err) + } + return count +} + +// Exist returns true if the query has elements in the graph. +func (_q *ProjectContributorQuery) Exist(ctx context.Context) (bool, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryExist) + switch _, err := _q.FirstID(ctx); { + case IsNotFound(err): + return false, nil + case err != nil: + return false, fmt.Errorf("ent: check existence: %w", err) + default: + return true, nil + } +} + +// ExistX is like Exist, but panics if an error occurs. +func (_q *ProjectContributorQuery) ExistX(ctx context.Context) bool { + exist, err := _q.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the ProjectContributorQuery builder, including all associated steps. It can be +// used to prepare common query builders and use them differently after the clone is made. +func (_q *ProjectContributorQuery) Clone() *ProjectContributorQuery { + if _q == nil { + return nil + } + return &ProjectContributorQuery{ + config: _q.config, + ctx: _q.ctx.Clone(), + order: append([]projectcontributor.OrderOption{}, _q.order...), + inters: append([]Interceptor{}, _q.inters...), + predicates: append([]predicate.ProjectContributor{}, _q.predicates...), + // clone intermediate query. + sql: _q.sql.Clone(), + path: _q.path, + } +} + +// GroupBy is used to group vertices by one or more fields/columns. +// It is often used with aggregate functions, like: count, max, mean, min, sum. +// +// Example: +// +// var v []struct { +// ProjectID uuid.UUID `json:"project_id,omitempty"` +// Count int `json:"count,omitempty"` +// } +// +// client.ProjectContributor.Query(). +// GroupBy(projectcontributor.FieldProjectID). +// Aggregate(ent.Count()). +// Scan(ctx, &v) +func (_q *ProjectContributorQuery) GroupBy(field string, fields ...string) *ProjectContributorGroupBy { + _q.ctx.Fields = append([]string{field}, fields...) + grbuild := &ProjectContributorGroupBy{build: _q} + grbuild.flds = &_q.ctx.Fields + grbuild.label = projectcontributor.Label + grbuild.scan = grbuild.Scan + return grbuild +} + +// Select allows the selection one or more fields/columns for the given query, +// instead of selecting all fields in the entity. +// +// Example: +// +// var v []struct { +// ProjectID uuid.UUID `json:"project_id,omitempty"` +// } +// +// client.ProjectContributor.Query(). +// Select(projectcontributor.FieldProjectID). +// Scan(ctx, &v) +func (_q *ProjectContributorQuery) Select(fields ...string) *ProjectContributorSelect { + _q.ctx.Fields = append(_q.ctx.Fields, fields...) + sbuild := &ProjectContributorSelect{ProjectContributorQuery: _q} + sbuild.label = projectcontributor.Label + sbuild.flds, sbuild.scan = &_q.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a ProjectContributorSelect configured with the given aggregations. +func (_q *ProjectContributorQuery) Aggregate(fns ...AggregateFunc) *ProjectContributorSelect { + return _q.Select().Aggregate(fns...) +} + +func (_q *ProjectContributorQuery) prepareQuery(ctx context.Context) error { + for _, inter := range _q.inters { + if inter == nil { + return fmt.Errorf("ent: uninitialized interceptor (forgotten import ent/runtime?)") + } + if trv, ok := inter.(Traverser); ok { + if err := trv.Traverse(ctx, _q); err != nil { + return err + } + } + } + for _, f := range _q.ctx.Fields { + if !projectcontributor.ValidColumn(f) { + return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + } + if _q.path != nil { + prev, err := _q.path(ctx) + if err != nil { + return err + } + _q.sql = prev + } + return nil +} + +func (_q *ProjectContributorQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*ProjectContributor, error) { + var ( + nodes = []*ProjectContributor{} + _spec = _q.querySpec() + ) + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*ProjectContributor).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &ProjectContributor{config: _q.config} + nodes = append(nodes, node) + return node.assignValues(columns, values) + } + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } + for i := range hooks { + hooks[i](ctx, _spec) + } + if err := sqlgraph.QueryNodes(ctx, _q.driver, _spec); err != nil { + return nil, err + } + if len(nodes) == 0 { + return nodes, nil + } + return nodes, nil +} + +func (_q *ProjectContributorQuery) sqlCount(ctx context.Context) (int, error) { + _spec := _q.querySpec() + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } + _spec.Node.Columns = _q.ctx.Fields + if len(_q.ctx.Fields) > 0 { + _spec.Unique = _q.ctx.Unique != nil && *_q.ctx.Unique + } + return sqlgraph.CountNodes(ctx, _q.driver, _spec) +} + +func (_q *ProjectContributorQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(projectcontributor.Table, projectcontributor.Columns, sqlgraph.NewFieldSpec(projectcontributor.FieldID, field.TypeUUID)) + _spec.From = _q.sql + if unique := _q.ctx.Unique; unique != nil { + _spec.Unique = *unique + } else if _q.path != nil { + _spec.Unique = true + } + if fields := _q.ctx.Fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, projectcontributor.FieldID) + for i := range fields { + if fields[i] != projectcontributor.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, fields[i]) + } + } + } + if ps := _q.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if limit := _q.ctx.Limit; limit != nil { + _spec.Limit = *limit + } + if offset := _q.ctx.Offset; offset != nil { + _spec.Offset = *offset + } + if ps := _q.order; len(ps) > 0 { + _spec.Order = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + return _spec +} + +func (_q *ProjectContributorQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(_q.driver.Dialect()) + t1 := builder.Table(projectcontributor.Table) + columns := _q.ctx.Fields + if len(columns) == 0 { + columns = projectcontributor.Columns + } + selector := builder.Select(t1.Columns(columns...)...).From(t1) + if _q.sql != nil { + selector = _q.sql + selector.Select(selector.Columns(columns...)...) + } + if _q.ctx.Unique != nil && *_q.ctx.Unique { + selector.Distinct() + } + for _, m := range _q.modifiers { + m(selector) + } + for _, p := range _q.predicates { + p(selector) + } + for _, p := range _q.order { + p(selector) + } + if offset := _q.ctx.Offset; offset != nil { + // limit is mandatory for offset clause. We start + // with default value, and override it below if needed. + selector.Offset(*offset).Limit(math.MaxInt32) + } + if limit := _q.ctx.Limit; limit != nil { + selector.Limit(*limit) + } + return selector +} + +// ForUpdate locks the selected rows against concurrent updates, and prevent them from being +// updated, deleted or "selected ... for update" by other sessions, until the transaction is +// either committed or rolled-back. +func (_q *ProjectContributorQuery) ForUpdate(opts ...sql.LockOption) *ProjectContributorQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForUpdate(opts...) + }) + return _q +} + +// ForShare behaves similarly to ForUpdate, except that it acquires a shared mode lock +// on any rows that are read. Other sessions can read the rows, but cannot modify them +// until your transaction commits. +func (_q *ProjectContributorQuery) ForShare(opts ...sql.LockOption) *ProjectContributorQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForShare(opts...) + }) + return _q +} + +// ProjectContributorGroupBy is the group-by builder for ProjectContributor entities. +type ProjectContributorGroupBy struct { + selector + build *ProjectContributorQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (_g *ProjectContributorGroupBy) Aggregate(fns ...AggregateFunc) *ProjectContributorGroupBy { + _g.fns = append(_g.fns, fns...) + return _g +} + +// Scan applies the selector query and scans the result into the given value. +func (_g *ProjectContributorGroupBy) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, _g.build.ctx, ent.OpQueryGroupBy) + if err := _g.build.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*ProjectContributorQuery, *ProjectContributorGroupBy](ctx, _g.build, _g, _g.build.inters, v) +} + +func (_g *ProjectContributorGroupBy) sqlScan(ctx context.Context, root *ProjectContributorQuery, v any) error { + selector := root.sqlQuery(ctx).Select() + aggregation := make([]string, 0, len(_g.fns)) + for _, fn := range _g.fns { + aggregation = append(aggregation, fn(selector)) + } + if len(selector.SelectedColumns()) == 0 { + columns := make([]string, 0, len(*_g.flds)+len(_g.fns)) + for _, f := range *_g.flds { + columns = append(columns, selector.C(f)) + } + columns = append(columns, aggregation...) + selector.Select(columns...) + } + selector.GroupBy(selector.Columns(*_g.flds...)...) + if err := selector.Err(); err != nil { + return err + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := _g.build.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} + +// ProjectContributorSelect is the builder for selecting fields of ProjectContributor entities. +type ProjectContributorSelect struct { + *ProjectContributorQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (_s *ProjectContributorSelect) Aggregate(fns ...AggregateFunc) *ProjectContributorSelect { + _s.fns = append(_s.fns, fns...) + return _s +} + +// Scan applies the selector query and scans the result into the given value. +func (_s *ProjectContributorSelect) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, _s.ctx, ent.OpQuerySelect) + if err := _s.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*ProjectContributorQuery, *ProjectContributorSelect](ctx, _s.ProjectContributorQuery, _s, _s.inters, v) +} + +func (_s *ProjectContributorSelect) sqlScan(ctx context.Context, root *ProjectContributorQuery, v any) error { + selector := root.sqlQuery(ctx) + aggregation := make([]string, 0, len(_s.fns)) + for _, fn := range _s.fns { + aggregation = append(aggregation, fn(selector)) + } + switch n := len(*_s.selector.flds); { + case n == 0 && len(aggregation) > 0: + selector.Select(aggregation...) + case n != 0 && len(aggregation) > 0: + selector.AppendSelect(aggregation...) + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := _s.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} diff --git a/pkg/ent/projectcontributor_update.go b/pkg/ent/projectcontributor_update.go new file mode 100644 index 000000000..644dccc62 --- /dev/null +++ b/pkg/ent/projectcontributor_update.go @@ -0,0 +1,633 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/GoogleCloudPlatform/scion/pkg/ent/projectcontributor" + "github.com/google/uuid" +) + +// ProjectContributorUpdate is the builder for updating ProjectContributor entities. +type ProjectContributorUpdate struct { + config + hooks []Hook + mutation *ProjectContributorMutation +} + +// Where appends a list predicates to the ProjectContributorUpdate builder. +func (_u *ProjectContributorUpdate) Where(ps ...predicate.ProjectContributor) *ProjectContributorUpdate { + _u.mutation.Where(ps...) + return _u +} + +// SetProjectID sets the "project_id" field. +func (_u *ProjectContributorUpdate) SetProjectID(v uuid.UUID) *ProjectContributorUpdate { + _u.mutation.SetProjectID(v) + return _u +} + +// SetNillableProjectID sets the "project_id" field if the given value is not nil. +func (_u *ProjectContributorUpdate) SetNillableProjectID(v *uuid.UUID) *ProjectContributorUpdate { + if v != nil { + _u.SetProjectID(*v) + } + return _u +} + +// SetBrokerID sets the "broker_id" field. +func (_u *ProjectContributorUpdate) SetBrokerID(v uuid.UUID) *ProjectContributorUpdate { + _u.mutation.SetBrokerID(v) + return _u +} + +// SetNillableBrokerID sets the "broker_id" field if the given value is not nil. +func (_u *ProjectContributorUpdate) SetNillableBrokerID(v *uuid.UUID) *ProjectContributorUpdate { + if v != nil { + _u.SetBrokerID(*v) + } + return _u +} + +// SetBrokerName sets the "broker_name" field. +func (_u *ProjectContributorUpdate) SetBrokerName(v string) *ProjectContributorUpdate { + _u.mutation.SetBrokerName(v) + return _u +} + +// SetNillableBrokerName sets the "broker_name" field if the given value is not nil. +func (_u *ProjectContributorUpdate) SetNillableBrokerName(v *string) *ProjectContributorUpdate { + if v != nil { + _u.SetBrokerName(*v) + } + return _u +} + +// SetMode sets the "mode" field. +func (_u *ProjectContributorUpdate) SetMode(v string) *ProjectContributorUpdate { + _u.mutation.SetMode(v) + return _u +} + +// SetNillableMode sets the "mode" field if the given value is not nil. +func (_u *ProjectContributorUpdate) SetNillableMode(v *string) *ProjectContributorUpdate { + if v != nil { + _u.SetMode(*v) + } + return _u +} + +// SetStatus sets the "status" field. +func (_u *ProjectContributorUpdate) SetStatus(v string) *ProjectContributorUpdate { + _u.mutation.SetStatus(v) + return _u +} + +// SetNillableStatus sets the "status" field if the given value is not nil. +func (_u *ProjectContributorUpdate) SetNillableStatus(v *string) *ProjectContributorUpdate { + if v != nil { + _u.SetStatus(*v) + } + return _u +} + +// SetProfiles sets the "profiles" field. +func (_u *ProjectContributorUpdate) SetProfiles(v string) *ProjectContributorUpdate { + _u.mutation.SetProfiles(v) + return _u +} + +// SetNillableProfiles sets the "profiles" field if the given value is not nil. +func (_u *ProjectContributorUpdate) SetNillableProfiles(v *string) *ProjectContributorUpdate { + if v != nil { + _u.SetProfiles(*v) + } + return _u +} + +// ClearProfiles clears the value of the "profiles" field. +func (_u *ProjectContributorUpdate) ClearProfiles() *ProjectContributorUpdate { + _u.mutation.ClearProfiles() + return _u +} + +// SetLastSeen sets the "last_seen" field. +func (_u *ProjectContributorUpdate) SetLastSeen(v time.Time) *ProjectContributorUpdate { + _u.mutation.SetLastSeen(v) + return _u +} + +// SetNillableLastSeen sets the "last_seen" field if the given value is not nil. +func (_u *ProjectContributorUpdate) SetNillableLastSeen(v *time.Time) *ProjectContributorUpdate { + if v != nil { + _u.SetLastSeen(*v) + } + return _u +} + +// ClearLastSeen clears the value of the "last_seen" field. +func (_u *ProjectContributorUpdate) ClearLastSeen() *ProjectContributorUpdate { + _u.mutation.ClearLastSeen() + return _u +} + +// SetLocalPath sets the "local_path" field. +func (_u *ProjectContributorUpdate) SetLocalPath(v string) *ProjectContributorUpdate { + _u.mutation.SetLocalPath(v) + return _u +} + +// SetNillableLocalPath sets the "local_path" field if the given value is not nil. +func (_u *ProjectContributorUpdate) SetNillableLocalPath(v *string) *ProjectContributorUpdate { + if v != nil { + _u.SetLocalPath(*v) + } + return _u +} + +// ClearLocalPath clears the value of the "local_path" field. +func (_u *ProjectContributorUpdate) ClearLocalPath() *ProjectContributorUpdate { + _u.mutation.ClearLocalPath() + return _u +} + +// SetLinkedBy sets the "linked_by" field. +func (_u *ProjectContributorUpdate) SetLinkedBy(v string) *ProjectContributorUpdate { + _u.mutation.SetLinkedBy(v) + return _u +} + +// SetNillableLinkedBy sets the "linked_by" field if the given value is not nil. +func (_u *ProjectContributorUpdate) SetNillableLinkedBy(v *string) *ProjectContributorUpdate { + if v != nil { + _u.SetLinkedBy(*v) + } + return _u +} + +// ClearLinkedBy clears the value of the "linked_by" field. +func (_u *ProjectContributorUpdate) ClearLinkedBy() *ProjectContributorUpdate { + _u.mutation.ClearLinkedBy() + return _u +} + +// SetLinkedAt sets the "linked_at" field. +func (_u *ProjectContributorUpdate) SetLinkedAt(v time.Time) *ProjectContributorUpdate { + _u.mutation.SetLinkedAt(v) + return _u +} + +// SetNillableLinkedAt sets the "linked_at" field if the given value is not nil. +func (_u *ProjectContributorUpdate) SetNillableLinkedAt(v *time.Time) *ProjectContributorUpdate { + if v != nil { + _u.SetLinkedAt(*v) + } + return _u +} + +// ClearLinkedAt clears the value of the "linked_at" field. +func (_u *ProjectContributorUpdate) ClearLinkedAt() *ProjectContributorUpdate { + _u.mutation.ClearLinkedAt() + return _u +} + +// Mutation returns the ProjectContributorMutation object of the builder. +func (_u *ProjectContributorUpdate) Mutation() *ProjectContributorMutation { + return _u.mutation +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (_u *ProjectContributorUpdate) Save(ctx context.Context) (int, error) { + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *ProjectContributorUpdate) SaveX(ctx context.Context) int { + affected, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (_u *ProjectContributorUpdate) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *ProjectContributorUpdate) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_u *ProjectContributorUpdate) check() error { + if v, ok := _u.mutation.BrokerName(); ok { + if err := projectcontributor.BrokerNameValidator(v); err != nil { + return &ValidationError{Name: "broker_name", err: fmt.Errorf(`ent: validator failed for field "ProjectContributor.broker_name": %w`, err)} + } + } + return nil +} + +func (_u *ProjectContributorUpdate) sqlSave(ctx context.Context) (_node int, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(projectcontributor.Table, projectcontributor.Columns, sqlgraph.NewFieldSpec(projectcontributor.FieldID, field.TypeUUID)) + if ps := _u.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := _u.mutation.ProjectID(); ok { + _spec.SetField(projectcontributor.FieldProjectID, field.TypeUUID, value) + } + if value, ok := _u.mutation.BrokerID(); ok { + _spec.SetField(projectcontributor.FieldBrokerID, field.TypeUUID, value) + } + if value, ok := _u.mutation.BrokerName(); ok { + _spec.SetField(projectcontributor.FieldBrokerName, field.TypeString, value) + } + if value, ok := _u.mutation.Mode(); ok { + _spec.SetField(projectcontributor.FieldMode, field.TypeString, value) + } + if value, ok := _u.mutation.Status(); ok { + _spec.SetField(projectcontributor.FieldStatus, field.TypeString, value) + } + if value, ok := _u.mutation.Profiles(); ok { + _spec.SetField(projectcontributor.FieldProfiles, field.TypeString, value) + } + if _u.mutation.ProfilesCleared() { + _spec.ClearField(projectcontributor.FieldProfiles, field.TypeString) + } + if value, ok := _u.mutation.LastSeen(); ok { + _spec.SetField(projectcontributor.FieldLastSeen, field.TypeTime, value) + } + if _u.mutation.LastSeenCleared() { + _spec.ClearField(projectcontributor.FieldLastSeen, field.TypeTime) + } + if value, ok := _u.mutation.LocalPath(); ok { + _spec.SetField(projectcontributor.FieldLocalPath, field.TypeString, value) + } + if _u.mutation.LocalPathCleared() { + _spec.ClearField(projectcontributor.FieldLocalPath, field.TypeString) + } + if value, ok := _u.mutation.LinkedBy(); ok { + _spec.SetField(projectcontributor.FieldLinkedBy, field.TypeString, value) + } + if _u.mutation.LinkedByCleared() { + _spec.ClearField(projectcontributor.FieldLinkedBy, field.TypeString) + } + if value, ok := _u.mutation.LinkedAt(); ok { + _spec.SetField(projectcontributor.FieldLinkedAt, field.TypeTime, value) + } + if _u.mutation.LinkedAtCleared() { + _spec.ClearField(projectcontributor.FieldLinkedAt, field.TypeTime) + } + if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{projectcontributor.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + _u.mutation.done = true + return _node, nil +} + +// ProjectContributorUpdateOne is the builder for updating a single ProjectContributor entity. +type ProjectContributorUpdateOne struct { + config + fields []string + hooks []Hook + mutation *ProjectContributorMutation +} + +// SetProjectID sets the "project_id" field. +func (_u *ProjectContributorUpdateOne) SetProjectID(v uuid.UUID) *ProjectContributorUpdateOne { + _u.mutation.SetProjectID(v) + return _u +} + +// SetNillableProjectID sets the "project_id" field if the given value is not nil. +func (_u *ProjectContributorUpdateOne) SetNillableProjectID(v *uuid.UUID) *ProjectContributorUpdateOne { + if v != nil { + _u.SetProjectID(*v) + } + return _u +} + +// SetBrokerID sets the "broker_id" field. +func (_u *ProjectContributorUpdateOne) SetBrokerID(v uuid.UUID) *ProjectContributorUpdateOne { + _u.mutation.SetBrokerID(v) + return _u +} + +// SetNillableBrokerID sets the "broker_id" field if the given value is not nil. +func (_u *ProjectContributorUpdateOne) SetNillableBrokerID(v *uuid.UUID) *ProjectContributorUpdateOne { + if v != nil { + _u.SetBrokerID(*v) + } + return _u +} + +// SetBrokerName sets the "broker_name" field. +func (_u *ProjectContributorUpdateOne) SetBrokerName(v string) *ProjectContributorUpdateOne { + _u.mutation.SetBrokerName(v) + return _u +} + +// SetNillableBrokerName sets the "broker_name" field if the given value is not nil. +func (_u *ProjectContributorUpdateOne) SetNillableBrokerName(v *string) *ProjectContributorUpdateOne { + if v != nil { + _u.SetBrokerName(*v) + } + return _u +} + +// SetMode sets the "mode" field. +func (_u *ProjectContributorUpdateOne) SetMode(v string) *ProjectContributorUpdateOne { + _u.mutation.SetMode(v) + return _u +} + +// SetNillableMode sets the "mode" field if the given value is not nil. +func (_u *ProjectContributorUpdateOne) SetNillableMode(v *string) *ProjectContributorUpdateOne { + if v != nil { + _u.SetMode(*v) + } + return _u +} + +// SetStatus sets the "status" field. +func (_u *ProjectContributorUpdateOne) SetStatus(v string) *ProjectContributorUpdateOne { + _u.mutation.SetStatus(v) + return _u +} + +// SetNillableStatus sets the "status" field if the given value is not nil. +func (_u *ProjectContributorUpdateOne) SetNillableStatus(v *string) *ProjectContributorUpdateOne { + if v != nil { + _u.SetStatus(*v) + } + return _u +} + +// SetProfiles sets the "profiles" field. +func (_u *ProjectContributorUpdateOne) SetProfiles(v string) *ProjectContributorUpdateOne { + _u.mutation.SetProfiles(v) + return _u +} + +// SetNillableProfiles sets the "profiles" field if the given value is not nil. +func (_u *ProjectContributorUpdateOne) SetNillableProfiles(v *string) *ProjectContributorUpdateOne { + if v != nil { + _u.SetProfiles(*v) + } + return _u +} + +// ClearProfiles clears the value of the "profiles" field. +func (_u *ProjectContributorUpdateOne) ClearProfiles() *ProjectContributorUpdateOne { + _u.mutation.ClearProfiles() + return _u +} + +// SetLastSeen sets the "last_seen" field. +func (_u *ProjectContributorUpdateOne) SetLastSeen(v time.Time) *ProjectContributorUpdateOne { + _u.mutation.SetLastSeen(v) + return _u +} + +// SetNillableLastSeen sets the "last_seen" field if the given value is not nil. +func (_u *ProjectContributorUpdateOne) SetNillableLastSeen(v *time.Time) *ProjectContributorUpdateOne { + if v != nil { + _u.SetLastSeen(*v) + } + return _u +} + +// ClearLastSeen clears the value of the "last_seen" field. +func (_u *ProjectContributorUpdateOne) ClearLastSeen() *ProjectContributorUpdateOne { + _u.mutation.ClearLastSeen() + return _u +} + +// SetLocalPath sets the "local_path" field. +func (_u *ProjectContributorUpdateOne) SetLocalPath(v string) *ProjectContributorUpdateOne { + _u.mutation.SetLocalPath(v) + return _u +} + +// SetNillableLocalPath sets the "local_path" field if the given value is not nil. +func (_u *ProjectContributorUpdateOne) SetNillableLocalPath(v *string) *ProjectContributorUpdateOne { + if v != nil { + _u.SetLocalPath(*v) + } + return _u +} + +// ClearLocalPath clears the value of the "local_path" field. +func (_u *ProjectContributorUpdateOne) ClearLocalPath() *ProjectContributorUpdateOne { + _u.mutation.ClearLocalPath() + return _u +} + +// SetLinkedBy sets the "linked_by" field. +func (_u *ProjectContributorUpdateOne) SetLinkedBy(v string) *ProjectContributorUpdateOne { + _u.mutation.SetLinkedBy(v) + return _u +} + +// SetNillableLinkedBy sets the "linked_by" field if the given value is not nil. +func (_u *ProjectContributorUpdateOne) SetNillableLinkedBy(v *string) *ProjectContributorUpdateOne { + if v != nil { + _u.SetLinkedBy(*v) + } + return _u +} + +// ClearLinkedBy clears the value of the "linked_by" field. +func (_u *ProjectContributorUpdateOne) ClearLinkedBy() *ProjectContributorUpdateOne { + _u.mutation.ClearLinkedBy() + return _u +} + +// SetLinkedAt sets the "linked_at" field. +func (_u *ProjectContributorUpdateOne) SetLinkedAt(v time.Time) *ProjectContributorUpdateOne { + _u.mutation.SetLinkedAt(v) + return _u +} + +// SetNillableLinkedAt sets the "linked_at" field if the given value is not nil. +func (_u *ProjectContributorUpdateOne) SetNillableLinkedAt(v *time.Time) *ProjectContributorUpdateOne { + if v != nil { + _u.SetLinkedAt(*v) + } + return _u +} + +// ClearLinkedAt clears the value of the "linked_at" field. +func (_u *ProjectContributorUpdateOne) ClearLinkedAt() *ProjectContributorUpdateOne { + _u.mutation.ClearLinkedAt() + return _u +} + +// Mutation returns the ProjectContributorMutation object of the builder. +func (_u *ProjectContributorUpdateOne) Mutation() *ProjectContributorMutation { + return _u.mutation +} + +// Where appends a list predicates to the ProjectContributorUpdate builder. +func (_u *ProjectContributorUpdateOne) Where(ps ...predicate.ProjectContributor) *ProjectContributorUpdateOne { + _u.mutation.Where(ps...) + return _u +} + +// Select allows selecting one or more fields (columns) of the returned entity. +// The default is selecting all fields defined in the entity schema. +func (_u *ProjectContributorUpdateOne) Select(field string, fields ...string) *ProjectContributorUpdateOne { + _u.fields = append([]string{field}, fields...) + return _u +} + +// Save executes the query and returns the updated ProjectContributor entity. +func (_u *ProjectContributorUpdateOne) Save(ctx context.Context) (*ProjectContributor, error) { + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *ProjectContributorUpdateOne) SaveX(ctx context.Context) *ProjectContributor { + node, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (_u *ProjectContributorUpdateOne) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *ProjectContributorUpdateOne) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_u *ProjectContributorUpdateOne) check() error { + if v, ok := _u.mutation.BrokerName(); ok { + if err := projectcontributor.BrokerNameValidator(v); err != nil { + return &ValidationError{Name: "broker_name", err: fmt.Errorf(`ent: validator failed for field "ProjectContributor.broker_name": %w`, err)} + } + } + return nil +} + +func (_u *ProjectContributorUpdateOne) sqlSave(ctx context.Context) (_node *ProjectContributor, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(projectcontributor.Table, projectcontributor.Columns, sqlgraph.NewFieldSpec(projectcontributor.FieldID, field.TypeUUID)) + id, ok := _u.mutation.ID() + if !ok { + return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "ProjectContributor.id" for update`)} + } + _spec.Node.ID.Value = id + if fields := _u.fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, projectcontributor.FieldID) + for _, f := range fields { + if !projectcontributor.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + if f != projectcontributor.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, f) + } + } + } + if ps := _u.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := _u.mutation.ProjectID(); ok { + _spec.SetField(projectcontributor.FieldProjectID, field.TypeUUID, value) + } + if value, ok := _u.mutation.BrokerID(); ok { + _spec.SetField(projectcontributor.FieldBrokerID, field.TypeUUID, value) + } + if value, ok := _u.mutation.BrokerName(); ok { + _spec.SetField(projectcontributor.FieldBrokerName, field.TypeString, value) + } + if value, ok := _u.mutation.Mode(); ok { + _spec.SetField(projectcontributor.FieldMode, field.TypeString, value) + } + if value, ok := _u.mutation.Status(); ok { + _spec.SetField(projectcontributor.FieldStatus, field.TypeString, value) + } + if value, ok := _u.mutation.Profiles(); ok { + _spec.SetField(projectcontributor.FieldProfiles, field.TypeString, value) + } + if _u.mutation.ProfilesCleared() { + _spec.ClearField(projectcontributor.FieldProfiles, field.TypeString) + } + if value, ok := _u.mutation.LastSeen(); ok { + _spec.SetField(projectcontributor.FieldLastSeen, field.TypeTime, value) + } + if _u.mutation.LastSeenCleared() { + _spec.ClearField(projectcontributor.FieldLastSeen, field.TypeTime) + } + if value, ok := _u.mutation.LocalPath(); ok { + _spec.SetField(projectcontributor.FieldLocalPath, field.TypeString, value) + } + if _u.mutation.LocalPathCleared() { + _spec.ClearField(projectcontributor.FieldLocalPath, field.TypeString) + } + if value, ok := _u.mutation.LinkedBy(); ok { + _spec.SetField(projectcontributor.FieldLinkedBy, field.TypeString, value) + } + if _u.mutation.LinkedByCleared() { + _spec.ClearField(projectcontributor.FieldLinkedBy, field.TypeString) + } + if value, ok := _u.mutation.LinkedAt(); ok { + _spec.SetField(projectcontributor.FieldLinkedAt, field.TypeTime, value) + } + if _u.mutation.LinkedAtCleared() { + _spec.ClearField(projectcontributor.FieldLinkedAt, field.TypeTime) + } + _node = &ProjectContributor{config: _u.config} + _spec.Assign = _node.assignValues + _spec.ScanValues = _node.scanValues + if err = sqlgraph.UpdateNode(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{projectcontributor.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + _u.mutation.done = true + return _node, nil +} diff --git a/pkg/ent/projectsyncstate.go b/pkg/ent/projectsyncstate.go new file mode 100644 index 000000000..5f21d5d5d --- /dev/null +++ b/pkg/ent/projectsyncstate.go @@ -0,0 +1,167 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "fmt" + "strings" + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "github.com/GoogleCloudPlatform/scion/pkg/ent/projectsyncstate" + "github.com/google/uuid" +) + +// ProjectSyncState is the model entity for the ProjectSyncState schema. +type ProjectSyncState struct { + config `json:"-"` + // ID of the ent. + ID uuid.UUID `json:"id,omitempty"` + // ProjectID holds the value of the "project_id" field. + ProjectID uuid.UUID `json:"project_id,omitempty"` + // BrokerID holds the value of the "broker_id" field. + BrokerID string `json:"broker_id,omitempty"` + // LastSyncTime holds the value of the "last_sync_time" field. + LastSyncTime *time.Time `json:"last_sync_time,omitempty"` + // LastCommitSha holds the value of the "last_commit_sha" field. + LastCommitSha string `json:"last_commit_sha,omitempty"` + // FileCount holds the value of the "file_count" field. + FileCount int `json:"file_count,omitempty"` + // TotalBytes holds the value of the "total_bytes" field. + TotalBytes int64 `json:"total_bytes,omitempty"` + selectValues sql.SelectValues +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*ProjectSyncState) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case projectsyncstate.FieldFileCount, projectsyncstate.FieldTotalBytes: + values[i] = new(sql.NullInt64) + case projectsyncstate.FieldBrokerID, projectsyncstate.FieldLastCommitSha: + values[i] = new(sql.NullString) + case projectsyncstate.FieldLastSyncTime: + values[i] = new(sql.NullTime) + case projectsyncstate.FieldID, projectsyncstate.FieldProjectID: + values[i] = new(uuid.UUID) + default: + values[i] = new(sql.UnknownType) + } + } + return values, nil +} + +// assignValues assigns the values that were returned from sql.Rows (after scanning) +// to the ProjectSyncState fields. +func (_m *ProjectSyncState) assignValues(columns []string, values []any) error { + if m, n := len(values), len(columns); m < n { + return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) + } + for i := range columns { + switch columns[i] { + case projectsyncstate.FieldID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field id", values[i]) + } else if value != nil { + _m.ID = *value + } + case projectsyncstate.FieldProjectID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field project_id", values[i]) + } else if value != nil { + _m.ProjectID = *value + } + case projectsyncstate.FieldBrokerID: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field broker_id", values[i]) + } else if value.Valid { + _m.BrokerID = value.String + } + case projectsyncstate.FieldLastSyncTime: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field last_sync_time", values[i]) + } else if value.Valid { + _m.LastSyncTime = new(time.Time) + *_m.LastSyncTime = value.Time + } + case projectsyncstate.FieldLastCommitSha: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field last_commit_sha", values[i]) + } else if value.Valid { + _m.LastCommitSha = value.String + } + case projectsyncstate.FieldFileCount: + if value, ok := values[i].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for field file_count", values[i]) + } else if value.Valid { + _m.FileCount = int(value.Int64) + } + case projectsyncstate.FieldTotalBytes: + if value, ok := values[i].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for field total_bytes", values[i]) + } else if value.Valid { + _m.TotalBytes = value.Int64 + } + default: + _m.selectValues.Set(columns[i], values[i]) + } + } + return nil +} + +// Value returns the ent.Value that was dynamically selected and assigned to the ProjectSyncState. +// This includes values selected through modifiers, order, etc. +func (_m *ProjectSyncState) Value(name string) (ent.Value, error) { + return _m.selectValues.Get(name) +} + +// Update returns a builder for updating this ProjectSyncState. +// Note that you need to call ProjectSyncState.Unwrap() before calling this method if this ProjectSyncState +// was returned from a transaction, and the transaction was committed or rolled back. +func (_m *ProjectSyncState) Update() *ProjectSyncStateUpdateOne { + return NewProjectSyncStateClient(_m.config).UpdateOne(_m) +} + +// Unwrap unwraps the ProjectSyncState entity that was returned from a transaction after it was closed, +// so that all future queries will be executed through the driver which created the transaction. +func (_m *ProjectSyncState) Unwrap() *ProjectSyncState { + _tx, ok := _m.config.driver.(*txDriver) + if !ok { + panic("ent: ProjectSyncState is not a transactional entity") + } + _m.config.driver = _tx.drv + return _m +} + +// String implements the fmt.Stringer. +func (_m *ProjectSyncState) String() string { + var builder strings.Builder + builder.WriteString("ProjectSyncState(") + builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID)) + builder.WriteString("project_id=") + builder.WriteString(fmt.Sprintf("%v", _m.ProjectID)) + builder.WriteString(", ") + builder.WriteString("broker_id=") + builder.WriteString(_m.BrokerID) + builder.WriteString(", ") + if v := _m.LastSyncTime; v != nil { + builder.WriteString("last_sync_time=") + builder.WriteString(v.Format(time.ANSIC)) + } + builder.WriteString(", ") + builder.WriteString("last_commit_sha=") + builder.WriteString(_m.LastCommitSha) + builder.WriteString(", ") + builder.WriteString("file_count=") + builder.WriteString(fmt.Sprintf("%v", _m.FileCount)) + builder.WriteString(", ") + builder.WriteString("total_bytes=") + builder.WriteString(fmt.Sprintf("%v", _m.TotalBytes)) + builder.WriteByte(')') + return builder.String() +} + +// ProjectSyncStates is a parsable slice of ProjectSyncState. +type ProjectSyncStates []*ProjectSyncState diff --git a/pkg/ent/projectsyncstate/projectsyncstate.go b/pkg/ent/projectsyncstate/projectsyncstate.go new file mode 100644 index 000000000..b0154db21 --- /dev/null +++ b/pkg/ent/projectsyncstate/projectsyncstate.go @@ -0,0 +1,99 @@ +// Code generated by ent, DO NOT EDIT. + +package projectsyncstate + +import ( + "entgo.io/ent/dialect/sql" + "github.com/google/uuid" +) + +const ( + // Label holds the string label denoting the projectsyncstate type in the database. + Label = "project_sync_state" + // FieldID holds the string denoting the id field in the database. + FieldID = "id" + // FieldProjectID holds the string denoting the project_id field in the database. + FieldProjectID = "project_id" + // FieldBrokerID holds the string denoting the broker_id field in the database. + FieldBrokerID = "broker_id" + // FieldLastSyncTime holds the string denoting the last_sync_time field in the database. + FieldLastSyncTime = "last_sync_time" + // FieldLastCommitSha holds the string denoting the last_commit_sha field in the database. + FieldLastCommitSha = "last_commit_sha" + // FieldFileCount holds the string denoting the file_count field in the database. + FieldFileCount = "file_count" + // FieldTotalBytes holds the string denoting the total_bytes field in the database. + FieldTotalBytes = "total_bytes" + // Table holds the table name of the projectsyncstate in the database. + Table = "project_sync_state" +) + +// Columns holds all SQL columns for projectsyncstate fields. +var Columns = []string{ + FieldID, + FieldProjectID, + FieldBrokerID, + FieldLastSyncTime, + FieldLastCommitSha, + FieldFileCount, + FieldTotalBytes, +} + +// ValidColumn reports if the column name is valid (part of the table columns). +func ValidColumn(column string) bool { + for i := range Columns { + if column == Columns[i] { + return true + } + } + return false +} + +var ( + // DefaultBrokerID holds the default value on creation for the "broker_id" field. + DefaultBrokerID string + // DefaultFileCount holds the default value on creation for the "file_count" field. + DefaultFileCount int + // DefaultTotalBytes holds the default value on creation for the "total_bytes" field. + DefaultTotalBytes int64 + // DefaultID holds the default value on creation for the "id" field. + DefaultID func() uuid.UUID +) + +// OrderOption defines the ordering options for the ProjectSyncState queries. +type OrderOption func(*sql.Selector) + +// ByID orders the results by the id field. +func ByID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldID, opts...).ToFunc() +} + +// ByProjectID orders the results by the project_id field. +func ByProjectID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldProjectID, opts...).ToFunc() +} + +// ByBrokerID orders the results by the broker_id field. +func ByBrokerID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldBrokerID, opts...).ToFunc() +} + +// ByLastSyncTime orders the results by the last_sync_time field. +func ByLastSyncTime(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldLastSyncTime, opts...).ToFunc() +} + +// ByLastCommitSha orders the results by the last_commit_sha field. +func ByLastCommitSha(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldLastCommitSha, opts...).ToFunc() +} + +// ByFileCount orders the results by the file_count field. +func ByFileCount(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldFileCount, opts...).ToFunc() +} + +// ByTotalBytes orders the results by the total_bytes field. +func ByTotalBytes(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldTotalBytes, opts...).ToFunc() +} diff --git a/pkg/ent/projectsyncstate/where.go b/pkg/ent/projectsyncstate/where.go new file mode 100644 index 000000000..6d80806c4 --- /dev/null +++ b/pkg/ent/projectsyncstate/where.go @@ -0,0 +1,411 @@ +// Code generated by ent, DO NOT EDIT. + +package projectsyncstate + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/google/uuid" +) + +// ID filters vertices based on their ID field. +func ID(id uuid.UUID) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldEQ(FieldID, id)) +} + +// IDEQ applies the EQ predicate on the ID field. +func IDEQ(id uuid.UUID) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldEQ(FieldID, id)) +} + +// IDNEQ applies the NEQ predicate on the ID field. +func IDNEQ(id uuid.UUID) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldNEQ(FieldID, id)) +} + +// IDIn applies the In predicate on the ID field. +func IDIn(ids ...uuid.UUID) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldIn(FieldID, ids...)) +} + +// IDNotIn applies the NotIn predicate on the ID field. +func IDNotIn(ids ...uuid.UUID) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldNotIn(FieldID, ids...)) +} + +// IDGT applies the GT predicate on the ID field. +func IDGT(id uuid.UUID) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldGT(FieldID, id)) +} + +// IDGTE applies the GTE predicate on the ID field. +func IDGTE(id uuid.UUID) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldGTE(FieldID, id)) +} + +// IDLT applies the LT predicate on the ID field. +func IDLT(id uuid.UUID) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldLT(FieldID, id)) +} + +// IDLTE applies the LTE predicate on the ID field. +func IDLTE(id uuid.UUID) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldLTE(FieldID, id)) +} + +// ProjectID applies equality check predicate on the "project_id" field. It's identical to ProjectIDEQ. +func ProjectID(v uuid.UUID) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldEQ(FieldProjectID, v)) +} + +// BrokerID applies equality check predicate on the "broker_id" field. It's identical to BrokerIDEQ. +func BrokerID(v string) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldEQ(FieldBrokerID, v)) +} + +// LastSyncTime applies equality check predicate on the "last_sync_time" field. It's identical to LastSyncTimeEQ. +func LastSyncTime(v time.Time) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldEQ(FieldLastSyncTime, v)) +} + +// LastCommitSha applies equality check predicate on the "last_commit_sha" field. It's identical to LastCommitShaEQ. +func LastCommitSha(v string) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldEQ(FieldLastCommitSha, v)) +} + +// FileCount applies equality check predicate on the "file_count" field. It's identical to FileCountEQ. +func FileCount(v int) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldEQ(FieldFileCount, v)) +} + +// TotalBytes applies equality check predicate on the "total_bytes" field. It's identical to TotalBytesEQ. +func TotalBytes(v int64) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldEQ(FieldTotalBytes, v)) +} + +// ProjectIDEQ applies the EQ predicate on the "project_id" field. +func ProjectIDEQ(v uuid.UUID) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldEQ(FieldProjectID, v)) +} + +// ProjectIDNEQ applies the NEQ predicate on the "project_id" field. +func ProjectIDNEQ(v uuid.UUID) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldNEQ(FieldProjectID, v)) +} + +// ProjectIDIn applies the In predicate on the "project_id" field. +func ProjectIDIn(vs ...uuid.UUID) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldIn(FieldProjectID, vs...)) +} + +// ProjectIDNotIn applies the NotIn predicate on the "project_id" field. +func ProjectIDNotIn(vs ...uuid.UUID) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldNotIn(FieldProjectID, vs...)) +} + +// ProjectIDGT applies the GT predicate on the "project_id" field. +func ProjectIDGT(v uuid.UUID) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldGT(FieldProjectID, v)) +} + +// ProjectIDGTE applies the GTE predicate on the "project_id" field. +func ProjectIDGTE(v uuid.UUID) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldGTE(FieldProjectID, v)) +} + +// ProjectIDLT applies the LT predicate on the "project_id" field. +func ProjectIDLT(v uuid.UUID) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldLT(FieldProjectID, v)) +} + +// ProjectIDLTE applies the LTE predicate on the "project_id" field. +func ProjectIDLTE(v uuid.UUID) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldLTE(FieldProjectID, v)) +} + +// BrokerIDEQ applies the EQ predicate on the "broker_id" field. +func BrokerIDEQ(v string) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldEQ(FieldBrokerID, v)) +} + +// BrokerIDNEQ applies the NEQ predicate on the "broker_id" field. +func BrokerIDNEQ(v string) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldNEQ(FieldBrokerID, v)) +} + +// BrokerIDIn applies the In predicate on the "broker_id" field. +func BrokerIDIn(vs ...string) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldIn(FieldBrokerID, vs...)) +} + +// BrokerIDNotIn applies the NotIn predicate on the "broker_id" field. +func BrokerIDNotIn(vs ...string) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldNotIn(FieldBrokerID, vs...)) +} + +// BrokerIDGT applies the GT predicate on the "broker_id" field. +func BrokerIDGT(v string) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldGT(FieldBrokerID, v)) +} + +// BrokerIDGTE applies the GTE predicate on the "broker_id" field. +func BrokerIDGTE(v string) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldGTE(FieldBrokerID, v)) +} + +// BrokerIDLT applies the LT predicate on the "broker_id" field. +func BrokerIDLT(v string) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldLT(FieldBrokerID, v)) +} + +// BrokerIDLTE applies the LTE predicate on the "broker_id" field. +func BrokerIDLTE(v string) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldLTE(FieldBrokerID, v)) +} + +// BrokerIDContains applies the Contains predicate on the "broker_id" field. +func BrokerIDContains(v string) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldContains(FieldBrokerID, v)) +} + +// BrokerIDHasPrefix applies the HasPrefix predicate on the "broker_id" field. +func BrokerIDHasPrefix(v string) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldHasPrefix(FieldBrokerID, v)) +} + +// BrokerIDHasSuffix applies the HasSuffix predicate on the "broker_id" field. +func BrokerIDHasSuffix(v string) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldHasSuffix(FieldBrokerID, v)) +} + +// BrokerIDEqualFold applies the EqualFold predicate on the "broker_id" field. +func BrokerIDEqualFold(v string) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldEqualFold(FieldBrokerID, v)) +} + +// BrokerIDContainsFold applies the ContainsFold predicate on the "broker_id" field. +func BrokerIDContainsFold(v string) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldContainsFold(FieldBrokerID, v)) +} + +// LastSyncTimeEQ applies the EQ predicate on the "last_sync_time" field. +func LastSyncTimeEQ(v time.Time) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldEQ(FieldLastSyncTime, v)) +} + +// LastSyncTimeNEQ applies the NEQ predicate on the "last_sync_time" field. +func LastSyncTimeNEQ(v time.Time) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldNEQ(FieldLastSyncTime, v)) +} + +// LastSyncTimeIn applies the In predicate on the "last_sync_time" field. +func LastSyncTimeIn(vs ...time.Time) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldIn(FieldLastSyncTime, vs...)) +} + +// LastSyncTimeNotIn applies the NotIn predicate on the "last_sync_time" field. +func LastSyncTimeNotIn(vs ...time.Time) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldNotIn(FieldLastSyncTime, vs...)) +} + +// LastSyncTimeGT applies the GT predicate on the "last_sync_time" field. +func LastSyncTimeGT(v time.Time) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldGT(FieldLastSyncTime, v)) +} + +// LastSyncTimeGTE applies the GTE predicate on the "last_sync_time" field. +func LastSyncTimeGTE(v time.Time) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldGTE(FieldLastSyncTime, v)) +} + +// LastSyncTimeLT applies the LT predicate on the "last_sync_time" field. +func LastSyncTimeLT(v time.Time) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldLT(FieldLastSyncTime, v)) +} + +// LastSyncTimeLTE applies the LTE predicate on the "last_sync_time" field. +func LastSyncTimeLTE(v time.Time) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldLTE(FieldLastSyncTime, v)) +} + +// LastSyncTimeIsNil applies the IsNil predicate on the "last_sync_time" field. +func LastSyncTimeIsNil() predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldIsNull(FieldLastSyncTime)) +} + +// LastSyncTimeNotNil applies the NotNil predicate on the "last_sync_time" field. +func LastSyncTimeNotNil() predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldNotNull(FieldLastSyncTime)) +} + +// LastCommitShaEQ applies the EQ predicate on the "last_commit_sha" field. +func LastCommitShaEQ(v string) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldEQ(FieldLastCommitSha, v)) +} + +// LastCommitShaNEQ applies the NEQ predicate on the "last_commit_sha" field. +func LastCommitShaNEQ(v string) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldNEQ(FieldLastCommitSha, v)) +} + +// LastCommitShaIn applies the In predicate on the "last_commit_sha" field. +func LastCommitShaIn(vs ...string) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldIn(FieldLastCommitSha, vs...)) +} + +// LastCommitShaNotIn applies the NotIn predicate on the "last_commit_sha" field. +func LastCommitShaNotIn(vs ...string) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldNotIn(FieldLastCommitSha, vs...)) +} + +// LastCommitShaGT applies the GT predicate on the "last_commit_sha" field. +func LastCommitShaGT(v string) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldGT(FieldLastCommitSha, v)) +} + +// LastCommitShaGTE applies the GTE predicate on the "last_commit_sha" field. +func LastCommitShaGTE(v string) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldGTE(FieldLastCommitSha, v)) +} + +// LastCommitShaLT applies the LT predicate on the "last_commit_sha" field. +func LastCommitShaLT(v string) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldLT(FieldLastCommitSha, v)) +} + +// LastCommitShaLTE applies the LTE predicate on the "last_commit_sha" field. +func LastCommitShaLTE(v string) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldLTE(FieldLastCommitSha, v)) +} + +// LastCommitShaContains applies the Contains predicate on the "last_commit_sha" field. +func LastCommitShaContains(v string) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldContains(FieldLastCommitSha, v)) +} + +// LastCommitShaHasPrefix applies the HasPrefix predicate on the "last_commit_sha" field. +func LastCommitShaHasPrefix(v string) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldHasPrefix(FieldLastCommitSha, v)) +} + +// LastCommitShaHasSuffix applies the HasSuffix predicate on the "last_commit_sha" field. +func LastCommitShaHasSuffix(v string) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldHasSuffix(FieldLastCommitSha, v)) +} + +// LastCommitShaIsNil applies the IsNil predicate on the "last_commit_sha" field. +func LastCommitShaIsNil() predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldIsNull(FieldLastCommitSha)) +} + +// LastCommitShaNotNil applies the NotNil predicate on the "last_commit_sha" field. +func LastCommitShaNotNil() predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldNotNull(FieldLastCommitSha)) +} + +// LastCommitShaEqualFold applies the EqualFold predicate on the "last_commit_sha" field. +func LastCommitShaEqualFold(v string) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldEqualFold(FieldLastCommitSha, v)) +} + +// LastCommitShaContainsFold applies the ContainsFold predicate on the "last_commit_sha" field. +func LastCommitShaContainsFold(v string) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldContainsFold(FieldLastCommitSha, v)) +} + +// FileCountEQ applies the EQ predicate on the "file_count" field. +func FileCountEQ(v int) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldEQ(FieldFileCount, v)) +} + +// FileCountNEQ applies the NEQ predicate on the "file_count" field. +func FileCountNEQ(v int) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldNEQ(FieldFileCount, v)) +} + +// FileCountIn applies the In predicate on the "file_count" field. +func FileCountIn(vs ...int) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldIn(FieldFileCount, vs...)) +} + +// FileCountNotIn applies the NotIn predicate on the "file_count" field. +func FileCountNotIn(vs ...int) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldNotIn(FieldFileCount, vs...)) +} + +// FileCountGT applies the GT predicate on the "file_count" field. +func FileCountGT(v int) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldGT(FieldFileCount, v)) +} + +// FileCountGTE applies the GTE predicate on the "file_count" field. +func FileCountGTE(v int) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldGTE(FieldFileCount, v)) +} + +// FileCountLT applies the LT predicate on the "file_count" field. +func FileCountLT(v int) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldLT(FieldFileCount, v)) +} + +// FileCountLTE applies the LTE predicate on the "file_count" field. +func FileCountLTE(v int) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldLTE(FieldFileCount, v)) +} + +// TotalBytesEQ applies the EQ predicate on the "total_bytes" field. +func TotalBytesEQ(v int64) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldEQ(FieldTotalBytes, v)) +} + +// TotalBytesNEQ applies the NEQ predicate on the "total_bytes" field. +func TotalBytesNEQ(v int64) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldNEQ(FieldTotalBytes, v)) +} + +// TotalBytesIn applies the In predicate on the "total_bytes" field. +func TotalBytesIn(vs ...int64) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldIn(FieldTotalBytes, vs...)) +} + +// TotalBytesNotIn applies the NotIn predicate on the "total_bytes" field. +func TotalBytesNotIn(vs ...int64) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldNotIn(FieldTotalBytes, vs...)) +} + +// TotalBytesGT applies the GT predicate on the "total_bytes" field. +func TotalBytesGT(v int64) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldGT(FieldTotalBytes, v)) +} + +// TotalBytesGTE applies the GTE predicate on the "total_bytes" field. +func TotalBytesGTE(v int64) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldGTE(FieldTotalBytes, v)) +} + +// TotalBytesLT applies the LT predicate on the "total_bytes" field. +func TotalBytesLT(v int64) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldLT(FieldTotalBytes, v)) +} + +// TotalBytesLTE applies the LTE predicate on the "total_bytes" field. +func TotalBytesLTE(v int64) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.FieldLTE(FieldTotalBytes, v)) +} + +// And groups predicates with the AND operator between them. +func And(predicates ...predicate.ProjectSyncState) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.AndPredicates(predicates...)) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.ProjectSyncState) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.OrPredicates(predicates...)) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.ProjectSyncState) predicate.ProjectSyncState { + return predicate.ProjectSyncState(sql.NotPredicates(p)) +} diff --git a/pkg/ent/projectsyncstate_create.go b/pkg/ent/projectsyncstate_create.go new file mode 100644 index 000000000..13e37464f --- /dev/null +++ b/pkg/ent/projectsyncstate_create.go @@ -0,0 +1,900 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/projectsyncstate" + "github.com/google/uuid" +) + +// ProjectSyncStateCreate is the builder for creating a ProjectSyncState entity. +type ProjectSyncStateCreate struct { + config + mutation *ProjectSyncStateMutation + hooks []Hook + conflict []sql.ConflictOption +} + +// SetProjectID sets the "project_id" field. +func (_c *ProjectSyncStateCreate) SetProjectID(v uuid.UUID) *ProjectSyncStateCreate { + _c.mutation.SetProjectID(v) + return _c +} + +// SetBrokerID sets the "broker_id" field. +func (_c *ProjectSyncStateCreate) SetBrokerID(v string) *ProjectSyncStateCreate { + _c.mutation.SetBrokerID(v) + return _c +} + +// SetNillableBrokerID sets the "broker_id" field if the given value is not nil. +func (_c *ProjectSyncStateCreate) SetNillableBrokerID(v *string) *ProjectSyncStateCreate { + if v != nil { + _c.SetBrokerID(*v) + } + return _c +} + +// SetLastSyncTime sets the "last_sync_time" field. +func (_c *ProjectSyncStateCreate) SetLastSyncTime(v time.Time) *ProjectSyncStateCreate { + _c.mutation.SetLastSyncTime(v) + return _c +} + +// SetNillableLastSyncTime sets the "last_sync_time" field if the given value is not nil. +func (_c *ProjectSyncStateCreate) SetNillableLastSyncTime(v *time.Time) *ProjectSyncStateCreate { + if v != nil { + _c.SetLastSyncTime(*v) + } + return _c +} + +// SetLastCommitSha sets the "last_commit_sha" field. +func (_c *ProjectSyncStateCreate) SetLastCommitSha(v string) *ProjectSyncStateCreate { + _c.mutation.SetLastCommitSha(v) + return _c +} + +// SetNillableLastCommitSha sets the "last_commit_sha" field if the given value is not nil. +func (_c *ProjectSyncStateCreate) SetNillableLastCommitSha(v *string) *ProjectSyncStateCreate { + if v != nil { + _c.SetLastCommitSha(*v) + } + return _c +} + +// SetFileCount sets the "file_count" field. +func (_c *ProjectSyncStateCreate) SetFileCount(v int) *ProjectSyncStateCreate { + _c.mutation.SetFileCount(v) + return _c +} + +// SetNillableFileCount sets the "file_count" field if the given value is not nil. +func (_c *ProjectSyncStateCreate) SetNillableFileCount(v *int) *ProjectSyncStateCreate { + if v != nil { + _c.SetFileCount(*v) + } + return _c +} + +// SetTotalBytes sets the "total_bytes" field. +func (_c *ProjectSyncStateCreate) SetTotalBytes(v int64) *ProjectSyncStateCreate { + _c.mutation.SetTotalBytes(v) + return _c +} + +// SetNillableTotalBytes sets the "total_bytes" field if the given value is not nil. +func (_c *ProjectSyncStateCreate) SetNillableTotalBytes(v *int64) *ProjectSyncStateCreate { + if v != nil { + _c.SetTotalBytes(*v) + } + return _c +} + +// SetID sets the "id" field. +func (_c *ProjectSyncStateCreate) SetID(v uuid.UUID) *ProjectSyncStateCreate { + _c.mutation.SetID(v) + return _c +} + +// SetNillableID sets the "id" field if the given value is not nil. +func (_c *ProjectSyncStateCreate) SetNillableID(v *uuid.UUID) *ProjectSyncStateCreate { + if v != nil { + _c.SetID(*v) + } + return _c +} + +// Mutation returns the ProjectSyncStateMutation object of the builder. +func (_c *ProjectSyncStateCreate) Mutation() *ProjectSyncStateMutation { + return _c.mutation +} + +// Save creates the ProjectSyncState in the database. +func (_c *ProjectSyncStateCreate) Save(ctx context.Context) (*ProjectSyncState, error) { + _c.defaults() + return withHooks(ctx, _c.sqlSave, _c.mutation, _c.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (_c *ProjectSyncStateCreate) SaveX(ctx context.Context) *ProjectSyncState { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *ProjectSyncStateCreate) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *ProjectSyncStateCreate) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_c *ProjectSyncStateCreate) defaults() { + if _, ok := _c.mutation.BrokerID(); !ok { + v := projectsyncstate.DefaultBrokerID + _c.mutation.SetBrokerID(v) + } + if _, ok := _c.mutation.FileCount(); !ok { + v := projectsyncstate.DefaultFileCount + _c.mutation.SetFileCount(v) + } + if _, ok := _c.mutation.TotalBytes(); !ok { + v := projectsyncstate.DefaultTotalBytes + _c.mutation.SetTotalBytes(v) + } + if _, ok := _c.mutation.ID(); !ok { + v := projectsyncstate.DefaultID() + _c.mutation.SetID(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_c *ProjectSyncStateCreate) check() error { + if _, ok := _c.mutation.ProjectID(); !ok { + return &ValidationError{Name: "project_id", err: errors.New(`ent: missing required field "ProjectSyncState.project_id"`)} + } + if _, ok := _c.mutation.BrokerID(); !ok { + return &ValidationError{Name: "broker_id", err: errors.New(`ent: missing required field "ProjectSyncState.broker_id"`)} + } + if _, ok := _c.mutation.FileCount(); !ok { + return &ValidationError{Name: "file_count", err: errors.New(`ent: missing required field "ProjectSyncState.file_count"`)} + } + if _, ok := _c.mutation.TotalBytes(); !ok { + return &ValidationError{Name: "total_bytes", err: errors.New(`ent: missing required field "ProjectSyncState.total_bytes"`)} + } + return nil +} + +func (_c *ProjectSyncStateCreate) sqlSave(ctx context.Context) (*ProjectSyncState, error) { + if err := _c.check(); err != nil { + return nil, err + } + _node, _spec := _c.createSpec() + if err := sqlgraph.CreateNode(ctx, _c.driver, _spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + if _spec.ID.Value != nil { + if id, ok := _spec.ID.Value.(*uuid.UUID); ok { + _node.ID = *id + } else if err := _node.ID.Scan(_spec.ID.Value); err != nil { + return nil, err + } + } + _c.mutation.id = &_node.ID + _c.mutation.done = true + return _node, nil +} + +func (_c *ProjectSyncStateCreate) createSpec() (*ProjectSyncState, *sqlgraph.CreateSpec) { + var ( + _node = &ProjectSyncState{config: _c.config} + _spec = sqlgraph.NewCreateSpec(projectsyncstate.Table, sqlgraph.NewFieldSpec(projectsyncstate.FieldID, field.TypeUUID)) + ) + _spec.OnConflict = _c.conflict + if id, ok := _c.mutation.ID(); ok { + _node.ID = id + _spec.ID.Value = &id + } + if value, ok := _c.mutation.ProjectID(); ok { + _spec.SetField(projectsyncstate.FieldProjectID, field.TypeUUID, value) + _node.ProjectID = value + } + if value, ok := _c.mutation.BrokerID(); ok { + _spec.SetField(projectsyncstate.FieldBrokerID, field.TypeString, value) + _node.BrokerID = value + } + if value, ok := _c.mutation.LastSyncTime(); ok { + _spec.SetField(projectsyncstate.FieldLastSyncTime, field.TypeTime, value) + _node.LastSyncTime = &value + } + if value, ok := _c.mutation.LastCommitSha(); ok { + _spec.SetField(projectsyncstate.FieldLastCommitSha, field.TypeString, value) + _node.LastCommitSha = value + } + if value, ok := _c.mutation.FileCount(); ok { + _spec.SetField(projectsyncstate.FieldFileCount, field.TypeInt, value) + _node.FileCount = value + } + if value, ok := _c.mutation.TotalBytes(); ok { + _spec.SetField(projectsyncstate.FieldTotalBytes, field.TypeInt64, value) + _node.TotalBytes = value + } + return _node, _spec +} + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.ProjectSyncState.Create(). +// SetProjectID(v). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.ProjectSyncStateUpsert) { +// SetProjectID(v+v). +// }). +// Exec(ctx) +func (_c *ProjectSyncStateCreate) OnConflict(opts ...sql.ConflictOption) *ProjectSyncStateUpsertOne { + _c.conflict = opts + return &ProjectSyncStateUpsertOne{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.ProjectSyncState.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *ProjectSyncStateCreate) OnConflictColumns(columns ...string) *ProjectSyncStateUpsertOne { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &ProjectSyncStateUpsertOne{ + create: _c, + } +} + +type ( + // ProjectSyncStateUpsertOne is the builder for "upsert"-ing + // one ProjectSyncState node. + ProjectSyncStateUpsertOne struct { + create *ProjectSyncStateCreate + } + + // ProjectSyncStateUpsert is the "OnConflict" setter. + ProjectSyncStateUpsert struct { + *sql.UpdateSet + } +) + +// SetProjectID sets the "project_id" field. +func (u *ProjectSyncStateUpsert) SetProjectID(v uuid.UUID) *ProjectSyncStateUpsert { + u.Set(projectsyncstate.FieldProjectID, v) + return u +} + +// UpdateProjectID sets the "project_id" field to the value that was provided on create. +func (u *ProjectSyncStateUpsert) UpdateProjectID() *ProjectSyncStateUpsert { + u.SetExcluded(projectsyncstate.FieldProjectID) + return u +} + +// SetBrokerID sets the "broker_id" field. +func (u *ProjectSyncStateUpsert) SetBrokerID(v string) *ProjectSyncStateUpsert { + u.Set(projectsyncstate.FieldBrokerID, v) + return u +} + +// UpdateBrokerID sets the "broker_id" field to the value that was provided on create. +func (u *ProjectSyncStateUpsert) UpdateBrokerID() *ProjectSyncStateUpsert { + u.SetExcluded(projectsyncstate.FieldBrokerID) + return u +} + +// SetLastSyncTime sets the "last_sync_time" field. +func (u *ProjectSyncStateUpsert) SetLastSyncTime(v time.Time) *ProjectSyncStateUpsert { + u.Set(projectsyncstate.FieldLastSyncTime, v) + return u +} + +// UpdateLastSyncTime sets the "last_sync_time" field to the value that was provided on create. +func (u *ProjectSyncStateUpsert) UpdateLastSyncTime() *ProjectSyncStateUpsert { + u.SetExcluded(projectsyncstate.FieldLastSyncTime) + return u +} + +// ClearLastSyncTime clears the value of the "last_sync_time" field. +func (u *ProjectSyncStateUpsert) ClearLastSyncTime() *ProjectSyncStateUpsert { + u.SetNull(projectsyncstate.FieldLastSyncTime) + return u +} + +// SetLastCommitSha sets the "last_commit_sha" field. +func (u *ProjectSyncStateUpsert) SetLastCommitSha(v string) *ProjectSyncStateUpsert { + u.Set(projectsyncstate.FieldLastCommitSha, v) + return u +} + +// UpdateLastCommitSha sets the "last_commit_sha" field to the value that was provided on create. +func (u *ProjectSyncStateUpsert) UpdateLastCommitSha() *ProjectSyncStateUpsert { + u.SetExcluded(projectsyncstate.FieldLastCommitSha) + return u +} + +// ClearLastCommitSha clears the value of the "last_commit_sha" field. +func (u *ProjectSyncStateUpsert) ClearLastCommitSha() *ProjectSyncStateUpsert { + u.SetNull(projectsyncstate.FieldLastCommitSha) + return u +} + +// SetFileCount sets the "file_count" field. +func (u *ProjectSyncStateUpsert) SetFileCount(v int) *ProjectSyncStateUpsert { + u.Set(projectsyncstate.FieldFileCount, v) + return u +} + +// UpdateFileCount sets the "file_count" field to the value that was provided on create. +func (u *ProjectSyncStateUpsert) UpdateFileCount() *ProjectSyncStateUpsert { + u.SetExcluded(projectsyncstate.FieldFileCount) + return u +} + +// AddFileCount adds v to the "file_count" field. +func (u *ProjectSyncStateUpsert) AddFileCount(v int) *ProjectSyncStateUpsert { + u.Add(projectsyncstate.FieldFileCount, v) + return u +} + +// SetTotalBytes sets the "total_bytes" field. +func (u *ProjectSyncStateUpsert) SetTotalBytes(v int64) *ProjectSyncStateUpsert { + u.Set(projectsyncstate.FieldTotalBytes, v) + return u +} + +// UpdateTotalBytes sets the "total_bytes" field to the value that was provided on create. +func (u *ProjectSyncStateUpsert) UpdateTotalBytes() *ProjectSyncStateUpsert { + u.SetExcluded(projectsyncstate.FieldTotalBytes) + return u +} + +// AddTotalBytes adds v to the "total_bytes" field. +func (u *ProjectSyncStateUpsert) AddTotalBytes(v int64) *ProjectSyncStateUpsert { + u.Add(projectsyncstate.FieldTotalBytes, v) + return u +} + +// UpdateNewValues updates the mutable fields using the new values that were set on create except the ID field. +// Using this option is equivalent to using: +// +// client.ProjectSyncState.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(projectsyncstate.FieldID) +// }), +// ). +// Exec(ctx) +func (u *ProjectSyncStateUpsertOne) UpdateNewValues() *ProjectSyncStateUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + if _, exists := u.create.mutation.ID(); exists { + s.SetIgnore(projectsyncstate.FieldID) + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.ProjectSyncState.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *ProjectSyncStateUpsertOne) Ignore() *ProjectSyncStateUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *ProjectSyncStateUpsertOne) DoNothing() *ProjectSyncStateUpsertOne { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the ProjectSyncStateCreate.OnConflict +// documentation for more info. +func (u *ProjectSyncStateUpsertOne) Update(set func(*ProjectSyncStateUpsert)) *ProjectSyncStateUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&ProjectSyncStateUpsert{UpdateSet: update}) + })) + return u +} + +// SetProjectID sets the "project_id" field. +func (u *ProjectSyncStateUpsertOne) SetProjectID(v uuid.UUID) *ProjectSyncStateUpsertOne { + return u.Update(func(s *ProjectSyncStateUpsert) { + s.SetProjectID(v) + }) +} + +// UpdateProjectID sets the "project_id" field to the value that was provided on create. +func (u *ProjectSyncStateUpsertOne) UpdateProjectID() *ProjectSyncStateUpsertOne { + return u.Update(func(s *ProjectSyncStateUpsert) { + s.UpdateProjectID() + }) +} + +// SetBrokerID sets the "broker_id" field. +func (u *ProjectSyncStateUpsertOne) SetBrokerID(v string) *ProjectSyncStateUpsertOne { + return u.Update(func(s *ProjectSyncStateUpsert) { + s.SetBrokerID(v) + }) +} + +// UpdateBrokerID sets the "broker_id" field to the value that was provided on create. +func (u *ProjectSyncStateUpsertOne) UpdateBrokerID() *ProjectSyncStateUpsertOne { + return u.Update(func(s *ProjectSyncStateUpsert) { + s.UpdateBrokerID() + }) +} + +// SetLastSyncTime sets the "last_sync_time" field. +func (u *ProjectSyncStateUpsertOne) SetLastSyncTime(v time.Time) *ProjectSyncStateUpsertOne { + return u.Update(func(s *ProjectSyncStateUpsert) { + s.SetLastSyncTime(v) + }) +} + +// UpdateLastSyncTime sets the "last_sync_time" field to the value that was provided on create. +func (u *ProjectSyncStateUpsertOne) UpdateLastSyncTime() *ProjectSyncStateUpsertOne { + return u.Update(func(s *ProjectSyncStateUpsert) { + s.UpdateLastSyncTime() + }) +} + +// ClearLastSyncTime clears the value of the "last_sync_time" field. +func (u *ProjectSyncStateUpsertOne) ClearLastSyncTime() *ProjectSyncStateUpsertOne { + return u.Update(func(s *ProjectSyncStateUpsert) { + s.ClearLastSyncTime() + }) +} + +// SetLastCommitSha sets the "last_commit_sha" field. +func (u *ProjectSyncStateUpsertOne) SetLastCommitSha(v string) *ProjectSyncStateUpsertOne { + return u.Update(func(s *ProjectSyncStateUpsert) { + s.SetLastCommitSha(v) + }) +} + +// UpdateLastCommitSha sets the "last_commit_sha" field to the value that was provided on create. +func (u *ProjectSyncStateUpsertOne) UpdateLastCommitSha() *ProjectSyncStateUpsertOne { + return u.Update(func(s *ProjectSyncStateUpsert) { + s.UpdateLastCommitSha() + }) +} + +// ClearLastCommitSha clears the value of the "last_commit_sha" field. +func (u *ProjectSyncStateUpsertOne) ClearLastCommitSha() *ProjectSyncStateUpsertOne { + return u.Update(func(s *ProjectSyncStateUpsert) { + s.ClearLastCommitSha() + }) +} + +// SetFileCount sets the "file_count" field. +func (u *ProjectSyncStateUpsertOne) SetFileCount(v int) *ProjectSyncStateUpsertOne { + return u.Update(func(s *ProjectSyncStateUpsert) { + s.SetFileCount(v) + }) +} + +// AddFileCount adds v to the "file_count" field. +func (u *ProjectSyncStateUpsertOne) AddFileCount(v int) *ProjectSyncStateUpsertOne { + return u.Update(func(s *ProjectSyncStateUpsert) { + s.AddFileCount(v) + }) +} + +// UpdateFileCount sets the "file_count" field to the value that was provided on create. +func (u *ProjectSyncStateUpsertOne) UpdateFileCount() *ProjectSyncStateUpsertOne { + return u.Update(func(s *ProjectSyncStateUpsert) { + s.UpdateFileCount() + }) +} + +// SetTotalBytes sets the "total_bytes" field. +func (u *ProjectSyncStateUpsertOne) SetTotalBytes(v int64) *ProjectSyncStateUpsertOne { + return u.Update(func(s *ProjectSyncStateUpsert) { + s.SetTotalBytes(v) + }) +} + +// AddTotalBytes adds v to the "total_bytes" field. +func (u *ProjectSyncStateUpsertOne) AddTotalBytes(v int64) *ProjectSyncStateUpsertOne { + return u.Update(func(s *ProjectSyncStateUpsert) { + s.AddTotalBytes(v) + }) +} + +// UpdateTotalBytes sets the "total_bytes" field to the value that was provided on create. +func (u *ProjectSyncStateUpsertOne) UpdateTotalBytes() *ProjectSyncStateUpsertOne { + return u.Update(func(s *ProjectSyncStateUpsert) { + s.UpdateTotalBytes() + }) +} + +// Exec executes the query. +func (u *ProjectSyncStateUpsertOne) Exec(ctx context.Context) error { + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for ProjectSyncStateCreate.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *ProjectSyncStateUpsertOne) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} + +// Exec executes the UPSERT query and returns the inserted/updated ID. +func (u *ProjectSyncStateUpsertOne) ID(ctx context.Context) (id uuid.UUID, err error) { + if u.create.driver.Dialect() == dialect.MySQL { + // In case of "ON CONFLICT", there is no way to get back non-numeric ID + // fields from the database since MySQL does not support the RETURNING clause. + return id, errors.New("ent: ProjectSyncStateUpsertOne.ID is not supported by MySQL driver. Use ProjectSyncStateUpsertOne.Exec instead") + } + node, err := u.create.Save(ctx) + if err != nil { + return id, err + } + return node.ID, nil +} + +// IDX is like ID, but panics if an error occurs. +func (u *ProjectSyncStateUpsertOne) IDX(ctx context.Context) uuid.UUID { + id, err := u.ID(ctx) + if err != nil { + panic(err) + } + return id +} + +// ProjectSyncStateCreateBulk is the builder for creating many ProjectSyncState entities in bulk. +type ProjectSyncStateCreateBulk struct { + config + err error + builders []*ProjectSyncStateCreate + conflict []sql.ConflictOption +} + +// Save creates the ProjectSyncState entities in the database. +func (_c *ProjectSyncStateCreateBulk) Save(ctx context.Context) ([]*ProjectSyncState, error) { + if _c.err != nil { + return nil, _c.err + } + specs := make([]*sqlgraph.CreateSpec, len(_c.builders)) + nodes := make([]*ProjectSyncState, len(_c.builders)) + mutators := make([]Mutator, len(_c.builders)) + for i := range _c.builders { + func(i int, root context.Context) { + builder := _c.builders[i] + builder.defaults() + var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { + mutation, ok := m.(*ProjectSyncStateMutation) + if !ok { + return nil, fmt.Errorf("unexpected mutation type %T", m) + } + if err := builder.check(); err != nil { + return nil, err + } + builder.mutation = mutation + var err error + nodes[i], specs[i] = builder.createSpec() + if i < len(mutators)-1 { + _, err = mutators[i+1].Mutate(root, _c.builders[i+1].mutation) + } else { + spec := &sqlgraph.BatchCreateSpec{Nodes: specs} + spec.OnConflict = _c.conflict + // Invoke the actual operation on the latest mutation in the chain. + if err = sqlgraph.BatchCreate(ctx, _c.driver, spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + } + } + if err != nil { + return nil, err + } + mutation.id = &nodes[i].ID + mutation.done = true + return nodes[i], nil + }) + for i := len(builder.hooks) - 1; i >= 0; i-- { + mut = builder.hooks[i](mut) + } + mutators[i] = mut + }(i, ctx) + } + if len(mutators) > 0 { + if _, err := mutators[0].Mutate(ctx, _c.builders[0].mutation); err != nil { + return nil, err + } + } + return nodes, nil +} + +// SaveX is like Save, but panics if an error occurs. +func (_c *ProjectSyncStateCreateBulk) SaveX(ctx context.Context) []*ProjectSyncState { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *ProjectSyncStateCreateBulk) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *ProjectSyncStateCreateBulk) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.ProjectSyncState.CreateBulk(builders...). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.ProjectSyncStateUpsert) { +// SetProjectID(v+v). +// }). +// Exec(ctx) +func (_c *ProjectSyncStateCreateBulk) OnConflict(opts ...sql.ConflictOption) *ProjectSyncStateUpsertBulk { + _c.conflict = opts + return &ProjectSyncStateUpsertBulk{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.ProjectSyncState.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *ProjectSyncStateCreateBulk) OnConflictColumns(columns ...string) *ProjectSyncStateUpsertBulk { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &ProjectSyncStateUpsertBulk{ + create: _c, + } +} + +// ProjectSyncStateUpsertBulk is the builder for "upsert"-ing +// a bulk of ProjectSyncState nodes. +type ProjectSyncStateUpsertBulk struct { + create *ProjectSyncStateCreateBulk +} + +// UpdateNewValues updates the mutable fields using the new values that +// were set on create. Using this option is equivalent to using: +// +// client.ProjectSyncState.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(projectsyncstate.FieldID) +// }), +// ). +// Exec(ctx) +func (u *ProjectSyncStateUpsertBulk) UpdateNewValues() *ProjectSyncStateUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + for _, b := range u.create.builders { + if _, exists := b.mutation.ID(); exists { + s.SetIgnore(projectsyncstate.FieldID) + } + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.ProjectSyncState.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *ProjectSyncStateUpsertBulk) Ignore() *ProjectSyncStateUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *ProjectSyncStateUpsertBulk) DoNothing() *ProjectSyncStateUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the ProjectSyncStateCreateBulk.OnConflict +// documentation for more info. +func (u *ProjectSyncStateUpsertBulk) Update(set func(*ProjectSyncStateUpsert)) *ProjectSyncStateUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&ProjectSyncStateUpsert{UpdateSet: update}) + })) + return u +} + +// SetProjectID sets the "project_id" field. +func (u *ProjectSyncStateUpsertBulk) SetProjectID(v uuid.UUID) *ProjectSyncStateUpsertBulk { + return u.Update(func(s *ProjectSyncStateUpsert) { + s.SetProjectID(v) + }) +} + +// UpdateProjectID sets the "project_id" field to the value that was provided on create. +func (u *ProjectSyncStateUpsertBulk) UpdateProjectID() *ProjectSyncStateUpsertBulk { + return u.Update(func(s *ProjectSyncStateUpsert) { + s.UpdateProjectID() + }) +} + +// SetBrokerID sets the "broker_id" field. +func (u *ProjectSyncStateUpsertBulk) SetBrokerID(v string) *ProjectSyncStateUpsertBulk { + return u.Update(func(s *ProjectSyncStateUpsert) { + s.SetBrokerID(v) + }) +} + +// UpdateBrokerID sets the "broker_id" field to the value that was provided on create. +func (u *ProjectSyncStateUpsertBulk) UpdateBrokerID() *ProjectSyncStateUpsertBulk { + return u.Update(func(s *ProjectSyncStateUpsert) { + s.UpdateBrokerID() + }) +} + +// SetLastSyncTime sets the "last_sync_time" field. +func (u *ProjectSyncStateUpsertBulk) SetLastSyncTime(v time.Time) *ProjectSyncStateUpsertBulk { + return u.Update(func(s *ProjectSyncStateUpsert) { + s.SetLastSyncTime(v) + }) +} + +// UpdateLastSyncTime sets the "last_sync_time" field to the value that was provided on create. +func (u *ProjectSyncStateUpsertBulk) UpdateLastSyncTime() *ProjectSyncStateUpsertBulk { + return u.Update(func(s *ProjectSyncStateUpsert) { + s.UpdateLastSyncTime() + }) +} + +// ClearLastSyncTime clears the value of the "last_sync_time" field. +func (u *ProjectSyncStateUpsertBulk) ClearLastSyncTime() *ProjectSyncStateUpsertBulk { + return u.Update(func(s *ProjectSyncStateUpsert) { + s.ClearLastSyncTime() + }) +} + +// SetLastCommitSha sets the "last_commit_sha" field. +func (u *ProjectSyncStateUpsertBulk) SetLastCommitSha(v string) *ProjectSyncStateUpsertBulk { + return u.Update(func(s *ProjectSyncStateUpsert) { + s.SetLastCommitSha(v) + }) +} + +// UpdateLastCommitSha sets the "last_commit_sha" field to the value that was provided on create. +func (u *ProjectSyncStateUpsertBulk) UpdateLastCommitSha() *ProjectSyncStateUpsertBulk { + return u.Update(func(s *ProjectSyncStateUpsert) { + s.UpdateLastCommitSha() + }) +} + +// ClearLastCommitSha clears the value of the "last_commit_sha" field. +func (u *ProjectSyncStateUpsertBulk) ClearLastCommitSha() *ProjectSyncStateUpsertBulk { + return u.Update(func(s *ProjectSyncStateUpsert) { + s.ClearLastCommitSha() + }) +} + +// SetFileCount sets the "file_count" field. +func (u *ProjectSyncStateUpsertBulk) SetFileCount(v int) *ProjectSyncStateUpsertBulk { + return u.Update(func(s *ProjectSyncStateUpsert) { + s.SetFileCount(v) + }) +} + +// AddFileCount adds v to the "file_count" field. +func (u *ProjectSyncStateUpsertBulk) AddFileCount(v int) *ProjectSyncStateUpsertBulk { + return u.Update(func(s *ProjectSyncStateUpsert) { + s.AddFileCount(v) + }) +} + +// UpdateFileCount sets the "file_count" field to the value that was provided on create. +func (u *ProjectSyncStateUpsertBulk) UpdateFileCount() *ProjectSyncStateUpsertBulk { + return u.Update(func(s *ProjectSyncStateUpsert) { + s.UpdateFileCount() + }) +} + +// SetTotalBytes sets the "total_bytes" field. +func (u *ProjectSyncStateUpsertBulk) SetTotalBytes(v int64) *ProjectSyncStateUpsertBulk { + return u.Update(func(s *ProjectSyncStateUpsert) { + s.SetTotalBytes(v) + }) +} + +// AddTotalBytes adds v to the "total_bytes" field. +func (u *ProjectSyncStateUpsertBulk) AddTotalBytes(v int64) *ProjectSyncStateUpsertBulk { + return u.Update(func(s *ProjectSyncStateUpsert) { + s.AddTotalBytes(v) + }) +} + +// UpdateTotalBytes sets the "total_bytes" field to the value that was provided on create. +func (u *ProjectSyncStateUpsertBulk) UpdateTotalBytes() *ProjectSyncStateUpsertBulk { + return u.Update(func(s *ProjectSyncStateUpsert) { + s.UpdateTotalBytes() + }) +} + +// Exec executes the query. +func (u *ProjectSyncStateUpsertBulk) Exec(ctx context.Context) error { + if u.create.err != nil { + return u.create.err + } + for i, b := range u.create.builders { + if len(b.conflict) != 0 { + return fmt.Errorf("ent: OnConflict was set for builder %d. Set it on the ProjectSyncStateCreateBulk instead", i) + } + } + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for ProjectSyncStateCreateBulk.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *ProjectSyncStateUpsertBulk) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/pkg/ent/projectsyncstate_delete.go b/pkg/ent/projectsyncstate_delete.go new file mode 100644 index 000000000..06d24dc03 --- /dev/null +++ b/pkg/ent/projectsyncstate_delete.go @@ -0,0 +1,88 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/GoogleCloudPlatform/scion/pkg/ent/projectsyncstate" +) + +// ProjectSyncStateDelete is the builder for deleting a ProjectSyncState entity. +type ProjectSyncStateDelete struct { + config + hooks []Hook + mutation *ProjectSyncStateMutation +} + +// Where appends a list predicates to the ProjectSyncStateDelete builder. +func (_d *ProjectSyncStateDelete) Where(ps ...predicate.ProjectSyncState) *ProjectSyncStateDelete { + _d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (_d *ProjectSyncStateDelete) Exec(ctx context.Context) (int, error) { + return withHooks(ctx, _d.sqlExec, _d.mutation, _d.hooks) +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *ProjectSyncStateDelete) ExecX(ctx context.Context) int { + n, err := _d.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (_d *ProjectSyncStateDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(projectsyncstate.Table, sqlgraph.NewFieldSpec(projectsyncstate.FieldID, field.TypeUUID)) + if ps := _d.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + affected, err := sqlgraph.DeleteNodes(ctx, _d.driver, _spec) + if err != nil && sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + _d.mutation.done = true + return affected, err +} + +// ProjectSyncStateDeleteOne is the builder for deleting a single ProjectSyncState entity. +type ProjectSyncStateDeleteOne struct { + _d *ProjectSyncStateDelete +} + +// Where appends a list predicates to the ProjectSyncStateDelete builder. +func (_d *ProjectSyncStateDeleteOne) Where(ps ...predicate.ProjectSyncState) *ProjectSyncStateDeleteOne { + _d._d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query. +func (_d *ProjectSyncStateDeleteOne) Exec(ctx context.Context) error { + n, err := _d._d.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{projectsyncstate.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *ProjectSyncStateDeleteOne) ExecX(ctx context.Context) { + if err := _d.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/pkg/ent/projectsyncstate_query.go b/pkg/ent/projectsyncstate_query.go new file mode 100644 index 000000000..d04ccdfb2 --- /dev/null +++ b/pkg/ent/projectsyncstate_query.go @@ -0,0 +1,565 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "fmt" + "math" + + "entgo.io/ent" + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/GoogleCloudPlatform/scion/pkg/ent/projectsyncstate" + "github.com/google/uuid" +) + +// ProjectSyncStateQuery is the builder for querying ProjectSyncState entities. +type ProjectSyncStateQuery struct { + config + ctx *QueryContext + order []projectsyncstate.OrderOption + inters []Interceptor + predicates []predicate.ProjectSyncState + modifiers []func(*sql.Selector) + // intermediate query (i.e. traversal path). + sql *sql.Selector + path func(context.Context) (*sql.Selector, error) +} + +// Where adds a new predicate for the ProjectSyncStateQuery builder. +func (_q *ProjectSyncStateQuery) Where(ps ...predicate.ProjectSyncState) *ProjectSyncStateQuery { + _q.predicates = append(_q.predicates, ps...) + return _q +} + +// Limit the number of records to be returned by this query. +func (_q *ProjectSyncStateQuery) Limit(limit int) *ProjectSyncStateQuery { + _q.ctx.Limit = &limit + return _q +} + +// Offset to start from. +func (_q *ProjectSyncStateQuery) Offset(offset int) *ProjectSyncStateQuery { + _q.ctx.Offset = &offset + return _q +} + +// Unique configures the query builder to filter duplicate records on query. +// By default, unique is set to true, and can be disabled using this method. +func (_q *ProjectSyncStateQuery) Unique(unique bool) *ProjectSyncStateQuery { + _q.ctx.Unique = &unique + return _q +} + +// Order specifies how the records should be ordered. +func (_q *ProjectSyncStateQuery) Order(o ...projectsyncstate.OrderOption) *ProjectSyncStateQuery { + _q.order = append(_q.order, o...) + return _q +} + +// First returns the first ProjectSyncState entity from the query. +// Returns a *NotFoundError when no ProjectSyncState was found. +func (_q *ProjectSyncStateQuery) First(ctx context.Context) (*ProjectSyncState, error) { + nodes, err := _q.Limit(1).All(setContextOp(ctx, _q.ctx, ent.OpQueryFirst)) + if err != nil { + return nil, err + } + if len(nodes) == 0 { + return nil, &NotFoundError{projectsyncstate.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (_q *ProjectSyncStateQuery) FirstX(ctx context.Context) *ProjectSyncState { + node, err := _q.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// FirstID returns the first ProjectSyncState ID from the query. +// Returns a *NotFoundError when no ProjectSyncState ID was found. +func (_q *ProjectSyncStateQuery) FirstID(ctx context.Context) (id uuid.UUID, err error) { + var ids []uuid.UUID + if ids, err = _q.Limit(1).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryFirstID)); err != nil { + return + } + if len(ids) == 0 { + err = &NotFoundError{projectsyncstate.Label} + return + } + return ids[0], nil +} + +// FirstIDX is like FirstID, but panics if an error occurs. +func (_q *ProjectSyncStateQuery) FirstIDX(ctx context.Context) uuid.UUID { + id, err := _q.FirstID(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return id +} + +// Only returns a single ProjectSyncState entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one ProjectSyncState entity is found. +// Returns a *NotFoundError when no ProjectSyncState entities are found. +func (_q *ProjectSyncStateQuery) Only(ctx context.Context) (*ProjectSyncState, error) { + nodes, err := _q.Limit(2).All(setContextOp(ctx, _q.ctx, ent.OpQueryOnly)) + if err != nil { + return nil, err + } + switch len(nodes) { + case 1: + return nodes[0], nil + case 0: + return nil, &NotFoundError{projectsyncstate.Label} + default: + return nil, &NotSingularError{projectsyncstate.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (_q *ProjectSyncStateQuery) OnlyX(ctx context.Context) *ProjectSyncState { + node, err := _q.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// OnlyID is like Only, but returns the only ProjectSyncState ID in the query. +// Returns a *NotSingularError when more than one ProjectSyncState ID is found. +// Returns a *NotFoundError when no entities are found. +func (_q *ProjectSyncStateQuery) OnlyID(ctx context.Context) (id uuid.UUID, err error) { + var ids []uuid.UUID + if ids, err = _q.Limit(2).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryOnlyID)); err != nil { + return + } + switch len(ids) { + case 1: + id = ids[0] + case 0: + err = &NotFoundError{projectsyncstate.Label} + default: + err = &NotSingularError{projectsyncstate.Label} + } + return +} + +// OnlyIDX is like OnlyID, but panics if an error occurs. +func (_q *ProjectSyncStateQuery) OnlyIDX(ctx context.Context) uuid.UUID { + id, err := _q.OnlyID(ctx) + if err != nil { + panic(err) + } + return id +} + +// All executes the query and returns a list of ProjectSyncStates. +func (_q *ProjectSyncStateQuery) All(ctx context.Context) ([]*ProjectSyncState, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryAll) + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*ProjectSyncState, *ProjectSyncStateQuery]() + return withInterceptors[[]*ProjectSyncState](ctx, _q, qr, _q.inters) +} + +// AllX is like All, but panics if an error occurs. +func (_q *ProjectSyncStateQuery) AllX(ctx context.Context) []*ProjectSyncState { + nodes, err := _q.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// IDs executes the query and returns a list of ProjectSyncState IDs. +func (_q *ProjectSyncStateQuery) IDs(ctx context.Context) (ids []uuid.UUID, err error) { + if _q.ctx.Unique == nil && _q.path != nil { + _q.Unique(true) + } + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryIDs) + if err = _q.Select(projectsyncstate.FieldID).Scan(ctx, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// IDsX is like IDs, but panics if an error occurs. +func (_q *ProjectSyncStateQuery) IDsX(ctx context.Context) []uuid.UUID { + ids, err := _q.IDs(ctx) + if err != nil { + panic(err) + } + return ids +} + +// Count returns the count of the given query. +func (_q *ProjectSyncStateQuery) Count(ctx context.Context) (int, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryCount) + if err := _q.prepareQuery(ctx); err != nil { + return 0, err + } + return withInterceptors[int](ctx, _q, querierCount[*ProjectSyncStateQuery](), _q.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (_q *ProjectSyncStateQuery) CountX(ctx context.Context) int { + count, err := _q.Count(ctx) + if err != nil { + panic(err) + } + return count +} + +// Exist returns true if the query has elements in the graph. +func (_q *ProjectSyncStateQuery) Exist(ctx context.Context) (bool, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryExist) + switch _, err := _q.FirstID(ctx); { + case IsNotFound(err): + return false, nil + case err != nil: + return false, fmt.Errorf("ent: check existence: %w", err) + default: + return true, nil + } +} + +// ExistX is like Exist, but panics if an error occurs. +func (_q *ProjectSyncStateQuery) ExistX(ctx context.Context) bool { + exist, err := _q.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the ProjectSyncStateQuery builder, including all associated steps. It can be +// used to prepare common query builders and use them differently after the clone is made. +func (_q *ProjectSyncStateQuery) Clone() *ProjectSyncStateQuery { + if _q == nil { + return nil + } + return &ProjectSyncStateQuery{ + config: _q.config, + ctx: _q.ctx.Clone(), + order: append([]projectsyncstate.OrderOption{}, _q.order...), + inters: append([]Interceptor{}, _q.inters...), + predicates: append([]predicate.ProjectSyncState{}, _q.predicates...), + // clone intermediate query. + sql: _q.sql.Clone(), + path: _q.path, + } +} + +// GroupBy is used to group vertices by one or more fields/columns. +// It is often used with aggregate functions, like: count, max, mean, min, sum. +// +// Example: +// +// var v []struct { +// ProjectID uuid.UUID `json:"project_id,omitempty"` +// Count int `json:"count,omitempty"` +// } +// +// client.ProjectSyncState.Query(). +// GroupBy(projectsyncstate.FieldProjectID). +// Aggregate(ent.Count()). +// Scan(ctx, &v) +func (_q *ProjectSyncStateQuery) GroupBy(field string, fields ...string) *ProjectSyncStateGroupBy { + _q.ctx.Fields = append([]string{field}, fields...) + grbuild := &ProjectSyncStateGroupBy{build: _q} + grbuild.flds = &_q.ctx.Fields + grbuild.label = projectsyncstate.Label + grbuild.scan = grbuild.Scan + return grbuild +} + +// Select allows the selection one or more fields/columns for the given query, +// instead of selecting all fields in the entity. +// +// Example: +// +// var v []struct { +// ProjectID uuid.UUID `json:"project_id,omitempty"` +// } +// +// client.ProjectSyncState.Query(). +// Select(projectsyncstate.FieldProjectID). +// Scan(ctx, &v) +func (_q *ProjectSyncStateQuery) Select(fields ...string) *ProjectSyncStateSelect { + _q.ctx.Fields = append(_q.ctx.Fields, fields...) + sbuild := &ProjectSyncStateSelect{ProjectSyncStateQuery: _q} + sbuild.label = projectsyncstate.Label + sbuild.flds, sbuild.scan = &_q.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a ProjectSyncStateSelect configured with the given aggregations. +func (_q *ProjectSyncStateQuery) Aggregate(fns ...AggregateFunc) *ProjectSyncStateSelect { + return _q.Select().Aggregate(fns...) +} + +func (_q *ProjectSyncStateQuery) prepareQuery(ctx context.Context) error { + for _, inter := range _q.inters { + if inter == nil { + return fmt.Errorf("ent: uninitialized interceptor (forgotten import ent/runtime?)") + } + if trv, ok := inter.(Traverser); ok { + if err := trv.Traverse(ctx, _q); err != nil { + return err + } + } + } + for _, f := range _q.ctx.Fields { + if !projectsyncstate.ValidColumn(f) { + return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + } + if _q.path != nil { + prev, err := _q.path(ctx) + if err != nil { + return err + } + _q.sql = prev + } + return nil +} + +func (_q *ProjectSyncStateQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*ProjectSyncState, error) { + var ( + nodes = []*ProjectSyncState{} + _spec = _q.querySpec() + ) + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*ProjectSyncState).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &ProjectSyncState{config: _q.config} + nodes = append(nodes, node) + return node.assignValues(columns, values) + } + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } + for i := range hooks { + hooks[i](ctx, _spec) + } + if err := sqlgraph.QueryNodes(ctx, _q.driver, _spec); err != nil { + return nil, err + } + if len(nodes) == 0 { + return nodes, nil + } + return nodes, nil +} + +func (_q *ProjectSyncStateQuery) sqlCount(ctx context.Context) (int, error) { + _spec := _q.querySpec() + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } + _spec.Node.Columns = _q.ctx.Fields + if len(_q.ctx.Fields) > 0 { + _spec.Unique = _q.ctx.Unique != nil && *_q.ctx.Unique + } + return sqlgraph.CountNodes(ctx, _q.driver, _spec) +} + +func (_q *ProjectSyncStateQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(projectsyncstate.Table, projectsyncstate.Columns, sqlgraph.NewFieldSpec(projectsyncstate.FieldID, field.TypeUUID)) + _spec.From = _q.sql + if unique := _q.ctx.Unique; unique != nil { + _spec.Unique = *unique + } else if _q.path != nil { + _spec.Unique = true + } + if fields := _q.ctx.Fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, projectsyncstate.FieldID) + for i := range fields { + if fields[i] != projectsyncstate.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, fields[i]) + } + } + } + if ps := _q.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if limit := _q.ctx.Limit; limit != nil { + _spec.Limit = *limit + } + if offset := _q.ctx.Offset; offset != nil { + _spec.Offset = *offset + } + if ps := _q.order; len(ps) > 0 { + _spec.Order = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + return _spec +} + +func (_q *ProjectSyncStateQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(_q.driver.Dialect()) + t1 := builder.Table(projectsyncstate.Table) + columns := _q.ctx.Fields + if len(columns) == 0 { + columns = projectsyncstate.Columns + } + selector := builder.Select(t1.Columns(columns...)...).From(t1) + if _q.sql != nil { + selector = _q.sql + selector.Select(selector.Columns(columns...)...) + } + if _q.ctx.Unique != nil && *_q.ctx.Unique { + selector.Distinct() + } + for _, m := range _q.modifiers { + m(selector) + } + for _, p := range _q.predicates { + p(selector) + } + for _, p := range _q.order { + p(selector) + } + if offset := _q.ctx.Offset; offset != nil { + // limit is mandatory for offset clause. We start + // with default value, and override it below if needed. + selector.Offset(*offset).Limit(math.MaxInt32) + } + if limit := _q.ctx.Limit; limit != nil { + selector.Limit(*limit) + } + return selector +} + +// ForUpdate locks the selected rows against concurrent updates, and prevent them from being +// updated, deleted or "selected ... for update" by other sessions, until the transaction is +// either committed or rolled-back. +func (_q *ProjectSyncStateQuery) ForUpdate(opts ...sql.LockOption) *ProjectSyncStateQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForUpdate(opts...) + }) + return _q +} + +// ForShare behaves similarly to ForUpdate, except that it acquires a shared mode lock +// on any rows that are read. Other sessions can read the rows, but cannot modify them +// until your transaction commits. +func (_q *ProjectSyncStateQuery) ForShare(opts ...sql.LockOption) *ProjectSyncStateQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForShare(opts...) + }) + return _q +} + +// ProjectSyncStateGroupBy is the group-by builder for ProjectSyncState entities. +type ProjectSyncStateGroupBy struct { + selector + build *ProjectSyncStateQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (_g *ProjectSyncStateGroupBy) Aggregate(fns ...AggregateFunc) *ProjectSyncStateGroupBy { + _g.fns = append(_g.fns, fns...) + return _g +} + +// Scan applies the selector query and scans the result into the given value. +func (_g *ProjectSyncStateGroupBy) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, _g.build.ctx, ent.OpQueryGroupBy) + if err := _g.build.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*ProjectSyncStateQuery, *ProjectSyncStateGroupBy](ctx, _g.build, _g, _g.build.inters, v) +} + +func (_g *ProjectSyncStateGroupBy) sqlScan(ctx context.Context, root *ProjectSyncStateQuery, v any) error { + selector := root.sqlQuery(ctx).Select() + aggregation := make([]string, 0, len(_g.fns)) + for _, fn := range _g.fns { + aggregation = append(aggregation, fn(selector)) + } + if len(selector.SelectedColumns()) == 0 { + columns := make([]string, 0, len(*_g.flds)+len(_g.fns)) + for _, f := range *_g.flds { + columns = append(columns, selector.C(f)) + } + columns = append(columns, aggregation...) + selector.Select(columns...) + } + selector.GroupBy(selector.Columns(*_g.flds...)...) + if err := selector.Err(); err != nil { + return err + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := _g.build.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} + +// ProjectSyncStateSelect is the builder for selecting fields of ProjectSyncState entities. +type ProjectSyncStateSelect struct { + *ProjectSyncStateQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (_s *ProjectSyncStateSelect) Aggregate(fns ...AggregateFunc) *ProjectSyncStateSelect { + _s.fns = append(_s.fns, fns...) + return _s +} + +// Scan applies the selector query and scans the result into the given value. +func (_s *ProjectSyncStateSelect) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, _s.ctx, ent.OpQuerySelect) + if err := _s.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*ProjectSyncStateQuery, *ProjectSyncStateSelect](ctx, _s.ProjectSyncStateQuery, _s, _s.inters, v) +} + +func (_s *ProjectSyncStateSelect) sqlScan(ctx context.Context, root *ProjectSyncStateQuery, v any) error { + selector := root.sqlQuery(ctx) + aggregation := make([]string, 0, len(_s.fns)) + for _, fn := range _s.fns { + aggregation = append(aggregation, fn(selector)) + } + switch n := len(*_s.selector.flds); { + case n == 0 && len(aggregation) > 0: + selector.Select(aggregation...) + case n != 0 && len(aggregation) > 0: + selector.AppendSelect(aggregation...) + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := _s.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} diff --git a/pkg/ent/projectsyncstate_update.go b/pkg/ent/projectsyncstate_update.go new file mode 100644 index 000000000..4339cd8b0 --- /dev/null +++ b/pkg/ent/projectsyncstate_update.go @@ -0,0 +1,457 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/GoogleCloudPlatform/scion/pkg/ent/projectsyncstate" + "github.com/google/uuid" +) + +// ProjectSyncStateUpdate is the builder for updating ProjectSyncState entities. +type ProjectSyncStateUpdate struct { + config + hooks []Hook + mutation *ProjectSyncStateMutation +} + +// Where appends a list predicates to the ProjectSyncStateUpdate builder. +func (_u *ProjectSyncStateUpdate) Where(ps ...predicate.ProjectSyncState) *ProjectSyncStateUpdate { + _u.mutation.Where(ps...) + return _u +} + +// SetProjectID sets the "project_id" field. +func (_u *ProjectSyncStateUpdate) SetProjectID(v uuid.UUID) *ProjectSyncStateUpdate { + _u.mutation.SetProjectID(v) + return _u +} + +// SetNillableProjectID sets the "project_id" field if the given value is not nil. +func (_u *ProjectSyncStateUpdate) SetNillableProjectID(v *uuid.UUID) *ProjectSyncStateUpdate { + if v != nil { + _u.SetProjectID(*v) + } + return _u +} + +// SetBrokerID sets the "broker_id" field. +func (_u *ProjectSyncStateUpdate) SetBrokerID(v string) *ProjectSyncStateUpdate { + _u.mutation.SetBrokerID(v) + return _u +} + +// SetNillableBrokerID sets the "broker_id" field if the given value is not nil. +func (_u *ProjectSyncStateUpdate) SetNillableBrokerID(v *string) *ProjectSyncStateUpdate { + if v != nil { + _u.SetBrokerID(*v) + } + return _u +} + +// SetLastSyncTime sets the "last_sync_time" field. +func (_u *ProjectSyncStateUpdate) SetLastSyncTime(v time.Time) *ProjectSyncStateUpdate { + _u.mutation.SetLastSyncTime(v) + return _u +} + +// SetNillableLastSyncTime sets the "last_sync_time" field if the given value is not nil. +func (_u *ProjectSyncStateUpdate) SetNillableLastSyncTime(v *time.Time) *ProjectSyncStateUpdate { + if v != nil { + _u.SetLastSyncTime(*v) + } + return _u +} + +// ClearLastSyncTime clears the value of the "last_sync_time" field. +func (_u *ProjectSyncStateUpdate) ClearLastSyncTime() *ProjectSyncStateUpdate { + _u.mutation.ClearLastSyncTime() + return _u +} + +// SetLastCommitSha sets the "last_commit_sha" field. +func (_u *ProjectSyncStateUpdate) SetLastCommitSha(v string) *ProjectSyncStateUpdate { + _u.mutation.SetLastCommitSha(v) + return _u +} + +// SetNillableLastCommitSha sets the "last_commit_sha" field if the given value is not nil. +func (_u *ProjectSyncStateUpdate) SetNillableLastCommitSha(v *string) *ProjectSyncStateUpdate { + if v != nil { + _u.SetLastCommitSha(*v) + } + return _u +} + +// ClearLastCommitSha clears the value of the "last_commit_sha" field. +func (_u *ProjectSyncStateUpdate) ClearLastCommitSha() *ProjectSyncStateUpdate { + _u.mutation.ClearLastCommitSha() + return _u +} + +// SetFileCount sets the "file_count" field. +func (_u *ProjectSyncStateUpdate) SetFileCount(v int) *ProjectSyncStateUpdate { + _u.mutation.ResetFileCount() + _u.mutation.SetFileCount(v) + return _u +} + +// SetNillableFileCount sets the "file_count" field if the given value is not nil. +func (_u *ProjectSyncStateUpdate) SetNillableFileCount(v *int) *ProjectSyncStateUpdate { + if v != nil { + _u.SetFileCount(*v) + } + return _u +} + +// AddFileCount adds value to the "file_count" field. +func (_u *ProjectSyncStateUpdate) AddFileCount(v int) *ProjectSyncStateUpdate { + _u.mutation.AddFileCount(v) + return _u +} + +// SetTotalBytes sets the "total_bytes" field. +func (_u *ProjectSyncStateUpdate) SetTotalBytes(v int64) *ProjectSyncStateUpdate { + _u.mutation.ResetTotalBytes() + _u.mutation.SetTotalBytes(v) + return _u +} + +// SetNillableTotalBytes sets the "total_bytes" field if the given value is not nil. +func (_u *ProjectSyncStateUpdate) SetNillableTotalBytes(v *int64) *ProjectSyncStateUpdate { + if v != nil { + _u.SetTotalBytes(*v) + } + return _u +} + +// AddTotalBytes adds value to the "total_bytes" field. +func (_u *ProjectSyncStateUpdate) AddTotalBytes(v int64) *ProjectSyncStateUpdate { + _u.mutation.AddTotalBytes(v) + return _u +} + +// Mutation returns the ProjectSyncStateMutation object of the builder. +func (_u *ProjectSyncStateUpdate) Mutation() *ProjectSyncStateMutation { + return _u.mutation +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (_u *ProjectSyncStateUpdate) Save(ctx context.Context) (int, error) { + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *ProjectSyncStateUpdate) SaveX(ctx context.Context) int { + affected, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (_u *ProjectSyncStateUpdate) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *ProjectSyncStateUpdate) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +func (_u *ProjectSyncStateUpdate) sqlSave(ctx context.Context) (_node int, err error) { + _spec := sqlgraph.NewUpdateSpec(projectsyncstate.Table, projectsyncstate.Columns, sqlgraph.NewFieldSpec(projectsyncstate.FieldID, field.TypeUUID)) + if ps := _u.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := _u.mutation.ProjectID(); ok { + _spec.SetField(projectsyncstate.FieldProjectID, field.TypeUUID, value) + } + if value, ok := _u.mutation.BrokerID(); ok { + _spec.SetField(projectsyncstate.FieldBrokerID, field.TypeString, value) + } + if value, ok := _u.mutation.LastSyncTime(); ok { + _spec.SetField(projectsyncstate.FieldLastSyncTime, field.TypeTime, value) + } + if _u.mutation.LastSyncTimeCleared() { + _spec.ClearField(projectsyncstate.FieldLastSyncTime, field.TypeTime) + } + if value, ok := _u.mutation.LastCommitSha(); ok { + _spec.SetField(projectsyncstate.FieldLastCommitSha, field.TypeString, value) + } + if _u.mutation.LastCommitShaCleared() { + _spec.ClearField(projectsyncstate.FieldLastCommitSha, field.TypeString) + } + if value, ok := _u.mutation.FileCount(); ok { + _spec.SetField(projectsyncstate.FieldFileCount, field.TypeInt, value) + } + if value, ok := _u.mutation.AddedFileCount(); ok { + _spec.AddField(projectsyncstate.FieldFileCount, field.TypeInt, value) + } + if value, ok := _u.mutation.TotalBytes(); ok { + _spec.SetField(projectsyncstate.FieldTotalBytes, field.TypeInt64, value) + } + if value, ok := _u.mutation.AddedTotalBytes(); ok { + _spec.AddField(projectsyncstate.FieldTotalBytes, field.TypeInt64, value) + } + if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{projectsyncstate.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + _u.mutation.done = true + return _node, nil +} + +// ProjectSyncStateUpdateOne is the builder for updating a single ProjectSyncState entity. +type ProjectSyncStateUpdateOne struct { + config + fields []string + hooks []Hook + mutation *ProjectSyncStateMutation +} + +// SetProjectID sets the "project_id" field. +func (_u *ProjectSyncStateUpdateOne) SetProjectID(v uuid.UUID) *ProjectSyncStateUpdateOne { + _u.mutation.SetProjectID(v) + return _u +} + +// SetNillableProjectID sets the "project_id" field if the given value is not nil. +func (_u *ProjectSyncStateUpdateOne) SetNillableProjectID(v *uuid.UUID) *ProjectSyncStateUpdateOne { + if v != nil { + _u.SetProjectID(*v) + } + return _u +} + +// SetBrokerID sets the "broker_id" field. +func (_u *ProjectSyncStateUpdateOne) SetBrokerID(v string) *ProjectSyncStateUpdateOne { + _u.mutation.SetBrokerID(v) + return _u +} + +// SetNillableBrokerID sets the "broker_id" field if the given value is not nil. +func (_u *ProjectSyncStateUpdateOne) SetNillableBrokerID(v *string) *ProjectSyncStateUpdateOne { + if v != nil { + _u.SetBrokerID(*v) + } + return _u +} + +// SetLastSyncTime sets the "last_sync_time" field. +func (_u *ProjectSyncStateUpdateOne) SetLastSyncTime(v time.Time) *ProjectSyncStateUpdateOne { + _u.mutation.SetLastSyncTime(v) + return _u +} + +// SetNillableLastSyncTime sets the "last_sync_time" field if the given value is not nil. +func (_u *ProjectSyncStateUpdateOne) SetNillableLastSyncTime(v *time.Time) *ProjectSyncStateUpdateOne { + if v != nil { + _u.SetLastSyncTime(*v) + } + return _u +} + +// ClearLastSyncTime clears the value of the "last_sync_time" field. +func (_u *ProjectSyncStateUpdateOne) ClearLastSyncTime() *ProjectSyncStateUpdateOne { + _u.mutation.ClearLastSyncTime() + return _u +} + +// SetLastCommitSha sets the "last_commit_sha" field. +func (_u *ProjectSyncStateUpdateOne) SetLastCommitSha(v string) *ProjectSyncStateUpdateOne { + _u.mutation.SetLastCommitSha(v) + return _u +} + +// SetNillableLastCommitSha sets the "last_commit_sha" field if the given value is not nil. +func (_u *ProjectSyncStateUpdateOne) SetNillableLastCommitSha(v *string) *ProjectSyncStateUpdateOne { + if v != nil { + _u.SetLastCommitSha(*v) + } + return _u +} + +// ClearLastCommitSha clears the value of the "last_commit_sha" field. +func (_u *ProjectSyncStateUpdateOne) ClearLastCommitSha() *ProjectSyncStateUpdateOne { + _u.mutation.ClearLastCommitSha() + return _u +} + +// SetFileCount sets the "file_count" field. +func (_u *ProjectSyncStateUpdateOne) SetFileCount(v int) *ProjectSyncStateUpdateOne { + _u.mutation.ResetFileCount() + _u.mutation.SetFileCount(v) + return _u +} + +// SetNillableFileCount sets the "file_count" field if the given value is not nil. +func (_u *ProjectSyncStateUpdateOne) SetNillableFileCount(v *int) *ProjectSyncStateUpdateOne { + if v != nil { + _u.SetFileCount(*v) + } + return _u +} + +// AddFileCount adds value to the "file_count" field. +func (_u *ProjectSyncStateUpdateOne) AddFileCount(v int) *ProjectSyncStateUpdateOne { + _u.mutation.AddFileCount(v) + return _u +} + +// SetTotalBytes sets the "total_bytes" field. +func (_u *ProjectSyncStateUpdateOne) SetTotalBytes(v int64) *ProjectSyncStateUpdateOne { + _u.mutation.ResetTotalBytes() + _u.mutation.SetTotalBytes(v) + return _u +} + +// SetNillableTotalBytes sets the "total_bytes" field if the given value is not nil. +func (_u *ProjectSyncStateUpdateOne) SetNillableTotalBytes(v *int64) *ProjectSyncStateUpdateOne { + if v != nil { + _u.SetTotalBytes(*v) + } + return _u +} + +// AddTotalBytes adds value to the "total_bytes" field. +func (_u *ProjectSyncStateUpdateOne) AddTotalBytes(v int64) *ProjectSyncStateUpdateOne { + _u.mutation.AddTotalBytes(v) + return _u +} + +// Mutation returns the ProjectSyncStateMutation object of the builder. +func (_u *ProjectSyncStateUpdateOne) Mutation() *ProjectSyncStateMutation { + return _u.mutation +} + +// Where appends a list predicates to the ProjectSyncStateUpdate builder. +func (_u *ProjectSyncStateUpdateOne) Where(ps ...predicate.ProjectSyncState) *ProjectSyncStateUpdateOne { + _u.mutation.Where(ps...) + return _u +} + +// Select allows selecting one or more fields (columns) of the returned entity. +// The default is selecting all fields defined in the entity schema. +func (_u *ProjectSyncStateUpdateOne) Select(field string, fields ...string) *ProjectSyncStateUpdateOne { + _u.fields = append([]string{field}, fields...) + return _u +} + +// Save executes the query and returns the updated ProjectSyncState entity. +func (_u *ProjectSyncStateUpdateOne) Save(ctx context.Context) (*ProjectSyncState, error) { + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *ProjectSyncStateUpdateOne) SaveX(ctx context.Context) *ProjectSyncState { + node, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (_u *ProjectSyncStateUpdateOne) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *ProjectSyncStateUpdateOne) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +func (_u *ProjectSyncStateUpdateOne) sqlSave(ctx context.Context) (_node *ProjectSyncState, err error) { + _spec := sqlgraph.NewUpdateSpec(projectsyncstate.Table, projectsyncstate.Columns, sqlgraph.NewFieldSpec(projectsyncstate.FieldID, field.TypeUUID)) + id, ok := _u.mutation.ID() + if !ok { + return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "ProjectSyncState.id" for update`)} + } + _spec.Node.ID.Value = id + if fields := _u.fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, projectsyncstate.FieldID) + for _, f := range fields { + if !projectsyncstate.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + if f != projectsyncstate.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, f) + } + } + } + if ps := _u.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := _u.mutation.ProjectID(); ok { + _spec.SetField(projectsyncstate.FieldProjectID, field.TypeUUID, value) + } + if value, ok := _u.mutation.BrokerID(); ok { + _spec.SetField(projectsyncstate.FieldBrokerID, field.TypeString, value) + } + if value, ok := _u.mutation.LastSyncTime(); ok { + _spec.SetField(projectsyncstate.FieldLastSyncTime, field.TypeTime, value) + } + if _u.mutation.LastSyncTimeCleared() { + _spec.ClearField(projectsyncstate.FieldLastSyncTime, field.TypeTime) + } + if value, ok := _u.mutation.LastCommitSha(); ok { + _spec.SetField(projectsyncstate.FieldLastCommitSha, field.TypeString, value) + } + if _u.mutation.LastCommitShaCleared() { + _spec.ClearField(projectsyncstate.FieldLastCommitSha, field.TypeString) + } + if value, ok := _u.mutation.FileCount(); ok { + _spec.SetField(projectsyncstate.FieldFileCount, field.TypeInt, value) + } + if value, ok := _u.mutation.AddedFileCount(); ok { + _spec.AddField(projectsyncstate.FieldFileCount, field.TypeInt, value) + } + if value, ok := _u.mutation.TotalBytes(); ok { + _spec.SetField(projectsyncstate.FieldTotalBytes, field.TypeInt64, value) + } + if value, ok := _u.mutation.AddedTotalBytes(); ok { + _spec.AddField(projectsyncstate.FieldTotalBytes, field.TypeInt64, value) + } + _node = &ProjectSyncState{config: _u.config} + _spec.Assign = _node.assignValues + _spec.ScanValues = _node.scanValues + if err = sqlgraph.UpdateNode(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{projectsyncstate.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + _u.mutation.done = true + return _node, nil +} diff --git a/pkg/ent/runtime.go b/pkg/ent/runtime.go index 09fe221e7..df13e271e 100644 --- a/pkg/ent/runtime.go +++ b/pkg/ent/runtime.go @@ -7,12 +7,35 @@ import ( "github.com/GoogleCloudPlatform/scion/pkg/ent/accesspolicy" "github.com/GoogleCloudPlatform/scion/pkg/ent/agent" + "github.com/GoogleCloudPlatform/scion/pkg/ent/allowlistentry" + "github.com/GoogleCloudPlatform/scion/pkg/ent/apikey" + "github.com/GoogleCloudPlatform/scion/pkg/ent/brokerjointoken" + "github.com/GoogleCloudPlatform/scion/pkg/ent/brokersecret" + "github.com/GoogleCloudPlatform/scion/pkg/ent/envvar" + "github.com/GoogleCloudPlatform/scion/pkg/ent/gcpserviceaccount" + "github.com/GoogleCloudPlatform/scion/pkg/ent/githubinstallation" "github.com/GoogleCloudPlatform/scion/pkg/ent/group" "github.com/GoogleCloudPlatform/scion/pkg/ent/groupmembership" + "github.com/GoogleCloudPlatform/scion/pkg/ent/harnessconfig" + "github.com/GoogleCloudPlatform/scion/pkg/ent/invitecode" + "github.com/GoogleCloudPlatform/scion/pkg/ent/maintenanceoperation" + "github.com/GoogleCloudPlatform/scion/pkg/ent/maintenanceoperationrun" + "github.com/GoogleCloudPlatform/scion/pkg/ent/message" + "github.com/GoogleCloudPlatform/scion/pkg/ent/notification" + "github.com/GoogleCloudPlatform/scion/pkg/ent/notificationsubscription" "github.com/GoogleCloudPlatform/scion/pkg/ent/policybinding" "github.com/GoogleCloudPlatform/scion/pkg/ent/project" + "github.com/GoogleCloudPlatform/scion/pkg/ent/projectcontributor" + "github.com/GoogleCloudPlatform/scion/pkg/ent/projectsyncstate" + "github.com/GoogleCloudPlatform/scion/pkg/ent/runtimebroker" + "github.com/GoogleCloudPlatform/scion/pkg/ent/schedule" + "github.com/GoogleCloudPlatform/scion/pkg/ent/scheduledevent" "github.com/GoogleCloudPlatform/scion/pkg/ent/schema" + "github.com/GoogleCloudPlatform/scion/pkg/ent/secret" + "github.com/GoogleCloudPlatform/scion/pkg/ent/subscriptiontemplate" + "github.com/GoogleCloudPlatform/scion/pkg/ent/template" "github.com/GoogleCloudPlatform/scion/pkg/ent/user" + "github.com/GoogleCloudPlatform/scion/pkg/ent/useraccesstoken" "github.com/google/uuid" ) @@ -66,20 +89,222 @@ func init() { agentDescVisibility := agentFields[9].Descriptor() // agent.DefaultVisibility holds the default value on creation for the visibility field. agent.DefaultVisibility = agentDescVisibility.Default.(string) + // agentDescCurrentTurns is the schema descriptor for current_turns field. + agentDescCurrentTurns := agentFields[19].Descriptor() + // agent.DefaultCurrentTurns holds the default value on creation for the current_turns field. + agent.DefaultCurrentTurns = agentDescCurrentTurns.Default.(int) + // agentDescCurrentModelCalls is the schema descriptor for current_model_calls field. + agentDescCurrentModelCalls := agentFields[20].Descriptor() + // agent.DefaultCurrentModelCalls holds the default value on creation for the current_model_calls field. + agent.DefaultCurrentModelCalls = agentDescCurrentModelCalls.Default.(int) + // agentDescDetached is the schema descriptor for detached field. + agentDescDetached := agentFields[22].Descriptor() + // agent.DefaultDetached holds the default value on creation for the detached field. + agent.DefaultDetached = agentDescDetached.Default.(bool) + // agentDescWebPtyEnabled is the schema descriptor for web_pty_enabled field. + agentDescWebPtyEnabled := agentFields[25].Descriptor() + // agent.DefaultWebPtyEnabled holds the default value on creation for the web_pty_enabled field. + agent.DefaultWebPtyEnabled = agentDescWebPtyEnabled.Default.(bool) // agentDescCreated is the schema descriptor for created field. - agentDescCreated := agentFields[10].Descriptor() + agentDescCreated := agentFields[30].Descriptor() // agent.DefaultCreated holds the default value on creation for the created field. agent.DefaultCreated = agentDescCreated.Default.(func() time.Time) // agentDescUpdated is the schema descriptor for updated field. - agentDescUpdated := agentFields[11].Descriptor() + agentDescUpdated := agentFields[31].Descriptor() // agent.DefaultUpdated holds the default value on creation for the updated field. agent.DefaultUpdated = agentDescUpdated.Default.(func() time.Time) // agent.UpdateDefaultUpdated holds the default value on update for the updated field. agent.UpdateDefaultUpdated = agentDescUpdated.UpdateDefault.(func() time.Time) + // agentDescStateVersion is the schema descriptor for state_version field. + agentDescStateVersion := agentFields[36].Descriptor() + // agent.DefaultStateVersion holds the default value on creation for the state_version field. + agent.DefaultStateVersion = agentDescStateVersion.Default.(int64) // agentDescID is the schema descriptor for id field. agentDescID := agentFields[0].Descriptor() // agent.DefaultID holds the default value on creation for the id field. agent.DefaultID = agentDescID.Default.(func() uuid.UUID) + allowlistentryFields := schema.AllowListEntry{}.Fields() + _ = allowlistentryFields + // allowlistentryDescEmail is the schema descriptor for email field. + allowlistentryDescEmail := allowlistentryFields[1].Descriptor() + // allowlistentry.EmailValidator is a validator for the "email" field. It is called by the builders before save. + allowlistentry.EmailValidator = allowlistentryDescEmail.Validators[0].(func(string) error) + // allowlistentryDescNote is the schema descriptor for note field. + allowlistentryDescNote := allowlistentryFields[2].Descriptor() + // allowlistentry.DefaultNote holds the default value on creation for the note field. + allowlistentry.DefaultNote = allowlistentryDescNote.Default.(string) + // allowlistentryDescAddedBy is the schema descriptor for added_by field. + allowlistentryDescAddedBy := allowlistentryFields[3].Descriptor() + // allowlistentry.AddedByValidator is a validator for the "added_by" field. It is called by the builders before save. + allowlistentry.AddedByValidator = allowlistentryDescAddedBy.Validators[0].(func(string) error) + // allowlistentryDescCreated is the schema descriptor for created field. + allowlistentryDescCreated := allowlistentryFields[5].Descriptor() + // allowlistentry.DefaultCreated holds the default value on creation for the created field. + allowlistentry.DefaultCreated = allowlistentryDescCreated.Default.(func() time.Time) + // allowlistentryDescID is the schema descriptor for id field. + allowlistentryDescID := allowlistentryFields[0].Descriptor() + // allowlistentry.DefaultID holds the default value on creation for the id field. + allowlistentry.DefaultID = allowlistentryDescID.Default.(func() uuid.UUID) + apikeyFields := schema.ApiKey{}.Fields() + _ = apikeyFields + // apikeyDescKeyHash is the schema descriptor for key_hash field. + apikeyDescKeyHash := apikeyFields[4].Descriptor() + // apikey.KeyHashValidator is a validator for the "key_hash" field. It is called by the builders before save. + apikey.KeyHashValidator = apikeyDescKeyHash.Validators[0].(func(string) error) + // apikeyDescRevoked is the schema descriptor for revoked field. + apikeyDescRevoked := apikeyFields[6].Descriptor() + // apikey.DefaultRevoked holds the default value on creation for the revoked field. + apikey.DefaultRevoked = apikeyDescRevoked.Default.(bool) + // apikeyDescCreated is the schema descriptor for created field. + apikeyDescCreated := apikeyFields[9].Descriptor() + // apikey.DefaultCreated holds the default value on creation for the created field. + apikey.DefaultCreated = apikeyDescCreated.Default.(func() time.Time) + // apikeyDescID is the schema descriptor for id field. + apikeyDescID := apikeyFields[0].Descriptor() + // apikey.DefaultID holds the default value on creation for the id field. + apikey.DefaultID = apikeyDescID.Default.(func() uuid.UUID) + brokerjointokenFields := schema.BrokerJoinToken{}.Fields() + _ = brokerjointokenFields + // brokerjointokenDescTokenHash is the schema descriptor for token_hash field. + brokerjointokenDescTokenHash := brokerjointokenFields[1].Descriptor() + // brokerjointoken.TokenHashValidator is a validator for the "token_hash" field. It is called by the builders before save. + brokerjointoken.TokenHashValidator = brokerjointokenDescTokenHash.Validators[0].(func(string) error) + // brokerjointokenDescCreatedBy is the schema descriptor for created_by field. + brokerjointokenDescCreatedBy := brokerjointokenFields[3].Descriptor() + // brokerjointoken.CreatedByValidator is a validator for the "created_by" field. It is called by the builders before save. + brokerjointoken.CreatedByValidator = brokerjointokenDescCreatedBy.Validators[0].(func(string) error) + // brokerjointokenDescCreated is the schema descriptor for created field. + brokerjointokenDescCreated := brokerjointokenFields[4].Descriptor() + // brokerjointoken.DefaultCreated holds the default value on creation for the created field. + brokerjointoken.DefaultCreated = brokerjointokenDescCreated.Default.(func() time.Time) + brokersecretFields := schema.BrokerSecret{}.Fields() + _ = brokersecretFields + // brokersecretDescSecretKey is the schema descriptor for secret_key field. + brokersecretDescSecretKey := brokersecretFields[1].Descriptor() + // brokersecret.SecretKeyValidator is a validator for the "secret_key" field. It is called by the builders before save. + brokersecret.SecretKeyValidator = brokersecretDescSecretKey.Validators[0].(func([]byte) error) + // brokersecretDescAlgorithm is the schema descriptor for algorithm field. + brokersecretDescAlgorithm := brokersecretFields[2].Descriptor() + // brokersecret.DefaultAlgorithm holds the default value on creation for the algorithm field. + brokersecret.DefaultAlgorithm = brokersecretDescAlgorithm.Default.(string) + // brokersecretDescStatus is the schema descriptor for status field. + brokersecretDescStatus := brokersecretFields[5].Descriptor() + // brokersecret.DefaultStatus holds the default value on creation for the status field. + brokersecret.DefaultStatus = brokersecretDescStatus.Default.(string) + // brokersecretDescCreated is the schema descriptor for created field. + brokersecretDescCreated := brokersecretFields[6].Descriptor() + // brokersecret.DefaultCreated holds the default value on creation for the created field. + brokersecret.DefaultCreated = brokersecretDescCreated.Default.(func() time.Time) + envvarFields := schema.EnvVar{}.Fields() + _ = envvarFields + // envvarDescKey is the schema descriptor for key field. + envvarDescKey := envvarFields[1].Descriptor() + // envvar.KeyValidator is a validator for the "key" field. It is called by the builders before save. + envvar.KeyValidator = envvarDescKey.Validators[0].(func(string) error) + // envvarDescScope is the schema descriptor for scope field. + envvarDescScope := envvarFields[3].Descriptor() + // envvar.ScopeValidator is a validator for the "scope" field. It is called by the builders before save. + envvar.ScopeValidator = envvarDescScope.Validators[0].(func(string) error) + // envvarDescSensitive is the schema descriptor for sensitive field. + envvarDescSensitive := envvarFields[6].Descriptor() + // envvar.DefaultSensitive holds the default value on creation for the sensitive field. + envvar.DefaultSensitive = envvarDescSensitive.Default.(bool) + // envvarDescSecret is the schema descriptor for secret field. + envvarDescSecret := envvarFields[8].Descriptor() + // envvar.DefaultSecret holds the default value on creation for the secret field. + envvar.DefaultSecret = envvarDescSecret.Default.(bool) + // envvarDescCreated is the schema descriptor for created field. + envvarDescCreated := envvarFields[10].Descriptor() + // envvar.DefaultCreated holds the default value on creation for the created field. + envvar.DefaultCreated = envvarDescCreated.Default.(func() time.Time) + // envvarDescUpdated is the schema descriptor for updated field. + envvarDescUpdated := envvarFields[11].Descriptor() + // envvar.DefaultUpdated holds the default value on creation for the updated field. + envvar.DefaultUpdated = envvarDescUpdated.Default.(func() time.Time) + // envvar.UpdateDefaultUpdated holds the default value on update for the updated field. + envvar.UpdateDefaultUpdated = envvarDescUpdated.UpdateDefault.(func() time.Time) + // envvarDescID is the schema descriptor for id field. + envvarDescID := envvarFields[0].Descriptor() + // envvar.DefaultID holds the default value on creation for the id field. + envvar.DefaultID = envvarDescID.Default.(func() uuid.UUID) + gcpserviceaccountFields := schema.GCPServiceAccount{}.Fields() + _ = gcpserviceaccountFields + // gcpserviceaccountDescScope is the schema descriptor for scope field. + gcpserviceaccountDescScope := gcpserviceaccountFields[1].Descriptor() + // gcpserviceaccount.ScopeValidator is a validator for the "scope" field. It is called by the builders before save. + gcpserviceaccount.ScopeValidator = gcpserviceaccountDescScope.Validators[0].(func(string) error) + // gcpserviceaccountDescScopeID is the schema descriptor for scope_id field. + gcpserviceaccountDescScopeID := gcpserviceaccountFields[2].Descriptor() + // gcpserviceaccount.ScopeIDValidator is a validator for the "scope_id" field. It is called by the builders before save. + gcpserviceaccount.ScopeIDValidator = gcpserviceaccountDescScopeID.Validators[0].(func(string) error) + // gcpserviceaccountDescEmail is the schema descriptor for email field. + gcpserviceaccountDescEmail := gcpserviceaccountFields[3].Descriptor() + // gcpserviceaccount.EmailValidator is a validator for the "email" field. It is called by the builders before save. + gcpserviceaccount.EmailValidator = gcpserviceaccountDescEmail.Validators[0].(func(string) error) + // gcpserviceaccountDescProjectID is the schema descriptor for project_id field. + gcpserviceaccountDescProjectID := gcpserviceaccountFields[4].Descriptor() + // gcpserviceaccount.ProjectIDValidator is a validator for the "project_id" field. It is called by the builders before save. + gcpserviceaccount.ProjectIDValidator = gcpserviceaccountDescProjectID.Validators[0].(func(string) error) + // gcpserviceaccountDescDisplayName is the schema descriptor for display_name field. + gcpserviceaccountDescDisplayName := gcpserviceaccountFields[5].Descriptor() + // gcpserviceaccount.DefaultDisplayName holds the default value on creation for the display_name field. + gcpserviceaccount.DefaultDisplayName = gcpserviceaccountDescDisplayName.Default.(string) + // gcpserviceaccountDescDefaultScopes is the schema descriptor for default_scopes field. + gcpserviceaccountDescDefaultScopes := gcpserviceaccountFields[6].Descriptor() + // gcpserviceaccount.DefaultDefaultScopes holds the default value on creation for the default_scopes field. + gcpserviceaccount.DefaultDefaultScopes = gcpserviceaccountDescDefaultScopes.Default.(string) + // gcpserviceaccountDescVerified is the schema descriptor for verified field. + gcpserviceaccountDescVerified := gcpserviceaccountFields[7].Descriptor() + // gcpserviceaccount.DefaultVerified holds the default value on creation for the verified field. + gcpserviceaccount.DefaultVerified = gcpserviceaccountDescVerified.Default.(bool) + // gcpserviceaccountDescCreatedBy is the schema descriptor for created_by field. + gcpserviceaccountDescCreatedBy := gcpserviceaccountFields[9].Descriptor() + // gcpserviceaccount.DefaultCreatedBy holds the default value on creation for the created_by field. + gcpserviceaccount.DefaultCreatedBy = gcpserviceaccountDescCreatedBy.Default.(string) + // gcpserviceaccountDescManaged is the schema descriptor for managed field. + gcpserviceaccountDescManaged := gcpserviceaccountFields[10].Descriptor() + // gcpserviceaccount.DefaultManaged holds the default value on creation for the managed field. + gcpserviceaccount.DefaultManaged = gcpserviceaccountDescManaged.Default.(bool) + // gcpserviceaccountDescManagedBy is the schema descriptor for managed_by field. + gcpserviceaccountDescManagedBy := gcpserviceaccountFields[11].Descriptor() + // gcpserviceaccount.DefaultManagedBy holds the default value on creation for the managed_by field. + gcpserviceaccount.DefaultManagedBy = gcpserviceaccountDescManagedBy.Default.(string) + // gcpserviceaccountDescCreated is the schema descriptor for created field. + gcpserviceaccountDescCreated := gcpserviceaccountFields[12].Descriptor() + // gcpserviceaccount.DefaultCreated holds the default value on creation for the created field. + gcpserviceaccount.DefaultCreated = gcpserviceaccountDescCreated.Default.(func() time.Time) + // gcpserviceaccountDescID is the schema descriptor for id field. + gcpserviceaccountDescID := gcpserviceaccountFields[0].Descriptor() + // gcpserviceaccount.DefaultID holds the default value on creation for the id field. + gcpserviceaccount.DefaultID = gcpserviceaccountDescID.Default.(func() uuid.UUID) + githubinstallationFields := schema.GithubInstallation{}.Fields() + _ = githubinstallationFields + // githubinstallationDescAccountLogin is the schema descriptor for account_login field. + githubinstallationDescAccountLogin := githubinstallationFields[1].Descriptor() + // githubinstallation.AccountLoginValidator is a validator for the "account_login" field. It is called by the builders before save. + githubinstallation.AccountLoginValidator = githubinstallationDescAccountLogin.Validators[0].(func(string) error) + // githubinstallationDescAccountType is the schema descriptor for account_type field. + githubinstallationDescAccountType := githubinstallationFields[2].Descriptor() + // githubinstallation.DefaultAccountType holds the default value on creation for the account_type field. + githubinstallation.DefaultAccountType = githubinstallationDescAccountType.Default.(string) + // githubinstallationDescRepositories is the schema descriptor for repositories field. + githubinstallationDescRepositories := githubinstallationFields[4].Descriptor() + // githubinstallation.DefaultRepositories holds the default value on creation for the repositories field. + githubinstallation.DefaultRepositories = githubinstallationDescRepositories.Default.(string) + // githubinstallationDescStatus is the schema descriptor for status field. + githubinstallationDescStatus := githubinstallationFields[5].Descriptor() + // githubinstallation.DefaultStatus holds the default value on creation for the status field. + githubinstallation.DefaultStatus = githubinstallationDescStatus.Default.(string) + // githubinstallationDescCreated is the schema descriptor for created field. + githubinstallationDescCreated := githubinstallationFields[6].Descriptor() + // githubinstallation.DefaultCreated holds the default value on creation for the created field. + githubinstallation.DefaultCreated = githubinstallationDescCreated.Default.(func() time.Time) + // githubinstallationDescUpdated is the schema descriptor for updated field. + githubinstallationDescUpdated := githubinstallationFields[7].Descriptor() + // githubinstallation.DefaultUpdated holds the default value on creation for the updated field. + githubinstallation.DefaultUpdated = githubinstallationDescUpdated.Default.(func() time.Time) + // githubinstallation.UpdateDefaultUpdated holds the default value on update for the updated field. + githubinstallation.UpdateDefaultUpdated = githubinstallationDescUpdated.UpdateDefault.(func() time.Time) groupFields := schema.Group{}.Fields() _ = groupFields // groupDescName is the schema descriptor for name field. @@ -114,6 +339,242 @@ func init() { groupmembershipDescID := groupmembershipFields[0].Descriptor() // groupmembership.DefaultID holds the default value on creation for the id field. groupmembership.DefaultID = groupmembershipDescID.Default.(func() uuid.UUID) + harnessconfigFields := schema.HarnessConfig{}.Fields() + _ = harnessconfigFields + // harnessconfigDescName is the schema descriptor for name field. + harnessconfigDescName := harnessconfigFields[1].Descriptor() + // harnessconfig.NameValidator is a validator for the "name" field. It is called by the builders before save. + harnessconfig.NameValidator = harnessconfigDescName.Validators[0].(func(string) error) + // harnessconfigDescSlug is the schema descriptor for slug field. + harnessconfigDescSlug := harnessconfigFields[2].Descriptor() + // harnessconfig.SlugValidator is a validator for the "slug" field. It is called by the builders before save. + harnessconfig.SlugValidator = harnessconfigDescSlug.Validators[0].(func(string) error) + // harnessconfigDescHarness is the schema descriptor for harness field. + harnessconfigDescHarness := harnessconfigFields[5].Descriptor() + // harnessconfig.HarnessValidator is a validator for the "harness" field. It is called by the builders before save. + harnessconfig.HarnessValidator = harnessconfigDescHarness.Validators[0].(func(string) error) + // harnessconfigDescScope is the schema descriptor for scope field. + harnessconfigDescScope := harnessconfigFields[8].Descriptor() + // harnessconfig.DefaultScope holds the default value on creation for the scope field. + harnessconfig.DefaultScope = harnessconfigDescScope.Default.(string) + // harnessconfigDescLocked is the schema descriptor for locked field. + harnessconfigDescLocked := harnessconfigFields[14].Descriptor() + // harnessconfig.DefaultLocked holds the default value on creation for the locked field. + harnessconfig.DefaultLocked = harnessconfigDescLocked.Default.(bool) + // harnessconfigDescVisibility is the schema descriptor for visibility field. + harnessconfigDescVisibility := harnessconfigFields[19].Descriptor() + // harnessconfig.DefaultVisibility holds the default value on creation for the visibility field. + harnessconfig.DefaultVisibility = harnessconfigDescVisibility.Default.(string) + // harnessconfigDescCreated is the schema descriptor for created field. + harnessconfigDescCreated := harnessconfigFields[20].Descriptor() + // harnessconfig.DefaultCreated holds the default value on creation for the created field. + harnessconfig.DefaultCreated = harnessconfigDescCreated.Default.(func() time.Time) + // harnessconfigDescUpdated is the schema descriptor for updated field. + harnessconfigDescUpdated := harnessconfigFields[21].Descriptor() + // harnessconfig.DefaultUpdated holds the default value on creation for the updated field. + harnessconfig.DefaultUpdated = harnessconfigDescUpdated.Default.(func() time.Time) + // harnessconfig.UpdateDefaultUpdated holds the default value on update for the updated field. + harnessconfig.UpdateDefaultUpdated = harnessconfigDescUpdated.UpdateDefault.(func() time.Time) + // harnessconfigDescID is the schema descriptor for id field. + harnessconfigDescID := harnessconfigFields[0].Descriptor() + // harnessconfig.DefaultID holds the default value on creation for the id field. + harnessconfig.DefaultID = harnessconfigDescID.Default.(func() uuid.UUID) + invitecodeFields := schema.InviteCode{}.Fields() + _ = invitecodeFields + // invitecodeDescCodeHash is the schema descriptor for code_hash field. + invitecodeDescCodeHash := invitecodeFields[1].Descriptor() + // invitecode.CodeHashValidator is a validator for the "code_hash" field. It is called by the builders before save. + invitecode.CodeHashValidator = invitecodeDescCodeHash.Validators[0].(func(string) error) + // invitecodeDescCodePrefix is the schema descriptor for code_prefix field. + invitecodeDescCodePrefix := invitecodeFields[2].Descriptor() + // invitecode.CodePrefixValidator is a validator for the "code_prefix" field. It is called by the builders before save. + invitecode.CodePrefixValidator = invitecodeDescCodePrefix.Validators[0].(func(string) error) + // invitecodeDescMaxUses is the schema descriptor for max_uses field. + invitecodeDescMaxUses := invitecodeFields[3].Descriptor() + // invitecode.DefaultMaxUses holds the default value on creation for the max_uses field. + invitecode.DefaultMaxUses = invitecodeDescMaxUses.Default.(int) + // invitecodeDescUseCount is the schema descriptor for use_count field. + invitecodeDescUseCount := invitecodeFields[4].Descriptor() + // invitecode.DefaultUseCount holds the default value on creation for the use_count field. + invitecode.DefaultUseCount = invitecodeDescUseCount.Default.(int) + // invitecodeDescRevoked is the schema descriptor for revoked field. + invitecodeDescRevoked := invitecodeFields[6].Descriptor() + // invitecode.DefaultRevoked holds the default value on creation for the revoked field. + invitecode.DefaultRevoked = invitecodeDescRevoked.Default.(bool) + // invitecodeDescCreatedBy is the schema descriptor for created_by field. + invitecodeDescCreatedBy := invitecodeFields[7].Descriptor() + // invitecode.CreatedByValidator is a validator for the "created_by" field. It is called by the builders before save. + invitecode.CreatedByValidator = invitecodeDescCreatedBy.Validators[0].(func(string) error) + // invitecodeDescNote is the schema descriptor for note field. + invitecodeDescNote := invitecodeFields[8].Descriptor() + // invitecode.DefaultNote holds the default value on creation for the note field. + invitecode.DefaultNote = invitecodeDescNote.Default.(string) + // invitecodeDescCreated is the schema descriptor for created field. + invitecodeDescCreated := invitecodeFields[9].Descriptor() + // invitecode.DefaultCreated holds the default value on creation for the created field. + invitecode.DefaultCreated = invitecodeDescCreated.Default.(func() time.Time) + // invitecodeDescID is the schema descriptor for id field. + invitecodeDescID := invitecodeFields[0].Descriptor() + // invitecode.DefaultID holds the default value on creation for the id field. + invitecode.DefaultID = invitecodeDescID.Default.(func() uuid.UUID) + maintenanceoperationFields := schema.MaintenanceOperation{}.Fields() + _ = maintenanceoperationFields + // maintenanceoperationDescKey is the schema descriptor for key field. + maintenanceoperationDescKey := maintenanceoperationFields[1].Descriptor() + // maintenanceoperation.KeyValidator is a validator for the "key" field. It is called by the builders before save. + maintenanceoperation.KeyValidator = maintenanceoperationDescKey.Validators[0].(func(string) error) + // maintenanceoperationDescTitle is the schema descriptor for title field. + maintenanceoperationDescTitle := maintenanceoperationFields[2].Descriptor() + // maintenanceoperation.TitleValidator is a validator for the "title" field. It is called by the builders before save. + maintenanceoperation.TitleValidator = maintenanceoperationDescTitle.Validators[0].(func(string) error) + // maintenanceoperationDescDescription is the schema descriptor for description field. + maintenanceoperationDescDescription := maintenanceoperationFields[3].Descriptor() + // maintenanceoperation.DefaultDescription holds the default value on creation for the description field. + maintenanceoperation.DefaultDescription = maintenanceoperationDescDescription.Default.(string) + // maintenanceoperationDescCategory is the schema descriptor for category field. + maintenanceoperationDescCategory := maintenanceoperationFields[4].Descriptor() + // maintenanceoperation.CategoryValidator is a validator for the "category" field. It is called by the builders before save. + maintenanceoperation.CategoryValidator = maintenanceoperationDescCategory.Validators[0].(func(string) error) + // maintenanceoperationDescStatus is the schema descriptor for status field. + maintenanceoperationDescStatus := maintenanceoperationFields[5].Descriptor() + // maintenanceoperation.DefaultStatus holds the default value on creation for the status field. + maintenanceoperation.DefaultStatus = maintenanceoperationDescStatus.Default.(string) + // maintenanceoperationDescMetadata is the schema descriptor for metadata field. + maintenanceoperationDescMetadata := maintenanceoperationFields[10].Descriptor() + // maintenanceoperation.DefaultMetadata holds the default value on creation for the metadata field. + maintenanceoperation.DefaultMetadata = maintenanceoperationDescMetadata.Default.(string) + // maintenanceoperationDescCreated is the schema descriptor for created field. + maintenanceoperationDescCreated := maintenanceoperationFields[11].Descriptor() + // maintenanceoperation.DefaultCreated holds the default value on creation for the created field. + maintenanceoperation.DefaultCreated = maintenanceoperationDescCreated.Default.(func() time.Time) + // maintenanceoperationDescID is the schema descriptor for id field. + maintenanceoperationDescID := maintenanceoperationFields[0].Descriptor() + // maintenanceoperation.DefaultID holds the default value on creation for the id field. + maintenanceoperation.DefaultID = maintenanceoperationDescID.Default.(func() uuid.UUID) + maintenanceoperationrunFields := schema.MaintenanceOperationRun{}.Fields() + _ = maintenanceoperationrunFields + // maintenanceoperationrunDescOperationKey is the schema descriptor for operation_key field. + maintenanceoperationrunDescOperationKey := maintenanceoperationrunFields[1].Descriptor() + // maintenanceoperationrun.OperationKeyValidator is a validator for the "operation_key" field. It is called by the builders before save. + maintenanceoperationrun.OperationKeyValidator = maintenanceoperationrunDescOperationKey.Validators[0].(func(string) error) + // maintenanceoperationrunDescStatus is the schema descriptor for status field. + maintenanceoperationrunDescStatus := maintenanceoperationrunFields[2].Descriptor() + // maintenanceoperationrun.DefaultStatus holds the default value on creation for the status field. + maintenanceoperationrun.DefaultStatus = maintenanceoperationrunDescStatus.Default.(string) + // maintenanceoperationrunDescStartedAt is the schema descriptor for started_at field. + maintenanceoperationrunDescStartedAt := maintenanceoperationrunFields[3].Descriptor() + // maintenanceoperationrun.DefaultStartedAt holds the default value on creation for the started_at field. + maintenanceoperationrun.DefaultStartedAt = maintenanceoperationrunDescStartedAt.Default.(func() time.Time) + // maintenanceoperationrunDescLog is the schema descriptor for log field. + maintenanceoperationrunDescLog := maintenanceoperationrunFields[7].Descriptor() + // maintenanceoperationrun.DefaultLog holds the default value on creation for the log field. + maintenanceoperationrun.DefaultLog = maintenanceoperationrunDescLog.Default.(string) + // maintenanceoperationrunDescID is the schema descriptor for id field. + maintenanceoperationrunDescID := maintenanceoperationrunFields[0].Descriptor() + // maintenanceoperationrun.DefaultID holds the default value on creation for the id field. + maintenanceoperationrun.DefaultID = maintenanceoperationrunDescID.Default.(func() uuid.UUID) + messageFields := schema.Message{}.Fields() + _ = messageFields + // messageDescSender is the schema descriptor for sender field. + messageDescSender := messageFields[2].Descriptor() + // message.SenderValidator is a validator for the "sender" field. It is called by the builders before save. + message.SenderValidator = messageDescSender.Validators[0].(func(string) error) + // messageDescRecipient is the schema descriptor for recipient field. + messageDescRecipient := messageFields[4].Descriptor() + // message.RecipientValidator is a validator for the "recipient" field. It is called by the builders before save. + message.RecipientValidator = messageDescRecipient.Validators[0].(func(string) error) + // messageDescMsg is the schema descriptor for msg field. + messageDescMsg := messageFields[6].Descriptor() + // message.MsgValidator is a validator for the "msg" field. It is called by the builders before save. + message.MsgValidator = messageDescMsg.Validators[0].(func(string) error) + // messageDescType is the schema descriptor for type field. + messageDescType := messageFields[7].Descriptor() + // message.DefaultType holds the default value on creation for the type field. + message.DefaultType = messageDescType.Default.(string) + // messageDescUrgent is the schema descriptor for urgent field. + messageDescUrgent := messageFields[8].Descriptor() + // message.DefaultUrgent holds the default value on creation for the urgent field. + message.DefaultUrgent = messageDescUrgent.Default.(bool) + // messageDescBroadcasted is the schema descriptor for broadcasted field. + messageDescBroadcasted := messageFields[9].Descriptor() + // message.DefaultBroadcasted holds the default value on creation for the broadcasted field. + message.DefaultBroadcasted = messageDescBroadcasted.Default.(bool) + // messageDescRead is the schema descriptor for read field. + messageDescRead := messageFields[10].Descriptor() + // message.DefaultRead holds the default value on creation for the read field. + message.DefaultRead = messageDescRead.Default.(bool) + // messageDescCreated is the schema descriptor for created field. + messageDescCreated := messageFields[13].Descriptor() + // message.DefaultCreated holds the default value on creation for the created field. + message.DefaultCreated = messageDescCreated.Default.(func() time.Time) + // messageDescID is the schema descriptor for id field. + messageDescID := messageFields[0].Descriptor() + // message.DefaultID holds the default value on creation for the id field. + message.DefaultID = messageDescID.Default.(func() uuid.UUID) + notificationFields := schema.Notification{}.Fields() + _ = notificationFields + // notificationDescSubscriberType is the schema descriptor for subscriber_type field. + notificationDescSubscriberType := notificationFields[4].Descriptor() + // notification.SubscriberTypeValidator is a validator for the "subscriber_type" field. It is called by the builders before save. + notification.SubscriberTypeValidator = notificationDescSubscriberType.Validators[0].(func(string) error) + // notificationDescSubscriberID is the schema descriptor for subscriber_id field. + notificationDescSubscriberID := notificationFields[5].Descriptor() + // notification.SubscriberIDValidator is a validator for the "subscriber_id" field. It is called by the builders before save. + notification.SubscriberIDValidator = notificationDescSubscriberID.Validators[0].(func(string) error) + // notificationDescStatus is the schema descriptor for status field. + notificationDescStatus := notificationFields[6].Descriptor() + // notification.StatusValidator is a validator for the "status" field. It is called by the builders before save. + notification.StatusValidator = notificationDescStatus.Validators[0].(func(string) error) + // notificationDescMessage is the schema descriptor for message field. + notificationDescMessage := notificationFields[7].Descriptor() + // notification.MessageValidator is a validator for the "message" field. It is called by the builders before save. + notification.MessageValidator = notificationDescMessage.Validators[0].(func(string) error) + // notificationDescDispatched is the schema descriptor for dispatched field. + notificationDescDispatched := notificationFields[8].Descriptor() + // notification.DefaultDispatched holds the default value on creation for the dispatched field. + notification.DefaultDispatched = notificationDescDispatched.Default.(bool) + // notificationDescAcknowledged is the schema descriptor for acknowledged field. + notificationDescAcknowledged := notificationFields[9].Descriptor() + // notification.DefaultAcknowledged holds the default value on creation for the acknowledged field. + notification.DefaultAcknowledged = notificationDescAcknowledged.Default.(bool) + // notificationDescCreated is the schema descriptor for created field. + notificationDescCreated := notificationFields[10].Descriptor() + // notification.DefaultCreated holds the default value on creation for the created field. + notification.DefaultCreated = notificationDescCreated.Default.(func() time.Time) + // notificationDescID is the schema descriptor for id field. + notificationDescID := notificationFields[0].Descriptor() + // notification.DefaultID holds the default value on creation for the id field. + notification.DefaultID = notificationDescID.Default.(func() uuid.UUID) + notificationsubscriptionFields := schema.NotificationSubscription{}.Fields() + _ = notificationsubscriptionFields + // notificationsubscriptionDescScope is the schema descriptor for scope field. + notificationsubscriptionDescScope := notificationsubscriptionFields[1].Descriptor() + // notificationsubscription.DefaultScope holds the default value on creation for the scope field. + notificationsubscription.DefaultScope = notificationsubscriptionDescScope.Default.(string) + // notificationsubscriptionDescSubscriberType is the schema descriptor for subscriber_type field. + notificationsubscriptionDescSubscriberType := notificationsubscriptionFields[3].Descriptor() + // notificationsubscription.DefaultSubscriberType holds the default value on creation for the subscriber_type field. + notificationsubscription.DefaultSubscriberType = notificationsubscriptionDescSubscriberType.Default.(string) + // notificationsubscriptionDescSubscriberID is the schema descriptor for subscriber_id field. + notificationsubscriptionDescSubscriberID := notificationsubscriptionFields[4].Descriptor() + // notificationsubscription.SubscriberIDValidator is a validator for the "subscriber_id" field. It is called by the builders before save. + notificationsubscription.SubscriberIDValidator = notificationsubscriptionDescSubscriberID.Validators[0].(func(string) error) + // notificationsubscriptionDescTriggerActivities is the schema descriptor for trigger_activities field. + notificationsubscriptionDescTriggerActivities := notificationsubscriptionFields[6].Descriptor() + // notificationsubscription.TriggerActivitiesValidator is a validator for the "trigger_activities" field. It is called by the builders before save. + notificationsubscription.TriggerActivitiesValidator = notificationsubscriptionDescTriggerActivities.Validators[0].(func(string) error) + // notificationsubscriptionDescCreatedBy is the schema descriptor for created_by field. + notificationsubscriptionDescCreatedBy := notificationsubscriptionFields[7].Descriptor() + // notificationsubscription.CreatedByValidator is a validator for the "created_by" field. It is called by the builders before save. + notificationsubscription.CreatedByValidator = notificationsubscriptionDescCreatedBy.Validators[0].(func(string) error) + // notificationsubscriptionDescCreated is the schema descriptor for created field. + notificationsubscriptionDescCreated := notificationsubscriptionFields[8].Descriptor() + // notificationsubscription.DefaultCreated holds the default value on creation for the created field. + notificationsubscription.DefaultCreated = notificationsubscriptionDescCreated.Default.(func() time.Time) + // notificationsubscriptionDescID is the schema descriptor for id field. + notificationsubscriptionDescID := notificationsubscriptionFields[0].Descriptor() + // notificationsubscription.DefaultID holds the default value on creation for the id field. + notificationsubscription.DefaultID = notificationsubscriptionDescID.Default.(func() uuid.UUID) policybindingFields := schema.PolicyBinding{}.Fields() _ = policybindingFields // policybindingDescCreated is the schema descriptor for created field. @@ -135,33 +596,265 @@ func init() { // project.SlugValidator is a validator for the "slug" field. It is called by the builders before save. project.SlugValidator = projectDescSlug.Validators[0].(func(string) error) // projectDescCreated is the schema descriptor for created field. - projectDescCreated := projectFields[6].Descriptor() + projectDescCreated := projectFields[8].Descriptor() // project.DefaultCreated holds the default value on creation for the created field. project.DefaultCreated = projectDescCreated.Default.(func() time.Time) // projectDescUpdated is the schema descriptor for updated field. - projectDescUpdated := projectFields[7].Descriptor() + projectDescUpdated := projectFields[9].Descriptor() // project.DefaultUpdated holds the default value on creation for the updated field. project.DefaultUpdated = projectDescUpdated.Default.(func() time.Time) // project.UpdateDefaultUpdated holds the default value on update for the updated field. project.UpdateDefaultUpdated = projectDescUpdated.UpdateDefault.(func() time.Time) // projectDescVisibility is the schema descriptor for visibility field. - projectDescVisibility := projectFields[10].Descriptor() + projectDescVisibility := projectFields[12].Descriptor() // project.DefaultVisibility holds the default value on creation for the visibility field. project.DefaultVisibility = projectDescVisibility.Default.(string) // projectDescID is the schema descriptor for id field. projectDescID := projectFields[0].Descriptor() // project.DefaultID holds the default value on creation for the id field. project.DefaultID = projectDescID.Default.(func() uuid.UUID) + projectcontributorFields := schema.ProjectContributor{}.Fields() + _ = projectcontributorFields + // projectcontributorDescBrokerName is the schema descriptor for broker_name field. + projectcontributorDescBrokerName := projectcontributorFields[3].Descriptor() + // projectcontributor.BrokerNameValidator is a validator for the "broker_name" field. It is called by the builders before save. + projectcontributor.BrokerNameValidator = projectcontributorDescBrokerName.Validators[0].(func(string) error) + // projectcontributorDescMode is the schema descriptor for mode field. + projectcontributorDescMode := projectcontributorFields[4].Descriptor() + // projectcontributor.DefaultMode holds the default value on creation for the mode field. + projectcontributor.DefaultMode = projectcontributorDescMode.Default.(string) + // projectcontributorDescStatus is the schema descriptor for status field. + projectcontributorDescStatus := projectcontributorFields[5].Descriptor() + // projectcontributor.DefaultStatus holds the default value on creation for the status field. + projectcontributor.DefaultStatus = projectcontributorDescStatus.Default.(string) + // projectcontributorDescID is the schema descriptor for id field. + projectcontributorDescID := projectcontributorFields[0].Descriptor() + // projectcontributor.DefaultID holds the default value on creation for the id field. + projectcontributor.DefaultID = projectcontributorDescID.Default.(func() uuid.UUID) + projectsyncstateFields := schema.ProjectSyncState{}.Fields() + _ = projectsyncstateFields + // projectsyncstateDescBrokerID is the schema descriptor for broker_id field. + projectsyncstateDescBrokerID := projectsyncstateFields[2].Descriptor() + // projectsyncstate.DefaultBrokerID holds the default value on creation for the broker_id field. + projectsyncstate.DefaultBrokerID = projectsyncstateDescBrokerID.Default.(string) + // projectsyncstateDescFileCount is the schema descriptor for file_count field. + projectsyncstateDescFileCount := projectsyncstateFields[5].Descriptor() + // projectsyncstate.DefaultFileCount holds the default value on creation for the file_count field. + projectsyncstate.DefaultFileCount = projectsyncstateDescFileCount.Default.(int) + // projectsyncstateDescTotalBytes is the schema descriptor for total_bytes field. + projectsyncstateDescTotalBytes := projectsyncstateFields[6].Descriptor() + // projectsyncstate.DefaultTotalBytes holds the default value on creation for the total_bytes field. + projectsyncstate.DefaultTotalBytes = projectsyncstateDescTotalBytes.Default.(int64) + // projectsyncstateDescID is the schema descriptor for id field. + projectsyncstateDescID := projectsyncstateFields[0].Descriptor() + // projectsyncstate.DefaultID holds the default value on creation for the id field. + projectsyncstate.DefaultID = projectsyncstateDescID.Default.(func() uuid.UUID) + runtimebrokerFields := schema.RuntimeBroker{}.Fields() + _ = runtimebrokerFields + // runtimebrokerDescName is the schema descriptor for name field. + runtimebrokerDescName := runtimebrokerFields[1].Descriptor() + // runtimebroker.NameValidator is a validator for the "name" field. It is called by the builders before save. + runtimebroker.NameValidator = runtimebrokerDescName.Validators[0].(func(string) error) + // runtimebrokerDescSlug is the schema descriptor for slug field. + runtimebrokerDescSlug := runtimebrokerFields[2].Descriptor() + // runtimebroker.SlugValidator is a validator for the "slug" field. It is called by the builders before save. + runtimebroker.SlugValidator = runtimebrokerDescSlug.Validators[0].(func(string) error) + // runtimebrokerDescMode is the schema descriptor for mode field. + runtimebrokerDescMode := runtimebrokerFields[4].Descriptor() + // runtimebroker.DefaultMode holds the default value on creation for the mode field. + runtimebroker.DefaultMode = runtimebrokerDescMode.Default.(string) + // runtimebrokerDescLockVersion is the schema descriptor for lock_version field. + runtimebrokerDescLockVersion := runtimebrokerFields[6].Descriptor() + // runtimebroker.DefaultLockVersion holds the default value on creation for the lock_version field. + runtimebroker.DefaultLockVersion = runtimebrokerDescLockVersion.Default.(int64) + // runtimebrokerDescStatus is the schema descriptor for status field. + runtimebrokerDescStatus := runtimebrokerFields[7].Descriptor() + // runtimebroker.DefaultStatus holds the default value on creation for the status field. + runtimebroker.DefaultStatus = runtimebrokerDescStatus.Default.(string) + // runtimebrokerDescConnectionState is the schema descriptor for connection_state field. + runtimebrokerDescConnectionState := runtimebrokerFields[8].Descriptor() + // runtimebroker.DefaultConnectionState holds the default value on creation for the connection_state field. + runtimebroker.DefaultConnectionState = runtimebrokerDescConnectionState.Default.(string) + // runtimebrokerDescAutoProvide is the schema descriptor for auto_provide field. + runtimebrokerDescAutoProvide := runtimebrokerFields[18].Descriptor() + // runtimebroker.DefaultAutoProvide holds the default value on creation for the auto_provide field. + runtimebroker.DefaultAutoProvide = runtimebrokerDescAutoProvide.Default.(bool) + // runtimebrokerDescCreated is the schema descriptor for created field. + runtimebrokerDescCreated := runtimebrokerFields[19].Descriptor() + // runtimebroker.DefaultCreated holds the default value on creation for the created field. + runtimebroker.DefaultCreated = runtimebrokerDescCreated.Default.(func() time.Time) + // runtimebrokerDescUpdated is the schema descriptor for updated field. + runtimebrokerDescUpdated := runtimebrokerFields[20].Descriptor() + // runtimebroker.DefaultUpdated holds the default value on creation for the updated field. + runtimebroker.DefaultUpdated = runtimebrokerDescUpdated.Default.(func() time.Time) + // runtimebroker.UpdateDefaultUpdated holds the default value on update for the updated field. + runtimebroker.UpdateDefaultUpdated = runtimebrokerDescUpdated.UpdateDefault.(func() time.Time) + // runtimebrokerDescID is the schema descriptor for id field. + runtimebrokerDescID := runtimebrokerFields[0].Descriptor() + // runtimebroker.DefaultID holds the default value on creation for the id field. + runtimebroker.DefaultID = runtimebrokerDescID.Default.(func() uuid.UUID) + scheduleFields := schema.Schedule{}.Fields() + _ = scheduleFields + // scheduleDescName is the schema descriptor for name field. + scheduleDescName := scheduleFields[2].Descriptor() + // schedule.NameValidator is a validator for the "name" field. It is called by the builders before save. + schedule.NameValidator = scheduleDescName.Validators[0].(func(string) error) + // scheduleDescCronExpr is the schema descriptor for cron_expr field. + scheduleDescCronExpr := scheduleFields[3].Descriptor() + // schedule.CronExprValidator is a validator for the "cron_expr" field. It is called by the builders before save. + schedule.CronExprValidator = scheduleDescCronExpr.Validators[0].(func(string) error) + // scheduleDescEventType is the schema descriptor for event_type field. + scheduleDescEventType := scheduleFields[4].Descriptor() + // schedule.EventTypeValidator is a validator for the "event_type" field. It is called by the builders before save. + schedule.EventTypeValidator = scheduleDescEventType.Validators[0].(func(string) error) + // scheduleDescPayload is the schema descriptor for payload field. + scheduleDescPayload := scheduleFields[5].Descriptor() + // schedule.DefaultPayload holds the default value on creation for the payload field. + schedule.DefaultPayload = scheduleDescPayload.Default.(string) + // scheduleDescStatus is the schema descriptor for status field. + scheduleDescStatus := scheduleFields[6].Descriptor() + // schedule.DefaultStatus holds the default value on creation for the status field. + schedule.DefaultStatus = scheduleDescStatus.Default.(string) + // scheduleDescRunCount is the schema descriptor for run_count field. + scheduleDescRunCount := scheduleFields[11].Descriptor() + // schedule.DefaultRunCount holds the default value on creation for the run_count field. + schedule.DefaultRunCount = scheduleDescRunCount.Default.(int) + // scheduleDescErrorCount is the schema descriptor for error_count field. + scheduleDescErrorCount := scheduleFields[12].Descriptor() + // schedule.DefaultErrorCount holds the default value on creation for the error_count field. + schedule.DefaultErrorCount = scheduleDescErrorCount.Default.(int) + // scheduleDescCreated is the schema descriptor for created field. + scheduleDescCreated := scheduleFields[14].Descriptor() + // schedule.DefaultCreated holds the default value on creation for the created field. + schedule.DefaultCreated = scheduleDescCreated.Default.(func() time.Time) + // scheduleDescUpdated is the schema descriptor for updated field. + scheduleDescUpdated := scheduleFields[15].Descriptor() + // schedule.DefaultUpdated holds the default value on creation for the updated field. + schedule.DefaultUpdated = scheduleDescUpdated.Default.(func() time.Time) + // schedule.UpdateDefaultUpdated holds the default value on update for the updated field. + schedule.UpdateDefaultUpdated = scheduleDescUpdated.UpdateDefault.(func() time.Time) + // scheduleDescID is the schema descriptor for id field. + scheduleDescID := scheduleFields[0].Descriptor() + // schedule.DefaultID holds the default value on creation for the id field. + schedule.DefaultID = scheduleDescID.Default.(func() uuid.UUID) + scheduledeventFields := schema.ScheduledEvent{}.Fields() + _ = scheduledeventFields + // scheduledeventDescEventType is the schema descriptor for event_type field. + scheduledeventDescEventType := scheduledeventFields[2].Descriptor() + // scheduledevent.EventTypeValidator is a validator for the "event_type" field. It is called by the builders before save. + scheduledevent.EventTypeValidator = scheduledeventDescEventType.Validators[0].(func(string) error) + // scheduledeventDescPayload is the schema descriptor for payload field. + scheduledeventDescPayload := scheduledeventFields[4].Descriptor() + // scheduledevent.PayloadValidator is a validator for the "payload" field. It is called by the builders before save. + scheduledevent.PayloadValidator = scheduledeventDescPayload.Validators[0].(func(string) error) + // scheduledeventDescStatus is the schema descriptor for status field. + scheduledeventDescStatus := scheduledeventFields[5].Descriptor() + // scheduledevent.DefaultStatus holds the default value on creation for the status field. + scheduledevent.DefaultStatus = scheduledeventDescStatus.Default.(string) + // scheduledeventDescCreated is the schema descriptor for created field. + scheduledeventDescCreated := scheduledeventFields[10].Descriptor() + // scheduledevent.DefaultCreated holds the default value on creation for the created field. + scheduledevent.DefaultCreated = scheduledeventDescCreated.Default.(func() time.Time) + // scheduledeventDescID is the schema descriptor for id field. + scheduledeventDescID := scheduledeventFields[0].Descriptor() + // scheduledevent.DefaultID holds the default value on creation for the id field. + scheduledevent.DefaultID = scheduledeventDescID.Default.(func() uuid.UUID) + secretFields := schema.Secret{}.Fields() + _ = secretFields + // secretDescKey is the schema descriptor for key field. + secretDescKey := secretFields[1].Descriptor() + // secret.KeyValidator is a validator for the "key" field. It is called by the builders before save. + secret.KeyValidator = secretDescKey.Validators[0].(func(string) error) + // secretDescScope is the schema descriptor for scope field. + secretDescScope := secretFields[6].Descriptor() + // secret.ScopeValidator is a validator for the "scope" field. It is called by the builders before save. + secret.ScopeValidator = secretDescScope.Validators[0].(func(string) error) + // secretDescAllowProgeny is the schema descriptor for allow_progeny field. + secretDescAllowProgeny := secretFields[10].Descriptor() + // secret.DefaultAllowProgeny holds the default value on creation for the allow_progeny field. + secret.DefaultAllowProgeny = secretDescAllowProgeny.Default.(bool) + // secretDescVersion is the schema descriptor for version field. + secretDescVersion := secretFields[11].Descriptor() + // secret.DefaultVersion holds the default value on creation for the version field. + secret.DefaultVersion = secretDescVersion.Default.(int) + // secretDescCreated is the schema descriptor for created field. + secretDescCreated := secretFields[14].Descriptor() + // secret.DefaultCreated holds the default value on creation for the created field. + secret.DefaultCreated = secretDescCreated.Default.(func() time.Time) + // secretDescUpdated is the schema descriptor for updated field. + secretDescUpdated := secretFields[15].Descriptor() + // secret.DefaultUpdated holds the default value on creation for the updated field. + secret.DefaultUpdated = secretDescUpdated.Default.(func() time.Time) + // secret.UpdateDefaultUpdated holds the default value on update for the updated field. + secret.UpdateDefaultUpdated = secretDescUpdated.UpdateDefault.(func() time.Time) + // secretDescID is the schema descriptor for id field. + secretDescID := secretFields[0].Descriptor() + // secret.DefaultID holds the default value on creation for the id field. + secret.DefaultID = secretDescID.Default.(func() uuid.UUID) + subscriptiontemplateFields := schema.SubscriptionTemplate{}.Fields() + _ = subscriptiontemplateFields + // subscriptiontemplateDescName is the schema descriptor for name field. + subscriptiontemplateDescName := subscriptiontemplateFields[1].Descriptor() + // subscriptiontemplate.NameValidator is a validator for the "name" field. It is called by the builders before save. + subscriptiontemplate.NameValidator = subscriptiontemplateDescName.Validators[0].(func(string) error) + // subscriptiontemplateDescScope is the schema descriptor for scope field. + subscriptiontemplateDescScope := subscriptiontemplateFields[2].Descriptor() + // subscriptiontemplate.DefaultScope holds the default value on creation for the scope field. + subscriptiontemplate.DefaultScope = subscriptiontemplateDescScope.Default.(string) + // subscriptiontemplateDescTriggerActivities is the schema descriptor for trigger_activities field. + subscriptiontemplateDescTriggerActivities := subscriptiontemplateFields[3].Descriptor() + // subscriptiontemplate.TriggerActivitiesValidator is a validator for the "trigger_activities" field. It is called by the builders before save. + subscriptiontemplate.TriggerActivitiesValidator = subscriptiontemplateDescTriggerActivities.Validators[0].(func(string) error) + // subscriptiontemplateDescCreatedBy is the schema descriptor for created_by field. + subscriptiontemplateDescCreatedBy := subscriptiontemplateFields[5].Descriptor() + // subscriptiontemplate.CreatedByValidator is a validator for the "created_by" field. It is called by the builders before save. + subscriptiontemplate.CreatedByValidator = subscriptiontemplateDescCreatedBy.Validators[0].(func(string) error) + // subscriptiontemplateDescID is the schema descriptor for id field. + subscriptiontemplateDescID := subscriptiontemplateFields[0].Descriptor() + // subscriptiontemplate.DefaultID holds the default value on creation for the id field. + subscriptiontemplate.DefaultID = subscriptiontemplateDescID.Default.(func() uuid.UUID) + templateFields := schema.Template{}.Fields() + _ = templateFields + // templateDescName is the schema descriptor for name field. + templateDescName := templateFields[1].Descriptor() + // template.NameValidator is a validator for the "name" field. It is called by the builders before save. + template.NameValidator = templateDescName.Validators[0].(func(string) error) + // templateDescSlug is the schema descriptor for slug field. + templateDescSlug := templateFields[2].Descriptor() + // template.SlugValidator is a validator for the "slug" field. It is called by the builders before save. + template.SlugValidator = templateDescSlug.Validators[0].(func(string) error) + // templateDescScope is the schema descriptor for scope field. + templateDescScope := templateFields[10].Descriptor() + // template.DefaultScope holds the default value on creation for the scope field. + template.DefaultScope = templateDescScope.Default.(string) + // templateDescLocked is the schema descriptor for locked field. + templateDescLocked := templateFields[18].Descriptor() + // template.DefaultLocked holds the default value on creation for the locked field. + template.DefaultLocked = templateDescLocked.Default.(bool) + // templateDescVisibility is the schema descriptor for visibility field. + templateDescVisibility := templateFields[23].Descriptor() + // template.DefaultVisibility holds the default value on creation for the visibility field. + template.DefaultVisibility = templateDescVisibility.Default.(string) + // templateDescCreated is the schema descriptor for created field. + templateDescCreated := templateFields[24].Descriptor() + // template.DefaultCreated holds the default value on creation for the created field. + template.DefaultCreated = templateDescCreated.Default.(func() time.Time) + // templateDescUpdated is the schema descriptor for updated field. + templateDescUpdated := templateFields[25].Descriptor() + // template.DefaultUpdated holds the default value on creation for the updated field. + template.DefaultUpdated = templateDescUpdated.Default.(func() time.Time) + // template.UpdateDefaultUpdated holds the default value on update for the updated field. + template.UpdateDefaultUpdated = templateDescUpdated.UpdateDefault.(func() time.Time) + // templateDescID is the schema descriptor for id field. + templateDescID := templateFields[0].Descriptor() + // template.DefaultID holds the default value on creation for the id field. + template.DefaultID = templateDescID.Default.(func() uuid.UUID) userFields := schema.User{}.Fields() _ = userFields // userDescEmail is the schema descriptor for email field. userDescEmail := userFields[1].Descriptor() // user.EmailValidator is a validator for the "email" field. It is called by the builders before save. user.EmailValidator = userDescEmail.Validators[0].(func(string) error) - // userDescDisplayName is the schema descriptor for display_name field. - userDescDisplayName := userFields[2].Descriptor() - // user.DisplayNameValidator is a validator for the "display_name" field. It is called by the builders before save. - user.DisplayNameValidator = userDescDisplayName.Validators[0].(func(string) error) // userDescCreated is the schema descriptor for created field. userDescCreated := userFields[7].Descriptor() // user.DefaultCreated holds the default value on creation for the created field. @@ -170,4 +863,34 @@ func init() { userDescID := userFields[0].Descriptor() // user.DefaultID holds the default value on creation for the id field. user.DefaultID = userDescID.Default.(func() uuid.UUID) + useraccesstokenFields := schema.UserAccessToken{}.Fields() + _ = useraccesstokenFields + // useraccesstokenDescName is the schema descriptor for name field. + useraccesstokenDescName := useraccesstokenFields[2].Descriptor() + // useraccesstoken.NameValidator is a validator for the "name" field. It is called by the builders before save. + useraccesstoken.NameValidator = useraccesstokenDescName.Validators[0].(func(string) error) + // useraccesstokenDescPrefix is the schema descriptor for prefix field. + useraccesstokenDescPrefix := useraccesstokenFields[3].Descriptor() + // useraccesstoken.PrefixValidator is a validator for the "prefix" field. It is called by the builders before save. + useraccesstoken.PrefixValidator = useraccesstokenDescPrefix.Validators[0].(func(string) error) + // useraccesstokenDescKeyHash is the schema descriptor for key_hash field. + useraccesstokenDescKeyHash := useraccesstokenFields[4].Descriptor() + // useraccesstoken.KeyHashValidator is a validator for the "key_hash" field. It is called by the builders before save. + useraccesstoken.KeyHashValidator = useraccesstokenDescKeyHash.Validators[0].(func(string) error) + // useraccesstokenDescScopes is the schema descriptor for scopes field. + useraccesstokenDescScopes := useraccesstokenFields[6].Descriptor() + // useraccesstoken.ScopesValidator is a validator for the "scopes" field. It is called by the builders before save. + useraccesstoken.ScopesValidator = useraccesstokenDescScopes.Validators[0].(func(string) error) + // useraccesstokenDescRevoked is the schema descriptor for revoked field. + useraccesstokenDescRevoked := useraccesstokenFields[7].Descriptor() + // useraccesstoken.DefaultRevoked holds the default value on creation for the revoked field. + useraccesstoken.DefaultRevoked = useraccesstokenDescRevoked.Default.(bool) + // useraccesstokenDescCreated is the schema descriptor for created field. + useraccesstokenDescCreated := useraccesstokenFields[10].Descriptor() + // useraccesstoken.DefaultCreated holds the default value on creation for the created field. + useraccesstoken.DefaultCreated = useraccesstokenDescCreated.Default.(func() time.Time) + // useraccesstokenDescID is the schema descriptor for id field. + useraccesstokenDescID := useraccesstokenFields[0].Descriptor() + // useraccesstoken.DefaultID holds the default value on creation for the id field. + useraccesstoken.DefaultID = useraccesstokenDescID.Default.(func() uuid.UUID) } diff --git a/pkg/ent/runtimebroker.go b/pkg/ent/runtimebroker.go new file mode 100644 index 000000000..ddcba587b --- /dev/null +++ b/pkg/ent/runtimebroker.go @@ -0,0 +1,323 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "fmt" + "strings" + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "github.com/GoogleCloudPlatform/scion/pkg/ent/runtimebroker" + "github.com/google/uuid" +) + +// RuntimeBroker is the model entity for the RuntimeBroker schema. +type RuntimeBroker struct { + config `json:"-"` + // ID of the ent. + ID uuid.UUID `json:"id,omitempty"` + // Name holds the value of the "name" field. + Name string `json:"name,omitempty"` + // Slug holds the value of the "slug" field. + Slug string `json:"slug,omitempty"` + // Type holds the value of the "type" field. + Type string `json:"type,omitempty"` + // Mode holds the value of the "mode" field. + Mode string `json:"mode,omitempty"` + // Version holds the value of the "version" field. + Version string `json:"version,omitempty"` + // LockVersion holds the value of the "lock_version" field. + LockVersion int64 `json:"lock_version,omitempty"` + // Status holds the value of the "status" field. + Status string `json:"status,omitempty"` + // ConnectionState holds the value of the "connection_state" field. + ConnectionState string `json:"connection_state,omitempty"` + // LastHeartbeat holds the value of the "last_heartbeat" field. + LastHeartbeat *time.Time `json:"last_heartbeat,omitempty"` + // Capabilities holds the value of the "capabilities" field. + Capabilities string `json:"capabilities,omitempty"` + // SupportedHarnesses holds the value of the "supported_harnesses" field. + SupportedHarnesses string `json:"supported_harnesses,omitempty"` + // Resources holds the value of the "resources" field. + Resources string `json:"resources,omitempty"` + // Runtimes holds the value of the "runtimes" field. + Runtimes string `json:"runtimes,omitempty"` + // Labels holds the value of the "labels" field. + Labels string `json:"labels,omitempty"` + // Annotations holds the value of the "annotations" field. + Annotations string `json:"annotations,omitempty"` + // Endpoint holds the value of the "endpoint" field. + Endpoint string `json:"endpoint,omitempty"` + // CreatedBy holds the value of the "created_by" field. + CreatedBy string `json:"created_by,omitempty"` + // AutoProvide holds the value of the "auto_provide" field. + AutoProvide bool `json:"auto_provide,omitempty"` + // Created holds the value of the "created" field. + Created time.Time `json:"created,omitempty"` + // Updated holds the value of the "updated" field. + Updated time.Time `json:"updated,omitempty"` + selectValues sql.SelectValues +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*RuntimeBroker) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case runtimebroker.FieldAutoProvide: + values[i] = new(sql.NullBool) + case runtimebroker.FieldLockVersion: + values[i] = new(sql.NullInt64) + case runtimebroker.FieldName, runtimebroker.FieldSlug, runtimebroker.FieldType, runtimebroker.FieldMode, runtimebroker.FieldVersion, runtimebroker.FieldStatus, runtimebroker.FieldConnectionState, runtimebroker.FieldCapabilities, runtimebroker.FieldSupportedHarnesses, runtimebroker.FieldResources, runtimebroker.FieldRuntimes, runtimebroker.FieldLabels, runtimebroker.FieldAnnotations, runtimebroker.FieldEndpoint, runtimebroker.FieldCreatedBy: + values[i] = new(sql.NullString) + case runtimebroker.FieldLastHeartbeat, runtimebroker.FieldCreated, runtimebroker.FieldUpdated: + values[i] = new(sql.NullTime) + case runtimebroker.FieldID: + values[i] = new(uuid.UUID) + default: + values[i] = new(sql.UnknownType) + } + } + return values, nil +} + +// assignValues assigns the values that were returned from sql.Rows (after scanning) +// to the RuntimeBroker fields. +func (_m *RuntimeBroker) assignValues(columns []string, values []any) error { + if m, n := len(values), len(columns); m < n { + return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) + } + for i := range columns { + switch columns[i] { + case runtimebroker.FieldID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field id", values[i]) + } else if value != nil { + _m.ID = *value + } + case runtimebroker.FieldName: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field name", values[i]) + } else if value.Valid { + _m.Name = value.String + } + case runtimebroker.FieldSlug: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field slug", values[i]) + } else if value.Valid { + _m.Slug = value.String + } + case runtimebroker.FieldType: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field type", values[i]) + } else if value.Valid { + _m.Type = value.String + } + case runtimebroker.FieldMode: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field mode", values[i]) + } else if value.Valid { + _m.Mode = value.String + } + case runtimebroker.FieldVersion: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field version", values[i]) + } else if value.Valid { + _m.Version = value.String + } + case runtimebroker.FieldLockVersion: + if value, ok := values[i].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for field lock_version", values[i]) + } else if value.Valid { + _m.LockVersion = value.Int64 + } + case runtimebroker.FieldStatus: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field status", values[i]) + } else if value.Valid { + _m.Status = value.String + } + case runtimebroker.FieldConnectionState: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field connection_state", values[i]) + } else if value.Valid { + _m.ConnectionState = value.String + } + case runtimebroker.FieldLastHeartbeat: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field last_heartbeat", values[i]) + } else if value.Valid { + _m.LastHeartbeat = new(time.Time) + *_m.LastHeartbeat = value.Time + } + case runtimebroker.FieldCapabilities: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field capabilities", values[i]) + } else if value.Valid { + _m.Capabilities = value.String + } + case runtimebroker.FieldSupportedHarnesses: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field supported_harnesses", values[i]) + } else if value.Valid { + _m.SupportedHarnesses = value.String + } + case runtimebroker.FieldResources: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field resources", values[i]) + } else if value.Valid { + _m.Resources = value.String + } + case runtimebroker.FieldRuntimes: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field runtimes", values[i]) + } else if value.Valid { + _m.Runtimes = value.String + } + case runtimebroker.FieldLabels: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field labels", values[i]) + } else if value.Valid { + _m.Labels = value.String + } + case runtimebroker.FieldAnnotations: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field annotations", values[i]) + } else if value.Valid { + _m.Annotations = value.String + } + case runtimebroker.FieldEndpoint: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field endpoint", values[i]) + } else if value.Valid { + _m.Endpoint = value.String + } + case runtimebroker.FieldCreatedBy: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field created_by", values[i]) + } else if value.Valid { + _m.CreatedBy = value.String + } + case runtimebroker.FieldAutoProvide: + if value, ok := values[i].(*sql.NullBool); !ok { + return fmt.Errorf("unexpected type %T for field auto_provide", values[i]) + } else if value.Valid { + _m.AutoProvide = value.Bool + } + case runtimebroker.FieldCreated: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field created", values[i]) + } else if value.Valid { + _m.Created = value.Time + } + case runtimebroker.FieldUpdated: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field updated", values[i]) + } else if value.Valid { + _m.Updated = value.Time + } + default: + _m.selectValues.Set(columns[i], values[i]) + } + } + return nil +} + +// Value returns the ent.Value that was dynamically selected and assigned to the RuntimeBroker. +// This includes values selected through modifiers, order, etc. +func (_m *RuntimeBroker) Value(name string) (ent.Value, error) { + return _m.selectValues.Get(name) +} + +// Update returns a builder for updating this RuntimeBroker. +// Note that you need to call RuntimeBroker.Unwrap() before calling this method if this RuntimeBroker +// was returned from a transaction, and the transaction was committed or rolled back. +func (_m *RuntimeBroker) Update() *RuntimeBrokerUpdateOne { + return NewRuntimeBrokerClient(_m.config).UpdateOne(_m) +} + +// Unwrap unwraps the RuntimeBroker entity that was returned from a transaction after it was closed, +// so that all future queries will be executed through the driver which created the transaction. +func (_m *RuntimeBroker) Unwrap() *RuntimeBroker { + _tx, ok := _m.config.driver.(*txDriver) + if !ok { + panic("ent: RuntimeBroker is not a transactional entity") + } + _m.config.driver = _tx.drv + return _m +} + +// String implements the fmt.Stringer. +func (_m *RuntimeBroker) String() string { + var builder strings.Builder + builder.WriteString("RuntimeBroker(") + builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID)) + builder.WriteString("name=") + builder.WriteString(_m.Name) + builder.WriteString(", ") + builder.WriteString("slug=") + builder.WriteString(_m.Slug) + builder.WriteString(", ") + builder.WriteString("type=") + builder.WriteString(_m.Type) + builder.WriteString(", ") + builder.WriteString("mode=") + builder.WriteString(_m.Mode) + builder.WriteString(", ") + builder.WriteString("version=") + builder.WriteString(_m.Version) + builder.WriteString(", ") + builder.WriteString("lock_version=") + builder.WriteString(fmt.Sprintf("%v", _m.LockVersion)) + builder.WriteString(", ") + builder.WriteString("status=") + builder.WriteString(_m.Status) + builder.WriteString(", ") + builder.WriteString("connection_state=") + builder.WriteString(_m.ConnectionState) + builder.WriteString(", ") + if v := _m.LastHeartbeat; v != nil { + builder.WriteString("last_heartbeat=") + builder.WriteString(v.Format(time.ANSIC)) + } + builder.WriteString(", ") + builder.WriteString("capabilities=") + builder.WriteString(_m.Capabilities) + builder.WriteString(", ") + builder.WriteString("supported_harnesses=") + builder.WriteString(_m.SupportedHarnesses) + builder.WriteString(", ") + builder.WriteString("resources=") + builder.WriteString(_m.Resources) + builder.WriteString(", ") + builder.WriteString("runtimes=") + builder.WriteString(_m.Runtimes) + builder.WriteString(", ") + builder.WriteString("labels=") + builder.WriteString(_m.Labels) + builder.WriteString(", ") + builder.WriteString("annotations=") + builder.WriteString(_m.Annotations) + builder.WriteString(", ") + builder.WriteString("endpoint=") + builder.WriteString(_m.Endpoint) + builder.WriteString(", ") + builder.WriteString("created_by=") + builder.WriteString(_m.CreatedBy) + builder.WriteString(", ") + builder.WriteString("auto_provide=") + builder.WriteString(fmt.Sprintf("%v", _m.AutoProvide)) + builder.WriteString(", ") + builder.WriteString("created=") + builder.WriteString(_m.Created.Format(time.ANSIC)) + builder.WriteString(", ") + builder.WriteString("updated=") + builder.WriteString(_m.Updated.Format(time.ANSIC)) + builder.WriteByte(')') + return builder.String() +} + +// RuntimeBrokers is a parsable slice of RuntimeBroker. +type RuntimeBrokers []*RuntimeBroker diff --git a/pkg/ent/runtimebroker/runtimebroker.go b/pkg/ent/runtimebroker/runtimebroker.go new file mode 100644 index 000000000..529fe8f01 --- /dev/null +++ b/pkg/ent/runtimebroker/runtimebroker.go @@ -0,0 +1,227 @@ +// Code generated by ent, DO NOT EDIT. + +package runtimebroker + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "github.com/google/uuid" +) + +const ( + // Label holds the string label denoting the runtimebroker type in the database. + Label = "runtime_broker" + // FieldID holds the string denoting the id field in the database. + FieldID = "id" + // FieldName holds the string denoting the name field in the database. + FieldName = "name" + // FieldSlug holds the string denoting the slug field in the database. + FieldSlug = "slug" + // FieldType holds the string denoting the type field in the database. + FieldType = "type" + // FieldMode holds the string denoting the mode field in the database. + FieldMode = "mode" + // FieldVersion holds the string denoting the version field in the database. + FieldVersion = "version" + // FieldLockVersion holds the string denoting the lock_version field in the database. + FieldLockVersion = "lock_version" + // FieldStatus holds the string denoting the status field in the database. + FieldStatus = "status" + // FieldConnectionState holds the string denoting the connection_state field in the database. + FieldConnectionState = "connection_state" + // FieldLastHeartbeat holds the string denoting the last_heartbeat field in the database. + FieldLastHeartbeat = "last_heartbeat" + // FieldCapabilities holds the string denoting the capabilities field in the database. + FieldCapabilities = "capabilities" + // FieldSupportedHarnesses holds the string denoting the supported_harnesses field in the database. + FieldSupportedHarnesses = "supported_harnesses" + // FieldResources holds the string denoting the resources field in the database. + FieldResources = "resources" + // FieldRuntimes holds the string denoting the runtimes field in the database. + FieldRuntimes = "runtimes" + // FieldLabels holds the string denoting the labels field in the database. + FieldLabels = "labels" + // FieldAnnotations holds the string denoting the annotations field in the database. + FieldAnnotations = "annotations" + // FieldEndpoint holds the string denoting the endpoint field in the database. + FieldEndpoint = "endpoint" + // FieldCreatedBy holds the string denoting the created_by field in the database. + FieldCreatedBy = "created_by" + // FieldAutoProvide holds the string denoting the auto_provide field in the database. + FieldAutoProvide = "auto_provide" + // FieldCreated holds the string denoting the created field in the database. + FieldCreated = "created" + // FieldUpdated holds the string denoting the updated field in the database. + FieldUpdated = "updated" + // Table holds the table name of the runtimebroker in the database. + Table = "runtime_brokers" +) + +// Columns holds all SQL columns for runtimebroker fields. +var Columns = []string{ + FieldID, + FieldName, + FieldSlug, + FieldType, + FieldMode, + FieldVersion, + FieldLockVersion, + FieldStatus, + FieldConnectionState, + FieldLastHeartbeat, + FieldCapabilities, + FieldSupportedHarnesses, + FieldResources, + FieldRuntimes, + FieldLabels, + FieldAnnotations, + FieldEndpoint, + FieldCreatedBy, + FieldAutoProvide, + FieldCreated, + FieldUpdated, +} + +// ValidColumn reports if the column name is valid (part of the table columns). +func ValidColumn(column string) bool { + for i := range Columns { + if column == Columns[i] { + return true + } + } + return false +} + +var ( + // NameValidator is a validator for the "name" field. It is called by the builders before save. + NameValidator func(string) error + // SlugValidator is a validator for the "slug" field. It is called by the builders before save. + SlugValidator func(string) error + // DefaultMode holds the default value on creation for the "mode" field. + DefaultMode string + // DefaultLockVersion holds the default value on creation for the "lock_version" field. + DefaultLockVersion int64 + // DefaultStatus holds the default value on creation for the "status" field. + DefaultStatus string + // DefaultConnectionState holds the default value on creation for the "connection_state" field. + DefaultConnectionState string + // DefaultAutoProvide holds the default value on creation for the "auto_provide" field. + DefaultAutoProvide bool + // DefaultCreated holds the default value on creation for the "created" field. + DefaultCreated func() time.Time + // DefaultUpdated holds the default value on creation for the "updated" field. + DefaultUpdated func() time.Time + // UpdateDefaultUpdated holds the default value on update for the "updated" field. + UpdateDefaultUpdated func() time.Time + // DefaultID holds the default value on creation for the "id" field. + DefaultID func() uuid.UUID +) + +// OrderOption defines the ordering options for the RuntimeBroker queries. +type OrderOption func(*sql.Selector) + +// ByID orders the results by the id field. +func ByID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldID, opts...).ToFunc() +} + +// ByName orders the results by the name field. +func ByName(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldName, opts...).ToFunc() +} + +// BySlug orders the results by the slug field. +func BySlug(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldSlug, opts...).ToFunc() +} + +// ByType orders the results by the type field. +func ByType(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldType, opts...).ToFunc() +} + +// ByMode orders the results by the mode field. +func ByMode(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldMode, opts...).ToFunc() +} + +// ByVersion orders the results by the version field. +func ByVersion(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldVersion, opts...).ToFunc() +} + +// ByLockVersion orders the results by the lock_version field. +func ByLockVersion(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldLockVersion, opts...).ToFunc() +} + +// ByStatus orders the results by the status field. +func ByStatus(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldStatus, opts...).ToFunc() +} + +// ByConnectionState orders the results by the connection_state field. +func ByConnectionState(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldConnectionState, opts...).ToFunc() +} + +// ByLastHeartbeat orders the results by the last_heartbeat field. +func ByLastHeartbeat(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldLastHeartbeat, opts...).ToFunc() +} + +// ByCapabilities orders the results by the capabilities field. +func ByCapabilities(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCapabilities, opts...).ToFunc() +} + +// BySupportedHarnesses orders the results by the supported_harnesses field. +func BySupportedHarnesses(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldSupportedHarnesses, opts...).ToFunc() +} + +// ByResources orders the results by the resources field. +func ByResources(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldResources, opts...).ToFunc() +} + +// ByRuntimes orders the results by the runtimes field. +func ByRuntimes(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldRuntimes, opts...).ToFunc() +} + +// ByLabels orders the results by the labels field. +func ByLabels(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldLabels, opts...).ToFunc() +} + +// ByAnnotations orders the results by the annotations field. +func ByAnnotations(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldAnnotations, opts...).ToFunc() +} + +// ByEndpoint orders the results by the endpoint field. +func ByEndpoint(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldEndpoint, opts...).ToFunc() +} + +// ByCreatedBy orders the results by the created_by field. +func ByCreatedBy(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCreatedBy, opts...).ToFunc() +} + +// ByAutoProvide orders the results by the auto_provide field. +func ByAutoProvide(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldAutoProvide, opts...).ToFunc() +} + +// ByCreated orders the results by the created field. +func ByCreated(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCreated, opts...).ToFunc() +} + +// ByUpdated orders the results by the updated field. +func ByUpdated(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldUpdated, opts...).ToFunc() +} diff --git a/pkg/ent/runtimebroker/where.go b/pkg/ent/runtimebroker/where.go new file mode 100644 index 000000000..b67733216 --- /dev/null +++ b/pkg/ent/runtimebroker/where.go @@ -0,0 +1,1426 @@ +// Code generated by ent, DO NOT EDIT. + +package runtimebroker + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/google/uuid" +) + +// ID filters vertices based on their ID field. +func ID(id uuid.UUID) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldEQ(FieldID, id)) +} + +// IDEQ applies the EQ predicate on the ID field. +func IDEQ(id uuid.UUID) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldEQ(FieldID, id)) +} + +// IDNEQ applies the NEQ predicate on the ID field. +func IDNEQ(id uuid.UUID) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldNEQ(FieldID, id)) +} + +// IDIn applies the In predicate on the ID field. +func IDIn(ids ...uuid.UUID) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldIn(FieldID, ids...)) +} + +// IDNotIn applies the NotIn predicate on the ID field. +func IDNotIn(ids ...uuid.UUID) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldNotIn(FieldID, ids...)) +} + +// IDGT applies the GT predicate on the ID field. +func IDGT(id uuid.UUID) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldGT(FieldID, id)) +} + +// IDGTE applies the GTE predicate on the ID field. +func IDGTE(id uuid.UUID) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldGTE(FieldID, id)) +} + +// IDLT applies the LT predicate on the ID field. +func IDLT(id uuid.UUID) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldLT(FieldID, id)) +} + +// IDLTE applies the LTE predicate on the ID field. +func IDLTE(id uuid.UUID) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldLTE(FieldID, id)) +} + +// Name applies equality check predicate on the "name" field. It's identical to NameEQ. +func Name(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldEQ(FieldName, v)) +} + +// Slug applies equality check predicate on the "slug" field. It's identical to SlugEQ. +func Slug(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldEQ(FieldSlug, v)) +} + +// Type applies equality check predicate on the "type" field. It's identical to TypeEQ. +func Type(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldEQ(FieldType, v)) +} + +// Mode applies equality check predicate on the "mode" field. It's identical to ModeEQ. +func Mode(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldEQ(FieldMode, v)) +} + +// Version applies equality check predicate on the "version" field. It's identical to VersionEQ. +func Version(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldEQ(FieldVersion, v)) +} + +// LockVersion applies equality check predicate on the "lock_version" field. It's identical to LockVersionEQ. +func LockVersion(v int64) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldEQ(FieldLockVersion, v)) +} + +// Status applies equality check predicate on the "status" field. It's identical to StatusEQ. +func Status(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldEQ(FieldStatus, v)) +} + +// ConnectionState applies equality check predicate on the "connection_state" field. It's identical to ConnectionStateEQ. +func ConnectionState(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldEQ(FieldConnectionState, v)) +} + +// LastHeartbeat applies equality check predicate on the "last_heartbeat" field. It's identical to LastHeartbeatEQ. +func LastHeartbeat(v time.Time) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldEQ(FieldLastHeartbeat, v)) +} + +// Capabilities applies equality check predicate on the "capabilities" field. It's identical to CapabilitiesEQ. +func Capabilities(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldEQ(FieldCapabilities, v)) +} + +// SupportedHarnesses applies equality check predicate on the "supported_harnesses" field. It's identical to SupportedHarnessesEQ. +func SupportedHarnesses(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldEQ(FieldSupportedHarnesses, v)) +} + +// Resources applies equality check predicate on the "resources" field. It's identical to ResourcesEQ. +func Resources(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldEQ(FieldResources, v)) +} + +// Runtimes applies equality check predicate on the "runtimes" field. It's identical to RuntimesEQ. +func Runtimes(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldEQ(FieldRuntimes, v)) +} + +// Labels applies equality check predicate on the "labels" field. It's identical to LabelsEQ. +func Labels(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldEQ(FieldLabels, v)) +} + +// Annotations applies equality check predicate on the "annotations" field. It's identical to AnnotationsEQ. +func Annotations(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldEQ(FieldAnnotations, v)) +} + +// Endpoint applies equality check predicate on the "endpoint" field. It's identical to EndpointEQ. +func Endpoint(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldEQ(FieldEndpoint, v)) +} + +// CreatedBy applies equality check predicate on the "created_by" field. It's identical to CreatedByEQ. +func CreatedBy(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldEQ(FieldCreatedBy, v)) +} + +// AutoProvide applies equality check predicate on the "auto_provide" field. It's identical to AutoProvideEQ. +func AutoProvide(v bool) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldEQ(FieldAutoProvide, v)) +} + +// Created applies equality check predicate on the "created" field. It's identical to CreatedEQ. +func Created(v time.Time) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldEQ(FieldCreated, v)) +} + +// Updated applies equality check predicate on the "updated" field. It's identical to UpdatedEQ. +func Updated(v time.Time) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldEQ(FieldUpdated, v)) +} + +// NameEQ applies the EQ predicate on the "name" field. +func NameEQ(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldEQ(FieldName, v)) +} + +// NameNEQ applies the NEQ predicate on the "name" field. +func NameNEQ(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldNEQ(FieldName, v)) +} + +// NameIn applies the In predicate on the "name" field. +func NameIn(vs ...string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldIn(FieldName, vs...)) +} + +// NameNotIn applies the NotIn predicate on the "name" field. +func NameNotIn(vs ...string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldNotIn(FieldName, vs...)) +} + +// NameGT applies the GT predicate on the "name" field. +func NameGT(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldGT(FieldName, v)) +} + +// NameGTE applies the GTE predicate on the "name" field. +func NameGTE(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldGTE(FieldName, v)) +} + +// NameLT applies the LT predicate on the "name" field. +func NameLT(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldLT(FieldName, v)) +} + +// NameLTE applies the LTE predicate on the "name" field. +func NameLTE(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldLTE(FieldName, v)) +} + +// NameContains applies the Contains predicate on the "name" field. +func NameContains(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldContains(FieldName, v)) +} + +// NameHasPrefix applies the HasPrefix predicate on the "name" field. +func NameHasPrefix(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldHasPrefix(FieldName, v)) +} + +// NameHasSuffix applies the HasSuffix predicate on the "name" field. +func NameHasSuffix(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldHasSuffix(FieldName, v)) +} + +// NameEqualFold applies the EqualFold predicate on the "name" field. +func NameEqualFold(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldEqualFold(FieldName, v)) +} + +// NameContainsFold applies the ContainsFold predicate on the "name" field. +func NameContainsFold(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldContainsFold(FieldName, v)) +} + +// SlugEQ applies the EQ predicate on the "slug" field. +func SlugEQ(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldEQ(FieldSlug, v)) +} + +// SlugNEQ applies the NEQ predicate on the "slug" field. +func SlugNEQ(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldNEQ(FieldSlug, v)) +} + +// SlugIn applies the In predicate on the "slug" field. +func SlugIn(vs ...string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldIn(FieldSlug, vs...)) +} + +// SlugNotIn applies the NotIn predicate on the "slug" field. +func SlugNotIn(vs ...string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldNotIn(FieldSlug, vs...)) +} + +// SlugGT applies the GT predicate on the "slug" field. +func SlugGT(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldGT(FieldSlug, v)) +} + +// SlugGTE applies the GTE predicate on the "slug" field. +func SlugGTE(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldGTE(FieldSlug, v)) +} + +// SlugLT applies the LT predicate on the "slug" field. +func SlugLT(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldLT(FieldSlug, v)) +} + +// SlugLTE applies the LTE predicate on the "slug" field. +func SlugLTE(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldLTE(FieldSlug, v)) +} + +// SlugContains applies the Contains predicate on the "slug" field. +func SlugContains(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldContains(FieldSlug, v)) +} + +// SlugHasPrefix applies the HasPrefix predicate on the "slug" field. +func SlugHasPrefix(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldHasPrefix(FieldSlug, v)) +} + +// SlugHasSuffix applies the HasSuffix predicate on the "slug" field. +func SlugHasSuffix(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldHasSuffix(FieldSlug, v)) +} + +// SlugEqualFold applies the EqualFold predicate on the "slug" field. +func SlugEqualFold(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldEqualFold(FieldSlug, v)) +} + +// SlugContainsFold applies the ContainsFold predicate on the "slug" field. +func SlugContainsFold(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldContainsFold(FieldSlug, v)) +} + +// TypeEQ applies the EQ predicate on the "type" field. +func TypeEQ(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldEQ(FieldType, v)) +} + +// TypeNEQ applies the NEQ predicate on the "type" field. +func TypeNEQ(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldNEQ(FieldType, v)) +} + +// TypeIn applies the In predicate on the "type" field. +func TypeIn(vs ...string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldIn(FieldType, vs...)) +} + +// TypeNotIn applies the NotIn predicate on the "type" field. +func TypeNotIn(vs ...string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldNotIn(FieldType, vs...)) +} + +// TypeGT applies the GT predicate on the "type" field. +func TypeGT(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldGT(FieldType, v)) +} + +// TypeGTE applies the GTE predicate on the "type" field. +func TypeGTE(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldGTE(FieldType, v)) +} + +// TypeLT applies the LT predicate on the "type" field. +func TypeLT(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldLT(FieldType, v)) +} + +// TypeLTE applies the LTE predicate on the "type" field. +func TypeLTE(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldLTE(FieldType, v)) +} + +// TypeContains applies the Contains predicate on the "type" field. +func TypeContains(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldContains(FieldType, v)) +} + +// TypeHasPrefix applies the HasPrefix predicate on the "type" field. +func TypeHasPrefix(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldHasPrefix(FieldType, v)) +} + +// TypeHasSuffix applies the HasSuffix predicate on the "type" field. +func TypeHasSuffix(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldHasSuffix(FieldType, v)) +} + +// TypeIsNil applies the IsNil predicate on the "type" field. +func TypeIsNil() predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldIsNull(FieldType)) +} + +// TypeNotNil applies the NotNil predicate on the "type" field. +func TypeNotNil() predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldNotNull(FieldType)) +} + +// TypeEqualFold applies the EqualFold predicate on the "type" field. +func TypeEqualFold(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldEqualFold(FieldType, v)) +} + +// TypeContainsFold applies the ContainsFold predicate on the "type" field. +func TypeContainsFold(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldContainsFold(FieldType, v)) +} + +// ModeEQ applies the EQ predicate on the "mode" field. +func ModeEQ(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldEQ(FieldMode, v)) +} + +// ModeNEQ applies the NEQ predicate on the "mode" field. +func ModeNEQ(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldNEQ(FieldMode, v)) +} + +// ModeIn applies the In predicate on the "mode" field. +func ModeIn(vs ...string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldIn(FieldMode, vs...)) +} + +// ModeNotIn applies the NotIn predicate on the "mode" field. +func ModeNotIn(vs ...string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldNotIn(FieldMode, vs...)) +} + +// ModeGT applies the GT predicate on the "mode" field. +func ModeGT(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldGT(FieldMode, v)) +} + +// ModeGTE applies the GTE predicate on the "mode" field. +func ModeGTE(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldGTE(FieldMode, v)) +} + +// ModeLT applies the LT predicate on the "mode" field. +func ModeLT(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldLT(FieldMode, v)) +} + +// ModeLTE applies the LTE predicate on the "mode" field. +func ModeLTE(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldLTE(FieldMode, v)) +} + +// ModeContains applies the Contains predicate on the "mode" field. +func ModeContains(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldContains(FieldMode, v)) +} + +// ModeHasPrefix applies the HasPrefix predicate on the "mode" field. +func ModeHasPrefix(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldHasPrefix(FieldMode, v)) +} + +// ModeHasSuffix applies the HasSuffix predicate on the "mode" field. +func ModeHasSuffix(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldHasSuffix(FieldMode, v)) +} + +// ModeEqualFold applies the EqualFold predicate on the "mode" field. +func ModeEqualFold(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldEqualFold(FieldMode, v)) +} + +// ModeContainsFold applies the ContainsFold predicate on the "mode" field. +func ModeContainsFold(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldContainsFold(FieldMode, v)) +} + +// VersionEQ applies the EQ predicate on the "version" field. +func VersionEQ(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldEQ(FieldVersion, v)) +} + +// VersionNEQ applies the NEQ predicate on the "version" field. +func VersionNEQ(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldNEQ(FieldVersion, v)) +} + +// VersionIn applies the In predicate on the "version" field. +func VersionIn(vs ...string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldIn(FieldVersion, vs...)) +} + +// VersionNotIn applies the NotIn predicate on the "version" field. +func VersionNotIn(vs ...string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldNotIn(FieldVersion, vs...)) +} + +// VersionGT applies the GT predicate on the "version" field. +func VersionGT(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldGT(FieldVersion, v)) +} + +// VersionGTE applies the GTE predicate on the "version" field. +func VersionGTE(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldGTE(FieldVersion, v)) +} + +// VersionLT applies the LT predicate on the "version" field. +func VersionLT(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldLT(FieldVersion, v)) +} + +// VersionLTE applies the LTE predicate on the "version" field. +func VersionLTE(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldLTE(FieldVersion, v)) +} + +// VersionContains applies the Contains predicate on the "version" field. +func VersionContains(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldContains(FieldVersion, v)) +} + +// VersionHasPrefix applies the HasPrefix predicate on the "version" field. +func VersionHasPrefix(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldHasPrefix(FieldVersion, v)) +} + +// VersionHasSuffix applies the HasSuffix predicate on the "version" field. +func VersionHasSuffix(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldHasSuffix(FieldVersion, v)) +} + +// VersionIsNil applies the IsNil predicate on the "version" field. +func VersionIsNil() predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldIsNull(FieldVersion)) +} + +// VersionNotNil applies the NotNil predicate on the "version" field. +func VersionNotNil() predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldNotNull(FieldVersion)) +} + +// VersionEqualFold applies the EqualFold predicate on the "version" field. +func VersionEqualFold(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldEqualFold(FieldVersion, v)) +} + +// VersionContainsFold applies the ContainsFold predicate on the "version" field. +func VersionContainsFold(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldContainsFold(FieldVersion, v)) +} + +// LockVersionEQ applies the EQ predicate on the "lock_version" field. +func LockVersionEQ(v int64) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldEQ(FieldLockVersion, v)) +} + +// LockVersionNEQ applies the NEQ predicate on the "lock_version" field. +func LockVersionNEQ(v int64) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldNEQ(FieldLockVersion, v)) +} + +// LockVersionIn applies the In predicate on the "lock_version" field. +func LockVersionIn(vs ...int64) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldIn(FieldLockVersion, vs...)) +} + +// LockVersionNotIn applies the NotIn predicate on the "lock_version" field. +func LockVersionNotIn(vs ...int64) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldNotIn(FieldLockVersion, vs...)) +} + +// LockVersionGT applies the GT predicate on the "lock_version" field. +func LockVersionGT(v int64) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldGT(FieldLockVersion, v)) +} + +// LockVersionGTE applies the GTE predicate on the "lock_version" field. +func LockVersionGTE(v int64) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldGTE(FieldLockVersion, v)) +} + +// LockVersionLT applies the LT predicate on the "lock_version" field. +func LockVersionLT(v int64) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldLT(FieldLockVersion, v)) +} + +// LockVersionLTE applies the LTE predicate on the "lock_version" field. +func LockVersionLTE(v int64) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldLTE(FieldLockVersion, v)) +} + +// StatusEQ applies the EQ predicate on the "status" field. +func StatusEQ(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldEQ(FieldStatus, v)) +} + +// StatusNEQ applies the NEQ predicate on the "status" field. +func StatusNEQ(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldNEQ(FieldStatus, v)) +} + +// StatusIn applies the In predicate on the "status" field. +func StatusIn(vs ...string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldIn(FieldStatus, vs...)) +} + +// StatusNotIn applies the NotIn predicate on the "status" field. +func StatusNotIn(vs ...string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldNotIn(FieldStatus, vs...)) +} + +// StatusGT applies the GT predicate on the "status" field. +func StatusGT(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldGT(FieldStatus, v)) +} + +// StatusGTE applies the GTE predicate on the "status" field. +func StatusGTE(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldGTE(FieldStatus, v)) +} + +// StatusLT applies the LT predicate on the "status" field. +func StatusLT(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldLT(FieldStatus, v)) +} + +// StatusLTE applies the LTE predicate on the "status" field. +func StatusLTE(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldLTE(FieldStatus, v)) +} + +// StatusContains applies the Contains predicate on the "status" field. +func StatusContains(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldContains(FieldStatus, v)) +} + +// StatusHasPrefix applies the HasPrefix predicate on the "status" field. +func StatusHasPrefix(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldHasPrefix(FieldStatus, v)) +} + +// StatusHasSuffix applies the HasSuffix predicate on the "status" field. +func StatusHasSuffix(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldHasSuffix(FieldStatus, v)) +} + +// StatusEqualFold applies the EqualFold predicate on the "status" field. +func StatusEqualFold(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldEqualFold(FieldStatus, v)) +} + +// StatusContainsFold applies the ContainsFold predicate on the "status" field. +func StatusContainsFold(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldContainsFold(FieldStatus, v)) +} + +// ConnectionStateEQ applies the EQ predicate on the "connection_state" field. +func ConnectionStateEQ(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldEQ(FieldConnectionState, v)) +} + +// ConnectionStateNEQ applies the NEQ predicate on the "connection_state" field. +func ConnectionStateNEQ(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldNEQ(FieldConnectionState, v)) +} + +// ConnectionStateIn applies the In predicate on the "connection_state" field. +func ConnectionStateIn(vs ...string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldIn(FieldConnectionState, vs...)) +} + +// ConnectionStateNotIn applies the NotIn predicate on the "connection_state" field. +func ConnectionStateNotIn(vs ...string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldNotIn(FieldConnectionState, vs...)) +} + +// ConnectionStateGT applies the GT predicate on the "connection_state" field. +func ConnectionStateGT(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldGT(FieldConnectionState, v)) +} + +// ConnectionStateGTE applies the GTE predicate on the "connection_state" field. +func ConnectionStateGTE(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldGTE(FieldConnectionState, v)) +} + +// ConnectionStateLT applies the LT predicate on the "connection_state" field. +func ConnectionStateLT(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldLT(FieldConnectionState, v)) +} + +// ConnectionStateLTE applies the LTE predicate on the "connection_state" field. +func ConnectionStateLTE(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldLTE(FieldConnectionState, v)) +} + +// ConnectionStateContains applies the Contains predicate on the "connection_state" field. +func ConnectionStateContains(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldContains(FieldConnectionState, v)) +} + +// ConnectionStateHasPrefix applies the HasPrefix predicate on the "connection_state" field. +func ConnectionStateHasPrefix(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldHasPrefix(FieldConnectionState, v)) +} + +// ConnectionStateHasSuffix applies the HasSuffix predicate on the "connection_state" field. +func ConnectionStateHasSuffix(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldHasSuffix(FieldConnectionState, v)) +} + +// ConnectionStateEqualFold applies the EqualFold predicate on the "connection_state" field. +func ConnectionStateEqualFold(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldEqualFold(FieldConnectionState, v)) +} + +// ConnectionStateContainsFold applies the ContainsFold predicate on the "connection_state" field. +func ConnectionStateContainsFold(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldContainsFold(FieldConnectionState, v)) +} + +// LastHeartbeatEQ applies the EQ predicate on the "last_heartbeat" field. +func LastHeartbeatEQ(v time.Time) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldEQ(FieldLastHeartbeat, v)) +} + +// LastHeartbeatNEQ applies the NEQ predicate on the "last_heartbeat" field. +func LastHeartbeatNEQ(v time.Time) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldNEQ(FieldLastHeartbeat, v)) +} + +// LastHeartbeatIn applies the In predicate on the "last_heartbeat" field. +func LastHeartbeatIn(vs ...time.Time) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldIn(FieldLastHeartbeat, vs...)) +} + +// LastHeartbeatNotIn applies the NotIn predicate on the "last_heartbeat" field. +func LastHeartbeatNotIn(vs ...time.Time) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldNotIn(FieldLastHeartbeat, vs...)) +} + +// LastHeartbeatGT applies the GT predicate on the "last_heartbeat" field. +func LastHeartbeatGT(v time.Time) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldGT(FieldLastHeartbeat, v)) +} + +// LastHeartbeatGTE applies the GTE predicate on the "last_heartbeat" field. +func LastHeartbeatGTE(v time.Time) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldGTE(FieldLastHeartbeat, v)) +} + +// LastHeartbeatLT applies the LT predicate on the "last_heartbeat" field. +func LastHeartbeatLT(v time.Time) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldLT(FieldLastHeartbeat, v)) +} + +// LastHeartbeatLTE applies the LTE predicate on the "last_heartbeat" field. +func LastHeartbeatLTE(v time.Time) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldLTE(FieldLastHeartbeat, v)) +} + +// LastHeartbeatIsNil applies the IsNil predicate on the "last_heartbeat" field. +func LastHeartbeatIsNil() predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldIsNull(FieldLastHeartbeat)) +} + +// LastHeartbeatNotNil applies the NotNil predicate on the "last_heartbeat" field. +func LastHeartbeatNotNil() predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldNotNull(FieldLastHeartbeat)) +} + +// CapabilitiesEQ applies the EQ predicate on the "capabilities" field. +func CapabilitiesEQ(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldEQ(FieldCapabilities, v)) +} + +// CapabilitiesNEQ applies the NEQ predicate on the "capabilities" field. +func CapabilitiesNEQ(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldNEQ(FieldCapabilities, v)) +} + +// CapabilitiesIn applies the In predicate on the "capabilities" field. +func CapabilitiesIn(vs ...string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldIn(FieldCapabilities, vs...)) +} + +// CapabilitiesNotIn applies the NotIn predicate on the "capabilities" field. +func CapabilitiesNotIn(vs ...string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldNotIn(FieldCapabilities, vs...)) +} + +// CapabilitiesGT applies the GT predicate on the "capabilities" field. +func CapabilitiesGT(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldGT(FieldCapabilities, v)) +} + +// CapabilitiesGTE applies the GTE predicate on the "capabilities" field. +func CapabilitiesGTE(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldGTE(FieldCapabilities, v)) +} + +// CapabilitiesLT applies the LT predicate on the "capabilities" field. +func CapabilitiesLT(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldLT(FieldCapabilities, v)) +} + +// CapabilitiesLTE applies the LTE predicate on the "capabilities" field. +func CapabilitiesLTE(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldLTE(FieldCapabilities, v)) +} + +// CapabilitiesContains applies the Contains predicate on the "capabilities" field. +func CapabilitiesContains(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldContains(FieldCapabilities, v)) +} + +// CapabilitiesHasPrefix applies the HasPrefix predicate on the "capabilities" field. +func CapabilitiesHasPrefix(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldHasPrefix(FieldCapabilities, v)) +} + +// CapabilitiesHasSuffix applies the HasSuffix predicate on the "capabilities" field. +func CapabilitiesHasSuffix(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldHasSuffix(FieldCapabilities, v)) +} + +// CapabilitiesIsNil applies the IsNil predicate on the "capabilities" field. +func CapabilitiesIsNil() predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldIsNull(FieldCapabilities)) +} + +// CapabilitiesNotNil applies the NotNil predicate on the "capabilities" field. +func CapabilitiesNotNil() predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldNotNull(FieldCapabilities)) +} + +// CapabilitiesEqualFold applies the EqualFold predicate on the "capabilities" field. +func CapabilitiesEqualFold(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldEqualFold(FieldCapabilities, v)) +} + +// CapabilitiesContainsFold applies the ContainsFold predicate on the "capabilities" field. +func CapabilitiesContainsFold(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldContainsFold(FieldCapabilities, v)) +} + +// SupportedHarnessesEQ applies the EQ predicate on the "supported_harnesses" field. +func SupportedHarnessesEQ(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldEQ(FieldSupportedHarnesses, v)) +} + +// SupportedHarnessesNEQ applies the NEQ predicate on the "supported_harnesses" field. +func SupportedHarnessesNEQ(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldNEQ(FieldSupportedHarnesses, v)) +} + +// SupportedHarnessesIn applies the In predicate on the "supported_harnesses" field. +func SupportedHarnessesIn(vs ...string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldIn(FieldSupportedHarnesses, vs...)) +} + +// SupportedHarnessesNotIn applies the NotIn predicate on the "supported_harnesses" field. +func SupportedHarnessesNotIn(vs ...string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldNotIn(FieldSupportedHarnesses, vs...)) +} + +// SupportedHarnessesGT applies the GT predicate on the "supported_harnesses" field. +func SupportedHarnessesGT(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldGT(FieldSupportedHarnesses, v)) +} + +// SupportedHarnessesGTE applies the GTE predicate on the "supported_harnesses" field. +func SupportedHarnessesGTE(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldGTE(FieldSupportedHarnesses, v)) +} + +// SupportedHarnessesLT applies the LT predicate on the "supported_harnesses" field. +func SupportedHarnessesLT(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldLT(FieldSupportedHarnesses, v)) +} + +// SupportedHarnessesLTE applies the LTE predicate on the "supported_harnesses" field. +func SupportedHarnessesLTE(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldLTE(FieldSupportedHarnesses, v)) +} + +// SupportedHarnessesContains applies the Contains predicate on the "supported_harnesses" field. +func SupportedHarnessesContains(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldContains(FieldSupportedHarnesses, v)) +} + +// SupportedHarnessesHasPrefix applies the HasPrefix predicate on the "supported_harnesses" field. +func SupportedHarnessesHasPrefix(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldHasPrefix(FieldSupportedHarnesses, v)) +} + +// SupportedHarnessesHasSuffix applies the HasSuffix predicate on the "supported_harnesses" field. +func SupportedHarnessesHasSuffix(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldHasSuffix(FieldSupportedHarnesses, v)) +} + +// SupportedHarnessesIsNil applies the IsNil predicate on the "supported_harnesses" field. +func SupportedHarnessesIsNil() predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldIsNull(FieldSupportedHarnesses)) +} + +// SupportedHarnessesNotNil applies the NotNil predicate on the "supported_harnesses" field. +func SupportedHarnessesNotNil() predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldNotNull(FieldSupportedHarnesses)) +} + +// SupportedHarnessesEqualFold applies the EqualFold predicate on the "supported_harnesses" field. +func SupportedHarnessesEqualFold(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldEqualFold(FieldSupportedHarnesses, v)) +} + +// SupportedHarnessesContainsFold applies the ContainsFold predicate on the "supported_harnesses" field. +func SupportedHarnessesContainsFold(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldContainsFold(FieldSupportedHarnesses, v)) +} + +// ResourcesEQ applies the EQ predicate on the "resources" field. +func ResourcesEQ(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldEQ(FieldResources, v)) +} + +// ResourcesNEQ applies the NEQ predicate on the "resources" field. +func ResourcesNEQ(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldNEQ(FieldResources, v)) +} + +// ResourcesIn applies the In predicate on the "resources" field. +func ResourcesIn(vs ...string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldIn(FieldResources, vs...)) +} + +// ResourcesNotIn applies the NotIn predicate on the "resources" field. +func ResourcesNotIn(vs ...string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldNotIn(FieldResources, vs...)) +} + +// ResourcesGT applies the GT predicate on the "resources" field. +func ResourcesGT(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldGT(FieldResources, v)) +} + +// ResourcesGTE applies the GTE predicate on the "resources" field. +func ResourcesGTE(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldGTE(FieldResources, v)) +} + +// ResourcesLT applies the LT predicate on the "resources" field. +func ResourcesLT(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldLT(FieldResources, v)) +} + +// ResourcesLTE applies the LTE predicate on the "resources" field. +func ResourcesLTE(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldLTE(FieldResources, v)) +} + +// ResourcesContains applies the Contains predicate on the "resources" field. +func ResourcesContains(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldContains(FieldResources, v)) +} + +// ResourcesHasPrefix applies the HasPrefix predicate on the "resources" field. +func ResourcesHasPrefix(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldHasPrefix(FieldResources, v)) +} + +// ResourcesHasSuffix applies the HasSuffix predicate on the "resources" field. +func ResourcesHasSuffix(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldHasSuffix(FieldResources, v)) +} + +// ResourcesIsNil applies the IsNil predicate on the "resources" field. +func ResourcesIsNil() predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldIsNull(FieldResources)) +} + +// ResourcesNotNil applies the NotNil predicate on the "resources" field. +func ResourcesNotNil() predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldNotNull(FieldResources)) +} + +// ResourcesEqualFold applies the EqualFold predicate on the "resources" field. +func ResourcesEqualFold(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldEqualFold(FieldResources, v)) +} + +// ResourcesContainsFold applies the ContainsFold predicate on the "resources" field. +func ResourcesContainsFold(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldContainsFold(FieldResources, v)) +} + +// RuntimesEQ applies the EQ predicate on the "runtimes" field. +func RuntimesEQ(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldEQ(FieldRuntimes, v)) +} + +// RuntimesNEQ applies the NEQ predicate on the "runtimes" field. +func RuntimesNEQ(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldNEQ(FieldRuntimes, v)) +} + +// RuntimesIn applies the In predicate on the "runtimes" field. +func RuntimesIn(vs ...string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldIn(FieldRuntimes, vs...)) +} + +// RuntimesNotIn applies the NotIn predicate on the "runtimes" field. +func RuntimesNotIn(vs ...string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldNotIn(FieldRuntimes, vs...)) +} + +// RuntimesGT applies the GT predicate on the "runtimes" field. +func RuntimesGT(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldGT(FieldRuntimes, v)) +} + +// RuntimesGTE applies the GTE predicate on the "runtimes" field. +func RuntimesGTE(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldGTE(FieldRuntimes, v)) +} + +// RuntimesLT applies the LT predicate on the "runtimes" field. +func RuntimesLT(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldLT(FieldRuntimes, v)) +} + +// RuntimesLTE applies the LTE predicate on the "runtimes" field. +func RuntimesLTE(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldLTE(FieldRuntimes, v)) +} + +// RuntimesContains applies the Contains predicate on the "runtimes" field. +func RuntimesContains(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldContains(FieldRuntimes, v)) +} + +// RuntimesHasPrefix applies the HasPrefix predicate on the "runtimes" field. +func RuntimesHasPrefix(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldHasPrefix(FieldRuntimes, v)) +} + +// RuntimesHasSuffix applies the HasSuffix predicate on the "runtimes" field. +func RuntimesHasSuffix(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldHasSuffix(FieldRuntimes, v)) +} + +// RuntimesIsNil applies the IsNil predicate on the "runtimes" field. +func RuntimesIsNil() predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldIsNull(FieldRuntimes)) +} + +// RuntimesNotNil applies the NotNil predicate on the "runtimes" field. +func RuntimesNotNil() predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldNotNull(FieldRuntimes)) +} + +// RuntimesEqualFold applies the EqualFold predicate on the "runtimes" field. +func RuntimesEqualFold(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldEqualFold(FieldRuntimes, v)) +} + +// RuntimesContainsFold applies the ContainsFold predicate on the "runtimes" field. +func RuntimesContainsFold(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldContainsFold(FieldRuntimes, v)) +} + +// LabelsEQ applies the EQ predicate on the "labels" field. +func LabelsEQ(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldEQ(FieldLabels, v)) +} + +// LabelsNEQ applies the NEQ predicate on the "labels" field. +func LabelsNEQ(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldNEQ(FieldLabels, v)) +} + +// LabelsIn applies the In predicate on the "labels" field. +func LabelsIn(vs ...string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldIn(FieldLabels, vs...)) +} + +// LabelsNotIn applies the NotIn predicate on the "labels" field. +func LabelsNotIn(vs ...string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldNotIn(FieldLabels, vs...)) +} + +// LabelsGT applies the GT predicate on the "labels" field. +func LabelsGT(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldGT(FieldLabels, v)) +} + +// LabelsGTE applies the GTE predicate on the "labels" field. +func LabelsGTE(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldGTE(FieldLabels, v)) +} + +// LabelsLT applies the LT predicate on the "labels" field. +func LabelsLT(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldLT(FieldLabels, v)) +} + +// LabelsLTE applies the LTE predicate on the "labels" field. +func LabelsLTE(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldLTE(FieldLabels, v)) +} + +// LabelsContains applies the Contains predicate on the "labels" field. +func LabelsContains(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldContains(FieldLabels, v)) +} + +// LabelsHasPrefix applies the HasPrefix predicate on the "labels" field. +func LabelsHasPrefix(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldHasPrefix(FieldLabels, v)) +} + +// LabelsHasSuffix applies the HasSuffix predicate on the "labels" field. +func LabelsHasSuffix(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldHasSuffix(FieldLabels, v)) +} + +// LabelsIsNil applies the IsNil predicate on the "labels" field. +func LabelsIsNil() predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldIsNull(FieldLabels)) +} + +// LabelsNotNil applies the NotNil predicate on the "labels" field. +func LabelsNotNil() predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldNotNull(FieldLabels)) +} + +// LabelsEqualFold applies the EqualFold predicate on the "labels" field. +func LabelsEqualFold(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldEqualFold(FieldLabels, v)) +} + +// LabelsContainsFold applies the ContainsFold predicate on the "labels" field. +func LabelsContainsFold(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldContainsFold(FieldLabels, v)) +} + +// AnnotationsEQ applies the EQ predicate on the "annotations" field. +func AnnotationsEQ(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldEQ(FieldAnnotations, v)) +} + +// AnnotationsNEQ applies the NEQ predicate on the "annotations" field. +func AnnotationsNEQ(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldNEQ(FieldAnnotations, v)) +} + +// AnnotationsIn applies the In predicate on the "annotations" field. +func AnnotationsIn(vs ...string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldIn(FieldAnnotations, vs...)) +} + +// AnnotationsNotIn applies the NotIn predicate on the "annotations" field. +func AnnotationsNotIn(vs ...string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldNotIn(FieldAnnotations, vs...)) +} + +// AnnotationsGT applies the GT predicate on the "annotations" field. +func AnnotationsGT(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldGT(FieldAnnotations, v)) +} + +// AnnotationsGTE applies the GTE predicate on the "annotations" field. +func AnnotationsGTE(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldGTE(FieldAnnotations, v)) +} + +// AnnotationsLT applies the LT predicate on the "annotations" field. +func AnnotationsLT(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldLT(FieldAnnotations, v)) +} + +// AnnotationsLTE applies the LTE predicate on the "annotations" field. +func AnnotationsLTE(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldLTE(FieldAnnotations, v)) +} + +// AnnotationsContains applies the Contains predicate on the "annotations" field. +func AnnotationsContains(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldContains(FieldAnnotations, v)) +} + +// AnnotationsHasPrefix applies the HasPrefix predicate on the "annotations" field. +func AnnotationsHasPrefix(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldHasPrefix(FieldAnnotations, v)) +} + +// AnnotationsHasSuffix applies the HasSuffix predicate on the "annotations" field. +func AnnotationsHasSuffix(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldHasSuffix(FieldAnnotations, v)) +} + +// AnnotationsIsNil applies the IsNil predicate on the "annotations" field. +func AnnotationsIsNil() predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldIsNull(FieldAnnotations)) +} + +// AnnotationsNotNil applies the NotNil predicate on the "annotations" field. +func AnnotationsNotNil() predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldNotNull(FieldAnnotations)) +} + +// AnnotationsEqualFold applies the EqualFold predicate on the "annotations" field. +func AnnotationsEqualFold(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldEqualFold(FieldAnnotations, v)) +} + +// AnnotationsContainsFold applies the ContainsFold predicate on the "annotations" field. +func AnnotationsContainsFold(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldContainsFold(FieldAnnotations, v)) +} + +// EndpointEQ applies the EQ predicate on the "endpoint" field. +func EndpointEQ(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldEQ(FieldEndpoint, v)) +} + +// EndpointNEQ applies the NEQ predicate on the "endpoint" field. +func EndpointNEQ(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldNEQ(FieldEndpoint, v)) +} + +// EndpointIn applies the In predicate on the "endpoint" field. +func EndpointIn(vs ...string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldIn(FieldEndpoint, vs...)) +} + +// EndpointNotIn applies the NotIn predicate on the "endpoint" field. +func EndpointNotIn(vs ...string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldNotIn(FieldEndpoint, vs...)) +} + +// EndpointGT applies the GT predicate on the "endpoint" field. +func EndpointGT(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldGT(FieldEndpoint, v)) +} + +// EndpointGTE applies the GTE predicate on the "endpoint" field. +func EndpointGTE(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldGTE(FieldEndpoint, v)) +} + +// EndpointLT applies the LT predicate on the "endpoint" field. +func EndpointLT(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldLT(FieldEndpoint, v)) +} + +// EndpointLTE applies the LTE predicate on the "endpoint" field. +func EndpointLTE(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldLTE(FieldEndpoint, v)) +} + +// EndpointContains applies the Contains predicate on the "endpoint" field. +func EndpointContains(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldContains(FieldEndpoint, v)) +} + +// EndpointHasPrefix applies the HasPrefix predicate on the "endpoint" field. +func EndpointHasPrefix(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldHasPrefix(FieldEndpoint, v)) +} + +// EndpointHasSuffix applies the HasSuffix predicate on the "endpoint" field. +func EndpointHasSuffix(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldHasSuffix(FieldEndpoint, v)) +} + +// EndpointIsNil applies the IsNil predicate on the "endpoint" field. +func EndpointIsNil() predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldIsNull(FieldEndpoint)) +} + +// EndpointNotNil applies the NotNil predicate on the "endpoint" field. +func EndpointNotNil() predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldNotNull(FieldEndpoint)) +} + +// EndpointEqualFold applies the EqualFold predicate on the "endpoint" field. +func EndpointEqualFold(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldEqualFold(FieldEndpoint, v)) +} + +// EndpointContainsFold applies the ContainsFold predicate on the "endpoint" field. +func EndpointContainsFold(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldContainsFold(FieldEndpoint, v)) +} + +// CreatedByEQ applies the EQ predicate on the "created_by" field. +func CreatedByEQ(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldEQ(FieldCreatedBy, v)) +} + +// CreatedByNEQ applies the NEQ predicate on the "created_by" field. +func CreatedByNEQ(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldNEQ(FieldCreatedBy, v)) +} + +// CreatedByIn applies the In predicate on the "created_by" field. +func CreatedByIn(vs ...string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldIn(FieldCreatedBy, vs...)) +} + +// CreatedByNotIn applies the NotIn predicate on the "created_by" field. +func CreatedByNotIn(vs ...string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldNotIn(FieldCreatedBy, vs...)) +} + +// CreatedByGT applies the GT predicate on the "created_by" field. +func CreatedByGT(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldGT(FieldCreatedBy, v)) +} + +// CreatedByGTE applies the GTE predicate on the "created_by" field. +func CreatedByGTE(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldGTE(FieldCreatedBy, v)) +} + +// CreatedByLT applies the LT predicate on the "created_by" field. +func CreatedByLT(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldLT(FieldCreatedBy, v)) +} + +// CreatedByLTE applies the LTE predicate on the "created_by" field. +func CreatedByLTE(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldLTE(FieldCreatedBy, v)) +} + +// CreatedByContains applies the Contains predicate on the "created_by" field. +func CreatedByContains(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldContains(FieldCreatedBy, v)) +} + +// CreatedByHasPrefix applies the HasPrefix predicate on the "created_by" field. +func CreatedByHasPrefix(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldHasPrefix(FieldCreatedBy, v)) +} + +// CreatedByHasSuffix applies the HasSuffix predicate on the "created_by" field. +func CreatedByHasSuffix(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldHasSuffix(FieldCreatedBy, v)) +} + +// CreatedByIsNil applies the IsNil predicate on the "created_by" field. +func CreatedByIsNil() predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldIsNull(FieldCreatedBy)) +} + +// CreatedByNotNil applies the NotNil predicate on the "created_by" field. +func CreatedByNotNil() predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldNotNull(FieldCreatedBy)) +} + +// CreatedByEqualFold applies the EqualFold predicate on the "created_by" field. +func CreatedByEqualFold(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldEqualFold(FieldCreatedBy, v)) +} + +// CreatedByContainsFold applies the ContainsFold predicate on the "created_by" field. +func CreatedByContainsFold(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldContainsFold(FieldCreatedBy, v)) +} + +// AutoProvideEQ applies the EQ predicate on the "auto_provide" field. +func AutoProvideEQ(v bool) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldEQ(FieldAutoProvide, v)) +} + +// AutoProvideNEQ applies the NEQ predicate on the "auto_provide" field. +func AutoProvideNEQ(v bool) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldNEQ(FieldAutoProvide, v)) +} + +// CreatedEQ applies the EQ predicate on the "created" field. +func CreatedEQ(v time.Time) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldEQ(FieldCreated, v)) +} + +// CreatedNEQ applies the NEQ predicate on the "created" field. +func CreatedNEQ(v time.Time) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldNEQ(FieldCreated, v)) +} + +// CreatedIn applies the In predicate on the "created" field. +func CreatedIn(vs ...time.Time) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldIn(FieldCreated, vs...)) +} + +// CreatedNotIn applies the NotIn predicate on the "created" field. +func CreatedNotIn(vs ...time.Time) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldNotIn(FieldCreated, vs...)) +} + +// CreatedGT applies the GT predicate on the "created" field. +func CreatedGT(v time.Time) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldGT(FieldCreated, v)) +} + +// CreatedGTE applies the GTE predicate on the "created" field. +func CreatedGTE(v time.Time) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldGTE(FieldCreated, v)) +} + +// CreatedLT applies the LT predicate on the "created" field. +func CreatedLT(v time.Time) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldLT(FieldCreated, v)) +} + +// CreatedLTE applies the LTE predicate on the "created" field. +func CreatedLTE(v time.Time) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldLTE(FieldCreated, v)) +} + +// UpdatedEQ applies the EQ predicate on the "updated" field. +func UpdatedEQ(v time.Time) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldEQ(FieldUpdated, v)) +} + +// UpdatedNEQ applies the NEQ predicate on the "updated" field. +func UpdatedNEQ(v time.Time) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldNEQ(FieldUpdated, v)) +} + +// UpdatedIn applies the In predicate on the "updated" field. +func UpdatedIn(vs ...time.Time) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldIn(FieldUpdated, vs...)) +} + +// UpdatedNotIn applies the NotIn predicate on the "updated" field. +func UpdatedNotIn(vs ...time.Time) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldNotIn(FieldUpdated, vs...)) +} + +// UpdatedGT applies the GT predicate on the "updated" field. +func UpdatedGT(v time.Time) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldGT(FieldUpdated, v)) +} + +// UpdatedGTE applies the GTE predicate on the "updated" field. +func UpdatedGTE(v time.Time) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldGTE(FieldUpdated, v)) +} + +// UpdatedLT applies the LT predicate on the "updated" field. +func UpdatedLT(v time.Time) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldLT(FieldUpdated, v)) +} + +// UpdatedLTE applies the LTE predicate on the "updated" field. +func UpdatedLTE(v time.Time) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldLTE(FieldUpdated, v)) +} + +// And groups predicates with the AND operator between them. +func And(predicates ...predicate.RuntimeBroker) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.AndPredicates(predicates...)) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.RuntimeBroker) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.OrPredicates(predicates...)) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.RuntimeBroker) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.NotPredicates(p)) +} diff --git a/pkg/ent/runtimebroker_create.go b/pkg/ent/runtimebroker_create.go new file mode 100644 index 000000000..f59f58467 --- /dev/null +++ b/pkg/ent/runtimebroker_create.go @@ -0,0 +1,1871 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/runtimebroker" + "github.com/google/uuid" +) + +// RuntimeBrokerCreate is the builder for creating a RuntimeBroker entity. +type RuntimeBrokerCreate struct { + config + mutation *RuntimeBrokerMutation + hooks []Hook + conflict []sql.ConflictOption +} + +// SetName sets the "name" field. +func (_c *RuntimeBrokerCreate) SetName(v string) *RuntimeBrokerCreate { + _c.mutation.SetName(v) + return _c +} + +// SetSlug sets the "slug" field. +func (_c *RuntimeBrokerCreate) SetSlug(v string) *RuntimeBrokerCreate { + _c.mutation.SetSlug(v) + return _c +} + +// SetType sets the "type" field. +func (_c *RuntimeBrokerCreate) SetType(v string) *RuntimeBrokerCreate { + _c.mutation.SetType(v) + return _c +} + +// SetNillableType sets the "type" field if the given value is not nil. +func (_c *RuntimeBrokerCreate) SetNillableType(v *string) *RuntimeBrokerCreate { + if v != nil { + _c.SetType(*v) + } + return _c +} + +// SetMode sets the "mode" field. +func (_c *RuntimeBrokerCreate) SetMode(v string) *RuntimeBrokerCreate { + _c.mutation.SetMode(v) + return _c +} + +// SetNillableMode sets the "mode" field if the given value is not nil. +func (_c *RuntimeBrokerCreate) SetNillableMode(v *string) *RuntimeBrokerCreate { + if v != nil { + _c.SetMode(*v) + } + return _c +} + +// SetVersion sets the "version" field. +func (_c *RuntimeBrokerCreate) SetVersion(v string) *RuntimeBrokerCreate { + _c.mutation.SetVersion(v) + return _c +} + +// SetNillableVersion sets the "version" field if the given value is not nil. +func (_c *RuntimeBrokerCreate) SetNillableVersion(v *string) *RuntimeBrokerCreate { + if v != nil { + _c.SetVersion(*v) + } + return _c +} + +// SetLockVersion sets the "lock_version" field. +func (_c *RuntimeBrokerCreate) SetLockVersion(v int64) *RuntimeBrokerCreate { + _c.mutation.SetLockVersion(v) + return _c +} + +// SetNillableLockVersion sets the "lock_version" field if the given value is not nil. +func (_c *RuntimeBrokerCreate) SetNillableLockVersion(v *int64) *RuntimeBrokerCreate { + if v != nil { + _c.SetLockVersion(*v) + } + return _c +} + +// SetStatus sets the "status" field. +func (_c *RuntimeBrokerCreate) SetStatus(v string) *RuntimeBrokerCreate { + _c.mutation.SetStatus(v) + return _c +} + +// SetNillableStatus sets the "status" field if the given value is not nil. +func (_c *RuntimeBrokerCreate) SetNillableStatus(v *string) *RuntimeBrokerCreate { + if v != nil { + _c.SetStatus(*v) + } + return _c +} + +// SetConnectionState sets the "connection_state" field. +func (_c *RuntimeBrokerCreate) SetConnectionState(v string) *RuntimeBrokerCreate { + _c.mutation.SetConnectionState(v) + return _c +} + +// SetNillableConnectionState sets the "connection_state" field if the given value is not nil. +func (_c *RuntimeBrokerCreate) SetNillableConnectionState(v *string) *RuntimeBrokerCreate { + if v != nil { + _c.SetConnectionState(*v) + } + return _c +} + +// SetLastHeartbeat sets the "last_heartbeat" field. +func (_c *RuntimeBrokerCreate) SetLastHeartbeat(v time.Time) *RuntimeBrokerCreate { + _c.mutation.SetLastHeartbeat(v) + return _c +} + +// SetNillableLastHeartbeat sets the "last_heartbeat" field if the given value is not nil. +func (_c *RuntimeBrokerCreate) SetNillableLastHeartbeat(v *time.Time) *RuntimeBrokerCreate { + if v != nil { + _c.SetLastHeartbeat(*v) + } + return _c +} + +// SetCapabilities sets the "capabilities" field. +func (_c *RuntimeBrokerCreate) SetCapabilities(v string) *RuntimeBrokerCreate { + _c.mutation.SetCapabilities(v) + return _c +} + +// SetNillableCapabilities sets the "capabilities" field if the given value is not nil. +func (_c *RuntimeBrokerCreate) SetNillableCapabilities(v *string) *RuntimeBrokerCreate { + if v != nil { + _c.SetCapabilities(*v) + } + return _c +} + +// SetSupportedHarnesses sets the "supported_harnesses" field. +func (_c *RuntimeBrokerCreate) SetSupportedHarnesses(v string) *RuntimeBrokerCreate { + _c.mutation.SetSupportedHarnesses(v) + return _c +} + +// SetNillableSupportedHarnesses sets the "supported_harnesses" field if the given value is not nil. +func (_c *RuntimeBrokerCreate) SetNillableSupportedHarnesses(v *string) *RuntimeBrokerCreate { + if v != nil { + _c.SetSupportedHarnesses(*v) + } + return _c +} + +// SetResources sets the "resources" field. +func (_c *RuntimeBrokerCreate) SetResources(v string) *RuntimeBrokerCreate { + _c.mutation.SetResources(v) + return _c +} + +// SetNillableResources sets the "resources" field if the given value is not nil. +func (_c *RuntimeBrokerCreate) SetNillableResources(v *string) *RuntimeBrokerCreate { + if v != nil { + _c.SetResources(*v) + } + return _c +} + +// SetRuntimes sets the "runtimes" field. +func (_c *RuntimeBrokerCreate) SetRuntimes(v string) *RuntimeBrokerCreate { + _c.mutation.SetRuntimes(v) + return _c +} + +// SetNillableRuntimes sets the "runtimes" field if the given value is not nil. +func (_c *RuntimeBrokerCreate) SetNillableRuntimes(v *string) *RuntimeBrokerCreate { + if v != nil { + _c.SetRuntimes(*v) + } + return _c +} + +// SetLabels sets the "labels" field. +func (_c *RuntimeBrokerCreate) SetLabels(v string) *RuntimeBrokerCreate { + _c.mutation.SetLabels(v) + return _c +} + +// SetNillableLabels sets the "labels" field if the given value is not nil. +func (_c *RuntimeBrokerCreate) SetNillableLabels(v *string) *RuntimeBrokerCreate { + if v != nil { + _c.SetLabels(*v) + } + return _c +} + +// SetAnnotations sets the "annotations" field. +func (_c *RuntimeBrokerCreate) SetAnnotations(v string) *RuntimeBrokerCreate { + _c.mutation.SetAnnotations(v) + return _c +} + +// SetNillableAnnotations sets the "annotations" field if the given value is not nil. +func (_c *RuntimeBrokerCreate) SetNillableAnnotations(v *string) *RuntimeBrokerCreate { + if v != nil { + _c.SetAnnotations(*v) + } + return _c +} + +// SetEndpoint sets the "endpoint" field. +func (_c *RuntimeBrokerCreate) SetEndpoint(v string) *RuntimeBrokerCreate { + _c.mutation.SetEndpoint(v) + return _c +} + +// SetNillableEndpoint sets the "endpoint" field if the given value is not nil. +func (_c *RuntimeBrokerCreate) SetNillableEndpoint(v *string) *RuntimeBrokerCreate { + if v != nil { + _c.SetEndpoint(*v) + } + return _c +} + +// SetCreatedBy sets the "created_by" field. +func (_c *RuntimeBrokerCreate) SetCreatedBy(v string) *RuntimeBrokerCreate { + _c.mutation.SetCreatedBy(v) + return _c +} + +// SetNillableCreatedBy sets the "created_by" field if the given value is not nil. +func (_c *RuntimeBrokerCreate) SetNillableCreatedBy(v *string) *RuntimeBrokerCreate { + if v != nil { + _c.SetCreatedBy(*v) + } + return _c +} + +// SetAutoProvide sets the "auto_provide" field. +func (_c *RuntimeBrokerCreate) SetAutoProvide(v bool) *RuntimeBrokerCreate { + _c.mutation.SetAutoProvide(v) + return _c +} + +// SetNillableAutoProvide sets the "auto_provide" field if the given value is not nil. +func (_c *RuntimeBrokerCreate) SetNillableAutoProvide(v *bool) *RuntimeBrokerCreate { + if v != nil { + _c.SetAutoProvide(*v) + } + return _c +} + +// SetCreated sets the "created" field. +func (_c *RuntimeBrokerCreate) SetCreated(v time.Time) *RuntimeBrokerCreate { + _c.mutation.SetCreated(v) + return _c +} + +// SetNillableCreated sets the "created" field if the given value is not nil. +func (_c *RuntimeBrokerCreate) SetNillableCreated(v *time.Time) *RuntimeBrokerCreate { + if v != nil { + _c.SetCreated(*v) + } + return _c +} + +// SetUpdated sets the "updated" field. +func (_c *RuntimeBrokerCreate) SetUpdated(v time.Time) *RuntimeBrokerCreate { + _c.mutation.SetUpdated(v) + return _c +} + +// SetNillableUpdated sets the "updated" field if the given value is not nil. +func (_c *RuntimeBrokerCreate) SetNillableUpdated(v *time.Time) *RuntimeBrokerCreate { + if v != nil { + _c.SetUpdated(*v) + } + return _c +} + +// SetID sets the "id" field. +func (_c *RuntimeBrokerCreate) SetID(v uuid.UUID) *RuntimeBrokerCreate { + _c.mutation.SetID(v) + return _c +} + +// SetNillableID sets the "id" field if the given value is not nil. +func (_c *RuntimeBrokerCreate) SetNillableID(v *uuid.UUID) *RuntimeBrokerCreate { + if v != nil { + _c.SetID(*v) + } + return _c +} + +// Mutation returns the RuntimeBrokerMutation object of the builder. +func (_c *RuntimeBrokerCreate) Mutation() *RuntimeBrokerMutation { + return _c.mutation +} + +// Save creates the RuntimeBroker in the database. +func (_c *RuntimeBrokerCreate) Save(ctx context.Context) (*RuntimeBroker, error) { + _c.defaults() + return withHooks(ctx, _c.sqlSave, _c.mutation, _c.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (_c *RuntimeBrokerCreate) SaveX(ctx context.Context) *RuntimeBroker { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *RuntimeBrokerCreate) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *RuntimeBrokerCreate) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_c *RuntimeBrokerCreate) defaults() { + if _, ok := _c.mutation.Mode(); !ok { + v := runtimebroker.DefaultMode + _c.mutation.SetMode(v) + } + if _, ok := _c.mutation.LockVersion(); !ok { + v := runtimebroker.DefaultLockVersion + _c.mutation.SetLockVersion(v) + } + if _, ok := _c.mutation.Status(); !ok { + v := runtimebroker.DefaultStatus + _c.mutation.SetStatus(v) + } + if _, ok := _c.mutation.ConnectionState(); !ok { + v := runtimebroker.DefaultConnectionState + _c.mutation.SetConnectionState(v) + } + if _, ok := _c.mutation.AutoProvide(); !ok { + v := runtimebroker.DefaultAutoProvide + _c.mutation.SetAutoProvide(v) + } + if _, ok := _c.mutation.Created(); !ok { + v := runtimebroker.DefaultCreated() + _c.mutation.SetCreated(v) + } + if _, ok := _c.mutation.Updated(); !ok { + v := runtimebroker.DefaultUpdated() + _c.mutation.SetUpdated(v) + } + if _, ok := _c.mutation.ID(); !ok { + v := runtimebroker.DefaultID() + _c.mutation.SetID(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_c *RuntimeBrokerCreate) check() error { + if _, ok := _c.mutation.Name(); !ok { + return &ValidationError{Name: "name", err: errors.New(`ent: missing required field "RuntimeBroker.name"`)} + } + if v, ok := _c.mutation.Name(); ok { + if err := runtimebroker.NameValidator(v); err != nil { + return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "RuntimeBroker.name": %w`, err)} + } + } + if _, ok := _c.mutation.Slug(); !ok { + return &ValidationError{Name: "slug", err: errors.New(`ent: missing required field "RuntimeBroker.slug"`)} + } + if v, ok := _c.mutation.Slug(); ok { + if err := runtimebroker.SlugValidator(v); err != nil { + return &ValidationError{Name: "slug", err: fmt.Errorf(`ent: validator failed for field "RuntimeBroker.slug": %w`, err)} + } + } + if _, ok := _c.mutation.Mode(); !ok { + return &ValidationError{Name: "mode", err: errors.New(`ent: missing required field "RuntimeBroker.mode"`)} + } + if _, ok := _c.mutation.LockVersion(); !ok { + return &ValidationError{Name: "lock_version", err: errors.New(`ent: missing required field "RuntimeBroker.lock_version"`)} + } + if _, ok := _c.mutation.Status(); !ok { + return &ValidationError{Name: "status", err: errors.New(`ent: missing required field "RuntimeBroker.status"`)} + } + if _, ok := _c.mutation.ConnectionState(); !ok { + return &ValidationError{Name: "connection_state", err: errors.New(`ent: missing required field "RuntimeBroker.connection_state"`)} + } + if _, ok := _c.mutation.AutoProvide(); !ok { + return &ValidationError{Name: "auto_provide", err: errors.New(`ent: missing required field "RuntimeBroker.auto_provide"`)} + } + if _, ok := _c.mutation.Created(); !ok { + return &ValidationError{Name: "created", err: errors.New(`ent: missing required field "RuntimeBroker.created"`)} + } + if _, ok := _c.mutation.Updated(); !ok { + return &ValidationError{Name: "updated", err: errors.New(`ent: missing required field "RuntimeBroker.updated"`)} + } + return nil +} + +func (_c *RuntimeBrokerCreate) sqlSave(ctx context.Context) (*RuntimeBroker, error) { + if err := _c.check(); err != nil { + return nil, err + } + _node, _spec := _c.createSpec() + if err := sqlgraph.CreateNode(ctx, _c.driver, _spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + if _spec.ID.Value != nil { + if id, ok := _spec.ID.Value.(*uuid.UUID); ok { + _node.ID = *id + } else if err := _node.ID.Scan(_spec.ID.Value); err != nil { + return nil, err + } + } + _c.mutation.id = &_node.ID + _c.mutation.done = true + return _node, nil +} + +func (_c *RuntimeBrokerCreate) createSpec() (*RuntimeBroker, *sqlgraph.CreateSpec) { + var ( + _node = &RuntimeBroker{config: _c.config} + _spec = sqlgraph.NewCreateSpec(runtimebroker.Table, sqlgraph.NewFieldSpec(runtimebroker.FieldID, field.TypeUUID)) + ) + _spec.OnConflict = _c.conflict + if id, ok := _c.mutation.ID(); ok { + _node.ID = id + _spec.ID.Value = &id + } + if value, ok := _c.mutation.Name(); ok { + _spec.SetField(runtimebroker.FieldName, field.TypeString, value) + _node.Name = value + } + if value, ok := _c.mutation.Slug(); ok { + _spec.SetField(runtimebroker.FieldSlug, field.TypeString, value) + _node.Slug = value + } + if value, ok := _c.mutation.GetType(); ok { + _spec.SetField(runtimebroker.FieldType, field.TypeString, value) + _node.Type = value + } + if value, ok := _c.mutation.Mode(); ok { + _spec.SetField(runtimebroker.FieldMode, field.TypeString, value) + _node.Mode = value + } + if value, ok := _c.mutation.Version(); ok { + _spec.SetField(runtimebroker.FieldVersion, field.TypeString, value) + _node.Version = value + } + if value, ok := _c.mutation.LockVersion(); ok { + _spec.SetField(runtimebroker.FieldLockVersion, field.TypeInt64, value) + _node.LockVersion = value + } + if value, ok := _c.mutation.Status(); ok { + _spec.SetField(runtimebroker.FieldStatus, field.TypeString, value) + _node.Status = value + } + if value, ok := _c.mutation.ConnectionState(); ok { + _spec.SetField(runtimebroker.FieldConnectionState, field.TypeString, value) + _node.ConnectionState = value + } + if value, ok := _c.mutation.LastHeartbeat(); ok { + _spec.SetField(runtimebroker.FieldLastHeartbeat, field.TypeTime, value) + _node.LastHeartbeat = &value + } + if value, ok := _c.mutation.Capabilities(); ok { + _spec.SetField(runtimebroker.FieldCapabilities, field.TypeString, value) + _node.Capabilities = value + } + if value, ok := _c.mutation.SupportedHarnesses(); ok { + _spec.SetField(runtimebroker.FieldSupportedHarnesses, field.TypeString, value) + _node.SupportedHarnesses = value + } + if value, ok := _c.mutation.Resources(); ok { + _spec.SetField(runtimebroker.FieldResources, field.TypeString, value) + _node.Resources = value + } + if value, ok := _c.mutation.Runtimes(); ok { + _spec.SetField(runtimebroker.FieldRuntimes, field.TypeString, value) + _node.Runtimes = value + } + if value, ok := _c.mutation.Labels(); ok { + _spec.SetField(runtimebroker.FieldLabels, field.TypeString, value) + _node.Labels = value + } + if value, ok := _c.mutation.Annotations(); ok { + _spec.SetField(runtimebroker.FieldAnnotations, field.TypeString, value) + _node.Annotations = value + } + if value, ok := _c.mutation.Endpoint(); ok { + _spec.SetField(runtimebroker.FieldEndpoint, field.TypeString, value) + _node.Endpoint = value + } + if value, ok := _c.mutation.CreatedBy(); ok { + _spec.SetField(runtimebroker.FieldCreatedBy, field.TypeString, value) + _node.CreatedBy = value + } + if value, ok := _c.mutation.AutoProvide(); ok { + _spec.SetField(runtimebroker.FieldAutoProvide, field.TypeBool, value) + _node.AutoProvide = value + } + if value, ok := _c.mutation.Created(); ok { + _spec.SetField(runtimebroker.FieldCreated, field.TypeTime, value) + _node.Created = value + } + if value, ok := _c.mutation.Updated(); ok { + _spec.SetField(runtimebroker.FieldUpdated, field.TypeTime, value) + _node.Updated = value + } + return _node, _spec +} + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.RuntimeBroker.Create(). +// SetName(v). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.RuntimeBrokerUpsert) { +// SetName(v+v). +// }). +// Exec(ctx) +func (_c *RuntimeBrokerCreate) OnConflict(opts ...sql.ConflictOption) *RuntimeBrokerUpsertOne { + _c.conflict = opts + return &RuntimeBrokerUpsertOne{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.RuntimeBroker.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *RuntimeBrokerCreate) OnConflictColumns(columns ...string) *RuntimeBrokerUpsertOne { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &RuntimeBrokerUpsertOne{ + create: _c, + } +} + +type ( + // RuntimeBrokerUpsertOne is the builder for "upsert"-ing + // one RuntimeBroker node. + RuntimeBrokerUpsertOne struct { + create *RuntimeBrokerCreate + } + + // RuntimeBrokerUpsert is the "OnConflict" setter. + RuntimeBrokerUpsert struct { + *sql.UpdateSet + } +) + +// SetName sets the "name" field. +func (u *RuntimeBrokerUpsert) SetName(v string) *RuntimeBrokerUpsert { + u.Set(runtimebroker.FieldName, v) + return u +} + +// UpdateName sets the "name" field to the value that was provided on create. +func (u *RuntimeBrokerUpsert) UpdateName() *RuntimeBrokerUpsert { + u.SetExcluded(runtimebroker.FieldName) + return u +} + +// SetSlug sets the "slug" field. +func (u *RuntimeBrokerUpsert) SetSlug(v string) *RuntimeBrokerUpsert { + u.Set(runtimebroker.FieldSlug, v) + return u +} + +// UpdateSlug sets the "slug" field to the value that was provided on create. +func (u *RuntimeBrokerUpsert) UpdateSlug() *RuntimeBrokerUpsert { + u.SetExcluded(runtimebroker.FieldSlug) + return u +} + +// SetType sets the "type" field. +func (u *RuntimeBrokerUpsert) SetType(v string) *RuntimeBrokerUpsert { + u.Set(runtimebroker.FieldType, v) + return u +} + +// UpdateType sets the "type" field to the value that was provided on create. +func (u *RuntimeBrokerUpsert) UpdateType() *RuntimeBrokerUpsert { + u.SetExcluded(runtimebroker.FieldType) + return u +} + +// ClearType clears the value of the "type" field. +func (u *RuntimeBrokerUpsert) ClearType() *RuntimeBrokerUpsert { + u.SetNull(runtimebroker.FieldType) + return u +} + +// SetMode sets the "mode" field. +func (u *RuntimeBrokerUpsert) SetMode(v string) *RuntimeBrokerUpsert { + u.Set(runtimebroker.FieldMode, v) + return u +} + +// UpdateMode sets the "mode" field to the value that was provided on create. +func (u *RuntimeBrokerUpsert) UpdateMode() *RuntimeBrokerUpsert { + u.SetExcluded(runtimebroker.FieldMode) + return u +} + +// SetVersion sets the "version" field. +func (u *RuntimeBrokerUpsert) SetVersion(v string) *RuntimeBrokerUpsert { + u.Set(runtimebroker.FieldVersion, v) + return u +} + +// UpdateVersion sets the "version" field to the value that was provided on create. +func (u *RuntimeBrokerUpsert) UpdateVersion() *RuntimeBrokerUpsert { + u.SetExcluded(runtimebroker.FieldVersion) + return u +} + +// ClearVersion clears the value of the "version" field. +func (u *RuntimeBrokerUpsert) ClearVersion() *RuntimeBrokerUpsert { + u.SetNull(runtimebroker.FieldVersion) + return u +} + +// SetLockVersion sets the "lock_version" field. +func (u *RuntimeBrokerUpsert) SetLockVersion(v int64) *RuntimeBrokerUpsert { + u.Set(runtimebroker.FieldLockVersion, v) + return u +} + +// UpdateLockVersion sets the "lock_version" field to the value that was provided on create. +func (u *RuntimeBrokerUpsert) UpdateLockVersion() *RuntimeBrokerUpsert { + u.SetExcluded(runtimebroker.FieldLockVersion) + return u +} + +// AddLockVersion adds v to the "lock_version" field. +func (u *RuntimeBrokerUpsert) AddLockVersion(v int64) *RuntimeBrokerUpsert { + u.Add(runtimebroker.FieldLockVersion, v) + return u +} + +// SetStatus sets the "status" field. +func (u *RuntimeBrokerUpsert) SetStatus(v string) *RuntimeBrokerUpsert { + u.Set(runtimebroker.FieldStatus, v) + return u +} + +// UpdateStatus sets the "status" field to the value that was provided on create. +func (u *RuntimeBrokerUpsert) UpdateStatus() *RuntimeBrokerUpsert { + u.SetExcluded(runtimebroker.FieldStatus) + return u +} + +// SetConnectionState sets the "connection_state" field. +func (u *RuntimeBrokerUpsert) SetConnectionState(v string) *RuntimeBrokerUpsert { + u.Set(runtimebroker.FieldConnectionState, v) + return u +} + +// UpdateConnectionState sets the "connection_state" field to the value that was provided on create. +func (u *RuntimeBrokerUpsert) UpdateConnectionState() *RuntimeBrokerUpsert { + u.SetExcluded(runtimebroker.FieldConnectionState) + return u +} + +// SetLastHeartbeat sets the "last_heartbeat" field. +func (u *RuntimeBrokerUpsert) SetLastHeartbeat(v time.Time) *RuntimeBrokerUpsert { + u.Set(runtimebroker.FieldLastHeartbeat, v) + return u +} + +// UpdateLastHeartbeat sets the "last_heartbeat" field to the value that was provided on create. +func (u *RuntimeBrokerUpsert) UpdateLastHeartbeat() *RuntimeBrokerUpsert { + u.SetExcluded(runtimebroker.FieldLastHeartbeat) + return u +} + +// ClearLastHeartbeat clears the value of the "last_heartbeat" field. +func (u *RuntimeBrokerUpsert) ClearLastHeartbeat() *RuntimeBrokerUpsert { + u.SetNull(runtimebroker.FieldLastHeartbeat) + return u +} + +// SetCapabilities sets the "capabilities" field. +func (u *RuntimeBrokerUpsert) SetCapabilities(v string) *RuntimeBrokerUpsert { + u.Set(runtimebroker.FieldCapabilities, v) + return u +} + +// UpdateCapabilities sets the "capabilities" field to the value that was provided on create. +func (u *RuntimeBrokerUpsert) UpdateCapabilities() *RuntimeBrokerUpsert { + u.SetExcluded(runtimebroker.FieldCapabilities) + return u +} + +// ClearCapabilities clears the value of the "capabilities" field. +func (u *RuntimeBrokerUpsert) ClearCapabilities() *RuntimeBrokerUpsert { + u.SetNull(runtimebroker.FieldCapabilities) + return u +} + +// SetSupportedHarnesses sets the "supported_harnesses" field. +func (u *RuntimeBrokerUpsert) SetSupportedHarnesses(v string) *RuntimeBrokerUpsert { + u.Set(runtimebroker.FieldSupportedHarnesses, v) + return u +} + +// UpdateSupportedHarnesses sets the "supported_harnesses" field to the value that was provided on create. +func (u *RuntimeBrokerUpsert) UpdateSupportedHarnesses() *RuntimeBrokerUpsert { + u.SetExcluded(runtimebroker.FieldSupportedHarnesses) + return u +} + +// ClearSupportedHarnesses clears the value of the "supported_harnesses" field. +func (u *RuntimeBrokerUpsert) ClearSupportedHarnesses() *RuntimeBrokerUpsert { + u.SetNull(runtimebroker.FieldSupportedHarnesses) + return u +} + +// SetResources sets the "resources" field. +func (u *RuntimeBrokerUpsert) SetResources(v string) *RuntimeBrokerUpsert { + u.Set(runtimebroker.FieldResources, v) + return u +} + +// UpdateResources sets the "resources" field to the value that was provided on create. +func (u *RuntimeBrokerUpsert) UpdateResources() *RuntimeBrokerUpsert { + u.SetExcluded(runtimebroker.FieldResources) + return u +} + +// ClearResources clears the value of the "resources" field. +func (u *RuntimeBrokerUpsert) ClearResources() *RuntimeBrokerUpsert { + u.SetNull(runtimebroker.FieldResources) + return u +} + +// SetRuntimes sets the "runtimes" field. +func (u *RuntimeBrokerUpsert) SetRuntimes(v string) *RuntimeBrokerUpsert { + u.Set(runtimebroker.FieldRuntimes, v) + return u +} + +// UpdateRuntimes sets the "runtimes" field to the value that was provided on create. +func (u *RuntimeBrokerUpsert) UpdateRuntimes() *RuntimeBrokerUpsert { + u.SetExcluded(runtimebroker.FieldRuntimes) + return u +} + +// ClearRuntimes clears the value of the "runtimes" field. +func (u *RuntimeBrokerUpsert) ClearRuntimes() *RuntimeBrokerUpsert { + u.SetNull(runtimebroker.FieldRuntimes) + return u +} + +// SetLabels sets the "labels" field. +func (u *RuntimeBrokerUpsert) SetLabels(v string) *RuntimeBrokerUpsert { + u.Set(runtimebroker.FieldLabels, v) + return u +} + +// UpdateLabels sets the "labels" field to the value that was provided on create. +func (u *RuntimeBrokerUpsert) UpdateLabels() *RuntimeBrokerUpsert { + u.SetExcluded(runtimebroker.FieldLabels) + return u +} + +// ClearLabels clears the value of the "labels" field. +func (u *RuntimeBrokerUpsert) ClearLabels() *RuntimeBrokerUpsert { + u.SetNull(runtimebroker.FieldLabels) + return u +} + +// SetAnnotations sets the "annotations" field. +func (u *RuntimeBrokerUpsert) SetAnnotations(v string) *RuntimeBrokerUpsert { + u.Set(runtimebroker.FieldAnnotations, v) + return u +} + +// UpdateAnnotations sets the "annotations" field to the value that was provided on create. +func (u *RuntimeBrokerUpsert) UpdateAnnotations() *RuntimeBrokerUpsert { + u.SetExcluded(runtimebroker.FieldAnnotations) + return u +} + +// ClearAnnotations clears the value of the "annotations" field. +func (u *RuntimeBrokerUpsert) ClearAnnotations() *RuntimeBrokerUpsert { + u.SetNull(runtimebroker.FieldAnnotations) + return u +} + +// SetEndpoint sets the "endpoint" field. +func (u *RuntimeBrokerUpsert) SetEndpoint(v string) *RuntimeBrokerUpsert { + u.Set(runtimebroker.FieldEndpoint, v) + return u +} + +// UpdateEndpoint sets the "endpoint" field to the value that was provided on create. +func (u *RuntimeBrokerUpsert) UpdateEndpoint() *RuntimeBrokerUpsert { + u.SetExcluded(runtimebroker.FieldEndpoint) + return u +} + +// ClearEndpoint clears the value of the "endpoint" field. +func (u *RuntimeBrokerUpsert) ClearEndpoint() *RuntimeBrokerUpsert { + u.SetNull(runtimebroker.FieldEndpoint) + return u +} + +// SetCreatedBy sets the "created_by" field. +func (u *RuntimeBrokerUpsert) SetCreatedBy(v string) *RuntimeBrokerUpsert { + u.Set(runtimebroker.FieldCreatedBy, v) + return u +} + +// UpdateCreatedBy sets the "created_by" field to the value that was provided on create. +func (u *RuntimeBrokerUpsert) UpdateCreatedBy() *RuntimeBrokerUpsert { + u.SetExcluded(runtimebroker.FieldCreatedBy) + return u +} + +// ClearCreatedBy clears the value of the "created_by" field. +func (u *RuntimeBrokerUpsert) ClearCreatedBy() *RuntimeBrokerUpsert { + u.SetNull(runtimebroker.FieldCreatedBy) + return u +} + +// SetAutoProvide sets the "auto_provide" field. +func (u *RuntimeBrokerUpsert) SetAutoProvide(v bool) *RuntimeBrokerUpsert { + u.Set(runtimebroker.FieldAutoProvide, v) + return u +} + +// UpdateAutoProvide sets the "auto_provide" field to the value that was provided on create. +func (u *RuntimeBrokerUpsert) UpdateAutoProvide() *RuntimeBrokerUpsert { + u.SetExcluded(runtimebroker.FieldAutoProvide) + return u +} + +// SetUpdated sets the "updated" field. +func (u *RuntimeBrokerUpsert) SetUpdated(v time.Time) *RuntimeBrokerUpsert { + u.Set(runtimebroker.FieldUpdated, v) + return u +} + +// UpdateUpdated sets the "updated" field to the value that was provided on create. +func (u *RuntimeBrokerUpsert) UpdateUpdated() *RuntimeBrokerUpsert { + u.SetExcluded(runtimebroker.FieldUpdated) + return u +} + +// UpdateNewValues updates the mutable fields using the new values that were set on create except the ID field. +// Using this option is equivalent to using: +// +// client.RuntimeBroker.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(runtimebroker.FieldID) +// }), +// ). +// Exec(ctx) +func (u *RuntimeBrokerUpsertOne) UpdateNewValues() *RuntimeBrokerUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + if _, exists := u.create.mutation.ID(); exists { + s.SetIgnore(runtimebroker.FieldID) + } + if _, exists := u.create.mutation.Created(); exists { + s.SetIgnore(runtimebroker.FieldCreated) + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.RuntimeBroker.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *RuntimeBrokerUpsertOne) Ignore() *RuntimeBrokerUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *RuntimeBrokerUpsertOne) DoNothing() *RuntimeBrokerUpsertOne { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the RuntimeBrokerCreate.OnConflict +// documentation for more info. +func (u *RuntimeBrokerUpsertOne) Update(set func(*RuntimeBrokerUpsert)) *RuntimeBrokerUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&RuntimeBrokerUpsert{UpdateSet: update}) + })) + return u +} + +// SetName sets the "name" field. +func (u *RuntimeBrokerUpsertOne) SetName(v string) *RuntimeBrokerUpsertOne { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.SetName(v) + }) +} + +// UpdateName sets the "name" field to the value that was provided on create. +func (u *RuntimeBrokerUpsertOne) UpdateName() *RuntimeBrokerUpsertOne { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.UpdateName() + }) +} + +// SetSlug sets the "slug" field. +func (u *RuntimeBrokerUpsertOne) SetSlug(v string) *RuntimeBrokerUpsertOne { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.SetSlug(v) + }) +} + +// UpdateSlug sets the "slug" field to the value that was provided on create. +func (u *RuntimeBrokerUpsertOne) UpdateSlug() *RuntimeBrokerUpsertOne { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.UpdateSlug() + }) +} + +// SetType sets the "type" field. +func (u *RuntimeBrokerUpsertOne) SetType(v string) *RuntimeBrokerUpsertOne { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.SetType(v) + }) +} + +// UpdateType sets the "type" field to the value that was provided on create. +func (u *RuntimeBrokerUpsertOne) UpdateType() *RuntimeBrokerUpsertOne { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.UpdateType() + }) +} + +// ClearType clears the value of the "type" field. +func (u *RuntimeBrokerUpsertOne) ClearType() *RuntimeBrokerUpsertOne { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.ClearType() + }) +} + +// SetMode sets the "mode" field. +func (u *RuntimeBrokerUpsertOne) SetMode(v string) *RuntimeBrokerUpsertOne { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.SetMode(v) + }) +} + +// UpdateMode sets the "mode" field to the value that was provided on create. +func (u *RuntimeBrokerUpsertOne) UpdateMode() *RuntimeBrokerUpsertOne { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.UpdateMode() + }) +} + +// SetVersion sets the "version" field. +func (u *RuntimeBrokerUpsertOne) SetVersion(v string) *RuntimeBrokerUpsertOne { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.SetVersion(v) + }) +} + +// UpdateVersion sets the "version" field to the value that was provided on create. +func (u *RuntimeBrokerUpsertOne) UpdateVersion() *RuntimeBrokerUpsertOne { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.UpdateVersion() + }) +} + +// ClearVersion clears the value of the "version" field. +func (u *RuntimeBrokerUpsertOne) ClearVersion() *RuntimeBrokerUpsertOne { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.ClearVersion() + }) +} + +// SetLockVersion sets the "lock_version" field. +func (u *RuntimeBrokerUpsertOne) SetLockVersion(v int64) *RuntimeBrokerUpsertOne { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.SetLockVersion(v) + }) +} + +// AddLockVersion adds v to the "lock_version" field. +func (u *RuntimeBrokerUpsertOne) AddLockVersion(v int64) *RuntimeBrokerUpsertOne { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.AddLockVersion(v) + }) +} + +// UpdateLockVersion sets the "lock_version" field to the value that was provided on create. +func (u *RuntimeBrokerUpsertOne) UpdateLockVersion() *RuntimeBrokerUpsertOne { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.UpdateLockVersion() + }) +} + +// SetStatus sets the "status" field. +func (u *RuntimeBrokerUpsertOne) SetStatus(v string) *RuntimeBrokerUpsertOne { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.SetStatus(v) + }) +} + +// UpdateStatus sets the "status" field to the value that was provided on create. +func (u *RuntimeBrokerUpsertOne) UpdateStatus() *RuntimeBrokerUpsertOne { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.UpdateStatus() + }) +} + +// SetConnectionState sets the "connection_state" field. +func (u *RuntimeBrokerUpsertOne) SetConnectionState(v string) *RuntimeBrokerUpsertOne { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.SetConnectionState(v) + }) +} + +// UpdateConnectionState sets the "connection_state" field to the value that was provided on create. +func (u *RuntimeBrokerUpsertOne) UpdateConnectionState() *RuntimeBrokerUpsertOne { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.UpdateConnectionState() + }) +} + +// SetLastHeartbeat sets the "last_heartbeat" field. +func (u *RuntimeBrokerUpsertOne) SetLastHeartbeat(v time.Time) *RuntimeBrokerUpsertOne { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.SetLastHeartbeat(v) + }) +} + +// UpdateLastHeartbeat sets the "last_heartbeat" field to the value that was provided on create. +func (u *RuntimeBrokerUpsertOne) UpdateLastHeartbeat() *RuntimeBrokerUpsertOne { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.UpdateLastHeartbeat() + }) +} + +// ClearLastHeartbeat clears the value of the "last_heartbeat" field. +func (u *RuntimeBrokerUpsertOne) ClearLastHeartbeat() *RuntimeBrokerUpsertOne { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.ClearLastHeartbeat() + }) +} + +// SetCapabilities sets the "capabilities" field. +func (u *RuntimeBrokerUpsertOne) SetCapabilities(v string) *RuntimeBrokerUpsertOne { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.SetCapabilities(v) + }) +} + +// UpdateCapabilities sets the "capabilities" field to the value that was provided on create. +func (u *RuntimeBrokerUpsertOne) UpdateCapabilities() *RuntimeBrokerUpsertOne { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.UpdateCapabilities() + }) +} + +// ClearCapabilities clears the value of the "capabilities" field. +func (u *RuntimeBrokerUpsertOne) ClearCapabilities() *RuntimeBrokerUpsertOne { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.ClearCapabilities() + }) +} + +// SetSupportedHarnesses sets the "supported_harnesses" field. +func (u *RuntimeBrokerUpsertOne) SetSupportedHarnesses(v string) *RuntimeBrokerUpsertOne { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.SetSupportedHarnesses(v) + }) +} + +// UpdateSupportedHarnesses sets the "supported_harnesses" field to the value that was provided on create. +func (u *RuntimeBrokerUpsertOne) UpdateSupportedHarnesses() *RuntimeBrokerUpsertOne { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.UpdateSupportedHarnesses() + }) +} + +// ClearSupportedHarnesses clears the value of the "supported_harnesses" field. +func (u *RuntimeBrokerUpsertOne) ClearSupportedHarnesses() *RuntimeBrokerUpsertOne { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.ClearSupportedHarnesses() + }) +} + +// SetResources sets the "resources" field. +func (u *RuntimeBrokerUpsertOne) SetResources(v string) *RuntimeBrokerUpsertOne { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.SetResources(v) + }) +} + +// UpdateResources sets the "resources" field to the value that was provided on create. +func (u *RuntimeBrokerUpsertOne) UpdateResources() *RuntimeBrokerUpsertOne { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.UpdateResources() + }) +} + +// ClearResources clears the value of the "resources" field. +func (u *RuntimeBrokerUpsertOne) ClearResources() *RuntimeBrokerUpsertOne { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.ClearResources() + }) +} + +// SetRuntimes sets the "runtimes" field. +func (u *RuntimeBrokerUpsertOne) SetRuntimes(v string) *RuntimeBrokerUpsertOne { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.SetRuntimes(v) + }) +} + +// UpdateRuntimes sets the "runtimes" field to the value that was provided on create. +func (u *RuntimeBrokerUpsertOne) UpdateRuntimes() *RuntimeBrokerUpsertOne { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.UpdateRuntimes() + }) +} + +// ClearRuntimes clears the value of the "runtimes" field. +func (u *RuntimeBrokerUpsertOne) ClearRuntimes() *RuntimeBrokerUpsertOne { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.ClearRuntimes() + }) +} + +// SetLabels sets the "labels" field. +func (u *RuntimeBrokerUpsertOne) SetLabels(v string) *RuntimeBrokerUpsertOne { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.SetLabels(v) + }) +} + +// UpdateLabels sets the "labels" field to the value that was provided on create. +func (u *RuntimeBrokerUpsertOne) UpdateLabels() *RuntimeBrokerUpsertOne { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.UpdateLabels() + }) +} + +// ClearLabels clears the value of the "labels" field. +func (u *RuntimeBrokerUpsertOne) ClearLabels() *RuntimeBrokerUpsertOne { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.ClearLabels() + }) +} + +// SetAnnotations sets the "annotations" field. +func (u *RuntimeBrokerUpsertOne) SetAnnotations(v string) *RuntimeBrokerUpsertOne { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.SetAnnotations(v) + }) +} + +// UpdateAnnotations sets the "annotations" field to the value that was provided on create. +func (u *RuntimeBrokerUpsertOne) UpdateAnnotations() *RuntimeBrokerUpsertOne { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.UpdateAnnotations() + }) +} + +// ClearAnnotations clears the value of the "annotations" field. +func (u *RuntimeBrokerUpsertOne) ClearAnnotations() *RuntimeBrokerUpsertOne { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.ClearAnnotations() + }) +} + +// SetEndpoint sets the "endpoint" field. +func (u *RuntimeBrokerUpsertOne) SetEndpoint(v string) *RuntimeBrokerUpsertOne { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.SetEndpoint(v) + }) +} + +// UpdateEndpoint sets the "endpoint" field to the value that was provided on create. +func (u *RuntimeBrokerUpsertOne) UpdateEndpoint() *RuntimeBrokerUpsertOne { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.UpdateEndpoint() + }) +} + +// ClearEndpoint clears the value of the "endpoint" field. +func (u *RuntimeBrokerUpsertOne) ClearEndpoint() *RuntimeBrokerUpsertOne { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.ClearEndpoint() + }) +} + +// SetCreatedBy sets the "created_by" field. +func (u *RuntimeBrokerUpsertOne) SetCreatedBy(v string) *RuntimeBrokerUpsertOne { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.SetCreatedBy(v) + }) +} + +// UpdateCreatedBy sets the "created_by" field to the value that was provided on create. +func (u *RuntimeBrokerUpsertOne) UpdateCreatedBy() *RuntimeBrokerUpsertOne { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.UpdateCreatedBy() + }) +} + +// ClearCreatedBy clears the value of the "created_by" field. +func (u *RuntimeBrokerUpsertOne) ClearCreatedBy() *RuntimeBrokerUpsertOne { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.ClearCreatedBy() + }) +} + +// SetAutoProvide sets the "auto_provide" field. +func (u *RuntimeBrokerUpsertOne) SetAutoProvide(v bool) *RuntimeBrokerUpsertOne { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.SetAutoProvide(v) + }) +} + +// UpdateAutoProvide sets the "auto_provide" field to the value that was provided on create. +func (u *RuntimeBrokerUpsertOne) UpdateAutoProvide() *RuntimeBrokerUpsertOne { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.UpdateAutoProvide() + }) +} + +// SetUpdated sets the "updated" field. +func (u *RuntimeBrokerUpsertOne) SetUpdated(v time.Time) *RuntimeBrokerUpsertOne { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.SetUpdated(v) + }) +} + +// UpdateUpdated sets the "updated" field to the value that was provided on create. +func (u *RuntimeBrokerUpsertOne) UpdateUpdated() *RuntimeBrokerUpsertOne { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.UpdateUpdated() + }) +} + +// Exec executes the query. +func (u *RuntimeBrokerUpsertOne) Exec(ctx context.Context) error { + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for RuntimeBrokerCreate.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *RuntimeBrokerUpsertOne) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} + +// Exec executes the UPSERT query and returns the inserted/updated ID. +func (u *RuntimeBrokerUpsertOne) ID(ctx context.Context) (id uuid.UUID, err error) { + if u.create.driver.Dialect() == dialect.MySQL { + // In case of "ON CONFLICT", there is no way to get back non-numeric ID + // fields from the database since MySQL does not support the RETURNING clause. + return id, errors.New("ent: RuntimeBrokerUpsertOne.ID is not supported by MySQL driver. Use RuntimeBrokerUpsertOne.Exec instead") + } + node, err := u.create.Save(ctx) + if err != nil { + return id, err + } + return node.ID, nil +} + +// IDX is like ID, but panics if an error occurs. +func (u *RuntimeBrokerUpsertOne) IDX(ctx context.Context) uuid.UUID { + id, err := u.ID(ctx) + if err != nil { + panic(err) + } + return id +} + +// RuntimeBrokerCreateBulk is the builder for creating many RuntimeBroker entities in bulk. +type RuntimeBrokerCreateBulk struct { + config + err error + builders []*RuntimeBrokerCreate + conflict []sql.ConflictOption +} + +// Save creates the RuntimeBroker entities in the database. +func (_c *RuntimeBrokerCreateBulk) Save(ctx context.Context) ([]*RuntimeBroker, error) { + if _c.err != nil { + return nil, _c.err + } + specs := make([]*sqlgraph.CreateSpec, len(_c.builders)) + nodes := make([]*RuntimeBroker, len(_c.builders)) + mutators := make([]Mutator, len(_c.builders)) + for i := range _c.builders { + func(i int, root context.Context) { + builder := _c.builders[i] + builder.defaults() + var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { + mutation, ok := m.(*RuntimeBrokerMutation) + if !ok { + return nil, fmt.Errorf("unexpected mutation type %T", m) + } + if err := builder.check(); err != nil { + return nil, err + } + builder.mutation = mutation + var err error + nodes[i], specs[i] = builder.createSpec() + if i < len(mutators)-1 { + _, err = mutators[i+1].Mutate(root, _c.builders[i+1].mutation) + } else { + spec := &sqlgraph.BatchCreateSpec{Nodes: specs} + spec.OnConflict = _c.conflict + // Invoke the actual operation on the latest mutation in the chain. + if err = sqlgraph.BatchCreate(ctx, _c.driver, spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + } + } + if err != nil { + return nil, err + } + mutation.id = &nodes[i].ID + mutation.done = true + return nodes[i], nil + }) + for i := len(builder.hooks) - 1; i >= 0; i-- { + mut = builder.hooks[i](mut) + } + mutators[i] = mut + }(i, ctx) + } + if len(mutators) > 0 { + if _, err := mutators[0].Mutate(ctx, _c.builders[0].mutation); err != nil { + return nil, err + } + } + return nodes, nil +} + +// SaveX is like Save, but panics if an error occurs. +func (_c *RuntimeBrokerCreateBulk) SaveX(ctx context.Context) []*RuntimeBroker { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *RuntimeBrokerCreateBulk) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *RuntimeBrokerCreateBulk) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.RuntimeBroker.CreateBulk(builders...). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.RuntimeBrokerUpsert) { +// SetName(v+v). +// }). +// Exec(ctx) +func (_c *RuntimeBrokerCreateBulk) OnConflict(opts ...sql.ConflictOption) *RuntimeBrokerUpsertBulk { + _c.conflict = opts + return &RuntimeBrokerUpsertBulk{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.RuntimeBroker.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *RuntimeBrokerCreateBulk) OnConflictColumns(columns ...string) *RuntimeBrokerUpsertBulk { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &RuntimeBrokerUpsertBulk{ + create: _c, + } +} + +// RuntimeBrokerUpsertBulk is the builder for "upsert"-ing +// a bulk of RuntimeBroker nodes. +type RuntimeBrokerUpsertBulk struct { + create *RuntimeBrokerCreateBulk +} + +// UpdateNewValues updates the mutable fields using the new values that +// were set on create. Using this option is equivalent to using: +// +// client.RuntimeBroker.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(runtimebroker.FieldID) +// }), +// ). +// Exec(ctx) +func (u *RuntimeBrokerUpsertBulk) UpdateNewValues() *RuntimeBrokerUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + for _, b := range u.create.builders { + if _, exists := b.mutation.ID(); exists { + s.SetIgnore(runtimebroker.FieldID) + } + if _, exists := b.mutation.Created(); exists { + s.SetIgnore(runtimebroker.FieldCreated) + } + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.RuntimeBroker.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *RuntimeBrokerUpsertBulk) Ignore() *RuntimeBrokerUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *RuntimeBrokerUpsertBulk) DoNothing() *RuntimeBrokerUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the RuntimeBrokerCreateBulk.OnConflict +// documentation for more info. +func (u *RuntimeBrokerUpsertBulk) Update(set func(*RuntimeBrokerUpsert)) *RuntimeBrokerUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&RuntimeBrokerUpsert{UpdateSet: update}) + })) + return u +} + +// SetName sets the "name" field. +func (u *RuntimeBrokerUpsertBulk) SetName(v string) *RuntimeBrokerUpsertBulk { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.SetName(v) + }) +} + +// UpdateName sets the "name" field to the value that was provided on create. +func (u *RuntimeBrokerUpsertBulk) UpdateName() *RuntimeBrokerUpsertBulk { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.UpdateName() + }) +} + +// SetSlug sets the "slug" field. +func (u *RuntimeBrokerUpsertBulk) SetSlug(v string) *RuntimeBrokerUpsertBulk { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.SetSlug(v) + }) +} + +// UpdateSlug sets the "slug" field to the value that was provided on create. +func (u *RuntimeBrokerUpsertBulk) UpdateSlug() *RuntimeBrokerUpsertBulk { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.UpdateSlug() + }) +} + +// SetType sets the "type" field. +func (u *RuntimeBrokerUpsertBulk) SetType(v string) *RuntimeBrokerUpsertBulk { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.SetType(v) + }) +} + +// UpdateType sets the "type" field to the value that was provided on create. +func (u *RuntimeBrokerUpsertBulk) UpdateType() *RuntimeBrokerUpsertBulk { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.UpdateType() + }) +} + +// ClearType clears the value of the "type" field. +func (u *RuntimeBrokerUpsertBulk) ClearType() *RuntimeBrokerUpsertBulk { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.ClearType() + }) +} + +// SetMode sets the "mode" field. +func (u *RuntimeBrokerUpsertBulk) SetMode(v string) *RuntimeBrokerUpsertBulk { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.SetMode(v) + }) +} + +// UpdateMode sets the "mode" field to the value that was provided on create. +func (u *RuntimeBrokerUpsertBulk) UpdateMode() *RuntimeBrokerUpsertBulk { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.UpdateMode() + }) +} + +// SetVersion sets the "version" field. +func (u *RuntimeBrokerUpsertBulk) SetVersion(v string) *RuntimeBrokerUpsertBulk { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.SetVersion(v) + }) +} + +// UpdateVersion sets the "version" field to the value that was provided on create. +func (u *RuntimeBrokerUpsertBulk) UpdateVersion() *RuntimeBrokerUpsertBulk { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.UpdateVersion() + }) +} + +// ClearVersion clears the value of the "version" field. +func (u *RuntimeBrokerUpsertBulk) ClearVersion() *RuntimeBrokerUpsertBulk { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.ClearVersion() + }) +} + +// SetLockVersion sets the "lock_version" field. +func (u *RuntimeBrokerUpsertBulk) SetLockVersion(v int64) *RuntimeBrokerUpsertBulk { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.SetLockVersion(v) + }) +} + +// AddLockVersion adds v to the "lock_version" field. +func (u *RuntimeBrokerUpsertBulk) AddLockVersion(v int64) *RuntimeBrokerUpsertBulk { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.AddLockVersion(v) + }) +} + +// UpdateLockVersion sets the "lock_version" field to the value that was provided on create. +func (u *RuntimeBrokerUpsertBulk) UpdateLockVersion() *RuntimeBrokerUpsertBulk { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.UpdateLockVersion() + }) +} + +// SetStatus sets the "status" field. +func (u *RuntimeBrokerUpsertBulk) SetStatus(v string) *RuntimeBrokerUpsertBulk { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.SetStatus(v) + }) +} + +// UpdateStatus sets the "status" field to the value that was provided on create. +func (u *RuntimeBrokerUpsertBulk) UpdateStatus() *RuntimeBrokerUpsertBulk { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.UpdateStatus() + }) +} + +// SetConnectionState sets the "connection_state" field. +func (u *RuntimeBrokerUpsertBulk) SetConnectionState(v string) *RuntimeBrokerUpsertBulk { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.SetConnectionState(v) + }) +} + +// UpdateConnectionState sets the "connection_state" field to the value that was provided on create. +func (u *RuntimeBrokerUpsertBulk) UpdateConnectionState() *RuntimeBrokerUpsertBulk { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.UpdateConnectionState() + }) +} + +// SetLastHeartbeat sets the "last_heartbeat" field. +func (u *RuntimeBrokerUpsertBulk) SetLastHeartbeat(v time.Time) *RuntimeBrokerUpsertBulk { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.SetLastHeartbeat(v) + }) +} + +// UpdateLastHeartbeat sets the "last_heartbeat" field to the value that was provided on create. +func (u *RuntimeBrokerUpsertBulk) UpdateLastHeartbeat() *RuntimeBrokerUpsertBulk { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.UpdateLastHeartbeat() + }) +} + +// ClearLastHeartbeat clears the value of the "last_heartbeat" field. +func (u *RuntimeBrokerUpsertBulk) ClearLastHeartbeat() *RuntimeBrokerUpsertBulk { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.ClearLastHeartbeat() + }) +} + +// SetCapabilities sets the "capabilities" field. +func (u *RuntimeBrokerUpsertBulk) SetCapabilities(v string) *RuntimeBrokerUpsertBulk { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.SetCapabilities(v) + }) +} + +// UpdateCapabilities sets the "capabilities" field to the value that was provided on create. +func (u *RuntimeBrokerUpsertBulk) UpdateCapabilities() *RuntimeBrokerUpsertBulk { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.UpdateCapabilities() + }) +} + +// ClearCapabilities clears the value of the "capabilities" field. +func (u *RuntimeBrokerUpsertBulk) ClearCapabilities() *RuntimeBrokerUpsertBulk { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.ClearCapabilities() + }) +} + +// SetSupportedHarnesses sets the "supported_harnesses" field. +func (u *RuntimeBrokerUpsertBulk) SetSupportedHarnesses(v string) *RuntimeBrokerUpsertBulk { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.SetSupportedHarnesses(v) + }) +} + +// UpdateSupportedHarnesses sets the "supported_harnesses" field to the value that was provided on create. +func (u *RuntimeBrokerUpsertBulk) UpdateSupportedHarnesses() *RuntimeBrokerUpsertBulk { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.UpdateSupportedHarnesses() + }) +} + +// ClearSupportedHarnesses clears the value of the "supported_harnesses" field. +func (u *RuntimeBrokerUpsertBulk) ClearSupportedHarnesses() *RuntimeBrokerUpsertBulk { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.ClearSupportedHarnesses() + }) +} + +// SetResources sets the "resources" field. +func (u *RuntimeBrokerUpsertBulk) SetResources(v string) *RuntimeBrokerUpsertBulk { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.SetResources(v) + }) +} + +// UpdateResources sets the "resources" field to the value that was provided on create. +func (u *RuntimeBrokerUpsertBulk) UpdateResources() *RuntimeBrokerUpsertBulk { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.UpdateResources() + }) +} + +// ClearResources clears the value of the "resources" field. +func (u *RuntimeBrokerUpsertBulk) ClearResources() *RuntimeBrokerUpsertBulk { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.ClearResources() + }) +} + +// SetRuntimes sets the "runtimes" field. +func (u *RuntimeBrokerUpsertBulk) SetRuntimes(v string) *RuntimeBrokerUpsertBulk { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.SetRuntimes(v) + }) +} + +// UpdateRuntimes sets the "runtimes" field to the value that was provided on create. +func (u *RuntimeBrokerUpsertBulk) UpdateRuntimes() *RuntimeBrokerUpsertBulk { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.UpdateRuntimes() + }) +} + +// ClearRuntimes clears the value of the "runtimes" field. +func (u *RuntimeBrokerUpsertBulk) ClearRuntimes() *RuntimeBrokerUpsertBulk { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.ClearRuntimes() + }) +} + +// SetLabels sets the "labels" field. +func (u *RuntimeBrokerUpsertBulk) SetLabels(v string) *RuntimeBrokerUpsertBulk { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.SetLabels(v) + }) +} + +// UpdateLabels sets the "labels" field to the value that was provided on create. +func (u *RuntimeBrokerUpsertBulk) UpdateLabels() *RuntimeBrokerUpsertBulk { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.UpdateLabels() + }) +} + +// ClearLabels clears the value of the "labels" field. +func (u *RuntimeBrokerUpsertBulk) ClearLabels() *RuntimeBrokerUpsertBulk { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.ClearLabels() + }) +} + +// SetAnnotations sets the "annotations" field. +func (u *RuntimeBrokerUpsertBulk) SetAnnotations(v string) *RuntimeBrokerUpsertBulk { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.SetAnnotations(v) + }) +} + +// UpdateAnnotations sets the "annotations" field to the value that was provided on create. +func (u *RuntimeBrokerUpsertBulk) UpdateAnnotations() *RuntimeBrokerUpsertBulk { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.UpdateAnnotations() + }) +} + +// ClearAnnotations clears the value of the "annotations" field. +func (u *RuntimeBrokerUpsertBulk) ClearAnnotations() *RuntimeBrokerUpsertBulk { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.ClearAnnotations() + }) +} + +// SetEndpoint sets the "endpoint" field. +func (u *RuntimeBrokerUpsertBulk) SetEndpoint(v string) *RuntimeBrokerUpsertBulk { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.SetEndpoint(v) + }) +} + +// UpdateEndpoint sets the "endpoint" field to the value that was provided on create. +func (u *RuntimeBrokerUpsertBulk) UpdateEndpoint() *RuntimeBrokerUpsertBulk { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.UpdateEndpoint() + }) +} + +// ClearEndpoint clears the value of the "endpoint" field. +func (u *RuntimeBrokerUpsertBulk) ClearEndpoint() *RuntimeBrokerUpsertBulk { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.ClearEndpoint() + }) +} + +// SetCreatedBy sets the "created_by" field. +func (u *RuntimeBrokerUpsertBulk) SetCreatedBy(v string) *RuntimeBrokerUpsertBulk { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.SetCreatedBy(v) + }) +} + +// UpdateCreatedBy sets the "created_by" field to the value that was provided on create. +func (u *RuntimeBrokerUpsertBulk) UpdateCreatedBy() *RuntimeBrokerUpsertBulk { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.UpdateCreatedBy() + }) +} + +// ClearCreatedBy clears the value of the "created_by" field. +func (u *RuntimeBrokerUpsertBulk) ClearCreatedBy() *RuntimeBrokerUpsertBulk { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.ClearCreatedBy() + }) +} + +// SetAutoProvide sets the "auto_provide" field. +func (u *RuntimeBrokerUpsertBulk) SetAutoProvide(v bool) *RuntimeBrokerUpsertBulk { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.SetAutoProvide(v) + }) +} + +// UpdateAutoProvide sets the "auto_provide" field to the value that was provided on create. +func (u *RuntimeBrokerUpsertBulk) UpdateAutoProvide() *RuntimeBrokerUpsertBulk { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.UpdateAutoProvide() + }) +} + +// SetUpdated sets the "updated" field. +func (u *RuntimeBrokerUpsertBulk) SetUpdated(v time.Time) *RuntimeBrokerUpsertBulk { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.SetUpdated(v) + }) +} + +// UpdateUpdated sets the "updated" field to the value that was provided on create. +func (u *RuntimeBrokerUpsertBulk) UpdateUpdated() *RuntimeBrokerUpsertBulk { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.UpdateUpdated() + }) +} + +// Exec executes the query. +func (u *RuntimeBrokerUpsertBulk) Exec(ctx context.Context) error { + if u.create.err != nil { + return u.create.err + } + for i, b := range u.create.builders { + if len(b.conflict) != 0 { + return fmt.Errorf("ent: OnConflict was set for builder %d. Set it on the RuntimeBrokerCreateBulk instead", i) + } + } + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for RuntimeBrokerCreateBulk.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *RuntimeBrokerUpsertBulk) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/pkg/ent/runtimebroker_delete.go b/pkg/ent/runtimebroker_delete.go new file mode 100644 index 000000000..5023bbb93 --- /dev/null +++ b/pkg/ent/runtimebroker_delete.go @@ -0,0 +1,88 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/GoogleCloudPlatform/scion/pkg/ent/runtimebroker" +) + +// RuntimeBrokerDelete is the builder for deleting a RuntimeBroker entity. +type RuntimeBrokerDelete struct { + config + hooks []Hook + mutation *RuntimeBrokerMutation +} + +// Where appends a list predicates to the RuntimeBrokerDelete builder. +func (_d *RuntimeBrokerDelete) Where(ps ...predicate.RuntimeBroker) *RuntimeBrokerDelete { + _d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (_d *RuntimeBrokerDelete) Exec(ctx context.Context) (int, error) { + return withHooks(ctx, _d.sqlExec, _d.mutation, _d.hooks) +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *RuntimeBrokerDelete) ExecX(ctx context.Context) int { + n, err := _d.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (_d *RuntimeBrokerDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(runtimebroker.Table, sqlgraph.NewFieldSpec(runtimebroker.FieldID, field.TypeUUID)) + if ps := _d.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + affected, err := sqlgraph.DeleteNodes(ctx, _d.driver, _spec) + if err != nil && sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + _d.mutation.done = true + return affected, err +} + +// RuntimeBrokerDeleteOne is the builder for deleting a single RuntimeBroker entity. +type RuntimeBrokerDeleteOne struct { + _d *RuntimeBrokerDelete +} + +// Where appends a list predicates to the RuntimeBrokerDelete builder. +func (_d *RuntimeBrokerDeleteOne) Where(ps ...predicate.RuntimeBroker) *RuntimeBrokerDeleteOne { + _d._d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query. +func (_d *RuntimeBrokerDeleteOne) Exec(ctx context.Context) error { + n, err := _d._d.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{runtimebroker.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *RuntimeBrokerDeleteOne) ExecX(ctx context.Context) { + if err := _d.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/pkg/ent/runtimebroker_query.go b/pkg/ent/runtimebroker_query.go new file mode 100644 index 000000000..ae30aa2e7 --- /dev/null +++ b/pkg/ent/runtimebroker_query.go @@ -0,0 +1,565 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "fmt" + "math" + + "entgo.io/ent" + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/GoogleCloudPlatform/scion/pkg/ent/runtimebroker" + "github.com/google/uuid" +) + +// RuntimeBrokerQuery is the builder for querying RuntimeBroker entities. +type RuntimeBrokerQuery struct { + config + ctx *QueryContext + order []runtimebroker.OrderOption + inters []Interceptor + predicates []predicate.RuntimeBroker + modifiers []func(*sql.Selector) + // intermediate query (i.e. traversal path). + sql *sql.Selector + path func(context.Context) (*sql.Selector, error) +} + +// Where adds a new predicate for the RuntimeBrokerQuery builder. +func (_q *RuntimeBrokerQuery) Where(ps ...predicate.RuntimeBroker) *RuntimeBrokerQuery { + _q.predicates = append(_q.predicates, ps...) + return _q +} + +// Limit the number of records to be returned by this query. +func (_q *RuntimeBrokerQuery) Limit(limit int) *RuntimeBrokerQuery { + _q.ctx.Limit = &limit + return _q +} + +// Offset to start from. +func (_q *RuntimeBrokerQuery) Offset(offset int) *RuntimeBrokerQuery { + _q.ctx.Offset = &offset + return _q +} + +// Unique configures the query builder to filter duplicate records on query. +// By default, unique is set to true, and can be disabled using this method. +func (_q *RuntimeBrokerQuery) Unique(unique bool) *RuntimeBrokerQuery { + _q.ctx.Unique = &unique + return _q +} + +// Order specifies how the records should be ordered. +func (_q *RuntimeBrokerQuery) Order(o ...runtimebroker.OrderOption) *RuntimeBrokerQuery { + _q.order = append(_q.order, o...) + return _q +} + +// First returns the first RuntimeBroker entity from the query. +// Returns a *NotFoundError when no RuntimeBroker was found. +func (_q *RuntimeBrokerQuery) First(ctx context.Context) (*RuntimeBroker, error) { + nodes, err := _q.Limit(1).All(setContextOp(ctx, _q.ctx, ent.OpQueryFirst)) + if err != nil { + return nil, err + } + if len(nodes) == 0 { + return nil, &NotFoundError{runtimebroker.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (_q *RuntimeBrokerQuery) FirstX(ctx context.Context) *RuntimeBroker { + node, err := _q.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// FirstID returns the first RuntimeBroker ID from the query. +// Returns a *NotFoundError when no RuntimeBroker ID was found. +func (_q *RuntimeBrokerQuery) FirstID(ctx context.Context) (id uuid.UUID, err error) { + var ids []uuid.UUID + if ids, err = _q.Limit(1).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryFirstID)); err != nil { + return + } + if len(ids) == 0 { + err = &NotFoundError{runtimebroker.Label} + return + } + return ids[0], nil +} + +// FirstIDX is like FirstID, but panics if an error occurs. +func (_q *RuntimeBrokerQuery) FirstIDX(ctx context.Context) uuid.UUID { + id, err := _q.FirstID(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return id +} + +// Only returns a single RuntimeBroker entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one RuntimeBroker entity is found. +// Returns a *NotFoundError when no RuntimeBroker entities are found. +func (_q *RuntimeBrokerQuery) Only(ctx context.Context) (*RuntimeBroker, error) { + nodes, err := _q.Limit(2).All(setContextOp(ctx, _q.ctx, ent.OpQueryOnly)) + if err != nil { + return nil, err + } + switch len(nodes) { + case 1: + return nodes[0], nil + case 0: + return nil, &NotFoundError{runtimebroker.Label} + default: + return nil, &NotSingularError{runtimebroker.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (_q *RuntimeBrokerQuery) OnlyX(ctx context.Context) *RuntimeBroker { + node, err := _q.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// OnlyID is like Only, but returns the only RuntimeBroker ID in the query. +// Returns a *NotSingularError when more than one RuntimeBroker ID is found. +// Returns a *NotFoundError when no entities are found. +func (_q *RuntimeBrokerQuery) OnlyID(ctx context.Context) (id uuid.UUID, err error) { + var ids []uuid.UUID + if ids, err = _q.Limit(2).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryOnlyID)); err != nil { + return + } + switch len(ids) { + case 1: + id = ids[0] + case 0: + err = &NotFoundError{runtimebroker.Label} + default: + err = &NotSingularError{runtimebroker.Label} + } + return +} + +// OnlyIDX is like OnlyID, but panics if an error occurs. +func (_q *RuntimeBrokerQuery) OnlyIDX(ctx context.Context) uuid.UUID { + id, err := _q.OnlyID(ctx) + if err != nil { + panic(err) + } + return id +} + +// All executes the query and returns a list of RuntimeBrokers. +func (_q *RuntimeBrokerQuery) All(ctx context.Context) ([]*RuntimeBroker, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryAll) + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*RuntimeBroker, *RuntimeBrokerQuery]() + return withInterceptors[[]*RuntimeBroker](ctx, _q, qr, _q.inters) +} + +// AllX is like All, but panics if an error occurs. +func (_q *RuntimeBrokerQuery) AllX(ctx context.Context) []*RuntimeBroker { + nodes, err := _q.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// IDs executes the query and returns a list of RuntimeBroker IDs. +func (_q *RuntimeBrokerQuery) IDs(ctx context.Context) (ids []uuid.UUID, err error) { + if _q.ctx.Unique == nil && _q.path != nil { + _q.Unique(true) + } + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryIDs) + if err = _q.Select(runtimebroker.FieldID).Scan(ctx, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// IDsX is like IDs, but panics if an error occurs. +func (_q *RuntimeBrokerQuery) IDsX(ctx context.Context) []uuid.UUID { + ids, err := _q.IDs(ctx) + if err != nil { + panic(err) + } + return ids +} + +// Count returns the count of the given query. +func (_q *RuntimeBrokerQuery) Count(ctx context.Context) (int, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryCount) + if err := _q.prepareQuery(ctx); err != nil { + return 0, err + } + return withInterceptors[int](ctx, _q, querierCount[*RuntimeBrokerQuery](), _q.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (_q *RuntimeBrokerQuery) CountX(ctx context.Context) int { + count, err := _q.Count(ctx) + if err != nil { + panic(err) + } + return count +} + +// Exist returns true if the query has elements in the graph. +func (_q *RuntimeBrokerQuery) Exist(ctx context.Context) (bool, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryExist) + switch _, err := _q.FirstID(ctx); { + case IsNotFound(err): + return false, nil + case err != nil: + return false, fmt.Errorf("ent: check existence: %w", err) + default: + return true, nil + } +} + +// ExistX is like Exist, but panics if an error occurs. +func (_q *RuntimeBrokerQuery) ExistX(ctx context.Context) bool { + exist, err := _q.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the RuntimeBrokerQuery builder, including all associated steps. It can be +// used to prepare common query builders and use them differently after the clone is made. +func (_q *RuntimeBrokerQuery) Clone() *RuntimeBrokerQuery { + if _q == nil { + return nil + } + return &RuntimeBrokerQuery{ + config: _q.config, + ctx: _q.ctx.Clone(), + order: append([]runtimebroker.OrderOption{}, _q.order...), + inters: append([]Interceptor{}, _q.inters...), + predicates: append([]predicate.RuntimeBroker{}, _q.predicates...), + // clone intermediate query. + sql: _q.sql.Clone(), + path: _q.path, + } +} + +// GroupBy is used to group vertices by one or more fields/columns. +// It is often used with aggregate functions, like: count, max, mean, min, sum. +// +// Example: +// +// var v []struct { +// Name string `json:"name,omitempty"` +// Count int `json:"count,omitempty"` +// } +// +// client.RuntimeBroker.Query(). +// GroupBy(runtimebroker.FieldName). +// Aggregate(ent.Count()). +// Scan(ctx, &v) +func (_q *RuntimeBrokerQuery) GroupBy(field string, fields ...string) *RuntimeBrokerGroupBy { + _q.ctx.Fields = append([]string{field}, fields...) + grbuild := &RuntimeBrokerGroupBy{build: _q} + grbuild.flds = &_q.ctx.Fields + grbuild.label = runtimebroker.Label + grbuild.scan = grbuild.Scan + return grbuild +} + +// Select allows the selection one or more fields/columns for the given query, +// instead of selecting all fields in the entity. +// +// Example: +// +// var v []struct { +// Name string `json:"name,omitempty"` +// } +// +// client.RuntimeBroker.Query(). +// Select(runtimebroker.FieldName). +// Scan(ctx, &v) +func (_q *RuntimeBrokerQuery) Select(fields ...string) *RuntimeBrokerSelect { + _q.ctx.Fields = append(_q.ctx.Fields, fields...) + sbuild := &RuntimeBrokerSelect{RuntimeBrokerQuery: _q} + sbuild.label = runtimebroker.Label + sbuild.flds, sbuild.scan = &_q.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a RuntimeBrokerSelect configured with the given aggregations. +func (_q *RuntimeBrokerQuery) Aggregate(fns ...AggregateFunc) *RuntimeBrokerSelect { + return _q.Select().Aggregate(fns...) +} + +func (_q *RuntimeBrokerQuery) prepareQuery(ctx context.Context) error { + for _, inter := range _q.inters { + if inter == nil { + return fmt.Errorf("ent: uninitialized interceptor (forgotten import ent/runtime?)") + } + if trv, ok := inter.(Traverser); ok { + if err := trv.Traverse(ctx, _q); err != nil { + return err + } + } + } + for _, f := range _q.ctx.Fields { + if !runtimebroker.ValidColumn(f) { + return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + } + if _q.path != nil { + prev, err := _q.path(ctx) + if err != nil { + return err + } + _q.sql = prev + } + return nil +} + +func (_q *RuntimeBrokerQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*RuntimeBroker, error) { + var ( + nodes = []*RuntimeBroker{} + _spec = _q.querySpec() + ) + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*RuntimeBroker).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &RuntimeBroker{config: _q.config} + nodes = append(nodes, node) + return node.assignValues(columns, values) + } + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } + for i := range hooks { + hooks[i](ctx, _spec) + } + if err := sqlgraph.QueryNodes(ctx, _q.driver, _spec); err != nil { + return nil, err + } + if len(nodes) == 0 { + return nodes, nil + } + return nodes, nil +} + +func (_q *RuntimeBrokerQuery) sqlCount(ctx context.Context) (int, error) { + _spec := _q.querySpec() + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } + _spec.Node.Columns = _q.ctx.Fields + if len(_q.ctx.Fields) > 0 { + _spec.Unique = _q.ctx.Unique != nil && *_q.ctx.Unique + } + return sqlgraph.CountNodes(ctx, _q.driver, _spec) +} + +func (_q *RuntimeBrokerQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(runtimebroker.Table, runtimebroker.Columns, sqlgraph.NewFieldSpec(runtimebroker.FieldID, field.TypeUUID)) + _spec.From = _q.sql + if unique := _q.ctx.Unique; unique != nil { + _spec.Unique = *unique + } else if _q.path != nil { + _spec.Unique = true + } + if fields := _q.ctx.Fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, runtimebroker.FieldID) + for i := range fields { + if fields[i] != runtimebroker.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, fields[i]) + } + } + } + if ps := _q.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if limit := _q.ctx.Limit; limit != nil { + _spec.Limit = *limit + } + if offset := _q.ctx.Offset; offset != nil { + _spec.Offset = *offset + } + if ps := _q.order; len(ps) > 0 { + _spec.Order = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + return _spec +} + +func (_q *RuntimeBrokerQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(_q.driver.Dialect()) + t1 := builder.Table(runtimebroker.Table) + columns := _q.ctx.Fields + if len(columns) == 0 { + columns = runtimebroker.Columns + } + selector := builder.Select(t1.Columns(columns...)...).From(t1) + if _q.sql != nil { + selector = _q.sql + selector.Select(selector.Columns(columns...)...) + } + if _q.ctx.Unique != nil && *_q.ctx.Unique { + selector.Distinct() + } + for _, m := range _q.modifiers { + m(selector) + } + for _, p := range _q.predicates { + p(selector) + } + for _, p := range _q.order { + p(selector) + } + if offset := _q.ctx.Offset; offset != nil { + // limit is mandatory for offset clause. We start + // with default value, and override it below if needed. + selector.Offset(*offset).Limit(math.MaxInt32) + } + if limit := _q.ctx.Limit; limit != nil { + selector.Limit(*limit) + } + return selector +} + +// ForUpdate locks the selected rows against concurrent updates, and prevent them from being +// updated, deleted or "selected ... for update" by other sessions, until the transaction is +// either committed or rolled-back. +func (_q *RuntimeBrokerQuery) ForUpdate(opts ...sql.LockOption) *RuntimeBrokerQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForUpdate(opts...) + }) + return _q +} + +// ForShare behaves similarly to ForUpdate, except that it acquires a shared mode lock +// on any rows that are read. Other sessions can read the rows, but cannot modify them +// until your transaction commits. +func (_q *RuntimeBrokerQuery) ForShare(opts ...sql.LockOption) *RuntimeBrokerQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForShare(opts...) + }) + return _q +} + +// RuntimeBrokerGroupBy is the group-by builder for RuntimeBroker entities. +type RuntimeBrokerGroupBy struct { + selector + build *RuntimeBrokerQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (_g *RuntimeBrokerGroupBy) Aggregate(fns ...AggregateFunc) *RuntimeBrokerGroupBy { + _g.fns = append(_g.fns, fns...) + return _g +} + +// Scan applies the selector query and scans the result into the given value. +func (_g *RuntimeBrokerGroupBy) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, _g.build.ctx, ent.OpQueryGroupBy) + if err := _g.build.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*RuntimeBrokerQuery, *RuntimeBrokerGroupBy](ctx, _g.build, _g, _g.build.inters, v) +} + +func (_g *RuntimeBrokerGroupBy) sqlScan(ctx context.Context, root *RuntimeBrokerQuery, v any) error { + selector := root.sqlQuery(ctx).Select() + aggregation := make([]string, 0, len(_g.fns)) + for _, fn := range _g.fns { + aggregation = append(aggregation, fn(selector)) + } + if len(selector.SelectedColumns()) == 0 { + columns := make([]string, 0, len(*_g.flds)+len(_g.fns)) + for _, f := range *_g.flds { + columns = append(columns, selector.C(f)) + } + columns = append(columns, aggregation...) + selector.Select(columns...) + } + selector.GroupBy(selector.Columns(*_g.flds...)...) + if err := selector.Err(); err != nil { + return err + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := _g.build.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} + +// RuntimeBrokerSelect is the builder for selecting fields of RuntimeBroker entities. +type RuntimeBrokerSelect struct { + *RuntimeBrokerQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (_s *RuntimeBrokerSelect) Aggregate(fns ...AggregateFunc) *RuntimeBrokerSelect { + _s.fns = append(_s.fns, fns...) + return _s +} + +// Scan applies the selector query and scans the result into the given value. +func (_s *RuntimeBrokerSelect) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, _s.ctx, ent.OpQuerySelect) + if err := _s.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*RuntimeBrokerQuery, *RuntimeBrokerSelect](ctx, _s.RuntimeBrokerQuery, _s, _s.inters, v) +} + +func (_s *RuntimeBrokerSelect) sqlScan(ctx context.Context, root *RuntimeBrokerQuery, v any) error { + selector := root.sqlQuery(ctx) + aggregation := make([]string, 0, len(_s.fns)) + for _, fn := range _s.fns { + aggregation = append(aggregation, fn(selector)) + } + switch n := len(*_s.selector.flds); { + case n == 0 && len(aggregation) > 0: + selector.Select(aggregation...) + case n != 0 && len(aggregation) > 0: + selector.AppendSelect(aggregation...) + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := _s.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} diff --git a/pkg/ent/runtimebroker_update.go b/pkg/ent/runtimebroker_update.go new file mode 100644 index 000000000..680b13fa0 --- /dev/null +++ b/pkg/ent/runtimebroker_update.go @@ -0,0 +1,1078 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/GoogleCloudPlatform/scion/pkg/ent/runtimebroker" +) + +// RuntimeBrokerUpdate is the builder for updating RuntimeBroker entities. +type RuntimeBrokerUpdate struct { + config + hooks []Hook + mutation *RuntimeBrokerMutation +} + +// Where appends a list predicates to the RuntimeBrokerUpdate builder. +func (_u *RuntimeBrokerUpdate) Where(ps ...predicate.RuntimeBroker) *RuntimeBrokerUpdate { + _u.mutation.Where(ps...) + return _u +} + +// SetName sets the "name" field. +func (_u *RuntimeBrokerUpdate) SetName(v string) *RuntimeBrokerUpdate { + _u.mutation.SetName(v) + return _u +} + +// SetNillableName sets the "name" field if the given value is not nil. +func (_u *RuntimeBrokerUpdate) SetNillableName(v *string) *RuntimeBrokerUpdate { + if v != nil { + _u.SetName(*v) + } + return _u +} + +// SetSlug sets the "slug" field. +func (_u *RuntimeBrokerUpdate) SetSlug(v string) *RuntimeBrokerUpdate { + _u.mutation.SetSlug(v) + return _u +} + +// SetNillableSlug sets the "slug" field if the given value is not nil. +func (_u *RuntimeBrokerUpdate) SetNillableSlug(v *string) *RuntimeBrokerUpdate { + if v != nil { + _u.SetSlug(*v) + } + return _u +} + +// SetType sets the "type" field. +func (_u *RuntimeBrokerUpdate) SetType(v string) *RuntimeBrokerUpdate { + _u.mutation.SetType(v) + return _u +} + +// SetNillableType sets the "type" field if the given value is not nil. +func (_u *RuntimeBrokerUpdate) SetNillableType(v *string) *RuntimeBrokerUpdate { + if v != nil { + _u.SetType(*v) + } + return _u +} + +// ClearType clears the value of the "type" field. +func (_u *RuntimeBrokerUpdate) ClearType() *RuntimeBrokerUpdate { + _u.mutation.ClearType() + return _u +} + +// SetMode sets the "mode" field. +func (_u *RuntimeBrokerUpdate) SetMode(v string) *RuntimeBrokerUpdate { + _u.mutation.SetMode(v) + return _u +} + +// SetNillableMode sets the "mode" field if the given value is not nil. +func (_u *RuntimeBrokerUpdate) SetNillableMode(v *string) *RuntimeBrokerUpdate { + if v != nil { + _u.SetMode(*v) + } + return _u +} + +// SetVersion sets the "version" field. +func (_u *RuntimeBrokerUpdate) SetVersion(v string) *RuntimeBrokerUpdate { + _u.mutation.SetVersion(v) + return _u +} + +// SetNillableVersion sets the "version" field if the given value is not nil. +func (_u *RuntimeBrokerUpdate) SetNillableVersion(v *string) *RuntimeBrokerUpdate { + if v != nil { + _u.SetVersion(*v) + } + return _u +} + +// ClearVersion clears the value of the "version" field. +func (_u *RuntimeBrokerUpdate) ClearVersion() *RuntimeBrokerUpdate { + _u.mutation.ClearVersion() + return _u +} + +// SetLockVersion sets the "lock_version" field. +func (_u *RuntimeBrokerUpdate) SetLockVersion(v int64) *RuntimeBrokerUpdate { + _u.mutation.ResetLockVersion() + _u.mutation.SetLockVersion(v) + return _u +} + +// SetNillableLockVersion sets the "lock_version" field if the given value is not nil. +func (_u *RuntimeBrokerUpdate) SetNillableLockVersion(v *int64) *RuntimeBrokerUpdate { + if v != nil { + _u.SetLockVersion(*v) + } + return _u +} + +// AddLockVersion adds value to the "lock_version" field. +func (_u *RuntimeBrokerUpdate) AddLockVersion(v int64) *RuntimeBrokerUpdate { + _u.mutation.AddLockVersion(v) + return _u +} + +// SetStatus sets the "status" field. +func (_u *RuntimeBrokerUpdate) SetStatus(v string) *RuntimeBrokerUpdate { + _u.mutation.SetStatus(v) + return _u +} + +// SetNillableStatus sets the "status" field if the given value is not nil. +func (_u *RuntimeBrokerUpdate) SetNillableStatus(v *string) *RuntimeBrokerUpdate { + if v != nil { + _u.SetStatus(*v) + } + return _u +} + +// SetConnectionState sets the "connection_state" field. +func (_u *RuntimeBrokerUpdate) SetConnectionState(v string) *RuntimeBrokerUpdate { + _u.mutation.SetConnectionState(v) + return _u +} + +// SetNillableConnectionState sets the "connection_state" field if the given value is not nil. +func (_u *RuntimeBrokerUpdate) SetNillableConnectionState(v *string) *RuntimeBrokerUpdate { + if v != nil { + _u.SetConnectionState(*v) + } + return _u +} + +// SetLastHeartbeat sets the "last_heartbeat" field. +func (_u *RuntimeBrokerUpdate) SetLastHeartbeat(v time.Time) *RuntimeBrokerUpdate { + _u.mutation.SetLastHeartbeat(v) + return _u +} + +// SetNillableLastHeartbeat sets the "last_heartbeat" field if the given value is not nil. +func (_u *RuntimeBrokerUpdate) SetNillableLastHeartbeat(v *time.Time) *RuntimeBrokerUpdate { + if v != nil { + _u.SetLastHeartbeat(*v) + } + return _u +} + +// ClearLastHeartbeat clears the value of the "last_heartbeat" field. +func (_u *RuntimeBrokerUpdate) ClearLastHeartbeat() *RuntimeBrokerUpdate { + _u.mutation.ClearLastHeartbeat() + return _u +} + +// SetCapabilities sets the "capabilities" field. +func (_u *RuntimeBrokerUpdate) SetCapabilities(v string) *RuntimeBrokerUpdate { + _u.mutation.SetCapabilities(v) + return _u +} + +// SetNillableCapabilities sets the "capabilities" field if the given value is not nil. +func (_u *RuntimeBrokerUpdate) SetNillableCapabilities(v *string) *RuntimeBrokerUpdate { + if v != nil { + _u.SetCapabilities(*v) + } + return _u +} + +// ClearCapabilities clears the value of the "capabilities" field. +func (_u *RuntimeBrokerUpdate) ClearCapabilities() *RuntimeBrokerUpdate { + _u.mutation.ClearCapabilities() + return _u +} + +// SetSupportedHarnesses sets the "supported_harnesses" field. +func (_u *RuntimeBrokerUpdate) SetSupportedHarnesses(v string) *RuntimeBrokerUpdate { + _u.mutation.SetSupportedHarnesses(v) + return _u +} + +// SetNillableSupportedHarnesses sets the "supported_harnesses" field if the given value is not nil. +func (_u *RuntimeBrokerUpdate) SetNillableSupportedHarnesses(v *string) *RuntimeBrokerUpdate { + if v != nil { + _u.SetSupportedHarnesses(*v) + } + return _u +} + +// ClearSupportedHarnesses clears the value of the "supported_harnesses" field. +func (_u *RuntimeBrokerUpdate) ClearSupportedHarnesses() *RuntimeBrokerUpdate { + _u.mutation.ClearSupportedHarnesses() + return _u +} + +// SetResources sets the "resources" field. +func (_u *RuntimeBrokerUpdate) SetResources(v string) *RuntimeBrokerUpdate { + _u.mutation.SetResources(v) + return _u +} + +// SetNillableResources sets the "resources" field if the given value is not nil. +func (_u *RuntimeBrokerUpdate) SetNillableResources(v *string) *RuntimeBrokerUpdate { + if v != nil { + _u.SetResources(*v) + } + return _u +} + +// ClearResources clears the value of the "resources" field. +func (_u *RuntimeBrokerUpdate) ClearResources() *RuntimeBrokerUpdate { + _u.mutation.ClearResources() + return _u +} + +// SetRuntimes sets the "runtimes" field. +func (_u *RuntimeBrokerUpdate) SetRuntimes(v string) *RuntimeBrokerUpdate { + _u.mutation.SetRuntimes(v) + return _u +} + +// SetNillableRuntimes sets the "runtimes" field if the given value is not nil. +func (_u *RuntimeBrokerUpdate) SetNillableRuntimes(v *string) *RuntimeBrokerUpdate { + if v != nil { + _u.SetRuntimes(*v) + } + return _u +} + +// ClearRuntimes clears the value of the "runtimes" field. +func (_u *RuntimeBrokerUpdate) ClearRuntimes() *RuntimeBrokerUpdate { + _u.mutation.ClearRuntimes() + return _u +} + +// SetLabels sets the "labels" field. +func (_u *RuntimeBrokerUpdate) SetLabels(v string) *RuntimeBrokerUpdate { + _u.mutation.SetLabels(v) + return _u +} + +// SetNillableLabels sets the "labels" field if the given value is not nil. +func (_u *RuntimeBrokerUpdate) SetNillableLabels(v *string) *RuntimeBrokerUpdate { + if v != nil { + _u.SetLabels(*v) + } + return _u +} + +// ClearLabels clears the value of the "labels" field. +func (_u *RuntimeBrokerUpdate) ClearLabels() *RuntimeBrokerUpdate { + _u.mutation.ClearLabels() + return _u +} + +// SetAnnotations sets the "annotations" field. +func (_u *RuntimeBrokerUpdate) SetAnnotations(v string) *RuntimeBrokerUpdate { + _u.mutation.SetAnnotations(v) + return _u +} + +// SetNillableAnnotations sets the "annotations" field if the given value is not nil. +func (_u *RuntimeBrokerUpdate) SetNillableAnnotations(v *string) *RuntimeBrokerUpdate { + if v != nil { + _u.SetAnnotations(*v) + } + return _u +} + +// ClearAnnotations clears the value of the "annotations" field. +func (_u *RuntimeBrokerUpdate) ClearAnnotations() *RuntimeBrokerUpdate { + _u.mutation.ClearAnnotations() + return _u +} + +// SetEndpoint sets the "endpoint" field. +func (_u *RuntimeBrokerUpdate) SetEndpoint(v string) *RuntimeBrokerUpdate { + _u.mutation.SetEndpoint(v) + return _u +} + +// SetNillableEndpoint sets the "endpoint" field if the given value is not nil. +func (_u *RuntimeBrokerUpdate) SetNillableEndpoint(v *string) *RuntimeBrokerUpdate { + if v != nil { + _u.SetEndpoint(*v) + } + return _u +} + +// ClearEndpoint clears the value of the "endpoint" field. +func (_u *RuntimeBrokerUpdate) ClearEndpoint() *RuntimeBrokerUpdate { + _u.mutation.ClearEndpoint() + return _u +} + +// SetCreatedBy sets the "created_by" field. +func (_u *RuntimeBrokerUpdate) SetCreatedBy(v string) *RuntimeBrokerUpdate { + _u.mutation.SetCreatedBy(v) + return _u +} + +// SetNillableCreatedBy sets the "created_by" field if the given value is not nil. +func (_u *RuntimeBrokerUpdate) SetNillableCreatedBy(v *string) *RuntimeBrokerUpdate { + if v != nil { + _u.SetCreatedBy(*v) + } + return _u +} + +// ClearCreatedBy clears the value of the "created_by" field. +func (_u *RuntimeBrokerUpdate) ClearCreatedBy() *RuntimeBrokerUpdate { + _u.mutation.ClearCreatedBy() + return _u +} + +// SetAutoProvide sets the "auto_provide" field. +func (_u *RuntimeBrokerUpdate) SetAutoProvide(v bool) *RuntimeBrokerUpdate { + _u.mutation.SetAutoProvide(v) + return _u +} + +// SetNillableAutoProvide sets the "auto_provide" field if the given value is not nil. +func (_u *RuntimeBrokerUpdate) SetNillableAutoProvide(v *bool) *RuntimeBrokerUpdate { + if v != nil { + _u.SetAutoProvide(*v) + } + return _u +} + +// SetUpdated sets the "updated" field. +func (_u *RuntimeBrokerUpdate) SetUpdated(v time.Time) *RuntimeBrokerUpdate { + _u.mutation.SetUpdated(v) + return _u +} + +// Mutation returns the RuntimeBrokerMutation object of the builder. +func (_u *RuntimeBrokerUpdate) Mutation() *RuntimeBrokerMutation { + return _u.mutation +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (_u *RuntimeBrokerUpdate) Save(ctx context.Context) (int, error) { + _u.defaults() + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *RuntimeBrokerUpdate) SaveX(ctx context.Context) int { + affected, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (_u *RuntimeBrokerUpdate) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *RuntimeBrokerUpdate) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_u *RuntimeBrokerUpdate) defaults() { + if _, ok := _u.mutation.Updated(); !ok { + v := runtimebroker.UpdateDefaultUpdated() + _u.mutation.SetUpdated(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_u *RuntimeBrokerUpdate) check() error { + if v, ok := _u.mutation.Name(); ok { + if err := runtimebroker.NameValidator(v); err != nil { + return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "RuntimeBroker.name": %w`, err)} + } + } + if v, ok := _u.mutation.Slug(); ok { + if err := runtimebroker.SlugValidator(v); err != nil { + return &ValidationError{Name: "slug", err: fmt.Errorf(`ent: validator failed for field "RuntimeBroker.slug": %w`, err)} + } + } + return nil +} + +func (_u *RuntimeBrokerUpdate) sqlSave(ctx context.Context) (_node int, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(runtimebroker.Table, runtimebroker.Columns, sqlgraph.NewFieldSpec(runtimebroker.FieldID, field.TypeUUID)) + if ps := _u.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := _u.mutation.Name(); ok { + _spec.SetField(runtimebroker.FieldName, field.TypeString, value) + } + if value, ok := _u.mutation.Slug(); ok { + _spec.SetField(runtimebroker.FieldSlug, field.TypeString, value) + } + if value, ok := _u.mutation.GetType(); ok { + _spec.SetField(runtimebroker.FieldType, field.TypeString, value) + } + if _u.mutation.TypeCleared() { + _spec.ClearField(runtimebroker.FieldType, field.TypeString) + } + if value, ok := _u.mutation.Mode(); ok { + _spec.SetField(runtimebroker.FieldMode, field.TypeString, value) + } + if value, ok := _u.mutation.Version(); ok { + _spec.SetField(runtimebroker.FieldVersion, field.TypeString, value) + } + if _u.mutation.VersionCleared() { + _spec.ClearField(runtimebroker.FieldVersion, field.TypeString) + } + if value, ok := _u.mutation.LockVersion(); ok { + _spec.SetField(runtimebroker.FieldLockVersion, field.TypeInt64, value) + } + if value, ok := _u.mutation.AddedLockVersion(); ok { + _spec.AddField(runtimebroker.FieldLockVersion, field.TypeInt64, value) + } + if value, ok := _u.mutation.Status(); ok { + _spec.SetField(runtimebroker.FieldStatus, field.TypeString, value) + } + if value, ok := _u.mutation.ConnectionState(); ok { + _spec.SetField(runtimebroker.FieldConnectionState, field.TypeString, value) + } + if value, ok := _u.mutation.LastHeartbeat(); ok { + _spec.SetField(runtimebroker.FieldLastHeartbeat, field.TypeTime, value) + } + if _u.mutation.LastHeartbeatCleared() { + _spec.ClearField(runtimebroker.FieldLastHeartbeat, field.TypeTime) + } + if value, ok := _u.mutation.Capabilities(); ok { + _spec.SetField(runtimebroker.FieldCapabilities, field.TypeString, value) + } + if _u.mutation.CapabilitiesCleared() { + _spec.ClearField(runtimebroker.FieldCapabilities, field.TypeString) + } + if value, ok := _u.mutation.SupportedHarnesses(); ok { + _spec.SetField(runtimebroker.FieldSupportedHarnesses, field.TypeString, value) + } + if _u.mutation.SupportedHarnessesCleared() { + _spec.ClearField(runtimebroker.FieldSupportedHarnesses, field.TypeString) + } + if value, ok := _u.mutation.Resources(); ok { + _spec.SetField(runtimebroker.FieldResources, field.TypeString, value) + } + if _u.mutation.ResourcesCleared() { + _spec.ClearField(runtimebroker.FieldResources, field.TypeString) + } + if value, ok := _u.mutation.Runtimes(); ok { + _spec.SetField(runtimebroker.FieldRuntimes, field.TypeString, value) + } + if _u.mutation.RuntimesCleared() { + _spec.ClearField(runtimebroker.FieldRuntimes, field.TypeString) + } + if value, ok := _u.mutation.Labels(); ok { + _spec.SetField(runtimebroker.FieldLabels, field.TypeString, value) + } + if _u.mutation.LabelsCleared() { + _spec.ClearField(runtimebroker.FieldLabels, field.TypeString) + } + if value, ok := _u.mutation.Annotations(); ok { + _spec.SetField(runtimebroker.FieldAnnotations, field.TypeString, value) + } + if _u.mutation.AnnotationsCleared() { + _spec.ClearField(runtimebroker.FieldAnnotations, field.TypeString) + } + if value, ok := _u.mutation.Endpoint(); ok { + _spec.SetField(runtimebroker.FieldEndpoint, field.TypeString, value) + } + if _u.mutation.EndpointCleared() { + _spec.ClearField(runtimebroker.FieldEndpoint, field.TypeString) + } + if value, ok := _u.mutation.CreatedBy(); ok { + _spec.SetField(runtimebroker.FieldCreatedBy, field.TypeString, value) + } + if _u.mutation.CreatedByCleared() { + _spec.ClearField(runtimebroker.FieldCreatedBy, field.TypeString) + } + if value, ok := _u.mutation.AutoProvide(); ok { + _spec.SetField(runtimebroker.FieldAutoProvide, field.TypeBool, value) + } + if value, ok := _u.mutation.Updated(); ok { + _spec.SetField(runtimebroker.FieldUpdated, field.TypeTime, value) + } + if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{runtimebroker.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + _u.mutation.done = true + return _node, nil +} + +// RuntimeBrokerUpdateOne is the builder for updating a single RuntimeBroker entity. +type RuntimeBrokerUpdateOne struct { + config + fields []string + hooks []Hook + mutation *RuntimeBrokerMutation +} + +// SetName sets the "name" field. +func (_u *RuntimeBrokerUpdateOne) SetName(v string) *RuntimeBrokerUpdateOne { + _u.mutation.SetName(v) + return _u +} + +// SetNillableName sets the "name" field if the given value is not nil. +func (_u *RuntimeBrokerUpdateOne) SetNillableName(v *string) *RuntimeBrokerUpdateOne { + if v != nil { + _u.SetName(*v) + } + return _u +} + +// SetSlug sets the "slug" field. +func (_u *RuntimeBrokerUpdateOne) SetSlug(v string) *RuntimeBrokerUpdateOne { + _u.mutation.SetSlug(v) + return _u +} + +// SetNillableSlug sets the "slug" field if the given value is not nil. +func (_u *RuntimeBrokerUpdateOne) SetNillableSlug(v *string) *RuntimeBrokerUpdateOne { + if v != nil { + _u.SetSlug(*v) + } + return _u +} + +// SetType sets the "type" field. +func (_u *RuntimeBrokerUpdateOne) SetType(v string) *RuntimeBrokerUpdateOne { + _u.mutation.SetType(v) + return _u +} + +// SetNillableType sets the "type" field if the given value is not nil. +func (_u *RuntimeBrokerUpdateOne) SetNillableType(v *string) *RuntimeBrokerUpdateOne { + if v != nil { + _u.SetType(*v) + } + return _u +} + +// ClearType clears the value of the "type" field. +func (_u *RuntimeBrokerUpdateOne) ClearType() *RuntimeBrokerUpdateOne { + _u.mutation.ClearType() + return _u +} + +// SetMode sets the "mode" field. +func (_u *RuntimeBrokerUpdateOne) SetMode(v string) *RuntimeBrokerUpdateOne { + _u.mutation.SetMode(v) + return _u +} + +// SetNillableMode sets the "mode" field if the given value is not nil. +func (_u *RuntimeBrokerUpdateOne) SetNillableMode(v *string) *RuntimeBrokerUpdateOne { + if v != nil { + _u.SetMode(*v) + } + return _u +} + +// SetVersion sets the "version" field. +func (_u *RuntimeBrokerUpdateOne) SetVersion(v string) *RuntimeBrokerUpdateOne { + _u.mutation.SetVersion(v) + return _u +} + +// SetNillableVersion sets the "version" field if the given value is not nil. +func (_u *RuntimeBrokerUpdateOne) SetNillableVersion(v *string) *RuntimeBrokerUpdateOne { + if v != nil { + _u.SetVersion(*v) + } + return _u +} + +// ClearVersion clears the value of the "version" field. +func (_u *RuntimeBrokerUpdateOne) ClearVersion() *RuntimeBrokerUpdateOne { + _u.mutation.ClearVersion() + return _u +} + +// SetLockVersion sets the "lock_version" field. +func (_u *RuntimeBrokerUpdateOne) SetLockVersion(v int64) *RuntimeBrokerUpdateOne { + _u.mutation.ResetLockVersion() + _u.mutation.SetLockVersion(v) + return _u +} + +// SetNillableLockVersion sets the "lock_version" field if the given value is not nil. +func (_u *RuntimeBrokerUpdateOne) SetNillableLockVersion(v *int64) *RuntimeBrokerUpdateOne { + if v != nil { + _u.SetLockVersion(*v) + } + return _u +} + +// AddLockVersion adds value to the "lock_version" field. +func (_u *RuntimeBrokerUpdateOne) AddLockVersion(v int64) *RuntimeBrokerUpdateOne { + _u.mutation.AddLockVersion(v) + return _u +} + +// SetStatus sets the "status" field. +func (_u *RuntimeBrokerUpdateOne) SetStatus(v string) *RuntimeBrokerUpdateOne { + _u.mutation.SetStatus(v) + return _u +} + +// SetNillableStatus sets the "status" field if the given value is not nil. +func (_u *RuntimeBrokerUpdateOne) SetNillableStatus(v *string) *RuntimeBrokerUpdateOne { + if v != nil { + _u.SetStatus(*v) + } + return _u +} + +// SetConnectionState sets the "connection_state" field. +func (_u *RuntimeBrokerUpdateOne) SetConnectionState(v string) *RuntimeBrokerUpdateOne { + _u.mutation.SetConnectionState(v) + return _u +} + +// SetNillableConnectionState sets the "connection_state" field if the given value is not nil. +func (_u *RuntimeBrokerUpdateOne) SetNillableConnectionState(v *string) *RuntimeBrokerUpdateOne { + if v != nil { + _u.SetConnectionState(*v) + } + return _u +} + +// SetLastHeartbeat sets the "last_heartbeat" field. +func (_u *RuntimeBrokerUpdateOne) SetLastHeartbeat(v time.Time) *RuntimeBrokerUpdateOne { + _u.mutation.SetLastHeartbeat(v) + return _u +} + +// SetNillableLastHeartbeat sets the "last_heartbeat" field if the given value is not nil. +func (_u *RuntimeBrokerUpdateOne) SetNillableLastHeartbeat(v *time.Time) *RuntimeBrokerUpdateOne { + if v != nil { + _u.SetLastHeartbeat(*v) + } + return _u +} + +// ClearLastHeartbeat clears the value of the "last_heartbeat" field. +func (_u *RuntimeBrokerUpdateOne) ClearLastHeartbeat() *RuntimeBrokerUpdateOne { + _u.mutation.ClearLastHeartbeat() + return _u +} + +// SetCapabilities sets the "capabilities" field. +func (_u *RuntimeBrokerUpdateOne) SetCapabilities(v string) *RuntimeBrokerUpdateOne { + _u.mutation.SetCapabilities(v) + return _u +} + +// SetNillableCapabilities sets the "capabilities" field if the given value is not nil. +func (_u *RuntimeBrokerUpdateOne) SetNillableCapabilities(v *string) *RuntimeBrokerUpdateOne { + if v != nil { + _u.SetCapabilities(*v) + } + return _u +} + +// ClearCapabilities clears the value of the "capabilities" field. +func (_u *RuntimeBrokerUpdateOne) ClearCapabilities() *RuntimeBrokerUpdateOne { + _u.mutation.ClearCapabilities() + return _u +} + +// SetSupportedHarnesses sets the "supported_harnesses" field. +func (_u *RuntimeBrokerUpdateOne) SetSupportedHarnesses(v string) *RuntimeBrokerUpdateOne { + _u.mutation.SetSupportedHarnesses(v) + return _u +} + +// SetNillableSupportedHarnesses sets the "supported_harnesses" field if the given value is not nil. +func (_u *RuntimeBrokerUpdateOne) SetNillableSupportedHarnesses(v *string) *RuntimeBrokerUpdateOne { + if v != nil { + _u.SetSupportedHarnesses(*v) + } + return _u +} + +// ClearSupportedHarnesses clears the value of the "supported_harnesses" field. +func (_u *RuntimeBrokerUpdateOne) ClearSupportedHarnesses() *RuntimeBrokerUpdateOne { + _u.mutation.ClearSupportedHarnesses() + return _u +} + +// SetResources sets the "resources" field. +func (_u *RuntimeBrokerUpdateOne) SetResources(v string) *RuntimeBrokerUpdateOne { + _u.mutation.SetResources(v) + return _u +} + +// SetNillableResources sets the "resources" field if the given value is not nil. +func (_u *RuntimeBrokerUpdateOne) SetNillableResources(v *string) *RuntimeBrokerUpdateOne { + if v != nil { + _u.SetResources(*v) + } + return _u +} + +// ClearResources clears the value of the "resources" field. +func (_u *RuntimeBrokerUpdateOne) ClearResources() *RuntimeBrokerUpdateOne { + _u.mutation.ClearResources() + return _u +} + +// SetRuntimes sets the "runtimes" field. +func (_u *RuntimeBrokerUpdateOne) SetRuntimes(v string) *RuntimeBrokerUpdateOne { + _u.mutation.SetRuntimes(v) + return _u +} + +// SetNillableRuntimes sets the "runtimes" field if the given value is not nil. +func (_u *RuntimeBrokerUpdateOne) SetNillableRuntimes(v *string) *RuntimeBrokerUpdateOne { + if v != nil { + _u.SetRuntimes(*v) + } + return _u +} + +// ClearRuntimes clears the value of the "runtimes" field. +func (_u *RuntimeBrokerUpdateOne) ClearRuntimes() *RuntimeBrokerUpdateOne { + _u.mutation.ClearRuntimes() + return _u +} + +// SetLabels sets the "labels" field. +func (_u *RuntimeBrokerUpdateOne) SetLabels(v string) *RuntimeBrokerUpdateOne { + _u.mutation.SetLabels(v) + return _u +} + +// SetNillableLabels sets the "labels" field if the given value is not nil. +func (_u *RuntimeBrokerUpdateOne) SetNillableLabels(v *string) *RuntimeBrokerUpdateOne { + if v != nil { + _u.SetLabels(*v) + } + return _u +} + +// ClearLabels clears the value of the "labels" field. +func (_u *RuntimeBrokerUpdateOne) ClearLabels() *RuntimeBrokerUpdateOne { + _u.mutation.ClearLabels() + return _u +} + +// SetAnnotations sets the "annotations" field. +func (_u *RuntimeBrokerUpdateOne) SetAnnotations(v string) *RuntimeBrokerUpdateOne { + _u.mutation.SetAnnotations(v) + return _u +} + +// SetNillableAnnotations sets the "annotations" field if the given value is not nil. +func (_u *RuntimeBrokerUpdateOne) SetNillableAnnotations(v *string) *RuntimeBrokerUpdateOne { + if v != nil { + _u.SetAnnotations(*v) + } + return _u +} + +// ClearAnnotations clears the value of the "annotations" field. +func (_u *RuntimeBrokerUpdateOne) ClearAnnotations() *RuntimeBrokerUpdateOne { + _u.mutation.ClearAnnotations() + return _u +} + +// SetEndpoint sets the "endpoint" field. +func (_u *RuntimeBrokerUpdateOne) SetEndpoint(v string) *RuntimeBrokerUpdateOne { + _u.mutation.SetEndpoint(v) + return _u +} + +// SetNillableEndpoint sets the "endpoint" field if the given value is not nil. +func (_u *RuntimeBrokerUpdateOne) SetNillableEndpoint(v *string) *RuntimeBrokerUpdateOne { + if v != nil { + _u.SetEndpoint(*v) + } + return _u +} + +// ClearEndpoint clears the value of the "endpoint" field. +func (_u *RuntimeBrokerUpdateOne) ClearEndpoint() *RuntimeBrokerUpdateOne { + _u.mutation.ClearEndpoint() + return _u +} + +// SetCreatedBy sets the "created_by" field. +func (_u *RuntimeBrokerUpdateOne) SetCreatedBy(v string) *RuntimeBrokerUpdateOne { + _u.mutation.SetCreatedBy(v) + return _u +} + +// SetNillableCreatedBy sets the "created_by" field if the given value is not nil. +func (_u *RuntimeBrokerUpdateOne) SetNillableCreatedBy(v *string) *RuntimeBrokerUpdateOne { + if v != nil { + _u.SetCreatedBy(*v) + } + return _u +} + +// ClearCreatedBy clears the value of the "created_by" field. +func (_u *RuntimeBrokerUpdateOne) ClearCreatedBy() *RuntimeBrokerUpdateOne { + _u.mutation.ClearCreatedBy() + return _u +} + +// SetAutoProvide sets the "auto_provide" field. +func (_u *RuntimeBrokerUpdateOne) SetAutoProvide(v bool) *RuntimeBrokerUpdateOne { + _u.mutation.SetAutoProvide(v) + return _u +} + +// SetNillableAutoProvide sets the "auto_provide" field if the given value is not nil. +func (_u *RuntimeBrokerUpdateOne) SetNillableAutoProvide(v *bool) *RuntimeBrokerUpdateOne { + if v != nil { + _u.SetAutoProvide(*v) + } + return _u +} + +// SetUpdated sets the "updated" field. +func (_u *RuntimeBrokerUpdateOne) SetUpdated(v time.Time) *RuntimeBrokerUpdateOne { + _u.mutation.SetUpdated(v) + return _u +} + +// Mutation returns the RuntimeBrokerMutation object of the builder. +func (_u *RuntimeBrokerUpdateOne) Mutation() *RuntimeBrokerMutation { + return _u.mutation +} + +// Where appends a list predicates to the RuntimeBrokerUpdate builder. +func (_u *RuntimeBrokerUpdateOne) Where(ps ...predicate.RuntimeBroker) *RuntimeBrokerUpdateOne { + _u.mutation.Where(ps...) + return _u +} + +// Select allows selecting one or more fields (columns) of the returned entity. +// The default is selecting all fields defined in the entity schema. +func (_u *RuntimeBrokerUpdateOne) Select(field string, fields ...string) *RuntimeBrokerUpdateOne { + _u.fields = append([]string{field}, fields...) + return _u +} + +// Save executes the query and returns the updated RuntimeBroker entity. +func (_u *RuntimeBrokerUpdateOne) Save(ctx context.Context) (*RuntimeBroker, error) { + _u.defaults() + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *RuntimeBrokerUpdateOne) SaveX(ctx context.Context) *RuntimeBroker { + node, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (_u *RuntimeBrokerUpdateOne) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *RuntimeBrokerUpdateOne) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_u *RuntimeBrokerUpdateOne) defaults() { + if _, ok := _u.mutation.Updated(); !ok { + v := runtimebroker.UpdateDefaultUpdated() + _u.mutation.SetUpdated(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_u *RuntimeBrokerUpdateOne) check() error { + if v, ok := _u.mutation.Name(); ok { + if err := runtimebroker.NameValidator(v); err != nil { + return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "RuntimeBroker.name": %w`, err)} + } + } + if v, ok := _u.mutation.Slug(); ok { + if err := runtimebroker.SlugValidator(v); err != nil { + return &ValidationError{Name: "slug", err: fmt.Errorf(`ent: validator failed for field "RuntimeBroker.slug": %w`, err)} + } + } + return nil +} + +func (_u *RuntimeBrokerUpdateOne) sqlSave(ctx context.Context) (_node *RuntimeBroker, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(runtimebroker.Table, runtimebroker.Columns, sqlgraph.NewFieldSpec(runtimebroker.FieldID, field.TypeUUID)) + id, ok := _u.mutation.ID() + if !ok { + return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "RuntimeBroker.id" for update`)} + } + _spec.Node.ID.Value = id + if fields := _u.fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, runtimebroker.FieldID) + for _, f := range fields { + if !runtimebroker.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + if f != runtimebroker.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, f) + } + } + } + if ps := _u.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := _u.mutation.Name(); ok { + _spec.SetField(runtimebroker.FieldName, field.TypeString, value) + } + if value, ok := _u.mutation.Slug(); ok { + _spec.SetField(runtimebroker.FieldSlug, field.TypeString, value) + } + if value, ok := _u.mutation.GetType(); ok { + _spec.SetField(runtimebroker.FieldType, field.TypeString, value) + } + if _u.mutation.TypeCleared() { + _spec.ClearField(runtimebroker.FieldType, field.TypeString) + } + if value, ok := _u.mutation.Mode(); ok { + _spec.SetField(runtimebroker.FieldMode, field.TypeString, value) + } + if value, ok := _u.mutation.Version(); ok { + _spec.SetField(runtimebroker.FieldVersion, field.TypeString, value) + } + if _u.mutation.VersionCleared() { + _spec.ClearField(runtimebroker.FieldVersion, field.TypeString) + } + if value, ok := _u.mutation.LockVersion(); ok { + _spec.SetField(runtimebroker.FieldLockVersion, field.TypeInt64, value) + } + if value, ok := _u.mutation.AddedLockVersion(); ok { + _spec.AddField(runtimebroker.FieldLockVersion, field.TypeInt64, value) + } + if value, ok := _u.mutation.Status(); ok { + _spec.SetField(runtimebroker.FieldStatus, field.TypeString, value) + } + if value, ok := _u.mutation.ConnectionState(); ok { + _spec.SetField(runtimebroker.FieldConnectionState, field.TypeString, value) + } + if value, ok := _u.mutation.LastHeartbeat(); ok { + _spec.SetField(runtimebroker.FieldLastHeartbeat, field.TypeTime, value) + } + if _u.mutation.LastHeartbeatCleared() { + _spec.ClearField(runtimebroker.FieldLastHeartbeat, field.TypeTime) + } + if value, ok := _u.mutation.Capabilities(); ok { + _spec.SetField(runtimebroker.FieldCapabilities, field.TypeString, value) + } + if _u.mutation.CapabilitiesCleared() { + _spec.ClearField(runtimebroker.FieldCapabilities, field.TypeString) + } + if value, ok := _u.mutation.SupportedHarnesses(); ok { + _spec.SetField(runtimebroker.FieldSupportedHarnesses, field.TypeString, value) + } + if _u.mutation.SupportedHarnessesCleared() { + _spec.ClearField(runtimebroker.FieldSupportedHarnesses, field.TypeString) + } + if value, ok := _u.mutation.Resources(); ok { + _spec.SetField(runtimebroker.FieldResources, field.TypeString, value) + } + if _u.mutation.ResourcesCleared() { + _spec.ClearField(runtimebroker.FieldResources, field.TypeString) + } + if value, ok := _u.mutation.Runtimes(); ok { + _spec.SetField(runtimebroker.FieldRuntimes, field.TypeString, value) + } + if _u.mutation.RuntimesCleared() { + _spec.ClearField(runtimebroker.FieldRuntimes, field.TypeString) + } + if value, ok := _u.mutation.Labels(); ok { + _spec.SetField(runtimebroker.FieldLabels, field.TypeString, value) + } + if _u.mutation.LabelsCleared() { + _spec.ClearField(runtimebroker.FieldLabels, field.TypeString) + } + if value, ok := _u.mutation.Annotations(); ok { + _spec.SetField(runtimebroker.FieldAnnotations, field.TypeString, value) + } + if _u.mutation.AnnotationsCleared() { + _spec.ClearField(runtimebroker.FieldAnnotations, field.TypeString) + } + if value, ok := _u.mutation.Endpoint(); ok { + _spec.SetField(runtimebroker.FieldEndpoint, field.TypeString, value) + } + if _u.mutation.EndpointCleared() { + _spec.ClearField(runtimebroker.FieldEndpoint, field.TypeString) + } + if value, ok := _u.mutation.CreatedBy(); ok { + _spec.SetField(runtimebroker.FieldCreatedBy, field.TypeString, value) + } + if _u.mutation.CreatedByCleared() { + _spec.ClearField(runtimebroker.FieldCreatedBy, field.TypeString) + } + if value, ok := _u.mutation.AutoProvide(); ok { + _spec.SetField(runtimebroker.FieldAutoProvide, field.TypeBool, value) + } + if value, ok := _u.mutation.Updated(); ok { + _spec.SetField(runtimebroker.FieldUpdated, field.TypeTime, value) + } + _node = &RuntimeBroker{config: _u.config} + _spec.Assign = _node.assignValues + _spec.ScanValues = _node.scanValues + if err = sqlgraph.UpdateNode(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{runtimebroker.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + _u.mutation.done = true + return _node, nil +} diff --git a/pkg/ent/schedule.go b/pkg/ent/schedule.go new file mode 100644 index 000000000..c67a95cc1 --- /dev/null +++ b/pkg/ent/schedule.go @@ -0,0 +1,269 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "fmt" + "strings" + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "github.com/GoogleCloudPlatform/scion/pkg/ent/schedule" + "github.com/google/uuid" +) + +// Schedule is the model entity for the Schedule schema. +type Schedule struct { + config `json:"-"` + // ID of the ent. + ID uuid.UUID `json:"id,omitempty"` + // ProjectID holds the value of the "project_id" field. + ProjectID uuid.UUID `json:"project_id,omitempty"` + // Name holds the value of the "name" field. + Name string `json:"name,omitempty"` + // CronExpr holds the value of the "cron_expr" field. + CronExpr string `json:"cron_expr,omitempty"` + // EventType holds the value of the "event_type" field. + EventType string `json:"event_type,omitempty"` + // Payload holds the value of the "payload" field. + Payload string `json:"payload,omitempty"` + // Status holds the value of the "status" field. + Status string `json:"status,omitempty"` + // NextRunAt holds the value of the "next_run_at" field. + NextRunAt *time.Time `json:"next_run_at,omitempty"` + // LastRunAt holds the value of the "last_run_at" field. + LastRunAt *time.Time `json:"last_run_at,omitempty"` + // LastRunStatus holds the value of the "last_run_status" field. + LastRunStatus string `json:"last_run_status,omitempty"` + // LastRunError holds the value of the "last_run_error" field. + LastRunError string `json:"last_run_error,omitempty"` + // RunCount holds the value of the "run_count" field. + RunCount int `json:"run_count,omitempty"` + // ErrorCount holds the value of the "error_count" field. + ErrorCount int `json:"error_count,omitempty"` + // CreatedBy holds the value of the "created_by" field. + CreatedBy string `json:"created_by,omitempty"` + // Created holds the value of the "created" field. + Created time.Time `json:"created,omitempty"` + // Updated holds the value of the "updated" field. + Updated time.Time `json:"updated,omitempty"` + selectValues sql.SelectValues +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*Schedule) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case schedule.FieldRunCount, schedule.FieldErrorCount: + values[i] = new(sql.NullInt64) + case schedule.FieldName, schedule.FieldCronExpr, schedule.FieldEventType, schedule.FieldPayload, schedule.FieldStatus, schedule.FieldLastRunStatus, schedule.FieldLastRunError, schedule.FieldCreatedBy: + values[i] = new(sql.NullString) + case schedule.FieldNextRunAt, schedule.FieldLastRunAt, schedule.FieldCreated, schedule.FieldUpdated: + values[i] = new(sql.NullTime) + case schedule.FieldID, schedule.FieldProjectID: + values[i] = new(uuid.UUID) + default: + values[i] = new(sql.UnknownType) + } + } + return values, nil +} + +// assignValues assigns the values that were returned from sql.Rows (after scanning) +// to the Schedule fields. +func (_m *Schedule) assignValues(columns []string, values []any) error { + if m, n := len(values), len(columns); m < n { + return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) + } + for i := range columns { + switch columns[i] { + case schedule.FieldID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field id", values[i]) + } else if value != nil { + _m.ID = *value + } + case schedule.FieldProjectID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field project_id", values[i]) + } else if value != nil { + _m.ProjectID = *value + } + case schedule.FieldName: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field name", values[i]) + } else if value.Valid { + _m.Name = value.String + } + case schedule.FieldCronExpr: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field cron_expr", values[i]) + } else if value.Valid { + _m.CronExpr = value.String + } + case schedule.FieldEventType: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field event_type", values[i]) + } else if value.Valid { + _m.EventType = value.String + } + case schedule.FieldPayload: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field payload", values[i]) + } else if value.Valid { + _m.Payload = value.String + } + case schedule.FieldStatus: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field status", values[i]) + } else if value.Valid { + _m.Status = value.String + } + case schedule.FieldNextRunAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field next_run_at", values[i]) + } else if value.Valid { + _m.NextRunAt = new(time.Time) + *_m.NextRunAt = value.Time + } + case schedule.FieldLastRunAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field last_run_at", values[i]) + } else if value.Valid { + _m.LastRunAt = new(time.Time) + *_m.LastRunAt = value.Time + } + case schedule.FieldLastRunStatus: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field last_run_status", values[i]) + } else if value.Valid { + _m.LastRunStatus = value.String + } + case schedule.FieldLastRunError: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field last_run_error", values[i]) + } else if value.Valid { + _m.LastRunError = value.String + } + case schedule.FieldRunCount: + if value, ok := values[i].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for field run_count", values[i]) + } else if value.Valid { + _m.RunCount = int(value.Int64) + } + case schedule.FieldErrorCount: + if value, ok := values[i].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for field error_count", values[i]) + } else if value.Valid { + _m.ErrorCount = int(value.Int64) + } + case schedule.FieldCreatedBy: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field created_by", values[i]) + } else if value.Valid { + _m.CreatedBy = value.String + } + case schedule.FieldCreated: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field created", values[i]) + } else if value.Valid { + _m.Created = value.Time + } + case schedule.FieldUpdated: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field updated", values[i]) + } else if value.Valid { + _m.Updated = value.Time + } + default: + _m.selectValues.Set(columns[i], values[i]) + } + } + return nil +} + +// Value returns the ent.Value that was dynamically selected and assigned to the Schedule. +// This includes values selected through modifiers, order, etc. +func (_m *Schedule) Value(name string) (ent.Value, error) { + return _m.selectValues.Get(name) +} + +// Update returns a builder for updating this Schedule. +// Note that you need to call Schedule.Unwrap() before calling this method if this Schedule +// was returned from a transaction, and the transaction was committed or rolled back. +func (_m *Schedule) Update() *ScheduleUpdateOne { + return NewScheduleClient(_m.config).UpdateOne(_m) +} + +// Unwrap unwraps the Schedule entity that was returned from a transaction after it was closed, +// so that all future queries will be executed through the driver which created the transaction. +func (_m *Schedule) Unwrap() *Schedule { + _tx, ok := _m.config.driver.(*txDriver) + if !ok { + panic("ent: Schedule is not a transactional entity") + } + _m.config.driver = _tx.drv + return _m +} + +// String implements the fmt.Stringer. +func (_m *Schedule) String() string { + var builder strings.Builder + builder.WriteString("Schedule(") + builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID)) + builder.WriteString("project_id=") + builder.WriteString(fmt.Sprintf("%v", _m.ProjectID)) + builder.WriteString(", ") + builder.WriteString("name=") + builder.WriteString(_m.Name) + builder.WriteString(", ") + builder.WriteString("cron_expr=") + builder.WriteString(_m.CronExpr) + builder.WriteString(", ") + builder.WriteString("event_type=") + builder.WriteString(_m.EventType) + builder.WriteString(", ") + builder.WriteString("payload=") + builder.WriteString(_m.Payload) + builder.WriteString(", ") + builder.WriteString("status=") + builder.WriteString(_m.Status) + builder.WriteString(", ") + if v := _m.NextRunAt; v != nil { + builder.WriteString("next_run_at=") + builder.WriteString(v.Format(time.ANSIC)) + } + builder.WriteString(", ") + if v := _m.LastRunAt; v != nil { + builder.WriteString("last_run_at=") + builder.WriteString(v.Format(time.ANSIC)) + } + builder.WriteString(", ") + builder.WriteString("last_run_status=") + builder.WriteString(_m.LastRunStatus) + builder.WriteString(", ") + builder.WriteString("last_run_error=") + builder.WriteString(_m.LastRunError) + builder.WriteString(", ") + builder.WriteString("run_count=") + builder.WriteString(fmt.Sprintf("%v", _m.RunCount)) + builder.WriteString(", ") + builder.WriteString("error_count=") + builder.WriteString(fmt.Sprintf("%v", _m.ErrorCount)) + builder.WriteString(", ") + builder.WriteString("created_by=") + builder.WriteString(_m.CreatedBy) + builder.WriteString(", ") + builder.WriteString("created=") + builder.WriteString(_m.Created.Format(time.ANSIC)) + builder.WriteString(", ") + builder.WriteString("updated=") + builder.WriteString(_m.Updated.Format(time.ANSIC)) + builder.WriteByte(')') + return builder.String() +} + +// Schedules is a parsable slice of Schedule. +type Schedules []*Schedule diff --git a/pkg/ent/schedule/schedule.go b/pkg/ent/schedule/schedule.go new file mode 100644 index 000000000..a5481c542 --- /dev/null +++ b/pkg/ent/schedule/schedule.go @@ -0,0 +1,187 @@ +// Code generated by ent, DO NOT EDIT. + +package schedule + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "github.com/google/uuid" +) + +const ( + // Label holds the string label denoting the schedule type in the database. + Label = "schedule" + // FieldID holds the string denoting the id field in the database. + FieldID = "id" + // FieldProjectID holds the string denoting the project_id field in the database. + FieldProjectID = "project_id" + // FieldName holds the string denoting the name field in the database. + FieldName = "name" + // FieldCronExpr holds the string denoting the cron_expr field in the database. + FieldCronExpr = "cron_expr" + // FieldEventType holds the string denoting the event_type field in the database. + FieldEventType = "event_type" + // FieldPayload holds the string denoting the payload field in the database. + FieldPayload = "payload" + // FieldStatus holds the string denoting the status field in the database. + FieldStatus = "status" + // FieldNextRunAt holds the string denoting the next_run_at field in the database. + FieldNextRunAt = "next_run_at" + // FieldLastRunAt holds the string denoting the last_run_at field in the database. + FieldLastRunAt = "last_run_at" + // FieldLastRunStatus holds the string denoting the last_run_status field in the database. + FieldLastRunStatus = "last_run_status" + // FieldLastRunError holds the string denoting the last_run_error field in the database. + FieldLastRunError = "last_run_error" + // FieldRunCount holds the string denoting the run_count field in the database. + FieldRunCount = "run_count" + // FieldErrorCount holds the string denoting the error_count field in the database. + FieldErrorCount = "error_count" + // FieldCreatedBy holds the string denoting the created_by field in the database. + FieldCreatedBy = "created_by" + // FieldCreated holds the string denoting the created field in the database. + FieldCreated = "created" + // FieldUpdated holds the string denoting the updated field in the database. + FieldUpdated = "updated" + // Table holds the table name of the schedule in the database. + Table = "schedules" +) + +// Columns holds all SQL columns for schedule fields. +var Columns = []string{ + FieldID, + FieldProjectID, + FieldName, + FieldCronExpr, + FieldEventType, + FieldPayload, + FieldStatus, + FieldNextRunAt, + FieldLastRunAt, + FieldLastRunStatus, + FieldLastRunError, + FieldRunCount, + FieldErrorCount, + FieldCreatedBy, + FieldCreated, + FieldUpdated, +} + +// ValidColumn reports if the column name is valid (part of the table columns). +func ValidColumn(column string) bool { + for i := range Columns { + if column == Columns[i] { + return true + } + } + return false +} + +var ( + // NameValidator is a validator for the "name" field. It is called by the builders before save. + NameValidator func(string) error + // CronExprValidator is a validator for the "cron_expr" field. It is called by the builders before save. + CronExprValidator func(string) error + // EventTypeValidator is a validator for the "event_type" field. It is called by the builders before save. + EventTypeValidator func(string) error + // DefaultPayload holds the default value on creation for the "payload" field. + DefaultPayload string + // DefaultStatus holds the default value on creation for the "status" field. + DefaultStatus string + // DefaultRunCount holds the default value on creation for the "run_count" field. + DefaultRunCount int + // DefaultErrorCount holds the default value on creation for the "error_count" field. + DefaultErrorCount int + // DefaultCreated holds the default value on creation for the "created" field. + DefaultCreated func() time.Time + // DefaultUpdated holds the default value on creation for the "updated" field. + DefaultUpdated func() time.Time + // UpdateDefaultUpdated holds the default value on update for the "updated" field. + UpdateDefaultUpdated func() time.Time + // DefaultID holds the default value on creation for the "id" field. + DefaultID func() uuid.UUID +) + +// OrderOption defines the ordering options for the Schedule queries. +type OrderOption func(*sql.Selector) + +// ByID orders the results by the id field. +func ByID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldID, opts...).ToFunc() +} + +// ByProjectID orders the results by the project_id field. +func ByProjectID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldProjectID, opts...).ToFunc() +} + +// ByName orders the results by the name field. +func ByName(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldName, opts...).ToFunc() +} + +// ByCronExpr orders the results by the cron_expr field. +func ByCronExpr(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCronExpr, opts...).ToFunc() +} + +// ByEventType orders the results by the event_type field. +func ByEventType(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldEventType, opts...).ToFunc() +} + +// ByPayload orders the results by the payload field. +func ByPayload(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldPayload, opts...).ToFunc() +} + +// ByStatus orders the results by the status field. +func ByStatus(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldStatus, opts...).ToFunc() +} + +// ByNextRunAt orders the results by the next_run_at field. +func ByNextRunAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldNextRunAt, opts...).ToFunc() +} + +// ByLastRunAt orders the results by the last_run_at field. +func ByLastRunAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldLastRunAt, opts...).ToFunc() +} + +// ByLastRunStatus orders the results by the last_run_status field. +func ByLastRunStatus(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldLastRunStatus, opts...).ToFunc() +} + +// ByLastRunError orders the results by the last_run_error field. +func ByLastRunError(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldLastRunError, opts...).ToFunc() +} + +// ByRunCount orders the results by the run_count field. +func ByRunCount(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldRunCount, opts...).ToFunc() +} + +// ByErrorCount orders the results by the error_count field. +func ByErrorCount(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldErrorCount, opts...).ToFunc() +} + +// ByCreatedBy orders the results by the created_by field. +func ByCreatedBy(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCreatedBy, opts...).ToFunc() +} + +// ByCreated orders the results by the created field. +func ByCreated(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCreated, opts...).ToFunc() +} + +// ByUpdated orders the results by the updated field. +func ByUpdated(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldUpdated, opts...).ToFunc() +} diff --git a/pkg/ent/schedule/where.go b/pkg/ent/schedule/where.go new file mode 100644 index 000000000..7d8fe4d22 --- /dev/null +++ b/pkg/ent/schedule/where.go @@ -0,0 +1,996 @@ +// Code generated by ent, DO NOT EDIT. + +package schedule + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/google/uuid" +) + +// ID filters vertices based on their ID field. +func ID(id uuid.UUID) predicate.Schedule { + return predicate.Schedule(sql.FieldEQ(FieldID, id)) +} + +// IDEQ applies the EQ predicate on the ID field. +func IDEQ(id uuid.UUID) predicate.Schedule { + return predicate.Schedule(sql.FieldEQ(FieldID, id)) +} + +// IDNEQ applies the NEQ predicate on the ID field. +func IDNEQ(id uuid.UUID) predicate.Schedule { + return predicate.Schedule(sql.FieldNEQ(FieldID, id)) +} + +// IDIn applies the In predicate on the ID field. +func IDIn(ids ...uuid.UUID) predicate.Schedule { + return predicate.Schedule(sql.FieldIn(FieldID, ids...)) +} + +// IDNotIn applies the NotIn predicate on the ID field. +func IDNotIn(ids ...uuid.UUID) predicate.Schedule { + return predicate.Schedule(sql.FieldNotIn(FieldID, ids...)) +} + +// IDGT applies the GT predicate on the ID field. +func IDGT(id uuid.UUID) predicate.Schedule { + return predicate.Schedule(sql.FieldGT(FieldID, id)) +} + +// IDGTE applies the GTE predicate on the ID field. +func IDGTE(id uuid.UUID) predicate.Schedule { + return predicate.Schedule(sql.FieldGTE(FieldID, id)) +} + +// IDLT applies the LT predicate on the ID field. +func IDLT(id uuid.UUID) predicate.Schedule { + return predicate.Schedule(sql.FieldLT(FieldID, id)) +} + +// IDLTE applies the LTE predicate on the ID field. +func IDLTE(id uuid.UUID) predicate.Schedule { + return predicate.Schedule(sql.FieldLTE(FieldID, id)) +} + +// ProjectID applies equality check predicate on the "project_id" field. It's identical to ProjectIDEQ. +func ProjectID(v uuid.UUID) predicate.Schedule { + return predicate.Schedule(sql.FieldEQ(FieldProjectID, v)) +} + +// Name applies equality check predicate on the "name" field. It's identical to NameEQ. +func Name(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldEQ(FieldName, v)) +} + +// CronExpr applies equality check predicate on the "cron_expr" field. It's identical to CronExprEQ. +func CronExpr(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldEQ(FieldCronExpr, v)) +} + +// EventType applies equality check predicate on the "event_type" field. It's identical to EventTypeEQ. +func EventType(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldEQ(FieldEventType, v)) +} + +// Payload applies equality check predicate on the "payload" field. It's identical to PayloadEQ. +func Payload(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldEQ(FieldPayload, v)) +} + +// Status applies equality check predicate on the "status" field. It's identical to StatusEQ. +func Status(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldEQ(FieldStatus, v)) +} + +// NextRunAt applies equality check predicate on the "next_run_at" field. It's identical to NextRunAtEQ. +func NextRunAt(v time.Time) predicate.Schedule { + return predicate.Schedule(sql.FieldEQ(FieldNextRunAt, v)) +} + +// LastRunAt applies equality check predicate on the "last_run_at" field. It's identical to LastRunAtEQ. +func LastRunAt(v time.Time) predicate.Schedule { + return predicate.Schedule(sql.FieldEQ(FieldLastRunAt, v)) +} + +// LastRunStatus applies equality check predicate on the "last_run_status" field. It's identical to LastRunStatusEQ. +func LastRunStatus(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldEQ(FieldLastRunStatus, v)) +} + +// LastRunError applies equality check predicate on the "last_run_error" field. It's identical to LastRunErrorEQ. +func LastRunError(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldEQ(FieldLastRunError, v)) +} + +// RunCount applies equality check predicate on the "run_count" field. It's identical to RunCountEQ. +func RunCount(v int) predicate.Schedule { + return predicate.Schedule(sql.FieldEQ(FieldRunCount, v)) +} + +// ErrorCount applies equality check predicate on the "error_count" field. It's identical to ErrorCountEQ. +func ErrorCount(v int) predicate.Schedule { + return predicate.Schedule(sql.FieldEQ(FieldErrorCount, v)) +} + +// CreatedBy applies equality check predicate on the "created_by" field. It's identical to CreatedByEQ. +func CreatedBy(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldEQ(FieldCreatedBy, v)) +} + +// Created applies equality check predicate on the "created" field. It's identical to CreatedEQ. +func Created(v time.Time) predicate.Schedule { + return predicate.Schedule(sql.FieldEQ(FieldCreated, v)) +} + +// Updated applies equality check predicate on the "updated" field. It's identical to UpdatedEQ. +func Updated(v time.Time) predicate.Schedule { + return predicate.Schedule(sql.FieldEQ(FieldUpdated, v)) +} + +// ProjectIDEQ applies the EQ predicate on the "project_id" field. +func ProjectIDEQ(v uuid.UUID) predicate.Schedule { + return predicate.Schedule(sql.FieldEQ(FieldProjectID, v)) +} + +// ProjectIDNEQ applies the NEQ predicate on the "project_id" field. +func ProjectIDNEQ(v uuid.UUID) predicate.Schedule { + return predicate.Schedule(sql.FieldNEQ(FieldProjectID, v)) +} + +// ProjectIDIn applies the In predicate on the "project_id" field. +func ProjectIDIn(vs ...uuid.UUID) predicate.Schedule { + return predicate.Schedule(sql.FieldIn(FieldProjectID, vs...)) +} + +// ProjectIDNotIn applies the NotIn predicate on the "project_id" field. +func ProjectIDNotIn(vs ...uuid.UUID) predicate.Schedule { + return predicate.Schedule(sql.FieldNotIn(FieldProjectID, vs...)) +} + +// ProjectIDGT applies the GT predicate on the "project_id" field. +func ProjectIDGT(v uuid.UUID) predicate.Schedule { + return predicate.Schedule(sql.FieldGT(FieldProjectID, v)) +} + +// ProjectIDGTE applies the GTE predicate on the "project_id" field. +func ProjectIDGTE(v uuid.UUID) predicate.Schedule { + return predicate.Schedule(sql.FieldGTE(FieldProjectID, v)) +} + +// ProjectIDLT applies the LT predicate on the "project_id" field. +func ProjectIDLT(v uuid.UUID) predicate.Schedule { + return predicate.Schedule(sql.FieldLT(FieldProjectID, v)) +} + +// ProjectIDLTE applies the LTE predicate on the "project_id" field. +func ProjectIDLTE(v uuid.UUID) predicate.Schedule { + return predicate.Schedule(sql.FieldLTE(FieldProjectID, v)) +} + +// NameEQ applies the EQ predicate on the "name" field. +func NameEQ(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldEQ(FieldName, v)) +} + +// NameNEQ applies the NEQ predicate on the "name" field. +func NameNEQ(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldNEQ(FieldName, v)) +} + +// NameIn applies the In predicate on the "name" field. +func NameIn(vs ...string) predicate.Schedule { + return predicate.Schedule(sql.FieldIn(FieldName, vs...)) +} + +// NameNotIn applies the NotIn predicate on the "name" field. +func NameNotIn(vs ...string) predicate.Schedule { + return predicate.Schedule(sql.FieldNotIn(FieldName, vs...)) +} + +// NameGT applies the GT predicate on the "name" field. +func NameGT(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldGT(FieldName, v)) +} + +// NameGTE applies the GTE predicate on the "name" field. +func NameGTE(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldGTE(FieldName, v)) +} + +// NameLT applies the LT predicate on the "name" field. +func NameLT(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldLT(FieldName, v)) +} + +// NameLTE applies the LTE predicate on the "name" field. +func NameLTE(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldLTE(FieldName, v)) +} + +// NameContains applies the Contains predicate on the "name" field. +func NameContains(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldContains(FieldName, v)) +} + +// NameHasPrefix applies the HasPrefix predicate on the "name" field. +func NameHasPrefix(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldHasPrefix(FieldName, v)) +} + +// NameHasSuffix applies the HasSuffix predicate on the "name" field. +func NameHasSuffix(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldHasSuffix(FieldName, v)) +} + +// NameEqualFold applies the EqualFold predicate on the "name" field. +func NameEqualFold(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldEqualFold(FieldName, v)) +} + +// NameContainsFold applies the ContainsFold predicate on the "name" field. +func NameContainsFold(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldContainsFold(FieldName, v)) +} + +// CronExprEQ applies the EQ predicate on the "cron_expr" field. +func CronExprEQ(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldEQ(FieldCronExpr, v)) +} + +// CronExprNEQ applies the NEQ predicate on the "cron_expr" field. +func CronExprNEQ(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldNEQ(FieldCronExpr, v)) +} + +// CronExprIn applies the In predicate on the "cron_expr" field. +func CronExprIn(vs ...string) predicate.Schedule { + return predicate.Schedule(sql.FieldIn(FieldCronExpr, vs...)) +} + +// CronExprNotIn applies the NotIn predicate on the "cron_expr" field. +func CronExprNotIn(vs ...string) predicate.Schedule { + return predicate.Schedule(sql.FieldNotIn(FieldCronExpr, vs...)) +} + +// CronExprGT applies the GT predicate on the "cron_expr" field. +func CronExprGT(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldGT(FieldCronExpr, v)) +} + +// CronExprGTE applies the GTE predicate on the "cron_expr" field. +func CronExprGTE(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldGTE(FieldCronExpr, v)) +} + +// CronExprLT applies the LT predicate on the "cron_expr" field. +func CronExprLT(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldLT(FieldCronExpr, v)) +} + +// CronExprLTE applies the LTE predicate on the "cron_expr" field. +func CronExprLTE(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldLTE(FieldCronExpr, v)) +} + +// CronExprContains applies the Contains predicate on the "cron_expr" field. +func CronExprContains(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldContains(FieldCronExpr, v)) +} + +// CronExprHasPrefix applies the HasPrefix predicate on the "cron_expr" field. +func CronExprHasPrefix(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldHasPrefix(FieldCronExpr, v)) +} + +// CronExprHasSuffix applies the HasSuffix predicate on the "cron_expr" field. +func CronExprHasSuffix(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldHasSuffix(FieldCronExpr, v)) +} + +// CronExprEqualFold applies the EqualFold predicate on the "cron_expr" field. +func CronExprEqualFold(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldEqualFold(FieldCronExpr, v)) +} + +// CronExprContainsFold applies the ContainsFold predicate on the "cron_expr" field. +func CronExprContainsFold(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldContainsFold(FieldCronExpr, v)) +} + +// EventTypeEQ applies the EQ predicate on the "event_type" field. +func EventTypeEQ(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldEQ(FieldEventType, v)) +} + +// EventTypeNEQ applies the NEQ predicate on the "event_type" field. +func EventTypeNEQ(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldNEQ(FieldEventType, v)) +} + +// EventTypeIn applies the In predicate on the "event_type" field. +func EventTypeIn(vs ...string) predicate.Schedule { + return predicate.Schedule(sql.FieldIn(FieldEventType, vs...)) +} + +// EventTypeNotIn applies the NotIn predicate on the "event_type" field. +func EventTypeNotIn(vs ...string) predicate.Schedule { + return predicate.Schedule(sql.FieldNotIn(FieldEventType, vs...)) +} + +// EventTypeGT applies the GT predicate on the "event_type" field. +func EventTypeGT(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldGT(FieldEventType, v)) +} + +// EventTypeGTE applies the GTE predicate on the "event_type" field. +func EventTypeGTE(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldGTE(FieldEventType, v)) +} + +// EventTypeLT applies the LT predicate on the "event_type" field. +func EventTypeLT(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldLT(FieldEventType, v)) +} + +// EventTypeLTE applies the LTE predicate on the "event_type" field. +func EventTypeLTE(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldLTE(FieldEventType, v)) +} + +// EventTypeContains applies the Contains predicate on the "event_type" field. +func EventTypeContains(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldContains(FieldEventType, v)) +} + +// EventTypeHasPrefix applies the HasPrefix predicate on the "event_type" field. +func EventTypeHasPrefix(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldHasPrefix(FieldEventType, v)) +} + +// EventTypeHasSuffix applies the HasSuffix predicate on the "event_type" field. +func EventTypeHasSuffix(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldHasSuffix(FieldEventType, v)) +} + +// EventTypeEqualFold applies the EqualFold predicate on the "event_type" field. +func EventTypeEqualFold(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldEqualFold(FieldEventType, v)) +} + +// EventTypeContainsFold applies the ContainsFold predicate on the "event_type" field. +func EventTypeContainsFold(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldContainsFold(FieldEventType, v)) +} + +// PayloadEQ applies the EQ predicate on the "payload" field. +func PayloadEQ(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldEQ(FieldPayload, v)) +} + +// PayloadNEQ applies the NEQ predicate on the "payload" field. +func PayloadNEQ(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldNEQ(FieldPayload, v)) +} + +// PayloadIn applies the In predicate on the "payload" field. +func PayloadIn(vs ...string) predicate.Schedule { + return predicate.Schedule(sql.FieldIn(FieldPayload, vs...)) +} + +// PayloadNotIn applies the NotIn predicate on the "payload" field. +func PayloadNotIn(vs ...string) predicate.Schedule { + return predicate.Schedule(sql.FieldNotIn(FieldPayload, vs...)) +} + +// PayloadGT applies the GT predicate on the "payload" field. +func PayloadGT(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldGT(FieldPayload, v)) +} + +// PayloadGTE applies the GTE predicate on the "payload" field. +func PayloadGTE(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldGTE(FieldPayload, v)) +} + +// PayloadLT applies the LT predicate on the "payload" field. +func PayloadLT(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldLT(FieldPayload, v)) +} + +// PayloadLTE applies the LTE predicate on the "payload" field. +func PayloadLTE(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldLTE(FieldPayload, v)) +} + +// PayloadContains applies the Contains predicate on the "payload" field. +func PayloadContains(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldContains(FieldPayload, v)) +} + +// PayloadHasPrefix applies the HasPrefix predicate on the "payload" field. +func PayloadHasPrefix(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldHasPrefix(FieldPayload, v)) +} + +// PayloadHasSuffix applies the HasSuffix predicate on the "payload" field. +func PayloadHasSuffix(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldHasSuffix(FieldPayload, v)) +} + +// PayloadEqualFold applies the EqualFold predicate on the "payload" field. +func PayloadEqualFold(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldEqualFold(FieldPayload, v)) +} + +// PayloadContainsFold applies the ContainsFold predicate on the "payload" field. +func PayloadContainsFold(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldContainsFold(FieldPayload, v)) +} + +// StatusEQ applies the EQ predicate on the "status" field. +func StatusEQ(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldEQ(FieldStatus, v)) +} + +// StatusNEQ applies the NEQ predicate on the "status" field. +func StatusNEQ(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldNEQ(FieldStatus, v)) +} + +// StatusIn applies the In predicate on the "status" field. +func StatusIn(vs ...string) predicate.Schedule { + return predicate.Schedule(sql.FieldIn(FieldStatus, vs...)) +} + +// StatusNotIn applies the NotIn predicate on the "status" field. +func StatusNotIn(vs ...string) predicate.Schedule { + return predicate.Schedule(sql.FieldNotIn(FieldStatus, vs...)) +} + +// StatusGT applies the GT predicate on the "status" field. +func StatusGT(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldGT(FieldStatus, v)) +} + +// StatusGTE applies the GTE predicate on the "status" field. +func StatusGTE(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldGTE(FieldStatus, v)) +} + +// StatusLT applies the LT predicate on the "status" field. +func StatusLT(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldLT(FieldStatus, v)) +} + +// StatusLTE applies the LTE predicate on the "status" field. +func StatusLTE(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldLTE(FieldStatus, v)) +} + +// StatusContains applies the Contains predicate on the "status" field. +func StatusContains(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldContains(FieldStatus, v)) +} + +// StatusHasPrefix applies the HasPrefix predicate on the "status" field. +func StatusHasPrefix(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldHasPrefix(FieldStatus, v)) +} + +// StatusHasSuffix applies the HasSuffix predicate on the "status" field. +func StatusHasSuffix(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldHasSuffix(FieldStatus, v)) +} + +// StatusEqualFold applies the EqualFold predicate on the "status" field. +func StatusEqualFold(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldEqualFold(FieldStatus, v)) +} + +// StatusContainsFold applies the ContainsFold predicate on the "status" field. +func StatusContainsFold(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldContainsFold(FieldStatus, v)) +} + +// NextRunAtEQ applies the EQ predicate on the "next_run_at" field. +func NextRunAtEQ(v time.Time) predicate.Schedule { + return predicate.Schedule(sql.FieldEQ(FieldNextRunAt, v)) +} + +// NextRunAtNEQ applies the NEQ predicate on the "next_run_at" field. +func NextRunAtNEQ(v time.Time) predicate.Schedule { + return predicate.Schedule(sql.FieldNEQ(FieldNextRunAt, v)) +} + +// NextRunAtIn applies the In predicate on the "next_run_at" field. +func NextRunAtIn(vs ...time.Time) predicate.Schedule { + return predicate.Schedule(sql.FieldIn(FieldNextRunAt, vs...)) +} + +// NextRunAtNotIn applies the NotIn predicate on the "next_run_at" field. +func NextRunAtNotIn(vs ...time.Time) predicate.Schedule { + return predicate.Schedule(sql.FieldNotIn(FieldNextRunAt, vs...)) +} + +// NextRunAtGT applies the GT predicate on the "next_run_at" field. +func NextRunAtGT(v time.Time) predicate.Schedule { + return predicate.Schedule(sql.FieldGT(FieldNextRunAt, v)) +} + +// NextRunAtGTE applies the GTE predicate on the "next_run_at" field. +func NextRunAtGTE(v time.Time) predicate.Schedule { + return predicate.Schedule(sql.FieldGTE(FieldNextRunAt, v)) +} + +// NextRunAtLT applies the LT predicate on the "next_run_at" field. +func NextRunAtLT(v time.Time) predicate.Schedule { + return predicate.Schedule(sql.FieldLT(FieldNextRunAt, v)) +} + +// NextRunAtLTE applies the LTE predicate on the "next_run_at" field. +func NextRunAtLTE(v time.Time) predicate.Schedule { + return predicate.Schedule(sql.FieldLTE(FieldNextRunAt, v)) +} + +// NextRunAtIsNil applies the IsNil predicate on the "next_run_at" field. +func NextRunAtIsNil() predicate.Schedule { + return predicate.Schedule(sql.FieldIsNull(FieldNextRunAt)) +} + +// NextRunAtNotNil applies the NotNil predicate on the "next_run_at" field. +func NextRunAtNotNil() predicate.Schedule { + return predicate.Schedule(sql.FieldNotNull(FieldNextRunAt)) +} + +// LastRunAtEQ applies the EQ predicate on the "last_run_at" field. +func LastRunAtEQ(v time.Time) predicate.Schedule { + return predicate.Schedule(sql.FieldEQ(FieldLastRunAt, v)) +} + +// LastRunAtNEQ applies the NEQ predicate on the "last_run_at" field. +func LastRunAtNEQ(v time.Time) predicate.Schedule { + return predicate.Schedule(sql.FieldNEQ(FieldLastRunAt, v)) +} + +// LastRunAtIn applies the In predicate on the "last_run_at" field. +func LastRunAtIn(vs ...time.Time) predicate.Schedule { + return predicate.Schedule(sql.FieldIn(FieldLastRunAt, vs...)) +} + +// LastRunAtNotIn applies the NotIn predicate on the "last_run_at" field. +func LastRunAtNotIn(vs ...time.Time) predicate.Schedule { + return predicate.Schedule(sql.FieldNotIn(FieldLastRunAt, vs...)) +} + +// LastRunAtGT applies the GT predicate on the "last_run_at" field. +func LastRunAtGT(v time.Time) predicate.Schedule { + return predicate.Schedule(sql.FieldGT(FieldLastRunAt, v)) +} + +// LastRunAtGTE applies the GTE predicate on the "last_run_at" field. +func LastRunAtGTE(v time.Time) predicate.Schedule { + return predicate.Schedule(sql.FieldGTE(FieldLastRunAt, v)) +} + +// LastRunAtLT applies the LT predicate on the "last_run_at" field. +func LastRunAtLT(v time.Time) predicate.Schedule { + return predicate.Schedule(sql.FieldLT(FieldLastRunAt, v)) +} + +// LastRunAtLTE applies the LTE predicate on the "last_run_at" field. +func LastRunAtLTE(v time.Time) predicate.Schedule { + return predicate.Schedule(sql.FieldLTE(FieldLastRunAt, v)) +} + +// LastRunAtIsNil applies the IsNil predicate on the "last_run_at" field. +func LastRunAtIsNil() predicate.Schedule { + return predicate.Schedule(sql.FieldIsNull(FieldLastRunAt)) +} + +// LastRunAtNotNil applies the NotNil predicate on the "last_run_at" field. +func LastRunAtNotNil() predicate.Schedule { + return predicate.Schedule(sql.FieldNotNull(FieldLastRunAt)) +} + +// LastRunStatusEQ applies the EQ predicate on the "last_run_status" field. +func LastRunStatusEQ(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldEQ(FieldLastRunStatus, v)) +} + +// LastRunStatusNEQ applies the NEQ predicate on the "last_run_status" field. +func LastRunStatusNEQ(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldNEQ(FieldLastRunStatus, v)) +} + +// LastRunStatusIn applies the In predicate on the "last_run_status" field. +func LastRunStatusIn(vs ...string) predicate.Schedule { + return predicate.Schedule(sql.FieldIn(FieldLastRunStatus, vs...)) +} + +// LastRunStatusNotIn applies the NotIn predicate on the "last_run_status" field. +func LastRunStatusNotIn(vs ...string) predicate.Schedule { + return predicate.Schedule(sql.FieldNotIn(FieldLastRunStatus, vs...)) +} + +// LastRunStatusGT applies the GT predicate on the "last_run_status" field. +func LastRunStatusGT(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldGT(FieldLastRunStatus, v)) +} + +// LastRunStatusGTE applies the GTE predicate on the "last_run_status" field. +func LastRunStatusGTE(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldGTE(FieldLastRunStatus, v)) +} + +// LastRunStatusLT applies the LT predicate on the "last_run_status" field. +func LastRunStatusLT(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldLT(FieldLastRunStatus, v)) +} + +// LastRunStatusLTE applies the LTE predicate on the "last_run_status" field. +func LastRunStatusLTE(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldLTE(FieldLastRunStatus, v)) +} + +// LastRunStatusContains applies the Contains predicate on the "last_run_status" field. +func LastRunStatusContains(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldContains(FieldLastRunStatus, v)) +} + +// LastRunStatusHasPrefix applies the HasPrefix predicate on the "last_run_status" field. +func LastRunStatusHasPrefix(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldHasPrefix(FieldLastRunStatus, v)) +} + +// LastRunStatusHasSuffix applies the HasSuffix predicate on the "last_run_status" field. +func LastRunStatusHasSuffix(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldHasSuffix(FieldLastRunStatus, v)) +} + +// LastRunStatusIsNil applies the IsNil predicate on the "last_run_status" field. +func LastRunStatusIsNil() predicate.Schedule { + return predicate.Schedule(sql.FieldIsNull(FieldLastRunStatus)) +} + +// LastRunStatusNotNil applies the NotNil predicate on the "last_run_status" field. +func LastRunStatusNotNil() predicate.Schedule { + return predicate.Schedule(sql.FieldNotNull(FieldLastRunStatus)) +} + +// LastRunStatusEqualFold applies the EqualFold predicate on the "last_run_status" field. +func LastRunStatusEqualFold(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldEqualFold(FieldLastRunStatus, v)) +} + +// LastRunStatusContainsFold applies the ContainsFold predicate on the "last_run_status" field. +func LastRunStatusContainsFold(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldContainsFold(FieldLastRunStatus, v)) +} + +// LastRunErrorEQ applies the EQ predicate on the "last_run_error" field. +func LastRunErrorEQ(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldEQ(FieldLastRunError, v)) +} + +// LastRunErrorNEQ applies the NEQ predicate on the "last_run_error" field. +func LastRunErrorNEQ(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldNEQ(FieldLastRunError, v)) +} + +// LastRunErrorIn applies the In predicate on the "last_run_error" field. +func LastRunErrorIn(vs ...string) predicate.Schedule { + return predicate.Schedule(sql.FieldIn(FieldLastRunError, vs...)) +} + +// LastRunErrorNotIn applies the NotIn predicate on the "last_run_error" field. +func LastRunErrorNotIn(vs ...string) predicate.Schedule { + return predicate.Schedule(sql.FieldNotIn(FieldLastRunError, vs...)) +} + +// LastRunErrorGT applies the GT predicate on the "last_run_error" field. +func LastRunErrorGT(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldGT(FieldLastRunError, v)) +} + +// LastRunErrorGTE applies the GTE predicate on the "last_run_error" field. +func LastRunErrorGTE(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldGTE(FieldLastRunError, v)) +} + +// LastRunErrorLT applies the LT predicate on the "last_run_error" field. +func LastRunErrorLT(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldLT(FieldLastRunError, v)) +} + +// LastRunErrorLTE applies the LTE predicate on the "last_run_error" field. +func LastRunErrorLTE(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldLTE(FieldLastRunError, v)) +} + +// LastRunErrorContains applies the Contains predicate on the "last_run_error" field. +func LastRunErrorContains(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldContains(FieldLastRunError, v)) +} + +// LastRunErrorHasPrefix applies the HasPrefix predicate on the "last_run_error" field. +func LastRunErrorHasPrefix(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldHasPrefix(FieldLastRunError, v)) +} + +// LastRunErrorHasSuffix applies the HasSuffix predicate on the "last_run_error" field. +func LastRunErrorHasSuffix(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldHasSuffix(FieldLastRunError, v)) +} + +// LastRunErrorIsNil applies the IsNil predicate on the "last_run_error" field. +func LastRunErrorIsNil() predicate.Schedule { + return predicate.Schedule(sql.FieldIsNull(FieldLastRunError)) +} + +// LastRunErrorNotNil applies the NotNil predicate on the "last_run_error" field. +func LastRunErrorNotNil() predicate.Schedule { + return predicate.Schedule(sql.FieldNotNull(FieldLastRunError)) +} + +// LastRunErrorEqualFold applies the EqualFold predicate on the "last_run_error" field. +func LastRunErrorEqualFold(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldEqualFold(FieldLastRunError, v)) +} + +// LastRunErrorContainsFold applies the ContainsFold predicate on the "last_run_error" field. +func LastRunErrorContainsFold(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldContainsFold(FieldLastRunError, v)) +} + +// RunCountEQ applies the EQ predicate on the "run_count" field. +func RunCountEQ(v int) predicate.Schedule { + return predicate.Schedule(sql.FieldEQ(FieldRunCount, v)) +} + +// RunCountNEQ applies the NEQ predicate on the "run_count" field. +func RunCountNEQ(v int) predicate.Schedule { + return predicate.Schedule(sql.FieldNEQ(FieldRunCount, v)) +} + +// RunCountIn applies the In predicate on the "run_count" field. +func RunCountIn(vs ...int) predicate.Schedule { + return predicate.Schedule(sql.FieldIn(FieldRunCount, vs...)) +} + +// RunCountNotIn applies the NotIn predicate on the "run_count" field. +func RunCountNotIn(vs ...int) predicate.Schedule { + return predicate.Schedule(sql.FieldNotIn(FieldRunCount, vs...)) +} + +// RunCountGT applies the GT predicate on the "run_count" field. +func RunCountGT(v int) predicate.Schedule { + return predicate.Schedule(sql.FieldGT(FieldRunCount, v)) +} + +// RunCountGTE applies the GTE predicate on the "run_count" field. +func RunCountGTE(v int) predicate.Schedule { + return predicate.Schedule(sql.FieldGTE(FieldRunCount, v)) +} + +// RunCountLT applies the LT predicate on the "run_count" field. +func RunCountLT(v int) predicate.Schedule { + return predicate.Schedule(sql.FieldLT(FieldRunCount, v)) +} + +// RunCountLTE applies the LTE predicate on the "run_count" field. +func RunCountLTE(v int) predicate.Schedule { + return predicate.Schedule(sql.FieldLTE(FieldRunCount, v)) +} + +// ErrorCountEQ applies the EQ predicate on the "error_count" field. +func ErrorCountEQ(v int) predicate.Schedule { + return predicate.Schedule(sql.FieldEQ(FieldErrorCount, v)) +} + +// ErrorCountNEQ applies the NEQ predicate on the "error_count" field. +func ErrorCountNEQ(v int) predicate.Schedule { + return predicate.Schedule(sql.FieldNEQ(FieldErrorCount, v)) +} + +// ErrorCountIn applies the In predicate on the "error_count" field. +func ErrorCountIn(vs ...int) predicate.Schedule { + return predicate.Schedule(sql.FieldIn(FieldErrorCount, vs...)) +} + +// ErrorCountNotIn applies the NotIn predicate on the "error_count" field. +func ErrorCountNotIn(vs ...int) predicate.Schedule { + return predicate.Schedule(sql.FieldNotIn(FieldErrorCount, vs...)) +} + +// ErrorCountGT applies the GT predicate on the "error_count" field. +func ErrorCountGT(v int) predicate.Schedule { + return predicate.Schedule(sql.FieldGT(FieldErrorCount, v)) +} + +// ErrorCountGTE applies the GTE predicate on the "error_count" field. +func ErrorCountGTE(v int) predicate.Schedule { + return predicate.Schedule(sql.FieldGTE(FieldErrorCount, v)) +} + +// ErrorCountLT applies the LT predicate on the "error_count" field. +func ErrorCountLT(v int) predicate.Schedule { + return predicate.Schedule(sql.FieldLT(FieldErrorCount, v)) +} + +// ErrorCountLTE applies the LTE predicate on the "error_count" field. +func ErrorCountLTE(v int) predicate.Schedule { + return predicate.Schedule(sql.FieldLTE(FieldErrorCount, v)) +} + +// CreatedByEQ applies the EQ predicate on the "created_by" field. +func CreatedByEQ(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldEQ(FieldCreatedBy, v)) +} + +// CreatedByNEQ applies the NEQ predicate on the "created_by" field. +func CreatedByNEQ(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldNEQ(FieldCreatedBy, v)) +} + +// CreatedByIn applies the In predicate on the "created_by" field. +func CreatedByIn(vs ...string) predicate.Schedule { + return predicate.Schedule(sql.FieldIn(FieldCreatedBy, vs...)) +} + +// CreatedByNotIn applies the NotIn predicate on the "created_by" field. +func CreatedByNotIn(vs ...string) predicate.Schedule { + return predicate.Schedule(sql.FieldNotIn(FieldCreatedBy, vs...)) +} + +// CreatedByGT applies the GT predicate on the "created_by" field. +func CreatedByGT(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldGT(FieldCreatedBy, v)) +} + +// CreatedByGTE applies the GTE predicate on the "created_by" field. +func CreatedByGTE(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldGTE(FieldCreatedBy, v)) +} + +// CreatedByLT applies the LT predicate on the "created_by" field. +func CreatedByLT(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldLT(FieldCreatedBy, v)) +} + +// CreatedByLTE applies the LTE predicate on the "created_by" field. +func CreatedByLTE(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldLTE(FieldCreatedBy, v)) +} + +// CreatedByContains applies the Contains predicate on the "created_by" field. +func CreatedByContains(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldContains(FieldCreatedBy, v)) +} + +// CreatedByHasPrefix applies the HasPrefix predicate on the "created_by" field. +func CreatedByHasPrefix(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldHasPrefix(FieldCreatedBy, v)) +} + +// CreatedByHasSuffix applies the HasSuffix predicate on the "created_by" field. +func CreatedByHasSuffix(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldHasSuffix(FieldCreatedBy, v)) +} + +// CreatedByIsNil applies the IsNil predicate on the "created_by" field. +func CreatedByIsNil() predicate.Schedule { + return predicate.Schedule(sql.FieldIsNull(FieldCreatedBy)) +} + +// CreatedByNotNil applies the NotNil predicate on the "created_by" field. +func CreatedByNotNil() predicate.Schedule { + return predicate.Schedule(sql.FieldNotNull(FieldCreatedBy)) +} + +// CreatedByEqualFold applies the EqualFold predicate on the "created_by" field. +func CreatedByEqualFold(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldEqualFold(FieldCreatedBy, v)) +} + +// CreatedByContainsFold applies the ContainsFold predicate on the "created_by" field. +func CreatedByContainsFold(v string) predicate.Schedule { + return predicate.Schedule(sql.FieldContainsFold(FieldCreatedBy, v)) +} + +// CreatedEQ applies the EQ predicate on the "created" field. +func CreatedEQ(v time.Time) predicate.Schedule { + return predicate.Schedule(sql.FieldEQ(FieldCreated, v)) +} + +// CreatedNEQ applies the NEQ predicate on the "created" field. +func CreatedNEQ(v time.Time) predicate.Schedule { + return predicate.Schedule(sql.FieldNEQ(FieldCreated, v)) +} + +// CreatedIn applies the In predicate on the "created" field. +func CreatedIn(vs ...time.Time) predicate.Schedule { + return predicate.Schedule(sql.FieldIn(FieldCreated, vs...)) +} + +// CreatedNotIn applies the NotIn predicate on the "created" field. +func CreatedNotIn(vs ...time.Time) predicate.Schedule { + return predicate.Schedule(sql.FieldNotIn(FieldCreated, vs...)) +} + +// CreatedGT applies the GT predicate on the "created" field. +func CreatedGT(v time.Time) predicate.Schedule { + return predicate.Schedule(sql.FieldGT(FieldCreated, v)) +} + +// CreatedGTE applies the GTE predicate on the "created" field. +func CreatedGTE(v time.Time) predicate.Schedule { + return predicate.Schedule(sql.FieldGTE(FieldCreated, v)) +} + +// CreatedLT applies the LT predicate on the "created" field. +func CreatedLT(v time.Time) predicate.Schedule { + return predicate.Schedule(sql.FieldLT(FieldCreated, v)) +} + +// CreatedLTE applies the LTE predicate on the "created" field. +func CreatedLTE(v time.Time) predicate.Schedule { + return predicate.Schedule(sql.FieldLTE(FieldCreated, v)) +} + +// UpdatedEQ applies the EQ predicate on the "updated" field. +func UpdatedEQ(v time.Time) predicate.Schedule { + return predicate.Schedule(sql.FieldEQ(FieldUpdated, v)) +} + +// UpdatedNEQ applies the NEQ predicate on the "updated" field. +func UpdatedNEQ(v time.Time) predicate.Schedule { + return predicate.Schedule(sql.FieldNEQ(FieldUpdated, v)) +} + +// UpdatedIn applies the In predicate on the "updated" field. +func UpdatedIn(vs ...time.Time) predicate.Schedule { + return predicate.Schedule(sql.FieldIn(FieldUpdated, vs...)) +} + +// UpdatedNotIn applies the NotIn predicate on the "updated" field. +func UpdatedNotIn(vs ...time.Time) predicate.Schedule { + return predicate.Schedule(sql.FieldNotIn(FieldUpdated, vs...)) +} + +// UpdatedGT applies the GT predicate on the "updated" field. +func UpdatedGT(v time.Time) predicate.Schedule { + return predicate.Schedule(sql.FieldGT(FieldUpdated, v)) +} + +// UpdatedGTE applies the GTE predicate on the "updated" field. +func UpdatedGTE(v time.Time) predicate.Schedule { + return predicate.Schedule(sql.FieldGTE(FieldUpdated, v)) +} + +// UpdatedLT applies the LT predicate on the "updated" field. +func UpdatedLT(v time.Time) predicate.Schedule { + return predicate.Schedule(sql.FieldLT(FieldUpdated, v)) +} + +// UpdatedLTE applies the LTE predicate on the "updated" field. +func UpdatedLTE(v time.Time) predicate.Schedule { + return predicate.Schedule(sql.FieldLTE(FieldUpdated, v)) +} + +// And groups predicates with the AND operator between them. +func And(predicates ...predicate.Schedule) predicate.Schedule { + return predicate.Schedule(sql.AndPredicates(predicates...)) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.Schedule) predicate.Schedule { + return predicate.Schedule(sql.OrPredicates(predicates...)) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.Schedule) predicate.Schedule { + return predicate.Schedule(sql.NotPredicates(p)) +} diff --git a/pkg/ent/schedule_create.go b/pkg/ent/schedule_create.go new file mode 100644 index 000000000..ecc44066c --- /dev/null +++ b/pkg/ent/schedule_create.go @@ -0,0 +1,1469 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/schedule" + "github.com/google/uuid" +) + +// ScheduleCreate is the builder for creating a Schedule entity. +type ScheduleCreate struct { + config + mutation *ScheduleMutation + hooks []Hook + conflict []sql.ConflictOption +} + +// SetProjectID sets the "project_id" field. +func (_c *ScheduleCreate) SetProjectID(v uuid.UUID) *ScheduleCreate { + _c.mutation.SetProjectID(v) + return _c +} + +// SetName sets the "name" field. +func (_c *ScheduleCreate) SetName(v string) *ScheduleCreate { + _c.mutation.SetName(v) + return _c +} + +// SetCronExpr sets the "cron_expr" field. +func (_c *ScheduleCreate) SetCronExpr(v string) *ScheduleCreate { + _c.mutation.SetCronExpr(v) + return _c +} + +// SetEventType sets the "event_type" field. +func (_c *ScheduleCreate) SetEventType(v string) *ScheduleCreate { + _c.mutation.SetEventType(v) + return _c +} + +// SetPayload sets the "payload" field. +func (_c *ScheduleCreate) SetPayload(v string) *ScheduleCreate { + _c.mutation.SetPayload(v) + return _c +} + +// SetNillablePayload sets the "payload" field if the given value is not nil. +func (_c *ScheduleCreate) SetNillablePayload(v *string) *ScheduleCreate { + if v != nil { + _c.SetPayload(*v) + } + return _c +} + +// SetStatus sets the "status" field. +func (_c *ScheduleCreate) SetStatus(v string) *ScheduleCreate { + _c.mutation.SetStatus(v) + return _c +} + +// SetNillableStatus sets the "status" field if the given value is not nil. +func (_c *ScheduleCreate) SetNillableStatus(v *string) *ScheduleCreate { + if v != nil { + _c.SetStatus(*v) + } + return _c +} + +// SetNextRunAt sets the "next_run_at" field. +func (_c *ScheduleCreate) SetNextRunAt(v time.Time) *ScheduleCreate { + _c.mutation.SetNextRunAt(v) + return _c +} + +// SetNillableNextRunAt sets the "next_run_at" field if the given value is not nil. +func (_c *ScheduleCreate) SetNillableNextRunAt(v *time.Time) *ScheduleCreate { + if v != nil { + _c.SetNextRunAt(*v) + } + return _c +} + +// SetLastRunAt sets the "last_run_at" field. +func (_c *ScheduleCreate) SetLastRunAt(v time.Time) *ScheduleCreate { + _c.mutation.SetLastRunAt(v) + return _c +} + +// SetNillableLastRunAt sets the "last_run_at" field if the given value is not nil. +func (_c *ScheduleCreate) SetNillableLastRunAt(v *time.Time) *ScheduleCreate { + if v != nil { + _c.SetLastRunAt(*v) + } + return _c +} + +// SetLastRunStatus sets the "last_run_status" field. +func (_c *ScheduleCreate) SetLastRunStatus(v string) *ScheduleCreate { + _c.mutation.SetLastRunStatus(v) + return _c +} + +// SetNillableLastRunStatus sets the "last_run_status" field if the given value is not nil. +func (_c *ScheduleCreate) SetNillableLastRunStatus(v *string) *ScheduleCreate { + if v != nil { + _c.SetLastRunStatus(*v) + } + return _c +} + +// SetLastRunError sets the "last_run_error" field. +func (_c *ScheduleCreate) SetLastRunError(v string) *ScheduleCreate { + _c.mutation.SetLastRunError(v) + return _c +} + +// SetNillableLastRunError sets the "last_run_error" field if the given value is not nil. +func (_c *ScheduleCreate) SetNillableLastRunError(v *string) *ScheduleCreate { + if v != nil { + _c.SetLastRunError(*v) + } + return _c +} + +// SetRunCount sets the "run_count" field. +func (_c *ScheduleCreate) SetRunCount(v int) *ScheduleCreate { + _c.mutation.SetRunCount(v) + return _c +} + +// SetNillableRunCount sets the "run_count" field if the given value is not nil. +func (_c *ScheduleCreate) SetNillableRunCount(v *int) *ScheduleCreate { + if v != nil { + _c.SetRunCount(*v) + } + return _c +} + +// SetErrorCount sets the "error_count" field. +func (_c *ScheduleCreate) SetErrorCount(v int) *ScheduleCreate { + _c.mutation.SetErrorCount(v) + return _c +} + +// SetNillableErrorCount sets the "error_count" field if the given value is not nil. +func (_c *ScheduleCreate) SetNillableErrorCount(v *int) *ScheduleCreate { + if v != nil { + _c.SetErrorCount(*v) + } + return _c +} + +// SetCreatedBy sets the "created_by" field. +func (_c *ScheduleCreate) SetCreatedBy(v string) *ScheduleCreate { + _c.mutation.SetCreatedBy(v) + return _c +} + +// SetNillableCreatedBy sets the "created_by" field if the given value is not nil. +func (_c *ScheduleCreate) SetNillableCreatedBy(v *string) *ScheduleCreate { + if v != nil { + _c.SetCreatedBy(*v) + } + return _c +} + +// SetCreated sets the "created" field. +func (_c *ScheduleCreate) SetCreated(v time.Time) *ScheduleCreate { + _c.mutation.SetCreated(v) + return _c +} + +// SetNillableCreated sets the "created" field if the given value is not nil. +func (_c *ScheduleCreate) SetNillableCreated(v *time.Time) *ScheduleCreate { + if v != nil { + _c.SetCreated(*v) + } + return _c +} + +// SetUpdated sets the "updated" field. +func (_c *ScheduleCreate) SetUpdated(v time.Time) *ScheduleCreate { + _c.mutation.SetUpdated(v) + return _c +} + +// SetNillableUpdated sets the "updated" field if the given value is not nil. +func (_c *ScheduleCreate) SetNillableUpdated(v *time.Time) *ScheduleCreate { + if v != nil { + _c.SetUpdated(*v) + } + return _c +} + +// SetID sets the "id" field. +func (_c *ScheduleCreate) SetID(v uuid.UUID) *ScheduleCreate { + _c.mutation.SetID(v) + return _c +} + +// SetNillableID sets the "id" field if the given value is not nil. +func (_c *ScheduleCreate) SetNillableID(v *uuid.UUID) *ScheduleCreate { + if v != nil { + _c.SetID(*v) + } + return _c +} + +// Mutation returns the ScheduleMutation object of the builder. +func (_c *ScheduleCreate) Mutation() *ScheduleMutation { + return _c.mutation +} + +// Save creates the Schedule in the database. +func (_c *ScheduleCreate) Save(ctx context.Context) (*Schedule, error) { + _c.defaults() + return withHooks(ctx, _c.sqlSave, _c.mutation, _c.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (_c *ScheduleCreate) SaveX(ctx context.Context) *Schedule { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *ScheduleCreate) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *ScheduleCreate) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_c *ScheduleCreate) defaults() { + if _, ok := _c.mutation.Payload(); !ok { + v := schedule.DefaultPayload + _c.mutation.SetPayload(v) + } + if _, ok := _c.mutation.Status(); !ok { + v := schedule.DefaultStatus + _c.mutation.SetStatus(v) + } + if _, ok := _c.mutation.RunCount(); !ok { + v := schedule.DefaultRunCount + _c.mutation.SetRunCount(v) + } + if _, ok := _c.mutation.ErrorCount(); !ok { + v := schedule.DefaultErrorCount + _c.mutation.SetErrorCount(v) + } + if _, ok := _c.mutation.Created(); !ok { + v := schedule.DefaultCreated() + _c.mutation.SetCreated(v) + } + if _, ok := _c.mutation.Updated(); !ok { + v := schedule.DefaultUpdated() + _c.mutation.SetUpdated(v) + } + if _, ok := _c.mutation.ID(); !ok { + v := schedule.DefaultID() + _c.mutation.SetID(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_c *ScheduleCreate) check() error { + if _, ok := _c.mutation.ProjectID(); !ok { + return &ValidationError{Name: "project_id", err: errors.New(`ent: missing required field "Schedule.project_id"`)} + } + if _, ok := _c.mutation.Name(); !ok { + return &ValidationError{Name: "name", err: errors.New(`ent: missing required field "Schedule.name"`)} + } + if v, ok := _c.mutation.Name(); ok { + if err := schedule.NameValidator(v); err != nil { + return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "Schedule.name": %w`, err)} + } + } + if _, ok := _c.mutation.CronExpr(); !ok { + return &ValidationError{Name: "cron_expr", err: errors.New(`ent: missing required field "Schedule.cron_expr"`)} + } + if v, ok := _c.mutation.CronExpr(); ok { + if err := schedule.CronExprValidator(v); err != nil { + return &ValidationError{Name: "cron_expr", err: fmt.Errorf(`ent: validator failed for field "Schedule.cron_expr": %w`, err)} + } + } + if _, ok := _c.mutation.EventType(); !ok { + return &ValidationError{Name: "event_type", err: errors.New(`ent: missing required field "Schedule.event_type"`)} + } + if v, ok := _c.mutation.EventType(); ok { + if err := schedule.EventTypeValidator(v); err != nil { + return &ValidationError{Name: "event_type", err: fmt.Errorf(`ent: validator failed for field "Schedule.event_type": %w`, err)} + } + } + if _, ok := _c.mutation.Payload(); !ok { + return &ValidationError{Name: "payload", err: errors.New(`ent: missing required field "Schedule.payload"`)} + } + if _, ok := _c.mutation.Status(); !ok { + return &ValidationError{Name: "status", err: errors.New(`ent: missing required field "Schedule.status"`)} + } + if _, ok := _c.mutation.RunCount(); !ok { + return &ValidationError{Name: "run_count", err: errors.New(`ent: missing required field "Schedule.run_count"`)} + } + if _, ok := _c.mutation.ErrorCount(); !ok { + return &ValidationError{Name: "error_count", err: errors.New(`ent: missing required field "Schedule.error_count"`)} + } + if _, ok := _c.mutation.Created(); !ok { + return &ValidationError{Name: "created", err: errors.New(`ent: missing required field "Schedule.created"`)} + } + if _, ok := _c.mutation.Updated(); !ok { + return &ValidationError{Name: "updated", err: errors.New(`ent: missing required field "Schedule.updated"`)} + } + return nil +} + +func (_c *ScheduleCreate) sqlSave(ctx context.Context) (*Schedule, error) { + if err := _c.check(); err != nil { + return nil, err + } + _node, _spec := _c.createSpec() + if err := sqlgraph.CreateNode(ctx, _c.driver, _spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + if _spec.ID.Value != nil { + if id, ok := _spec.ID.Value.(*uuid.UUID); ok { + _node.ID = *id + } else if err := _node.ID.Scan(_spec.ID.Value); err != nil { + return nil, err + } + } + _c.mutation.id = &_node.ID + _c.mutation.done = true + return _node, nil +} + +func (_c *ScheduleCreate) createSpec() (*Schedule, *sqlgraph.CreateSpec) { + var ( + _node = &Schedule{config: _c.config} + _spec = sqlgraph.NewCreateSpec(schedule.Table, sqlgraph.NewFieldSpec(schedule.FieldID, field.TypeUUID)) + ) + _spec.OnConflict = _c.conflict + if id, ok := _c.mutation.ID(); ok { + _node.ID = id + _spec.ID.Value = &id + } + if value, ok := _c.mutation.ProjectID(); ok { + _spec.SetField(schedule.FieldProjectID, field.TypeUUID, value) + _node.ProjectID = value + } + if value, ok := _c.mutation.Name(); ok { + _spec.SetField(schedule.FieldName, field.TypeString, value) + _node.Name = value + } + if value, ok := _c.mutation.CronExpr(); ok { + _spec.SetField(schedule.FieldCronExpr, field.TypeString, value) + _node.CronExpr = value + } + if value, ok := _c.mutation.EventType(); ok { + _spec.SetField(schedule.FieldEventType, field.TypeString, value) + _node.EventType = value + } + if value, ok := _c.mutation.Payload(); ok { + _spec.SetField(schedule.FieldPayload, field.TypeString, value) + _node.Payload = value + } + if value, ok := _c.mutation.Status(); ok { + _spec.SetField(schedule.FieldStatus, field.TypeString, value) + _node.Status = value + } + if value, ok := _c.mutation.NextRunAt(); ok { + _spec.SetField(schedule.FieldNextRunAt, field.TypeTime, value) + _node.NextRunAt = &value + } + if value, ok := _c.mutation.LastRunAt(); ok { + _spec.SetField(schedule.FieldLastRunAt, field.TypeTime, value) + _node.LastRunAt = &value + } + if value, ok := _c.mutation.LastRunStatus(); ok { + _spec.SetField(schedule.FieldLastRunStatus, field.TypeString, value) + _node.LastRunStatus = value + } + if value, ok := _c.mutation.LastRunError(); ok { + _spec.SetField(schedule.FieldLastRunError, field.TypeString, value) + _node.LastRunError = value + } + if value, ok := _c.mutation.RunCount(); ok { + _spec.SetField(schedule.FieldRunCount, field.TypeInt, value) + _node.RunCount = value + } + if value, ok := _c.mutation.ErrorCount(); ok { + _spec.SetField(schedule.FieldErrorCount, field.TypeInt, value) + _node.ErrorCount = value + } + if value, ok := _c.mutation.CreatedBy(); ok { + _spec.SetField(schedule.FieldCreatedBy, field.TypeString, value) + _node.CreatedBy = value + } + if value, ok := _c.mutation.Created(); ok { + _spec.SetField(schedule.FieldCreated, field.TypeTime, value) + _node.Created = value + } + if value, ok := _c.mutation.Updated(); ok { + _spec.SetField(schedule.FieldUpdated, field.TypeTime, value) + _node.Updated = value + } + return _node, _spec +} + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.Schedule.Create(). +// SetProjectID(v). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.ScheduleUpsert) { +// SetProjectID(v+v). +// }). +// Exec(ctx) +func (_c *ScheduleCreate) OnConflict(opts ...sql.ConflictOption) *ScheduleUpsertOne { + _c.conflict = opts + return &ScheduleUpsertOne{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.Schedule.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *ScheduleCreate) OnConflictColumns(columns ...string) *ScheduleUpsertOne { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &ScheduleUpsertOne{ + create: _c, + } +} + +type ( + // ScheduleUpsertOne is the builder for "upsert"-ing + // one Schedule node. + ScheduleUpsertOne struct { + create *ScheduleCreate + } + + // ScheduleUpsert is the "OnConflict" setter. + ScheduleUpsert struct { + *sql.UpdateSet + } +) + +// SetProjectID sets the "project_id" field. +func (u *ScheduleUpsert) SetProjectID(v uuid.UUID) *ScheduleUpsert { + u.Set(schedule.FieldProjectID, v) + return u +} + +// UpdateProjectID sets the "project_id" field to the value that was provided on create. +func (u *ScheduleUpsert) UpdateProjectID() *ScheduleUpsert { + u.SetExcluded(schedule.FieldProjectID) + return u +} + +// SetName sets the "name" field. +func (u *ScheduleUpsert) SetName(v string) *ScheduleUpsert { + u.Set(schedule.FieldName, v) + return u +} + +// UpdateName sets the "name" field to the value that was provided on create. +func (u *ScheduleUpsert) UpdateName() *ScheduleUpsert { + u.SetExcluded(schedule.FieldName) + return u +} + +// SetCronExpr sets the "cron_expr" field. +func (u *ScheduleUpsert) SetCronExpr(v string) *ScheduleUpsert { + u.Set(schedule.FieldCronExpr, v) + return u +} + +// UpdateCronExpr sets the "cron_expr" field to the value that was provided on create. +func (u *ScheduleUpsert) UpdateCronExpr() *ScheduleUpsert { + u.SetExcluded(schedule.FieldCronExpr) + return u +} + +// SetEventType sets the "event_type" field. +func (u *ScheduleUpsert) SetEventType(v string) *ScheduleUpsert { + u.Set(schedule.FieldEventType, v) + return u +} + +// UpdateEventType sets the "event_type" field to the value that was provided on create. +func (u *ScheduleUpsert) UpdateEventType() *ScheduleUpsert { + u.SetExcluded(schedule.FieldEventType) + return u +} + +// SetPayload sets the "payload" field. +func (u *ScheduleUpsert) SetPayload(v string) *ScheduleUpsert { + u.Set(schedule.FieldPayload, v) + return u +} + +// UpdatePayload sets the "payload" field to the value that was provided on create. +func (u *ScheduleUpsert) UpdatePayload() *ScheduleUpsert { + u.SetExcluded(schedule.FieldPayload) + return u +} + +// SetStatus sets the "status" field. +func (u *ScheduleUpsert) SetStatus(v string) *ScheduleUpsert { + u.Set(schedule.FieldStatus, v) + return u +} + +// UpdateStatus sets the "status" field to the value that was provided on create. +func (u *ScheduleUpsert) UpdateStatus() *ScheduleUpsert { + u.SetExcluded(schedule.FieldStatus) + return u +} + +// SetNextRunAt sets the "next_run_at" field. +func (u *ScheduleUpsert) SetNextRunAt(v time.Time) *ScheduleUpsert { + u.Set(schedule.FieldNextRunAt, v) + return u +} + +// UpdateNextRunAt sets the "next_run_at" field to the value that was provided on create. +func (u *ScheduleUpsert) UpdateNextRunAt() *ScheduleUpsert { + u.SetExcluded(schedule.FieldNextRunAt) + return u +} + +// ClearNextRunAt clears the value of the "next_run_at" field. +func (u *ScheduleUpsert) ClearNextRunAt() *ScheduleUpsert { + u.SetNull(schedule.FieldNextRunAt) + return u +} + +// SetLastRunAt sets the "last_run_at" field. +func (u *ScheduleUpsert) SetLastRunAt(v time.Time) *ScheduleUpsert { + u.Set(schedule.FieldLastRunAt, v) + return u +} + +// UpdateLastRunAt sets the "last_run_at" field to the value that was provided on create. +func (u *ScheduleUpsert) UpdateLastRunAt() *ScheduleUpsert { + u.SetExcluded(schedule.FieldLastRunAt) + return u +} + +// ClearLastRunAt clears the value of the "last_run_at" field. +func (u *ScheduleUpsert) ClearLastRunAt() *ScheduleUpsert { + u.SetNull(schedule.FieldLastRunAt) + return u +} + +// SetLastRunStatus sets the "last_run_status" field. +func (u *ScheduleUpsert) SetLastRunStatus(v string) *ScheduleUpsert { + u.Set(schedule.FieldLastRunStatus, v) + return u +} + +// UpdateLastRunStatus sets the "last_run_status" field to the value that was provided on create. +func (u *ScheduleUpsert) UpdateLastRunStatus() *ScheduleUpsert { + u.SetExcluded(schedule.FieldLastRunStatus) + return u +} + +// ClearLastRunStatus clears the value of the "last_run_status" field. +func (u *ScheduleUpsert) ClearLastRunStatus() *ScheduleUpsert { + u.SetNull(schedule.FieldLastRunStatus) + return u +} + +// SetLastRunError sets the "last_run_error" field. +func (u *ScheduleUpsert) SetLastRunError(v string) *ScheduleUpsert { + u.Set(schedule.FieldLastRunError, v) + return u +} + +// UpdateLastRunError sets the "last_run_error" field to the value that was provided on create. +func (u *ScheduleUpsert) UpdateLastRunError() *ScheduleUpsert { + u.SetExcluded(schedule.FieldLastRunError) + return u +} + +// ClearLastRunError clears the value of the "last_run_error" field. +func (u *ScheduleUpsert) ClearLastRunError() *ScheduleUpsert { + u.SetNull(schedule.FieldLastRunError) + return u +} + +// SetRunCount sets the "run_count" field. +func (u *ScheduleUpsert) SetRunCount(v int) *ScheduleUpsert { + u.Set(schedule.FieldRunCount, v) + return u +} + +// UpdateRunCount sets the "run_count" field to the value that was provided on create. +func (u *ScheduleUpsert) UpdateRunCount() *ScheduleUpsert { + u.SetExcluded(schedule.FieldRunCount) + return u +} + +// AddRunCount adds v to the "run_count" field. +func (u *ScheduleUpsert) AddRunCount(v int) *ScheduleUpsert { + u.Add(schedule.FieldRunCount, v) + return u +} + +// SetErrorCount sets the "error_count" field. +func (u *ScheduleUpsert) SetErrorCount(v int) *ScheduleUpsert { + u.Set(schedule.FieldErrorCount, v) + return u +} + +// UpdateErrorCount sets the "error_count" field to the value that was provided on create. +func (u *ScheduleUpsert) UpdateErrorCount() *ScheduleUpsert { + u.SetExcluded(schedule.FieldErrorCount) + return u +} + +// AddErrorCount adds v to the "error_count" field. +func (u *ScheduleUpsert) AddErrorCount(v int) *ScheduleUpsert { + u.Add(schedule.FieldErrorCount, v) + return u +} + +// SetCreatedBy sets the "created_by" field. +func (u *ScheduleUpsert) SetCreatedBy(v string) *ScheduleUpsert { + u.Set(schedule.FieldCreatedBy, v) + return u +} + +// UpdateCreatedBy sets the "created_by" field to the value that was provided on create. +func (u *ScheduleUpsert) UpdateCreatedBy() *ScheduleUpsert { + u.SetExcluded(schedule.FieldCreatedBy) + return u +} + +// ClearCreatedBy clears the value of the "created_by" field. +func (u *ScheduleUpsert) ClearCreatedBy() *ScheduleUpsert { + u.SetNull(schedule.FieldCreatedBy) + return u +} + +// SetUpdated sets the "updated" field. +func (u *ScheduleUpsert) SetUpdated(v time.Time) *ScheduleUpsert { + u.Set(schedule.FieldUpdated, v) + return u +} + +// UpdateUpdated sets the "updated" field to the value that was provided on create. +func (u *ScheduleUpsert) UpdateUpdated() *ScheduleUpsert { + u.SetExcluded(schedule.FieldUpdated) + return u +} + +// UpdateNewValues updates the mutable fields using the new values that were set on create except the ID field. +// Using this option is equivalent to using: +// +// client.Schedule.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(schedule.FieldID) +// }), +// ). +// Exec(ctx) +func (u *ScheduleUpsertOne) UpdateNewValues() *ScheduleUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + if _, exists := u.create.mutation.ID(); exists { + s.SetIgnore(schedule.FieldID) + } + if _, exists := u.create.mutation.Created(); exists { + s.SetIgnore(schedule.FieldCreated) + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.Schedule.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *ScheduleUpsertOne) Ignore() *ScheduleUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *ScheduleUpsertOne) DoNothing() *ScheduleUpsertOne { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the ScheduleCreate.OnConflict +// documentation for more info. +func (u *ScheduleUpsertOne) Update(set func(*ScheduleUpsert)) *ScheduleUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&ScheduleUpsert{UpdateSet: update}) + })) + return u +} + +// SetProjectID sets the "project_id" field. +func (u *ScheduleUpsertOne) SetProjectID(v uuid.UUID) *ScheduleUpsertOne { + return u.Update(func(s *ScheduleUpsert) { + s.SetProjectID(v) + }) +} + +// UpdateProjectID sets the "project_id" field to the value that was provided on create. +func (u *ScheduleUpsertOne) UpdateProjectID() *ScheduleUpsertOne { + return u.Update(func(s *ScheduleUpsert) { + s.UpdateProjectID() + }) +} + +// SetName sets the "name" field. +func (u *ScheduleUpsertOne) SetName(v string) *ScheduleUpsertOne { + return u.Update(func(s *ScheduleUpsert) { + s.SetName(v) + }) +} + +// UpdateName sets the "name" field to the value that was provided on create. +func (u *ScheduleUpsertOne) UpdateName() *ScheduleUpsertOne { + return u.Update(func(s *ScheduleUpsert) { + s.UpdateName() + }) +} + +// SetCronExpr sets the "cron_expr" field. +func (u *ScheduleUpsertOne) SetCronExpr(v string) *ScheduleUpsertOne { + return u.Update(func(s *ScheduleUpsert) { + s.SetCronExpr(v) + }) +} + +// UpdateCronExpr sets the "cron_expr" field to the value that was provided on create. +func (u *ScheduleUpsertOne) UpdateCronExpr() *ScheduleUpsertOne { + return u.Update(func(s *ScheduleUpsert) { + s.UpdateCronExpr() + }) +} + +// SetEventType sets the "event_type" field. +func (u *ScheduleUpsertOne) SetEventType(v string) *ScheduleUpsertOne { + return u.Update(func(s *ScheduleUpsert) { + s.SetEventType(v) + }) +} + +// UpdateEventType sets the "event_type" field to the value that was provided on create. +func (u *ScheduleUpsertOne) UpdateEventType() *ScheduleUpsertOne { + return u.Update(func(s *ScheduleUpsert) { + s.UpdateEventType() + }) +} + +// SetPayload sets the "payload" field. +func (u *ScheduleUpsertOne) SetPayload(v string) *ScheduleUpsertOne { + return u.Update(func(s *ScheduleUpsert) { + s.SetPayload(v) + }) +} + +// UpdatePayload sets the "payload" field to the value that was provided on create. +func (u *ScheduleUpsertOne) UpdatePayload() *ScheduleUpsertOne { + return u.Update(func(s *ScheduleUpsert) { + s.UpdatePayload() + }) +} + +// SetStatus sets the "status" field. +func (u *ScheduleUpsertOne) SetStatus(v string) *ScheduleUpsertOne { + return u.Update(func(s *ScheduleUpsert) { + s.SetStatus(v) + }) +} + +// UpdateStatus sets the "status" field to the value that was provided on create. +func (u *ScheduleUpsertOne) UpdateStatus() *ScheduleUpsertOne { + return u.Update(func(s *ScheduleUpsert) { + s.UpdateStatus() + }) +} + +// SetNextRunAt sets the "next_run_at" field. +func (u *ScheduleUpsertOne) SetNextRunAt(v time.Time) *ScheduleUpsertOne { + return u.Update(func(s *ScheduleUpsert) { + s.SetNextRunAt(v) + }) +} + +// UpdateNextRunAt sets the "next_run_at" field to the value that was provided on create. +func (u *ScheduleUpsertOne) UpdateNextRunAt() *ScheduleUpsertOne { + return u.Update(func(s *ScheduleUpsert) { + s.UpdateNextRunAt() + }) +} + +// ClearNextRunAt clears the value of the "next_run_at" field. +func (u *ScheduleUpsertOne) ClearNextRunAt() *ScheduleUpsertOne { + return u.Update(func(s *ScheduleUpsert) { + s.ClearNextRunAt() + }) +} + +// SetLastRunAt sets the "last_run_at" field. +func (u *ScheduleUpsertOne) SetLastRunAt(v time.Time) *ScheduleUpsertOne { + return u.Update(func(s *ScheduleUpsert) { + s.SetLastRunAt(v) + }) +} + +// UpdateLastRunAt sets the "last_run_at" field to the value that was provided on create. +func (u *ScheduleUpsertOne) UpdateLastRunAt() *ScheduleUpsertOne { + return u.Update(func(s *ScheduleUpsert) { + s.UpdateLastRunAt() + }) +} + +// ClearLastRunAt clears the value of the "last_run_at" field. +func (u *ScheduleUpsertOne) ClearLastRunAt() *ScheduleUpsertOne { + return u.Update(func(s *ScheduleUpsert) { + s.ClearLastRunAt() + }) +} + +// SetLastRunStatus sets the "last_run_status" field. +func (u *ScheduleUpsertOne) SetLastRunStatus(v string) *ScheduleUpsertOne { + return u.Update(func(s *ScheduleUpsert) { + s.SetLastRunStatus(v) + }) +} + +// UpdateLastRunStatus sets the "last_run_status" field to the value that was provided on create. +func (u *ScheduleUpsertOne) UpdateLastRunStatus() *ScheduleUpsertOne { + return u.Update(func(s *ScheduleUpsert) { + s.UpdateLastRunStatus() + }) +} + +// ClearLastRunStatus clears the value of the "last_run_status" field. +func (u *ScheduleUpsertOne) ClearLastRunStatus() *ScheduleUpsertOne { + return u.Update(func(s *ScheduleUpsert) { + s.ClearLastRunStatus() + }) +} + +// SetLastRunError sets the "last_run_error" field. +func (u *ScheduleUpsertOne) SetLastRunError(v string) *ScheduleUpsertOne { + return u.Update(func(s *ScheduleUpsert) { + s.SetLastRunError(v) + }) +} + +// UpdateLastRunError sets the "last_run_error" field to the value that was provided on create. +func (u *ScheduleUpsertOne) UpdateLastRunError() *ScheduleUpsertOne { + return u.Update(func(s *ScheduleUpsert) { + s.UpdateLastRunError() + }) +} + +// ClearLastRunError clears the value of the "last_run_error" field. +func (u *ScheduleUpsertOne) ClearLastRunError() *ScheduleUpsertOne { + return u.Update(func(s *ScheduleUpsert) { + s.ClearLastRunError() + }) +} + +// SetRunCount sets the "run_count" field. +func (u *ScheduleUpsertOne) SetRunCount(v int) *ScheduleUpsertOne { + return u.Update(func(s *ScheduleUpsert) { + s.SetRunCount(v) + }) +} + +// AddRunCount adds v to the "run_count" field. +func (u *ScheduleUpsertOne) AddRunCount(v int) *ScheduleUpsertOne { + return u.Update(func(s *ScheduleUpsert) { + s.AddRunCount(v) + }) +} + +// UpdateRunCount sets the "run_count" field to the value that was provided on create. +func (u *ScheduleUpsertOne) UpdateRunCount() *ScheduleUpsertOne { + return u.Update(func(s *ScheduleUpsert) { + s.UpdateRunCount() + }) +} + +// SetErrorCount sets the "error_count" field. +func (u *ScheduleUpsertOne) SetErrorCount(v int) *ScheduleUpsertOne { + return u.Update(func(s *ScheduleUpsert) { + s.SetErrorCount(v) + }) +} + +// AddErrorCount adds v to the "error_count" field. +func (u *ScheduleUpsertOne) AddErrorCount(v int) *ScheduleUpsertOne { + return u.Update(func(s *ScheduleUpsert) { + s.AddErrorCount(v) + }) +} + +// UpdateErrorCount sets the "error_count" field to the value that was provided on create. +func (u *ScheduleUpsertOne) UpdateErrorCount() *ScheduleUpsertOne { + return u.Update(func(s *ScheduleUpsert) { + s.UpdateErrorCount() + }) +} + +// SetCreatedBy sets the "created_by" field. +func (u *ScheduleUpsertOne) SetCreatedBy(v string) *ScheduleUpsertOne { + return u.Update(func(s *ScheduleUpsert) { + s.SetCreatedBy(v) + }) +} + +// UpdateCreatedBy sets the "created_by" field to the value that was provided on create. +func (u *ScheduleUpsertOne) UpdateCreatedBy() *ScheduleUpsertOne { + return u.Update(func(s *ScheduleUpsert) { + s.UpdateCreatedBy() + }) +} + +// ClearCreatedBy clears the value of the "created_by" field. +func (u *ScheduleUpsertOne) ClearCreatedBy() *ScheduleUpsertOne { + return u.Update(func(s *ScheduleUpsert) { + s.ClearCreatedBy() + }) +} + +// SetUpdated sets the "updated" field. +func (u *ScheduleUpsertOne) SetUpdated(v time.Time) *ScheduleUpsertOne { + return u.Update(func(s *ScheduleUpsert) { + s.SetUpdated(v) + }) +} + +// UpdateUpdated sets the "updated" field to the value that was provided on create. +func (u *ScheduleUpsertOne) UpdateUpdated() *ScheduleUpsertOne { + return u.Update(func(s *ScheduleUpsert) { + s.UpdateUpdated() + }) +} + +// Exec executes the query. +func (u *ScheduleUpsertOne) Exec(ctx context.Context) error { + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for ScheduleCreate.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *ScheduleUpsertOne) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} + +// Exec executes the UPSERT query and returns the inserted/updated ID. +func (u *ScheduleUpsertOne) ID(ctx context.Context) (id uuid.UUID, err error) { + if u.create.driver.Dialect() == dialect.MySQL { + // In case of "ON CONFLICT", there is no way to get back non-numeric ID + // fields from the database since MySQL does not support the RETURNING clause. + return id, errors.New("ent: ScheduleUpsertOne.ID is not supported by MySQL driver. Use ScheduleUpsertOne.Exec instead") + } + node, err := u.create.Save(ctx) + if err != nil { + return id, err + } + return node.ID, nil +} + +// IDX is like ID, but panics if an error occurs. +func (u *ScheduleUpsertOne) IDX(ctx context.Context) uuid.UUID { + id, err := u.ID(ctx) + if err != nil { + panic(err) + } + return id +} + +// ScheduleCreateBulk is the builder for creating many Schedule entities in bulk. +type ScheduleCreateBulk struct { + config + err error + builders []*ScheduleCreate + conflict []sql.ConflictOption +} + +// Save creates the Schedule entities in the database. +func (_c *ScheduleCreateBulk) Save(ctx context.Context) ([]*Schedule, error) { + if _c.err != nil { + return nil, _c.err + } + specs := make([]*sqlgraph.CreateSpec, len(_c.builders)) + nodes := make([]*Schedule, len(_c.builders)) + mutators := make([]Mutator, len(_c.builders)) + for i := range _c.builders { + func(i int, root context.Context) { + builder := _c.builders[i] + builder.defaults() + var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { + mutation, ok := m.(*ScheduleMutation) + if !ok { + return nil, fmt.Errorf("unexpected mutation type %T", m) + } + if err := builder.check(); err != nil { + return nil, err + } + builder.mutation = mutation + var err error + nodes[i], specs[i] = builder.createSpec() + if i < len(mutators)-1 { + _, err = mutators[i+1].Mutate(root, _c.builders[i+1].mutation) + } else { + spec := &sqlgraph.BatchCreateSpec{Nodes: specs} + spec.OnConflict = _c.conflict + // Invoke the actual operation on the latest mutation in the chain. + if err = sqlgraph.BatchCreate(ctx, _c.driver, spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + } + } + if err != nil { + return nil, err + } + mutation.id = &nodes[i].ID + mutation.done = true + return nodes[i], nil + }) + for i := len(builder.hooks) - 1; i >= 0; i-- { + mut = builder.hooks[i](mut) + } + mutators[i] = mut + }(i, ctx) + } + if len(mutators) > 0 { + if _, err := mutators[0].Mutate(ctx, _c.builders[0].mutation); err != nil { + return nil, err + } + } + return nodes, nil +} + +// SaveX is like Save, but panics if an error occurs. +func (_c *ScheduleCreateBulk) SaveX(ctx context.Context) []*Schedule { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *ScheduleCreateBulk) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *ScheduleCreateBulk) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.Schedule.CreateBulk(builders...). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.ScheduleUpsert) { +// SetProjectID(v+v). +// }). +// Exec(ctx) +func (_c *ScheduleCreateBulk) OnConflict(opts ...sql.ConflictOption) *ScheduleUpsertBulk { + _c.conflict = opts + return &ScheduleUpsertBulk{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.Schedule.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *ScheduleCreateBulk) OnConflictColumns(columns ...string) *ScheduleUpsertBulk { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &ScheduleUpsertBulk{ + create: _c, + } +} + +// ScheduleUpsertBulk is the builder for "upsert"-ing +// a bulk of Schedule nodes. +type ScheduleUpsertBulk struct { + create *ScheduleCreateBulk +} + +// UpdateNewValues updates the mutable fields using the new values that +// were set on create. Using this option is equivalent to using: +// +// client.Schedule.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(schedule.FieldID) +// }), +// ). +// Exec(ctx) +func (u *ScheduleUpsertBulk) UpdateNewValues() *ScheduleUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + for _, b := range u.create.builders { + if _, exists := b.mutation.ID(); exists { + s.SetIgnore(schedule.FieldID) + } + if _, exists := b.mutation.Created(); exists { + s.SetIgnore(schedule.FieldCreated) + } + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.Schedule.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *ScheduleUpsertBulk) Ignore() *ScheduleUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *ScheduleUpsertBulk) DoNothing() *ScheduleUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the ScheduleCreateBulk.OnConflict +// documentation for more info. +func (u *ScheduleUpsertBulk) Update(set func(*ScheduleUpsert)) *ScheduleUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&ScheduleUpsert{UpdateSet: update}) + })) + return u +} + +// SetProjectID sets the "project_id" field. +func (u *ScheduleUpsertBulk) SetProjectID(v uuid.UUID) *ScheduleUpsertBulk { + return u.Update(func(s *ScheduleUpsert) { + s.SetProjectID(v) + }) +} + +// UpdateProjectID sets the "project_id" field to the value that was provided on create. +func (u *ScheduleUpsertBulk) UpdateProjectID() *ScheduleUpsertBulk { + return u.Update(func(s *ScheduleUpsert) { + s.UpdateProjectID() + }) +} + +// SetName sets the "name" field. +func (u *ScheduleUpsertBulk) SetName(v string) *ScheduleUpsertBulk { + return u.Update(func(s *ScheduleUpsert) { + s.SetName(v) + }) +} + +// UpdateName sets the "name" field to the value that was provided on create. +func (u *ScheduleUpsertBulk) UpdateName() *ScheduleUpsertBulk { + return u.Update(func(s *ScheduleUpsert) { + s.UpdateName() + }) +} + +// SetCronExpr sets the "cron_expr" field. +func (u *ScheduleUpsertBulk) SetCronExpr(v string) *ScheduleUpsertBulk { + return u.Update(func(s *ScheduleUpsert) { + s.SetCronExpr(v) + }) +} + +// UpdateCronExpr sets the "cron_expr" field to the value that was provided on create. +func (u *ScheduleUpsertBulk) UpdateCronExpr() *ScheduleUpsertBulk { + return u.Update(func(s *ScheduleUpsert) { + s.UpdateCronExpr() + }) +} + +// SetEventType sets the "event_type" field. +func (u *ScheduleUpsertBulk) SetEventType(v string) *ScheduleUpsertBulk { + return u.Update(func(s *ScheduleUpsert) { + s.SetEventType(v) + }) +} + +// UpdateEventType sets the "event_type" field to the value that was provided on create. +func (u *ScheduleUpsertBulk) UpdateEventType() *ScheduleUpsertBulk { + return u.Update(func(s *ScheduleUpsert) { + s.UpdateEventType() + }) +} + +// SetPayload sets the "payload" field. +func (u *ScheduleUpsertBulk) SetPayload(v string) *ScheduleUpsertBulk { + return u.Update(func(s *ScheduleUpsert) { + s.SetPayload(v) + }) +} + +// UpdatePayload sets the "payload" field to the value that was provided on create. +func (u *ScheduleUpsertBulk) UpdatePayload() *ScheduleUpsertBulk { + return u.Update(func(s *ScheduleUpsert) { + s.UpdatePayload() + }) +} + +// SetStatus sets the "status" field. +func (u *ScheduleUpsertBulk) SetStatus(v string) *ScheduleUpsertBulk { + return u.Update(func(s *ScheduleUpsert) { + s.SetStatus(v) + }) +} + +// UpdateStatus sets the "status" field to the value that was provided on create. +func (u *ScheduleUpsertBulk) UpdateStatus() *ScheduleUpsertBulk { + return u.Update(func(s *ScheduleUpsert) { + s.UpdateStatus() + }) +} + +// SetNextRunAt sets the "next_run_at" field. +func (u *ScheduleUpsertBulk) SetNextRunAt(v time.Time) *ScheduleUpsertBulk { + return u.Update(func(s *ScheduleUpsert) { + s.SetNextRunAt(v) + }) +} + +// UpdateNextRunAt sets the "next_run_at" field to the value that was provided on create. +func (u *ScheduleUpsertBulk) UpdateNextRunAt() *ScheduleUpsertBulk { + return u.Update(func(s *ScheduleUpsert) { + s.UpdateNextRunAt() + }) +} + +// ClearNextRunAt clears the value of the "next_run_at" field. +func (u *ScheduleUpsertBulk) ClearNextRunAt() *ScheduleUpsertBulk { + return u.Update(func(s *ScheduleUpsert) { + s.ClearNextRunAt() + }) +} + +// SetLastRunAt sets the "last_run_at" field. +func (u *ScheduleUpsertBulk) SetLastRunAt(v time.Time) *ScheduleUpsertBulk { + return u.Update(func(s *ScheduleUpsert) { + s.SetLastRunAt(v) + }) +} + +// UpdateLastRunAt sets the "last_run_at" field to the value that was provided on create. +func (u *ScheduleUpsertBulk) UpdateLastRunAt() *ScheduleUpsertBulk { + return u.Update(func(s *ScheduleUpsert) { + s.UpdateLastRunAt() + }) +} + +// ClearLastRunAt clears the value of the "last_run_at" field. +func (u *ScheduleUpsertBulk) ClearLastRunAt() *ScheduleUpsertBulk { + return u.Update(func(s *ScheduleUpsert) { + s.ClearLastRunAt() + }) +} + +// SetLastRunStatus sets the "last_run_status" field. +func (u *ScheduleUpsertBulk) SetLastRunStatus(v string) *ScheduleUpsertBulk { + return u.Update(func(s *ScheduleUpsert) { + s.SetLastRunStatus(v) + }) +} + +// UpdateLastRunStatus sets the "last_run_status" field to the value that was provided on create. +func (u *ScheduleUpsertBulk) UpdateLastRunStatus() *ScheduleUpsertBulk { + return u.Update(func(s *ScheduleUpsert) { + s.UpdateLastRunStatus() + }) +} + +// ClearLastRunStatus clears the value of the "last_run_status" field. +func (u *ScheduleUpsertBulk) ClearLastRunStatus() *ScheduleUpsertBulk { + return u.Update(func(s *ScheduleUpsert) { + s.ClearLastRunStatus() + }) +} + +// SetLastRunError sets the "last_run_error" field. +func (u *ScheduleUpsertBulk) SetLastRunError(v string) *ScheduleUpsertBulk { + return u.Update(func(s *ScheduleUpsert) { + s.SetLastRunError(v) + }) +} + +// UpdateLastRunError sets the "last_run_error" field to the value that was provided on create. +func (u *ScheduleUpsertBulk) UpdateLastRunError() *ScheduleUpsertBulk { + return u.Update(func(s *ScheduleUpsert) { + s.UpdateLastRunError() + }) +} + +// ClearLastRunError clears the value of the "last_run_error" field. +func (u *ScheduleUpsertBulk) ClearLastRunError() *ScheduleUpsertBulk { + return u.Update(func(s *ScheduleUpsert) { + s.ClearLastRunError() + }) +} + +// SetRunCount sets the "run_count" field. +func (u *ScheduleUpsertBulk) SetRunCount(v int) *ScheduleUpsertBulk { + return u.Update(func(s *ScheduleUpsert) { + s.SetRunCount(v) + }) +} + +// AddRunCount adds v to the "run_count" field. +func (u *ScheduleUpsertBulk) AddRunCount(v int) *ScheduleUpsertBulk { + return u.Update(func(s *ScheduleUpsert) { + s.AddRunCount(v) + }) +} + +// UpdateRunCount sets the "run_count" field to the value that was provided on create. +func (u *ScheduleUpsertBulk) UpdateRunCount() *ScheduleUpsertBulk { + return u.Update(func(s *ScheduleUpsert) { + s.UpdateRunCount() + }) +} + +// SetErrorCount sets the "error_count" field. +func (u *ScheduleUpsertBulk) SetErrorCount(v int) *ScheduleUpsertBulk { + return u.Update(func(s *ScheduleUpsert) { + s.SetErrorCount(v) + }) +} + +// AddErrorCount adds v to the "error_count" field. +func (u *ScheduleUpsertBulk) AddErrorCount(v int) *ScheduleUpsertBulk { + return u.Update(func(s *ScheduleUpsert) { + s.AddErrorCount(v) + }) +} + +// UpdateErrorCount sets the "error_count" field to the value that was provided on create. +func (u *ScheduleUpsertBulk) UpdateErrorCount() *ScheduleUpsertBulk { + return u.Update(func(s *ScheduleUpsert) { + s.UpdateErrorCount() + }) +} + +// SetCreatedBy sets the "created_by" field. +func (u *ScheduleUpsertBulk) SetCreatedBy(v string) *ScheduleUpsertBulk { + return u.Update(func(s *ScheduleUpsert) { + s.SetCreatedBy(v) + }) +} + +// UpdateCreatedBy sets the "created_by" field to the value that was provided on create. +func (u *ScheduleUpsertBulk) UpdateCreatedBy() *ScheduleUpsertBulk { + return u.Update(func(s *ScheduleUpsert) { + s.UpdateCreatedBy() + }) +} + +// ClearCreatedBy clears the value of the "created_by" field. +func (u *ScheduleUpsertBulk) ClearCreatedBy() *ScheduleUpsertBulk { + return u.Update(func(s *ScheduleUpsert) { + s.ClearCreatedBy() + }) +} + +// SetUpdated sets the "updated" field. +func (u *ScheduleUpsertBulk) SetUpdated(v time.Time) *ScheduleUpsertBulk { + return u.Update(func(s *ScheduleUpsert) { + s.SetUpdated(v) + }) +} + +// UpdateUpdated sets the "updated" field to the value that was provided on create. +func (u *ScheduleUpsertBulk) UpdateUpdated() *ScheduleUpsertBulk { + return u.Update(func(s *ScheduleUpsert) { + s.UpdateUpdated() + }) +} + +// Exec executes the query. +func (u *ScheduleUpsertBulk) Exec(ctx context.Context) error { + if u.create.err != nil { + return u.create.err + } + for i, b := range u.create.builders { + if len(b.conflict) != 0 { + return fmt.Errorf("ent: OnConflict was set for builder %d. Set it on the ScheduleCreateBulk instead", i) + } + } + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for ScheduleCreateBulk.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *ScheduleUpsertBulk) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/pkg/ent/schedule_delete.go b/pkg/ent/schedule_delete.go new file mode 100644 index 000000000..23ab83a62 --- /dev/null +++ b/pkg/ent/schedule_delete.go @@ -0,0 +1,88 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/GoogleCloudPlatform/scion/pkg/ent/schedule" +) + +// ScheduleDelete is the builder for deleting a Schedule entity. +type ScheduleDelete struct { + config + hooks []Hook + mutation *ScheduleMutation +} + +// Where appends a list predicates to the ScheduleDelete builder. +func (_d *ScheduleDelete) Where(ps ...predicate.Schedule) *ScheduleDelete { + _d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (_d *ScheduleDelete) Exec(ctx context.Context) (int, error) { + return withHooks(ctx, _d.sqlExec, _d.mutation, _d.hooks) +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *ScheduleDelete) ExecX(ctx context.Context) int { + n, err := _d.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (_d *ScheduleDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(schedule.Table, sqlgraph.NewFieldSpec(schedule.FieldID, field.TypeUUID)) + if ps := _d.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + affected, err := sqlgraph.DeleteNodes(ctx, _d.driver, _spec) + if err != nil && sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + _d.mutation.done = true + return affected, err +} + +// ScheduleDeleteOne is the builder for deleting a single Schedule entity. +type ScheduleDeleteOne struct { + _d *ScheduleDelete +} + +// Where appends a list predicates to the ScheduleDelete builder. +func (_d *ScheduleDeleteOne) Where(ps ...predicate.Schedule) *ScheduleDeleteOne { + _d._d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query. +func (_d *ScheduleDeleteOne) Exec(ctx context.Context) error { + n, err := _d._d.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{schedule.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *ScheduleDeleteOne) ExecX(ctx context.Context) { + if err := _d.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/pkg/ent/schedule_query.go b/pkg/ent/schedule_query.go new file mode 100644 index 000000000..005b80ab8 --- /dev/null +++ b/pkg/ent/schedule_query.go @@ -0,0 +1,565 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "fmt" + "math" + + "entgo.io/ent" + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/GoogleCloudPlatform/scion/pkg/ent/schedule" + "github.com/google/uuid" +) + +// ScheduleQuery is the builder for querying Schedule entities. +type ScheduleQuery struct { + config + ctx *QueryContext + order []schedule.OrderOption + inters []Interceptor + predicates []predicate.Schedule + modifiers []func(*sql.Selector) + // intermediate query (i.e. traversal path). + sql *sql.Selector + path func(context.Context) (*sql.Selector, error) +} + +// Where adds a new predicate for the ScheduleQuery builder. +func (_q *ScheduleQuery) Where(ps ...predicate.Schedule) *ScheduleQuery { + _q.predicates = append(_q.predicates, ps...) + return _q +} + +// Limit the number of records to be returned by this query. +func (_q *ScheduleQuery) Limit(limit int) *ScheduleQuery { + _q.ctx.Limit = &limit + return _q +} + +// Offset to start from. +func (_q *ScheduleQuery) Offset(offset int) *ScheduleQuery { + _q.ctx.Offset = &offset + return _q +} + +// Unique configures the query builder to filter duplicate records on query. +// By default, unique is set to true, and can be disabled using this method. +func (_q *ScheduleQuery) Unique(unique bool) *ScheduleQuery { + _q.ctx.Unique = &unique + return _q +} + +// Order specifies how the records should be ordered. +func (_q *ScheduleQuery) Order(o ...schedule.OrderOption) *ScheduleQuery { + _q.order = append(_q.order, o...) + return _q +} + +// First returns the first Schedule entity from the query. +// Returns a *NotFoundError when no Schedule was found. +func (_q *ScheduleQuery) First(ctx context.Context) (*Schedule, error) { + nodes, err := _q.Limit(1).All(setContextOp(ctx, _q.ctx, ent.OpQueryFirst)) + if err != nil { + return nil, err + } + if len(nodes) == 0 { + return nil, &NotFoundError{schedule.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (_q *ScheduleQuery) FirstX(ctx context.Context) *Schedule { + node, err := _q.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// FirstID returns the first Schedule ID from the query. +// Returns a *NotFoundError when no Schedule ID was found. +func (_q *ScheduleQuery) FirstID(ctx context.Context) (id uuid.UUID, err error) { + var ids []uuid.UUID + if ids, err = _q.Limit(1).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryFirstID)); err != nil { + return + } + if len(ids) == 0 { + err = &NotFoundError{schedule.Label} + return + } + return ids[0], nil +} + +// FirstIDX is like FirstID, but panics if an error occurs. +func (_q *ScheduleQuery) FirstIDX(ctx context.Context) uuid.UUID { + id, err := _q.FirstID(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return id +} + +// Only returns a single Schedule entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one Schedule entity is found. +// Returns a *NotFoundError when no Schedule entities are found. +func (_q *ScheduleQuery) Only(ctx context.Context) (*Schedule, error) { + nodes, err := _q.Limit(2).All(setContextOp(ctx, _q.ctx, ent.OpQueryOnly)) + if err != nil { + return nil, err + } + switch len(nodes) { + case 1: + return nodes[0], nil + case 0: + return nil, &NotFoundError{schedule.Label} + default: + return nil, &NotSingularError{schedule.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (_q *ScheduleQuery) OnlyX(ctx context.Context) *Schedule { + node, err := _q.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// OnlyID is like Only, but returns the only Schedule ID in the query. +// Returns a *NotSingularError when more than one Schedule ID is found. +// Returns a *NotFoundError when no entities are found. +func (_q *ScheduleQuery) OnlyID(ctx context.Context) (id uuid.UUID, err error) { + var ids []uuid.UUID + if ids, err = _q.Limit(2).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryOnlyID)); err != nil { + return + } + switch len(ids) { + case 1: + id = ids[0] + case 0: + err = &NotFoundError{schedule.Label} + default: + err = &NotSingularError{schedule.Label} + } + return +} + +// OnlyIDX is like OnlyID, but panics if an error occurs. +func (_q *ScheduleQuery) OnlyIDX(ctx context.Context) uuid.UUID { + id, err := _q.OnlyID(ctx) + if err != nil { + panic(err) + } + return id +} + +// All executes the query and returns a list of Schedules. +func (_q *ScheduleQuery) All(ctx context.Context) ([]*Schedule, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryAll) + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*Schedule, *ScheduleQuery]() + return withInterceptors[[]*Schedule](ctx, _q, qr, _q.inters) +} + +// AllX is like All, but panics if an error occurs. +func (_q *ScheduleQuery) AllX(ctx context.Context) []*Schedule { + nodes, err := _q.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// IDs executes the query and returns a list of Schedule IDs. +func (_q *ScheduleQuery) IDs(ctx context.Context) (ids []uuid.UUID, err error) { + if _q.ctx.Unique == nil && _q.path != nil { + _q.Unique(true) + } + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryIDs) + if err = _q.Select(schedule.FieldID).Scan(ctx, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// IDsX is like IDs, but panics if an error occurs. +func (_q *ScheduleQuery) IDsX(ctx context.Context) []uuid.UUID { + ids, err := _q.IDs(ctx) + if err != nil { + panic(err) + } + return ids +} + +// Count returns the count of the given query. +func (_q *ScheduleQuery) Count(ctx context.Context) (int, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryCount) + if err := _q.prepareQuery(ctx); err != nil { + return 0, err + } + return withInterceptors[int](ctx, _q, querierCount[*ScheduleQuery](), _q.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (_q *ScheduleQuery) CountX(ctx context.Context) int { + count, err := _q.Count(ctx) + if err != nil { + panic(err) + } + return count +} + +// Exist returns true if the query has elements in the graph. +func (_q *ScheduleQuery) Exist(ctx context.Context) (bool, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryExist) + switch _, err := _q.FirstID(ctx); { + case IsNotFound(err): + return false, nil + case err != nil: + return false, fmt.Errorf("ent: check existence: %w", err) + default: + return true, nil + } +} + +// ExistX is like Exist, but panics if an error occurs. +func (_q *ScheduleQuery) ExistX(ctx context.Context) bool { + exist, err := _q.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the ScheduleQuery builder, including all associated steps. It can be +// used to prepare common query builders and use them differently after the clone is made. +func (_q *ScheduleQuery) Clone() *ScheduleQuery { + if _q == nil { + return nil + } + return &ScheduleQuery{ + config: _q.config, + ctx: _q.ctx.Clone(), + order: append([]schedule.OrderOption{}, _q.order...), + inters: append([]Interceptor{}, _q.inters...), + predicates: append([]predicate.Schedule{}, _q.predicates...), + // clone intermediate query. + sql: _q.sql.Clone(), + path: _q.path, + } +} + +// GroupBy is used to group vertices by one or more fields/columns. +// It is often used with aggregate functions, like: count, max, mean, min, sum. +// +// Example: +// +// var v []struct { +// ProjectID uuid.UUID `json:"project_id,omitempty"` +// Count int `json:"count,omitempty"` +// } +// +// client.Schedule.Query(). +// GroupBy(schedule.FieldProjectID). +// Aggregate(ent.Count()). +// Scan(ctx, &v) +func (_q *ScheduleQuery) GroupBy(field string, fields ...string) *ScheduleGroupBy { + _q.ctx.Fields = append([]string{field}, fields...) + grbuild := &ScheduleGroupBy{build: _q} + grbuild.flds = &_q.ctx.Fields + grbuild.label = schedule.Label + grbuild.scan = grbuild.Scan + return grbuild +} + +// Select allows the selection one or more fields/columns for the given query, +// instead of selecting all fields in the entity. +// +// Example: +// +// var v []struct { +// ProjectID uuid.UUID `json:"project_id,omitempty"` +// } +// +// client.Schedule.Query(). +// Select(schedule.FieldProjectID). +// Scan(ctx, &v) +func (_q *ScheduleQuery) Select(fields ...string) *ScheduleSelect { + _q.ctx.Fields = append(_q.ctx.Fields, fields...) + sbuild := &ScheduleSelect{ScheduleQuery: _q} + sbuild.label = schedule.Label + sbuild.flds, sbuild.scan = &_q.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a ScheduleSelect configured with the given aggregations. +func (_q *ScheduleQuery) Aggregate(fns ...AggregateFunc) *ScheduleSelect { + return _q.Select().Aggregate(fns...) +} + +func (_q *ScheduleQuery) prepareQuery(ctx context.Context) error { + for _, inter := range _q.inters { + if inter == nil { + return fmt.Errorf("ent: uninitialized interceptor (forgotten import ent/runtime?)") + } + if trv, ok := inter.(Traverser); ok { + if err := trv.Traverse(ctx, _q); err != nil { + return err + } + } + } + for _, f := range _q.ctx.Fields { + if !schedule.ValidColumn(f) { + return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + } + if _q.path != nil { + prev, err := _q.path(ctx) + if err != nil { + return err + } + _q.sql = prev + } + return nil +} + +func (_q *ScheduleQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*Schedule, error) { + var ( + nodes = []*Schedule{} + _spec = _q.querySpec() + ) + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*Schedule).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &Schedule{config: _q.config} + nodes = append(nodes, node) + return node.assignValues(columns, values) + } + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } + for i := range hooks { + hooks[i](ctx, _spec) + } + if err := sqlgraph.QueryNodes(ctx, _q.driver, _spec); err != nil { + return nil, err + } + if len(nodes) == 0 { + return nodes, nil + } + return nodes, nil +} + +func (_q *ScheduleQuery) sqlCount(ctx context.Context) (int, error) { + _spec := _q.querySpec() + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } + _spec.Node.Columns = _q.ctx.Fields + if len(_q.ctx.Fields) > 0 { + _spec.Unique = _q.ctx.Unique != nil && *_q.ctx.Unique + } + return sqlgraph.CountNodes(ctx, _q.driver, _spec) +} + +func (_q *ScheduleQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(schedule.Table, schedule.Columns, sqlgraph.NewFieldSpec(schedule.FieldID, field.TypeUUID)) + _spec.From = _q.sql + if unique := _q.ctx.Unique; unique != nil { + _spec.Unique = *unique + } else if _q.path != nil { + _spec.Unique = true + } + if fields := _q.ctx.Fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, schedule.FieldID) + for i := range fields { + if fields[i] != schedule.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, fields[i]) + } + } + } + if ps := _q.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if limit := _q.ctx.Limit; limit != nil { + _spec.Limit = *limit + } + if offset := _q.ctx.Offset; offset != nil { + _spec.Offset = *offset + } + if ps := _q.order; len(ps) > 0 { + _spec.Order = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + return _spec +} + +func (_q *ScheduleQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(_q.driver.Dialect()) + t1 := builder.Table(schedule.Table) + columns := _q.ctx.Fields + if len(columns) == 0 { + columns = schedule.Columns + } + selector := builder.Select(t1.Columns(columns...)...).From(t1) + if _q.sql != nil { + selector = _q.sql + selector.Select(selector.Columns(columns...)...) + } + if _q.ctx.Unique != nil && *_q.ctx.Unique { + selector.Distinct() + } + for _, m := range _q.modifiers { + m(selector) + } + for _, p := range _q.predicates { + p(selector) + } + for _, p := range _q.order { + p(selector) + } + if offset := _q.ctx.Offset; offset != nil { + // limit is mandatory for offset clause. We start + // with default value, and override it below if needed. + selector.Offset(*offset).Limit(math.MaxInt32) + } + if limit := _q.ctx.Limit; limit != nil { + selector.Limit(*limit) + } + return selector +} + +// ForUpdate locks the selected rows against concurrent updates, and prevent them from being +// updated, deleted or "selected ... for update" by other sessions, until the transaction is +// either committed or rolled-back. +func (_q *ScheduleQuery) ForUpdate(opts ...sql.LockOption) *ScheduleQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForUpdate(opts...) + }) + return _q +} + +// ForShare behaves similarly to ForUpdate, except that it acquires a shared mode lock +// on any rows that are read. Other sessions can read the rows, but cannot modify them +// until your transaction commits. +func (_q *ScheduleQuery) ForShare(opts ...sql.LockOption) *ScheduleQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForShare(opts...) + }) + return _q +} + +// ScheduleGroupBy is the group-by builder for Schedule entities. +type ScheduleGroupBy struct { + selector + build *ScheduleQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (_g *ScheduleGroupBy) Aggregate(fns ...AggregateFunc) *ScheduleGroupBy { + _g.fns = append(_g.fns, fns...) + return _g +} + +// Scan applies the selector query and scans the result into the given value. +func (_g *ScheduleGroupBy) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, _g.build.ctx, ent.OpQueryGroupBy) + if err := _g.build.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*ScheduleQuery, *ScheduleGroupBy](ctx, _g.build, _g, _g.build.inters, v) +} + +func (_g *ScheduleGroupBy) sqlScan(ctx context.Context, root *ScheduleQuery, v any) error { + selector := root.sqlQuery(ctx).Select() + aggregation := make([]string, 0, len(_g.fns)) + for _, fn := range _g.fns { + aggregation = append(aggregation, fn(selector)) + } + if len(selector.SelectedColumns()) == 0 { + columns := make([]string, 0, len(*_g.flds)+len(_g.fns)) + for _, f := range *_g.flds { + columns = append(columns, selector.C(f)) + } + columns = append(columns, aggregation...) + selector.Select(columns...) + } + selector.GroupBy(selector.Columns(*_g.flds...)...) + if err := selector.Err(); err != nil { + return err + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := _g.build.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} + +// ScheduleSelect is the builder for selecting fields of Schedule entities. +type ScheduleSelect struct { + *ScheduleQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (_s *ScheduleSelect) Aggregate(fns ...AggregateFunc) *ScheduleSelect { + _s.fns = append(_s.fns, fns...) + return _s +} + +// Scan applies the selector query and scans the result into the given value. +func (_s *ScheduleSelect) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, _s.ctx, ent.OpQuerySelect) + if err := _s.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*ScheduleQuery, *ScheduleSelect](ctx, _s.ScheduleQuery, _s, _s.inters, v) +} + +func (_s *ScheduleSelect) sqlScan(ctx context.Context, root *ScheduleQuery, v any) error { + selector := root.sqlQuery(ctx) + aggregation := make([]string, 0, len(_s.fns)) + for _, fn := range _s.fns { + aggregation = append(aggregation, fn(selector)) + } + switch n := len(*_s.selector.flds); { + case n == 0 && len(aggregation) > 0: + selector.Select(aggregation...) + case n != 0 && len(aggregation) > 0: + selector.AppendSelect(aggregation...) + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := _s.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} diff --git a/pkg/ent/schedule_update.go b/pkg/ent/schedule_update.go new file mode 100644 index 000000000..791a6eb52 --- /dev/null +++ b/pkg/ent/schedule_update.go @@ -0,0 +1,831 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/GoogleCloudPlatform/scion/pkg/ent/schedule" + "github.com/google/uuid" +) + +// ScheduleUpdate is the builder for updating Schedule entities. +type ScheduleUpdate struct { + config + hooks []Hook + mutation *ScheduleMutation +} + +// Where appends a list predicates to the ScheduleUpdate builder. +func (_u *ScheduleUpdate) Where(ps ...predicate.Schedule) *ScheduleUpdate { + _u.mutation.Where(ps...) + return _u +} + +// SetProjectID sets the "project_id" field. +func (_u *ScheduleUpdate) SetProjectID(v uuid.UUID) *ScheduleUpdate { + _u.mutation.SetProjectID(v) + return _u +} + +// SetNillableProjectID sets the "project_id" field if the given value is not nil. +func (_u *ScheduleUpdate) SetNillableProjectID(v *uuid.UUID) *ScheduleUpdate { + if v != nil { + _u.SetProjectID(*v) + } + return _u +} + +// SetName sets the "name" field. +func (_u *ScheduleUpdate) SetName(v string) *ScheduleUpdate { + _u.mutation.SetName(v) + return _u +} + +// SetNillableName sets the "name" field if the given value is not nil. +func (_u *ScheduleUpdate) SetNillableName(v *string) *ScheduleUpdate { + if v != nil { + _u.SetName(*v) + } + return _u +} + +// SetCronExpr sets the "cron_expr" field. +func (_u *ScheduleUpdate) SetCronExpr(v string) *ScheduleUpdate { + _u.mutation.SetCronExpr(v) + return _u +} + +// SetNillableCronExpr sets the "cron_expr" field if the given value is not nil. +func (_u *ScheduleUpdate) SetNillableCronExpr(v *string) *ScheduleUpdate { + if v != nil { + _u.SetCronExpr(*v) + } + return _u +} + +// SetEventType sets the "event_type" field. +func (_u *ScheduleUpdate) SetEventType(v string) *ScheduleUpdate { + _u.mutation.SetEventType(v) + return _u +} + +// SetNillableEventType sets the "event_type" field if the given value is not nil. +func (_u *ScheduleUpdate) SetNillableEventType(v *string) *ScheduleUpdate { + if v != nil { + _u.SetEventType(*v) + } + return _u +} + +// SetPayload sets the "payload" field. +func (_u *ScheduleUpdate) SetPayload(v string) *ScheduleUpdate { + _u.mutation.SetPayload(v) + return _u +} + +// SetNillablePayload sets the "payload" field if the given value is not nil. +func (_u *ScheduleUpdate) SetNillablePayload(v *string) *ScheduleUpdate { + if v != nil { + _u.SetPayload(*v) + } + return _u +} + +// SetStatus sets the "status" field. +func (_u *ScheduleUpdate) SetStatus(v string) *ScheduleUpdate { + _u.mutation.SetStatus(v) + return _u +} + +// SetNillableStatus sets the "status" field if the given value is not nil. +func (_u *ScheduleUpdate) SetNillableStatus(v *string) *ScheduleUpdate { + if v != nil { + _u.SetStatus(*v) + } + return _u +} + +// SetNextRunAt sets the "next_run_at" field. +func (_u *ScheduleUpdate) SetNextRunAt(v time.Time) *ScheduleUpdate { + _u.mutation.SetNextRunAt(v) + return _u +} + +// SetNillableNextRunAt sets the "next_run_at" field if the given value is not nil. +func (_u *ScheduleUpdate) SetNillableNextRunAt(v *time.Time) *ScheduleUpdate { + if v != nil { + _u.SetNextRunAt(*v) + } + return _u +} + +// ClearNextRunAt clears the value of the "next_run_at" field. +func (_u *ScheduleUpdate) ClearNextRunAt() *ScheduleUpdate { + _u.mutation.ClearNextRunAt() + return _u +} + +// SetLastRunAt sets the "last_run_at" field. +func (_u *ScheduleUpdate) SetLastRunAt(v time.Time) *ScheduleUpdate { + _u.mutation.SetLastRunAt(v) + return _u +} + +// SetNillableLastRunAt sets the "last_run_at" field if the given value is not nil. +func (_u *ScheduleUpdate) SetNillableLastRunAt(v *time.Time) *ScheduleUpdate { + if v != nil { + _u.SetLastRunAt(*v) + } + return _u +} + +// ClearLastRunAt clears the value of the "last_run_at" field. +func (_u *ScheduleUpdate) ClearLastRunAt() *ScheduleUpdate { + _u.mutation.ClearLastRunAt() + return _u +} + +// SetLastRunStatus sets the "last_run_status" field. +func (_u *ScheduleUpdate) SetLastRunStatus(v string) *ScheduleUpdate { + _u.mutation.SetLastRunStatus(v) + return _u +} + +// SetNillableLastRunStatus sets the "last_run_status" field if the given value is not nil. +func (_u *ScheduleUpdate) SetNillableLastRunStatus(v *string) *ScheduleUpdate { + if v != nil { + _u.SetLastRunStatus(*v) + } + return _u +} + +// ClearLastRunStatus clears the value of the "last_run_status" field. +func (_u *ScheduleUpdate) ClearLastRunStatus() *ScheduleUpdate { + _u.mutation.ClearLastRunStatus() + return _u +} + +// SetLastRunError sets the "last_run_error" field. +func (_u *ScheduleUpdate) SetLastRunError(v string) *ScheduleUpdate { + _u.mutation.SetLastRunError(v) + return _u +} + +// SetNillableLastRunError sets the "last_run_error" field if the given value is not nil. +func (_u *ScheduleUpdate) SetNillableLastRunError(v *string) *ScheduleUpdate { + if v != nil { + _u.SetLastRunError(*v) + } + return _u +} + +// ClearLastRunError clears the value of the "last_run_error" field. +func (_u *ScheduleUpdate) ClearLastRunError() *ScheduleUpdate { + _u.mutation.ClearLastRunError() + return _u +} + +// SetRunCount sets the "run_count" field. +func (_u *ScheduleUpdate) SetRunCount(v int) *ScheduleUpdate { + _u.mutation.ResetRunCount() + _u.mutation.SetRunCount(v) + return _u +} + +// SetNillableRunCount sets the "run_count" field if the given value is not nil. +func (_u *ScheduleUpdate) SetNillableRunCount(v *int) *ScheduleUpdate { + if v != nil { + _u.SetRunCount(*v) + } + return _u +} + +// AddRunCount adds value to the "run_count" field. +func (_u *ScheduleUpdate) AddRunCount(v int) *ScheduleUpdate { + _u.mutation.AddRunCount(v) + return _u +} + +// SetErrorCount sets the "error_count" field. +func (_u *ScheduleUpdate) SetErrorCount(v int) *ScheduleUpdate { + _u.mutation.ResetErrorCount() + _u.mutation.SetErrorCount(v) + return _u +} + +// SetNillableErrorCount sets the "error_count" field if the given value is not nil. +func (_u *ScheduleUpdate) SetNillableErrorCount(v *int) *ScheduleUpdate { + if v != nil { + _u.SetErrorCount(*v) + } + return _u +} + +// AddErrorCount adds value to the "error_count" field. +func (_u *ScheduleUpdate) AddErrorCount(v int) *ScheduleUpdate { + _u.mutation.AddErrorCount(v) + return _u +} + +// SetCreatedBy sets the "created_by" field. +func (_u *ScheduleUpdate) SetCreatedBy(v string) *ScheduleUpdate { + _u.mutation.SetCreatedBy(v) + return _u +} + +// SetNillableCreatedBy sets the "created_by" field if the given value is not nil. +func (_u *ScheduleUpdate) SetNillableCreatedBy(v *string) *ScheduleUpdate { + if v != nil { + _u.SetCreatedBy(*v) + } + return _u +} + +// ClearCreatedBy clears the value of the "created_by" field. +func (_u *ScheduleUpdate) ClearCreatedBy() *ScheduleUpdate { + _u.mutation.ClearCreatedBy() + return _u +} + +// SetUpdated sets the "updated" field. +func (_u *ScheduleUpdate) SetUpdated(v time.Time) *ScheduleUpdate { + _u.mutation.SetUpdated(v) + return _u +} + +// Mutation returns the ScheduleMutation object of the builder. +func (_u *ScheduleUpdate) Mutation() *ScheduleMutation { + return _u.mutation +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (_u *ScheduleUpdate) Save(ctx context.Context) (int, error) { + _u.defaults() + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *ScheduleUpdate) SaveX(ctx context.Context) int { + affected, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (_u *ScheduleUpdate) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *ScheduleUpdate) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_u *ScheduleUpdate) defaults() { + if _, ok := _u.mutation.Updated(); !ok { + v := schedule.UpdateDefaultUpdated() + _u.mutation.SetUpdated(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_u *ScheduleUpdate) check() error { + if v, ok := _u.mutation.Name(); ok { + if err := schedule.NameValidator(v); err != nil { + return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "Schedule.name": %w`, err)} + } + } + if v, ok := _u.mutation.CronExpr(); ok { + if err := schedule.CronExprValidator(v); err != nil { + return &ValidationError{Name: "cron_expr", err: fmt.Errorf(`ent: validator failed for field "Schedule.cron_expr": %w`, err)} + } + } + if v, ok := _u.mutation.EventType(); ok { + if err := schedule.EventTypeValidator(v); err != nil { + return &ValidationError{Name: "event_type", err: fmt.Errorf(`ent: validator failed for field "Schedule.event_type": %w`, err)} + } + } + return nil +} + +func (_u *ScheduleUpdate) sqlSave(ctx context.Context) (_node int, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(schedule.Table, schedule.Columns, sqlgraph.NewFieldSpec(schedule.FieldID, field.TypeUUID)) + if ps := _u.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := _u.mutation.ProjectID(); ok { + _spec.SetField(schedule.FieldProjectID, field.TypeUUID, value) + } + if value, ok := _u.mutation.Name(); ok { + _spec.SetField(schedule.FieldName, field.TypeString, value) + } + if value, ok := _u.mutation.CronExpr(); ok { + _spec.SetField(schedule.FieldCronExpr, field.TypeString, value) + } + if value, ok := _u.mutation.EventType(); ok { + _spec.SetField(schedule.FieldEventType, field.TypeString, value) + } + if value, ok := _u.mutation.Payload(); ok { + _spec.SetField(schedule.FieldPayload, field.TypeString, value) + } + if value, ok := _u.mutation.Status(); ok { + _spec.SetField(schedule.FieldStatus, field.TypeString, value) + } + if value, ok := _u.mutation.NextRunAt(); ok { + _spec.SetField(schedule.FieldNextRunAt, field.TypeTime, value) + } + if _u.mutation.NextRunAtCleared() { + _spec.ClearField(schedule.FieldNextRunAt, field.TypeTime) + } + if value, ok := _u.mutation.LastRunAt(); ok { + _spec.SetField(schedule.FieldLastRunAt, field.TypeTime, value) + } + if _u.mutation.LastRunAtCleared() { + _spec.ClearField(schedule.FieldLastRunAt, field.TypeTime) + } + if value, ok := _u.mutation.LastRunStatus(); ok { + _spec.SetField(schedule.FieldLastRunStatus, field.TypeString, value) + } + if _u.mutation.LastRunStatusCleared() { + _spec.ClearField(schedule.FieldLastRunStatus, field.TypeString) + } + if value, ok := _u.mutation.LastRunError(); ok { + _spec.SetField(schedule.FieldLastRunError, field.TypeString, value) + } + if _u.mutation.LastRunErrorCleared() { + _spec.ClearField(schedule.FieldLastRunError, field.TypeString) + } + if value, ok := _u.mutation.RunCount(); ok { + _spec.SetField(schedule.FieldRunCount, field.TypeInt, value) + } + if value, ok := _u.mutation.AddedRunCount(); ok { + _spec.AddField(schedule.FieldRunCount, field.TypeInt, value) + } + if value, ok := _u.mutation.ErrorCount(); ok { + _spec.SetField(schedule.FieldErrorCount, field.TypeInt, value) + } + if value, ok := _u.mutation.AddedErrorCount(); ok { + _spec.AddField(schedule.FieldErrorCount, field.TypeInt, value) + } + if value, ok := _u.mutation.CreatedBy(); ok { + _spec.SetField(schedule.FieldCreatedBy, field.TypeString, value) + } + if _u.mutation.CreatedByCleared() { + _spec.ClearField(schedule.FieldCreatedBy, field.TypeString) + } + if value, ok := _u.mutation.Updated(); ok { + _spec.SetField(schedule.FieldUpdated, field.TypeTime, value) + } + if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{schedule.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + _u.mutation.done = true + return _node, nil +} + +// ScheduleUpdateOne is the builder for updating a single Schedule entity. +type ScheduleUpdateOne struct { + config + fields []string + hooks []Hook + mutation *ScheduleMutation +} + +// SetProjectID sets the "project_id" field. +func (_u *ScheduleUpdateOne) SetProjectID(v uuid.UUID) *ScheduleUpdateOne { + _u.mutation.SetProjectID(v) + return _u +} + +// SetNillableProjectID sets the "project_id" field if the given value is not nil. +func (_u *ScheduleUpdateOne) SetNillableProjectID(v *uuid.UUID) *ScheduleUpdateOne { + if v != nil { + _u.SetProjectID(*v) + } + return _u +} + +// SetName sets the "name" field. +func (_u *ScheduleUpdateOne) SetName(v string) *ScheduleUpdateOne { + _u.mutation.SetName(v) + return _u +} + +// SetNillableName sets the "name" field if the given value is not nil. +func (_u *ScheduleUpdateOne) SetNillableName(v *string) *ScheduleUpdateOne { + if v != nil { + _u.SetName(*v) + } + return _u +} + +// SetCronExpr sets the "cron_expr" field. +func (_u *ScheduleUpdateOne) SetCronExpr(v string) *ScheduleUpdateOne { + _u.mutation.SetCronExpr(v) + return _u +} + +// SetNillableCronExpr sets the "cron_expr" field if the given value is not nil. +func (_u *ScheduleUpdateOne) SetNillableCronExpr(v *string) *ScheduleUpdateOne { + if v != nil { + _u.SetCronExpr(*v) + } + return _u +} + +// SetEventType sets the "event_type" field. +func (_u *ScheduleUpdateOne) SetEventType(v string) *ScheduleUpdateOne { + _u.mutation.SetEventType(v) + return _u +} + +// SetNillableEventType sets the "event_type" field if the given value is not nil. +func (_u *ScheduleUpdateOne) SetNillableEventType(v *string) *ScheduleUpdateOne { + if v != nil { + _u.SetEventType(*v) + } + return _u +} + +// SetPayload sets the "payload" field. +func (_u *ScheduleUpdateOne) SetPayload(v string) *ScheduleUpdateOne { + _u.mutation.SetPayload(v) + return _u +} + +// SetNillablePayload sets the "payload" field if the given value is not nil. +func (_u *ScheduleUpdateOne) SetNillablePayload(v *string) *ScheduleUpdateOne { + if v != nil { + _u.SetPayload(*v) + } + return _u +} + +// SetStatus sets the "status" field. +func (_u *ScheduleUpdateOne) SetStatus(v string) *ScheduleUpdateOne { + _u.mutation.SetStatus(v) + return _u +} + +// SetNillableStatus sets the "status" field if the given value is not nil. +func (_u *ScheduleUpdateOne) SetNillableStatus(v *string) *ScheduleUpdateOne { + if v != nil { + _u.SetStatus(*v) + } + return _u +} + +// SetNextRunAt sets the "next_run_at" field. +func (_u *ScheduleUpdateOne) SetNextRunAt(v time.Time) *ScheduleUpdateOne { + _u.mutation.SetNextRunAt(v) + return _u +} + +// SetNillableNextRunAt sets the "next_run_at" field if the given value is not nil. +func (_u *ScheduleUpdateOne) SetNillableNextRunAt(v *time.Time) *ScheduleUpdateOne { + if v != nil { + _u.SetNextRunAt(*v) + } + return _u +} + +// ClearNextRunAt clears the value of the "next_run_at" field. +func (_u *ScheduleUpdateOne) ClearNextRunAt() *ScheduleUpdateOne { + _u.mutation.ClearNextRunAt() + return _u +} + +// SetLastRunAt sets the "last_run_at" field. +func (_u *ScheduleUpdateOne) SetLastRunAt(v time.Time) *ScheduleUpdateOne { + _u.mutation.SetLastRunAt(v) + return _u +} + +// SetNillableLastRunAt sets the "last_run_at" field if the given value is not nil. +func (_u *ScheduleUpdateOne) SetNillableLastRunAt(v *time.Time) *ScheduleUpdateOne { + if v != nil { + _u.SetLastRunAt(*v) + } + return _u +} + +// ClearLastRunAt clears the value of the "last_run_at" field. +func (_u *ScheduleUpdateOne) ClearLastRunAt() *ScheduleUpdateOne { + _u.mutation.ClearLastRunAt() + return _u +} + +// SetLastRunStatus sets the "last_run_status" field. +func (_u *ScheduleUpdateOne) SetLastRunStatus(v string) *ScheduleUpdateOne { + _u.mutation.SetLastRunStatus(v) + return _u +} + +// SetNillableLastRunStatus sets the "last_run_status" field if the given value is not nil. +func (_u *ScheduleUpdateOne) SetNillableLastRunStatus(v *string) *ScheduleUpdateOne { + if v != nil { + _u.SetLastRunStatus(*v) + } + return _u +} + +// ClearLastRunStatus clears the value of the "last_run_status" field. +func (_u *ScheduleUpdateOne) ClearLastRunStatus() *ScheduleUpdateOne { + _u.mutation.ClearLastRunStatus() + return _u +} + +// SetLastRunError sets the "last_run_error" field. +func (_u *ScheduleUpdateOne) SetLastRunError(v string) *ScheduleUpdateOne { + _u.mutation.SetLastRunError(v) + return _u +} + +// SetNillableLastRunError sets the "last_run_error" field if the given value is not nil. +func (_u *ScheduleUpdateOne) SetNillableLastRunError(v *string) *ScheduleUpdateOne { + if v != nil { + _u.SetLastRunError(*v) + } + return _u +} + +// ClearLastRunError clears the value of the "last_run_error" field. +func (_u *ScheduleUpdateOne) ClearLastRunError() *ScheduleUpdateOne { + _u.mutation.ClearLastRunError() + return _u +} + +// SetRunCount sets the "run_count" field. +func (_u *ScheduleUpdateOne) SetRunCount(v int) *ScheduleUpdateOne { + _u.mutation.ResetRunCount() + _u.mutation.SetRunCount(v) + return _u +} + +// SetNillableRunCount sets the "run_count" field if the given value is not nil. +func (_u *ScheduleUpdateOne) SetNillableRunCount(v *int) *ScheduleUpdateOne { + if v != nil { + _u.SetRunCount(*v) + } + return _u +} + +// AddRunCount adds value to the "run_count" field. +func (_u *ScheduleUpdateOne) AddRunCount(v int) *ScheduleUpdateOne { + _u.mutation.AddRunCount(v) + return _u +} + +// SetErrorCount sets the "error_count" field. +func (_u *ScheduleUpdateOne) SetErrorCount(v int) *ScheduleUpdateOne { + _u.mutation.ResetErrorCount() + _u.mutation.SetErrorCount(v) + return _u +} + +// SetNillableErrorCount sets the "error_count" field if the given value is not nil. +func (_u *ScheduleUpdateOne) SetNillableErrorCount(v *int) *ScheduleUpdateOne { + if v != nil { + _u.SetErrorCount(*v) + } + return _u +} + +// AddErrorCount adds value to the "error_count" field. +func (_u *ScheduleUpdateOne) AddErrorCount(v int) *ScheduleUpdateOne { + _u.mutation.AddErrorCount(v) + return _u +} + +// SetCreatedBy sets the "created_by" field. +func (_u *ScheduleUpdateOne) SetCreatedBy(v string) *ScheduleUpdateOne { + _u.mutation.SetCreatedBy(v) + return _u +} + +// SetNillableCreatedBy sets the "created_by" field if the given value is not nil. +func (_u *ScheduleUpdateOne) SetNillableCreatedBy(v *string) *ScheduleUpdateOne { + if v != nil { + _u.SetCreatedBy(*v) + } + return _u +} + +// ClearCreatedBy clears the value of the "created_by" field. +func (_u *ScheduleUpdateOne) ClearCreatedBy() *ScheduleUpdateOne { + _u.mutation.ClearCreatedBy() + return _u +} + +// SetUpdated sets the "updated" field. +func (_u *ScheduleUpdateOne) SetUpdated(v time.Time) *ScheduleUpdateOne { + _u.mutation.SetUpdated(v) + return _u +} + +// Mutation returns the ScheduleMutation object of the builder. +func (_u *ScheduleUpdateOne) Mutation() *ScheduleMutation { + return _u.mutation +} + +// Where appends a list predicates to the ScheduleUpdate builder. +func (_u *ScheduleUpdateOne) Where(ps ...predicate.Schedule) *ScheduleUpdateOne { + _u.mutation.Where(ps...) + return _u +} + +// Select allows selecting one or more fields (columns) of the returned entity. +// The default is selecting all fields defined in the entity schema. +func (_u *ScheduleUpdateOne) Select(field string, fields ...string) *ScheduleUpdateOne { + _u.fields = append([]string{field}, fields...) + return _u +} + +// Save executes the query and returns the updated Schedule entity. +func (_u *ScheduleUpdateOne) Save(ctx context.Context) (*Schedule, error) { + _u.defaults() + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *ScheduleUpdateOne) SaveX(ctx context.Context) *Schedule { + node, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (_u *ScheduleUpdateOne) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *ScheduleUpdateOne) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_u *ScheduleUpdateOne) defaults() { + if _, ok := _u.mutation.Updated(); !ok { + v := schedule.UpdateDefaultUpdated() + _u.mutation.SetUpdated(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_u *ScheduleUpdateOne) check() error { + if v, ok := _u.mutation.Name(); ok { + if err := schedule.NameValidator(v); err != nil { + return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "Schedule.name": %w`, err)} + } + } + if v, ok := _u.mutation.CronExpr(); ok { + if err := schedule.CronExprValidator(v); err != nil { + return &ValidationError{Name: "cron_expr", err: fmt.Errorf(`ent: validator failed for field "Schedule.cron_expr": %w`, err)} + } + } + if v, ok := _u.mutation.EventType(); ok { + if err := schedule.EventTypeValidator(v); err != nil { + return &ValidationError{Name: "event_type", err: fmt.Errorf(`ent: validator failed for field "Schedule.event_type": %w`, err)} + } + } + return nil +} + +func (_u *ScheduleUpdateOne) sqlSave(ctx context.Context) (_node *Schedule, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(schedule.Table, schedule.Columns, sqlgraph.NewFieldSpec(schedule.FieldID, field.TypeUUID)) + id, ok := _u.mutation.ID() + if !ok { + return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "Schedule.id" for update`)} + } + _spec.Node.ID.Value = id + if fields := _u.fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, schedule.FieldID) + for _, f := range fields { + if !schedule.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + if f != schedule.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, f) + } + } + } + if ps := _u.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := _u.mutation.ProjectID(); ok { + _spec.SetField(schedule.FieldProjectID, field.TypeUUID, value) + } + if value, ok := _u.mutation.Name(); ok { + _spec.SetField(schedule.FieldName, field.TypeString, value) + } + if value, ok := _u.mutation.CronExpr(); ok { + _spec.SetField(schedule.FieldCronExpr, field.TypeString, value) + } + if value, ok := _u.mutation.EventType(); ok { + _spec.SetField(schedule.FieldEventType, field.TypeString, value) + } + if value, ok := _u.mutation.Payload(); ok { + _spec.SetField(schedule.FieldPayload, field.TypeString, value) + } + if value, ok := _u.mutation.Status(); ok { + _spec.SetField(schedule.FieldStatus, field.TypeString, value) + } + if value, ok := _u.mutation.NextRunAt(); ok { + _spec.SetField(schedule.FieldNextRunAt, field.TypeTime, value) + } + if _u.mutation.NextRunAtCleared() { + _spec.ClearField(schedule.FieldNextRunAt, field.TypeTime) + } + if value, ok := _u.mutation.LastRunAt(); ok { + _spec.SetField(schedule.FieldLastRunAt, field.TypeTime, value) + } + if _u.mutation.LastRunAtCleared() { + _spec.ClearField(schedule.FieldLastRunAt, field.TypeTime) + } + if value, ok := _u.mutation.LastRunStatus(); ok { + _spec.SetField(schedule.FieldLastRunStatus, field.TypeString, value) + } + if _u.mutation.LastRunStatusCleared() { + _spec.ClearField(schedule.FieldLastRunStatus, field.TypeString) + } + if value, ok := _u.mutation.LastRunError(); ok { + _spec.SetField(schedule.FieldLastRunError, field.TypeString, value) + } + if _u.mutation.LastRunErrorCleared() { + _spec.ClearField(schedule.FieldLastRunError, field.TypeString) + } + if value, ok := _u.mutation.RunCount(); ok { + _spec.SetField(schedule.FieldRunCount, field.TypeInt, value) + } + if value, ok := _u.mutation.AddedRunCount(); ok { + _spec.AddField(schedule.FieldRunCount, field.TypeInt, value) + } + if value, ok := _u.mutation.ErrorCount(); ok { + _spec.SetField(schedule.FieldErrorCount, field.TypeInt, value) + } + if value, ok := _u.mutation.AddedErrorCount(); ok { + _spec.AddField(schedule.FieldErrorCount, field.TypeInt, value) + } + if value, ok := _u.mutation.CreatedBy(); ok { + _spec.SetField(schedule.FieldCreatedBy, field.TypeString, value) + } + if _u.mutation.CreatedByCleared() { + _spec.ClearField(schedule.FieldCreatedBy, field.TypeString) + } + if value, ok := _u.mutation.Updated(); ok { + _spec.SetField(schedule.FieldUpdated, field.TypeTime, value) + } + _node = &Schedule{config: _u.config} + _spec.Assign = _node.assignValues + _spec.ScanValues = _node.scanValues + if err = sqlgraph.UpdateNode(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{schedule.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + _u.mutation.done = true + return _node, nil +} diff --git a/pkg/ent/scheduledevent.go b/pkg/ent/scheduledevent.go new file mode 100644 index 000000000..9cd40bec0 --- /dev/null +++ b/pkg/ent/scheduledevent.go @@ -0,0 +1,209 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "fmt" + "strings" + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "github.com/GoogleCloudPlatform/scion/pkg/ent/scheduledevent" + "github.com/google/uuid" +) + +// ScheduledEvent is the model entity for the ScheduledEvent schema. +type ScheduledEvent struct { + config `json:"-"` + // ID of the ent. + ID uuid.UUID `json:"id,omitempty"` + // ProjectID holds the value of the "project_id" field. + ProjectID uuid.UUID `json:"project_id,omitempty"` + // EventType holds the value of the "event_type" field. + EventType string `json:"event_type,omitempty"` + // FireAt holds the value of the "fire_at" field. + FireAt time.Time `json:"fire_at,omitempty"` + // Payload holds the value of the "payload" field. + Payload string `json:"payload,omitempty"` + // Status holds the value of the "status" field. + Status string `json:"status,omitempty"` + // CreatedBy holds the value of the "created_by" field. + CreatedBy string `json:"created_by,omitempty"` + // FiredAt holds the value of the "fired_at" field. + FiredAt *time.Time `json:"fired_at,omitempty"` + // Error holds the value of the "error" field. + Error string `json:"error,omitempty"` + // ScheduleID holds the value of the "schedule_id" field. + ScheduleID string `json:"schedule_id,omitempty"` + // Created holds the value of the "created" field. + Created time.Time `json:"created,omitempty"` + selectValues sql.SelectValues +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*ScheduledEvent) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case scheduledevent.FieldEventType, scheduledevent.FieldPayload, scheduledevent.FieldStatus, scheduledevent.FieldCreatedBy, scheduledevent.FieldError, scheduledevent.FieldScheduleID: + values[i] = new(sql.NullString) + case scheduledevent.FieldFireAt, scheduledevent.FieldFiredAt, scheduledevent.FieldCreated: + values[i] = new(sql.NullTime) + case scheduledevent.FieldID, scheduledevent.FieldProjectID: + values[i] = new(uuid.UUID) + default: + values[i] = new(sql.UnknownType) + } + } + return values, nil +} + +// assignValues assigns the values that were returned from sql.Rows (after scanning) +// to the ScheduledEvent fields. +func (_m *ScheduledEvent) assignValues(columns []string, values []any) error { + if m, n := len(values), len(columns); m < n { + return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) + } + for i := range columns { + switch columns[i] { + case scheduledevent.FieldID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field id", values[i]) + } else if value != nil { + _m.ID = *value + } + case scheduledevent.FieldProjectID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field project_id", values[i]) + } else if value != nil { + _m.ProjectID = *value + } + case scheduledevent.FieldEventType: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field event_type", values[i]) + } else if value.Valid { + _m.EventType = value.String + } + case scheduledevent.FieldFireAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field fire_at", values[i]) + } else if value.Valid { + _m.FireAt = value.Time + } + case scheduledevent.FieldPayload: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field payload", values[i]) + } else if value.Valid { + _m.Payload = value.String + } + case scheduledevent.FieldStatus: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field status", values[i]) + } else if value.Valid { + _m.Status = value.String + } + case scheduledevent.FieldCreatedBy: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field created_by", values[i]) + } else if value.Valid { + _m.CreatedBy = value.String + } + case scheduledevent.FieldFiredAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field fired_at", values[i]) + } else if value.Valid { + _m.FiredAt = new(time.Time) + *_m.FiredAt = value.Time + } + case scheduledevent.FieldError: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field error", values[i]) + } else if value.Valid { + _m.Error = value.String + } + case scheduledevent.FieldScheduleID: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field schedule_id", values[i]) + } else if value.Valid { + _m.ScheduleID = value.String + } + case scheduledevent.FieldCreated: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field created", values[i]) + } else if value.Valid { + _m.Created = value.Time + } + default: + _m.selectValues.Set(columns[i], values[i]) + } + } + return nil +} + +// Value returns the ent.Value that was dynamically selected and assigned to the ScheduledEvent. +// This includes values selected through modifiers, order, etc. +func (_m *ScheduledEvent) Value(name string) (ent.Value, error) { + return _m.selectValues.Get(name) +} + +// Update returns a builder for updating this ScheduledEvent. +// Note that you need to call ScheduledEvent.Unwrap() before calling this method if this ScheduledEvent +// was returned from a transaction, and the transaction was committed or rolled back. +func (_m *ScheduledEvent) Update() *ScheduledEventUpdateOne { + return NewScheduledEventClient(_m.config).UpdateOne(_m) +} + +// Unwrap unwraps the ScheduledEvent entity that was returned from a transaction after it was closed, +// so that all future queries will be executed through the driver which created the transaction. +func (_m *ScheduledEvent) Unwrap() *ScheduledEvent { + _tx, ok := _m.config.driver.(*txDriver) + if !ok { + panic("ent: ScheduledEvent is not a transactional entity") + } + _m.config.driver = _tx.drv + return _m +} + +// String implements the fmt.Stringer. +func (_m *ScheduledEvent) String() string { + var builder strings.Builder + builder.WriteString("ScheduledEvent(") + builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID)) + builder.WriteString("project_id=") + builder.WriteString(fmt.Sprintf("%v", _m.ProjectID)) + builder.WriteString(", ") + builder.WriteString("event_type=") + builder.WriteString(_m.EventType) + builder.WriteString(", ") + builder.WriteString("fire_at=") + builder.WriteString(_m.FireAt.Format(time.ANSIC)) + builder.WriteString(", ") + builder.WriteString("payload=") + builder.WriteString(_m.Payload) + builder.WriteString(", ") + builder.WriteString("status=") + builder.WriteString(_m.Status) + builder.WriteString(", ") + builder.WriteString("created_by=") + builder.WriteString(_m.CreatedBy) + builder.WriteString(", ") + if v := _m.FiredAt; v != nil { + builder.WriteString("fired_at=") + builder.WriteString(v.Format(time.ANSIC)) + } + builder.WriteString(", ") + builder.WriteString("error=") + builder.WriteString(_m.Error) + builder.WriteString(", ") + builder.WriteString("schedule_id=") + builder.WriteString(_m.ScheduleID) + builder.WriteString(", ") + builder.WriteString("created=") + builder.WriteString(_m.Created.Format(time.ANSIC)) + builder.WriteByte(')') + return builder.String() +} + +// ScheduledEvents is a parsable slice of ScheduledEvent. +type ScheduledEvents []*ScheduledEvent diff --git a/pkg/ent/scheduledevent/scheduledevent.go b/pkg/ent/scheduledevent/scheduledevent.go new file mode 100644 index 000000000..e69cafb1f --- /dev/null +++ b/pkg/ent/scheduledevent/scheduledevent.go @@ -0,0 +1,135 @@ +// Code generated by ent, DO NOT EDIT. + +package scheduledevent + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "github.com/google/uuid" +) + +const ( + // Label holds the string label denoting the scheduledevent type in the database. + Label = "scheduled_event" + // FieldID holds the string denoting the id field in the database. + FieldID = "id" + // FieldProjectID holds the string denoting the project_id field in the database. + FieldProjectID = "project_id" + // FieldEventType holds the string denoting the event_type field in the database. + FieldEventType = "event_type" + // FieldFireAt holds the string denoting the fire_at field in the database. + FieldFireAt = "fire_at" + // FieldPayload holds the string denoting the payload field in the database. + FieldPayload = "payload" + // FieldStatus holds the string denoting the status field in the database. + FieldStatus = "status" + // FieldCreatedBy holds the string denoting the created_by field in the database. + FieldCreatedBy = "created_by" + // FieldFiredAt holds the string denoting the fired_at field in the database. + FieldFiredAt = "fired_at" + // FieldError holds the string denoting the error field in the database. + FieldError = "error" + // FieldScheduleID holds the string denoting the schedule_id field in the database. + FieldScheduleID = "schedule_id" + // FieldCreated holds the string denoting the created field in the database. + FieldCreated = "created" + // Table holds the table name of the scheduledevent in the database. + Table = "scheduled_events" +) + +// Columns holds all SQL columns for scheduledevent fields. +var Columns = []string{ + FieldID, + FieldProjectID, + FieldEventType, + FieldFireAt, + FieldPayload, + FieldStatus, + FieldCreatedBy, + FieldFiredAt, + FieldError, + FieldScheduleID, + FieldCreated, +} + +// ValidColumn reports if the column name is valid (part of the table columns). +func ValidColumn(column string) bool { + for i := range Columns { + if column == Columns[i] { + return true + } + } + return false +} + +var ( + // EventTypeValidator is a validator for the "event_type" field. It is called by the builders before save. + EventTypeValidator func(string) error + // PayloadValidator is a validator for the "payload" field. It is called by the builders before save. + PayloadValidator func(string) error + // DefaultStatus holds the default value on creation for the "status" field. + DefaultStatus string + // DefaultCreated holds the default value on creation for the "created" field. + DefaultCreated func() time.Time + // DefaultID holds the default value on creation for the "id" field. + DefaultID func() uuid.UUID +) + +// OrderOption defines the ordering options for the ScheduledEvent queries. +type OrderOption func(*sql.Selector) + +// ByID orders the results by the id field. +func ByID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldID, opts...).ToFunc() +} + +// ByProjectID orders the results by the project_id field. +func ByProjectID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldProjectID, opts...).ToFunc() +} + +// ByEventType orders the results by the event_type field. +func ByEventType(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldEventType, opts...).ToFunc() +} + +// ByFireAt orders the results by the fire_at field. +func ByFireAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldFireAt, opts...).ToFunc() +} + +// ByPayload orders the results by the payload field. +func ByPayload(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldPayload, opts...).ToFunc() +} + +// ByStatus orders the results by the status field. +func ByStatus(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldStatus, opts...).ToFunc() +} + +// ByCreatedBy orders the results by the created_by field. +func ByCreatedBy(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCreatedBy, opts...).ToFunc() +} + +// ByFiredAt orders the results by the fired_at field. +func ByFiredAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldFiredAt, opts...).ToFunc() +} + +// ByError orders the results by the error field. +func ByError(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldError, opts...).ToFunc() +} + +// ByScheduleID orders the results by the schedule_id field. +func ByScheduleID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldScheduleID, opts...).ToFunc() +} + +// ByCreated orders the results by the created field. +func ByCreated(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCreated, opts...).ToFunc() +} diff --git a/pkg/ent/scheduledevent/where.go b/pkg/ent/scheduledevent/where.go new file mode 100644 index 000000000..0719b4f37 --- /dev/null +++ b/pkg/ent/scheduledevent/where.go @@ -0,0 +1,711 @@ +// Code generated by ent, DO NOT EDIT. + +package scheduledevent + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/google/uuid" +) + +// ID filters vertices based on their ID field. +func ID(id uuid.UUID) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldEQ(FieldID, id)) +} + +// IDEQ applies the EQ predicate on the ID field. +func IDEQ(id uuid.UUID) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldEQ(FieldID, id)) +} + +// IDNEQ applies the NEQ predicate on the ID field. +func IDNEQ(id uuid.UUID) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldNEQ(FieldID, id)) +} + +// IDIn applies the In predicate on the ID field. +func IDIn(ids ...uuid.UUID) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldIn(FieldID, ids...)) +} + +// IDNotIn applies the NotIn predicate on the ID field. +func IDNotIn(ids ...uuid.UUID) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldNotIn(FieldID, ids...)) +} + +// IDGT applies the GT predicate on the ID field. +func IDGT(id uuid.UUID) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldGT(FieldID, id)) +} + +// IDGTE applies the GTE predicate on the ID field. +func IDGTE(id uuid.UUID) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldGTE(FieldID, id)) +} + +// IDLT applies the LT predicate on the ID field. +func IDLT(id uuid.UUID) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldLT(FieldID, id)) +} + +// IDLTE applies the LTE predicate on the ID field. +func IDLTE(id uuid.UUID) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldLTE(FieldID, id)) +} + +// ProjectID applies equality check predicate on the "project_id" field. It's identical to ProjectIDEQ. +func ProjectID(v uuid.UUID) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldEQ(FieldProjectID, v)) +} + +// EventType applies equality check predicate on the "event_type" field. It's identical to EventTypeEQ. +func EventType(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldEQ(FieldEventType, v)) +} + +// FireAt applies equality check predicate on the "fire_at" field. It's identical to FireAtEQ. +func FireAt(v time.Time) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldEQ(FieldFireAt, v)) +} + +// Payload applies equality check predicate on the "payload" field. It's identical to PayloadEQ. +func Payload(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldEQ(FieldPayload, v)) +} + +// Status applies equality check predicate on the "status" field. It's identical to StatusEQ. +func Status(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldEQ(FieldStatus, v)) +} + +// CreatedBy applies equality check predicate on the "created_by" field. It's identical to CreatedByEQ. +func CreatedBy(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldEQ(FieldCreatedBy, v)) +} + +// FiredAt applies equality check predicate on the "fired_at" field. It's identical to FiredAtEQ. +func FiredAt(v time.Time) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldEQ(FieldFiredAt, v)) +} + +// Error applies equality check predicate on the "error" field. It's identical to ErrorEQ. +func Error(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldEQ(FieldError, v)) +} + +// ScheduleID applies equality check predicate on the "schedule_id" field. It's identical to ScheduleIDEQ. +func ScheduleID(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldEQ(FieldScheduleID, v)) +} + +// Created applies equality check predicate on the "created" field. It's identical to CreatedEQ. +func Created(v time.Time) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldEQ(FieldCreated, v)) +} + +// ProjectIDEQ applies the EQ predicate on the "project_id" field. +func ProjectIDEQ(v uuid.UUID) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldEQ(FieldProjectID, v)) +} + +// ProjectIDNEQ applies the NEQ predicate on the "project_id" field. +func ProjectIDNEQ(v uuid.UUID) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldNEQ(FieldProjectID, v)) +} + +// ProjectIDIn applies the In predicate on the "project_id" field. +func ProjectIDIn(vs ...uuid.UUID) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldIn(FieldProjectID, vs...)) +} + +// ProjectIDNotIn applies the NotIn predicate on the "project_id" field. +func ProjectIDNotIn(vs ...uuid.UUID) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldNotIn(FieldProjectID, vs...)) +} + +// ProjectIDGT applies the GT predicate on the "project_id" field. +func ProjectIDGT(v uuid.UUID) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldGT(FieldProjectID, v)) +} + +// ProjectIDGTE applies the GTE predicate on the "project_id" field. +func ProjectIDGTE(v uuid.UUID) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldGTE(FieldProjectID, v)) +} + +// ProjectIDLT applies the LT predicate on the "project_id" field. +func ProjectIDLT(v uuid.UUID) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldLT(FieldProjectID, v)) +} + +// ProjectIDLTE applies the LTE predicate on the "project_id" field. +func ProjectIDLTE(v uuid.UUID) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldLTE(FieldProjectID, v)) +} + +// EventTypeEQ applies the EQ predicate on the "event_type" field. +func EventTypeEQ(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldEQ(FieldEventType, v)) +} + +// EventTypeNEQ applies the NEQ predicate on the "event_type" field. +func EventTypeNEQ(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldNEQ(FieldEventType, v)) +} + +// EventTypeIn applies the In predicate on the "event_type" field. +func EventTypeIn(vs ...string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldIn(FieldEventType, vs...)) +} + +// EventTypeNotIn applies the NotIn predicate on the "event_type" field. +func EventTypeNotIn(vs ...string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldNotIn(FieldEventType, vs...)) +} + +// EventTypeGT applies the GT predicate on the "event_type" field. +func EventTypeGT(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldGT(FieldEventType, v)) +} + +// EventTypeGTE applies the GTE predicate on the "event_type" field. +func EventTypeGTE(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldGTE(FieldEventType, v)) +} + +// EventTypeLT applies the LT predicate on the "event_type" field. +func EventTypeLT(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldLT(FieldEventType, v)) +} + +// EventTypeLTE applies the LTE predicate on the "event_type" field. +func EventTypeLTE(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldLTE(FieldEventType, v)) +} + +// EventTypeContains applies the Contains predicate on the "event_type" field. +func EventTypeContains(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldContains(FieldEventType, v)) +} + +// EventTypeHasPrefix applies the HasPrefix predicate on the "event_type" field. +func EventTypeHasPrefix(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldHasPrefix(FieldEventType, v)) +} + +// EventTypeHasSuffix applies the HasSuffix predicate on the "event_type" field. +func EventTypeHasSuffix(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldHasSuffix(FieldEventType, v)) +} + +// EventTypeEqualFold applies the EqualFold predicate on the "event_type" field. +func EventTypeEqualFold(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldEqualFold(FieldEventType, v)) +} + +// EventTypeContainsFold applies the ContainsFold predicate on the "event_type" field. +func EventTypeContainsFold(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldContainsFold(FieldEventType, v)) +} + +// FireAtEQ applies the EQ predicate on the "fire_at" field. +func FireAtEQ(v time.Time) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldEQ(FieldFireAt, v)) +} + +// FireAtNEQ applies the NEQ predicate on the "fire_at" field. +func FireAtNEQ(v time.Time) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldNEQ(FieldFireAt, v)) +} + +// FireAtIn applies the In predicate on the "fire_at" field. +func FireAtIn(vs ...time.Time) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldIn(FieldFireAt, vs...)) +} + +// FireAtNotIn applies the NotIn predicate on the "fire_at" field. +func FireAtNotIn(vs ...time.Time) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldNotIn(FieldFireAt, vs...)) +} + +// FireAtGT applies the GT predicate on the "fire_at" field. +func FireAtGT(v time.Time) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldGT(FieldFireAt, v)) +} + +// FireAtGTE applies the GTE predicate on the "fire_at" field. +func FireAtGTE(v time.Time) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldGTE(FieldFireAt, v)) +} + +// FireAtLT applies the LT predicate on the "fire_at" field. +func FireAtLT(v time.Time) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldLT(FieldFireAt, v)) +} + +// FireAtLTE applies the LTE predicate on the "fire_at" field. +func FireAtLTE(v time.Time) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldLTE(FieldFireAt, v)) +} + +// PayloadEQ applies the EQ predicate on the "payload" field. +func PayloadEQ(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldEQ(FieldPayload, v)) +} + +// PayloadNEQ applies the NEQ predicate on the "payload" field. +func PayloadNEQ(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldNEQ(FieldPayload, v)) +} + +// PayloadIn applies the In predicate on the "payload" field. +func PayloadIn(vs ...string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldIn(FieldPayload, vs...)) +} + +// PayloadNotIn applies the NotIn predicate on the "payload" field. +func PayloadNotIn(vs ...string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldNotIn(FieldPayload, vs...)) +} + +// PayloadGT applies the GT predicate on the "payload" field. +func PayloadGT(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldGT(FieldPayload, v)) +} + +// PayloadGTE applies the GTE predicate on the "payload" field. +func PayloadGTE(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldGTE(FieldPayload, v)) +} + +// PayloadLT applies the LT predicate on the "payload" field. +func PayloadLT(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldLT(FieldPayload, v)) +} + +// PayloadLTE applies the LTE predicate on the "payload" field. +func PayloadLTE(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldLTE(FieldPayload, v)) +} + +// PayloadContains applies the Contains predicate on the "payload" field. +func PayloadContains(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldContains(FieldPayload, v)) +} + +// PayloadHasPrefix applies the HasPrefix predicate on the "payload" field. +func PayloadHasPrefix(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldHasPrefix(FieldPayload, v)) +} + +// PayloadHasSuffix applies the HasSuffix predicate on the "payload" field. +func PayloadHasSuffix(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldHasSuffix(FieldPayload, v)) +} + +// PayloadEqualFold applies the EqualFold predicate on the "payload" field. +func PayloadEqualFold(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldEqualFold(FieldPayload, v)) +} + +// PayloadContainsFold applies the ContainsFold predicate on the "payload" field. +func PayloadContainsFold(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldContainsFold(FieldPayload, v)) +} + +// StatusEQ applies the EQ predicate on the "status" field. +func StatusEQ(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldEQ(FieldStatus, v)) +} + +// StatusNEQ applies the NEQ predicate on the "status" field. +func StatusNEQ(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldNEQ(FieldStatus, v)) +} + +// StatusIn applies the In predicate on the "status" field. +func StatusIn(vs ...string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldIn(FieldStatus, vs...)) +} + +// StatusNotIn applies the NotIn predicate on the "status" field. +func StatusNotIn(vs ...string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldNotIn(FieldStatus, vs...)) +} + +// StatusGT applies the GT predicate on the "status" field. +func StatusGT(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldGT(FieldStatus, v)) +} + +// StatusGTE applies the GTE predicate on the "status" field. +func StatusGTE(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldGTE(FieldStatus, v)) +} + +// StatusLT applies the LT predicate on the "status" field. +func StatusLT(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldLT(FieldStatus, v)) +} + +// StatusLTE applies the LTE predicate on the "status" field. +func StatusLTE(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldLTE(FieldStatus, v)) +} + +// StatusContains applies the Contains predicate on the "status" field. +func StatusContains(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldContains(FieldStatus, v)) +} + +// StatusHasPrefix applies the HasPrefix predicate on the "status" field. +func StatusHasPrefix(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldHasPrefix(FieldStatus, v)) +} + +// StatusHasSuffix applies the HasSuffix predicate on the "status" field. +func StatusHasSuffix(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldHasSuffix(FieldStatus, v)) +} + +// StatusEqualFold applies the EqualFold predicate on the "status" field. +func StatusEqualFold(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldEqualFold(FieldStatus, v)) +} + +// StatusContainsFold applies the ContainsFold predicate on the "status" field. +func StatusContainsFold(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldContainsFold(FieldStatus, v)) +} + +// CreatedByEQ applies the EQ predicate on the "created_by" field. +func CreatedByEQ(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldEQ(FieldCreatedBy, v)) +} + +// CreatedByNEQ applies the NEQ predicate on the "created_by" field. +func CreatedByNEQ(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldNEQ(FieldCreatedBy, v)) +} + +// CreatedByIn applies the In predicate on the "created_by" field. +func CreatedByIn(vs ...string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldIn(FieldCreatedBy, vs...)) +} + +// CreatedByNotIn applies the NotIn predicate on the "created_by" field. +func CreatedByNotIn(vs ...string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldNotIn(FieldCreatedBy, vs...)) +} + +// CreatedByGT applies the GT predicate on the "created_by" field. +func CreatedByGT(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldGT(FieldCreatedBy, v)) +} + +// CreatedByGTE applies the GTE predicate on the "created_by" field. +func CreatedByGTE(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldGTE(FieldCreatedBy, v)) +} + +// CreatedByLT applies the LT predicate on the "created_by" field. +func CreatedByLT(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldLT(FieldCreatedBy, v)) +} + +// CreatedByLTE applies the LTE predicate on the "created_by" field. +func CreatedByLTE(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldLTE(FieldCreatedBy, v)) +} + +// CreatedByContains applies the Contains predicate on the "created_by" field. +func CreatedByContains(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldContains(FieldCreatedBy, v)) +} + +// CreatedByHasPrefix applies the HasPrefix predicate on the "created_by" field. +func CreatedByHasPrefix(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldHasPrefix(FieldCreatedBy, v)) +} + +// CreatedByHasSuffix applies the HasSuffix predicate on the "created_by" field. +func CreatedByHasSuffix(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldHasSuffix(FieldCreatedBy, v)) +} + +// CreatedByIsNil applies the IsNil predicate on the "created_by" field. +func CreatedByIsNil() predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldIsNull(FieldCreatedBy)) +} + +// CreatedByNotNil applies the NotNil predicate on the "created_by" field. +func CreatedByNotNil() predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldNotNull(FieldCreatedBy)) +} + +// CreatedByEqualFold applies the EqualFold predicate on the "created_by" field. +func CreatedByEqualFold(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldEqualFold(FieldCreatedBy, v)) +} + +// CreatedByContainsFold applies the ContainsFold predicate on the "created_by" field. +func CreatedByContainsFold(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldContainsFold(FieldCreatedBy, v)) +} + +// FiredAtEQ applies the EQ predicate on the "fired_at" field. +func FiredAtEQ(v time.Time) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldEQ(FieldFiredAt, v)) +} + +// FiredAtNEQ applies the NEQ predicate on the "fired_at" field. +func FiredAtNEQ(v time.Time) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldNEQ(FieldFiredAt, v)) +} + +// FiredAtIn applies the In predicate on the "fired_at" field. +func FiredAtIn(vs ...time.Time) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldIn(FieldFiredAt, vs...)) +} + +// FiredAtNotIn applies the NotIn predicate on the "fired_at" field. +func FiredAtNotIn(vs ...time.Time) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldNotIn(FieldFiredAt, vs...)) +} + +// FiredAtGT applies the GT predicate on the "fired_at" field. +func FiredAtGT(v time.Time) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldGT(FieldFiredAt, v)) +} + +// FiredAtGTE applies the GTE predicate on the "fired_at" field. +func FiredAtGTE(v time.Time) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldGTE(FieldFiredAt, v)) +} + +// FiredAtLT applies the LT predicate on the "fired_at" field. +func FiredAtLT(v time.Time) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldLT(FieldFiredAt, v)) +} + +// FiredAtLTE applies the LTE predicate on the "fired_at" field. +func FiredAtLTE(v time.Time) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldLTE(FieldFiredAt, v)) +} + +// FiredAtIsNil applies the IsNil predicate on the "fired_at" field. +func FiredAtIsNil() predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldIsNull(FieldFiredAt)) +} + +// FiredAtNotNil applies the NotNil predicate on the "fired_at" field. +func FiredAtNotNil() predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldNotNull(FieldFiredAt)) +} + +// ErrorEQ applies the EQ predicate on the "error" field. +func ErrorEQ(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldEQ(FieldError, v)) +} + +// ErrorNEQ applies the NEQ predicate on the "error" field. +func ErrorNEQ(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldNEQ(FieldError, v)) +} + +// ErrorIn applies the In predicate on the "error" field. +func ErrorIn(vs ...string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldIn(FieldError, vs...)) +} + +// ErrorNotIn applies the NotIn predicate on the "error" field. +func ErrorNotIn(vs ...string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldNotIn(FieldError, vs...)) +} + +// ErrorGT applies the GT predicate on the "error" field. +func ErrorGT(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldGT(FieldError, v)) +} + +// ErrorGTE applies the GTE predicate on the "error" field. +func ErrorGTE(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldGTE(FieldError, v)) +} + +// ErrorLT applies the LT predicate on the "error" field. +func ErrorLT(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldLT(FieldError, v)) +} + +// ErrorLTE applies the LTE predicate on the "error" field. +func ErrorLTE(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldLTE(FieldError, v)) +} + +// ErrorContains applies the Contains predicate on the "error" field. +func ErrorContains(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldContains(FieldError, v)) +} + +// ErrorHasPrefix applies the HasPrefix predicate on the "error" field. +func ErrorHasPrefix(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldHasPrefix(FieldError, v)) +} + +// ErrorHasSuffix applies the HasSuffix predicate on the "error" field. +func ErrorHasSuffix(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldHasSuffix(FieldError, v)) +} + +// ErrorIsNil applies the IsNil predicate on the "error" field. +func ErrorIsNil() predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldIsNull(FieldError)) +} + +// ErrorNotNil applies the NotNil predicate on the "error" field. +func ErrorNotNil() predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldNotNull(FieldError)) +} + +// ErrorEqualFold applies the EqualFold predicate on the "error" field. +func ErrorEqualFold(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldEqualFold(FieldError, v)) +} + +// ErrorContainsFold applies the ContainsFold predicate on the "error" field. +func ErrorContainsFold(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldContainsFold(FieldError, v)) +} + +// ScheduleIDEQ applies the EQ predicate on the "schedule_id" field. +func ScheduleIDEQ(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldEQ(FieldScheduleID, v)) +} + +// ScheduleIDNEQ applies the NEQ predicate on the "schedule_id" field. +func ScheduleIDNEQ(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldNEQ(FieldScheduleID, v)) +} + +// ScheduleIDIn applies the In predicate on the "schedule_id" field. +func ScheduleIDIn(vs ...string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldIn(FieldScheduleID, vs...)) +} + +// ScheduleIDNotIn applies the NotIn predicate on the "schedule_id" field. +func ScheduleIDNotIn(vs ...string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldNotIn(FieldScheduleID, vs...)) +} + +// ScheduleIDGT applies the GT predicate on the "schedule_id" field. +func ScheduleIDGT(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldGT(FieldScheduleID, v)) +} + +// ScheduleIDGTE applies the GTE predicate on the "schedule_id" field. +func ScheduleIDGTE(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldGTE(FieldScheduleID, v)) +} + +// ScheduleIDLT applies the LT predicate on the "schedule_id" field. +func ScheduleIDLT(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldLT(FieldScheduleID, v)) +} + +// ScheduleIDLTE applies the LTE predicate on the "schedule_id" field. +func ScheduleIDLTE(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldLTE(FieldScheduleID, v)) +} + +// ScheduleIDContains applies the Contains predicate on the "schedule_id" field. +func ScheduleIDContains(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldContains(FieldScheduleID, v)) +} + +// ScheduleIDHasPrefix applies the HasPrefix predicate on the "schedule_id" field. +func ScheduleIDHasPrefix(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldHasPrefix(FieldScheduleID, v)) +} + +// ScheduleIDHasSuffix applies the HasSuffix predicate on the "schedule_id" field. +func ScheduleIDHasSuffix(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldHasSuffix(FieldScheduleID, v)) +} + +// ScheduleIDIsNil applies the IsNil predicate on the "schedule_id" field. +func ScheduleIDIsNil() predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldIsNull(FieldScheduleID)) +} + +// ScheduleIDNotNil applies the NotNil predicate on the "schedule_id" field. +func ScheduleIDNotNil() predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldNotNull(FieldScheduleID)) +} + +// ScheduleIDEqualFold applies the EqualFold predicate on the "schedule_id" field. +func ScheduleIDEqualFold(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldEqualFold(FieldScheduleID, v)) +} + +// ScheduleIDContainsFold applies the ContainsFold predicate on the "schedule_id" field. +func ScheduleIDContainsFold(v string) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldContainsFold(FieldScheduleID, v)) +} + +// CreatedEQ applies the EQ predicate on the "created" field. +func CreatedEQ(v time.Time) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldEQ(FieldCreated, v)) +} + +// CreatedNEQ applies the NEQ predicate on the "created" field. +func CreatedNEQ(v time.Time) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldNEQ(FieldCreated, v)) +} + +// CreatedIn applies the In predicate on the "created" field. +func CreatedIn(vs ...time.Time) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldIn(FieldCreated, vs...)) +} + +// CreatedNotIn applies the NotIn predicate on the "created" field. +func CreatedNotIn(vs ...time.Time) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldNotIn(FieldCreated, vs...)) +} + +// CreatedGT applies the GT predicate on the "created" field. +func CreatedGT(v time.Time) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldGT(FieldCreated, v)) +} + +// CreatedGTE applies the GTE predicate on the "created" field. +func CreatedGTE(v time.Time) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldGTE(FieldCreated, v)) +} + +// CreatedLT applies the LT predicate on the "created" field. +func CreatedLT(v time.Time) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldLT(FieldCreated, v)) +} + +// CreatedLTE applies the LTE predicate on the "created" field. +func CreatedLTE(v time.Time) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.FieldLTE(FieldCreated, v)) +} + +// And groups predicates with the AND operator between them. +func And(predicates ...predicate.ScheduledEvent) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.AndPredicates(predicates...)) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.ScheduledEvent) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.OrPredicates(predicates...)) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.ScheduledEvent) predicate.ScheduledEvent { + return predicate.ScheduledEvent(sql.NotPredicates(p)) +} diff --git a/pkg/ent/scheduledevent_create.go b/pkg/ent/scheduledevent_create.go new file mode 100644 index 000000000..790facdc5 --- /dev/null +++ b/pkg/ent/scheduledevent_create.go @@ -0,0 +1,1086 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/scheduledevent" + "github.com/google/uuid" +) + +// ScheduledEventCreate is the builder for creating a ScheduledEvent entity. +type ScheduledEventCreate struct { + config + mutation *ScheduledEventMutation + hooks []Hook + conflict []sql.ConflictOption +} + +// SetProjectID sets the "project_id" field. +func (_c *ScheduledEventCreate) SetProjectID(v uuid.UUID) *ScheduledEventCreate { + _c.mutation.SetProjectID(v) + return _c +} + +// SetEventType sets the "event_type" field. +func (_c *ScheduledEventCreate) SetEventType(v string) *ScheduledEventCreate { + _c.mutation.SetEventType(v) + return _c +} + +// SetFireAt sets the "fire_at" field. +func (_c *ScheduledEventCreate) SetFireAt(v time.Time) *ScheduledEventCreate { + _c.mutation.SetFireAt(v) + return _c +} + +// SetPayload sets the "payload" field. +func (_c *ScheduledEventCreate) SetPayload(v string) *ScheduledEventCreate { + _c.mutation.SetPayload(v) + return _c +} + +// SetStatus sets the "status" field. +func (_c *ScheduledEventCreate) SetStatus(v string) *ScheduledEventCreate { + _c.mutation.SetStatus(v) + return _c +} + +// SetNillableStatus sets the "status" field if the given value is not nil. +func (_c *ScheduledEventCreate) SetNillableStatus(v *string) *ScheduledEventCreate { + if v != nil { + _c.SetStatus(*v) + } + return _c +} + +// SetCreatedBy sets the "created_by" field. +func (_c *ScheduledEventCreate) SetCreatedBy(v string) *ScheduledEventCreate { + _c.mutation.SetCreatedBy(v) + return _c +} + +// SetNillableCreatedBy sets the "created_by" field if the given value is not nil. +func (_c *ScheduledEventCreate) SetNillableCreatedBy(v *string) *ScheduledEventCreate { + if v != nil { + _c.SetCreatedBy(*v) + } + return _c +} + +// SetFiredAt sets the "fired_at" field. +func (_c *ScheduledEventCreate) SetFiredAt(v time.Time) *ScheduledEventCreate { + _c.mutation.SetFiredAt(v) + return _c +} + +// SetNillableFiredAt sets the "fired_at" field if the given value is not nil. +func (_c *ScheduledEventCreate) SetNillableFiredAt(v *time.Time) *ScheduledEventCreate { + if v != nil { + _c.SetFiredAt(*v) + } + return _c +} + +// SetError sets the "error" field. +func (_c *ScheduledEventCreate) SetError(v string) *ScheduledEventCreate { + _c.mutation.SetError(v) + return _c +} + +// SetNillableError sets the "error" field if the given value is not nil. +func (_c *ScheduledEventCreate) SetNillableError(v *string) *ScheduledEventCreate { + if v != nil { + _c.SetError(*v) + } + return _c +} + +// SetScheduleID sets the "schedule_id" field. +func (_c *ScheduledEventCreate) SetScheduleID(v string) *ScheduledEventCreate { + _c.mutation.SetScheduleID(v) + return _c +} + +// SetNillableScheduleID sets the "schedule_id" field if the given value is not nil. +func (_c *ScheduledEventCreate) SetNillableScheduleID(v *string) *ScheduledEventCreate { + if v != nil { + _c.SetScheduleID(*v) + } + return _c +} + +// SetCreated sets the "created" field. +func (_c *ScheduledEventCreate) SetCreated(v time.Time) *ScheduledEventCreate { + _c.mutation.SetCreated(v) + return _c +} + +// SetNillableCreated sets the "created" field if the given value is not nil. +func (_c *ScheduledEventCreate) SetNillableCreated(v *time.Time) *ScheduledEventCreate { + if v != nil { + _c.SetCreated(*v) + } + return _c +} + +// SetID sets the "id" field. +func (_c *ScheduledEventCreate) SetID(v uuid.UUID) *ScheduledEventCreate { + _c.mutation.SetID(v) + return _c +} + +// SetNillableID sets the "id" field if the given value is not nil. +func (_c *ScheduledEventCreate) SetNillableID(v *uuid.UUID) *ScheduledEventCreate { + if v != nil { + _c.SetID(*v) + } + return _c +} + +// Mutation returns the ScheduledEventMutation object of the builder. +func (_c *ScheduledEventCreate) Mutation() *ScheduledEventMutation { + return _c.mutation +} + +// Save creates the ScheduledEvent in the database. +func (_c *ScheduledEventCreate) Save(ctx context.Context) (*ScheduledEvent, error) { + _c.defaults() + return withHooks(ctx, _c.sqlSave, _c.mutation, _c.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (_c *ScheduledEventCreate) SaveX(ctx context.Context) *ScheduledEvent { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *ScheduledEventCreate) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *ScheduledEventCreate) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_c *ScheduledEventCreate) defaults() { + if _, ok := _c.mutation.Status(); !ok { + v := scheduledevent.DefaultStatus + _c.mutation.SetStatus(v) + } + if _, ok := _c.mutation.Created(); !ok { + v := scheduledevent.DefaultCreated() + _c.mutation.SetCreated(v) + } + if _, ok := _c.mutation.ID(); !ok { + v := scheduledevent.DefaultID() + _c.mutation.SetID(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_c *ScheduledEventCreate) check() error { + if _, ok := _c.mutation.ProjectID(); !ok { + return &ValidationError{Name: "project_id", err: errors.New(`ent: missing required field "ScheduledEvent.project_id"`)} + } + if _, ok := _c.mutation.EventType(); !ok { + return &ValidationError{Name: "event_type", err: errors.New(`ent: missing required field "ScheduledEvent.event_type"`)} + } + if v, ok := _c.mutation.EventType(); ok { + if err := scheduledevent.EventTypeValidator(v); err != nil { + return &ValidationError{Name: "event_type", err: fmt.Errorf(`ent: validator failed for field "ScheduledEvent.event_type": %w`, err)} + } + } + if _, ok := _c.mutation.FireAt(); !ok { + return &ValidationError{Name: "fire_at", err: errors.New(`ent: missing required field "ScheduledEvent.fire_at"`)} + } + if _, ok := _c.mutation.Payload(); !ok { + return &ValidationError{Name: "payload", err: errors.New(`ent: missing required field "ScheduledEvent.payload"`)} + } + if v, ok := _c.mutation.Payload(); ok { + if err := scheduledevent.PayloadValidator(v); err != nil { + return &ValidationError{Name: "payload", err: fmt.Errorf(`ent: validator failed for field "ScheduledEvent.payload": %w`, err)} + } + } + if _, ok := _c.mutation.Status(); !ok { + return &ValidationError{Name: "status", err: errors.New(`ent: missing required field "ScheduledEvent.status"`)} + } + if _, ok := _c.mutation.Created(); !ok { + return &ValidationError{Name: "created", err: errors.New(`ent: missing required field "ScheduledEvent.created"`)} + } + return nil +} + +func (_c *ScheduledEventCreate) sqlSave(ctx context.Context) (*ScheduledEvent, error) { + if err := _c.check(); err != nil { + return nil, err + } + _node, _spec := _c.createSpec() + if err := sqlgraph.CreateNode(ctx, _c.driver, _spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + if _spec.ID.Value != nil { + if id, ok := _spec.ID.Value.(*uuid.UUID); ok { + _node.ID = *id + } else if err := _node.ID.Scan(_spec.ID.Value); err != nil { + return nil, err + } + } + _c.mutation.id = &_node.ID + _c.mutation.done = true + return _node, nil +} + +func (_c *ScheduledEventCreate) createSpec() (*ScheduledEvent, *sqlgraph.CreateSpec) { + var ( + _node = &ScheduledEvent{config: _c.config} + _spec = sqlgraph.NewCreateSpec(scheduledevent.Table, sqlgraph.NewFieldSpec(scheduledevent.FieldID, field.TypeUUID)) + ) + _spec.OnConflict = _c.conflict + if id, ok := _c.mutation.ID(); ok { + _node.ID = id + _spec.ID.Value = &id + } + if value, ok := _c.mutation.ProjectID(); ok { + _spec.SetField(scheduledevent.FieldProjectID, field.TypeUUID, value) + _node.ProjectID = value + } + if value, ok := _c.mutation.EventType(); ok { + _spec.SetField(scheduledevent.FieldEventType, field.TypeString, value) + _node.EventType = value + } + if value, ok := _c.mutation.FireAt(); ok { + _spec.SetField(scheduledevent.FieldFireAt, field.TypeTime, value) + _node.FireAt = value + } + if value, ok := _c.mutation.Payload(); ok { + _spec.SetField(scheduledevent.FieldPayload, field.TypeString, value) + _node.Payload = value + } + if value, ok := _c.mutation.Status(); ok { + _spec.SetField(scheduledevent.FieldStatus, field.TypeString, value) + _node.Status = value + } + if value, ok := _c.mutation.CreatedBy(); ok { + _spec.SetField(scheduledevent.FieldCreatedBy, field.TypeString, value) + _node.CreatedBy = value + } + if value, ok := _c.mutation.FiredAt(); ok { + _spec.SetField(scheduledevent.FieldFiredAt, field.TypeTime, value) + _node.FiredAt = &value + } + if value, ok := _c.mutation.Error(); ok { + _spec.SetField(scheduledevent.FieldError, field.TypeString, value) + _node.Error = value + } + if value, ok := _c.mutation.ScheduleID(); ok { + _spec.SetField(scheduledevent.FieldScheduleID, field.TypeString, value) + _node.ScheduleID = value + } + if value, ok := _c.mutation.Created(); ok { + _spec.SetField(scheduledevent.FieldCreated, field.TypeTime, value) + _node.Created = value + } + return _node, _spec +} + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.ScheduledEvent.Create(). +// SetProjectID(v). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.ScheduledEventUpsert) { +// SetProjectID(v+v). +// }). +// Exec(ctx) +func (_c *ScheduledEventCreate) OnConflict(opts ...sql.ConflictOption) *ScheduledEventUpsertOne { + _c.conflict = opts + return &ScheduledEventUpsertOne{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.ScheduledEvent.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *ScheduledEventCreate) OnConflictColumns(columns ...string) *ScheduledEventUpsertOne { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &ScheduledEventUpsertOne{ + create: _c, + } +} + +type ( + // ScheduledEventUpsertOne is the builder for "upsert"-ing + // one ScheduledEvent node. + ScheduledEventUpsertOne struct { + create *ScheduledEventCreate + } + + // ScheduledEventUpsert is the "OnConflict" setter. + ScheduledEventUpsert struct { + *sql.UpdateSet + } +) + +// SetProjectID sets the "project_id" field. +func (u *ScheduledEventUpsert) SetProjectID(v uuid.UUID) *ScheduledEventUpsert { + u.Set(scheduledevent.FieldProjectID, v) + return u +} + +// UpdateProjectID sets the "project_id" field to the value that was provided on create. +func (u *ScheduledEventUpsert) UpdateProjectID() *ScheduledEventUpsert { + u.SetExcluded(scheduledevent.FieldProjectID) + return u +} + +// SetEventType sets the "event_type" field. +func (u *ScheduledEventUpsert) SetEventType(v string) *ScheduledEventUpsert { + u.Set(scheduledevent.FieldEventType, v) + return u +} + +// UpdateEventType sets the "event_type" field to the value that was provided on create. +func (u *ScheduledEventUpsert) UpdateEventType() *ScheduledEventUpsert { + u.SetExcluded(scheduledevent.FieldEventType) + return u +} + +// SetFireAt sets the "fire_at" field. +func (u *ScheduledEventUpsert) SetFireAt(v time.Time) *ScheduledEventUpsert { + u.Set(scheduledevent.FieldFireAt, v) + return u +} + +// UpdateFireAt sets the "fire_at" field to the value that was provided on create. +func (u *ScheduledEventUpsert) UpdateFireAt() *ScheduledEventUpsert { + u.SetExcluded(scheduledevent.FieldFireAt) + return u +} + +// SetPayload sets the "payload" field. +func (u *ScheduledEventUpsert) SetPayload(v string) *ScheduledEventUpsert { + u.Set(scheduledevent.FieldPayload, v) + return u +} + +// UpdatePayload sets the "payload" field to the value that was provided on create. +func (u *ScheduledEventUpsert) UpdatePayload() *ScheduledEventUpsert { + u.SetExcluded(scheduledevent.FieldPayload) + return u +} + +// SetStatus sets the "status" field. +func (u *ScheduledEventUpsert) SetStatus(v string) *ScheduledEventUpsert { + u.Set(scheduledevent.FieldStatus, v) + return u +} + +// UpdateStatus sets the "status" field to the value that was provided on create. +func (u *ScheduledEventUpsert) UpdateStatus() *ScheduledEventUpsert { + u.SetExcluded(scheduledevent.FieldStatus) + return u +} + +// SetCreatedBy sets the "created_by" field. +func (u *ScheduledEventUpsert) SetCreatedBy(v string) *ScheduledEventUpsert { + u.Set(scheduledevent.FieldCreatedBy, v) + return u +} + +// UpdateCreatedBy sets the "created_by" field to the value that was provided on create. +func (u *ScheduledEventUpsert) UpdateCreatedBy() *ScheduledEventUpsert { + u.SetExcluded(scheduledevent.FieldCreatedBy) + return u +} + +// ClearCreatedBy clears the value of the "created_by" field. +func (u *ScheduledEventUpsert) ClearCreatedBy() *ScheduledEventUpsert { + u.SetNull(scheduledevent.FieldCreatedBy) + return u +} + +// SetFiredAt sets the "fired_at" field. +func (u *ScheduledEventUpsert) SetFiredAt(v time.Time) *ScheduledEventUpsert { + u.Set(scheduledevent.FieldFiredAt, v) + return u +} + +// UpdateFiredAt sets the "fired_at" field to the value that was provided on create. +func (u *ScheduledEventUpsert) UpdateFiredAt() *ScheduledEventUpsert { + u.SetExcluded(scheduledevent.FieldFiredAt) + return u +} + +// ClearFiredAt clears the value of the "fired_at" field. +func (u *ScheduledEventUpsert) ClearFiredAt() *ScheduledEventUpsert { + u.SetNull(scheduledevent.FieldFiredAt) + return u +} + +// SetError sets the "error" field. +func (u *ScheduledEventUpsert) SetError(v string) *ScheduledEventUpsert { + u.Set(scheduledevent.FieldError, v) + return u +} + +// UpdateError sets the "error" field to the value that was provided on create. +func (u *ScheduledEventUpsert) UpdateError() *ScheduledEventUpsert { + u.SetExcluded(scheduledevent.FieldError) + return u +} + +// ClearError clears the value of the "error" field. +func (u *ScheduledEventUpsert) ClearError() *ScheduledEventUpsert { + u.SetNull(scheduledevent.FieldError) + return u +} + +// SetScheduleID sets the "schedule_id" field. +func (u *ScheduledEventUpsert) SetScheduleID(v string) *ScheduledEventUpsert { + u.Set(scheduledevent.FieldScheduleID, v) + return u +} + +// UpdateScheduleID sets the "schedule_id" field to the value that was provided on create. +func (u *ScheduledEventUpsert) UpdateScheduleID() *ScheduledEventUpsert { + u.SetExcluded(scheduledevent.FieldScheduleID) + return u +} + +// ClearScheduleID clears the value of the "schedule_id" field. +func (u *ScheduledEventUpsert) ClearScheduleID() *ScheduledEventUpsert { + u.SetNull(scheduledevent.FieldScheduleID) + return u +} + +// UpdateNewValues updates the mutable fields using the new values that were set on create except the ID field. +// Using this option is equivalent to using: +// +// client.ScheduledEvent.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(scheduledevent.FieldID) +// }), +// ). +// Exec(ctx) +func (u *ScheduledEventUpsertOne) UpdateNewValues() *ScheduledEventUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + if _, exists := u.create.mutation.ID(); exists { + s.SetIgnore(scheduledevent.FieldID) + } + if _, exists := u.create.mutation.Created(); exists { + s.SetIgnore(scheduledevent.FieldCreated) + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.ScheduledEvent.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *ScheduledEventUpsertOne) Ignore() *ScheduledEventUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *ScheduledEventUpsertOne) DoNothing() *ScheduledEventUpsertOne { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the ScheduledEventCreate.OnConflict +// documentation for more info. +func (u *ScheduledEventUpsertOne) Update(set func(*ScheduledEventUpsert)) *ScheduledEventUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&ScheduledEventUpsert{UpdateSet: update}) + })) + return u +} + +// SetProjectID sets the "project_id" field. +func (u *ScheduledEventUpsertOne) SetProjectID(v uuid.UUID) *ScheduledEventUpsertOne { + return u.Update(func(s *ScheduledEventUpsert) { + s.SetProjectID(v) + }) +} + +// UpdateProjectID sets the "project_id" field to the value that was provided on create. +func (u *ScheduledEventUpsertOne) UpdateProjectID() *ScheduledEventUpsertOne { + return u.Update(func(s *ScheduledEventUpsert) { + s.UpdateProjectID() + }) +} + +// SetEventType sets the "event_type" field. +func (u *ScheduledEventUpsertOne) SetEventType(v string) *ScheduledEventUpsertOne { + return u.Update(func(s *ScheduledEventUpsert) { + s.SetEventType(v) + }) +} + +// UpdateEventType sets the "event_type" field to the value that was provided on create. +func (u *ScheduledEventUpsertOne) UpdateEventType() *ScheduledEventUpsertOne { + return u.Update(func(s *ScheduledEventUpsert) { + s.UpdateEventType() + }) +} + +// SetFireAt sets the "fire_at" field. +func (u *ScheduledEventUpsertOne) SetFireAt(v time.Time) *ScheduledEventUpsertOne { + return u.Update(func(s *ScheduledEventUpsert) { + s.SetFireAt(v) + }) +} + +// UpdateFireAt sets the "fire_at" field to the value that was provided on create. +func (u *ScheduledEventUpsertOne) UpdateFireAt() *ScheduledEventUpsertOne { + return u.Update(func(s *ScheduledEventUpsert) { + s.UpdateFireAt() + }) +} + +// SetPayload sets the "payload" field. +func (u *ScheduledEventUpsertOne) SetPayload(v string) *ScheduledEventUpsertOne { + return u.Update(func(s *ScheduledEventUpsert) { + s.SetPayload(v) + }) +} + +// UpdatePayload sets the "payload" field to the value that was provided on create. +func (u *ScheduledEventUpsertOne) UpdatePayload() *ScheduledEventUpsertOne { + return u.Update(func(s *ScheduledEventUpsert) { + s.UpdatePayload() + }) +} + +// SetStatus sets the "status" field. +func (u *ScheduledEventUpsertOne) SetStatus(v string) *ScheduledEventUpsertOne { + return u.Update(func(s *ScheduledEventUpsert) { + s.SetStatus(v) + }) +} + +// UpdateStatus sets the "status" field to the value that was provided on create. +func (u *ScheduledEventUpsertOne) UpdateStatus() *ScheduledEventUpsertOne { + return u.Update(func(s *ScheduledEventUpsert) { + s.UpdateStatus() + }) +} + +// SetCreatedBy sets the "created_by" field. +func (u *ScheduledEventUpsertOne) SetCreatedBy(v string) *ScheduledEventUpsertOne { + return u.Update(func(s *ScheduledEventUpsert) { + s.SetCreatedBy(v) + }) +} + +// UpdateCreatedBy sets the "created_by" field to the value that was provided on create. +func (u *ScheduledEventUpsertOne) UpdateCreatedBy() *ScheduledEventUpsertOne { + return u.Update(func(s *ScheduledEventUpsert) { + s.UpdateCreatedBy() + }) +} + +// ClearCreatedBy clears the value of the "created_by" field. +func (u *ScheduledEventUpsertOne) ClearCreatedBy() *ScheduledEventUpsertOne { + return u.Update(func(s *ScheduledEventUpsert) { + s.ClearCreatedBy() + }) +} + +// SetFiredAt sets the "fired_at" field. +func (u *ScheduledEventUpsertOne) SetFiredAt(v time.Time) *ScheduledEventUpsertOne { + return u.Update(func(s *ScheduledEventUpsert) { + s.SetFiredAt(v) + }) +} + +// UpdateFiredAt sets the "fired_at" field to the value that was provided on create. +func (u *ScheduledEventUpsertOne) UpdateFiredAt() *ScheduledEventUpsertOne { + return u.Update(func(s *ScheduledEventUpsert) { + s.UpdateFiredAt() + }) +} + +// ClearFiredAt clears the value of the "fired_at" field. +func (u *ScheduledEventUpsertOne) ClearFiredAt() *ScheduledEventUpsertOne { + return u.Update(func(s *ScheduledEventUpsert) { + s.ClearFiredAt() + }) +} + +// SetError sets the "error" field. +func (u *ScheduledEventUpsertOne) SetError(v string) *ScheduledEventUpsertOne { + return u.Update(func(s *ScheduledEventUpsert) { + s.SetError(v) + }) +} + +// UpdateError sets the "error" field to the value that was provided on create. +func (u *ScheduledEventUpsertOne) UpdateError() *ScheduledEventUpsertOne { + return u.Update(func(s *ScheduledEventUpsert) { + s.UpdateError() + }) +} + +// ClearError clears the value of the "error" field. +func (u *ScheduledEventUpsertOne) ClearError() *ScheduledEventUpsertOne { + return u.Update(func(s *ScheduledEventUpsert) { + s.ClearError() + }) +} + +// SetScheduleID sets the "schedule_id" field. +func (u *ScheduledEventUpsertOne) SetScheduleID(v string) *ScheduledEventUpsertOne { + return u.Update(func(s *ScheduledEventUpsert) { + s.SetScheduleID(v) + }) +} + +// UpdateScheduleID sets the "schedule_id" field to the value that was provided on create. +func (u *ScheduledEventUpsertOne) UpdateScheduleID() *ScheduledEventUpsertOne { + return u.Update(func(s *ScheduledEventUpsert) { + s.UpdateScheduleID() + }) +} + +// ClearScheduleID clears the value of the "schedule_id" field. +func (u *ScheduledEventUpsertOne) ClearScheduleID() *ScheduledEventUpsertOne { + return u.Update(func(s *ScheduledEventUpsert) { + s.ClearScheduleID() + }) +} + +// Exec executes the query. +func (u *ScheduledEventUpsertOne) Exec(ctx context.Context) error { + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for ScheduledEventCreate.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *ScheduledEventUpsertOne) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} + +// Exec executes the UPSERT query and returns the inserted/updated ID. +func (u *ScheduledEventUpsertOne) ID(ctx context.Context) (id uuid.UUID, err error) { + if u.create.driver.Dialect() == dialect.MySQL { + // In case of "ON CONFLICT", there is no way to get back non-numeric ID + // fields from the database since MySQL does not support the RETURNING clause. + return id, errors.New("ent: ScheduledEventUpsertOne.ID is not supported by MySQL driver. Use ScheduledEventUpsertOne.Exec instead") + } + node, err := u.create.Save(ctx) + if err != nil { + return id, err + } + return node.ID, nil +} + +// IDX is like ID, but panics if an error occurs. +func (u *ScheduledEventUpsertOne) IDX(ctx context.Context) uuid.UUID { + id, err := u.ID(ctx) + if err != nil { + panic(err) + } + return id +} + +// ScheduledEventCreateBulk is the builder for creating many ScheduledEvent entities in bulk. +type ScheduledEventCreateBulk struct { + config + err error + builders []*ScheduledEventCreate + conflict []sql.ConflictOption +} + +// Save creates the ScheduledEvent entities in the database. +func (_c *ScheduledEventCreateBulk) Save(ctx context.Context) ([]*ScheduledEvent, error) { + if _c.err != nil { + return nil, _c.err + } + specs := make([]*sqlgraph.CreateSpec, len(_c.builders)) + nodes := make([]*ScheduledEvent, len(_c.builders)) + mutators := make([]Mutator, len(_c.builders)) + for i := range _c.builders { + func(i int, root context.Context) { + builder := _c.builders[i] + builder.defaults() + var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { + mutation, ok := m.(*ScheduledEventMutation) + if !ok { + return nil, fmt.Errorf("unexpected mutation type %T", m) + } + if err := builder.check(); err != nil { + return nil, err + } + builder.mutation = mutation + var err error + nodes[i], specs[i] = builder.createSpec() + if i < len(mutators)-1 { + _, err = mutators[i+1].Mutate(root, _c.builders[i+1].mutation) + } else { + spec := &sqlgraph.BatchCreateSpec{Nodes: specs} + spec.OnConflict = _c.conflict + // Invoke the actual operation on the latest mutation in the chain. + if err = sqlgraph.BatchCreate(ctx, _c.driver, spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + } + } + if err != nil { + return nil, err + } + mutation.id = &nodes[i].ID + mutation.done = true + return nodes[i], nil + }) + for i := len(builder.hooks) - 1; i >= 0; i-- { + mut = builder.hooks[i](mut) + } + mutators[i] = mut + }(i, ctx) + } + if len(mutators) > 0 { + if _, err := mutators[0].Mutate(ctx, _c.builders[0].mutation); err != nil { + return nil, err + } + } + return nodes, nil +} + +// SaveX is like Save, but panics if an error occurs. +func (_c *ScheduledEventCreateBulk) SaveX(ctx context.Context) []*ScheduledEvent { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *ScheduledEventCreateBulk) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *ScheduledEventCreateBulk) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.ScheduledEvent.CreateBulk(builders...). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.ScheduledEventUpsert) { +// SetProjectID(v+v). +// }). +// Exec(ctx) +func (_c *ScheduledEventCreateBulk) OnConflict(opts ...sql.ConflictOption) *ScheduledEventUpsertBulk { + _c.conflict = opts + return &ScheduledEventUpsertBulk{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.ScheduledEvent.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *ScheduledEventCreateBulk) OnConflictColumns(columns ...string) *ScheduledEventUpsertBulk { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &ScheduledEventUpsertBulk{ + create: _c, + } +} + +// ScheduledEventUpsertBulk is the builder for "upsert"-ing +// a bulk of ScheduledEvent nodes. +type ScheduledEventUpsertBulk struct { + create *ScheduledEventCreateBulk +} + +// UpdateNewValues updates the mutable fields using the new values that +// were set on create. Using this option is equivalent to using: +// +// client.ScheduledEvent.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(scheduledevent.FieldID) +// }), +// ). +// Exec(ctx) +func (u *ScheduledEventUpsertBulk) UpdateNewValues() *ScheduledEventUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + for _, b := range u.create.builders { + if _, exists := b.mutation.ID(); exists { + s.SetIgnore(scheduledevent.FieldID) + } + if _, exists := b.mutation.Created(); exists { + s.SetIgnore(scheduledevent.FieldCreated) + } + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.ScheduledEvent.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *ScheduledEventUpsertBulk) Ignore() *ScheduledEventUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *ScheduledEventUpsertBulk) DoNothing() *ScheduledEventUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the ScheduledEventCreateBulk.OnConflict +// documentation for more info. +func (u *ScheduledEventUpsertBulk) Update(set func(*ScheduledEventUpsert)) *ScheduledEventUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&ScheduledEventUpsert{UpdateSet: update}) + })) + return u +} + +// SetProjectID sets the "project_id" field. +func (u *ScheduledEventUpsertBulk) SetProjectID(v uuid.UUID) *ScheduledEventUpsertBulk { + return u.Update(func(s *ScheduledEventUpsert) { + s.SetProjectID(v) + }) +} + +// UpdateProjectID sets the "project_id" field to the value that was provided on create. +func (u *ScheduledEventUpsertBulk) UpdateProjectID() *ScheduledEventUpsertBulk { + return u.Update(func(s *ScheduledEventUpsert) { + s.UpdateProjectID() + }) +} + +// SetEventType sets the "event_type" field. +func (u *ScheduledEventUpsertBulk) SetEventType(v string) *ScheduledEventUpsertBulk { + return u.Update(func(s *ScheduledEventUpsert) { + s.SetEventType(v) + }) +} + +// UpdateEventType sets the "event_type" field to the value that was provided on create. +func (u *ScheduledEventUpsertBulk) UpdateEventType() *ScheduledEventUpsertBulk { + return u.Update(func(s *ScheduledEventUpsert) { + s.UpdateEventType() + }) +} + +// SetFireAt sets the "fire_at" field. +func (u *ScheduledEventUpsertBulk) SetFireAt(v time.Time) *ScheduledEventUpsertBulk { + return u.Update(func(s *ScheduledEventUpsert) { + s.SetFireAt(v) + }) +} + +// UpdateFireAt sets the "fire_at" field to the value that was provided on create. +func (u *ScheduledEventUpsertBulk) UpdateFireAt() *ScheduledEventUpsertBulk { + return u.Update(func(s *ScheduledEventUpsert) { + s.UpdateFireAt() + }) +} + +// SetPayload sets the "payload" field. +func (u *ScheduledEventUpsertBulk) SetPayload(v string) *ScheduledEventUpsertBulk { + return u.Update(func(s *ScheduledEventUpsert) { + s.SetPayload(v) + }) +} + +// UpdatePayload sets the "payload" field to the value that was provided on create. +func (u *ScheduledEventUpsertBulk) UpdatePayload() *ScheduledEventUpsertBulk { + return u.Update(func(s *ScheduledEventUpsert) { + s.UpdatePayload() + }) +} + +// SetStatus sets the "status" field. +func (u *ScheduledEventUpsertBulk) SetStatus(v string) *ScheduledEventUpsertBulk { + return u.Update(func(s *ScheduledEventUpsert) { + s.SetStatus(v) + }) +} + +// UpdateStatus sets the "status" field to the value that was provided on create. +func (u *ScheduledEventUpsertBulk) UpdateStatus() *ScheduledEventUpsertBulk { + return u.Update(func(s *ScheduledEventUpsert) { + s.UpdateStatus() + }) +} + +// SetCreatedBy sets the "created_by" field. +func (u *ScheduledEventUpsertBulk) SetCreatedBy(v string) *ScheduledEventUpsertBulk { + return u.Update(func(s *ScheduledEventUpsert) { + s.SetCreatedBy(v) + }) +} + +// UpdateCreatedBy sets the "created_by" field to the value that was provided on create. +func (u *ScheduledEventUpsertBulk) UpdateCreatedBy() *ScheduledEventUpsertBulk { + return u.Update(func(s *ScheduledEventUpsert) { + s.UpdateCreatedBy() + }) +} + +// ClearCreatedBy clears the value of the "created_by" field. +func (u *ScheduledEventUpsertBulk) ClearCreatedBy() *ScheduledEventUpsertBulk { + return u.Update(func(s *ScheduledEventUpsert) { + s.ClearCreatedBy() + }) +} + +// SetFiredAt sets the "fired_at" field. +func (u *ScheduledEventUpsertBulk) SetFiredAt(v time.Time) *ScheduledEventUpsertBulk { + return u.Update(func(s *ScheduledEventUpsert) { + s.SetFiredAt(v) + }) +} + +// UpdateFiredAt sets the "fired_at" field to the value that was provided on create. +func (u *ScheduledEventUpsertBulk) UpdateFiredAt() *ScheduledEventUpsertBulk { + return u.Update(func(s *ScheduledEventUpsert) { + s.UpdateFiredAt() + }) +} + +// ClearFiredAt clears the value of the "fired_at" field. +func (u *ScheduledEventUpsertBulk) ClearFiredAt() *ScheduledEventUpsertBulk { + return u.Update(func(s *ScheduledEventUpsert) { + s.ClearFiredAt() + }) +} + +// SetError sets the "error" field. +func (u *ScheduledEventUpsertBulk) SetError(v string) *ScheduledEventUpsertBulk { + return u.Update(func(s *ScheduledEventUpsert) { + s.SetError(v) + }) +} + +// UpdateError sets the "error" field to the value that was provided on create. +func (u *ScheduledEventUpsertBulk) UpdateError() *ScheduledEventUpsertBulk { + return u.Update(func(s *ScheduledEventUpsert) { + s.UpdateError() + }) +} + +// ClearError clears the value of the "error" field. +func (u *ScheduledEventUpsertBulk) ClearError() *ScheduledEventUpsertBulk { + return u.Update(func(s *ScheduledEventUpsert) { + s.ClearError() + }) +} + +// SetScheduleID sets the "schedule_id" field. +func (u *ScheduledEventUpsertBulk) SetScheduleID(v string) *ScheduledEventUpsertBulk { + return u.Update(func(s *ScheduledEventUpsert) { + s.SetScheduleID(v) + }) +} + +// UpdateScheduleID sets the "schedule_id" field to the value that was provided on create. +func (u *ScheduledEventUpsertBulk) UpdateScheduleID() *ScheduledEventUpsertBulk { + return u.Update(func(s *ScheduledEventUpsert) { + s.UpdateScheduleID() + }) +} + +// ClearScheduleID clears the value of the "schedule_id" field. +func (u *ScheduledEventUpsertBulk) ClearScheduleID() *ScheduledEventUpsertBulk { + return u.Update(func(s *ScheduledEventUpsert) { + s.ClearScheduleID() + }) +} + +// Exec executes the query. +func (u *ScheduledEventUpsertBulk) Exec(ctx context.Context) error { + if u.create.err != nil { + return u.create.err + } + for i, b := range u.create.builders { + if len(b.conflict) != 0 { + return fmt.Errorf("ent: OnConflict was set for builder %d. Set it on the ScheduledEventCreateBulk instead", i) + } + } + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for ScheduledEventCreateBulk.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *ScheduledEventUpsertBulk) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/pkg/ent/scheduledevent_delete.go b/pkg/ent/scheduledevent_delete.go new file mode 100644 index 000000000..14bc697b5 --- /dev/null +++ b/pkg/ent/scheduledevent_delete.go @@ -0,0 +1,88 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/GoogleCloudPlatform/scion/pkg/ent/scheduledevent" +) + +// ScheduledEventDelete is the builder for deleting a ScheduledEvent entity. +type ScheduledEventDelete struct { + config + hooks []Hook + mutation *ScheduledEventMutation +} + +// Where appends a list predicates to the ScheduledEventDelete builder. +func (_d *ScheduledEventDelete) Where(ps ...predicate.ScheduledEvent) *ScheduledEventDelete { + _d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (_d *ScheduledEventDelete) Exec(ctx context.Context) (int, error) { + return withHooks(ctx, _d.sqlExec, _d.mutation, _d.hooks) +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *ScheduledEventDelete) ExecX(ctx context.Context) int { + n, err := _d.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (_d *ScheduledEventDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(scheduledevent.Table, sqlgraph.NewFieldSpec(scheduledevent.FieldID, field.TypeUUID)) + if ps := _d.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + affected, err := sqlgraph.DeleteNodes(ctx, _d.driver, _spec) + if err != nil && sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + _d.mutation.done = true + return affected, err +} + +// ScheduledEventDeleteOne is the builder for deleting a single ScheduledEvent entity. +type ScheduledEventDeleteOne struct { + _d *ScheduledEventDelete +} + +// Where appends a list predicates to the ScheduledEventDelete builder. +func (_d *ScheduledEventDeleteOne) Where(ps ...predicate.ScheduledEvent) *ScheduledEventDeleteOne { + _d._d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query. +func (_d *ScheduledEventDeleteOne) Exec(ctx context.Context) error { + n, err := _d._d.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{scheduledevent.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *ScheduledEventDeleteOne) ExecX(ctx context.Context) { + if err := _d.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/pkg/ent/scheduledevent_query.go b/pkg/ent/scheduledevent_query.go new file mode 100644 index 000000000..1d55bbb76 --- /dev/null +++ b/pkg/ent/scheduledevent_query.go @@ -0,0 +1,565 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "fmt" + "math" + + "entgo.io/ent" + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/GoogleCloudPlatform/scion/pkg/ent/scheduledevent" + "github.com/google/uuid" +) + +// ScheduledEventQuery is the builder for querying ScheduledEvent entities. +type ScheduledEventQuery struct { + config + ctx *QueryContext + order []scheduledevent.OrderOption + inters []Interceptor + predicates []predicate.ScheduledEvent + modifiers []func(*sql.Selector) + // intermediate query (i.e. traversal path). + sql *sql.Selector + path func(context.Context) (*sql.Selector, error) +} + +// Where adds a new predicate for the ScheduledEventQuery builder. +func (_q *ScheduledEventQuery) Where(ps ...predicate.ScheduledEvent) *ScheduledEventQuery { + _q.predicates = append(_q.predicates, ps...) + return _q +} + +// Limit the number of records to be returned by this query. +func (_q *ScheduledEventQuery) Limit(limit int) *ScheduledEventQuery { + _q.ctx.Limit = &limit + return _q +} + +// Offset to start from. +func (_q *ScheduledEventQuery) Offset(offset int) *ScheduledEventQuery { + _q.ctx.Offset = &offset + return _q +} + +// Unique configures the query builder to filter duplicate records on query. +// By default, unique is set to true, and can be disabled using this method. +func (_q *ScheduledEventQuery) Unique(unique bool) *ScheduledEventQuery { + _q.ctx.Unique = &unique + return _q +} + +// Order specifies how the records should be ordered. +func (_q *ScheduledEventQuery) Order(o ...scheduledevent.OrderOption) *ScheduledEventQuery { + _q.order = append(_q.order, o...) + return _q +} + +// First returns the first ScheduledEvent entity from the query. +// Returns a *NotFoundError when no ScheduledEvent was found. +func (_q *ScheduledEventQuery) First(ctx context.Context) (*ScheduledEvent, error) { + nodes, err := _q.Limit(1).All(setContextOp(ctx, _q.ctx, ent.OpQueryFirst)) + if err != nil { + return nil, err + } + if len(nodes) == 0 { + return nil, &NotFoundError{scheduledevent.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (_q *ScheduledEventQuery) FirstX(ctx context.Context) *ScheduledEvent { + node, err := _q.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// FirstID returns the first ScheduledEvent ID from the query. +// Returns a *NotFoundError when no ScheduledEvent ID was found. +func (_q *ScheduledEventQuery) FirstID(ctx context.Context) (id uuid.UUID, err error) { + var ids []uuid.UUID + if ids, err = _q.Limit(1).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryFirstID)); err != nil { + return + } + if len(ids) == 0 { + err = &NotFoundError{scheduledevent.Label} + return + } + return ids[0], nil +} + +// FirstIDX is like FirstID, but panics if an error occurs. +func (_q *ScheduledEventQuery) FirstIDX(ctx context.Context) uuid.UUID { + id, err := _q.FirstID(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return id +} + +// Only returns a single ScheduledEvent entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one ScheduledEvent entity is found. +// Returns a *NotFoundError when no ScheduledEvent entities are found. +func (_q *ScheduledEventQuery) Only(ctx context.Context) (*ScheduledEvent, error) { + nodes, err := _q.Limit(2).All(setContextOp(ctx, _q.ctx, ent.OpQueryOnly)) + if err != nil { + return nil, err + } + switch len(nodes) { + case 1: + return nodes[0], nil + case 0: + return nil, &NotFoundError{scheduledevent.Label} + default: + return nil, &NotSingularError{scheduledevent.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (_q *ScheduledEventQuery) OnlyX(ctx context.Context) *ScheduledEvent { + node, err := _q.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// OnlyID is like Only, but returns the only ScheduledEvent ID in the query. +// Returns a *NotSingularError when more than one ScheduledEvent ID is found. +// Returns a *NotFoundError when no entities are found. +func (_q *ScheduledEventQuery) OnlyID(ctx context.Context) (id uuid.UUID, err error) { + var ids []uuid.UUID + if ids, err = _q.Limit(2).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryOnlyID)); err != nil { + return + } + switch len(ids) { + case 1: + id = ids[0] + case 0: + err = &NotFoundError{scheduledevent.Label} + default: + err = &NotSingularError{scheduledevent.Label} + } + return +} + +// OnlyIDX is like OnlyID, but panics if an error occurs. +func (_q *ScheduledEventQuery) OnlyIDX(ctx context.Context) uuid.UUID { + id, err := _q.OnlyID(ctx) + if err != nil { + panic(err) + } + return id +} + +// All executes the query and returns a list of ScheduledEvents. +func (_q *ScheduledEventQuery) All(ctx context.Context) ([]*ScheduledEvent, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryAll) + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*ScheduledEvent, *ScheduledEventQuery]() + return withInterceptors[[]*ScheduledEvent](ctx, _q, qr, _q.inters) +} + +// AllX is like All, but panics if an error occurs. +func (_q *ScheduledEventQuery) AllX(ctx context.Context) []*ScheduledEvent { + nodes, err := _q.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// IDs executes the query and returns a list of ScheduledEvent IDs. +func (_q *ScheduledEventQuery) IDs(ctx context.Context) (ids []uuid.UUID, err error) { + if _q.ctx.Unique == nil && _q.path != nil { + _q.Unique(true) + } + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryIDs) + if err = _q.Select(scheduledevent.FieldID).Scan(ctx, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// IDsX is like IDs, but panics if an error occurs. +func (_q *ScheduledEventQuery) IDsX(ctx context.Context) []uuid.UUID { + ids, err := _q.IDs(ctx) + if err != nil { + panic(err) + } + return ids +} + +// Count returns the count of the given query. +func (_q *ScheduledEventQuery) Count(ctx context.Context) (int, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryCount) + if err := _q.prepareQuery(ctx); err != nil { + return 0, err + } + return withInterceptors[int](ctx, _q, querierCount[*ScheduledEventQuery](), _q.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (_q *ScheduledEventQuery) CountX(ctx context.Context) int { + count, err := _q.Count(ctx) + if err != nil { + panic(err) + } + return count +} + +// Exist returns true if the query has elements in the graph. +func (_q *ScheduledEventQuery) Exist(ctx context.Context) (bool, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryExist) + switch _, err := _q.FirstID(ctx); { + case IsNotFound(err): + return false, nil + case err != nil: + return false, fmt.Errorf("ent: check existence: %w", err) + default: + return true, nil + } +} + +// ExistX is like Exist, but panics if an error occurs. +func (_q *ScheduledEventQuery) ExistX(ctx context.Context) bool { + exist, err := _q.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the ScheduledEventQuery builder, including all associated steps. It can be +// used to prepare common query builders and use them differently after the clone is made. +func (_q *ScheduledEventQuery) Clone() *ScheduledEventQuery { + if _q == nil { + return nil + } + return &ScheduledEventQuery{ + config: _q.config, + ctx: _q.ctx.Clone(), + order: append([]scheduledevent.OrderOption{}, _q.order...), + inters: append([]Interceptor{}, _q.inters...), + predicates: append([]predicate.ScheduledEvent{}, _q.predicates...), + // clone intermediate query. + sql: _q.sql.Clone(), + path: _q.path, + } +} + +// GroupBy is used to group vertices by one or more fields/columns. +// It is often used with aggregate functions, like: count, max, mean, min, sum. +// +// Example: +// +// var v []struct { +// ProjectID uuid.UUID `json:"project_id,omitempty"` +// Count int `json:"count,omitempty"` +// } +// +// client.ScheduledEvent.Query(). +// GroupBy(scheduledevent.FieldProjectID). +// Aggregate(ent.Count()). +// Scan(ctx, &v) +func (_q *ScheduledEventQuery) GroupBy(field string, fields ...string) *ScheduledEventGroupBy { + _q.ctx.Fields = append([]string{field}, fields...) + grbuild := &ScheduledEventGroupBy{build: _q} + grbuild.flds = &_q.ctx.Fields + grbuild.label = scheduledevent.Label + grbuild.scan = grbuild.Scan + return grbuild +} + +// Select allows the selection one or more fields/columns for the given query, +// instead of selecting all fields in the entity. +// +// Example: +// +// var v []struct { +// ProjectID uuid.UUID `json:"project_id,omitempty"` +// } +// +// client.ScheduledEvent.Query(). +// Select(scheduledevent.FieldProjectID). +// Scan(ctx, &v) +func (_q *ScheduledEventQuery) Select(fields ...string) *ScheduledEventSelect { + _q.ctx.Fields = append(_q.ctx.Fields, fields...) + sbuild := &ScheduledEventSelect{ScheduledEventQuery: _q} + sbuild.label = scheduledevent.Label + sbuild.flds, sbuild.scan = &_q.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a ScheduledEventSelect configured with the given aggregations. +func (_q *ScheduledEventQuery) Aggregate(fns ...AggregateFunc) *ScheduledEventSelect { + return _q.Select().Aggregate(fns...) +} + +func (_q *ScheduledEventQuery) prepareQuery(ctx context.Context) error { + for _, inter := range _q.inters { + if inter == nil { + return fmt.Errorf("ent: uninitialized interceptor (forgotten import ent/runtime?)") + } + if trv, ok := inter.(Traverser); ok { + if err := trv.Traverse(ctx, _q); err != nil { + return err + } + } + } + for _, f := range _q.ctx.Fields { + if !scheduledevent.ValidColumn(f) { + return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + } + if _q.path != nil { + prev, err := _q.path(ctx) + if err != nil { + return err + } + _q.sql = prev + } + return nil +} + +func (_q *ScheduledEventQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*ScheduledEvent, error) { + var ( + nodes = []*ScheduledEvent{} + _spec = _q.querySpec() + ) + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*ScheduledEvent).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &ScheduledEvent{config: _q.config} + nodes = append(nodes, node) + return node.assignValues(columns, values) + } + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } + for i := range hooks { + hooks[i](ctx, _spec) + } + if err := sqlgraph.QueryNodes(ctx, _q.driver, _spec); err != nil { + return nil, err + } + if len(nodes) == 0 { + return nodes, nil + } + return nodes, nil +} + +func (_q *ScheduledEventQuery) sqlCount(ctx context.Context) (int, error) { + _spec := _q.querySpec() + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } + _spec.Node.Columns = _q.ctx.Fields + if len(_q.ctx.Fields) > 0 { + _spec.Unique = _q.ctx.Unique != nil && *_q.ctx.Unique + } + return sqlgraph.CountNodes(ctx, _q.driver, _spec) +} + +func (_q *ScheduledEventQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(scheduledevent.Table, scheduledevent.Columns, sqlgraph.NewFieldSpec(scheduledevent.FieldID, field.TypeUUID)) + _spec.From = _q.sql + if unique := _q.ctx.Unique; unique != nil { + _spec.Unique = *unique + } else if _q.path != nil { + _spec.Unique = true + } + if fields := _q.ctx.Fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, scheduledevent.FieldID) + for i := range fields { + if fields[i] != scheduledevent.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, fields[i]) + } + } + } + if ps := _q.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if limit := _q.ctx.Limit; limit != nil { + _spec.Limit = *limit + } + if offset := _q.ctx.Offset; offset != nil { + _spec.Offset = *offset + } + if ps := _q.order; len(ps) > 0 { + _spec.Order = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + return _spec +} + +func (_q *ScheduledEventQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(_q.driver.Dialect()) + t1 := builder.Table(scheduledevent.Table) + columns := _q.ctx.Fields + if len(columns) == 0 { + columns = scheduledevent.Columns + } + selector := builder.Select(t1.Columns(columns...)...).From(t1) + if _q.sql != nil { + selector = _q.sql + selector.Select(selector.Columns(columns...)...) + } + if _q.ctx.Unique != nil && *_q.ctx.Unique { + selector.Distinct() + } + for _, m := range _q.modifiers { + m(selector) + } + for _, p := range _q.predicates { + p(selector) + } + for _, p := range _q.order { + p(selector) + } + if offset := _q.ctx.Offset; offset != nil { + // limit is mandatory for offset clause. We start + // with default value, and override it below if needed. + selector.Offset(*offset).Limit(math.MaxInt32) + } + if limit := _q.ctx.Limit; limit != nil { + selector.Limit(*limit) + } + return selector +} + +// ForUpdate locks the selected rows against concurrent updates, and prevent them from being +// updated, deleted or "selected ... for update" by other sessions, until the transaction is +// either committed or rolled-back. +func (_q *ScheduledEventQuery) ForUpdate(opts ...sql.LockOption) *ScheduledEventQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForUpdate(opts...) + }) + return _q +} + +// ForShare behaves similarly to ForUpdate, except that it acquires a shared mode lock +// on any rows that are read. Other sessions can read the rows, but cannot modify them +// until your transaction commits. +func (_q *ScheduledEventQuery) ForShare(opts ...sql.LockOption) *ScheduledEventQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForShare(opts...) + }) + return _q +} + +// ScheduledEventGroupBy is the group-by builder for ScheduledEvent entities. +type ScheduledEventGroupBy struct { + selector + build *ScheduledEventQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (_g *ScheduledEventGroupBy) Aggregate(fns ...AggregateFunc) *ScheduledEventGroupBy { + _g.fns = append(_g.fns, fns...) + return _g +} + +// Scan applies the selector query and scans the result into the given value. +func (_g *ScheduledEventGroupBy) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, _g.build.ctx, ent.OpQueryGroupBy) + if err := _g.build.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*ScheduledEventQuery, *ScheduledEventGroupBy](ctx, _g.build, _g, _g.build.inters, v) +} + +func (_g *ScheduledEventGroupBy) sqlScan(ctx context.Context, root *ScheduledEventQuery, v any) error { + selector := root.sqlQuery(ctx).Select() + aggregation := make([]string, 0, len(_g.fns)) + for _, fn := range _g.fns { + aggregation = append(aggregation, fn(selector)) + } + if len(selector.SelectedColumns()) == 0 { + columns := make([]string, 0, len(*_g.flds)+len(_g.fns)) + for _, f := range *_g.flds { + columns = append(columns, selector.C(f)) + } + columns = append(columns, aggregation...) + selector.Select(columns...) + } + selector.GroupBy(selector.Columns(*_g.flds...)...) + if err := selector.Err(); err != nil { + return err + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := _g.build.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} + +// ScheduledEventSelect is the builder for selecting fields of ScheduledEvent entities. +type ScheduledEventSelect struct { + *ScheduledEventQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (_s *ScheduledEventSelect) Aggregate(fns ...AggregateFunc) *ScheduledEventSelect { + _s.fns = append(_s.fns, fns...) + return _s +} + +// Scan applies the selector query and scans the result into the given value. +func (_s *ScheduledEventSelect) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, _s.ctx, ent.OpQuerySelect) + if err := _s.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*ScheduledEventQuery, *ScheduledEventSelect](ctx, _s.ScheduledEventQuery, _s, _s.inters, v) +} + +func (_s *ScheduledEventSelect) sqlScan(ctx context.Context, root *ScheduledEventQuery, v any) error { + selector := root.sqlQuery(ctx) + aggregation := make([]string, 0, len(_s.fns)) + for _, fn := range _s.fns { + aggregation = append(aggregation, fn(selector)) + } + switch n := len(*_s.selector.flds); { + case n == 0 && len(aggregation) > 0: + selector.Select(aggregation...) + case n != 0 && len(aggregation) > 0: + selector.AppendSelect(aggregation...) + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := _s.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} diff --git a/pkg/ent/scheduledevent_update.go b/pkg/ent/scheduledevent_update.go new file mode 100644 index 000000000..724d055e3 --- /dev/null +++ b/pkg/ent/scheduledevent_update.go @@ -0,0 +1,591 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/GoogleCloudPlatform/scion/pkg/ent/scheduledevent" + "github.com/google/uuid" +) + +// ScheduledEventUpdate is the builder for updating ScheduledEvent entities. +type ScheduledEventUpdate struct { + config + hooks []Hook + mutation *ScheduledEventMutation +} + +// Where appends a list predicates to the ScheduledEventUpdate builder. +func (_u *ScheduledEventUpdate) Where(ps ...predicate.ScheduledEvent) *ScheduledEventUpdate { + _u.mutation.Where(ps...) + return _u +} + +// SetProjectID sets the "project_id" field. +func (_u *ScheduledEventUpdate) SetProjectID(v uuid.UUID) *ScheduledEventUpdate { + _u.mutation.SetProjectID(v) + return _u +} + +// SetNillableProjectID sets the "project_id" field if the given value is not nil. +func (_u *ScheduledEventUpdate) SetNillableProjectID(v *uuid.UUID) *ScheduledEventUpdate { + if v != nil { + _u.SetProjectID(*v) + } + return _u +} + +// SetEventType sets the "event_type" field. +func (_u *ScheduledEventUpdate) SetEventType(v string) *ScheduledEventUpdate { + _u.mutation.SetEventType(v) + return _u +} + +// SetNillableEventType sets the "event_type" field if the given value is not nil. +func (_u *ScheduledEventUpdate) SetNillableEventType(v *string) *ScheduledEventUpdate { + if v != nil { + _u.SetEventType(*v) + } + return _u +} + +// SetFireAt sets the "fire_at" field. +func (_u *ScheduledEventUpdate) SetFireAt(v time.Time) *ScheduledEventUpdate { + _u.mutation.SetFireAt(v) + return _u +} + +// SetNillableFireAt sets the "fire_at" field if the given value is not nil. +func (_u *ScheduledEventUpdate) SetNillableFireAt(v *time.Time) *ScheduledEventUpdate { + if v != nil { + _u.SetFireAt(*v) + } + return _u +} + +// SetPayload sets the "payload" field. +func (_u *ScheduledEventUpdate) SetPayload(v string) *ScheduledEventUpdate { + _u.mutation.SetPayload(v) + return _u +} + +// SetNillablePayload sets the "payload" field if the given value is not nil. +func (_u *ScheduledEventUpdate) SetNillablePayload(v *string) *ScheduledEventUpdate { + if v != nil { + _u.SetPayload(*v) + } + return _u +} + +// SetStatus sets the "status" field. +func (_u *ScheduledEventUpdate) SetStatus(v string) *ScheduledEventUpdate { + _u.mutation.SetStatus(v) + return _u +} + +// SetNillableStatus sets the "status" field if the given value is not nil. +func (_u *ScheduledEventUpdate) SetNillableStatus(v *string) *ScheduledEventUpdate { + if v != nil { + _u.SetStatus(*v) + } + return _u +} + +// SetCreatedBy sets the "created_by" field. +func (_u *ScheduledEventUpdate) SetCreatedBy(v string) *ScheduledEventUpdate { + _u.mutation.SetCreatedBy(v) + return _u +} + +// SetNillableCreatedBy sets the "created_by" field if the given value is not nil. +func (_u *ScheduledEventUpdate) SetNillableCreatedBy(v *string) *ScheduledEventUpdate { + if v != nil { + _u.SetCreatedBy(*v) + } + return _u +} + +// ClearCreatedBy clears the value of the "created_by" field. +func (_u *ScheduledEventUpdate) ClearCreatedBy() *ScheduledEventUpdate { + _u.mutation.ClearCreatedBy() + return _u +} + +// SetFiredAt sets the "fired_at" field. +func (_u *ScheduledEventUpdate) SetFiredAt(v time.Time) *ScheduledEventUpdate { + _u.mutation.SetFiredAt(v) + return _u +} + +// SetNillableFiredAt sets the "fired_at" field if the given value is not nil. +func (_u *ScheduledEventUpdate) SetNillableFiredAt(v *time.Time) *ScheduledEventUpdate { + if v != nil { + _u.SetFiredAt(*v) + } + return _u +} + +// ClearFiredAt clears the value of the "fired_at" field. +func (_u *ScheduledEventUpdate) ClearFiredAt() *ScheduledEventUpdate { + _u.mutation.ClearFiredAt() + return _u +} + +// SetError sets the "error" field. +func (_u *ScheduledEventUpdate) SetError(v string) *ScheduledEventUpdate { + _u.mutation.SetError(v) + return _u +} + +// SetNillableError sets the "error" field if the given value is not nil. +func (_u *ScheduledEventUpdate) SetNillableError(v *string) *ScheduledEventUpdate { + if v != nil { + _u.SetError(*v) + } + return _u +} + +// ClearError clears the value of the "error" field. +func (_u *ScheduledEventUpdate) ClearError() *ScheduledEventUpdate { + _u.mutation.ClearError() + return _u +} + +// SetScheduleID sets the "schedule_id" field. +func (_u *ScheduledEventUpdate) SetScheduleID(v string) *ScheduledEventUpdate { + _u.mutation.SetScheduleID(v) + return _u +} + +// SetNillableScheduleID sets the "schedule_id" field if the given value is not nil. +func (_u *ScheduledEventUpdate) SetNillableScheduleID(v *string) *ScheduledEventUpdate { + if v != nil { + _u.SetScheduleID(*v) + } + return _u +} + +// ClearScheduleID clears the value of the "schedule_id" field. +func (_u *ScheduledEventUpdate) ClearScheduleID() *ScheduledEventUpdate { + _u.mutation.ClearScheduleID() + return _u +} + +// Mutation returns the ScheduledEventMutation object of the builder. +func (_u *ScheduledEventUpdate) Mutation() *ScheduledEventMutation { + return _u.mutation +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (_u *ScheduledEventUpdate) Save(ctx context.Context) (int, error) { + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *ScheduledEventUpdate) SaveX(ctx context.Context) int { + affected, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (_u *ScheduledEventUpdate) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *ScheduledEventUpdate) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_u *ScheduledEventUpdate) check() error { + if v, ok := _u.mutation.EventType(); ok { + if err := scheduledevent.EventTypeValidator(v); err != nil { + return &ValidationError{Name: "event_type", err: fmt.Errorf(`ent: validator failed for field "ScheduledEvent.event_type": %w`, err)} + } + } + if v, ok := _u.mutation.Payload(); ok { + if err := scheduledevent.PayloadValidator(v); err != nil { + return &ValidationError{Name: "payload", err: fmt.Errorf(`ent: validator failed for field "ScheduledEvent.payload": %w`, err)} + } + } + return nil +} + +func (_u *ScheduledEventUpdate) sqlSave(ctx context.Context) (_node int, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(scheduledevent.Table, scheduledevent.Columns, sqlgraph.NewFieldSpec(scheduledevent.FieldID, field.TypeUUID)) + if ps := _u.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := _u.mutation.ProjectID(); ok { + _spec.SetField(scheduledevent.FieldProjectID, field.TypeUUID, value) + } + if value, ok := _u.mutation.EventType(); ok { + _spec.SetField(scheduledevent.FieldEventType, field.TypeString, value) + } + if value, ok := _u.mutation.FireAt(); ok { + _spec.SetField(scheduledevent.FieldFireAt, field.TypeTime, value) + } + if value, ok := _u.mutation.Payload(); ok { + _spec.SetField(scheduledevent.FieldPayload, field.TypeString, value) + } + if value, ok := _u.mutation.Status(); ok { + _spec.SetField(scheduledevent.FieldStatus, field.TypeString, value) + } + if value, ok := _u.mutation.CreatedBy(); ok { + _spec.SetField(scheduledevent.FieldCreatedBy, field.TypeString, value) + } + if _u.mutation.CreatedByCleared() { + _spec.ClearField(scheduledevent.FieldCreatedBy, field.TypeString) + } + if value, ok := _u.mutation.FiredAt(); ok { + _spec.SetField(scheduledevent.FieldFiredAt, field.TypeTime, value) + } + if _u.mutation.FiredAtCleared() { + _spec.ClearField(scheduledevent.FieldFiredAt, field.TypeTime) + } + if value, ok := _u.mutation.Error(); ok { + _spec.SetField(scheduledevent.FieldError, field.TypeString, value) + } + if _u.mutation.ErrorCleared() { + _spec.ClearField(scheduledevent.FieldError, field.TypeString) + } + if value, ok := _u.mutation.ScheduleID(); ok { + _spec.SetField(scheduledevent.FieldScheduleID, field.TypeString, value) + } + if _u.mutation.ScheduleIDCleared() { + _spec.ClearField(scheduledevent.FieldScheduleID, field.TypeString) + } + if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{scheduledevent.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + _u.mutation.done = true + return _node, nil +} + +// ScheduledEventUpdateOne is the builder for updating a single ScheduledEvent entity. +type ScheduledEventUpdateOne struct { + config + fields []string + hooks []Hook + mutation *ScheduledEventMutation +} + +// SetProjectID sets the "project_id" field. +func (_u *ScheduledEventUpdateOne) SetProjectID(v uuid.UUID) *ScheduledEventUpdateOne { + _u.mutation.SetProjectID(v) + return _u +} + +// SetNillableProjectID sets the "project_id" field if the given value is not nil. +func (_u *ScheduledEventUpdateOne) SetNillableProjectID(v *uuid.UUID) *ScheduledEventUpdateOne { + if v != nil { + _u.SetProjectID(*v) + } + return _u +} + +// SetEventType sets the "event_type" field. +func (_u *ScheduledEventUpdateOne) SetEventType(v string) *ScheduledEventUpdateOne { + _u.mutation.SetEventType(v) + return _u +} + +// SetNillableEventType sets the "event_type" field if the given value is not nil. +func (_u *ScheduledEventUpdateOne) SetNillableEventType(v *string) *ScheduledEventUpdateOne { + if v != nil { + _u.SetEventType(*v) + } + return _u +} + +// SetFireAt sets the "fire_at" field. +func (_u *ScheduledEventUpdateOne) SetFireAt(v time.Time) *ScheduledEventUpdateOne { + _u.mutation.SetFireAt(v) + return _u +} + +// SetNillableFireAt sets the "fire_at" field if the given value is not nil. +func (_u *ScheduledEventUpdateOne) SetNillableFireAt(v *time.Time) *ScheduledEventUpdateOne { + if v != nil { + _u.SetFireAt(*v) + } + return _u +} + +// SetPayload sets the "payload" field. +func (_u *ScheduledEventUpdateOne) SetPayload(v string) *ScheduledEventUpdateOne { + _u.mutation.SetPayload(v) + return _u +} + +// SetNillablePayload sets the "payload" field if the given value is not nil. +func (_u *ScheduledEventUpdateOne) SetNillablePayload(v *string) *ScheduledEventUpdateOne { + if v != nil { + _u.SetPayload(*v) + } + return _u +} + +// SetStatus sets the "status" field. +func (_u *ScheduledEventUpdateOne) SetStatus(v string) *ScheduledEventUpdateOne { + _u.mutation.SetStatus(v) + return _u +} + +// SetNillableStatus sets the "status" field if the given value is not nil. +func (_u *ScheduledEventUpdateOne) SetNillableStatus(v *string) *ScheduledEventUpdateOne { + if v != nil { + _u.SetStatus(*v) + } + return _u +} + +// SetCreatedBy sets the "created_by" field. +func (_u *ScheduledEventUpdateOne) SetCreatedBy(v string) *ScheduledEventUpdateOne { + _u.mutation.SetCreatedBy(v) + return _u +} + +// SetNillableCreatedBy sets the "created_by" field if the given value is not nil. +func (_u *ScheduledEventUpdateOne) SetNillableCreatedBy(v *string) *ScheduledEventUpdateOne { + if v != nil { + _u.SetCreatedBy(*v) + } + return _u +} + +// ClearCreatedBy clears the value of the "created_by" field. +func (_u *ScheduledEventUpdateOne) ClearCreatedBy() *ScheduledEventUpdateOne { + _u.mutation.ClearCreatedBy() + return _u +} + +// SetFiredAt sets the "fired_at" field. +func (_u *ScheduledEventUpdateOne) SetFiredAt(v time.Time) *ScheduledEventUpdateOne { + _u.mutation.SetFiredAt(v) + return _u +} + +// SetNillableFiredAt sets the "fired_at" field if the given value is not nil. +func (_u *ScheduledEventUpdateOne) SetNillableFiredAt(v *time.Time) *ScheduledEventUpdateOne { + if v != nil { + _u.SetFiredAt(*v) + } + return _u +} + +// ClearFiredAt clears the value of the "fired_at" field. +func (_u *ScheduledEventUpdateOne) ClearFiredAt() *ScheduledEventUpdateOne { + _u.mutation.ClearFiredAt() + return _u +} + +// SetError sets the "error" field. +func (_u *ScheduledEventUpdateOne) SetError(v string) *ScheduledEventUpdateOne { + _u.mutation.SetError(v) + return _u +} + +// SetNillableError sets the "error" field if the given value is not nil. +func (_u *ScheduledEventUpdateOne) SetNillableError(v *string) *ScheduledEventUpdateOne { + if v != nil { + _u.SetError(*v) + } + return _u +} + +// ClearError clears the value of the "error" field. +func (_u *ScheduledEventUpdateOne) ClearError() *ScheduledEventUpdateOne { + _u.mutation.ClearError() + return _u +} + +// SetScheduleID sets the "schedule_id" field. +func (_u *ScheduledEventUpdateOne) SetScheduleID(v string) *ScheduledEventUpdateOne { + _u.mutation.SetScheduleID(v) + return _u +} + +// SetNillableScheduleID sets the "schedule_id" field if the given value is not nil. +func (_u *ScheduledEventUpdateOne) SetNillableScheduleID(v *string) *ScheduledEventUpdateOne { + if v != nil { + _u.SetScheduleID(*v) + } + return _u +} + +// ClearScheduleID clears the value of the "schedule_id" field. +func (_u *ScheduledEventUpdateOne) ClearScheduleID() *ScheduledEventUpdateOne { + _u.mutation.ClearScheduleID() + return _u +} + +// Mutation returns the ScheduledEventMutation object of the builder. +func (_u *ScheduledEventUpdateOne) Mutation() *ScheduledEventMutation { + return _u.mutation +} + +// Where appends a list predicates to the ScheduledEventUpdate builder. +func (_u *ScheduledEventUpdateOne) Where(ps ...predicate.ScheduledEvent) *ScheduledEventUpdateOne { + _u.mutation.Where(ps...) + return _u +} + +// Select allows selecting one or more fields (columns) of the returned entity. +// The default is selecting all fields defined in the entity schema. +func (_u *ScheduledEventUpdateOne) Select(field string, fields ...string) *ScheduledEventUpdateOne { + _u.fields = append([]string{field}, fields...) + return _u +} + +// Save executes the query and returns the updated ScheduledEvent entity. +func (_u *ScheduledEventUpdateOne) Save(ctx context.Context) (*ScheduledEvent, error) { + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *ScheduledEventUpdateOne) SaveX(ctx context.Context) *ScheduledEvent { + node, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (_u *ScheduledEventUpdateOne) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *ScheduledEventUpdateOne) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_u *ScheduledEventUpdateOne) check() error { + if v, ok := _u.mutation.EventType(); ok { + if err := scheduledevent.EventTypeValidator(v); err != nil { + return &ValidationError{Name: "event_type", err: fmt.Errorf(`ent: validator failed for field "ScheduledEvent.event_type": %w`, err)} + } + } + if v, ok := _u.mutation.Payload(); ok { + if err := scheduledevent.PayloadValidator(v); err != nil { + return &ValidationError{Name: "payload", err: fmt.Errorf(`ent: validator failed for field "ScheduledEvent.payload": %w`, err)} + } + } + return nil +} + +func (_u *ScheduledEventUpdateOne) sqlSave(ctx context.Context) (_node *ScheduledEvent, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(scheduledevent.Table, scheduledevent.Columns, sqlgraph.NewFieldSpec(scheduledevent.FieldID, field.TypeUUID)) + id, ok := _u.mutation.ID() + if !ok { + return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "ScheduledEvent.id" for update`)} + } + _spec.Node.ID.Value = id + if fields := _u.fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, scheduledevent.FieldID) + for _, f := range fields { + if !scheduledevent.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + if f != scheduledevent.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, f) + } + } + } + if ps := _u.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := _u.mutation.ProjectID(); ok { + _spec.SetField(scheduledevent.FieldProjectID, field.TypeUUID, value) + } + if value, ok := _u.mutation.EventType(); ok { + _spec.SetField(scheduledevent.FieldEventType, field.TypeString, value) + } + if value, ok := _u.mutation.FireAt(); ok { + _spec.SetField(scheduledevent.FieldFireAt, field.TypeTime, value) + } + if value, ok := _u.mutation.Payload(); ok { + _spec.SetField(scheduledevent.FieldPayload, field.TypeString, value) + } + if value, ok := _u.mutation.Status(); ok { + _spec.SetField(scheduledevent.FieldStatus, field.TypeString, value) + } + if value, ok := _u.mutation.CreatedBy(); ok { + _spec.SetField(scheduledevent.FieldCreatedBy, field.TypeString, value) + } + if _u.mutation.CreatedByCleared() { + _spec.ClearField(scheduledevent.FieldCreatedBy, field.TypeString) + } + if value, ok := _u.mutation.FiredAt(); ok { + _spec.SetField(scheduledevent.FieldFiredAt, field.TypeTime, value) + } + if _u.mutation.FiredAtCleared() { + _spec.ClearField(scheduledevent.FieldFiredAt, field.TypeTime) + } + if value, ok := _u.mutation.Error(); ok { + _spec.SetField(scheduledevent.FieldError, field.TypeString, value) + } + if _u.mutation.ErrorCleared() { + _spec.ClearField(scheduledevent.FieldError, field.TypeString) + } + if value, ok := _u.mutation.ScheduleID(); ok { + _spec.SetField(scheduledevent.FieldScheduleID, field.TypeString, value) + } + if _u.mutation.ScheduleIDCleared() { + _spec.ClearField(scheduledevent.FieldScheduleID, field.TypeString) + } + _node = &ScheduledEvent{config: _u.config} + _spec.Assign = _node.assignValues + _spec.ScanValues = _node.scanValues + if err = sqlgraph.UpdateNode(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{scheduledevent.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + _u.mutation.done = true + return _node, nil +} diff --git a/pkg/ent/schema/agent.go b/pkg/ent/schema/agent.go index b683d83e4..a94c5fdbc 100644 --- a/pkg/ent/schema/agent.go +++ b/pkg/ent/schema/agent.go @@ -25,9 +25,12 @@ import ( ) // Agent holds the schema definition for the Agent entity. -// Only principal-relevant fields are included; operational fields -// (ContainerStatus, RuntimeState, etc.) will be added when the -// agent entity is fully migrated to Ent. +// +// The agent entity carries both the principal-relevant fields used by the +// authorization layer (created_by, owner_id, delegation_enabled, visibility) +// and the full set of operational fields required to back store.Agent through +// the Ent adapter (P2-port-agent). Together they give the Ent-backed agent +// store parity with the former raw-SQL store implementation. type Agent struct { ent.Schema } @@ -59,12 +62,90 @@ func (Agent) Fields() []ent.Field { Default(false), field.String("visibility"). Default("private"), + + // --- Metadata (stored as JSON) --- + field.JSON("labels", map[string]string{}). + Optional(), + field.JSON("annotations", map[string]string{}). + Optional(), + + // --- Runtime status --- + field.String("phase"). + Optional(), + field.String("activity"). + Optional(), + field.String("tool_name"). + Optional(), + field.String("connection_state"). + Optional(), + field.String("container_status"). + Optional(), + field.String("runtime_state"). + Optional(), + field.String("stalled_from_activity"). + Optional(), + + // --- Limits tracking --- + field.Int("current_turns"). + Default(0), + field.Int("current_model_calls"). + Default(0), + + // --- Runtime configuration --- + field.String("image"). + Optional(), + field.Bool("detached"). + Default(false), + field.String("runtime"). + Optional(), + field.String("runtime_broker_id"). + Optional(), + field.Bool("web_pty_enabled"). + Default(false), + field.String("task_summary"). + Optional(), + field.String("message"). + Optional(), + + // applied_config is the agent's resolved configuration, persisted as a + // JSON document (store.AgentAppliedConfig). Stored as text to keep the + // Ent schema decoupled from the store package's struct definition. + field.Text("applied_config"). + Optional(), + + // ancestry is the ordered chain of ancestor principal IDs used for + // transitive access control. Stored as a JSON array so the dialect-aware + // json_each / json_array_elements_text membership filter can be applied. + field.JSON("ancestry", []string{}). + Optional(), + + // --- Timestamps --- field.Time("created"). Default(time.Now). Immutable(), field.Time("updated"). Default(time.Now). UpdateDefault(time.Now), + field.Time("last_seen"). + Optional(). + Nillable(), + field.Time("last_activity_event"). + Optional(). + Nillable(), + field.Time("started_at"). + Optional(). + Nillable(), + // deleted_at backs soft-delete: a non-nil value excludes the agent from + // default listings (filtered via the DeletedAtIsNil Ent predicate). + field.Time("deleted_at"). + Optional(). + Nillable(), + + // --- Optimistic locking --- + // state_version is incremented on every UpdateAgent and used as a CAS + // guard to detect concurrent modifications under multi-replica Postgres. + field.Int64("state_version"). + Default(1), } } diff --git a/pkg/ent/schema/allowlist.go b/pkg/ent/schema/allowlist.go new file mode 100644 index 000000000..bafd22f3a --- /dev/null +++ b/pkg/ent/schema/allowlist.go @@ -0,0 +1,71 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package schema + +import ( + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/entsql" + "entgo.io/ent/schema" + "entgo.io/ent/schema/field" + "entgo.io/ent/schema/index" + "github.com/google/uuid" +) + +// AllowListEntry holds the schema definition for the AllowListEntry entity, +// mapping the legacy SQLite `allow_list` table. +// +// email was UNIQUE COLLATE NOCASE in SQLite. Postgres has no NOCASE collation, +// so a plain unique index is declared here; case-insensitive matching (citext +// or a lower(email) functional index) is a port-layer concern. +type AllowListEntry struct { + ent.Schema +} + +// Fields of the AllowListEntry. +func (AllowListEntry) Fields() []ent.Field { + return []ent.Field{ + field.UUID("id", uuid.UUID{}). + Default(uuid.New). + Immutable(), + field.String("email"). + Unique(). + NotEmpty(), + field.String("note"). + Default(""), + field.String("added_by"). + NotEmpty(), + field.String("invite_id"). + Optional(), + field.Time("created"). + Default(time.Now). + Immutable(), + } +} + +// Indexes of the AllowListEntry. +func (AllowListEntry) Indexes() []ent.Index { + return []ent.Index{ + index.Fields("created", "id"), + } +} + +// Annotations of the AllowListEntry. +func (AllowListEntry) Annotations() []schema.Annotation { + return []schema.Annotation{ + entsql.Annotation{Table: "allow_list"}, + } +} diff --git a/pkg/ent/schema/apikey.go b/pkg/ent/schema/apikey.go new file mode 100644 index 000000000..6d4bd7829 --- /dev/null +++ b/pkg/ent/schema/apikey.go @@ -0,0 +1,81 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package schema + +import ( + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/entsql" + "entgo.io/ent/schema" + "entgo.io/ent/schema/field" + "entgo.io/ent/schema/index" + "github.com/google/uuid" +) + +// ApiKey holds the schema definition for the ApiKey entity, mapping the legacy +// SQLite `api_keys` table. +// +// NOTE: api_keys is a legacy table superseded by user_access_tokens (V34). It is +// schematized here for completeness/migration fidelity; confirm with the +// coordinator whether it is still in active use. +type ApiKey struct { + ent.Schema +} + +// Fields of the ApiKey. +func (ApiKey) Fields() []ent.Field { + return []ent.Field{ + field.UUID("id", uuid.UUID{}). + Default(uuid.New). + Immutable(), + field.UUID("user_id", uuid.UUID{}), + field.String("name"). + Optional(), + field.String("prefix"). + Optional(), + field.String("key_hash"). + Sensitive(). + Unique(). + NotEmpty(), + field.String("scopes"). + Optional(), + field.Bool("revoked"). + Default(false), + field.Time("expires_at"). + Optional(). + Nillable(), + field.Time("last_used"). + Optional(). + Nillable(), + field.Time("created"). + Default(time.Now). + Immutable(), + } +} + +// Indexes of the ApiKey. +func (ApiKey) Indexes() []ent.Index { + return []ent.Index{ + index.Fields("user_id"), + } +} + +// Annotations of the ApiKey. +func (ApiKey) Annotations() []schema.Annotation { + return []schema.Annotation{ + entsql.Annotation{Table: "api_keys"}, + } +} diff --git a/pkg/ent/schema/brokerjointoken.go b/pkg/ent/schema/brokerjointoken.go new file mode 100644 index 000000000..1d4375b3b --- /dev/null +++ b/pkg/ent/schema/brokerjointoken.go @@ -0,0 +1,67 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package schema + +import ( + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/entsql" + "entgo.io/ent/schema" + "entgo.io/ent/schema/field" + "entgo.io/ent/schema/index" + "github.com/google/uuid" +) + +// BrokerJoinToken holds the schema definition for the BrokerJoinToken entity, +// mapping the legacy SQLite `broker_join_tokens` table. +// +// The primary key is broker_id (one active join token per runtime broker), so +// the id field is stored in the broker_id column with no generated default. +type BrokerJoinToken struct { + ent.Schema +} + +// Fields of the BrokerJoinToken. +func (BrokerJoinToken) Fields() []ent.Field { + return []ent.Field{ + field.UUID("id", uuid.UUID{}). + StorageKey("broker_id"). + Immutable(), + field.String("token_hash"). + Unique(). + NotEmpty(), + field.Time("expires_at"), + field.String("created_by"). + NotEmpty(), + field.Time("created"). + Default(time.Now). + Immutable(), + } +} + +// Indexes of the BrokerJoinToken. +func (BrokerJoinToken) Indexes() []ent.Index { + return []ent.Index{ + index.Fields("expires_at"), + } +} + +// Annotations of the BrokerJoinToken. +func (BrokerJoinToken) Annotations() []schema.Annotation { + return []schema.Annotation{ + entsql.Annotation{Table: "broker_join_tokens"}, + } +} diff --git a/pkg/ent/schema/brokersecret.go b/pkg/ent/schema/brokersecret.go new file mode 100644 index 000000000..36a05d74e --- /dev/null +++ b/pkg/ent/schema/brokersecret.go @@ -0,0 +1,67 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package schema + +import ( + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/entsql" + "entgo.io/ent/schema" + "entgo.io/ent/schema/field" + "github.com/google/uuid" +) + +// BrokerSecret holds the schema definition for the BrokerSecret entity, mapping +// the legacy SQLite `broker_secrets` table. +// +// The primary key is broker_id (one secret per runtime broker), so the id field +// is stored in the broker_id column with no generated default. secret_key is a +// binary HMAC key (SQLite BLOB → Postgres bytea) and is marked Sensitive. +type BrokerSecret struct { + ent.Schema +} + +// Fields of the BrokerSecret. +func (BrokerSecret) Fields() []ent.Field { + return []ent.Field{ + field.UUID("id", uuid.UUID{}). + StorageKey("broker_id"). + Immutable(), + field.Bytes("secret_key"). + Sensitive(). + NotEmpty(), + field.String("algorithm"). + Default("hmac-sha256"), + field.Time("rotated_at"). + Optional(). + Nillable(), + field.Time("expires_at"). + Optional(). + Nillable(), + field.String("status"). + Default("active"), + field.Time("created"). + Default(time.Now). + Immutable(), + } +} + +// Annotations of the BrokerSecret. +func (BrokerSecret) Annotations() []schema.Annotation { + return []schema.Annotation{ + entsql.Annotation{Table: "broker_secrets"}, + } +} diff --git a/pkg/ent/schema/envvar.go b/pkg/ent/schema/envvar.go new file mode 100644 index 000000000..115f42684 --- /dev/null +++ b/pkg/ent/schema/envvar.go @@ -0,0 +1,81 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package schema + +import ( + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/entsql" + "entgo.io/ent/schema" + "entgo.io/ent/schema/field" + "entgo.io/ent/schema/index" + "github.com/google/uuid" +) + +// EnvVar holds the schema definition for the EnvVar entity, mapping the legacy +// SQLite `env_vars` table. Like Secret, it is polymorphically scoped via +// (scope, scope_id) with no FK edges. +type EnvVar struct { + ent.Schema +} + +// Fields of the EnvVar. +func (EnvVar) Fields() []ent.Field { + return []ent.Field{ + field.UUID("id", uuid.UUID{}). + Default(uuid.New). + Immutable(), + field.String("key"). + NotEmpty(), + field.String("value"), + field.String("scope"). + NotEmpty(), + field.String("scope_id"), + field.String("description"). + Optional(), + field.Bool("sensitive"). + Default(false), + field.Enum("injection_mode"). + Values("always", "as_needed"). + Default("as_needed"), + field.Bool("secret"). + Default(false), + field.String("created_by"). + Optional(), + field.Time("created"). + Default(time.Now). + Immutable(), + field.Time("updated"). + Default(time.Now). + UpdateDefault(time.Now), + } +} + +// Indexes of the EnvVar. +func (EnvVar) Indexes() []ent.Index { + return []ent.Index{ + index.Fields("key", "scope", "scope_id"). + Unique(), + index.Fields("scope", "scope_id"), + } +} + +// Annotations of the EnvVar. +func (EnvVar) Annotations() []schema.Annotation { + return []schema.Annotation{ + entsql.Annotation{Table: "env_vars"}, + } +} diff --git a/pkg/ent/schema/gcpserviceaccount.go b/pkg/ent/schema/gcpserviceaccount.go new file mode 100644 index 000000000..7b89fd679 --- /dev/null +++ b/pkg/ent/schema/gcpserviceaccount.go @@ -0,0 +1,88 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package schema + +import ( + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/entsql" + "entgo.io/ent/schema" + "entgo.io/ent/schema/field" + "entgo.io/ent/schema/index" + "github.com/google/uuid" +) + +// GCPServiceAccount holds the schema definition for the GCPServiceAccount +// entity, mapping the legacy SQLite `gcp_service_accounts` table. +// +// Accounts are polymorphically scoped via (scope, scope_id); default_scopes is +// a raw string (JSON/CSV) kept dialect-neutral. +type GCPServiceAccount struct { + ent.Schema +} + +// Fields of the GCPServiceAccount. +func (GCPServiceAccount) Fields() []ent.Field { + return []ent.Field{ + field.UUID("id", uuid.UUID{}). + Default(uuid.New). + Immutable(), + field.String("scope"). + NotEmpty(), + field.String("scope_id"). + NotEmpty(), + field.String("email"). + NotEmpty(), + // project_id holds the GCP *cloud project* identifier (e.g. + // "my-project-123"), which is a free-form string, not a UUID. + field.String("project_id"). + NotEmpty(), + field.String("display_name"). + Default(""), + field.String("default_scopes"). + Default(""), + field.Bool("verified"). + Default(false), + field.Time("verified_at"). + Optional(). + Nillable(), + field.String("created_by"). + Default(""), + field.Bool("managed"). + Default(false), + field.String("managed_by"). + Default(""), + field.Time("created"). + Default(time.Now). + Immutable(), + } +} + +// Indexes of the GCPServiceAccount. +func (GCPServiceAccount) Indexes() []ent.Index { + return []ent.Index{ + index.Fields("email", "scope", "scope_id"). + Unique(), + index.Fields("scope", "scope_id"), + } +} + +// Annotations of the GCPServiceAccount. +func (GCPServiceAccount) Annotations() []schema.Annotation { + return []schema.Annotation{ + entsql.Annotation{Table: "gcp_service_accounts"}, + } +} diff --git a/pkg/ent/schema/githubinstallation.go b/pkg/ent/schema/githubinstallation.go new file mode 100644 index 000000000..6e2626ae9 --- /dev/null +++ b/pkg/ent/schema/githubinstallation.go @@ -0,0 +1,65 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package schema + +import ( + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/entsql" + "entgo.io/ent/schema" + "entgo.io/ent/schema/field" +) + +// GithubInstallation holds the schema definition for the GithubInstallation +// entity, mapping the legacy SQLite `github_installations` table. +// +// The primary key is the GitHub-provided installation_id (a real integer id, +// NOT a UUID), so id is an int64 stored in the installation_id column with no +// generated default. repositories is a raw JSON string. +type GithubInstallation struct { + ent.Schema +} + +// Fields of the GithubInstallation. +func (GithubInstallation) Fields() []ent.Field { + return []ent.Field{ + field.Int64("id"). + StorageKey("installation_id"). + Immutable(), + field.String("account_login"). + NotEmpty(), + field.String("account_type"). + Default("Organization"), + field.Int64("app_id"), + field.String("repositories"). + Default("[]"), + field.String("status"). + Default("active"), + field.Time("created"). + Default(time.Now). + Immutable(), + field.Time("updated"). + Default(time.Now). + UpdateDefault(time.Now), + } +} + +// Annotations of the GithubInstallation. +func (GithubInstallation) Annotations() []schema.Annotation { + return []schema.Annotation{ + entsql.Annotation{Table: "github_installations"}, + } +} diff --git a/pkg/ent/schema/harnessconfig.go b/pkg/ent/schema/harnessconfig.go new file mode 100644 index 000000000..7856e992f --- /dev/null +++ b/pkg/ent/schema/harnessconfig.go @@ -0,0 +1,105 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package schema + +import ( + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/entsql" + "entgo.io/ent/schema" + "entgo.io/ent/schema/field" + "entgo.io/ent/schema/index" + "github.com/google/uuid" +) + +// HarnessConfig holds the schema definition for the HarnessConfig entity, +// mapping the legacy SQLite `harness_configs` table. It is scope/scope_id +// addressed (no project_id FK column); JSON columns (config, files) are kept +// as raw strings for dialect neutrality. +type HarnessConfig struct { + ent.Schema +} + +// Fields of the HarnessConfig. +func (HarnessConfig) Fields() []ent.Field { + return []ent.Field{ + field.UUID("id", uuid.UUID{}). + Default(uuid.New). + Immutable(), + field.String("name"). + NotEmpty(), + field.String("slug"). + NotEmpty(), + field.String("display_name"). + Optional(), + field.String("description"). + Optional(), + field.String("harness"). + NotEmpty(), + field.String("config"). + Optional(), + field.String("content_hash"). + Optional(), + field.String("scope"). + Default("global"), + field.String("scope_id"). + Optional(), + field.String("storage_uri"). + Optional(), + field.String("storage_bucket"). + Optional(), + field.String("storage_path"). + Optional(), + field.String("files"). + Optional(), + field.Bool("locked"). + Default(false), + field.Enum("status"). + Values("pending", "active", "archived"). + Default("active"), + field.String("owner_id"). + Optional(), + field.String("created_by"). + Optional(), + field.String("updated_by"). + Optional(), + field.String("visibility"). + Default("private"), + field.Time("created"). + Default(time.Now). + Immutable(), + field.Time("updated"). + Default(time.Now). + UpdateDefault(time.Now), + } +} + +// Indexes of the HarnessConfig. +func (HarnessConfig) Indexes() []ent.Index { + return []ent.Index{ + index.Fields("slug", "scope"), + index.Fields("harness"), + index.Fields("status"), + index.Fields("content_hash"), + } +} + +// Annotations of the HarnessConfig. +func (HarnessConfig) Annotations() []schema.Annotation { + return []schema.Annotation{ + entsql.Annotation{Table: "harness_configs"}, + } +} diff --git a/pkg/ent/schema/invitecode.go b/pkg/ent/schema/invitecode.go new file mode 100644 index 000000000..0f8eccf8c --- /dev/null +++ b/pkg/ent/schema/invitecode.go @@ -0,0 +1,76 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package schema + +import ( + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/entsql" + "entgo.io/ent/schema" + "entgo.io/ent/schema/field" + "entgo.io/ent/schema/index" + "github.com/google/uuid" +) + +// InviteCode holds the schema definition for the InviteCode entity, mapping the +// legacy SQLite `invite_codes` table. code_hash is the unique lookup key and is +// marked Sensitive. +type InviteCode struct { + ent.Schema +} + +// Fields of the InviteCode. +func (InviteCode) Fields() []ent.Field { + return []ent.Field{ + field.UUID("id", uuid.UUID{}). + Default(uuid.New). + Immutable(), + field.String("code_hash"). + Sensitive(). + Unique(). + NotEmpty(), + field.String("code_prefix"). + NotEmpty(), + field.Int("max_uses"). + Default(1), + field.Int("use_count"). + Default(0), + field.Time("expires_at"), + field.Bool("revoked"). + Default(false), + field.String("created_by"). + NotEmpty(), + field.String("note"). + Default(""), + field.Time("created"). + Default(time.Now). + Immutable(), + } +} + +// Indexes of the InviteCode. +func (InviteCode) Indexes() []ent.Index { + return []ent.Index{ + index.Fields("expires_at"), + } +} + +// Annotations of the InviteCode. +func (InviteCode) Annotations() []schema.Annotation { + return []schema.Annotation{ + entsql.Annotation{Table: "invite_codes"}, + } +} diff --git a/pkg/ent/schema/maintenanceoperation.go b/pkg/ent/schema/maintenanceoperation.go new file mode 100644 index 000000000..bbe7149a6 --- /dev/null +++ b/pkg/ent/schema/maintenanceoperation.go @@ -0,0 +1,76 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package schema + +import ( + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/entsql" + "entgo.io/ent/schema" + "entgo.io/ent/schema/field" + "github.com/google/uuid" +) + +// MaintenanceOperation holds the schema definition for the MaintenanceOperation +// entity, mapping the legacy SQLite `maintenance_operations` table. +// +// `key` is a stable unique business key. Seed rows that SQLite created via +// hex(randomblob(...)) move to Go-side seeding (out of scope for this schema). +type MaintenanceOperation struct { + ent.Schema +} + +// Fields of the MaintenanceOperation. +func (MaintenanceOperation) Fields() []ent.Field { + return []ent.Field{ + field.UUID("id", uuid.UUID{}). + Default(uuid.New). + Immutable(), + field.String("key"). + Unique(). + NotEmpty(), + field.String("title"). + NotEmpty(), + field.String("description"). + Default(""), + field.String("category"). + NotEmpty(), + field.String("status"). + Default("pending"), + field.Time("started_at"). + Optional(). + Nillable(), + field.Time("completed_at"). + Optional(). + Nillable(), + field.String("started_by"). + Optional(), + field.String("result"). + Optional(), + field.String("metadata"). + Default("{}"), + field.Time("created"). + Default(time.Now). + Immutable(), + } +} + +// Annotations of the MaintenanceOperation. +func (MaintenanceOperation) Annotations() []schema.Annotation { + return []schema.Annotation{ + entsql.Annotation{Table: "maintenance_operations"}, + } +} diff --git a/pkg/ent/schema/maintenanceoperationrun.go b/pkg/ent/schema/maintenanceoperationrun.go new file mode 100644 index 000000000..388fa9397 --- /dev/null +++ b/pkg/ent/schema/maintenanceoperationrun.go @@ -0,0 +1,76 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package schema + +import ( + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/entsql" + "entgo.io/ent/schema" + "entgo.io/ent/schema/field" + "entgo.io/ent/schema/index" + "github.com/google/uuid" +) + +// MaintenanceOperationRun holds the schema definition for the +// MaintenanceOperationRun entity, mapping the legacy SQLite +// `maintenance_operation_runs` table. +// +// operation_key references maintenance_operations(key) — a non-id unique +// column — so it is modeled as a plain string rather than an Ent edge (which +// binds to id). The FK is reconstructed at the port/migration layer. +type MaintenanceOperationRun struct { + ent.Schema +} + +// Fields of the MaintenanceOperationRun. +func (MaintenanceOperationRun) Fields() []ent.Field { + return []ent.Field{ + field.UUID("id", uuid.UUID{}). + Default(uuid.New). + Immutable(), + field.String("operation_key"). + NotEmpty(), + field.String("status"). + Default("running"), + field.Time("started_at"). + Default(time.Now). + Immutable(), + field.Time("completed_at"). + Optional(). + Nillable(), + field.String("started_by"). + Optional(), + field.String("result"). + Optional(), + field.String("log"). + Default(""), + } +} + +// Indexes of the MaintenanceOperationRun. +func (MaintenanceOperationRun) Indexes() []ent.Index { + return []ent.Index{ + index.Fields("operation_key"), + } +} + +// Annotations of the MaintenanceOperationRun. +func (MaintenanceOperationRun) Annotations() []schema.Annotation { + return []schema.Annotation{ + entsql.Annotation{Table: "maintenance_operation_runs"}, + } +} diff --git a/pkg/ent/schema/message.go b/pkg/ent/schema/message.go new file mode 100644 index 000000000..a909e9884 --- /dev/null +++ b/pkg/ent/schema/message.go @@ -0,0 +1,87 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package schema + +import ( + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/entsql" + "entgo.io/ent/schema" + "entgo.io/ent/schema/field" + "entgo.io/ent/schema/index" + "github.com/google/uuid" +) + +// Message holds the schema definition for the Message entity, mapping the legacy +// SQLite `messages` table. +// +// sender_id/recipient_id/agent_id/group_id are kept as plain strings (they hold +// heterogeneous principal identifiers and defaulted to ” in SQLite), while +// project_id is a required UUID. +type Message struct { + ent.Schema +} + +// Fields of the Message. +func (Message) Fields() []ent.Field { + return []ent.Field{ + field.UUID("id", uuid.UUID{}). + Default(uuid.New). + Immutable(), + field.UUID("project_id", uuid.UUID{}), + field.String("sender"). + NotEmpty(), + field.String("sender_id"). + Optional(), + field.String("recipient"). + NotEmpty(), + field.String("recipient_id"). + Optional(), + field.String("msg"). + NotEmpty(), + field.String("type"). + Default("instruction"), + field.Bool("urgent"). + Default(false), + field.Bool("broadcasted"). + Default(false), + field.Bool("read"). + Default(false), + field.String("agent_id"). + Optional(), + field.String("group_id"). + Optional(), + field.Time("created"). + Default(time.Now). + Immutable(), + } +} + +// Indexes of the Message. +func (Message) Indexes() []ent.Index { + return []ent.Index{ + index.Fields("project_id"), + index.Fields("recipient", "recipient_id"), + index.Fields("created"), + } +} + +// Annotations of the Message. +func (Message) Annotations() []schema.Annotation { + return []schema.Annotation{ + entsql.Annotation{Table: "messages"}, + } +} diff --git a/pkg/ent/schema/notification.go b/pkg/ent/schema/notification.go new file mode 100644 index 000000000..dc1bec137 --- /dev/null +++ b/pkg/ent/schema/notification.go @@ -0,0 +1,78 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package schema + +import ( + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/entsql" + "entgo.io/ent/schema" + "entgo.io/ent/schema/field" + "entgo.io/ent/schema/index" + "github.com/google/uuid" +) + +// Notification holds the schema definition for the Notification entity, mapping +// the legacy SQLite `notifications` table. +// +// Foreign keys (subscription_id, agent_id, project_id) are modeled as plain +// UUID columns rather than Ent edges to keep this periphery schema independent; +// edge wiring is deferred to a later pass. +type Notification struct { + ent.Schema +} + +// Fields of the Notification. +func (Notification) Fields() []ent.Field { + return []ent.Field{ + field.UUID("id", uuid.UUID{}). + Default(uuid.New). + Immutable(), + field.UUID("subscription_id", uuid.UUID{}), + field.UUID("agent_id", uuid.UUID{}), + field.UUID("project_id", uuid.UUID{}), + field.String("subscriber_type"). + NotEmpty(), + field.String("subscriber_id"). + NotEmpty(), + field.String("status"). + NotEmpty(), + field.String("message"). + NotEmpty(), + field.Bool("dispatched"). + Default(false), + field.Bool("acknowledged"). + Default(false), + field.Time("created"). + Default(time.Now). + Immutable(), + } +} + +// Indexes of the Notification. +func (Notification) Indexes() []ent.Index { + return []ent.Index{ + index.Fields("subscription_id"), + index.Fields("project_id", "subscriber_type", "subscriber_id"), + } +} + +// Annotations of the Notification. +func (Notification) Annotations() []schema.Annotation { + return []schema.Annotation{ + entsql.Annotation{Table: "notifications"}, + } +} diff --git a/pkg/ent/schema/notificationsubscription.go b/pkg/ent/schema/notificationsubscription.go new file mode 100644 index 000000000..82ff3c316 --- /dev/null +++ b/pkg/ent/schema/notificationsubscription.go @@ -0,0 +1,81 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package schema + +import ( + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/entsql" + "entgo.io/ent/schema" + "entgo.io/ent/schema/field" + "entgo.io/ent/schema/index" + "github.com/google/uuid" +) + +// NotificationSubscription holds the schema definition for the +// NotificationSubscription entity, mapping the legacy SQLite +// `notification_subscriptions` table. +// +// trigger_activities is kept as a raw JSON string to stay dialect-neutral. The +// SQLite store enforced uniqueness via a COALESCE-based partial index over +// (scope, agent_id, subscriber_type, subscriber_id, project_id); that +// expression index is not dialect-neutral, so a plain composite index is +// declared here and the unique-with-NULL semantics are deferred to the port +// layer / migration. +type NotificationSubscription struct { + ent.Schema +} + +// Fields of the NotificationSubscription. +func (NotificationSubscription) Fields() []ent.Field { + return []ent.Field{ + field.UUID("id", uuid.UUID{}). + Default(uuid.New). + Immutable(), + field.String("scope"). + Default("agent"), + field.UUID("agent_id", uuid.UUID{}). + Optional(). + Nillable(), + field.String("subscriber_type"). + Default("agent"), + field.String("subscriber_id"). + NotEmpty(), + field.UUID("project_id", uuid.UUID{}), + field.String("trigger_activities"). + NotEmpty(), + field.String("created_by"). + NotEmpty(), + field.Time("created"). + Default(time.Now). + Immutable(), + } +} + +// Indexes of the NotificationSubscription. +func (NotificationSubscription) Indexes() []ent.Index { + return []ent.Index{ + index.Fields("scope", "agent_id", "subscriber_type", "subscriber_id", "project_id"), + index.Fields("project_id"), + } +} + +// Annotations of the NotificationSubscription. +func (NotificationSubscription) Annotations() []schema.Annotation { + return []schema.Annotation{ + entsql.Annotation{Table: "notification_subscriptions"}, + } +} diff --git a/pkg/ent/schema/project.go b/pkg/ent/schema/project.go index c61b3d61d..b795ee85a 100644 --- a/pkg/ent/schema/project.go +++ b/pkg/ent/schema/project.go @@ -25,9 +25,15 @@ import ( "github.com/google/uuid" ) -// Project holds the schema definition for the Project entity. -// This is a minimal schema for edge compilation; operational fields -// will be added when the project entity is fully migrated to Ent. +// Project holds the schema definition for the Project entity, mapping the legacy +// SQLite `projects` table (groves). +// +// JSON-bearing operational columns (shared_dirs, github_permissions, +// github_app_status, git_identity) are kept as raw JSON strings to stay +// dialect-neutral and to avoid importing the store/api model types into the +// schema package, matching the RuntimeBroker convention. The port layer +// (entadapter) marshals/unmarshals them. Computed fields on store.Project +// (AgentCount, ActiveBrokerCount, ProjectType, OwnerName) are not persisted. type Project struct { ent.Schema } @@ -46,10 +52,15 @@ func (Project) Fields() []ent.Field { field.String("git_remote"). Optional(). Nillable(), + field.String("default_runtime_broker_id"). + Optional(). + Nillable(), field.JSON("labels", map[string]string{}). Optional(), field.JSON("annotations", map[string]string{}). Optional(), + field.String("shared_dirs"). + Optional(), field.Time("created"). Default(time.Now). Immutable(), @@ -62,6 +73,15 @@ func (Project) Fields() []ent.Field { Optional(), field.String("visibility"). Default("private"), + field.Int64("github_installation_id"). + Optional(). + Nillable(), + field.String("github_permissions"). + Optional(), + field.String("github_app_status"). + Optional(), + field.String("git_identity"). + Optional(), } } diff --git a/pkg/ent/schema/projectcontributor.go b/pkg/ent/schema/projectcontributor.go new file mode 100644 index 000000000..58146f5da --- /dev/null +++ b/pkg/ent/schema/projectcontributor.go @@ -0,0 +1,81 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package schema + +import ( + "entgo.io/ent" + "entgo.io/ent/dialect/entsql" + "entgo.io/ent/schema" + "entgo.io/ent/schema/field" + "entgo.io/ent/schema/index" + "github.com/google/uuid" +) + +// ProjectContributor holds the schema definition for the ProjectContributor +// entity, mapping the legacy SQLite `project_contributors` table (was +// `grove_contributors` before the V50 grove→project rename). It records which +// runtime brokers contribute to / provide for a project. +// +// SQLite used a composite primary key (project_id, broker_id) with no id column; +// Ent prefers a single id, so a surrogate UUID id is added and the original key +// is enforced via a unique index. profiles is a raw JSON string. +type ProjectContributor struct { + ent.Schema +} + +// Fields of the ProjectContributor. +func (ProjectContributor) Fields() []ent.Field { + return []ent.Field{ + field.UUID("id", uuid.UUID{}). + Default(uuid.New). + Immutable(), + field.UUID("project_id", uuid.UUID{}), + field.UUID("broker_id", uuid.UUID{}), + field.String("broker_name"). + NotEmpty(), + field.String("mode"). + Default("connected"), + field.String("status"). + Default("offline"), + field.String("profiles"). + Optional(), + field.Time("last_seen"). + Optional(). + Nillable(), + field.String("local_path"). + Optional(), + field.String("linked_by"). + Optional(), + field.Time("linked_at"). + Optional(). + Nillable(), + } +} + +// Indexes of the ProjectContributor. +func (ProjectContributor) Indexes() []ent.Index { + return []ent.Index{ + index.Fields("project_id", "broker_id"). + Unique(), + index.Fields("broker_id"), + } +} + +// Annotations of the ProjectContributor. +func (ProjectContributor) Annotations() []schema.Annotation { + return []schema.Annotation{ + entsql.Annotation{Table: "project_contributors"}, + } +} diff --git a/pkg/ent/schema/projectsyncstate.go b/pkg/ent/schema/projectsyncstate.go new file mode 100644 index 000000000..089aa0c88 --- /dev/null +++ b/pkg/ent/schema/projectsyncstate.go @@ -0,0 +1,72 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package schema + +import ( + "entgo.io/ent" + "entgo.io/ent/dialect/entsql" + "entgo.io/ent/schema" + "entgo.io/ent/schema/field" + "entgo.io/ent/schema/index" + "github.com/google/uuid" +) + +// ProjectSyncState holds the schema definition for the ProjectSyncState entity, +// mapping the legacy SQLite `project_sync_state` table (was `grove_sync_state` +// before the V50 grove→project rename). +// +// SQLite used a composite primary key (project_id, broker_id) with no id column; +// Ent prefers a single id, so a surrogate UUID id is added and the original key +// is enforced via a unique index. broker_id is a plain string (it defaulted to +// ” in SQLite for project-wide sync state). +type ProjectSyncState struct { + ent.Schema +} + +// Fields of the ProjectSyncState. +func (ProjectSyncState) Fields() []ent.Field { + return []ent.Field{ + field.UUID("id", uuid.UUID{}). + Default(uuid.New). + Immutable(), + field.UUID("project_id", uuid.UUID{}), + field.String("broker_id"). + Default(""), + field.Time("last_sync_time"). + Optional(). + Nillable(), + field.String("last_commit_sha"). + Optional(), + field.Int("file_count"). + Default(0), + field.Int64("total_bytes"). + Default(0), + } +} + +// Indexes of the ProjectSyncState. +func (ProjectSyncState) Indexes() []ent.Index { + return []ent.Index{ + index.Fields("project_id", "broker_id"). + Unique(), + } +} + +// Annotations of the ProjectSyncState. +func (ProjectSyncState) Annotations() []schema.Annotation { + return []schema.Annotation{ + entsql.Annotation{Table: "project_sync_state"}, + } +} diff --git a/pkg/ent/schema/runtimebroker.go b/pkg/ent/schema/runtimebroker.go new file mode 100644 index 000000000..5fcbc7fb2 --- /dev/null +++ b/pkg/ent/schema/runtimebroker.go @@ -0,0 +1,111 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package schema + +import ( + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/entsql" + "entgo.io/ent/schema" + "entgo.io/ent/schema/field" + "entgo.io/ent/schema/index" + "github.com/google/uuid" +) + +// RuntimeBroker holds the schema definition for the RuntimeBroker entity, +// mapping the legacy SQLite `runtime_brokers` table. +// +// JSON-bearing columns (capabilities, supported_harnesses, resources, runtimes, +// labels, annotations) are kept as raw strings to stay dialect-neutral and match +// the existing store's raw-marshaling behavior during the dual-write phase. +type RuntimeBroker struct { + ent.Schema +} + +// Fields of the RuntimeBroker. +func (RuntimeBroker) Fields() []ent.Field { + return []ent.Field{ + field.UUID("id", uuid.UUID{}). + Default(uuid.New). + Immutable(), + field.String("name"). + NotEmpty(), + field.String("slug"). + NotEmpty(), + // type/mode are vestigial columns in the legacy store (the SQLite store + // always writes ""); kept Optional for column parity rather than required. + field.String("type"). + Optional(), + field.String("mode"). + Default("connected"), + field.String("version"). + Optional(), + // lock_version is an internal optimistic-concurrency token (not surfaced + // on store.RuntimeBroker, which already uses "version" for the broker + // software version). The heartbeat and full-update paths compare-and-set + // this column to serialize concurrent writers without SELECT ... FOR + // UPDATE, so the same logic is correct on both SQLite (tests) and + // Postgres (production). + field.Int64("lock_version"). + Default(0), + field.String("status"). + Default("offline"), + field.String("connection_state"). + Default("disconnected"), + field.Time("last_heartbeat"). + Optional(). + Nillable(), + field.String("capabilities"). + Optional(), + field.String("supported_harnesses"). + Optional(), + field.String("resources"). + Optional(), + field.String("runtimes"). + Optional(), + field.String("labels"). + Optional(), + field.String("annotations"). + Optional(), + field.String("endpoint"). + Optional(), + field.String("created_by"). + Optional(), + field.Bool("auto_provide"). + Default(false), + field.Time("created"). + Default(time.Now). + Immutable(), + field.Time("updated"). + Default(time.Now). + UpdateDefault(time.Now), + } +} + +// Indexes of the RuntimeBroker. +func (RuntimeBroker) Indexes() []ent.Index { + return []ent.Index{ + index.Fields("slug"), + index.Fields("status"), + } +} + +// Annotations of the RuntimeBroker. +func (RuntimeBroker) Annotations() []schema.Annotation { + return []schema.Annotation{ + entsql.Annotation{Table: "runtime_brokers"}, + } +} diff --git a/pkg/ent/schema/schedule.go b/pkg/ent/schema/schedule.go new file mode 100644 index 000000000..6d4041c52 --- /dev/null +++ b/pkg/ent/schema/schedule.go @@ -0,0 +1,94 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package schema + +import ( + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/entsql" + "entgo.io/ent/schema" + "entgo.io/ent/schema/field" + "entgo.io/ent/schema/index" + "github.com/google/uuid" +) + +// Schedule holds the schema definition for the Schedule entity, mapping the +// legacy SQLite `schedules` table. +// +// payload is a raw JSON string. The SQLite store used a partial index on +// next_run_at (WHERE status='active'); a plain index is declared here to stay +// dialect-neutral. +type Schedule struct { + ent.Schema +} + +// Fields of the Schedule. +func (Schedule) Fields() []ent.Field { + return []ent.Field{ + field.UUID("id", uuid.UUID{}). + Default(uuid.New). + Immutable(), + field.UUID("project_id", uuid.UUID{}), + field.String("name"). + NotEmpty(), + field.String("cron_expr"). + NotEmpty(), + field.String("event_type"). + NotEmpty(), + field.String("payload"). + Default("{}"), + field.String("status"). + Default("active"), + field.Time("next_run_at"). + Optional(). + Nillable(), + field.Time("last_run_at"). + Optional(). + Nillable(), + field.String("last_run_status"). + Optional(), + field.String("last_run_error"). + Optional(), + field.Int("run_count"). + Default(0), + field.Int("error_count"). + Default(0), + field.String("created_by"). + Optional(), + field.Time("created"). + Default(time.Now). + Immutable(), + field.Time("updated"). + Default(time.Now). + UpdateDefault(time.Now), + } +} + +// Indexes of the Schedule. +func (Schedule) Indexes() []ent.Index { + return []ent.Index{ + index.Fields("project_id", "name"). + Unique(), + index.Fields("next_run_at"), + } +} + +// Annotations of the Schedule. +func (Schedule) Annotations() []schema.Annotation { + return []schema.Annotation{ + entsql.Annotation{Table: "schedules"}, + } +} diff --git a/pkg/ent/schema/scheduledevent.go b/pkg/ent/schema/scheduledevent.go new file mode 100644 index 000000000..5a869d000 --- /dev/null +++ b/pkg/ent/schema/scheduledevent.go @@ -0,0 +1,82 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package schema + +import ( + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/entsql" + "entgo.io/ent/schema" + "entgo.io/ent/schema/field" + "entgo.io/ent/schema/index" + "github.com/google/uuid" +) + +// ScheduledEvent holds the schema definition for the ScheduledEvent entity, +// mapping the legacy SQLite `scheduled_events` table. +// +// payload is a raw JSON string. The SQLite store used a partial index on fire_at +// (WHERE status='pending'); a plain index is declared here to stay +// dialect-neutral. schedule_id is an optional back-reference string (defaulted +// to ” in SQLite). +type ScheduledEvent struct { + ent.Schema +} + +// Fields of the ScheduledEvent. +func (ScheduledEvent) Fields() []ent.Field { + return []ent.Field{ + field.UUID("id", uuid.UUID{}). + Default(uuid.New). + Immutable(), + field.UUID("project_id", uuid.UUID{}), + field.String("event_type"). + NotEmpty(), + field.Time("fire_at"), + field.String("payload"). + NotEmpty(), + field.String("status"). + Default("pending"), + field.String("created_by"). + Optional(), + field.Time("fired_at"). + Optional(). + Nillable(), + field.String("error"). + Optional(), + field.String("schedule_id"). + Optional(), + field.Time("created"). + Default(time.Now). + Immutable(), + } +} + +// Indexes of the ScheduledEvent. +func (ScheduledEvent) Indexes() []ent.Index { + return []ent.Index{ + index.Fields("fire_at"), + index.Fields("project_id"), + index.Fields("status"), + } +} + +// Annotations of the ScheduledEvent. +func (ScheduledEvent) Annotations() []schema.Annotation { + return []schema.Annotation{ + entsql.Annotation{Table: "scheduled_events"}, + } +} diff --git a/pkg/ent/schema/secret.go b/pkg/ent/schema/secret.go new file mode 100644 index 000000000..8fa72c289 --- /dev/null +++ b/pkg/ent/schema/secret.go @@ -0,0 +1,94 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package schema + +import ( + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/entsql" + "entgo.io/ent/schema" + "entgo.io/ent/schema/field" + "entgo.io/ent/schema/index" + "github.com/google/uuid" +) + +// Secret holds the schema definition for the Secret entity, mapping the legacy +// SQLite `secrets` table. Secrets are polymorphically scoped (hub/user/project/ +// runtime_broker) via (scope, scope_id), so no FK edges are declared. +// +// encrypted_value stores the encrypted secret payload as TEXT (base64), not a +// BLOB, and is marked Sensitive so it is never logged or serialized. +type Secret struct { + ent.Schema +} + +// Fields of the Secret. +func (Secret) Fields() []ent.Field { + return []ent.Field{ + field.UUID("id", uuid.UUID{}). + Default(uuid.New). + Immutable(), + field.String("key"). + NotEmpty(), + field.String("encrypted_value"). + Sensitive(), + field.String("secret_ref"). + Optional(), + field.Enum("secret_type"). + Values("environment", "variable", "file", "internal"). + Default("environment"), + field.String("target"). + Optional(), + field.String("scope"). + NotEmpty(), + field.String("scope_id"), + field.String("description"). + Optional(), + field.Enum("injection_mode"). + Values("always", "as_needed"). + Default("as_needed"), + field.Bool("allow_progeny"). + Default(false), + field.Int("version"). + Default(1), + field.String("created_by"). + Optional(), + field.String("updated_by"). + Optional(), + field.Time("created"). + Default(time.Now). + Immutable(), + field.Time("updated"). + Default(time.Now). + UpdateDefault(time.Now), + } +} + +// Indexes of the Secret. +func (Secret) Indexes() []ent.Index { + return []ent.Index{ + index.Fields("key", "scope", "scope_id"). + Unique(), + index.Fields("scope", "scope_id"), + } +} + +// Annotations of the Secret. +func (Secret) Annotations() []schema.Annotation { + return []schema.Annotation{ + entsql.Annotation{Table: "secrets"}, + } +} diff --git a/pkg/ent/schema/subscriptiontemplate.go b/pkg/ent/schema/subscriptiontemplate.go new file mode 100644 index 000000000..baaed7578 --- /dev/null +++ b/pkg/ent/schema/subscriptiontemplate.go @@ -0,0 +1,68 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package schema + +import ( + "entgo.io/ent" + "entgo.io/ent/dialect/entsql" + "entgo.io/ent/schema" + "entgo.io/ent/schema/field" + "entgo.io/ent/schema/index" + "github.com/google/uuid" +) + +// SubscriptionTemplate holds the schema definition for the SubscriptionTemplate +// entity, mapping the legacy SQLite `subscription_templates` table. +// +// project_id is nullable (the SQLite column defaulted to ” for global-scoped +// templates); the (project_id, name) uniqueness is enforced via a unique index. +type SubscriptionTemplate struct { + ent.Schema +} + +// Fields of the SubscriptionTemplate. +func (SubscriptionTemplate) Fields() []ent.Field { + return []ent.Field{ + field.UUID("id", uuid.UUID{}). + Default(uuid.New). + Immutable(), + field.String("name"). + NotEmpty(), + field.String("scope"). + Default("project"), + field.String("trigger_activities"). + NotEmpty(), + field.UUID("project_id", uuid.UUID{}). + Optional(). + Nillable(), + field.String("created_by"). + NotEmpty(), + } +} + +// Indexes of the SubscriptionTemplate. +func (SubscriptionTemplate) Indexes() []ent.Index { + return []ent.Index{ + index.Fields("project_id", "name"). + Unique(), + } +} + +// Annotations of the SubscriptionTemplate. +func (SubscriptionTemplate) Annotations() []schema.Annotation { + return []schema.Annotation{ + entsql.Annotation{Table: "subscription_templates"}, + } +} diff --git a/pkg/ent/schema/template.go b/pkg/ent/schema/template.go new file mode 100644 index 000000000..473c4469b --- /dev/null +++ b/pkg/ent/schema/template.go @@ -0,0 +1,119 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package schema + +import ( + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/entsql" + "entgo.io/ent/schema" + "entgo.io/ent/schema/field" + "entgo.io/ent/schema/index" + "github.com/google/uuid" +) + +// Template holds the schema definition for the Template entity, mapping the +// legacy SQLite `templates` table (final post-V50 state; grove_id renamed to +// project_id). +// +// JSON columns (config, files) are kept as raw strings to stay dialect-neutral. +// Foreign references (project_id, scope_id, owner ids) are modeled as plain +// strings rather than edges so global/unscoped rows with empty values port +// cleanly; edges can be added later when the full entity migrates. +type Template struct { + ent.Schema +} + +// Fields of the Template. +func (Template) Fields() []ent.Field { + return []ent.Field{ + field.UUID("id", uuid.UUID{}). + Default(uuid.New). + Immutable(), + field.String("name"). + NotEmpty(), + field.String("slug"). + NotEmpty(), + field.String("display_name"). + Optional(), + field.String("description"). + Optional(), + // harness may be empty: a directory template that declares no harness type + // leaves this blank; the raw-SQL store allowed it and BootstrapTemplatesFromDir + // relies on storing such templates rather than skipping them. + field.String("harness"), + field.String("default_harness_config"). + Optional(), + field.String("image"). + Optional(), + field.String("config"). + Optional(), + field.String("content_hash"). + Optional(), + field.String("scope"). + Default("global"), + field.String("scope_id"). + Optional(), + field.String("project_id"). + Optional(), + field.String("storage_uri"). + Optional(), + field.String("storage_bucket"). + Optional(), + field.String("storage_path"). + Optional(), + field.String("files"). + Optional(), + field.String("base_template"). + Optional(), + field.Bool("locked"). + Default(false), + field.Enum("status"). + Values("pending", "active", "archived"). + Default("active"), + field.String("owner_id"). + Optional(), + field.String("created_by"). + Optional(), + field.String("updated_by"). + Optional(), + field.String("visibility"). + Default("private"), + field.Time("created"). + Default(time.Now). + Immutable(), + field.Time("updated"). + Default(time.Now). + UpdateDefault(time.Now), + } +} + +// Indexes of the Template. +func (Template) Indexes() []ent.Index { + return []ent.Index{ + index.Fields("slug", "scope"), + index.Fields("harness"), + index.Fields("status"), + index.Fields("content_hash"), + } +} + +// Annotations of the Template. +func (Template) Annotations() []schema.Annotation { + return []schema.Annotation{ + entsql.Annotation{Table: "templates"}, + } +} diff --git a/pkg/ent/schema/user.go b/pkg/ent/schema/user.go index e061c7896..b67cf85f9 100644 --- a/pkg/ent/schema/user.go +++ b/pkg/ent/schema/user.go @@ -20,6 +20,7 @@ import ( "entgo.io/ent" "entgo.io/ent/schema/edge" "entgo.io/ent/schema/field" + "entgo.io/ent/schema/index" "github.com/google/uuid" ) @@ -34,11 +35,24 @@ func (User) Fields() []ent.Field { field.UUID("id", uuid.UUID{}). Default(uuid.New). Immutable(), + // email was UNIQUE COLLATE NOCASE in the legacy SQLite schema. Postgres + // has no NOCASE collation, so case-insensitive uniqueness and lookup are + // enforced at the port layer (entadapter): emails are normalized to + // lower case on write and matched with EmailEqualFold (lower(email) = + // lower($1)) on read. The Unique() index below therefore enforces + // case-insensitive uniqueness because every stored value is normalized. + // This is equivalent to a lower(email) functional unique index without + // requiring an expression index, which ent codegen + AutoMigrate cannot + // emit for both SQLite (tests) and Postgres. field.String("email"). Unique(). NotEmpty(), - field.String("display_name"). - NotEmpty(), + // display_name is required (NOT NULL) but may be empty, matching the + // former raw-SQL store (display_name TEXT NOT NULL). Some identity + // providers omit a display name; the broker/user handlers fall back to + // the email in that case, so empty values must be storable. A stricter + // NotEmpty() here would reject those users and break the fallback. + field.String("display_name"), field.String("avatar_url"). Optional(), field.Enum("role"). @@ -55,6 +69,17 @@ func (User) Fields() []ent.Field { field.Time("last_login"). Optional(). Nillable(), + field.Time("last_seen"). + Optional(). + Nillable(), + } +} + +// Indexes of the User. +func (User) Indexes() []ent.Index { + return []ent.Index{ + // Supports the lastSeen sort option in ListUsers. + index.Fields("last_seen"), } } diff --git a/pkg/ent/schema/useraccesstoken.go b/pkg/ent/schema/useraccesstoken.go new file mode 100644 index 000000000..1fbc346f3 --- /dev/null +++ b/pkg/ent/schema/useraccesstoken.go @@ -0,0 +1,83 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package schema + +import ( + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/entsql" + "entgo.io/ent/schema" + "entgo.io/ent/schema/field" + "entgo.io/ent/schema/index" + "github.com/google/uuid" +) + +// UserAccessToken holds the schema definition for the UserAccessToken entity, +// mapping the legacy SQLite `user_access_tokens` table. +// +// user_id and project_id are required UUID foreign keys (modeled as plain +// columns, no Ent edges); scopes is a raw JSON string. key_hash is the unique +// lookup key and is marked Sensitive. +type UserAccessToken struct { + ent.Schema +} + +// Fields of the UserAccessToken. +func (UserAccessToken) Fields() []ent.Field { + return []ent.Field{ + field.UUID("id", uuid.UUID{}). + Default(uuid.New). + Immutable(), + field.UUID("user_id", uuid.UUID{}), + field.String("name"). + NotEmpty(), + field.String("prefix"). + NotEmpty(), + field.String("key_hash"). + Sensitive(). + Unique(). + NotEmpty(), + field.UUID("project_id", uuid.UUID{}), + field.String("scopes"). + NotEmpty(), + field.Bool("revoked"). + Default(false), + field.Time("expires_at"). + Optional(). + Nillable(), + field.Time("last_used"). + Optional(). + Nillable(), + field.Time("created"). + Default(time.Now). + Immutable(), + } +} + +// Indexes of the UserAccessToken. +func (UserAccessToken) Indexes() []ent.Index { + return []ent.Index{ + index.Fields("user_id"), + index.Fields("project_id"), + } +} + +// Annotations of the UserAccessToken. +func (UserAccessToken) Annotations() []schema.Annotation { + return []schema.Annotation{ + entsql.Annotation{Table: "user_access_tokens"}, + } +} diff --git a/pkg/ent/secret.go b/pkg/ent/secret.go new file mode 100644 index 000000000..fc3a3f34f --- /dev/null +++ b/pkg/ent/secret.go @@ -0,0 +1,264 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "fmt" + "strings" + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "github.com/GoogleCloudPlatform/scion/pkg/ent/secret" + "github.com/google/uuid" +) + +// Secret is the model entity for the Secret schema. +type Secret struct { + config `json:"-"` + // ID of the ent. + ID uuid.UUID `json:"id,omitempty"` + // Key holds the value of the "key" field. + Key string `json:"key,omitempty"` + // EncryptedValue holds the value of the "encrypted_value" field. + EncryptedValue string `json:"-"` + // SecretRef holds the value of the "secret_ref" field. + SecretRef string `json:"secret_ref,omitempty"` + // SecretType holds the value of the "secret_type" field. + SecretType secret.SecretType `json:"secret_type,omitempty"` + // Target holds the value of the "target" field. + Target string `json:"target,omitempty"` + // Scope holds the value of the "scope" field. + Scope string `json:"scope,omitempty"` + // ScopeID holds the value of the "scope_id" field. + ScopeID string `json:"scope_id,omitempty"` + // Description holds the value of the "description" field. + Description string `json:"description,omitempty"` + // InjectionMode holds the value of the "injection_mode" field. + InjectionMode secret.InjectionMode `json:"injection_mode,omitempty"` + // AllowProgeny holds the value of the "allow_progeny" field. + AllowProgeny bool `json:"allow_progeny,omitempty"` + // Version holds the value of the "version" field. + Version int `json:"version,omitempty"` + // CreatedBy holds the value of the "created_by" field. + CreatedBy string `json:"created_by,omitempty"` + // UpdatedBy holds the value of the "updated_by" field. + UpdatedBy string `json:"updated_by,omitempty"` + // Created holds the value of the "created" field. + Created time.Time `json:"created,omitempty"` + // Updated holds the value of the "updated" field. + Updated time.Time `json:"updated,omitempty"` + selectValues sql.SelectValues +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*Secret) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case secret.FieldAllowProgeny: + values[i] = new(sql.NullBool) + case secret.FieldVersion: + values[i] = new(sql.NullInt64) + case secret.FieldKey, secret.FieldEncryptedValue, secret.FieldSecretRef, secret.FieldSecretType, secret.FieldTarget, secret.FieldScope, secret.FieldScopeID, secret.FieldDescription, secret.FieldInjectionMode, secret.FieldCreatedBy, secret.FieldUpdatedBy: + values[i] = new(sql.NullString) + case secret.FieldCreated, secret.FieldUpdated: + values[i] = new(sql.NullTime) + case secret.FieldID: + values[i] = new(uuid.UUID) + default: + values[i] = new(sql.UnknownType) + } + } + return values, nil +} + +// assignValues assigns the values that were returned from sql.Rows (after scanning) +// to the Secret fields. +func (_m *Secret) assignValues(columns []string, values []any) error { + if m, n := len(values), len(columns); m < n { + return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) + } + for i := range columns { + switch columns[i] { + case secret.FieldID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field id", values[i]) + } else if value != nil { + _m.ID = *value + } + case secret.FieldKey: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field key", values[i]) + } else if value.Valid { + _m.Key = value.String + } + case secret.FieldEncryptedValue: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field encrypted_value", values[i]) + } else if value.Valid { + _m.EncryptedValue = value.String + } + case secret.FieldSecretRef: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field secret_ref", values[i]) + } else if value.Valid { + _m.SecretRef = value.String + } + case secret.FieldSecretType: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field secret_type", values[i]) + } else if value.Valid { + _m.SecretType = secret.SecretType(value.String) + } + case secret.FieldTarget: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field target", values[i]) + } else if value.Valid { + _m.Target = value.String + } + case secret.FieldScope: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field scope", values[i]) + } else if value.Valid { + _m.Scope = value.String + } + case secret.FieldScopeID: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field scope_id", values[i]) + } else if value.Valid { + _m.ScopeID = value.String + } + case secret.FieldDescription: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field description", values[i]) + } else if value.Valid { + _m.Description = value.String + } + case secret.FieldInjectionMode: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field injection_mode", values[i]) + } else if value.Valid { + _m.InjectionMode = secret.InjectionMode(value.String) + } + case secret.FieldAllowProgeny: + if value, ok := values[i].(*sql.NullBool); !ok { + return fmt.Errorf("unexpected type %T for field allow_progeny", values[i]) + } else if value.Valid { + _m.AllowProgeny = value.Bool + } + case secret.FieldVersion: + if value, ok := values[i].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for field version", values[i]) + } else if value.Valid { + _m.Version = int(value.Int64) + } + case secret.FieldCreatedBy: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field created_by", values[i]) + } else if value.Valid { + _m.CreatedBy = value.String + } + case secret.FieldUpdatedBy: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field updated_by", values[i]) + } else if value.Valid { + _m.UpdatedBy = value.String + } + case secret.FieldCreated: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field created", values[i]) + } else if value.Valid { + _m.Created = value.Time + } + case secret.FieldUpdated: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field updated", values[i]) + } else if value.Valid { + _m.Updated = value.Time + } + default: + _m.selectValues.Set(columns[i], values[i]) + } + } + return nil +} + +// Value returns the ent.Value that was dynamically selected and assigned to the Secret. +// This includes values selected through modifiers, order, etc. +func (_m *Secret) Value(name string) (ent.Value, error) { + return _m.selectValues.Get(name) +} + +// Update returns a builder for updating this Secret. +// Note that you need to call Secret.Unwrap() before calling this method if this Secret +// was returned from a transaction, and the transaction was committed or rolled back. +func (_m *Secret) Update() *SecretUpdateOne { + return NewSecretClient(_m.config).UpdateOne(_m) +} + +// Unwrap unwraps the Secret entity that was returned from a transaction after it was closed, +// so that all future queries will be executed through the driver which created the transaction. +func (_m *Secret) Unwrap() *Secret { + _tx, ok := _m.config.driver.(*txDriver) + if !ok { + panic("ent: Secret is not a transactional entity") + } + _m.config.driver = _tx.drv + return _m +} + +// String implements the fmt.Stringer. +func (_m *Secret) String() string { + var builder strings.Builder + builder.WriteString("Secret(") + builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID)) + builder.WriteString("key=") + builder.WriteString(_m.Key) + builder.WriteString(", ") + builder.WriteString("encrypted_value=") + builder.WriteString(", ") + builder.WriteString("secret_ref=") + builder.WriteString(_m.SecretRef) + builder.WriteString(", ") + builder.WriteString("secret_type=") + builder.WriteString(fmt.Sprintf("%v", _m.SecretType)) + builder.WriteString(", ") + builder.WriteString("target=") + builder.WriteString(_m.Target) + builder.WriteString(", ") + builder.WriteString("scope=") + builder.WriteString(_m.Scope) + builder.WriteString(", ") + builder.WriteString("scope_id=") + builder.WriteString(_m.ScopeID) + builder.WriteString(", ") + builder.WriteString("description=") + builder.WriteString(_m.Description) + builder.WriteString(", ") + builder.WriteString("injection_mode=") + builder.WriteString(fmt.Sprintf("%v", _m.InjectionMode)) + builder.WriteString(", ") + builder.WriteString("allow_progeny=") + builder.WriteString(fmt.Sprintf("%v", _m.AllowProgeny)) + builder.WriteString(", ") + builder.WriteString("version=") + builder.WriteString(fmt.Sprintf("%v", _m.Version)) + builder.WriteString(", ") + builder.WriteString("created_by=") + builder.WriteString(_m.CreatedBy) + builder.WriteString(", ") + builder.WriteString("updated_by=") + builder.WriteString(_m.UpdatedBy) + builder.WriteString(", ") + builder.WriteString("created=") + builder.WriteString(_m.Created.Format(time.ANSIC)) + builder.WriteString(", ") + builder.WriteString("updated=") + builder.WriteString(_m.Updated.Format(time.ANSIC)) + builder.WriteByte(')') + return builder.String() +} + +// Secrets is a parsable slice of Secret. +type Secrets []*Secret diff --git a/pkg/ent/secret/secret.go b/pkg/ent/secret/secret.go new file mode 100644 index 000000000..a5ce96f8a --- /dev/null +++ b/pkg/ent/secret/secret.go @@ -0,0 +1,236 @@ +// Code generated by ent, DO NOT EDIT. + +package secret + +import ( + "fmt" + "time" + + "entgo.io/ent/dialect/sql" + "github.com/google/uuid" +) + +const ( + // Label holds the string label denoting the secret type in the database. + Label = "secret" + // FieldID holds the string denoting the id field in the database. + FieldID = "id" + // FieldKey holds the string denoting the key field in the database. + FieldKey = "key" + // FieldEncryptedValue holds the string denoting the encrypted_value field in the database. + FieldEncryptedValue = "encrypted_value" + // FieldSecretRef holds the string denoting the secret_ref field in the database. + FieldSecretRef = "secret_ref" + // FieldSecretType holds the string denoting the secret_type field in the database. + FieldSecretType = "secret_type" + // FieldTarget holds the string denoting the target field in the database. + FieldTarget = "target" + // FieldScope holds the string denoting the scope field in the database. + FieldScope = "scope" + // FieldScopeID holds the string denoting the scope_id field in the database. + FieldScopeID = "scope_id" + // FieldDescription holds the string denoting the description field in the database. + FieldDescription = "description" + // FieldInjectionMode holds the string denoting the injection_mode field in the database. + FieldInjectionMode = "injection_mode" + // FieldAllowProgeny holds the string denoting the allow_progeny field in the database. + FieldAllowProgeny = "allow_progeny" + // FieldVersion holds the string denoting the version field in the database. + FieldVersion = "version" + // FieldCreatedBy holds the string denoting the created_by field in the database. + FieldCreatedBy = "created_by" + // FieldUpdatedBy holds the string denoting the updated_by field in the database. + FieldUpdatedBy = "updated_by" + // FieldCreated holds the string denoting the created field in the database. + FieldCreated = "created" + // FieldUpdated holds the string denoting the updated field in the database. + FieldUpdated = "updated" + // Table holds the table name of the secret in the database. + Table = "secrets" +) + +// Columns holds all SQL columns for secret fields. +var Columns = []string{ + FieldID, + FieldKey, + FieldEncryptedValue, + FieldSecretRef, + FieldSecretType, + FieldTarget, + FieldScope, + FieldScopeID, + FieldDescription, + FieldInjectionMode, + FieldAllowProgeny, + FieldVersion, + FieldCreatedBy, + FieldUpdatedBy, + FieldCreated, + FieldUpdated, +} + +// ValidColumn reports if the column name is valid (part of the table columns). +func ValidColumn(column string) bool { + for i := range Columns { + if column == Columns[i] { + return true + } + } + return false +} + +var ( + // KeyValidator is a validator for the "key" field. It is called by the builders before save. + KeyValidator func(string) error + // ScopeValidator is a validator for the "scope" field. It is called by the builders before save. + ScopeValidator func(string) error + // DefaultAllowProgeny holds the default value on creation for the "allow_progeny" field. + DefaultAllowProgeny bool + // DefaultVersion holds the default value on creation for the "version" field. + DefaultVersion int + // DefaultCreated holds the default value on creation for the "created" field. + DefaultCreated func() time.Time + // DefaultUpdated holds the default value on creation for the "updated" field. + DefaultUpdated func() time.Time + // UpdateDefaultUpdated holds the default value on update for the "updated" field. + UpdateDefaultUpdated func() time.Time + // DefaultID holds the default value on creation for the "id" field. + DefaultID func() uuid.UUID +) + +// SecretType defines the type for the "secret_type" enum field. +type SecretType string + +// SecretTypeEnvironment is the default value of the SecretType enum. +const DefaultSecretType = SecretTypeEnvironment + +// SecretType values. +const ( + SecretTypeEnvironment SecretType = "environment" + SecretTypeVariable SecretType = "variable" + SecretTypeFile SecretType = "file" + SecretTypeInternal SecretType = "internal" +) + +func (st SecretType) String() string { + return string(st) +} + +// SecretTypeValidator is a validator for the "secret_type" field enum values. It is called by the builders before save. +func SecretTypeValidator(st SecretType) error { + switch st { + case SecretTypeEnvironment, SecretTypeVariable, SecretTypeFile, SecretTypeInternal: + return nil + default: + return fmt.Errorf("secret: invalid enum value for secret_type field: %q", st) + } +} + +// InjectionMode defines the type for the "injection_mode" enum field. +type InjectionMode string + +// InjectionModeAsNeeded is the default value of the InjectionMode enum. +const DefaultInjectionMode = InjectionModeAsNeeded + +// InjectionMode values. +const ( + InjectionModeAlways InjectionMode = "always" + InjectionModeAsNeeded InjectionMode = "as_needed" +) + +func (im InjectionMode) String() string { + return string(im) +} + +// InjectionModeValidator is a validator for the "injection_mode" field enum values. It is called by the builders before save. +func InjectionModeValidator(im InjectionMode) error { + switch im { + case InjectionModeAlways, InjectionModeAsNeeded: + return nil + default: + return fmt.Errorf("secret: invalid enum value for injection_mode field: %q", im) + } +} + +// OrderOption defines the ordering options for the Secret queries. +type OrderOption func(*sql.Selector) + +// ByID orders the results by the id field. +func ByID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldID, opts...).ToFunc() +} + +// ByKey orders the results by the key field. +func ByKey(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldKey, opts...).ToFunc() +} + +// ByEncryptedValue orders the results by the encrypted_value field. +func ByEncryptedValue(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldEncryptedValue, opts...).ToFunc() +} + +// BySecretRef orders the results by the secret_ref field. +func BySecretRef(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldSecretRef, opts...).ToFunc() +} + +// BySecretType orders the results by the secret_type field. +func BySecretType(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldSecretType, opts...).ToFunc() +} + +// ByTarget orders the results by the target field. +func ByTarget(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldTarget, opts...).ToFunc() +} + +// ByScope orders the results by the scope field. +func ByScope(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldScope, opts...).ToFunc() +} + +// ByScopeID orders the results by the scope_id field. +func ByScopeID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldScopeID, opts...).ToFunc() +} + +// ByDescription orders the results by the description field. +func ByDescription(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldDescription, opts...).ToFunc() +} + +// ByInjectionMode orders the results by the injection_mode field. +func ByInjectionMode(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldInjectionMode, opts...).ToFunc() +} + +// ByAllowProgeny orders the results by the allow_progeny field. +func ByAllowProgeny(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldAllowProgeny, opts...).ToFunc() +} + +// ByVersion orders the results by the version field. +func ByVersion(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldVersion, opts...).ToFunc() +} + +// ByCreatedBy orders the results by the created_by field. +func ByCreatedBy(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCreatedBy, opts...).ToFunc() +} + +// ByUpdatedBy orders the results by the updated_by field. +func ByUpdatedBy(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldUpdatedBy, opts...).ToFunc() +} + +// ByCreated orders the results by the created field. +func ByCreated(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCreated, opts...).ToFunc() +} + +// ByUpdated orders the results by the updated field. +func ByUpdated(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldUpdated, opts...).ToFunc() +} diff --git a/pkg/ent/secret/where.go b/pkg/ent/secret/where.go new file mode 100644 index 000000000..6da76db84 --- /dev/null +++ b/pkg/ent/secret/where.go @@ -0,0 +1,941 @@ +// Code generated by ent, DO NOT EDIT. + +package secret + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/google/uuid" +) + +// ID filters vertices based on their ID field. +func ID(id uuid.UUID) predicate.Secret { + return predicate.Secret(sql.FieldEQ(FieldID, id)) +} + +// IDEQ applies the EQ predicate on the ID field. +func IDEQ(id uuid.UUID) predicate.Secret { + return predicate.Secret(sql.FieldEQ(FieldID, id)) +} + +// IDNEQ applies the NEQ predicate on the ID field. +func IDNEQ(id uuid.UUID) predicate.Secret { + return predicate.Secret(sql.FieldNEQ(FieldID, id)) +} + +// IDIn applies the In predicate on the ID field. +func IDIn(ids ...uuid.UUID) predicate.Secret { + return predicate.Secret(sql.FieldIn(FieldID, ids...)) +} + +// IDNotIn applies the NotIn predicate on the ID field. +func IDNotIn(ids ...uuid.UUID) predicate.Secret { + return predicate.Secret(sql.FieldNotIn(FieldID, ids...)) +} + +// IDGT applies the GT predicate on the ID field. +func IDGT(id uuid.UUID) predicate.Secret { + return predicate.Secret(sql.FieldGT(FieldID, id)) +} + +// IDGTE applies the GTE predicate on the ID field. +func IDGTE(id uuid.UUID) predicate.Secret { + return predicate.Secret(sql.FieldGTE(FieldID, id)) +} + +// IDLT applies the LT predicate on the ID field. +func IDLT(id uuid.UUID) predicate.Secret { + return predicate.Secret(sql.FieldLT(FieldID, id)) +} + +// IDLTE applies the LTE predicate on the ID field. +func IDLTE(id uuid.UUID) predicate.Secret { + return predicate.Secret(sql.FieldLTE(FieldID, id)) +} + +// Key applies equality check predicate on the "key" field. It's identical to KeyEQ. +func Key(v string) predicate.Secret { + return predicate.Secret(sql.FieldEQ(FieldKey, v)) +} + +// EncryptedValue applies equality check predicate on the "encrypted_value" field. It's identical to EncryptedValueEQ. +func EncryptedValue(v string) predicate.Secret { + return predicate.Secret(sql.FieldEQ(FieldEncryptedValue, v)) +} + +// SecretRef applies equality check predicate on the "secret_ref" field. It's identical to SecretRefEQ. +func SecretRef(v string) predicate.Secret { + return predicate.Secret(sql.FieldEQ(FieldSecretRef, v)) +} + +// Target applies equality check predicate on the "target" field. It's identical to TargetEQ. +func Target(v string) predicate.Secret { + return predicate.Secret(sql.FieldEQ(FieldTarget, v)) +} + +// Scope applies equality check predicate on the "scope" field. It's identical to ScopeEQ. +func Scope(v string) predicate.Secret { + return predicate.Secret(sql.FieldEQ(FieldScope, v)) +} + +// ScopeID applies equality check predicate on the "scope_id" field. It's identical to ScopeIDEQ. +func ScopeID(v string) predicate.Secret { + return predicate.Secret(sql.FieldEQ(FieldScopeID, v)) +} + +// Description applies equality check predicate on the "description" field. It's identical to DescriptionEQ. +func Description(v string) predicate.Secret { + return predicate.Secret(sql.FieldEQ(FieldDescription, v)) +} + +// AllowProgeny applies equality check predicate on the "allow_progeny" field. It's identical to AllowProgenyEQ. +func AllowProgeny(v bool) predicate.Secret { + return predicate.Secret(sql.FieldEQ(FieldAllowProgeny, v)) +} + +// Version applies equality check predicate on the "version" field. It's identical to VersionEQ. +func Version(v int) predicate.Secret { + return predicate.Secret(sql.FieldEQ(FieldVersion, v)) +} + +// CreatedBy applies equality check predicate on the "created_by" field. It's identical to CreatedByEQ. +func CreatedBy(v string) predicate.Secret { + return predicate.Secret(sql.FieldEQ(FieldCreatedBy, v)) +} + +// UpdatedBy applies equality check predicate on the "updated_by" field. It's identical to UpdatedByEQ. +func UpdatedBy(v string) predicate.Secret { + return predicate.Secret(sql.FieldEQ(FieldUpdatedBy, v)) +} + +// Created applies equality check predicate on the "created" field. It's identical to CreatedEQ. +func Created(v time.Time) predicate.Secret { + return predicate.Secret(sql.FieldEQ(FieldCreated, v)) +} + +// Updated applies equality check predicate on the "updated" field. It's identical to UpdatedEQ. +func Updated(v time.Time) predicate.Secret { + return predicate.Secret(sql.FieldEQ(FieldUpdated, v)) +} + +// KeyEQ applies the EQ predicate on the "key" field. +func KeyEQ(v string) predicate.Secret { + return predicate.Secret(sql.FieldEQ(FieldKey, v)) +} + +// KeyNEQ applies the NEQ predicate on the "key" field. +func KeyNEQ(v string) predicate.Secret { + return predicate.Secret(sql.FieldNEQ(FieldKey, v)) +} + +// KeyIn applies the In predicate on the "key" field. +func KeyIn(vs ...string) predicate.Secret { + return predicate.Secret(sql.FieldIn(FieldKey, vs...)) +} + +// KeyNotIn applies the NotIn predicate on the "key" field. +func KeyNotIn(vs ...string) predicate.Secret { + return predicate.Secret(sql.FieldNotIn(FieldKey, vs...)) +} + +// KeyGT applies the GT predicate on the "key" field. +func KeyGT(v string) predicate.Secret { + return predicate.Secret(sql.FieldGT(FieldKey, v)) +} + +// KeyGTE applies the GTE predicate on the "key" field. +func KeyGTE(v string) predicate.Secret { + return predicate.Secret(sql.FieldGTE(FieldKey, v)) +} + +// KeyLT applies the LT predicate on the "key" field. +func KeyLT(v string) predicate.Secret { + return predicate.Secret(sql.FieldLT(FieldKey, v)) +} + +// KeyLTE applies the LTE predicate on the "key" field. +func KeyLTE(v string) predicate.Secret { + return predicate.Secret(sql.FieldLTE(FieldKey, v)) +} + +// KeyContains applies the Contains predicate on the "key" field. +func KeyContains(v string) predicate.Secret { + return predicate.Secret(sql.FieldContains(FieldKey, v)) +} + +// KeyHasPrefix applies the HasPrefix predicate on the "key" field. +func KeyHasPrefix(v string) predicate.Secret { + return predicate.Secret(sql.FieldHasPrefix(FieldKey, v)) +} + +// KeyHasSuffix applies the HasSuffix predicate on the "key" field. +func KeyHasSuffix(v string) predicate.Secret { + return predicate.Secret(sql.FieldHasSuffix(FieldKey, v)) +} + +// KeyEqualFold applies the EqualFold predicate on the "key" field. +func KeyEqualFold(v string) predicate.Secret { + return predicate.Secret(sql.FieldEqualFold(FieldKey, v)) +} + +// KeyContainsFold applies the ContainsFold predicate on the "key" field. +func KeyContainsFold(v string) predicate.Secret { + return predicate.Secret(sql.FieldContainsFold(FieldKey, v)) +} + +// EncryptedValueEQ applies the EQ predicate on the "encrypted_value" field. +func EncryptedValueEQ(v string) predicate.Secret { + return predicate.Secret(sql.FieldEQ(FieldEncryptedValue, v)) +} + +// EncryptedValueNEQ applies the NEQ predicate on the "encrypted_value" field. +func EncryptedValueNEQ(v string) predicate.Secret { + return predicate.Secret(sql.FieldNEQ(FieldEncryptedValue, v)) +} + +// EncryptedValueIn applies the In predicate on the "encrypted_value" field. +func EncryptedValueIn(vs ...string) predicate.Secret { + return predicate.Secret(sql.FieldIn(FieldEncryptedValue, vs...)) +} + +// EncryptedValueNotIn applies the NotIn predicate on the "encrypted_value" field. +func EncryptedValueNotIn(vs ...string) predicate.Secret { + return predicate.Secret(sql.FieldNotIn(FieldEncryptedValue, vs...)) +} + +// EncryptedValueGT applies the GT predicate on the "encrypted_value" field. +func EncryptedValueGT(v string) predicate.Secret { + return predicate.Secret(sql.FieldGT(FieldEncryptedValue, v)) +} + +// EncryptedValueGTE applies the GTE predicate on the "encrypted_value" field. +func EncryptedValueGTE(v string) predicate.Secret { + return predicate.Secret(sql.FieldGTE(FieldEncryptedValue, v)) +} + +// EncryptedValueLT applies the LT predicate on the "encrypted_value" field. +func EncryptedValueLT(v string) predicate.Secret { + return predicate.Secret(sql.FieldLT(FieldEncryptedValue, v)) +} + +// EncryptedValueLTE applies the LTE predicate on the "encrypted_value" field. +func EncryptedValueLTE(v string) predicate.Secret { + return predicate.Secret(sql.FieldLTE(FieldEncryptedValue, v)) +} + +// EncryptedValueContains applies the Contains predicate on the "encrypted_value" field. +func EncryptedValueContains(v string) predicate.Secret { + return predicate.Secret(sql.FieldContains(FieldEncryptedValue, v)) +} + +// EncryptedValueHasPrefix applies the HasPrefix predicate on the "encrypted_value" field. +func EncryptedValueHasPrefix(v string) predicate.Secret { + return predicate.Secret(sql.FieldHasPrefix(FieldEncryptedValue, v)) +} + +// EncryptedValueHasSuffix applies the HasSuffix predicate on the "encrypted_value" field. +func EncryptedValueHasSuffix(v string) predicate.Secret { + return predicate.Secret(sql.FieldHasSuffix(FieldEncryptedValue, v)) +} + +// EncryptedValueEqualFold applies the EqualFold predicate on the "encrypted_value" field. +func EncryptedValueEqualFold(v string) predicate.Secret { + return predicate.Secret(sql.FieldEqualFold(FieldEncryptedValue, v)) +} + +// EncryptedValueContainsFold applies the ContainsFold predicate on the "encrypted_value" field. +func EncryptedValueContainsFold(v string) predicate.Secret { + return predicate.Secret(sql.FieldContainsFold(FieldEncryptedValue, v)) +} + +// SecretRefEQ applies the EQ predicate on the "secret_ref" field. +func SecretRefEQ(v string) predicate.Secret { + return predicate.Secret(sql.FieldEQ(FieldSecretRef, v)) +} + +// SecretRefNEQ applies the NEQ predicate on the "secret_ref" field. +func SecretRefNEQ(v string) predicate.Secret { + return predicate.Secret(sql.FieldNEQ(FieldSecretRef, v)) +} + +// SecretRefIn applies the In predicate on the "secret_ref" field. +func SecretRefIn(vs ...string) predicate.Secret { + return predicate.Secret(sql.FieldIn(FieldSecretRef, vs...)) +} + +// SecretRefNotIn applies the NotIn predicate on the "secret_ref" field. +func SecretRefNotIn(vs ...string) predicate.Secret { + return predicate.Secret(sql.FieldNotIn(FieldSecretRef, vs...)) +} + +// SecretRefGT applies the GT predicate on the "secret_ref" field. +func SecretRefGT(v string) predicate.Secret { + return predicate.Secret(sql.FieldGT(FieldSecretRef, v)) +} + +// SecretRefGTE applies the GTE predicate on the "secret_ref" field. +func SecretRefGTE(v string) predicate.Secret { + return predicate.Secret(sql.FieldGTE(FieldSecretRef, v)) +} + +// SecretRefLT applies the LT predicate on the "secret_ref" field. +func SecretRefLT(v string) predicate.Secret { + return predicate.Secret(sql.FieldLT(FieldSecretRef, v)) +} + +// SecretRefLTE applies the LTE predicate on the "secret_ref" field. +func SecretRefLTE(v string) predicate.Secret { + return predicate.Secret(sql.FieldLTE(FieldSecretRef, v)) +} + +// SecretRefContains applies the Contains predicate on the "secret_ref" field. +func SecretRefContains(v string) predicate.Secret { + return predicate.Secret(sql.FieldContains(FieldSecretRef, v)) +} + +// SecretRefHasPrefix applies the HasPrefix predicate on the "secret_ref" field. +func SecretRefHasPrefix(v string) predicate.Secret { + return predicate.Secret(sql.FieldHasPrefix(FieldSecretRef, v)) +} + +// SecretRefHasSuffix applies the HasSuffix predicate on the "secret_ref" field. +func SecretRefHasSuffix(v string) predicate.Secret { + return predicate.Secret(sql.FieldHasSuffix(FieldSecretRef, v)) +} + +// SecretRefIsNil applies the IsNil predicate on the "secret_ref" field. +func SecretRefIsNil() predicate.Secret { + return predicate.Secret(sql.FieldIsNull(FieldSecretRef)) +} + +// SecretRefNotNil applies the NotNil predicate on the "secret_ref" field. +func SecretRefNotNil() predicate.Secret { + return predicate.Secret(sql.FieldNotNull(FieldSecretRef)) +} + +// SecretRefEqualFold applies the EqualFold predicate on the "secret_ref" field. +func SecretRefEqualFold(v string) predicate.Secret { + return predicate.Secret(sql.FieldEqualFold(FieldSecretRef, v)) +} + +// SecretRefContainsFold applies the ContainsFold predicate on the "secret_ref" field. +func SecretRefContainsFold(v string) predicate.Secret { + return predicate.Secret(sql.FieldContainsFold(FieldSecretRef, v)) +} + +// SecretTypeEQ applies the EQ predicate on the "secret_type" field. +func SecretTypeEQ(v SecretType) predicate.Secret { + return predicate.Secret(sql.FieldEQ(FieldSecretType, v)) +} + +// SecretTypeNEQ applies the NEQ predicate on the "secret_type" field. +func SecretTypeNEQ(v SecretType) predicate.Secret { + return predicate.Secret(sql.FieldNEQ(FieldSecretType, v)) +} + +// SecretTypeIn applies the In predicate on the "secret_type" field. +func SecretTypeIn(vs ...SecretType) predicate.Secret { + return predicate.Secret(sql.FieldIn(FieldSecretType, vs...)) +} + +// SecretTypeNotIn applies the NotIn predicate on the "secret_type" field. +func SecretTypeNotIn(vs ...SecretType) predicate.Secret { + return predicate.Secret(sql.FieldNotIn(FieldSecretType, vs...)) +} + +// TargetEQ applies the EQ predicate on the "target" field. +func TargetEQ(v string) predicate.Secret { + return predicate.Secret(sql.FieldEQ(FieldTarget, v)) +} + +// TargetNEQ applies the NEQ predicate on the "target" field. +func TargetNEQ(v string) predicate.Secret { + return predicate.Secret(sql.FieldNEQ(FieldTarget, v)) +} + +// TargetIn applies the In predicate on the "target" field. +func TargetIn(vs ...string) predicate.Secret { + return predicate.Secret(sql.FieldIn(FieldTarget, vs...)) +} + +// TargetNotIn applies the NotIn predicate on the "target" field. +func TargetNotIn(vs ...string) predicate.Secret { + return predicate.Secret(sql.FieldNotIn(FieldTarget, vs...)) +} + +// TargetGT applies the GT predicate on the "target" field. +func TargetGT(v string) predicate.Secret { + return predicate.Secret(sql.FieldGT(FieldTarget, v)) +} + +// TargetGTE applies the GTE predicate on the "target" field. +func TargetGTE(v string) predicate.Secret { + return predicate.Secret(sql.FieldGTE(FieldTarget, v)) +} + +// TargetLT applies the LT predicate on the "target" field. +func TargetLT(v string) predicate.Secret { + return predicate.Secret(sql.FieldLT(FieldTarget, v)) +} + +// TargetLTE applies the LTE predicate on the "target" field. +func TargetLTE(v string) predicate.Secret { + return predicate.Secret(sql.FieldLTE(FieldTarget, v)) +} + +// TargetContains applies the Contains predicate on the "target" field. +func TargetContains(v string) predicate.Secret { + return predicate.Secret(sql.FieldContains(FieldTarget, v)) +} + +// TargetHasPrefix applies the HasPrefix predicate on the "target" field. +func TargetHasPrefix(v string) predicate.Secret { + return predicate.Secret(sql.FieldHasPrefix(FieldTarget, v)) +} + +// TargetHasSuffix applies the HasSuffix predicate on the "target" field. +func TargetHasSuffix(v string) predicate.Secret { + return predicate.Secret(sql.FieldHasSuffix(FieldTarget, v)) +} + +// TargetIsNil applies the IsNil predicate on the "target" field. +func TargetIsNil() predicate.Secret { + return predicate.Secret(sql.FieldIsNull(FieldTarget)) +} + +// TargetNotNil applies the NotNil predicate on the "target" field. +func TargetNotNil() predicate.Secret { + return predicate.Secret(sql.FieldNotNull(FieldTarget)) +} + +// TargetEqualFold applies the EqualFold predicate on the "target" field. +func TargetEqualFold(v string) predicate.Secret { + return predicate.Secret(sql.FieldEqualFold(FieldTarget, v)) +} + +// TargetContainsFold applies the ContainsFold predicate on the "target" field. +func TargetContainsFold(v string) predicate.Secret { + return predicate.Secret(sql.FieldContainsFold(FieldTarget, v)) +} + +// ScopeEQ applies the EQ predicate on the "scope" field. +func ScopeEQ(v string) predicate.Secret { + return predicate.Secret(sql.FieldEQ(FieldScope, v)) +} + +// ScopeNEQ applies the NEQ predicate on the "scope" field. +func ScopeNEQ(v string) predicate.Secret { + return predicate.Secret(sql.FieldNEQ(FieldScope, v)) +} + +// ScopeIn applies the In predicate on the "scope" field. +func ScopeIn(vs ...string) predicate.Secret { + return predicate.Secret(sql.FieldIn(FieldScope, vs...)) +} + +// ScopeNotIn applies the NotIn predicate on the "scope" field. +func ScopeNotIn(vs ...string) predicate.Secret { + return predicate.Secret(sql.FieldNotIn(FieldScope, vs...)) +} + +// ScopeGT applies the GT predicate on the "scope" field. +func ScopeGT(v string) predicate.Secret { + return predicate.Secret(sql.FieldGT(FieldScope, v)) +} + +// ScopeGTE applies the GTE predicate on the "scope" field. +func ScopeGTE(v string) predicate.Secret { + return predicate.Secret(sql.FieldGTE(FieldScope, v)) +} + +// ScopeLT applies the LT predicate on the "scope" field. +func ScopeLT(v string) predicate.Secret { + return predicate.Secret(sql.FieldLT(FieldScope, v)) +} + +// ScopeLTE applies the LTE predicate on the "scope" field. +func ScopeLTE(v string) predicate.Secret { + return predicate.Secret(sql.FieldLTE(FieldScope, v)) +} + +// ScopeContains applies the Contains predicate on the "scope" field. +func ScopeContains(v string) predicate.Secret { + return predicate.Secret(sql.FieldContains(FieldScope, v)) +} + +// ScopeHasPrefix applies the HasPrefix predicate on the "scope" field. +func ScopeHasPrefix(v string) predicate.Secret { + return predicate.Secret(sql.FieldHasPrefix(FieldScope, v)) +} + +// ScopeHasSuffix applies the HasSuffix predicate on the "scope" field. +func ScopeHasSuffix(v string) predicate.Secret { + return predicate.Secret(sql.FieldHasSuffix(FieldScope, v)) +} + +// ScopeEqualFold applies the EqualFold predicate on the "scope" field. +func ScopeEqualFold(v string) predicate.Secret { + return predicate.Secret(sql.FieldEqualFold(FieldScope, v)) +} + +// ScopeContainsFold applies the ContainsFold predicate on the "scope" field. +func ScopeContainsFold(v string) predicate.Secret { + return predicate.Secret(sql.FieldContainsFold(FieldScope, v)) +} + +// ScopeIDEQ applies the EQ predicate on the "scope_id" field. +func ScopeIDEQ(v string) predicate.Secret { + return predicate.Secret(sql.FieldEQ(FieldScopeID, v)) +} + +// ScopeIDNEQ applies the NEQ predicate on the "scope_id" field. +func ScopeIDNEQ(v string) predicate.Secret { + return predicate.Secret(sql.FieldNEQ(FieldScopeID, v)) +} + +// ScopeIDIn applies the In predicate on the "scope_id" field. +func ScopeIDIn(vs ...string) predicate.Secret { + return predicate.Secret(sql.FieldIn(FieldScopeID, vs...)) +} + +// ScopeIDNotIn applies the NotIn predicate on the "scope_id" field. +func ScopeIDNotIn(vs ...string) predicate.Secret { + return predicate.Secret(sql.FieldNotIn(FieldScopeID, vs...)) +} + +// ScopeIDGT applies the GT predicate on the "scope_id" field. +func ScopeIDGT(v string) predicate.Secret { + return predicate.Secret(sql.FieldGT(FieldScopeID, v)) +} + +// ScopeIDGTE applies the GTE predicate on the "scope_id" field. +func ScopeIDGTE(v string) predicate.Secret { + return predicate.Secret(sql.FieldGTE(FieldScopeID, v)) +} + +// ScopeIDLT applies the LT predicate on the "scope_id" field. +func ScopeIDLT(v string) predicate.Secret { + return predicate.Secret(sql.FieldLT(FieldScopeID, v)) +} + +// ScopeIDLTE applies the LTE predicate on the "scope_id" field. +func ScopeIDLTE(v string) predicate.Secret { + return predicate.Secret(sql.FieldLTE(FieldScopeID, v)) +} + +// ScopeIDContains applies the Contains predicate on the "scope_id" field. +func ScopeIDContains(v string) predicate.Secret { + return predicate.Secret(sql.FieldContains(FieldScopeID, v)) +} + +// ScopeIDHasPrefix applies the HasPrefix predicate on the "scope_id" field. +func ScopeIDHasPrefix(v string) predicate.Secret { + return predicate.Secret(sql.FieldHasPrefix(FieldScopeID, v)) +} + +// ScopeIDHasSuffix applies the HasSuffix predicate on the "scope_id" field. +func ScopeIDHasSuffix(v string) predicate.Secret { + return predicate.Secret(sql.FieldHasSuffix(FieldScopeID, v)) +} + +// ScopeIDEqualFold applies the EqualFold predicate on the "scope_id" field. +func ScopeIDEqualFold(v string) predicate.Secret { + return predicate.Secret(sql.FieldEqualFold(FieldScopeID, v)) +} + +// ScopeIDContainsFold applies the ContainsFold predicate on the "scope_id" field. +func ScopeIDContainsFold(v string) predicate.Secret { + return predicate.Secret(sql.FieldContainsFold(FieldScopeID, v)) +} + +// DescriptionEQ applies the EQ predicate on the "description" field. +func DescriptionEQ(v string) predicate.Secret { + return predicate.Secret(sql.FieldEQ(FieldDescription, v)) +} + +// DescriptionNEQ applies the NEQ predicate on the "description" field. +func DescriptionNEQ(v string) predicate.Secret { + return predicate.Secret(sql.FieldNEQ(FieldDescription, v)) +} + +// DescriptionIn applies the In predicate on the "description" field. +func DescriptionIn(vs ...string) predicate.Secret { + return predicate.Secret(sql.FieldIn(FieldDescription, vs...)) +} + +// DescriptionNotIn applies the NotIn predicate on the "description" field. +func DescriptionNotIn(vs ...string) predicate.Secret { + return predicate.Secret(sql.FieldNotIn(FieldDescription, vs...)) +} + +// DescriptionGT applies the GT predicate on the "description" field. +func DescriptionGT(v string) predicate.Secret { + return predicate.Secret(sql.FieldGT(FieldDescription, v)) +} + +// DescriptionGTE applies the GTE predicate on the "description" field. +func DescriptionGTE(v string) predicate.Secret { + return predicate.Secret(sql.FieldGTE(FieldDescription, v)) +} + +// DescriptionLT applies the LT predicate on the "description" field. +func DescriptionLT(v string) predicate.Secret { + return predicate.Secret(sql.FieldLT(FieldDescription, v)) +} + +// DescriptionLTE applies the LTE predicate on the "description" field. +func DescriptionLTE(v string) predicate.Secret { + return predicate.Secret(sql.FieldLTE(FieldDescription, v)) +} + +// DescriptionContains applies the Contains predicate on the "description" field. +func DescriptionContains(v string) predicate.Secret { + return predicate.Secret(sql.FieldContains(FieldDescription, v)) +} + +// DescriptionHasPrefix applies the HasPrefix predicate on the "description" field. +func DescriptionHasPrefix(v string) predicate.Secret { + return predicate.Secret(sql.FieldHasPrefix(FieldDescription, v)) +} + +// DescriptionHasSuffix applies the HasSuffix predicate on the "description" field. +func DescriptionHasSuffix(v string) predicate.Secret { + return predicate.Secret(sql.FieldHasSuffix(FieldDescription, v)) +} + +// DescriptionIsNil applies the IsNil predicate on the "description" field. +func DescriptionIsNil() predicate.Secret { + return predicate.Secret(sql.FieldIsNull(FieldDescription)) +} + +// DescriptionNotNil applies the NotNil predicate on the "description" field. +func DescriptionNotNil() predicate.Secret { + return predicate.Secret(sql.FieldNotNull(FieldDescription)) +} + +// DescriptionEqualFold applies the EqualFold predicate on the "description" field. +func DescriptionEqualFold(v string) predicate.Secret { + return predicate.Secret(sql.FieldEqualFold(FieldDescription, v)) +} + +// DescriptionContainsFold applies the ContainsFold predicate on the "description" field. +func DescriptionContainsFold(v string) predicate.Secret { + return predicate.Secret(sql.FieldContainsFold(FieldDescription, v)) +} + +// InjectionModeEQ applies the EQ predicate on the "injection_mode" field. +func InjectionModeEQ(v InjectionMode) predicate.Secret { + return predicate.Secret(sql.FieldEQ(FieldInjectionMode, v)) +} + +// InjectionModeNEQ applies the NEQ predicate on the "injection_mode" field. +func InjectionModeNEQ(v InjectionMode) predicate.Secret { + return predicate.Secret(sql.FieldNEQ(FieldInjectionMode, v)) +} + +// InjectionModeIn applies the In predicate on the "injection_mode" field. +func InjectionModeIn(vs ...InjectionMode) predicate.Secret { + return predicate.Secret(sql.FieldIn(FieldInjectionMode, vs...)) +} + +// InjectionModeNotIn applies the NotIn predicate on the "injection_mode" field. +func InjectionModeNotIn(vs ...InjectionMode) predicate.Secret { + return predicate.Secret(sql.FieldNotIn(FieldInjectionMode, vs...)) +} + +// AllowProgenyEQ applies the EQ predicate on the "allow_progeny" field. +func AllowProgenyEQ(v bool) predicate.Secret { + return predicate.Secret(sql.FieldEQ(FieldAllowProgeny, v)) +} + +// AllowProgenyNEQ applies the NEQ predicate on the "allow_progeny" field. +func AllowProgenyNEQ(v bool) predicate.Secret { + return predicate.Secret(sql.FieldNEQ(FieldAllowProgeny, v)) +} + +// VersionEQ applies the EQ predicate on the "version" field. +func VersionEQ(v int) predicate.Secret { + return predicate.Secret(sql.FieldEQ(FieldVersion, v)) +} + +// VersionNEQ applies the NEQ predicate on the "version" field. +func VersionNEQ(v int) predicate.Secret { + return predicate.Secret(sql.FieldNEQ(FieldVersion, v)) +} + +// VersionIn applies the In predicate on the "version" field. +func VersionIn(vs ...int) predicate.Secret { + return predicate.Secret(sql.FieldIn(FieldVersion, vs...)) +} + +// VersionNotIn applies the NotIn predicate on the "version" field. +func VersionNotIn(vs ...int) predicate.Secret { + return predicate.Secret(sql.FieldNotIn(FieldVersion, vs...)) +} + +// VersionGT applies the GT predicate on the "version" field. +func VersionGT(v int) predicate.Secret { + return predicate.Secret(sql.FieldGT(FieldVersion, v)) +} + +// VersionGTE applies the GTE predicate on the "version" field. +func VersionGTE(v int) predicate.Secret { + return predicate.Secret(sql.FieldGTE(FieldVersion, v)) +} + +// VersionLT applies the LT predicate on the "version" field. +func VersionLT(v int) predicate.Secret { + return predicate.Secret(sql.FieldLT(FieldVersion, v)) +} + +// VersionLTE applies the LTE predicate on the "version" field. +func VersionLTE(v int) predicate.Secret { + return predicate.Secret(sql.FieldLTE(FieldVersion, v)) +} + +// CreatedByEQ applies the EQ predicate on the "created_by" field. +func CreatedByEQ(v string) predicate.Secret { + return predicate.Secret(sql.FieldEQ(FieldCreatedBy, v)) +} + +// CreatedByNEQ applies the NEQ predicate on the "created_by" field. +func CreatedByNEQ(v string) predicate.Secret { + return predicate.Secret(sql.FieldNEQ(FieldCreatedBy, v)) +} + +// CreatedByIn applies the In predicate on the "created_by" field. +func CreatedByIn(vs ...string) predicate.Secret { + return predicate.Secret(sql.FieldIn(FieldCreatedBy, vs...)) +} + +// CreatedByNotIn applies the NotIn predicate on the "created_by" field. +func CreatedByNotIn(vs ...string) predicate.Secret { + return predicate.Secret(sql.FieldNotIn(FieldCreatedBy, vs...)) +} + +// CreatedByGT applies the GT predicate on the "created_by" field. +func CreatedByGT(v string) predicate.Secret { + return predicate.Secret(sql.FieldGT(FieldCreatedBy, v)) +} + +// CreatedByGTE applies the GTE predicate on the "created_by" field. +func CreatedByGTE(v string) predicate.Secret { + return predicate.Secret(sql.FieldGTE(FieldCreatedBy, v)) +} + +// CreatedByLT applies the LT predicate on the "created_by" field. +func CreatedByLT(v string) predicate.Secret { + return predicate.Secret(sql.FieldLT(FieldCreatedBy, v)) +} + +// CreatedByLTE applies the LTE predicate on the "created_by" field. +func CreatedByLTE(v string) predicate.Secret { + return predicate.Secret(sql.FieldLTE(FieldCreatedBy, v)) +} + +// CreatedByContains applies the Contains predicate on the "created_by" field. +func CreatedByContains(v string) predicate.Secret { + return predicate.Secret(sql.FieldContains(FieldCreatedBy, v)) +} + +// CreatedByHasPrefix applies the HasPrefix predicate on the "created_by" field. +func CreatedByHasPrefix(v string) predicate.Secret { + return predicate.Secret(sql.FieldHasPrefix(FieldCreatedBy, v)) +} + +// CreatedByHasSuffix applies the HasSuffix predicate on the "created_by" field. +func CreatedByHasSuffix(v string) predicate.Secret { + return predicate.Secret(sql.FieldHasSuffix(FieldCreatedBy, v)) +} + +// CreatedByIsNil applies the IsNil predicate on the "created_by" field. +func CreatedByIsNil() predicate.Secret { + return predicate.Secret(sql.FieldIsNull(FieldCreatedBy)) +} + +// CreatedByNotNil applies the NotNil predicate on the "created_by" field. +func CreatedByNotNil() predicate.Secret { + return predicate.Secret(sql.FieldNotNull(FieldCreatedBy)) +} + +// CreatedByEqualFold applies the EqualFold predicate on the "created_by" field. +func CreatedByEqualFold(v string) predicate.Secret { + return predicate.Secret(sql.FieldEqualFold(FieldCreatedBy, v)) +} + +// CreatedByContainsFold applies the ContainsFold predicate on the "created_by" field. +func CreatedByContainsFold(v string) predicate.Secret { + return predicate.Secret(sql.FieldContainsFold(FieldCreatedBy, v)) +} + +// UpdatedByEQ applies the EQ predicate on the "updated_by" field. +func UpdatedByEQ(v string) predicate.Secret { + return predicate.Secret(sql.FieldEQ(FieldUpdatedBy, v)) +} + +// UpdatedByNEQ applies the NEQ predicate on the "updated_by" field. +func UpdatedByNEQ(v string) predicate.Secret { + return predicate.Secret(sql.FieldNEQ(FieldUpdatedBy, v)) +} + +// UpdatedByIn applies the In predicate on the "updated_by" field. +func UpdatedByIn(vs ...string) predicate.Secret { + return predicate.Secret(sql.FieldIn(FieldUpdatedBy, vs...)) +} + +// UpdatedByNotIn applies the NotIn predicate on the "updated_by" field. +func UpdatedByNotIn(vs ...string) predicate.Secret { + return predicate.Secret(sql.FieldNotIn(FieldUpdatedBy, vs...)) +} + +// UpdatedByGT applies the GT predicate on the "updated_by" field. +func UpdatedByGT(v string) predicate.Secret { + return predicate.Secret(sql.FieldGT(FieldUpdatedBy, v)) +} + +// UpdatedByGTE applies the GTE predicate on the "updated_by" field. +func UpdatedByGTE(v string) predicate.Secret { + return predicate.Secret(sql.FieldGTE(FieldUpdatedBy, v)) +} + +// UpdatedByLT applies the LT predicate on the "updated_by" field. +func UpdatedByLT(v string) predicate.Secret { + return predicate.Secret(sql.FieldLT(FieldUpdatedBy, v)) +} + +// UpdatedByLTE applies the LTE predicate on the "updated_by" field. +func UpdatedByLTE(v string) predicate.Secret { + return predicate.Secret(sql.FieldLTE(FieldUpdatedBy, v)) +} + +// UpdatedByContains applies the Contains predicate on the "updated_by" field. +func UpdatedByContains(v string) predicate.Secret { + return predicate.Secret(sql.FieldContains(FieldUpdatedBy, v)) +} + +// UpdatedByHasPrefix applies the HasPrefix predicate on the "updated_by" field. +func UpdatedByHasPrefix(v string) predicate.Secret { + return predicate.Secret(sql.FieldHasPrefix(FieldUpdatedBy, v)) +} + +// UpdatedByHasSuffix applies the HasSuffix predicate on the "updated_by" field. +func UpdatedByHasSuffix(v string) predicate.Secret { + return predicate.Secret(sql.FieldHasSuffix(FieldUpdatedBy, v)) +} + +// UpdatedByIsNil applies the IsNil predicate on the "updated_by" field. +func UpdatedByIsNil() predicate.Secret { + return predicate.Secret(sql.FieldIsNull(FieldUpdatedBy)) +} + +// UpdatedByNotNil applies the NotNil predicate on the "updated_by" field. +func UpdatedByNotNil() predicate.Secret { + return predicate.Secret(sql.FieldNotNull(FieldUpdatedBy)) +} + +// UpdatedByEqualFold applies the EqualFold predicate on the "updated_by" field. +func UpdatedByEqualFold(v string) predicate.Secret { + return predicate.Secret(sql.FieldEqualFold(FieldUpdatedBy, v)) +} + +// UpdatedByContainsFold applies the ContainsFold predicate on the "updated_by" field. +func UpdatedByContainsFold(v string) predicate.Secret { + return predicate.Secret(sql.FieldContainsFold(FieldUpdatedBy, v)) +} + +// CreatedEQ applies the EQ predicate on the "created" field. +func CreatedEQ(v time.Time) predicate.Secret { + return predicate.Secret(sql.FieldEQ(FieldCreated, v)) +} + +// CreatedNEQ applies the NEQ predicate on the "created" field. +func CreatedNEQ(v time.Time) predicate.Secret { + return predicate.Secret(sql.FieldNEQ(FieldCreated, v)) +} + +// CreatedIn applies the In predicate on the "created" field. +func CreatedIn(vs ...time.Time) predicate.Secret { + return predicate.Secret(sql.FieldIn(FieldCreated, vs...)) +} + +// CreatedNotIn applies the NotIn predicate on the "created" field. +func CreatedNotIn(vs ...time.Time) predicate.Secret { + return predicate.Secret(sql.FieldNotIn(FieldCreated, vs...)) +} + +// CreatedGT applies the GT predicate on the "created" field. +func CreatedGT(v time.Time) predicate.Secret { + return predicate.Secret(sql.FieldGT(FieldCreated, v)) +} + +// CreatedGTE applies the GTE predicate on the "created" field. +func CreatedGTE(v time.Time) predicate.Secret { + return predicate.Secret(sql.FieldGTE(FieldCreated, v)) +} + +// CreatedLT applies the LT predicate on the "created" field. +func CreatedLT(v time.Time) predicate.Secret { + return predicate.Secret(sql.FieldLT(FieldCreated, v)) +} + +// CreatedLTE applies the LTE predicate on the "created" field. +func CreatedLTE(v time.Time) predicate.Secret { + return predicate.Secret(sql.FieldLTE(FieldCreated, v)) +} + +// UpdatedEQ applies the EQ predicate on the "updated" field. +func UpdatedEQ(v time.Time) predicate.Secret { + return predicate.Secret(sql.FieldEQ(FieldUpdated, v)) +} + +// UpdatedNEQ applies the NEQ predicate on the "updated" field. +func UpdatedNEQ(v time.Time) predicate.Secret { + return predicate.Secret(sql.FieldNEQ(FieldUpdated, v)) +} + +// UpdatedIn applies the In predicate on the "updated" field. +func UpdatedIn(vs ...time.Time) predicate.Secret { + return predicate.Secret(sql.FieldIn(FieldUpdated, vs...)) +} + +// UpdatedNotIn applies the NotIn predicate on the "updated" field. +func UpdatedNotIn(vs ...time.Time) predicate.Secret { + return predicate.Secret(sql.FieldNotIn(FieldUpdated, vs...)) +} + +// UpdatedGT applies the GT predicate on the "updated" field. +func UpdatedGT(v time.Time) predicate.Secret { + return predicate.Secret(sql.FieldGT(FieldUpdated, v)) +} + +// UpdatedGTE applies the GTE predicate on the "updated" field. +func UpdatedGTE(v time.Time) predicate.Secret { + return predicate.Secret(sql.FieldGTE(FieldUpdated, v)) +} + +// UpdatedLT applies the LT predicate on the "updated" field. +func UpdatedLT(v time.Time) predicate.Secret { + return predicate.Secret(sql.FieldLT(FieldUpdated, v)) +} + +// UpdatedLTE applies the LTE predicate on the "updated" field. +func UpdatedLTE(v time.Time) predicate.Secret { + return predicate.Secret(sql.FieldLTE(FieldUpdated, v)) +} + +// And groups predicates with the AND operator between them. +func And(predicates ...predicate.Secret) predicate.Secret { + return predicate.Secret(sql.AndPredicates(predicates...)) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.Secret) predicate.Secret { + return predicate.Secret(sql.OrPredicates(predicates...)) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.Secret) predicate.Secret { + return predicate.Secret(sql.NotPredicates(p)) +} diff --git a/pkg/ent/secret_create.go b/pkg/ent/secret_create.go new file mode 100644 index 000000000..01ff9ccd7 --- /dev/null +++ b/pkg/ent/secret_create.go @@ -0,0 +1,1454 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/secret" + "github.com/google/uuid" +) + +// SecretCreate is the builder for creating a Secret entity. +type SecretCreate struct { + config + mutation *SecretMutation + hooks []Hook + conflict []sql.ConflictOption +} + +// SetKey sets the "key" field. +func (_c *SecretCreate) SetKey(v string) *SecretCreate { + _c.mutation.SetKey(v) + return _c +} + +// SetEncryptedValue sets the "encrypted_value" field. +func (_c *SecretCreate) SetEncryptedValue(v string) *SecretCreate { + _c.mutation.SetEncryptedValue(v) + return _c +} + +// SetSecretRef sets the "secret_ref" field. +func (_c *SecretCreate) SetSecretRef(v string) *SecretCreate { + _c.mutation.SetSecretRef(v) + return _c +} + +// SetNillableSecretRef sets the "secret_ref" field if the given value is not nil. +func (_c *SecretCreate) SetNillableSecretRef(v *string) *SecretCreate { + if v != nil { + _c.SetSecretRef(*v) + } + return _c +} + +// SetSecretType sets the "secret_type" field. +func (_c *SecretCreate) SetSecretType(v secret.SecretType) *SecretCreate { + _c.mutation.SetSecretType(v) + return _c +} + +// SetNillableSecretType sets the "secret_type" field if the given value is not nil. +func (_c *SecretCreate) SetNillableSecretType(v *secret.SecretType) *SecretCreate { + if v != nil { + _c.SetSecretType(*v) + } + return _c +} + +// SetTarget sets the "target" field. +func (_c *SecretCreate) SetTarget(v string) *SecretCreate { + _c.mutation.SetTarget(v) + return _c +} + +// SetNillableTarget sets the "target" field if the given value is not nil. +func (_c *SecretCreate) SetNillableTarget(v *string) *SecretCreate { + if v != nil { + _c.SetTarget(*v) + } + return _c +} + +// SetScope sets the "scope" field. +func (_c *SecretCreate) SetScope(v string) *SecretCreate { + _c.mutation.SetScope(v) + return _c +} + +// SetScopeID sets the "scope_id" field. +func (_c *SecretCreate) SetScopeID(v string) *SecretCreate { + _c.mutation.SetScopeID(v) + return _c +} + +// SetDescription sets the "description" field. +func (_c *SecretCreate) SetDescription(v string) *SecretCreate { + _c.mutation.SetDescription(v) + return _c +} + +// SetNillableDescription sets the "description" field if the given value is not nil. +func (_c *SecretCreate) SetNillableDescription(v *string) *SecretCreate { + if v != nil { + _c.SetDescription(*v) + } + return _c +} + +// SetInjectionMode sets the "injection_mode" field. +func (_c *SecretCreate) SetInjectionMode(v secret.InjectionMode) *SecretCreate { + _c.mutation.SetInjectionMode(v) + return _c +} + +// SetNillableInjectionMode sets the "injection_mode" field if the given value is not nil. +func (_c *SecretCreate) SetNillableInjectionMode(v *secret.InjectionMode) *SecretCreate { + if v != nil { + _c.SetInjectionMode(*v) + } + return _c +} + +// SetAllowProgeny sets the "allow_progeny" field. +func (_c *SecretCreate) SetAllowProgeny(v bool) *SecretCreate { + _c.mutation.SetAllowProgeny(v) + return _c +} + +// SetNillableAllowProgeny sets the "allow_progeny" field if the given value is not nil. +func (_c *SecretCreate) SetNillableAllowProgeny(v *bool) *SecretCreate { + if v != nil { + _c.SetAllowProgeny(*v) + } + return _c +} + +// SetVersion sets the "version" field. +func (_c *SecretCreate) SetVersion(v int) *SecretCreate { + _c.mutation.SetVersion(v) + return _c +} + +// SetNillableVersion sets the "version" field if the given value is not nil. +func (_c *SecretCreate) SetNillableVersion(v *int) *SecretCreate { + if v != nil { + _c.SetVersion(*v) + } + return _c +} + +// SetCreatedBy sets the "created_by" field. +func (_c *SecretCreate) SetCreatedBy(v string) *SecretCreate { + _c.mutation.SetCreatedBy(v) + return _c +} + +// SetNillableCreatedBy sets the "created_by" field if the given value is not nil. +func (_c *SecretCreate) SetNillableCreatedBy(v *string) *SecretCreate { + if v != nil { + _c.SetCreatedBy(*v) + } + return _c +} + +// SetUpdatedBy sets the "updated_by" field. +func (_c *SecretCreate) SetUpdatedBy(v string) *SecretCreate { + _c.mutation.SetUpdatedBy(v) + return _c +} + +// SetNillableUpdatedBy sets the "updated_by" field if the given value is not nil. +func (_c *SecretCreate) SetNillableUpdatedBy(v *string) *SecretCreate { + if v != nil { + _c.SetUpdatedBy(*v) + } + return _c +} + +// SetCreated sets the "created" field. +func (_c *SecretCreate) SetCreated(v time.Time) *SecretCreate { + _c.mutation.SetCreated(v) + return _c +} + +// SetNillableCreated sets the "created" field if the given value is not nil. +func (_c *SecretCreate) SetNillableCreated(v *time.Time) *SecretCreate { + if v != nil { + _c.SetCreated(*v) + } + return _c +} + +// SetUpdated sets the "updated" field. +func (_c *SecretCreate) SetUpdated(v time.Time) *SecretCreate { + _c.mutation.SetUpdated(v) + return _c +} + +// SetNillableUpdated sets the "updated" field if the given value is not nil. +func (_c *SecretCreate) SetNillableUpdated(v *time.Time) *SecretCreate { + if v != nil { + _c.SetUpdated(*v) + } + return _c +} + +// SetID sets the "id" field. +func (_c *SecretCreate) SetID(v uuid.UUID) *SecretCreate { + _c.mutation.SetID(v) + return _c +} + +// SetNillableID sets the "id" field if the given value is not nil. +func (_c *SecretCreate) SetNillableID(v *uuid.UUID) *SecretCreate { + if v != nil { + _c.SetID(*v) + } + return _c +} + +// Mutation returns the SecretMutation object of the builder. +func (_c *SecretCreate) Mutation() *SecretMutation { + return _c.mutation +} + +// Save creates the Secret in the database. +func (_c *SecretCreate) Save(ctx context.Context) (*Secret, error) { + _c.defaults() + return withHooks(ctx, _c.sqlSave, _c.mutation, _c.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (_c *SecretCreate) SaveX(ctx context.Context) *Secret { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *SecretCreate) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *SecretCreate) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_c *SecretCreate) defaults() { + if _, ok := _c.mutation.SecretType(); !ok { + v := secret.DefaultSecretType + _c.mutation.SetSecretType(v) + } + if _, ok := _c.mutation.InjectionMode(); !ok { + v := secret.DefaultInjectionMode + _c.mutation.SetInjectionMode(v) + } + if _, ok := _c.mutation.AllowProgeny(); !ok { + v := secret.DefaultAllowProgeny + _c.mutation.SetAllowProgeny(v) + } + if _, ok := _c.mutation.Version(); !ok { + v := secret.DefaultVersion + _c.mutation.SetVersion(v) + } + if _, ok := _c.mutation.Created(); !ok { + v := secret.DefaultCreated() + _c.mutation.SetCreated(v) + } + if _, ok := _c.mutation.Updated(); !ok { + v := secret.DefaultUpdated() + _c.mutation.SetUpdated(v) + } + if _, ok := _c.mutation.ID(); !ok { + v := secret.DefaultID() + _c.mutation.SetID(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_c *SecretCreate) check() error { + if _, ok := _c.mutation.Key(); !ok { + return &ValidationError{Name: "key", err: errors.New(`ent: missing required field "Secret.key"`)} + } + if v, ok := _c.mutation.Key(); ok { + if err := secret.KeyValidator(v); err != nil { + return &ValidationError{Name: "key", err: fmt.Errorf(`ent: validator failed for field "Secret.key": %w`, err)} + } + } + if _, ok := _c.mutation.EncryptedValue(); !ok { + return &ValidationError{Name: "encrypted_value", err: errors.New(`ent: missing required field "Secret.encrypted_value"`)} + } + if _, ok := _c.mutation.SecretType(); !ok { + return &ValidationError{Name: "secret_type", err: errors.New(`ent: missing required field "Secret.secret_type"`)} + } + if v, ok := _c.mutation.SecretType(); ok { + if err := secret.SecretTypeValidator(v); err != nil { + return &ValidationError{Name: "secret_type", err: fmt.Errorf(`ent: validator failed for field "Secret.secret_type": %w`, err)} + } + } + if _, ok := _c.mutation.Scope(); !ok { + return &ValidationError{Name: "scope", err: errors.New(`ent: missing required field "Secret.scope"`)} + } + if v, ok := _c.mutation.Scope(); ok { + if err := secret.ScopeValidator(v); err != nil { + return &ValidationError{Name: "scope", err: fmt.Errorf(`ent: validator failed for field "Secret.scope": %w`, err)} + } + } + if _, ok := _c.mutation.ScopeID(); !ok { + return &ValidationError{Name: "scope_id", err: errors.New(`ent: missing required field "Secret.scope_id"`)} + } + if _, ok := _c.mutation.InjectionMode(); !ok { + return &ValidationError{Name: "injection_mode", err: errors.New(`ent: missing required field "Secret.injection_mode"`)} + } + if v, ok := _c.mutation.InjectionMode(); ok { + if err := secret.InjectionModeValidator(v); err != nil { + return &ValidationError{Name: "injection_mode", err: fmt.Errorf(`ent: validator failed for field "Secret.injection_mode": %w`, err)} + } + } + if _, ok := _c.mutation.AllowProgeny(); !ok { + return &ValidationError{Name: "allow_progeny", err: errors.New(`ent: missing required field "Secret.allow_progeny"`)} + } + if _, ok := _c.mutation.Version(); !ok { + return &ValidationError{Name: "version", err: errors.New(`ent: missing required field "Secret.version"`)} + } + if _, ok := _c.mutation.Created(); !ok { + return &ValidationError{Name: "created", err: errors.New(`ent: missing required field "Secret.created"`)} + } + if _, ok := _c.mutation.Updated(); !ok { + return &ValidationError{Name: "updated", err: errors.New(`ent: missing required field "Secret.updated"`)} + } + return nil +} + +func (_c *SecretCreate) sqlSave(ctx context.Context) (*Secret, error) { + if err := _c.check(); err != nil { + return nil, err + } + _node, _spec := _c.createSpec() + if err := sqlgraph.CreateNode(ctx, _c.driver, _spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + if _spec.ID.Value != nil { + if id, ok := _spec.ID.Value.(*uuid.UUID); ok { + _node.ID = *id + } else if err := _node.ID.Scan(_spec.ID.Value); err != nil { + return nil, err + } + } + _c.mutation.id = &_node.ID + _c.mutation.done = true + return _node, nil +} + +func (_c *SecretCreate) createSpec() (*Secret, *sqlgraph.CreateSpec) { + var ( + _node = &Secret{config: _c.config} + _spec = sqlgraph.NewCreateSpec(secret.Table, sqlgraph.NewFieldSpec(secret.FieldID, field.TypeUUID)) + ) + _spec.OnConflict = _c.conflict + if id, ok := _c.mutation.ID(); ok { + _node.ID = id + _spec.ID.Value = &id + } + if value, ok := _c.mutation.Key(); ok { + _spec.SetField(secret.FieldKey, field.TypeString, value) + _node.Key = value + } + if value, ok := _c.mutation.EncryptedValue(); ok { + _spec.SetField(secret.FieldEncryptedValue, field.TypeString, value) + _node.EncryptedValue = value + } + if value, ok := _c.mutation.SecretRef(); ok { + _spec.SetField(secret.FieldSecretRef, field.TypeString, value) + _node.SecretRef = value + } + if value, ok := _c.mutation.SecretType(); ok { + _spec.SetField(secret.FieldSecretType, field.TypeEnum, value) + _node.SecretType = value + } + if value, ok := _c.mutation.Target(); ok { + _spec.SetField(secret.FieldTarget, field.TypeString, value) + _node.Target = value + } + if value, ok := _c.mutation.Scope(); ok { + _spec.SetField(secret.FieldScope, field.TypeString, value) + _node.Scope = value + } + if value, ok := _c.mutation.ScopeID(); ok { + _spec.SetField(secret.FieldScopeID, field.TypeString, value) + _node.ScopeID = value + } + if value, ok := _c.mutation.Description(); ok { + _spec.SetField(secret.FieldDescription, field.TypeString, value) + _node.Description = value + } + if value, ok := _c.mutation.InjectionMode(); ok { + _spec.SetField(secret.FieldInjectionMode, field.TypeEnum, value) + _node.InjectionMode = value + } + if value, ok := _c.mutation.AllowProgeny(); ok { + _spec.SetField(secret.FieldAllowProgeny, field.TypeBool, value) + _node.AllowProgeny = value + } + if value, ok := _c.mutation.Version(); ok { + _spec.SetField(secret.FieldVersion, field.TypeInt, value) + _node.Version = value + } + if value, ok := _c.mutation.CreatedBy(); ok { + _spec.SetField(secret.FieldCreatedBy, field.TypeString, value) + _node.CreatedBy = value + } + if value, ok := _c.mutation.UpdatedBy(); ok { + _spec.SetField(secret.FieldUpdatedBy, field.TypeString, value) + _node.UpdatedBy = value + } + if value, ok := _c.mutation.Created(); ok { + _spec.SetField(secret.FieldCreated, field.TypeTime, value) + _node.Created = value + } + if value, ok := _c.mutation.Updated(); ok { + _spec.SetField(secret.FieldUpdated, field.TypeTime, value) + _node.Updated = value + } + return _node, _spec +} + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.Secret.Create(). +// SetKey(v). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.SecretUpsert) { +// SetKey(v+v). +// }). +// Exec(ctx) +func (_c *SecretCreate) OnConflict(opts ...sql.ConflictOption) *SecretUpsertOne { + _c.conflict = opts + return &SecretUpsertOne{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.Secret.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *SecretCreate) OnConflictColumns(columns ...string) *SecretUpsertOne { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &SecretUpsertOne{ + create: _c, + } +} + +type ( + // SecretUpsertOne is the builder for "upsert"-ing + // one Secret node. + SecretUpsertOne struct { + create *SecretCreate + } + + // SecretUpsert is the "OnConflict" setter. + SecretUpsert struct { + *sql.UpdateSet + } +) + +// SetKey sets the "key" field. +func (u *SecretUpsert) SetKey(v string) *SecretUpsert { + u.Set(secret.FieldKey, v) + return u +} + +// UpdateKey sets the "key" field to the value that was provided on create. +func (u *SecretUpsert) UpdateKey() *SecretUpsert { + u.SetExcluded(secret.FieldKey) + return u +} + +// SetEncryptedValue sets the "encrypted_value" field. +func (u *SecretUpsert) SetEncryptedValue(v string) *SecretUpsert { + u.Set(secret.FieldEncryptedValue, v) + return u +} + +// UpdateEncryptedValue sets the "encrypted_value" field to the value that was provided on create. +func (u *SecretUpsert) UpdateEncryptedValue() *SecretUpsert { + u.SetExcluded(secret.FieldEncryptedValue) + return u +} + +// SetSecretRef sets the "secret_ref" field. +func (u *SecretUpsert) SetSecretRef(v string) *SecretUpsert { + u.Set(secret.FieldSecretRef, v) + return u +} + +// UpdateSecretRef sets the "secret_ref" field to the value that was provided on create. +func (u *SecretUpsert) UpdateSecretRef() *SecretUpsert { + u.SetExcluded(secret.FieldSecretRef) + return u +} + +// ClearSecretRef clears the value of the "secret_ref" field. +func (u *SecretUpsert) ClearSecretRef() *SecretUpsert { + u.SetNull(secret.FieldSecretRef) + return u +} + +// SetSecretType sets the "secret_type" field. +func (u *SecretUpsert) SetSecretType(v secret.SecretType) *SecretUpsert { + u.Set(secret.FieldSecretType, v) + return u +} + +// UpdateSecretType sets the "secret_type" field to the value that was provided on create. +func (u *SecretUpsert) UpdateSecretType() *SecretUpsert { + u.SetExcluded(secret.FieldSecretType) + return u +} + +// SetTarget sets the "target" field. +func (u *SecretUpsert) SetTarget(v string) *SecretUpsert { + u.Set(secret.FieldTarget, v) + return u +} + +// UpdateTarget sets the "target" field to the value that was provided on create. +func (u *SecretUpsert) UpdateTarget() *SecretUpsert { + u.SetExcluded(secret.FieldTarget) + return u +} + +// ClearTarget clears the value of the "target" field. +func (u *SecretUpsert) ClearTarget() *SecretUpsert { + u.SetNull(secret.FieldTarget) + return u +} + +// SetScope sets the "scope" field. +func (u *SecretUpsert) SetScope(v string) *SecretUpsert { + u.Set(secret.FieldScope, v) + return u +} + +// UpdateScope sets the "scope" field to the value that was provided on create. +func (u *SecretUpsert) UpdateScope() *SecretUpsert { + u.SetExcluded(secret.FieldScope) + return u +} + +// SetScopeID sets the "scope_id" field. +func (u *SecretUpsert) SetScopeID(v string) *SecretUpsert { + u.Set(secret.FieldScopeID, v) + return u +} + +// UpdateScopeID sets the "scope_id" field to the value that was provided on create. +func (u *SecretUpsert) UpdateScopeID() *SecretUpsert { + u.SetExcluded(secret.FieldScopeID) + return u +} + +// SetDescription sets the "description" field. +func (u *SecretUpsert) SetDescription(v string) *SecretUpsert { + u.Set(secret.FieldDescription, v) + return u +} + +// UpdateDescription sets the "description" field to the value that was provided on create. +func (u *SecretUpsert) UpdateDescription() *SecretUpsert { + u.SetExcluded(secret.FieldDescription) + return u +} + +// ClearDescription clears the value of the "description" field. +func (u *SecretUpsert) ClearDescription() *SecretUpsert { + u.SetNull(secret.FieldDescription) + return u +} + +// SetInjectionMode sets the "injection_mode" field. +func (u *SecretUpsert) SetInjectionMode(v secret.InjectionMode) *SecretUpsert { + u.Set(secret.FieldInjectionMode, v) + return u +} + +// UpdateInjectionMode sets the "injection_mode" field to the value that was provided on create. +func (u *SecretUpsert) UpdateInjectionMode() *SecretUpsert { + u.SetExcluded(secret.FieldInjectionMode) + return u +} + +// SetAllowProgeny sets the "allow_progeny" field. +func (u *SecretUpsert) SetAllowProgeny(v bool) *SecretUpsert { + u.Set(secret.FieldAllowProgeny, v) + return u +} + +// UpdateAllowProgeny sets the "allow_progeny" field to the value that was provided on create. +func (u *SecretUpsert) UpdateAllowProgeny() *SecretUpsert { + u.SetExcluded(secret.FieldAllowProgeny) + return u +} + +// SetVersion sets the "version" field. +func (u *SecretUpsert) SetVersion(v int) *SecretUpsert { + u.Set(secret.FieldVersion, v) + return u +} + +// UpdateVersion sets the "version" field to the value that was provided on create. +func (u *SecretUpsert) UpdateVersion() *SecretUpsert { + u.SetExcluded(secret.FieldVersion) + return u +} + +// AddVersion adds v to the "version" field. +func (u *SecretUpsert) AddVersion(v int) *SecretUpsert { + u.Add(secret.FieldVersion, v) + return u +} + +// SetCreatedBy sets the "created_by" field. +func (u *SecretUpsert) SetCreatedBy(v string) *SecretUpsert { + u.Set(secret.FieldCreatedBy, v) + return u +} + +// UpdateCreatedBy sets the "created_by" field to the value that was provided on create. +func (u *SecretUpsert) UpdateCreatedBy() *SecretUpsert { + u.SetExcluded(secret.FieldCreatedBy) + return u +} + +// ClearCreatedBy clears the value of the "created_by" field. +func (u *SecretUpsert) ClearCreatedBy() *SecretUpsert { + u.SetNull(secret.FieldCreatedBy) + return u +} + +// SetUpdatedBy sets the "updated_by" field. +func (u *SecretUpsert) SetUpdatedBy(v string) *SecretUpsert { + u.Set(secret.FieldUpdatedBy, v) + return u +} + +// UpdateUpdatedBy sets the "updated_by" field to the value that was provided on create. +func (u *SecretUpsert) UpdateUpdatedBy() *SecretUpsert { + u.SetExcluded(secret.FieldUpdatedBy) + return u +} + +// ClearUpdatedBy clears the value of the "updated_by" field. +func (u *SecretUpsert) ClearUpdatedBy() *SecretUpsert { + u.SetNull(secret.FieldUpdatedBy) + return u +} + +// SetUpdated sets the "updated" field. +func (u *SecretUpsert) SetUpdated(v time.Time) *SecretUpsert { + u.Set(secret.FieldUpdated, v) + return u +} + +// UpdateUpdated sets the "updated" field to the value that was provided on create. +func (u *SecretUpsert) UpdateUpdated() *SecretUpsert { + u.SetExcluded(secret.FieldUpdated) + return u +} + +// UpdateNewValues updates the mutable fields using the new values that were set on create except the ID field. +// Using this option is equivalent to using: +// +// client.Secret.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(secret.FieldID) +// }), +// ). +// Exec(ctx) +func (u *SecretUpsertOne) UpdateNewValues() *SecretUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + if _, exists := u.create.mutation.ID(); exists { + s.SetIgnore(secret.FieldID) + } + if _, exists := u.create.mutation.Created(); exists { + s.SetIgnore(secret.FieldCreated) + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.Secret.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *SecretUpsertOne) Ignore() *SecretUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *SecretUpsertOne) DoNothing() *SecretUpsertOne { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the SecretCreate.OnConflict +// documentation for more info. +func (u *SecretUpsertOne) Update(set func(*SecretUpsert)) *SecretUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&SecretUpsert{UpdateSet: update}) + })) + return u +} + +// SetKey sets the "key" field. +func (u *SecretUpsertOne) SetKey(v string) *SecretUpsertOne { + return u.Update(func(s *SecretUpsert) { + s.SetKey(v) + }) +} + +// UpdateKey sets the "key" field to the value that was provided on create. +func (u *SecretUpsertOne) UpdateKey() *SecretUpsertOne { + return u.Update(func(s *SecretUpsert) { + s.UpdateKey() + }) +} + +// SetEncryptedValue sets the "encrypted_value" field. +func (u *SecretUpsertOne) SetEncryptedValue(v string) *SecretUpsertOne { + return u.Update(func(s *SecretUpsert) { + s.SetEncryptedValue(v) + }) +} + +// UpdateEncryptedValue sets the "encrypted_value" field to the value that was provided on create. +func (u *SecretUpsertOne) UpdateEncryptedValue() *SecretUpsertOne { + return u.Update(func(s *SecretUpsert) { + s.UpdateEncryptedValue() + }) +} + +// SetSecretRef sets the "secret_ref" field. +func (u *SecretUpsertOne) SetSecretRef(v string) *SecretUpsertOne { + return u.Update(func(s *SecretUpsert) { + s.SetSecretRef(v) + }) +} + +// UpdateSecretRef sets the "secret_ref" field to the value that was provided on create. +func (u *SecretUpsertOne) UpdateSecretRef() *SecretUpsertOne { + return u.Update(func(s *SecretUpsert) { + s.UpdateSecretRef() + }) +} + +// ClearSecretRef clears the value of the "secret_ref" field. +func (u *SecretUpsertOne) ClearSecretRef() *SecretUpsertOne { + return u.Update(func(s *SecretUpsert) { + s.ClearSecretRef() + }) +} + +// SetSecretType sets the "secret_type" field. +func (u *SecretUpsertOne) SetSecretType(v secret.SecretType) *SecretUpsertOne { + return u.Update(func(s *SecretUpsert) { + s.SetSecretType(v) + }) +} + +// UpdateSecretType sets the "secret_type" field to the value that was provided on create. +func (u *SecretUpsertOne) UpdateSecretType() *SecretUpsertOne { + return u.Update(func(s *SecretUpsert) { + s.UpdateSecretType() + }) +} + +// SetTarget sets the "target" field. +func (u *SecretUpsertOne) SetTarget(v string) *SecretUpsertOne { + return u.Update(func(s *SecretUpsert) { + s.SetTarget(v) + }) +} + +// UpdateTarget sets the "target" field to the value that was provided on create. +func (u *SecretUpsertOne) UpdateTarget() *SecretUpsertOne { + return u.Update(func(s *SecretUpsert) { + s.UpdateTarget() + }) +} + +// ClearTarget clears the value of the "target" field. +func (u *SecretUpsertOne) ClearTarget() *SecretUpsertOne { + return u.Update(func(s *SecretUpsert) { + s.ClearTarget() + }) +} + +// SetScope sets the "scope" field. +func (u *SecretUpsertOne) SetScope(v string) *SecretUpsertOne { + return u.Update(func(s *SecretUpsert) { + s.SetScope(v) + }) +} + +// UpdateScope sets the "scope" field to the value that was provided on create. +func (u *SecretUpsertOne) UpdateScope() *SecretUpsertOne { + return u.Update(func(s *SecretUpsert) { + s.UpdateScope() + }) +} + +// SetScopeID sets the "scope_id" field. +func (u *SecretUpsertOne) SetScopeID(v string) *SecretUpsertOne { + return u.Update(func(s *SecretUpsert) { + s.SetScopeID(v) + }) +} + +// UpdateScopeID sets the "scope_id" field to the value that was provided on create. +func (u *SecretUpsertOne) UpdateScopeID() *SecretUpsertOne { + return u.Update(func(s *SecretUpsert) { + s.UpdateScopeID() + }) +} + +// SetDescription sets the "description" field. +func (u *SecretUpsertOne) SetDescription(v string) *SecretUpsertOne { + return u.Update(func(s *SecretUpsert) { + s.SetDescription(v) + }) +} + +// UpdateDescription sets the "description" field to the value that was provided on create. +func (u *SecretUpsertOne) UpdateDescription() *SecretUpsertOne { + return u.Update(func(s *SecretUpsert) { + s.UpdateDescription() + }) +} + +// ClearDescription clears the value of the "description" field. +func (u *SecretUpsertOne) ClearDescription() *SecretUpsertOne { + return u.Update(func(s *SecretUpsert) { + s.ClearDescription() + }) +} + +// SetInjectionMode sets the "injection_mode" field. +func (u *SecretUpsertOne) SetInjectionMode(v secret.InjectionMode) *SecretUpsertOne { + return u.Update(func(s *SecretUpsert) { + s.SetInjectionMode(v) + }) +} + +// UpdateInjectionMode sets the "injection_mode" field to the value that was provided on create. +func (u *SecretUpsertOne) UpdateInjectionMode() *SecretUpsertOne { + return u.Update(func(s *SecretUpsert) { + s.UpdateInjectionMode() + }) +} + +// SetAllowProgeny sets the "allow_progeny" field. +func (u *SecretUpsertOne) SetAllowProgeny(v bool) *SecretUpsertOne { + return u.Update(func(s *SecretUpsert) { + s.SetAllowProgeny(v) + }) +} + +// UpdateAllowProgeny sets the "allow_progeny" field to the value that was provided on create. +func (u *SecretUpsertOne) UpdateAllowProgeny() *SecretUpsertOne { + return u.Update(func(s *SecretUpsert) { + s.UpdateAllowProgeny() + }) +} + +// SetVersion sets the "version" field. +func (u *SecretUpsertOne) SetVersion(v int) *SecretUpsertOne { + return u.Update(func(s *SecretUpsert) { + s.SetVersion(v) + }) +} + +// AddVersion adds v to the "version" field. +func (u *SecretUpsertOne) AddVersion(v int) *SecretUpsertOne { + return u.Update(func(s *SecretUpsert) { + s.AddVersion(v) + }) +} + +// UpdateVersion sets the "version" field to the value that was provided on create. +func (u *SecretUpsertOne) UpdateVersion() *SecretUpsertOne { + return u.Update(func(s *SecretUpsert) { + s.UpdateVersion() + }) +} + +// SetCreatedBy sets the "created_by" field. +func (u *SecretUpsertOne) SetCreatedBy(v string) *SecretUpsertOne { + return u.Update(func(s *SecretUpsert) { + s.SetCreatedBy(v) + }) +} + +// UpdateCreatedBy sets the "created_by" field to the value that was provided on create. +func (u *SecretUpsertOne) UpdateCreatedBy() *SecretUpsertOne { + return u.Update(func(s *SecretUpsert) { + s.UpdateCreatedBy() + }) +} + +// ClearCreatedBy clears the value of the "created_by" field. +func (u *SecretUpsertOne) ClearCreatedBy() *SecretUpsertOne { + return u.Update(func(s *SecretUpsert) { + s.ClearCreatedBy() + }) +} + +// SetUpdatedBy sets the "updated_by" field. +func (u *SecretUpsertOne) SetUpdatedBy(v string) *SecretUpsertOne { + return u.Update(func(s *SecretUpsert) { + s.SetUpdatedBy(v) + }) +} + +// UpdateUpdatedBy sets the "updated_by" field to the value that was provided on create. +func (u *SecretUpsertOne) UpdateUpdatedBy() *SecretUpsertOne { + return u.Update(func(s *SecretUpsert) { + s.UpdateUpdatedBy() + }) +} + +// ClearUpdatedBy clears the value of the "updated_by" field. +func (u *SecretUpsertOne) ClearUpdatedBy() *SecretUpsertOne { + return u.Update(func(s *SecretUpsert) { + s.ClearUpdatedBy() + }) +} + +// SetUpdated sets the "updated" field. +func (u *SecretUpsertOne) SetUpdated(v time.Time) *SecretUpsertOne { + return u.Update(func(s *SecretUpsert) { + s.SetUpdated(v) + }) +} + +// UpdateUpdated sets the "updated" field to the value that was provided on create. +func (u *SecretUpsertOne) UpdateUpdated() *SecretUpsertOne { + return u.Update(func(s *SecretUpsert) { + s.UpdateUpdated() + }) +} + +// Exec executes the query. +func (u *SecretUpsertOne) Exec(ctx context.Context) error { + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for SecretCreate.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *SecretUpsertOne) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} + +// Exec executes the UPSERT query and returns the inserted/updated ID. +func (u *SecretUpsertOne) ID(ctx context.Context) (id uuid.UUID, err error) { + if u.create.driver.Dialect() == dialect.MySQL { + // In case of "ON CONFLICT", there is no way to get back non-numeric ID + // fields from the database since MySQL does not support the RETURNING clause. + return id, errors.New("ent: SecretUpsertOne.ID is not supported by MySQL driver. Use SecretUpsertOne.Exec instead") + } + node, err := u.create.Save(ctx) + if err != nil { + return id, err + } + return node.ID, nil +} + +// IDX is like ID, but panics if an error occurs. +func (u *SecretUpsertOne) IDX(ctx context.Context) uuid.UUID { + id, err := u.ID(ctx) + if err != nil { + panic(err) + } + return id +} + +// SecretCreateBulk is the builder for creating many Secret entities in bulk. +type SecretCreateBulk struct { + config + err error + builders []*SecretCreate + conflict []sql.ConflictOption +} + +// Save creates the Secret entities in the database. +func (_c *SecretCreateBulk) Save(ctx context.Context) ([]*Secret, error) { + if _c.err != nil { + return nil, _c.err + } + specs := make([]*sqlgraph.CreateSpec, len(_c.builders)) + nodes := make([]*Secret, len(_c.builders)) + mutators := make([]Mutator, len(_c.builders)) + for i := range _c.builders { + func(i int, root context.Context) { + builder := _c.builders[i] + builder.defaults() + var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { + mutation, ok := m.(*SecretMutation) + if !ok { + return nil, fmt.Errorf("unexpected mutation type %T", m) + } + if err := builder.check(); err != nil { + return nil, err + } + builder.mutation = mutation + var err error + nodes[i], specs[i] = builder.createSpec() + if i < len(mutators)-1 { + _, err = mutators[i+1].Mutate(root, _c.builders[i+1].mutation) + } else { + spec := &sqlgraph.BatchCreateSpec{Nodes: specs} + spec.OnConflict = _c.conflict + // Invoke the actual operation on the latest mutation in the chain. + if err = sqlgraph.BatchCreate(ctx, _c.driver, spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + } + } + if err != nil { + return nil, err + } + mutation.id = &nodes[i].ID + mutation.done = true + return nodes[i], nil + }) + for i := len(builder.hooks) - 1; i >= 0; i-- { + mut = builder.hooks[i](mut) + } + mutators[i] = mut + }(i, ctx) + } + if len(mutators) > 0 { + if _, err := mutators[0].Mutate(ctx, _c.builders[0].mutation); err != nil { + return nil, err + } + } + return nodes, nil +} + +// SaveX is like Save, but panics if an error occurs. +func (_c *SecretCreateBulk) SaveX(ctx context.Context) []*Secret { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *SecretCreateBulk) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *SecretCreateBulk) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.Secret.CreateBulk(builders...). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.SecretUpsert) { +// SetKey(v+v). +// }). +// Exec(ctx) +func (_c *SecretCreateBulk) OnConflict(opts ...sql.ConflictOption) *SecretUpsertBulk { + _c.conflict = opts + return &SecretUpsertBulk{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.Secret.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *SecretCreateBulk) OnConflictColumns(columns ...string) *SecretUpsertBulk { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &SecretUpsertBulk{ + create: _c, + } +} + +// SecretUpsertBulk is the builder for "upsert"-ing +// a bulk of Secret nodes. +type SecretUpsertBulk struct { + create *SecretCreateBulk +} + +// UpdateNewValues updates the mutable fields using the new values that +// were set on create. Using this option is equivalent to using: +// +// client.Secret.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(secret.FieldID) +// }), +// ). +// Exec(ctx) +func (u *SecretUpsertBulk) UpdateNewValues() *SecretUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + for _, b := range u.create.builders { + if _, exists := b.mutation.ID(); exists { + s.SetIgnore(secret.FieldID) + } + if _, exists := b.mutation.Created(); exists { + s.SetIgnore(secret.FieldCreated) + } + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.Secret.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *SecretUpsertBulk) Ignore() *SecretUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *SecretUpsertBulk) DoNothing() *SecretUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the SecretCreateBulk.OnConflict +// documentation for more info. +func (u *SecretUpsertBulk) Update(set func(*SecretUpsert)) *SecretUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&SecretUpsert{UpdateSet: update}) + })) + return u +} + +// SetKey sets the "key" field. +func (u *SecretUpsertBulk) SetKey(v string) *SecretUpsertBulk { + return u.Update(func(s *SecretUpsert) { + s.SetKey(v) + }) +} + +// UpdateKey sets the "key" field to the value that was provided on create. +func (u *SecretUpsertBulk) UpdateKey() *SecretUpsertBulk { + return u.Update(func(s *SecretUpsert) { + s.UpdateKey() + }) +} + +// SetEncryptedValue sets the "encrypted_value" field. +func (u *SecretUpsertBulk) SetEncryptedValue(v string) *SecretUpsertBulk { + return u.Update(func(s *SecretUpsert) { + s.SetEncryptedValue(v) + }) +} + +// UpdateEncryptedValue sets the "encrypted_value" field to the value that was provided on create. +func (u *SecretUpsertBulk) UpdateEncryptedValue() *SecretUpsertBulk { + return u.Update(func(s *SecretUpsert) { + s.UpdateEncryptedValue() + }) +} + +// SetSecretRef sets the "secret_ref" field. +func (u *SecretUpsertBulk) SetSecretRef(v string) *SecretUpsertBulk { + return u.Update(func(s *SecretUpsert) { + s.SetSecretRef(v) + }) +} + +// UpdateSecretRef sets the "secret_ref" field to the value that was provided on create. +func (u *SecretUpsertBulk) UpdateSecretRef() *SecretUpsertBulk { + return u.Update(func(s *SecretUpsert) { + s.UpdateSecretRef() + }) +} + +// ClearSecretRef clears the value of the "secret_ref" field. +func (u *SecretUpsertBulk) ClearSecretRef() *SecretUpsertBulk { + return u.Update(func(s *SecretUpsert) { + s.ClearSecretRef() + }) +} + +// SetSecretType sets the "secret_type" field. +func (u *SecretUpsertBulk) SetSecretType(v secret.SecretType) *SecretUpsertBulk { + return u.Update(func(s *SecretUpsert) { + s.SetSecretType(v) + }) +} + +// UpdateSecretType sets the "secret_type" field to the value that was provided on create. +func (u *SecretUpsertBulk) UpdateSecretType() *SecretUpsertBulk { + return u.Update(func(s *SecretUpsert) { + s.UpdateSecretType() + }) +} + +// SetTarget sets the "target" field. +func (u *SecretUpsertBulk) SetTarget(v string) *SecretUpsertBulk { + return u.Update(func(s *SecretUpsert) { + s.SetTarget(v) + }) +} + +// UpdateTarget sets the "target" field to the value that was provided on create. +func (u *SecretUpsertBulk) UpdateTarget() *SecretUpsertBulk { + return u.Update(func(s *SecretUpsert) { + s.UpdateTarget() + }) +} + +// ClearTarget clears the value of the "target" field. +func (u *SecretUpsertBulk) ClearTarget() *SecretUpsertBulk { + return u.Update(func(s *SecretUpsert) { + s.ClearTarget() + }) +} + +// SetScope sets the "scope" field. +func (u *SecretUpsertBulk) SetScope(v string) *SecretUpsertBulk { + return u.Update(func(s *SecretUpsert) { + s.SetScope(v) + }) +} + +// UpdateScope sets the "scope" field to the value that was provided on create. +func (u *SecretUpsertBulk) UpdateScope() *SecretUpsertBulk { + return u.Update(func(s *SecretUpsert) { + s.UpdateScope() + }) +} + +// SetScopeID sets the "scope_id" field. +func (u *SecretUpsertBulk) SetScopeID(v string) *SecretUpsertBulk { + return u.Update(func(s *SecretUpsert) { + s.SetScopeID(v) + }) +} + +// UpdateScopeID sets the "scope_id" field to the value that was provided on create. +func (u *SecretUpsertBulk) UpdateScopeID() *SecretUpsertBulk { + return u.Update(func(s *SecretUpsert) { + s.UpdateScopeID() + }) +} + +// SetDescription sets the "description" field. +func (u *SecretUpsertBulk) SetDescription(v string) *SecretUpsertBulk { + return u.Update(func(s *SecretUpsert) { + s.SetDescription(v) + }) +} + +// UpdateDescription sets the "description" field to the value that was provided on create. +func (u *SecretUpsertBulk) UpdateDescription() *SecretUpsertBulk { + return u.Update(func(s *SecretUpsert) { + s.UpdateDescription() + }) +} + +// ClearDescription clears the value of the "description" field. +func (u *SecretUpsertBulk) ClearDescription() *SecretUpsertBulk { + return u.Update(func(s *SecretUpsert) { + s.ClearDescription() + }) +} + +// SetInjectionMode sets the "injection_mode" field. +func (u *SecretUpsertBulk) SetInjectionMode(v secret.InjectionMode) *SecretUpsertBulk { + return u.Update(func(s *SecretUpsert) { + s.SetInjectionMode(v) + }) +} + +// UpdateInjectionMode sets the "injection_mode" field to the value that was provided on create. +func (u *SecretUpsertBulk) UpdateInjectionMode() *SecretUpsertBulk { + return u.Update(func(s *SecretUpsert) { + s.UpdateInjectionMode() + }) +} + +// SetAllowProgeny sets the "allow_progeny" field. +func (u *SecretUpsertBulk) SetAllowProgeny(v bool) *SecretUpsertBulk { + return u.Update(func(s *SecretUpsert) { + s.SetAllowProgeny(v) + }) +} + +// UpdateAllowProgeny sets the "allow_progeny" field to the value that was provided on create. +func (u *SecretUpsertBulk) UpdateAllowProgeny() *SecretUpsertBulk { + return u.Update(func(s *SecretUpsert) { + s.UpdateAllowProgeny() + }) +} + +// SetVersion sets the "version" field. +func (u *SecretUpsertBulk) SetVersion(v int) *SecretUpsertBulk { + return u.Update(func(s *SecretUpsert) { + s.SetVersion(v) + }) +} + +// AddVersion adds v to the "version" field. +func (u *SecretUpsertBulk) AddVersion(v int) *SecretUpsertBulk { + return u.Update(func(s *SecretUpsert) { + s.AddVersion(v) + }) +} + +// UpdateVersion sets the "version" field to the value that was provided on create. +func (u *SecretUpsertBulk) UpdateVersion() *SecretUpsertBulk { + return u.Update(func(s *SecretUpsert) { + s.UpdateVersion() + }) +} + +// SetCreatedBy sets the "created_by" field. +func (u *SecretUpsertBulk) SetCreatedBy(v string) *SecretUpsertBulk { + return u.Update(func(s *SecretUpsert) { + s.SetCreatedBy(v) + }) +} + +// UpdateCreatedBy sets the "created_by" field to the value that was provided on create. +func (u *SecretUpsertBulk) UpdateCreatedBy() *SecretUpsertBulk { + return u.Update(func(s *SecretUpsert) { + s.UpdateCreatedBy() + }) +} + +// ClearCreatedBy clears the value of the "created_by" field. +func (u *SecretUpsertBulk) ClearCreatedBy() *SecretUpsertBulk { + return u.Update(func(s *SecretUpsert) { + s.ClearCreatedBy() + }) +} + +// SetUpdatedBy sets the "updated_by" field. +func (u *SecretUpsertBulk) SetUpdatedBy(v string) *SecretUpsertBulk { + return u.Update(func(s *SecretUpsert) { + s.SetUpdatedBy(v) + }) +} + +// UpdateUpdatedBy sets the "updated_by" field to the value that was provided on create. +func (u *SecretUpsertBulk) UpdateUpdatedBy() *SecretUpsertBulk { + return u.Update(func(s *SecretUpsert) { + s.UpdateUpdatedBy() + }) +} + +// ClearUpdatedBy clears the value of the "updated_by" field. +func (u *SecretUpsertBulk) ClearUpdatedBy() *SecretUpsertBulk { + return u.Update(func(s *SecretUpsert) { + s.ClearUpdatedBy() + }) +} + +// SetUpdated sets the "updated" field. +func (u *SecretUpsertBulk) SetUpdated(v time.Time) *SecretUpsertBulk { + return u.Update(func(s *SecretUpsert) { + s.SetUpdated(v) + }) +} + +// UpdateUpdated sets the "updated" field to the value that was provided on create. +func (u *SecretUpsertBulk) UpdateUpdated() *SecretUpsertBulk { + return u.Update(func(s *SecretUpsert) { + s.UpdateUpdated() + }) +} + +// Exec executes the query. +func (u *SecretUpsertBulk) Exec(ctx context.Context) error { + if u.create.err != nil { + return u.create.err + } + for i, b := range u.create.builders { + if len(b.conflict) != 0 { + return fmt.Errorf("ent: OnConflict was set for builder %d. Set it on the SecretCreateBulk instead", i) + } + } + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for SecretCreateBulk.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *SecretUpsertBulk) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/pkg/ent/secret_delete.go b/pkg/ent/secret_delete.go new file mode 100644 index 000000000..0eb496d29 --- /dev/null +++ b/pkg/ent/secret_delete.go @@ -0,0 +1,88 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/GoogleCloudPlatform/scion/pkg/ent/secret" +) + +// SecretDelete is the builder for deleting a Secret entity. +type SecretDelete struct { + config + hooks []Hook + mutation *SecretMutation +} + +// Where appends a list predicates to the SecretDelete builder. +func (_d *SecretDelete) Where(ps ...predicate.Secret) *SecretDelete { + _d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (_d *SecretDelete) Exec(ctx context.Context) (int, error) { + return withHooks(ctx, _d.sqlExec, _d.mutation, _d.hooks) +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *SecretDelete) ExecX(ctx context.Context) int { + n, err := _d.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (_d *SecretDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(secret.Table, sqlgraph.NewFieldSpec(secret.FieldID, field.TypeUUID)) + if ps := _d.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + affected, err := sqlgraph.DeleteNodes(ctx, _d.driver, _spec) + if err != nil && sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + _d.mutation.done = true + return affected, err +} + +// SecretDeleteOne is the builder for deleting a single Secret entity. +type SecretDeleteOne struct { + _d *SecretDelete +} + +// Where appends a list predicates to the SecretDelete builder. +func (_d *SecretDeleteOne) Where(ps ...predicate.Secret) *SecretDeleteOne { + _d._d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query. +func (_d *SecretDeleteOne) Exec(ctx context.Context) error { + n, err := _d._d.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{secret.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *SecretDeleteOne) ExecX(ctx context.Context) { + if err := _d.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/pkg/ent/secret_query.go b/pkg/ent/secret_query.go new file mode 100644 index 000000000..b02ea7f6d --- /dev/null +++ b/pkg/ent/secret_query.go @@ -0,0 +1,565 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "fmt" + "math" + + "entgo.io/ent" + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/GoogleCloudPlatform/scion/pkg/ent/secret" + "github.com/google/uuid" +) + +// SecretQuery is the builder for querying Secret entities. +type SecretQuery struct { + config + ctx *QueryContext + order []secret.OrderOption + inters []Interceptor + predicates []predicate.Secret + modifiers []func(*sql.Selector) + // intermediate query (i.e. traversal path). + sql *sql.Selector + path func(context.Context) (*sql.Selector, error) +} + +// Where adds a new predicate for the SecretQuery builder. +func (_q *SecretQuery) Where(ps ...predicate.Secret) *SecretQuery { + _q.predicates = append(_q.predicates, ps...) + return _q +} + +// Limit the number of records to be returned by this query. +func (_q *SecretQuery) Limit(limit int) *SecretQuery { + _q.ctx.Limit = &limit + return _q +} + +// Offset to start from. +func (_q *SecretQuery) Offset(offset int) *SecretQuery { + _q.ctx.Offset = &offset + return _q +} + +// Unique configures the query builder to filter duplicate records on query. +// By default, unique is set to true, and can be disabled using this method. +func (_q *SecretQuery) Unique(unique bool) *SecretQuery { + _q.ctx.Unique = &unique + return _q +} + +// Order specifies how the records should be ordered. +func (_q *SecretQuery) Order(o ...secret.OrderOption) *SecretQuery { + _q.order = append(_q.order, o...) + return _q +} + +// First returns the first Secret entity from the query. +// Returns a *NotFoundError when no Secret was found. +func (_q *SecretQuery) First(ctx context.Context) (*Secret, error) { + nodes, err := _q.Limit(1).All(setContextOp(ctx, _q.ctx, ent.OpQueryFirst)) + if err != nil { + return nil, err + } + if len(nodes) == 0 { + return nil, &NotFoundError{secret.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (_q *SecretQuery) FirstX(ctx context.Context) *Secret { + node, err := _q.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// FirstID returns the first Secret ID from the query. +// Returns a *NotFoundError when no Secret ID was found. +func (_q *SecretQuery) FirstID(ctx context.Context) (id uuid.UUID, err error) { + var ids []uuid.UUID + if ids, err = _q.Limit(1).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryFirstID)); err != nil { + return + } + if len(ids) == 0 { + err = &NotFoundError{secret.Label} + return + } + return ids[0], nil +} + +// FirstIDX is like FirstID, but panics if an error occurs. +func (_q *SecretQuery) FirstIDX(ctx context.Context) uuid.UUID { + id, err := _q.FirstID(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return id +} + +// Only returns a single Secret entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one Secret entity is found. +// Returns a *NotFoundError when no Secret entities are found. +func (_q *SecretQuery) Only(ctx context.Context) (*Secret, error) { + nodes, err := _q.Limit(2).All(setContextOp(ctx, _q.ctx, ent.OpQueryOnly)) + if err != nil { + return nil, err + } + switch len(nodes) { + case 1: + return nodes[0], nil + case 0: + return nil, &NotFoundError{secret.Label} + default: + return nil, &NotSingularError{secret.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (_q *SecretQuery) OnlyX(ctx context.Context) *Secret { + node, err := _q.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// OnlyID is like Only, but returns the only Secret ID in the query. +// Returns a *NotSingularError when more than one Secret ID is found. +// Returns a *NotFoundError when no entities are found. +func (_q *SecretQuery) OnlyID(ctx context.Context) (id uuid.UUID, err error) { + var ids []uuid.UUID + if ids, err = _q.Limit(2).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryOnlyID)); err != nil { + return + } + switch len(ids) { + case 1: + id = ids[0] + case 0: + err = &NotFoundError{secret.Label} + default: + err = &NotSingularError{secret.Label} + } + return +} + +// OnlyIDX is like OnlyID, but panics if an error occurs. +func (_q *SecretQuery) OnlyIDX(ctx context.Context) uuid.UUID { + id, err := _q.OnlyID(ctx) + if err != nil { + panic(err) + } + return id +} + +// All executes the query and returns a list of Secrets. +func (_q *SecretQuery) All(ctx context.Context) ([]*Secret, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryAll) + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*Secret, *SecretQuery]() + return withInterceptors[[]*Secret](ctx, _q, qr, _q.inters) +} + +// AllX is like All, but panics if an error occurs. +func (_q *SecretQuery) AllX(ctx context.Context) []*Secret { + nodes, err := _q.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// IDs executes the query and returns a list of Secret IDs. +func (_q *SecretQuery) IDs(ctx context.Context) (ids []uuid.UUID, err error) { + if _q.ctx.Unique == nil && _q.path != nil { + _q.Unique(true) + } + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryIDs) + if err = _q.Select(secret.FieldID).Scan(ctx, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// IDsX is like IDs, but panics if an error occurs. +func (_q *SecretQuery) IDsX(ctx context.Context) []uuid.UUID { + ids, err := _q.IDs(ctx) + if err != nil { + panic(err) + } + return ids +} + +// Count returns the count of the given query. +func (_q *SecretQuery) Count(ctx context.Context) (int, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryCount) + if err := _q.prepareQuery(ctx); err != nil { + return 0, err + } + return withInterceptors[int](ctx, _q, querierCount[*SecretQuery](), _q.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (_q *SecretQuery) CountX(ctx context.Context) int { + count, err := _q.Count(ctx) + if err != nil { + panic(err) + } + return count +} + +// Exist returns true if the query has elements in the graph. +func (_q *SecretQuery) Exist(ctx context.Context) (bool, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryExist) + switch _, err := _q.FirstID(ctx); { + case IsNotFound(err): + return false, nil + case err != nil: + return false, fmt.Errorf("ent: check existence: %w", err) + default: + return true, nil + } +} + +// ExistX is like Exist, but panics if an error occurs. +func (_q *SecretQuery) ExistX(ctx context.Context) bool { + exist, err := _q.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the SecretQuery builder, including all associated steps. It can be +// used to prepare common query builders and use them differently after the clone is made. +func (_q *SecretQuery) Clone() *SecretQuery { + if _q == nil { + return nil + } + return &SecretQuery{ + config: _q.config, + ctx: _q.ctx.Clone(), + order: append([]secret.OrderOption{}, _q.order...), + inters: append([]Interceptor{}, _q.inters...), + predicates: append([]predicate.Secret{}, _q.predicates...), + // clone intermediate query. + sql: _q.sql.Clone(), + path: _q.path, + } +} + +// GroupBy is used to group vertices by one or more fields/columns. +// It is often used with aggregate functions, like: count, max, mean, min, sum. +// +// Example: +// +// var v []struct { +// Key string `json:"key,omitempty"` +// Count int `json:"count,omitempty"` +// } +// +// client.Secret.Query(). +// GroupBy(secret.FieldKey). +// Aggregate(ent.Count()). +// Scan(ctx, &v) +func (_q *SecretQuery) GroupBy(field string, fields ...string) *SecretGroupBy { + _q.ctx.Fields = append([]string{field}, fields...) + grbuild := &SecretGroupBy{build: _q} + grbuild.flds = &_q.ctx.Fields + grbuild.label = secret.Label + grbuild.scan = grbuild.Scan + return grbuild +} + +// Select allows the selection one or more fields/columns for the given query, +// instead of selecting all fields in the entity. +// +// Example: +// +// var v []struct { +// Key string `json:"key,omitempty"` +// } +// +// client.Secret.Query(). +// Select(secret.FieldKey). +// Scan(ctx, &v) +func (_q *SecretQuery) Select(fields ...string) *SecretSelect { + _q.ctx.Fields = append(_q.ctx.Fields, fields...) + sbuild := &SecretSelect{SecretQuery: _q} + sbuild.label = secret.Label + sbuild.flds, sbuild.scan = &_q.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a SecretSelect configured with the given aggregations. +func (_q *SecretQuery) Aggregate(fns ...AggregateFunc) *SecretSelect { + return _q.Select().Aggregate(fns...) +} + +func (_q *SecretQuery) prepareQuery(ctx context.Context) error { + for _, inter := range _q.inters { + if inter == nil { + return fmt.Errorf("ent: uninitialized interceptor (forgotten import ent/runtime?)") + } + if trv, ok := inter.(Traverser); ok { + if err := trv.Traverse(ctx, _q); err != nil { + return err + } + } + } + for _, f := range _q.ctx.Fields { + if !secret.ValidColumn(f) { + return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + } + if _q.path != nil { + prev, err := _q.path(ctx) + if err != nil { + return err + } + _q.sql = prev + } + return nil +} + +func (_q *SecretQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*Secret, error) { + var ( + nodes = []*Secret{} + _spec = _q.querySpec() + ) + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*Secret).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &Secret{config: _q.config} + nodes = append(nodes, node) + return node.assignValues(columns, values) + } + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } + for i := range hooks { + hooks[i](ctx, _spec) + } + if err := sqlgraph.QueryNodes(ctx, _q.driver, _spec); err != nil { + return nil, err + } + if len(nodes) == 0 { + return nodes, nil + } + return nodes, nil +} + +func (_q *SecretQuery) sqlCount(ctx context.Context) (int, error) { + _spec := _q.querySpec() + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } + _spec.Node.Columns = _q.ctx.Fields + if len(_q.ctx.Fields) > 0 { + _spec.Unique = _q.ctx.Unique != nil && *_q.ctx.Unique + } + return sqlgraph.CountNodes(ctx, _q.driver, _spec) +} + +func (_q *SecretQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(secret.Table, secret.Columns, sqlgraph.NewFieldSpec(secret.FieldID, field.TypeUUID)) + _spec.From = _q.sql + if unique := _q.ctx.Unique; unique != nil { + _spec.Unique = *unique + } else if _q.path != nil { + _spec.Unique = true + } + if fields := _q.ctx.Fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, secret.FieldID) + for i := range fields { + if fields[i] != secret.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, fields[i]) + } + } + } + if ps := _q.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if limit := _q.ctx.Limit; limit != nil { + _spec.Limit = *limit + } + if offset := _q.ctx.Offset; offset != nil { + _spec.Offset = *offset + } + if ps := _q.order; len(ps) > 0 { + _spec.Order = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + return _spec +} + +func (_q *SecretQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(_q.driver.Dialect()) + t1 := builder.Table(secret.Table) + columns := _q.ctx.Fields + if len(columns) == 0 { + columns = secret.Columns + } + selector := builder.Select(t1.Columns(columns...)...).From(t1) + if _q.sql != nil { + selector = _q.sql + selector.Select(selector.Columns(columns...)...) + } + if _q.ctx.Unique != nil && *_q.ctx.Unique { + selector.Distinct() + } + for _, m := range _q.modifiers { + m(selector) + } + for _, p := range _q.predicates { + p(selector) + } + for _, p := range _q.order { + p(selector) + } + if offset := _q.ctx.Offset; offset != nil { + // limit is mandatory for offset clause. We start + // with default value, and override it below if needed. + selector.Offset(*offset).Limit(math.MaxInt32) + } + if limit := _q.ctx.Limit; limit != nil { + selector.Limit(*limit) + } + return selector +} + +// ForUpdate locks the selected rows against concurrent updates, and prevent them from being +// updated, deleted or "selected ... for update" by other sessions, until the transaction is +// either committed or rolled-back. +func (_q *SecretQuery) ForUpdate(opts ...sql.LockOption) *SecretQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForUpdate(opts...) + }) + return _q +} + +// ForShare behaves similarly to ForUpdate, except that it acquires a shared mode lock +// on any rows that are read. Other sessions can read the rows, but cannot modify them +// until your transaction commits. +func (_q *SecretQuery) ForShare(opts ...sql.LockOption) *SecretQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForShare(opts...) + }) + return _q +} + +// SecretGroupBy is the group-by builder for Secret entities. +type SecretGroupBy struct { + selector + build *SecretQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (_g *SecretGroupBy) Aggregate(fns ...AggregateFunc) *SecretGroupBy { + _g.fns = append(_g.fns, fns...) + return _g +} + +// Scan applies the selector query and scans the result into the given value. +func (_g *SecretGroupBy) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, _g.build.ctx, ent.OpQueryGroupBy) + if err := _g.build.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*SecretQuery, *SecretGroupBy](ctx, _g.build, _g, _g.build.inters, v) +} + +func (_g *SecretGroupBy) sqlScan(ctx context.Context, root *SecretQuery, v any) error { + selector := root.sqlQuery(ctx).Select() + aggregation := make([]string, 0, len(_g.fns)) + for _, fn := range _g.fns { + aggregation = append(aggregation, fn(selector)) + } + if len(selector.SelectedColumns()) == 0 { + columns := make([]string, 0, len(*_g.flds)+len(_g.fns)) + for _, f := range *_g.flds { + columns = append(columns, selector.C(f)) + } + columns = append(columns, aggregation...) + selector.Select(columns...) + } + selector.GroupBy(selector.Columns(*_g.flds...)...) + if err := selector.Err(); err != nil { + return err + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := _g.build.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} + +// SecretSelect is the builder for selecting fields of Secret entities. +type SecretSelect struct { + *SecretQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (_s *SecretSelect) Aggregate(fns ...AggregateFunc) *SecretSelect { + _s.fns = append(_s.fns, fns...) + return _s +} + +// Scan applies the selector query and scans the result into the given value. +func (_s *SecretSelect) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, _s.ctx, ent.OpQuerySelect) + if err := _s.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*SecretQuery, *SecretSelect](ctx, _s.SecretQuery, _s, _s.inters, v) +} + +func (_s *SecretSelect) sqlScan(ctx context.Context, root *SecretQuery, v any) error { + selector := root.sqlQuery(ctx) + aggregation := make([]string, 0, len(_s.fns)) + for _, fn := range _s.fns { + aggregation = append(aggregation, fn(selector)) + } + switch n := len(*_s.selector.flds); { + case n == 0 && len(aggregation) > 0: + selector.Select(aggregation...) + case n != 0 && len(aggregation) > 0: + selector.AppendSelect(aggregation...) + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := _s.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} diff --git a/pkg/ent/secret_update.go b/pkg/ent/secret_update.go new file mode 100644 index 000000000..18de73a22 --- /dev/null +++ b/pkg/ent/secret_update.go @@ -0,0 +1,820 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/GoogleCloudPlatform/scion/pkg/ent/secret" +) + +// SecretUpdate is the builder for updating Secret entities. +type SecretUpdate struct { + config + hooks []Hook + mutation *SecretMutation +} + +// Where appends a list predicates to the SecretUpdate builder. +func (_u *SecretUpdate) Where(ps ...predicate.Secret) *SecretUpdate { + _u.mutation.Where(ps...) + return _u +} + +// SetKey sets the "key" field. +func (_u *SecretUpdate) SetKey(v string) *SecretUpdate { + _u.mutation.SetKey(v) + return _u +} + +// SetNillableKey sets the "key" field if the given value is not nil. +func (_u *SecretUpdate) SetNillableKey(v *string) *SecretUpdate { + if v != nil { + _u.SetKey(*v) + } + return _u +} + +// SetEncryptedValue sets the "encrypted_value" field. +func (_u *SecretUpdate) SetEncryptedValue(v string) *SecretUpdate { + _u.mutation.SetEncryptedValue(v) + return _u +} + +// SetNillableEncryptedValue sets the "encrypted_value" field if the given value is not nil. +func (_u *SecretUpdate) SetNillableEncryptedValue(v *string) *SecretUpdate { + if v != nil { + _u.SetEncryptedValue(*v) + } + return _u +} + +// SetSecretRef sets the "secret_ref" field. +func (_u *SecretUpdate) SetSecretRef(v string) *SecretUpdate { + _u.mutation.SetSecretRef(v) + return _u +} + +// SetNillableSecretRef sets the "secret_ref" field if the given value is not nil. +func (_u *SecretUpdate) SetNillableSecretRef(v *string) *SecretUpdate { + if v != nil { + _u.SetSecretRef(*v) + } + return _u +} + +// ClearSecretRef clears the value of the "secret_ref" field. +func (_u *SecretUpdate) ClearSecretRef() *SecretUpdate { + _u.mutation.ClearSecretRef() + return _u +} + +// SetSecretType sets the "secret_type" field. +func (_u *SecretUpdate) SetSecretType(v secret.SecretType) *SecretUpdate { + _u.mutation.SetSecretType(v) + return _u +} + +// SetNillableSecretType sets the "secret_type" field if the given value is not nil. +func (_u *SecretUpdate) SetNillableSecretType(v *secret.SecretType) *SecretUpdate { + if v != nil { + _u.SetSecretType(*v) + } + return _u +} + +// SetTarget sets the "target" field. +func (_u *SecretUpdate) SetTarget(v string) *SecretUpdate { + _u.mutation.SetTarget(v) + return _u +} + +// SetNillableTarget sets the "target" field if the given value is not nil. +func (_u *SecretUpdate) SetNillableTarget(v *string) *SecretUpdate { + if v != nil { + _u.SetTarget(*v) + } + return _u +} + +// ClearTarget clears the value of the "target" field. +func (_u *SecretUpdate) ClearTarget() *SecretUpdate { + _u.mutation.ClearTarget() + return _u +} + +// SetScope sets the "scope" field. +func (_u *SecretUpdate) SetScope(v string) *SecretUpdate { + _u.mutation.SetScope(v) + return _u +} + +// SetNillableScope sets the "scope" field if the given value is not nil. +func (_u *SecretUpdate) SetNillableScope(v *string) *SecretUpdate { + if v != nil { + _u.SetScope(*v) + } + return _u +} + +// SetScopeID sets the "scope_id" field. +func (_u *SecretUpdate) SetScopeID(v string) *SecretUpdate { + _u.mutation.SetScopeID(v) + return _u +} + +// SetNillableScopeID sets the "scope_id" field if the given value is not nil. +func (_u *SecretUpdate) SetNillableScopeID(v *string) *SecretUpdate { + if v != nil { + _u.SetScopeID(*v) + } + return _u +} + +// SetDescription sets the "description" field. +func (_u *SecretUpdate) SetDescription(v string) *SecretUpdate { + _u.mutation.SetDescription(v) + return _u +} + +// SetNillableDescription sets the "description" field if the given value is not nil. +func (_u *SecretUpdate) SetNillableDescription(v *string) *SecretUpdate { + if v != nil { + _u.SetDescription(*v) + } + return _u +} + +// ClearDescription clears the value of the "description" field. +func (_u *SecretUpdate) ClearDescription() *SecretUpdate { + _u.mutation.ClearDescription() + return _u +} + +// SetInjectionMode sets the "injection_mode" field. +func (_u *SecretUpdate) SetInjectionMode(v secret.InjectionMode) *SecretUpdate { + _u.mutation.SetInjectionMode(v) + return _u +} + +// SetNillableInjectionMode sets the "injection_mode" field if the given value is not nil. +func (_u *SecretUpdate) SetNillableInjectionMode(v *secret.InjectionMode) *SecretUpdate { + if v != nil { + _u.SetInjectionMode(*v) + } + return _u +} + +// SetAllowProgeny sets the "allow_progeny" field. +func (_u *SecretUpdate) SetAllowProgeny(v bool) *SecretUpdate { + _u.mutation.SetAllowProgeny(v) + return _u +} + +// SetNillableAllowProgeny sets the "allow_progeny" field if the given value is not nil. +func (_u *SecretUpdate) SetNillableAllowProgeny(v *bool) *SecretUpdate { + if v != nil { + _u.SetAllowProgeny(*v) + } + return _u +} + +// SetVersion sets the "version" field. +func (_u *SecretUpdate) SetVersion(v int) *SecretUpdate { + _u.mutation.ResetVersion() + _u.mutation.SetVersion(v) + return _u +} + +// SetNillableVersion sets the "version" field if the given value is not nil. +func (_u *SecretUpdate) SetNillableVersion(v *int) *SecretUpdate { + if v != nil { + _u.SetVersion(*v) + } + return _u +} + +// AddVersion adds value to the "version" field. +func (_u *SecretUpdate) AddVersion(v int) *SecretUpdate { + _u.mutation.AddVersion(v) + return _u +} + +// SetCreatedBy sets the "created_by" field. +func (_u *SecretUpdate) SetCreatedBy(v string) *SecretUpdate { + _u.mutation.SetCreatedBy(v) + return _u +} + +// SetNillableCreatedBy sets the "created_by" field if the given value is not nil. +func (_u *SecretUpdate) SetNillableCreatedBy(v *string) *SecretUpdate { + if v != nil { + _u.SetCreatedBy(*v) + } + return _u +} + +// ClearCreatedBy clears the value of the "created_by" field. +func (_u *SecretUpdate) ClearCreatedBy() *SecretUpdate { + _u.mutation.ClearCreatedBy() + return _u +} + +// SetUpdatedBy sets the "updated_by" field. +func (_u *SecretUpdate) SetUpdatedBy(v string) *SecretUpdate { + _u.mutation.SetUpdatedBy(v) + return _u +} + +// SetNillableUpdatedBy sets the "updated_by" field if the given value is not nil. +func (_u *SecretUpdate) SetNillableUpdatedBy(v *string) *SecretUpdate { + if v != nil { + _u.SetUpdatedBy(*v) + } + return _u +} + +// ClearUpdatedBy clears the value of the "updated_by" field. +func (_u *SecretUpdate) ClearUpdatedBy() *SecretUpdate { + _u.mutation.ClearUpdatedBy() + return _u +} + +// SetUpdated sets the "updated" field. +func (_u *SecretUpdate) SetUpdated(v time.Time) *SecretUpdate { + _u.mutation.SetUpdated(v) + return _u +} + +// Mutation returns the SecretMutation object of the builder. +func (_u *SecretUpdate) Mutation() *SecretMutation { + return _u.mutation +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (_u *SecretUpdate) Save(ctx context.Context) (int, error) { + _u.defaults() + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *SecretUpdate) SaveX(ctx context.Context) int { + affected, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (_u *SecretUpdate) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *SecretUpdate) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_u *SecretUpdate) defaults() { + if _, ok := _u.mutation.Updated(); !ok { + v := secret.UpdateDefaultUpdated() + _u.mutation.SetUpdated(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_u *SecretUpdate) check() error { + if v, ok := _u.mutation.Key(); ok { + if err := secret.KeyValidator(v); err != nil { + return &ValidationError{Name: "key", err: fmt.Errorf(`ent: validator failed for field "Secret.key": %w`, err)} + } + } + if v, ok := _u.mutation.SecretType(); ok { + if err := secret.SecretTypeValidator(v); err != nil { + return &ValidationError{Name: "secret_type", err: fmt.Errorf(`ent: validator failed for field "Secret.secret_type": %w`, err)} + } + } + if v, ok := _u.mutation.Scope(); ok { + if err := secret.ScopeValidator(v); err != nil { + return &ValidationError{Name: "scope", err: fmt.Errorf(`ent: validator failed for field "Secret.scope": %w`, err)} + } + } + if v, ok := _u.mutation.InjectionMode(); ok { + if err := secret.InjectionModeValidator(v); err != nil { + return &ValidationError{Name: "injection_mode", err: fmt.Errorf(`ent: validator failed for field "Secret.injection_mode": %w`, err)} + } + } + return nil +} + +func (_u *SecretUpdate) sqlSave(ctx context.Context) (_node int, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(secret.Table, secret.Columns, sqlgraph.NewFieldSpec(secret.FieldID, field.TypeUUID)) + if ps := _u.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := _u.mutation.Key(); ok { + _spec.SetField(secret.FieldKey, field.TypeString, value) + } + if value, ok := _u.mutation.EncryptedValue(); ok { + _spec.SetField(secret.FieldEncryptedValue, field.TypeString, value) + } + if value, ok := _u.mutation.SecretRef(); ok { + _spec.SetField(secret.FieldSecretRef, field.TypeString, value) + } + if _u.mutation.SecretRefCleared() { + _spec.ClearField(secret.FieldSecretRef, field.TypeString) + } + if value, ok := _u.mutation.SecretType(); ok { + _spec.SetField(secret.FieldSecretType, field.TypeEnum, value) + } + if value, ok := _u.mutation.Target(); ok { + _spec.SetField(secret.FieldTarget, field.TypeString, value) + } + if _u.mutation.TargetCleared() { + _spec.ClearField(secret.FieldTarget, field.TypeString) + } + if value, ok := _u.mutation.Scope(); ok { + _spec.SetField(secret.FieldScope, field.TypeString, value) + } + if value, ok := _u.mutation.ScopeID(); ok { + _spec.SetField(secret.FieldScopeID, field.TypeString, value) + } + if value, ok := _u.mutation.Description(); ok { + _spec.SetField(secret.FieldDescription, field.TypeString, value) + } + if _u.mutation.DescriptionCleared() { + _spec.ClearField(secret.FieldDescription, field.TypeString) + } + if value, ok := _u.mutation.InjectionMode(); ok { + _spec.SetField(secret.FieldInjectionMode, field.TypeEnum, value) + } + if value, ok := _u.mutation.AllowProgeny(); ok { + _spec.SetField(secret.FieldAllowProgeny, field.TypeBool, value) + } + if value, ok := _u.mutation.Version(); ok { + _spec.SetField(secret.FieldVersion, field.TypeInt, value) + } + if value, ok := _u.mutation.AddedVersion(); ok { + _spec.AddField(secret.FieldVersion, field.TypeInt, value) + } + if value, ok := _u.mutation.CreatedBy(); ok { + _spec.SetField(secret.FieldCreatedBy, field.TypeString, value) + } + if _u.mutation.CreatedByCleared() { + _spec.ClearField(secret.FieldCreatedBy, field.TypeString) + } + if value, ok := _u.mutation.UpdatedBy(); ok { + _spec.SetField(secret.FieldUpdatedBy, field.TypeString, value) + } + if _u.mutation.UpdatedByCleared() { + _spec.ClearField(secret.FieldUpdatedBy, field.TypeString) + } + if value, ok := _u.mutation.Updated(); ok { + _spec.SetField(secret.FieldUpdated, field.TypeTime, value) + } + if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{secret.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + _u.mutation.done = true + return _node, nil +} + +// SecretUpdateOne is the builder for updating a single Secret entity. +type SecretUpdateOne struct { + config + fields []string + hooks []Hook + mutation *SecretMutation +} + +// SetKey sets the "key" field. +func (_u *SecretUpdateOne) SetKey(v string) *SecretUpdateOne { + _u.mutation.SetKey(v) + return _u +} + +// SetNillableKey sets the "key" field if the given value is not nil. +func (_u *SecretUpdateOne) SetNillableKey(v *string) *SecretUpdateOne { + if v != nil { + _u.SetKey(*v) + } + return _u +} + +// SetEncryptedValue sets the "encrypted_value" field. +func (_u *SecretUpdateOne) SetEncryptedValue(v string) *SecretUpdateOne { + _u.mutation.SetEncryptedValue(v) + return _u +} + +// SetNillableEncryptedValue sets the "encrypted_value" field if the given value is not nil. +func (_u *SecretUpdateOne) SetNillableEncryptedValue(v *string) *SecretUpdateOne { + if v != nil { + _u.SetEncryptedValue(*v) + } + return _u +} + +// SetSecretRef sets the "secret_ref" field. +func (_u *SecretUpdateOne) SetSecretRef(v string) *SecretUpdateOne { + _u.mutation.SetSecretRef(v) + return _u +} + +// SetNillableSecretRef sets the "secret_ref" field if the given value is not nil. +func (_u *SecretUpdateOne) SetNillableSecretRef(v *string) *SecretUpdateOne { + if v != nil { + _u.SetSecretRef(*v) + } + return _u +} + +// ClearSecretRef clears the value of the "secret_ref" field. +func (_u *SecretUpdateOne) ClearSecretRef() *SecretUpdateOne { + _u.mutation.ClearSecretRef() + return _u +} + +// SetSecretType sets the "secret_type" field. +func (_u *SecretUpdateOne) SetSecretType(v secret.SecretType) *SecretUpdateOne { + _u.mutation.SetSecretType(v) + return _u +} + +// SetNillableSecretType sets the "secret_type" field if the given value is not nil. +func (_u *SecretUpdateOne) SetNillableSecretType(v *secret.SecretType) *SecretUpdateOne { + if v != nil { + _u.SetSecretType(*v) + } + return _u +} + +// SetTarget sets the "target" field. +func (_u *SecretUpdateOne) SetTarget(v string) *SecretUpdateOne { + _u.mutation.SetTarget(v) + return _u +} + +// SetNillableTarget sets the "target" field if the given value is not nil. +func (_u *SecretUpdateOne) SetNillableTarget(v *string) *SecretUpdateOne { + if v != nil { + _u.SetTarget(*v) + } + return _u +} + +// ClearTarget clears the value of the "target" field. +func (_u *SecretUpdateOne) ClearTarget() *SecretUpdateOne { + _u.mutation.ClearTarget() + return _u +} + +// SetScope sets the "scope" field. +func (_u *SecretUpdateOne) SetScope(v string) *SecretUpdateOne { + _u.mutation.SetScope(v) + return _u +} + +// SetNillableScope sets the "scope" field if the given value is not nil. +func (_u *SecretUpdateOne) SetNillableScope(v *string) *SecretUpdateOne { + if v != nil { + _u.SetScope(*v) + } + return _u +} + +// SetScopeID sets the "scope_id" field. +func (_u *SecretUpdateOne) SetScopeID(v string) *SecretUpdateOne { + _u.mutation.SetScopeID(v) + return _u +} + +// SetNillableScopeID sets the "scope_id" field if the given value is not nil. +func (_u *SecretUpdateOne) SetNillableScopeID(v *string) *SecretUpdateOne { + if v != nil { + _u.SetScopeID(*v) + } + return _u +} + +// SetDescription sets the "description" field. +func (_u *SecretUpdateOne) SetDescription(v string) *SecretUpdateOne { + _u.mutation.SetDescription(v) + return _u +} + +// SetNillableDescription sets the "description" field if the given value is not nil. +func (_u *SecretUpdateOne) SetNillableDescription(v *string) *SecretUpdateOne { + if v != nil { + _u.SetDescription(*v) + } + return _u +} + +// ClearDescription clears the value of the "description" field. +func (_u *SecretUpdateOne) ClearDescription() *SecretUpdateOne { + _u.mutation.ClearDescription() + return _u +} + +// SetInjectionMode sets the "injection_mode" field. +func (_u *SecretUpdateOne) SetInjectionMode(v secret.InjectionMode) *SecretUpdateOne { + _u.mutation.SetInjectionMode(v) + return _u +} + +// SetNillableInjectionMode sets the "injection_mode" field if the given value is not nil. +func (_u *SecretUpdateOne) SetNillableInjectionMode(v *secret.InjectionMode) *SecretUpdateOne { + if v != nil { + _u.SetInjectionMode(*v) + } + return _u +} + +// SetAllowProgeny sets the "allow_progeny" field. +func (_u *SecretUpdateOne) SetAllowProgeny(v bool) *SecretUpdateOne { + _u.mutation.SetAllowProgeny(v) + return _u +} + +// SetNillableAllowProgeny sets the "allow_progeny" field if the given value is not nil. +func (_u *SecretUpdateOne) SetNillableAllowProgeny(v *bool) *SecretUpdateOne { + if v != nil { + _u.SetAllowProgeny(*v) + } + return _u +} + +// SetVersion sets the "version" field. +func (_u *SecretUpdateOne) SetVersion(v int) *SecretUpdateOne { + _u.mutation.ResetVersion() + _u.mutation.SetVersion(v) + return _u +} + +// SetNillableVersion sets the "version" field if the given value is not nil. +func (_u *SecretUpdateOne) SetNillableVersion(v *int) *SecretUpdateOne { + if v != nil { + _u.SetVersion(*v) + } + return _u +} + +// AddVersion adds value to the "version" field. +func (_u *SecretUpdateOne) AddVersion(v int) *SecretUpdateOne { + _u.mutation.AddVersion(v) + return _u +} + +// SetCreatedBy sets the "created_by" field. +func (_u *SecretUpdateOne) SetCreatedBy(v string) *SecretUpdateOne { + _u.mutation.SetCreatedBy(v) + return _u +} + +// SetNillableCreatedBy sets the "created_by" field if the given value is not nil. +func (_u *SecretUpdateOne) SetNillableCreatedBy(v *string) *SecretUpdateOne { + if v != nil { + _u.SetCreatedBy(*v) + } + return _u +} + +// ClearCreatedBy clears the value of the "created_by" field. +func (_u *SecretUpdateOne) ClearCreatedBy() *SecretUpdateOne { + _u.mutation.ClearCreatedBy() + return _u +} + +// SetUpdatedBy sets the "updated_by" field. +func (_u *SecretUpdateOne) SetUpdatedBy(v string) *SecretUpdateOne { + _u.mutation.SetUpdatedBy(v) + return _u +} + +// SetNillableUpdatedBy sets the "updated_by" field if the given value is not nil. +func (_u *SecretUpdateOne) SetNillableUpdatedBy(v *string) *SecretUpdateOne { + if v != nil { + _u.SetUpdatedBy(*v) + } + return _u +} + +// ClearUpdatedBy clears the value of the "updated_by" field. +func (_u *SecretUpdateOne) ClearUpdatedBy() *SecretUpdateOne { + _u.mutation.ClearUpdatedBy() + return _u +} + +// SetUpdated sets the "updated" field. +func (_u *SecretUpdateOne) SetUpdated(v time.Time) *SecretUpdateOne { + _u.mutation.SetUpdated(v) + return _u +} + +// Mutation returns the SecretMutation object of the builder. +func (_u *SecretUpdateOne) Mutation() *SecretMutation { + return _u.mutation +} + +// Where appends a list predicates to the SecretUpdate builder. +func (_u *SecretUpdateOne) Where(ps ...predicate.Secret) *SecretUpdateOne { + _u.mutation.Where(ps...) + return _u +} + +// Select allows selecting one or more fields (columns) of the returned entity. +// The default is selecting all fields defined in the entity schema. +func (_u *SecretUpdateOne) Select(field string, fields ...string) *SecretUpdateOne { + _u.fields = append([]string{field}, fields...) + return _u +} + +// Save executes the query and returns the updated Secret entity. +func (_u *SecretUpdateOne) Save(ctx context.Context) (*Secret, error) { + _u.defaults() + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *SecretUpdateOne) SaveX(ctx context.Context) *Secret { + node, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (_u *SecretUpdateOne) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *SecretUpdateOne) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_u *SecretUpdateOne) defaults() { + if _, ok := _u.mutation.Updated(); !ok { + v := secret.UpdateDefaultUpdated() + _u.mutation.SetUpdated(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_u *SecretUpdateOne) check() error { + if v, ok := _u.mutation.Key(); ok { + if err := secret.KeyValidator(v); err != nil { + return &ValidationError{Name: "key", err: fmt.Errorf(`ent: validator failed for field "Secret.key": %w`, err)} + } + } + if v, ok := _u.mutation.SecretType(); ok { + if err := secret.SecretTypeValidator(v); err != nil { + return &ValidationError{Name: "secret_type", err: fmt.Errorf(`ent: validator failed for field "Secret.secret_type": %w`, err)} + } + } + if v, ok := _u.mutation.Scope(); ok { + if err := secret.ScopeValidator(v); err != nil { + return &ValidationError{Name: "scope", err: fmt.Errorf(`ent: validator failed for field "Secret.scope": %w`, err)} + } + } + if v, ok := _u.mutation.InjectionMode(); ok { + if err := secret.InjectionModeValidator(v); err != nil { + return &ValidationError{Name: "injection_mode", err: fmt.Errorf(`ent: validator failed for field "Secret.injection_mode": %w`, err)} + } + } + return nil +} + +func (_u *SecretUpdateOne) sqlSave(ctx context.Context) (_node *Secret, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(secret.Table, secret.Columns, sqlgraph.NewFieldSpec(secret.FieldID, field.TypeUUID)) + id, ok := _u.mutation.ID() + if !ok { + return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "Secret.id" for update`)} + } + _spec.Node.ID.Value = id + if fields := _u.fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, secret.FieldID) + for _, f := range fields { + if !secret.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + if f != secret.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, f) + } + } + } + if ps := _u.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := _u.mutation.Key(); ok { + _spec.SetField(secret.FieldKey, field.TypeString, value) + } + if value, ok := _u.mutation.EncryptedValue(); ok { + _spec.SetField(secret.FieldEncryptedValue, field.TypeString, value) + } + if value, ok := _u.mutation.SecretRef(); ok { + _spec.SetField(secret.FieldSecretRef, field.TypeString, value) + } + if _u.mutation.SecretRefCleared() { + _spec.ClearField(secret.FieldSecretRef, field.TypeString) + } + if value, ok := _u.mutation.SecretType(); ok { + _spec.SetField(secret.FieldSecretType, field.TypeEnum, value) + } + if value, ok := _u.mutation.Target(); ok { + _spec.SetField(secret.FieldTarget, field.TypeString, value) + } + if _u.mutation.TargetCleared() { + _spec.ClearField(secret.FieldTarget, field.TypeString) + } + if value, ok := _u.mutation.Scope(); ok { + _spec.SetField(secret.FieldScope, field.TypeString, value) + } + if value, ok := _u.mutation.ScopeID(); ok { + _spec.SetField(secret.FieldScopeID, field.TypeString, value) + } + if value, ok := _u.mutation.Description(); ok { + _spec.SetField(secret.FieldDescription, field.TypeString, value) + } + if _u.mutation.DescriptionCleared() { + _spec.ClearField(secret.FieldDescription, field.TypeString) + } + if value, ok := _u.mutation.InjectionMode(); ok { + _spec.SetField(secret.FieldInjectionMode, field.TypeEnum, value) + } + if value, ok := _u.mutation.AllowProgeny(); ok { + _spec.SetField(secret.FieldAllowProgeny, field.TypeBool, value) + } + if value, ok := _u.mutation.Version(); ok { + _spec.SetField(secret.FieldVersion, field.TypeInt, value) + } + if value, ok := _u.mutation.AddedVersion(); ok { + _spec.AddField(secret.FieldVersion, field.TypeInt, value) + } + if value, ok := _u.mutation.CreatedBy(); ok { + _spec.SetField(secret.FieldCreatedBy, field.TypeString, value) + } + if _u.mutation.CreatedByCleared() { + _spec.ClearField(secret.FieldCreatedBy, field.TypeString) + } + if value, ok := _u.mutation.UpdatedBy(); ok { + _spec.SetField(secret.FieldUpdatedBy, field.TypeString, value) + } + if _u.mutation.UpdatedByCleared() { + _spec.ClearField(secret.FieldUpdatedBy, field.TypeString) + } + if value, ok := _u.mutation.Updated(); ok { + _spec.SetField(secret.FieldUpdated, field.TypeTime, value) + } + _node = &Secret{config: _u.config} + _spec.Assign = _node.assignValues + _spec.ScanValues = _node.scanValues + if err = sqlgraph.UpdateNode(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{secret.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + _u.mutation.done = true + return _node, nil +} diff --git a/pkg/ent/subscriptiontemplate.go b/pkg/ent/subscriptiontemplate.go new file mode 100644 index 000000000..ee67c9fce --- /dev/null +++ b/pkg/ent/subscriptiontemplate.go @@ -0,0 +1,153 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "fmt" + "strings" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "github.com/GoogleCloudPlatform/scion/pkg/ent/subscriptiontemplate" + "github.com/google/uuid" +) + +// SubscriptionTemplate is the model entity for the SubscriptionTemplate schema. +type SubscriptionTemplate struct { + config `json:"-"` + // ID of the ent. + ID uuid.UUID `json:"id,omitempty"` + // Name holds the value of the "name" field. + Name string `json:"name,omitempty"` + // Scope holds the value of the "scope" field. + Scope string `json:"scope,omitempty"` + // TriggerActivities holds the value of the "trigger_activities" field. + TriggerActivities string `json:"trigger_activities,omitempty"` + // ProjectID holds the value of the "project_id" field. + ProjectID *uuid.UUID `json:"project_id,omitempty"` + // CreatedBy holds the value of the "created_by" field. + CreatedBy string `json:"created_by,omitempty"` + selectValues sql.SelectValues +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*SubscriptionTemplate) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case subscriptiontemplate.FieldProjectID: + values[i] = &sql.NullScanner{S: new(uuid.UUID)} + case subscriptiontemplate.FieldName, subscriptiontemplate.FieldScope, subscriptiontemplate.FieldTriggerActivities, subscriptiontemplate.FieldCreatedBy: + values[i] = new(sql.NullString) + case subscriptiontemplate.FieldID: + values[i] = new(uuid.UUID) + default: + values[i] = new(sql.UnknownType) + } + } + return values, nil +} + +// assignValues assigns the values that were returned from sql.Rows (after scanning) +// to the SubscriptionTemplate fields. +func (_m *SubscriptionTemplate) assignValues(columns []string, values []any) error { + if m, n := len(values), len(columns); m < n { + return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) + } + for i := range columns { + switch columns[i] { + case subscriptiontemplate.FieldID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field id", values[i]) + } else if value != nil { + _m.ID = *value + } + case subscriptiontemplate.FieldName: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field name", values[i]) + } else if value.Valid { + _m.Name = value.String + } + case subscriptiontemplate.FieldScope: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field scope", values[i]) + } else if value.Valid { + _m.Scope = value.String + } + case subscriptiontemplate.FieldTriggerActivities: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field trigger_activities", values[i]) + } else if value.Valid { + _m.TriggerActivities = value.String + } + case subscriptiontemplate.FieldProjectID: + if value, ok := values[i].(*sql.NullScanner); !ok { + return fmt.Errorf("unexpected type %T for field project_id", values[i]) + } else if value.Valid { + _m.ProjectID = new(uuid.UUID) + *_m.ProjectID = *value.S.(*uuid.UUID) + } + case subscriptiontemplate.FieldCreatedBy: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field created_by", values[i]) + } else if value.Valid { + _m.CreatedBy = value.String + } + default: + _m.selectValues.Set(columns[i], values[i]) + } + } + return nil +} + +// Value returns the ent.Value that was dynamically selected and assigned to the SubscriptionTemplate. +// This includes values selected through modifiers, order, etc. +func (_m *SubscriptionTemplate) Value(name string) (ent.Value, error) { + return _m.selectValues.Get(name) +} + +// Update returns a builder for updating this SubscriptionTemplate. +// Note that you need to call SubscriptionTemplate.Unwrap() before calling this method if this SubscriptionTemplate +// was returned from a transaction, and the transaction was committed or rolled back. +func (_m *SubscriptionTemplate) Update() *SubscriptionTemplateUpdateOne { + return NewSubscriptionTemplateClient(_m.config).UpdateOne(_m) +} + +// Unwrap unwraps the SubscriptionTemplate entity that was returned from a transaction after it was closed, +// so that all future queries will be executed through the driver which created the transaction. +func (_m *SubscriptionTemplate) Unwrap() *SubscriptionTemplate { + _tx, ok := _m.config.driver.(*txDriver) + if !ok { + panic("ent: SubscriptionTemplate is not a transactional entity") + } + _m.config.driver = _tx.drv + return _m +} + +// String implements the fmt.Stringer. +func (_m *SubscriptionTemplate) String() string { + var builder strings.Builder + builder.WriteString("SubscriptionTemplate(") + builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID)) + builder.WriteString("name=") + builder.WriteString(_m.Name) + builder.WriteString(", ") + builder.WriteString("scope=") + builder.WriteString(_m.Scope) + builder.WriteString(", ") + builder.WriteString("trigger_activities=") + builder.WriteString(_m.TriggerActivities) + builder.WriteString(", ") + if v := _m.ProjectID; v != nil { + builder.WriteString("project_id=") + builder.WriteString(fmt.Sprintf("%v", *v)) + } + builder.WriteString(", ") + builder.WriteString("created_by=") + builder.WriteString(_m.CreatedBy) + builder.WriteByte(')') + return builder.String() +} + +// SubscriptionTemplates is a parsable slice of SubscriptionTemplate. +type SubscriptionTemplates []*SubscriptionTemplate diff --git a/pkg/ent/subscriptiontemplate/subscriptiontemplate.go b/pkg/ent/subscriptiontemplate/subscriptiontemplate.go new file mode 100644 index 000000000..4a8ed211c --- /dev/null +++ b/pkg/ent/subscriptiontemplate/subscriptiontemplate.go @@ -0,0 +1,93 @@ +// Code generated by ent, DO NOT EDIT. + +package subscriptiontemplate + +import ( + "entgo.io/ent/dialect/sql" + "github.com/google/uuid" +) + +const ( + // Label holds the string label denoting the subscriptiontemplate type in the database. + Label = "subscription_template" + // FieldID holds the string denoting the id field in the database. + FieldID = "id" + // FieldName holds the string denoting the name field in the database. + FieldName = "name" + // FieldScope holds the string denoting the scope field in the database. + FieldScope = "scope" + // FieldTriggerActivities holds the string denoting the trigger_activities field in the database. + FieldTriggerActivities = "trigger_activities" + // FieldProjectID holds the string denoting the project_id field in the database. + FieldProjectID = "project_id" + // FieldCreatedBy holds the string denoting the created_by field in the database. + FieldCreatedBy = "created_by" + // Table holds the table name of the subscriptiontemplate in the database. + Table = "subscription_templates" +) + +// Columns holds all SQL columns for subscriptiontemplate fields. +var Columns = []string{ + FieldID, + FieldName, + FieldScope, + FieldTriggerActivities, + FieldProjectID, + FieldCreatedBy, +} + +// ValidColumn reports if the column name is valid (part of the table columns). +func ValidColumn(column string) bool { + for i := range Columns { + if column == Columns[i] { + return true + } + } + return false +} + +var ( + // NameValidator is a validator for the "name" field. It is called by the builders before save. + NameValidator func(string) error + // DefaultScope holds the default value on creation for the "scope" field. + DefaultScope string + // TriggerActivitiesValidator is a validator for the "trigger_activities" field. It is called by the builders before save. + TriggerActivitiesValidator func(string) error + // CreatedByValidator is a validator for the "created_by" field. It is called by the builders before save. + CreatedByValidator func(string) error + // DefaultID holds the default value on creation for the "id" field. + DefaultID func() uuid.UUID +) + +// OrderOption defines the ordering options for the SubscriptionTemplate queries. +type OrderOption func(*sql.Selector) + +// ByID orders the results by the id field. +func ByID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldID, opts...).ToFunc() +} + +// ByName orders the results by the name field. +func ByName(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldName, opts...).ToFunc() +} + +// ByScope orders the results by the scope field. +func ByScope(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldScope, opts...).ToFunc() +} + +// ByTriggerActivities orders the results by the trigger_activities field. +func ByTriggerActivities(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldTriggerActivities, opts...).ToFunc() +} + +// ByProjectID orders the results by the project_id field. +func ByProjectID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldProjectID, opts...).ToFunc() +} + +// ByCreatedBy orders the results by the created_by field. +func ByCreatedBy(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCreatedBy, opts...).ToFunc() +} diff --git a/pkg/ent/subscriptiontemplate/where.go b/pkg/ent/subscriptiontemplate/where.go new file mode 100644 index 000000000..0afd24e9e --- /dev/null +++ b/pkg/ent/subscriptiontemplate/where.go @@ -0,0 +1,404 @@ +// Code generated by ent, DO NOT EDIT. + +package subscriptiontemplate + +import ( + "entgo.io/ent/dialect/sql" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/google/uuid" +) + +// ID filters vertices based on their ID field. +func ID(id uuid.UUID) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldEQ(FieldID, id)) +} + +// IDEQ applies the EQ predicate on the ID field. +func IDEQ(id uuid.UUID) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldEQ(FieldID, id)) +} + +// IDNEQ applies the NEQ predicate on the ID field. +func IDNEQ(id uuid.UUID) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldNEQ(FieldID, id)) +} + +// IDIn applies the In predicate on the ID field. +func IDIn(ids ...uuid.UUID) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldIn(FieldID, ids...)) +} + +// IDNotIn applies the NotIn predicate on the ID field. +func IDNotIn(ids ...uuid.UUID) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldNotIn(FieldID, ids...)) +} + +// IDGT applies the GT predicate on the ID field. +func IDGT(id uuid.UUID) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldGT(FieldID, id)) +} + +// IDGTE applies the GTE predicate on the ID field. +func IDGTE(id uuid.UUID) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldGTE(FieldID, id)) +} + +// IDLT applies the LT predicate on the ID field. +func IDLT(id uuid.UUID) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldLT(FieldID, id)) +} + +// IDLTE applies the LTE predicate on the ID field. +func IDLTE(id uuid.UUID) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldLTE(FieldID, id)) +} + +// Name applies equality check predicate on the "name" field. It's identical to NameEQ. +func Name(v string) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldEQ(FieldName, v)) +} + +// Scope applies equality check predicate on the "scope" field. It's identical to ScopeEQ. +func Scope(v string) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldEQ(FieldScope, v)) +} + +// TriggerActivities applies equality check predicate on the "trigger_activities" field. It's identical to TriggerActivitiesEQ. +func TriggerActivities(v string) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldEQ(FieldTriggerActivities, v)) +} + +// ProjectID applies equality check predicate on the "project_id" field. It's identical to ProjectIDEQ. +func ProjectID(v uuid.UUID) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldEQ(FieldProjectID, v)) +} + +// CreatedBy applies equality check predicate on the "created_by" field. It's identical to CreatedByEQ. +func CreatedBy(v string) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldEQ(FieldCreatedBy, v)) +} + +// NameEQ applies the EQ predicate on the "name" field. +func NameEQ(v string) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldEQ(FieldName, v)) +} + +// NameNEQ applies the NEQ predicate on the "name" field. +func NameNEQ(v string) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldNEQ(FieldName, v)) +} + +// NameIn applies the In predicate on the "name" field. +func NameIn(vs ...string) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldIn(FieldName, vs...)) +} + +// NameNotIn applies the NotIn predicate on the "name" field. +func NameNotIn(vs ...string) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldNotIn(FieldName, vs...)) +} + +// NameGT applies the GT predicate on the "name" field. +func NameGT(v string) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldGT(FieldName, v)) +} + +// NameGTE applies the GTE predicate on the "name" field. +func NameGTE(v string) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldGTE(FieldName, v)) +} + +// NameLT applies the LT predicate on the "name" field. +func NameLT(v string) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldLT(FieldName, v)) +} + +// NameLTE applies the LTE predicate on the "name" field. +func NameLTE(v string) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldLTE(FieldName, v)) +} + +// NameContains applies the Contains predicate on the "name" field. +func NameContains(v string) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldContains(FieldName, v)) +} + +// NameHasPrefix applies the HasPrefix predicate on the "name" field. +func NameHasPrefix(v string) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldHasPrefix(FieldName, v)) +} + +// NameHasSuffix applies the HasSuffix predicate on the "name" field. +func NameHasSuffix(v string) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldHasSuffix(FieldName, v)) +} + +// NameEqualFold applies the EqualFold predicate on the "name" field. +func NameEqualFold(v string) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldEqualFold(FieldName, v)) +} + +// NameContainsFold applies the ContainsFold predicate on the "name" field. +func NameContainsFold(v string) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldContainsFold(FieldName, v)) +} + +// ScopeEQ applies the EQ predicate on the "scope" field. +func ScopeEQ(v string) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldEQ(FieldScope, v)) +} + +// ScopeNEQ applies the NEQ predicate on the "scope" field. +func ScopeNEQ(v string) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldNEQ(FieldScope, v)) +} + +// ScopeIn applies the In predicate on the "scope" field. +func ScopeIn(vs ...string) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldIn(FieldScope, vs...)) +} + +// ScopeNotIn applies the NotIn predicate on the "scope" field. +func ScopeNotIn(vs ...string) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldNotIn(FieldScope, vs...)) +} + +// ScopeGT applies the GT predicate on the "scope" field. +func ScopeGT(v string) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldGT(FieldScope, v)) +} + +// ScopeGTE applies the GTE predicate on the "scope" field. +func ScopeGTE(v string) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldGTE(FieldScope, v)) +} + +// ScopeLT applies the LT predicate on the "scope" field. +func ScopeLT(v string) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldLT(FieldScope, v)) +} + +// ScopeLTE applies the LTE predicate on the "scope" field. +func ScopeLTE(v string) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldLTE(FieldScope, v)) +} + +// ScopeContains applies the Contains predicate on the "scope" field. +func ScopeContains(v string) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldContains(FieldScope, v)) +} + +// ScopeHasPrefix applies the HasPrefix predicate on the "scope" field. +func ScopeHasPrefix(v string) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldHasPrefix(FieldScope, v)) +} + +// ScopeHasSuffix applies the HasSuffix predicate on the "scope" field. +func ScopeHasSuffix(v string) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldHasSuffix(FieldScope, v)) +} + +// ScopeEqualFold applies the EqualFold predicate on the "scope" field. +func ScopeEqualFold(v string) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldEqualFold(FieldScope, v)) +} + +// ScopeContainsFold applies the ContainsFold predicate on the "scope" field. +func ScopeContainsFold(v string) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldContainsFold(FieldScope, v)) +} + +// TriggerActivitiesEQ applies the EQ predicate on the "trigger_activities" field. +func TriggerActivitiesEQ(v string) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldEQ(FieldTriggerActivities, v)) +} + +// TriggerActivitiesNEQ applies the NEQ predicate on the "trigger_activities" field. +func TriggerActivitiesNEQ(v string) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldNEQ(FieldTriggerActivities, v)) +} + +// TriggerActivitiesIn applies the In predicate on the "trigger_activities" field. +func TriggerActivitiesIn(vs ...string) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldIn(FieldTriggerActivities, vs...)) +} + +// TriggerActivitiesNotIn applies the NotIn predicate on the "trigger_activities" field. +func TriggerActivitiesNotIn(vs ...string) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldNotIn(FieldTriggerActivities, vs...)) +} + +// TriggerActivitiesGT applies the GT predicate on the "trigger_activities" field. +func TriggerActivitiesGT(v string) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldGT(FieldTriggerActivities, v)) +} + +// TriggerActivitiesGTE applies the GTE predicate on the "trigger_activities" field. +func TriggerActivitiesGTE(v string) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldGTE(FieldTriggerActivities, v)) +} + +// TriggerActivitiesLT applies the LT predicate on the "trigger_activities" field. +func TriggerActivitiesLT(v string) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldLT(FieldTriggerActivities, v)) +} + +// TriggerActivitiesLTE applies the LTE predicate on the "trigger_activities" field. +func TriggerActivitiesLTE(v string) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldLTE(FieldTriggerActivities, v)) +} + +// TriggerActivitiesContains applies the Contains predicate on the "trigger_activities" field. +func TriggerActivitiesContains(v string) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldContains(FieldTriggerActivities, v)) +} + +// TriggerActivitiesHasPrefix applies the HasPrefix predicate on the "trigger_activities" field. +func TriggerActivitiesHasPrefix(v string) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldHasPrefix(FieldTriggerActivities, v)) +} + +// TriggerActivitiesHasSuffix applies the HasSuffix predicate on the "trigger_activities" field. +func TriggerActivitiesHasSuffix(v string) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldHasSuffix(FieldTriggerActivities, v)) +} + +// TriggerActivitiesEqualFold applies the EqualFold predicate on the "trigger_activities" field. +func TriggerActivitiesEqualFold(v string) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldEqualFold(FieldTriggerActivities, v)) +} + +// TriggerActivitiesContainsFold applies the ContainsFold predicate on the "trigger_activities" field. +func TriggerActivitiesContainsFold(v string) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldContainsFold(FieldTriggerActivities, v)) +} + +// ProjectIDEQ applies the EQ predicate on the "project_id" field. +func ProjectIDEQ(v uuid.UUID) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldEQ(FieldProjectID, v)) +} + +// ProjectIDNEQ applies the NEQ predicate on the "project_id" field. +func ProjectIDNEQ(v uuid.UUID) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldNEQ(FieldProjectID, v)) +} + +// ProjectIDIn applies the In predicate on the "project_id" field. +func ProjectIDIn(vs ...uuid.UUID) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldIn(FieldProjectID, vs...)) +} + +// ProjectIDNotIn applies the NotIn predicate on the "project_id" field. +func ProjectIDNotIn(vs ...uuid.UUID) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldNotIn(FieldProjectID, vs...)) +} + +// ProjectIDGT applies the GT predicate on the "project_id" field. +func ProjectIDGT(v uuid.UUID) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldGT(FieldProjectID, v)) +} + +// ProjectIDGTE applies the GTE predicate on the "project_id" field. +func ProjectIDGTE(v uuid.UUID) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldGTE(FieldProjectID, v)) +} + +// ProjectIDLT applies the LT predicate on the "project_id" field. +func ProjectIDLT(v uuid.UUID) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldLT(FieldProjectID, v)) +} + +// ProjectIDLTE applies the LTE predicate on the "project_id" field. +func ProjectIDLTE(v uuid.UUID) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldLTE(FieldProjectID, v)) +} + +// ProjectIDIsNil applies the IsNil predicate on the "project_id" field. +func ProjectIDIsNil() predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldIsNull(FieldProjectID)) +} + +// ProjectIDNotNil applies the NotNil predicate on the "project_id" field. +func ProjectIDNotNil() predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldNotNull(FieldProjectID)) +} + +// CreatedByEQ applies the EQ predicate on the "created_by" field. +func CreatedByEQ(v string) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldEQ(FieldCreatedBy, v)) +} + +// CreatedByNEQ applies the NEQ predicate on the "created_by" field. +func CreatedByNEQ(v string) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldNEQ(FieldCreatedBy, v)) +} + +// CreatedByIn applies the In predicate on the "created_by" field. +func CreatedByIn(vs ...string) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldIn(FieldCreatedBy, vs...)) +} + +// CreatedByNotIn applies the NotIn predicate on the "created_by" field. +func CreatedByNotIn(vs ...string) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldNotIn(FieldCreatedBy, vs...)) +} + +// CreatedByGT applies the GT predicate on the "created_by" field. +func CreatedByGT(v string) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldGT(FieldCreatedBy, v)) +} + +// CreatedByGTE applies the GTE predicate on the "created_by" field. +func CreatedByGTE(v string) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldGTE(FieldCreatedBy, v)) +} + +// CreatedByLT applies the LT predicate on the "created_by" field. +func CreatedByLT(v string) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldLT(FieldCreatedBy, v)) +} + +// CreatedByLTE applies the LTE predicate on the "created_by" field. +func CreatedByLTE(v string) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldLTE(FieldCreatedBy, v)) +} + +// CreatedByContains applies the Contains predicate on the "created_by" field. +func CreatedByContains(v string) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldContains(FieldCreatedBy, v)) +} + +// CreatedByHasPrefix applies the HasPrefix predicate on the "created_by" field. +func CreatedByHasPrefix(v string) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldHasPrefix(FieldCreatedBy, v)) +} + +// CreatedByHasSuffix applies the HasSuffix predicate on the "created_by" field. +func CreatedByHasSuffix(v string) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldHasSuffix(FieldCreatedBy, v)) +} + +// CreatedByEqualFold applies the EqualFold predicate on the "created_by" field. +func CreatedByEqualFold(v string) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldEqualFold(FieldCreatedBy, v)) +} + +// CreatedByContainsFold applies the ContainsFold predicate on the "created_by" field. +func CreatedByContainsFold(v string) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.FieldContainsFold(FieldCreatedBy, v)) +} + +// And groups predicates with the AND operator between them. +func And(predicates ...predicate.SubscriptionTemplate) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.AndPredicates(predicates...)) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.SubscriptionTemplate) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.OrPredicates(predicates...)) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.SubscriptionTemplate) predicate.SubscriptionTemplate { + return predicate.SubscriptionTemplate(sql.NotPredicates(p)) +} diff --git a/pkg/ent/subscriptiontemplate_create.go b/pkg/ent/subscriptiontemplate_create.go new file mode 100644 index 000000000..c45d33af7 --- /dev/null +++ b/pkg/ent/subscriptiontemplate_create.go @@ -0,0 +1,772 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/subscriptiontemplate" + "github.com/google/uuid" +) + +// SubscriptionTemplateCreate is the builder for creating a SubscriptionTemplate entity. +type SubscriptionTemplateCreate struct { + config + mutation *SubscriptionTemplateMutation + hooks []Hook + conflict []sql.ConflictOption +} + +// SetName sets the "name" field. +func (_c *SubscriptionTemplateCreate) SetName(v string) *SubscriptionTemplateCreate { + _c.mutation.SetName(v) + return _c +} + +// SetScope sets the "scope" field. +func (_c *SubscriptionTemplateCreate) SetScope(v string) *SubscriptionTemplateCreate { + _c.mutation.SetScope(v) + return _c +} + +// SetNillableScope sets the "scope" field if the given value is not nil. +func (_c *SubscriptionTemplateCreate) SetNillableScope(v *string) *SubscriptionTemplateCreate { + if v != nil { + _c.SetScope(*v) + } + return _c +} + +// SetTriggerActivities sets the "trigger_activities" field. +func (_c *SubscriptionTemplateCreate) SetTriggerActivities(v string) *SubscriptionTemplateCreate { + _c.mutation.SetTriggerActivities(v) + return _c +} + +// SetProjectID sets the "project_id" field. +func (_c *SubscriptionTemplateCreate) SetProjectID(v uuid.UUID) *SubscriptionTemplateCreate { + _c.mutation.SetProjectID(v) + return _c +} + +// SetNillableProjectID sets the "project_id" field if the given value is not nil. +func (_c *SubscriptionTemplateCreate) SetNillableProjectID(v *uuid.UUID) *SubscriptionTemplateCreate { + if v != nil { + _c.SetProjectID(*v) + } + return _c +} + +// SetCreatedBy sets the "created_by" field. +func (_c *SubscriptionTemplateCreate) SetCreatedBy(v string) *SubscriptionTemplateCreate { + _c.mutation.SetCreatedBy(v) + return _c +} + +// SetID sets the "id" field. +func (_c *SubscriptionTemplateCreate) SetID(v uuid.UUID) *SubscriptionTemplateCreate { + _c.mutation.SetID(v) + return _c +} + +// SetNillableID sets the "id" field if the given value is not nil. +func (_c *SubscriptionTemplateCreate) SetNillableID(v *uuid.UUID) *SubscriptionTemplateCreate { + if v != nil { + _c.SetID(*v) + } + return _c +} + +// Mutation returns the SubscriptionTemplateMutation object of the builder. +func (_c *SubscriptionTemplateCreate) Mutation() *SubscriptionTemplateMutation { + return _c.mutation +} + +// Save creates the SubscriptionTemplate in the database. +func (_c *SubscriptionTemplateCreate) Save(ctx context.Context) (*SubscriptionTemplate, error) { + _c.defaults() + return withHooks(ctx, _c.sqlSave, _c.mutation, _c.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (_c *SubscriptionTemplateCreate) SaveX(ctx context.Context) *SubscriptionTemplate { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *SubscriptionTemplateCreate) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *SubscriptionTemplateCreate) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_c *SubscriptionTemplateCreate) defaults() { + if _, ok := _c.mutation.Scope(); !ok { + v := subscriptiontemplate.DefaultScope + _c.mutation.SetScope(v) + } + if _, ok := _c.mutation.ID(); !ok { + v := subscriptiontemplate.DefaultID() + _c.mutation.SetID(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_c *SubscriptionTemplateCreate) check() error { + if _, ok := _c.mutation.Name(); !ok { + return &ValidationError{Name: "name", err: errors.New(`ent: missing required field "SubscriptionTemplate.name"`)} + } + if v, ok := _c.mutation.Name(); ok { + if err := subscriptiontemplate.NameValidator(v); err != nil { + return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "SubscriptionTemplate.name": %w`, err)} + } + } + if _, ok := _c.mutation.Scope(); !ok { + return &ValidationError{Name: "scope", err: errors.New(`ent: missing required field "SubscriptionTemplate.scope"`)} + } + if _, ok := _c.mutation.TriggerActivities(); !ok { + return &ValidationError{Name: "trigger_activities", err: errors.New(`ent: missing required field "SubscriptionTemplate.trigger_activities"`)} + } + if v, ok := _c.mutation.TriggerActivities(); ok { + if err := subscriptiontemplate.TriggerActivitiesValidator(v); err != nil { + return &ValidationError{Name: "trigger_activities", err: fmt.Errorf(`ent: validator failed for field "SubscriptionTemplate.trigger_activities": %w`, err)} + } + } + if _, ok := _c.mutation.CreatedBy(); !ok { + return &ValidationError{Name: "created_by", err: errors.New(`ent: missing required field "SubscriptionTemplate.created_by"`)} + } + if v, ok := _c.mutation.CreatedBy(); ok { + if err := subscriptiontemplate.CreatedByValidator(v); err != nil { + return &ValidationError{Name: "created_by", err: fmt.Errorf(`ent: validator failed for field "SubscriptionTemplate.created_by": %w`, err)} + } + } + return nil +} + +func (_c *SubscriptionTemplateCreate) sqlSave(ctx context.Context) (*SubscriptionTemplate, error) { + if err := _c.check(); err != nil { + return nil, err + } + _node, _spec := _c.createSpec() + if err := sqlgraph.CreateNode(ctx, _c.driver, _spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + if _spec.ID.Value != nil { + if id, ok := _spec.ID.Value.(*uuid.UUID); ok { + _node.ID = *id + } else if err := _node.ID.Scan(_spec.ID.Value); err != nil { + return nil, err + } + } + _c.mutation.id = &_node.ID + _c.mutation.done = true + return _node, nil +} + +func (_c *SubscriptionTemplateCreate) createSpec() (*SubscriptionTemplate, *sqlgraph.CreateSpec) { + var ( + _node = &SubscriptionTemplate{config: _c.config} + _spec = sqlgraph.NewCreateSpec(subscriptiontemplate.Table, sqlgraph.NewFieldSpec(subscriptiontemplate.FieldID, field.TypeUUID)) + ) + _spec.OnConflict = _c.conflict + if id, ok := _c.mutation.ID(); ok { + _node.ID = id + _spec.ID.Value = &id + } + if value, ok := _c.mutation.Name(); ok { + _spec.SetField(subscriptiontemplate.FieldName, field.TypeString, value) + _node.Name = value + } + if value, ok := _c.mutation.Scope(); ok { + _spec.SetField(subscriptiontemplate.FieldScope, field.TypeString, value) + _node.Scope = value + } + if value, ok := _c.mutation.TriggerActivities(); ok { + _spec.SetField(subscriptiontemplate.FieldTriggerActivities, field.TypeString, value) + _node.TriggerActivities = value + } + if value, ok := _c.mutation.ProjectID(); ok { + _spec.SetField(subscriptiontemplate.FieldProjectID, field.TypeUUID, value) + _node.ProjectID = &value + } + if value, ok := _c.mutation.CreatedBy(); ok { + _spec.SetField(subscriptiontemplate.FieldCreatedBy, field.TypeString, value) + _node.CreatedBy = value + } + return _node, _spec +} + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.SubscriptionTemplate.Create(). +// SetName(v). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.SubscriptionTemplateUpsert) { +// SetName(v+v). +// }). +// Exec(ctx) +func (_c *SubscriptionTemplateCreate) OnConflict(opts ...sql.ConflictOption) *SubscriptionTemplateUpsertOne { + _c.conflict = opts + return &SubscriptionTemplateUpsertOne{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.SubscriptionTemplate.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *SubscriptionTemplateCreate) OnConflictColumns(columns ...string) *SubscriptionTemplateUpsertOne { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &SubscriptionTemplateUpsertOne{ + create: _c, + } +} + +type ( + // SubscriptionTemplateUpsertOne is the builder for "upsert"-ing + // one SubscriptionTemplate node. + SubscriptionTemplateUpsertOne struct { + create *SubscriptionTemplateCreate + } + + // SubscriptionTemplateUpsert is the "OnConflict" setter. + SubscriptionTemplateUpsert struct { + *sql.UpdateSet + } +) + +// SetName sets the "name" field. +func (u *SubscriptionTemplateUpsert) SetName(v string) *SubscriptionTemplateUpsert { + u.Set(subscriptiontemplate.FieldName, v) + return u +} + +// UpdateName sets the "name" field to the value that was provided on create. +func (u *SubscriptionTemplateUpsert) UpdateName() *SubscriptionTemplateUpsert { + u.SetExcluded(subscriptiontemplate.FieldName) + return u +} + +// SetScope sets the "scope" field. +func (u *SubscriptionTemplateUpsert) SetScope(v string) *SubscriptionTemplateUpsert { + u.Set(subscriptiontemplate.FieldScope, v) + return u +} + +// UpdateScope sets the "scope" field to the value that was provided on create. +func (u *SubscriptionTemplateUpsert) UpdateScope() *SubscriptionTemplateUpsert { + u.SetExcluded(subscriptiontemplate.FieldScope) + return u +} + +// SetTriggerActivities sets the "trigger_activities" field. +func (u *SubscriptionTemplateUpsert) SetTriggerActivities(v string) *SubscriptionTemplateUpsert { + u.Set(subscriptiontemplate.FieldTriggerActivities, v) + return u +} + +// UpdateTriggerActivities sets the "trigger_activities" field to the value that was provided on create. +func (u *SubscriptionTemplateUpsert) UpdateTriggerActivities() *SubscriptionTemplateUpsert { + u.SetExcluded(subscriptiontemplate.FieldTriggerActivities) + return u +} + +// SetProjectID sets the "project_id" field. +func (u *SubscriptionTemplateUpsert) SetProjectID(v uuid.UUID) *SubscriptionTemplateUpsert { + u.Set(subscriptiontemplate.FieldProjectID, v) + return u +} + +// UpdateProjectID sets the "project_id" field to the value that was provided on create. +func (u *SubscriptionTemplateUpsert) UpdateProjectID() *SubscriptionTemplateUpsert { + u.SetExcluded(subscriptiontemplate.FieldProjectID) + return u +} + +// ClearProjectID clears the value of the "project_id" field. +func (u *SubscriptionTemplateUpsert) ClearProjectID() *SubscriptionTemplateUpsert { + u.SetNull(subscriptiontemplate.FieldProjectID) + return u +} + +// SetCreatedBy sets the "created_by" field. +func (u *SubscriptionTemplateUpsert) SetCreatedBy(v string) *SubscriptionTemplateUpsert { + u.Set(subscriptiontemplate.FieldCreatedBy, v) + return u +} + +// UpdateCreatedBy sets the "created_by" field to the value that was provided on create. +func (u *SubscriptionTemplateUpsert) UpdateCreatedBy() *SubscriptionTemplateUpsert { + u.SetExcluded(subscriptiontemplate.FieldCreatedBy) + return u +} + +// UpdateNewValues updates the mutable fields using the new values that were set on create except the ID field. +// Using this option is equivalent to using: +// +// client.SubscriptionTemplate.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(subscriptiontemplate.FieldID) +// }), +// ). +// Exec(ctx) +func (u *SubscriptionTemplateUpsertOne) UpdateNewValues() *SubscriptionTemplateUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + if _, exists := u.create.mutation.ID(); exists { + s.SetIgnore(subscriptiontemplate.FieldID) + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.SubscriptionTemplate.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *SubscriptionTemplateUpsertOne) Ignore() *SubscriptionTemplateUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *SubscriptionTemplateUpsertOne) DoNothing() *SubscriptionTemplateUpsertOne { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the SubscriptionTemplateCreate.OnConflict +// documentation for more info. +func (u *SubscriptionTemplateUpsertOne) Update(set func(*SubscriptionTemplateUpsert)) *SubscriptionTemplateUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&SubscriptionTemplateUpsert{UpdateSet: update}) + })) + return u +} + +// SetName sets the "name" field. +func (u *SubscriptionTemplateUpsertOne) SetName(v string) *SubscriptionTemplateUpsertOne { + return u.Update(func(s *SubscriptionTemplateUpsert) { + s.SetName(v) + }) +} + +// UpdateName sets the "name" field to the value that was provided on create. +func (u *SubscriptionTemplateUpsertOne) UpdateName() *SubscriptionTemplateUpsertOne { + return u.Update(func(s *SubscriptionTemplateUpsert) { + s.UpdateName() + }) +} + +// SetScope sets the "scope" field. +func (u *SubscriptionTemplateUpsertOne) SetScope(v string) *SubscriptionTemplateUpsertOne { + return u.Update(func(s *SubscriptionTemplateUpsert) { + s.SetScope(v) + }) +} + +// UpdateScope sets the "scope" field to the value that was provided on create. +func (u *SubscriptionTemplateUpsertOne) UpdateScope() *SubscriptionTemplateUpsertOne { + return u.Update(func(s *SubscriptionTemplateUpsert) { + s.UpdateScope() + }) +} + +// SetTriggerActivities sets the "trigger_activities" field. +func (u *SubscriptionTemplateUpsertOne) SetTriggerActivities(v string) *SubscriptionTemplateUpsertOne { + return u.Update(func(s *SubscriptionTemplateUpsert) { + s.SetTriggerActivities(v) + }) +} + +// UpdateTriggerActivities sets the "trigger_activities" field to the value that was provided on create. +func (u *SubscriptionTemplateUpsertOne) UpdateTriggerActivities() *SubscriptionTemplateUpsertOne { + return u.Update(func(s *SubscriptionTemplateUpsert) { + s.UpdateTriggerActivities() + }) +} + +// SetProjectID sets the "project_id" field. +func (u *SubscriptionTemplateUpsertOne) SetProjectID(v uuid.UUID) *SubscriptionTemplateUpsertOne { + return u.Update(func(s *SubscriptionTemplateUpsert) { + s.SetProjectID(v) + }) +} + +// UpdateProjectID sets the "project_id" field to the value that was provided on create. +func (u *SubscriptionTemplateUpsertOne) UpdateProjectID() *SubscriptionTemplateUpsertOne { + return u.Update(func(s *SubscriptionTemplateUpsert) { + s.UpdateProjectID() + }) +} + +// ClearProjectID clears the value of the "project_id" field. +func (u *SubscriptionTemplateUpsertOne) ClearProjectID() *SubscriptionTemplateUpsertOne { + return u.Update(func(s *SubscriptionTemplateUpsert) { + s.ClearProjectID() + }) +} + +// SetCreatedBy sets the "created_by" field. +func (u *SubscriptionTemplateUpsertOne) SetCreatedBy(v string) *SubscriptionTemplateUpsertOne { + return u.Update(func(s *SubscriptionTemplateUpsert) { + s.SetCreatedBy(v) + }) +} + +// UpdateCreatedBy sets the "created_by" field to the value that was provided on create. +func (u *SubscriptionTemplateUpsertOne) UpdateCreatedBy() *SubscriptionTemplateUpsertOne { + return u.Update(func(s *SubscriptionTemplateUpsert) { + s.UpdateCreatedBy() + }) +} + +// Exec executes the query. +func (u *SubscriptionTemplateUpsertOne) Exec(ctx context.Context) error { + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for SubscriptionTemplateCreate.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *SubscriptionTemplateUpsertOne) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} + +// Exec executes the UPSERT query and returns the inserted/updated ID. +func (u *SubscriptionTemplateUpsertOne) ID(ctx context.Context) (id uuid.UUID, err error) { + if u.create.driver.Dialect() == dialect.MySQL { + // In case of "ON CONFLICT", there is no way to get back non-numeric ID + // fields from the database since MySQL does not support the RETURNING clause. + return id, errors.New("ent: SubscriptionTemplateUpsertOne.ID is not supported by MySQL driver. Use SubscriptionTemplateUpsertOne.Exec instead") + } + node, err := u.create.Save(ctx) + if err != nil { + return id, err + } + return node.ID, nil +} + +// IDX is like ID, but panics if an error occurs. +func (u *SubscriptionTemplateUpsertOne) IDX(ctx context.Context) uuid.UUID { + id, err := u.ID(ctx) + if err != nil { + panic(err) + } + return id +} + +// SubscriptionTemplateCreateBulk is the builder for creating many SubscriptionTemplate entities in bulk. +type SubscriptionTemplateCreateBulk struct { + config + err error + builders []*SubscriptionTemplateCreate + conflict []sql.ConflictOption +} + +// Save creates the SubscriptionTemplate entities in the database. +func (_c *SubscriptionTemplateCreateBulk) Save(ctx context.Context) ([]*SubscriptionTemplate, error) { + if _c.err != nil { + return nil, _c.err + } + specs := make([]*sqlgraph.CreateSpec, len(_c.builders)) + nodes := make([]*SubscriptionTemplate, len(_c.builders)) + mutators := make([]Mutator, len(_c.builders)) + for i := range _c.builders { + func(i int, root context.Context) { + builder := _c.builders[i] + builder.defaults() + var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { + mutation, ok := m.(*SubscriptionTemplateMutation) + if !ok { + return nil, fmt.Errorf("unexpected mutation type %T", m) + } + if err := builder.check(); err != nil { + return nil, err + } + builder.mutation = mutation + var err error + nodes[i], specs[i] = builder.createSpec() + if i < len(mutators)-1 { + _, err = mutators[i+1].Mutate(root, _c.builders[i+1].mutation) + } else { + spec := &sqlgraph.BatchCreateSpec{Nodes: specs} + spec.OnConflict = _c.conflict + // Invoke the actual operation on the latest mutation in the chain. + if err = sqlgraph.BatchCreate(ctx, _c.driver, spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + } + } + if err != nil { + return nil, err + } + mutation.id = &nodes[i].ID + mutation.done = true + return nodes[i], nil + }) + for i := len(builder.hooks) - 1; i >= 0; i-- { + mut = builder.hooks[i](mut) + } + mutators[i] = mut + }(i, ctx) + } + if len(mutators) > 0 { + if _, err := mutators[0].Mutate(ctx, _c.builders[0].mutation); err != nil { + return nil, err + } + } + return nodes, nil +} + +// SaveX is like Save, but panics if an error occurs. +func (_c *SubscriptionTemplateCreateBulk) SaveX(ctx context.Context) []*SubscriptionTemplate { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *SubscriptionTemplateCreateBulk) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *SubscriptionTemplateCreateBulk) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.SubscriptionTemplate.CreateBulk(builders...). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.SubscriptionTemplateUpsert) { +// SetName(v+v). +// }). +// Exec(ctx) +func (_c *SubscriptionTemplateCreateBulk) OnConflict(opts ...sql.ConflictOption) *SubscriptionTemplateUpsertBulk { + _c.conflict = opts + return &SubscriptionTemplateUpsertBulk{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.SubscriptionTemplate.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *SubscriptionTemplateCreateBulk) OnConflictColumns(columns ...string) *SubscriptionTemplateUpsertBulk { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &SubscriptionTemplateUpsertBulk{ + create: _c, + } +} + +// SubscriptionTemplateUpsertBulk is the builder for "upsert"-ing +// a bulk of SubscriptionTemplate nodes. +type SubscriptionTemplateUpsertBulk struct { + create *SubscriptionTemplateCreateBulk +} + +// UpdateNewValues updates the mutable fields using the new values that +// were set on create. Using this option is equivalent to using: +// +// client.SubscriptionTemplate.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(subscriptiontemplate.FieldID) +// }), +// ). +// Exec(ctx) +func (u *SubscriptionTemplateUpsertBulk) UpdateNewValues() *SubscriptionTemplateUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + for _, b := range u.create.builders { + if _, exists := b.mutation.ID(); exists { + s.SetIgnore(subscriptiontemplate.FieldID) + } + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.SubscriptionTemplate.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *SubscriptionTemplateUpsertBulk) Ignore() *SubscriptionTemplateUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *SubscriptionTemplateUpsertBulk) DoNothing() *SubscriptionTemplateUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the SubscriptionTemplateCreateBulk.OnConflict +// documentation for more info. +func (u *SubscriptionTemplateUpsertBulk) Update(set func(*SubscriptionTemplateUpsert)) *SubscriptionTemplateUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&SubscriptionTemplateUpsert{UpdateSet: update}) + })) + return u +} + +// SetName sets the "name" field. +func (u *SubscriptionTemplateUpsertBulk) SetName(v string) *SubscriptionTemplateUpsertBulk { + return u.Update(func(s *SubscriptionTemplateUpsert) { + s.SetName(v) + }) +} + +// UpdateName sets the "name" field to the value that was provided on create. +func (u *SubscriptionTemplateUpsertBulk) UpdateName() *SubscriptionTemplateUpsertBulk { + return u.Update(func(s *SubscriptionTemplateUpsert) { + s.UpdateName() + }) +} + +// SetScope sets the "scope" field. +func (u *SubscriptionTemplateUpsertBulk) SetScope(v string) *SubscriptionTemplateUpsertBulk { + return u.Update(func(s *SubscriptionTemplateUpsert) { + s.SetScope(v) + }) +} + +// UpdateScope sets the "scope" field to the value that was provided on create. +func (u *SubscriptionTemplateUpsertBulk) UpdateScope() *SubscriptionTemplateUpsertBulk { + return u.Update(func(s *SubscriptionTemplateUpsert) { + s.UpdateScope() + }) +} + +// SetTriggerActivities sets the "trigger_activities" field. +func (u *SubscriptionTemplateUpsertBulk) SetTriggerActivities(v string) *SubscriptionTemplateUpsertBulk { + return u.Update(func(s *SubscriptionTemplateUpsert) { + s.SetTriggerActivities(v) + }) +} + +// UpdateTriggerActivities sets the "trigger_activities" field to the value that was provided on create. +func (u *SubscriptionTemplateUpsertBulk) UpdateTriggerActivities() *SubscriptionTemplateUpsertBulk { + return u.Update(func(s *SubscriptionTemplateUpsert) { + s.UpdateTriggerActivities() + }) +} + +// SetProjectID sets the "project_id" field. +func (u *SubscriptionTemplateUpsertBulk) SetProjectID(v uuid.UUID) *SubscriptionTemplateUpsertBulk { + return u.Update(func(s *SubscriptionTemplateUpsert) { + s.SetProjectID(v) + }) +} + +// UpdateProjectID sets the "project_id" field to the value that was provided on create. +func (u *SubscriptionTemplateUpsertBulk) UpdateProjectID() *SubscriptionTemplateUpsertBulk { + return u.Update(func(s *SubscriptionTemplateUpsert) { + s.UpdateProjectID() + }) +} + +// ClearProjectID clears the value of the "project_id" field. +func (u *SubscriptionTemplateUpsertBulk) ClearProjectID() *SubscriptionTemplateUpsertBulk { + return u.Update(func(s *SubscriptionTemplateUpsert) { + s.ClearProjectID() + }) +} + +// SetCreatedBy sets the "created_by" field. +func (u *SubscriptionTemplateUpsertBulk) SetCreatedBy(v string) *SubscriptionTemplateUpsertBulk { + return u.Update(func(s *SubscriptionTemplateUpsert) { + s.SetCreatedBy(v) + }) +} + +// UpdateCreatedBy sets the "created_by" field to the value that was provided on create. +func (u *SubscriptionTemplateUpsertBulk) UpdateCreatedBy() *SubscriptionTemplateUpsertBulk { + return u.Update(func(s *SubscriptionTemplateUpsert) { + s.UpdateCreatedBy() + }) +} + +// Exec executes the query. +func (u *SubscriptionTemplateUpsertBulk) Exec(ctx context.Context) error { + if u.create.err != nil { + return u.create.err + } + for i, b := range u.create.builders { + if len(b.conflict) != 0 { + return fmt.Errorf("ent: OnConflict was set for builder %d. Set it on the SubscriptionTemplateCreateBulk instead", i) + } + } + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for SubscriptionTemplateCreateBulk.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *SubscriptionTemplateUpsertBulk) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/pkg/ent/subscriptiontemplate_delete.go b/pkg/ent/subscriptiontemplate_delete.go new file mode 100644 index 000000000..b6aa232ee --- /dev/null +++ b/pkg/ent/subscriptiontemplate_delete.go @@ -0,0 +1,88 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/GoogleCloudPlatform/scion/pkg/ent/subscriptiontemplate" +) + +// SubscriptionTemplateDelete is the builder for deleting a SubscriptionTemplate entity. +type SubscriptionTemplateDelete struct { + config + hooks []Hook + mutation *SubscriptionTemplateMutation +} + +// Where appends a list predicates to the SubscriptionTemplateDelete builder. +func (_d *SubscriptionTemplateDelete) Where(ps ...predicate.SubscriptionTemplate) *SubscriptionTemplateDelete { + _d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (_d *SubscriptionTemplateDelete) Exec(ctx context.Context) (int, error) { + return withHooks(ctx, _d.sqlExec, _d.mutation, _d.hooks) +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *SubscriptionTemplateDelete) ExecX(ctx context.Context) int { + n, err := _d.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (_d *SubscriptionTemplateDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(subscriptiontemplate.Table, sqlgraph.NewFieldSpec(subscriptiontemplate.FieldID, field.TypeUUID)) + if ps := _d.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + affected, err := sqlgraph.DeleteNodes(ctx, _d.driver, _spec) + if err != nil && sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + _d.mutation.done = true + return affected, err +} + +// SubscriptionTemplateDeleteOne is the builder for deleting a single SubscriptionTemplate entity. +type SubscriptionTemplateDeleteOne struct { + _d *SubscriptionTemplateDelete +} + +// Where appends a list predicates to the SubscriptionTemplateDelete builder. +func (_d *SubscriptionTemplateDeleteOne) Where(ps ...predicate.SubscriptionTemplate) *SubscriptionTemplateDeleteOne { + _d._d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query. +func (_d *SubscriptionTemplateDeleteOne) Exec(ctx context.Context) error { + n, err := _d._d.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{subscriptiontemplate.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *SubscriptionTemplateDeleteOne) ExecX(ctx context.Context) { + if err := _d.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/pkg/ent/subscriptiontemplate_query.go b/pkg/ent/subscriptiontemplate_query.go new file mode 100644 index 000000000..6c38c23f5 --- /dev/null +++ b/pkg/ent/subscriptiontemplate_query.go @@ -0,0 +1,565 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "fmt" + "math" + + "entgo.io/ent" + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/GoogleCloudPlatform/scion/pkg/ent/subscriptiontemplate" + "github.com/google/uuid" +) + +// SubscriptionTemplateQuery is the builder for querying SubscriptionTemplate entities. +type SubscriptionTemplateQuery struct { + config + ctx *QueryContext + order []subscriptiontemplate.OrderOption + inters []Interceptor + predicates []predicate.SubscriptionTemplate + modifiers []func(*sql.Selector) + // intermediate query (i.e. traversal path). + sql *sql.Selector + path func(context.Context) (*sql.Selector, error) +} + +// Where adds a new predicate for the SubscriptionTemplateQuery builder. +func (_q *SubscriptionTemplateQuery) Where(ps ...predicate.SubscriptionTemplate) *SubscriptionTemplateQuery { + _q.predicates = append(_q.predicates, ps...) + return _q +} + +// Limit the number of records to be returned by this query. +func (_q *SubscriptionTemplateQuery) Limit(limit int) *SubscriptionTemplateQuery { + _q.ctx.Limit = &limit + return _q +} + +// Offset to start from. +func (_q *SubscriptionTemplateQuery) Offset(offset int) *SubscriptionTemplateQuery { + _q.ctx.Offset = &offset + return _q +} + +// Unique configures the query builder to filter duplicate records on query. +// By default, unique is set to true, and can be disabled using this method. +func (_q *SubscriptionTemplateQuery) Unique(unique bool) *SubscriptionTemplateQuery { + _q.ctx.Unique = &unique + return _q +} + +// Order specifies how the records should be ordered. +func (_q *SubscriptionTemplateQuery) Order(o ...subscriptiontemplate.OrderOption) *SubscriptionTemplateQuery { + _q.order = append(_q.order, o...) + return _q +} + +// First returns the first SubscriptionTemplate entity from the query. +// Returns a *NotFoundError when no SubscriptionTemplate was found. +func (_q *SubscriptionTemplateQuery) First(ctx context.Context) (*SubscriptionTemplate, error) { + nodes, err := _q.Limit(1).All(setContextOp(ctx, _q.ctx, ent.OpQueryFirst)) + if err != nil { + return nil, err + } + if len(nodes) == 0 { + return nil, &NotFoundError{subscriptiontemplate.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (_q *SubscriptionTemplateQuery) FirstX(ctx context.Context) *SubscriptionTemplate { + node, err := _q.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// FirstID returns the first SubscriptionTemplate ID from the query. +// Returns a *NotFoundError when no SubscriptionTemplate ID was found. +func (_q *SubscriptionTemplateQuery) FirstID(ctx context.Context) (id uuid.UUID, err error) { + var ids []uuid.UUID + if ids, err = _q.Limit(1).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryFirstID)); err != nil { + return + } + if len(ids) == 0 { + err = &NotFoundError{subscriptiontemplate.Label} + return + } + return ids[0], nil +} + +// FirstIDX is like FirstID, but panics if an error occurs. +func (_q *SubscriptionTemplateQuery) FirstIDX(ctx context.Context) uuid.UUID { + id, err := _q.FirstID(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return id +} + +// Only returns a single SubscriptionTemplate entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one SubscriptionTemplate entity is found. +// Returns a *NotFoundError when no SubscriptionTemplate entities are found. +func (_q *SubscriptionTemplateQuery) Only(ctx context.Context) (*SubscriptionTemplate, error) { + nodes, err := _q.Limit(2).All(setContextOp(ctx, _q.ctx, ent.OpQueryOnly)) + if err != nil { + return nil, err + } + switch len(nodes) { + case 1: + return nodes[0], nil + case 0: + return nil, &NotFoundError{subscriptiontemplate.Label} + default: + return nil, &NotSingularError{subscriptiontemplate.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (_q *SubscriptionTemplateQuery) OnlyX(ctx context.Context) *SubscriptionTemplate { + node, err := _q.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// OnlyID is like Only, but returns the only SubscriptionTemplate ID in the query. +// Returns a *NotSingularError when more than one SubscriptionTemplate ID is found. +// Returns a *NotFoundError when no entities are found. +func (_q *SubscriptionTemplateQuery) OnlyID(ctx context.Context) (id uuid.UUID, err error) { + var ids []uuid.UUID + if ids, err = _q.Limit(2).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryOnlyID)); err != nil { + return + } + switch len(ids) { + case 1: + id = ids[0] + case 0: + err = &NotFoundError{subscriptiontemplate.Label} + default: + err = &NotSingularError{subscriptiontemplate.Label} + } + return +} + +// OnlyIDX is like OnlyID, but panics if an error occurs. +func (_q *SubscriptionTemplateQuery) OnlyIDX(ctx context.Context) uuid.UUID { + id, err := _q.OnlyID(ctx) + if err != nil { + panic(err) + } + return id +} + +// All executes the query and returns a list of SubscriptionTemplates. +func (_q *SubscriptionTemplateQuery) All(ctx context.Context) ([]*SubscriptionTemplate, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryAll) + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*SubscriptionTemplate, *SubscriptionTemplateQuery]() + return withInterceptors[[]*SubscriptionTemplate](ctx, _q, qr, _q.inters) +} + +// AllX is like All, but panics if an error occurs. +func (_q *SubscriptionTemplateQuery) AllX(ctx context.Context) []*SubscriptionTemplate { + nodes, err := _q.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// IDs executes the query and returns a list of SubscriptionTemplate IDs. +func (_q *SubscriptionTemplateQuery) IDs(ctx context.Context) (ids []uuid.UUID, err error) { + if _q.ctx.Unique == nil && _q.path != nil { + _q.Unique(true) + } + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryIDs) + if err = _q.Select(subscriptiontemplate.FieldID).Scan(ctx, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// IDsX is like IDs, but panics if an error occurs. +func (_q *SubscriptionTemplateQuery) IDsX(ctx context.Context) []uuid.UUID { + ids, err := _q.IDs(ctx) + if err != nil { + panic(err) + } + return ids +} + +// Count returns the count of the given query. +func (_q *SubscriptionTemplateQuery) Count(ctx context.Context) (int, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryCount) + if err := _q.prepareQuery(ctx); err != nil { + return 0, err + } + return withInterceptors[int](ctx, _q, querierCount[*SubscriptionTemplateQuery](), _q.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (_q *SubscriptionTemplateQuery) CountX(ctx context.Context) int { + count, err := _q.Count(ctx) + if err != nil { + panic(err) + } + return count +} + +// Exist returns true if the query has elements in the graph. +func (_q *SubscriptionTemplateQuery) Exist(ctx context.Context) (bool, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryExist) + switch _, err := _q.FirstID(ctx); { + case IsNotFound(err): + return false, nil + case err != nil: + return false, fmt.Errorf("ent: check existence: %w", err) + default: + return true, nil + } +} + +// ExistX is like Exist, but panics if an error occurs. +func (_q *SubscriptionTemplateQuery) ExistX(ctx context.Context) bool { + exist, err := _q.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the SubscriptionTemplateQuery builder, including all associated steps. It can be +// used to prepare common query builders and use them differently after the clone is made. +func (_q *SubscriptionTemplateQuery) Clone() *SubscriptionTemplateQuery { + if _q == nil { + return nil + } + return &SubscriptionTemplateQuery{ + config: _q.config, + ctx: _q.ctx.Clone(), + order: append([]subscriptiontemplate.OrderOption{}, _q.order...), + inters: append([]Interceptor{}, _q.inters...), + predicates: append([]predicate.SubscriptionTemplate{}, _q.predicates...), + // clone intermediate query. + sql: _q.sql.Clone(), + path: _q.path, + } +} + +// GroupBy is used to group vertices by one or more fields/columns. +// It is often used with aggregate functions, like: count, max, mean, min, sum. +// +// Example: +// +// var v []struct { +// Name string `json:"name,omitempty"` +// Count int `json:"count,omitempty"` +// } +// +// client.SubscriptionTemplate.Query(). +// GroupBy(subscriptiontemplate.FieldName). +// Aggregate(ent.Count()). +// Scan(ctx, &v) +func (_q *SubscriptionTemplateQuery) GroupBy(field string, fields ...string) *SubscriptionTemplateGroupBy { + _q.ctx.Fields = append([]string{field}, fields...) + grbuild := &SubscriptionTemplateGroupBy{build: _q} + grbuild.flds = &_q.ctx.Fields + grbuild.label = subscriptiontemplate.Label + grbuild.scan = grbuild.Scan + return grbuild +} + +// Select allows the selection one or more fields/columns for the given query, +// instead of selecting all fields in the entity. +// +// Example: +// +// var v []struct { +// Name string `json:"name,omitempty"` +// } +// +// client.SubscriptionTemplate.Query(). +// Select(subscriptiontemplate.FieldName). +// Scan(ctx, &v) +func (_q *SubscriptionTemplateQuery) Select(fields ...string) *SubscriptionTemplateSelect { + _q.ctx.Fields = append(_q.ctx.Fields, fields...) + sbuild := &SubscriptionTemplateSelect{SubscriptionTemplateQuery: _q} + sbuild.label = subscriptiontemplate.Label + sbuild.flds, sbuild.scan = &_q.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a SubscriptionTemplateSelect configured with the given aggregations. +func (_q *SubscriptionTemplateQuery) Aggregate(fns ...AggregateFunc) *SubscriptionTemplateSelect { + return _q.Select().Aggregate(fns...) +} + +func (_q *SubscriptionTemplateQuery) prepareQuery(ctx context.Context) error { + for _, inter := range _q.inters { + if inter == nil { + return fmt.Errorf("ent: uninitialized interceptor (forgotten import ent/runtime?)") + } + if trv, ok := inter.(Traverser); ok { + if err := trv.Traverse(ctx, _q); err != nil { + return err + } + } + } + for _, f := range _q.ctx.Fields { + if !subscriptiontemplate.ValidColumn(f) { + return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + } + if _q.path != nil { + prev, err := _q.path(ctx) + if err != nil { + return err + } + _q.sql = prev + } + return nil +} + +func (_q *SubscriptionTemplateQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*SubscriptionTemplate, error) { + var ( + nodes = []*SubscriptionTemplate{} + _spec = _q.querySpec() + ) + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*SubscriptionTemplate).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &SubscriptionTemplate{config: _q.config} + nodes = append(nodes, node) + return node.assignValues(columns, values) + } + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } + for i := range hooks { + hooks[i](ctx, _spec) + } + if err := sqlgraph.QueryNodes(ctx, _q.driver, _spec); err != nil { + return nil, err + } + if len(nodes) == 0 { + return nodes, nil + } + return nodes, nil +} + +func (_q *SubscriptionTemplateQuery) sqlCount(ctx context.Context) (int, error) { + _spec := _q.querySpec() + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } + _spec.Node.Columns = _q.ctx.Fields + if len(_q.ctx.Fields) > 0 { + _spec.Unique = _q.ctx.Unique != nil && *_q.ctx.Unique + } + return sqlgraph.CountNodes(ctx, _q.driver, _spec) +} + +func (_q *SubscriptionTemplateQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(subscriptiontemplate.Table, subscriptiontemplate.Columns, sqlgraph.NewFieldSpec(subscriptiontemplate.FieldID, field.TypeUUID)) + _spec.From = _q.sql + if unique := _q.ctx.Unique; unique != nil { + _spec.Unique = *unique + } else if _q.path != nil { + _spec.Unique = true + } + if fields := _q.ctx.Fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, subscriptiontemplate.FieldID) + for i := range fields { + if fields[i] != subscriptiontemplate.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, fields[i]) + } + } + } + if ps := _q.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if limit := _q.ctx.Limit; limit != nil { + _spec.Limit = *limit + } + if offset := _q.ctx.Offset; offset != nil { + _spec.Offset = *offset + } + if ps := _q.order; len(ps) > 0 { + _spec.Order = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + return _spec +} + +func (_q *SubscriptionTemplateQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(_q.driver.Dialect()) + t1 := builder.Table(subscriptiontemplate.Table) + columns := _q.ctx.Fields + if len(columns) == 0 { + columns = subscriptiontemplate.Columns + } + selector := builder.Select(t1.Columns(columns...)...).From(t1) + if _q.sql != nil { + selector = _q.sql + selector.Select(selector.Columns(columns...)...) + } + if _q.ctx.Unique != nil && *_q.ctx.Unique { + selector.Distinct() + } + for _, m := range _q.modifiers { + m(selector) + } + for _, p := range _q.predicates { + p(selector) + } + for _, p := range _q.order { + p(selector) + } + if offset := _q.ctx.Offset; offset != nil { + // limit is mandatory for offset clause. We start + // with default value, and override it below if needed. + selector.Offset(*offset).Limit(math.MaxInt32) + } + if limit := _q.ctx.Limit; limit != nil { + selector.Limit(*limit) + } + return selector +} + +// ForUpdate locks the selected rows against concurrent updates, and prevent them from being +// updated, deleted or "selected ... for update" by other sessions, until the transaction is +// either committed or rolled-back. +func (_q *SubscriptionTemplateQuery) ForUpdate(opts ...sql.LockOption) *SubscriptionTemplateQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForUpdate(opts...) + }) + return _q +} + +// ForShare behaves similarly to ForUpdate, except that it acquires a shared mode lock +// on any rows that are read. Other sessions can read the rows, but cannot modify them +// until your transaction commits. +func (_q *SubscriptionTemplateQuery) ForShare(opts ...sql.LockOption) *SubscriptionTemplateQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForShare(opts...) + }) + return _q +} + +// SubscriptionTemplateGroupBy is the group-by builder for SubscriptionTemplate entities. +type SubscriptionTemplateGroupBy struct { + selector + build *SubscriptionTemplateQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (_g *SubscriptionTemplateGroupBy) Aggregate(fns ...AggregateFunc) *SubscriptionTemplateGroupBy { + _g.fns = append(_g.fns, fns...) + return _g +} + +// Scan applies the selector query and scans the result into the given value. +func (_g *SubscriptionTemplateGroupBy) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, _g.build.ctx, ent.OpQueryGroupBy) + if err := _g.build.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*SubscriptionTemplateQuery, *SubscriptionTemplateGroupBy](ctx, _g.build, _g, _g.build.inters, v) +} + +func (_g *SubscriptionTemplateGroupBy) sqlScan(ctx context.Context, root *SubscriptionTemplateQuery, v any) error { + selector := root.sqlQuery(ctx).Select() + aggregation := make([]string, 0, len(_g.fns)) + for _, fn := range _g.fns { + aggregation = append(aggregation, fn(selector)) + } + if len(selector.SelectedColumns()) == 0 { + columns := make([]string, 0, len(*_g.flds)+len(_g.fns)) + for _, f := range *_g.flds { + columns = append(columns, selector.C(f)) + } + columns = append(columns, aggregation...) + selector.Select(columns...) + } + selector.GroupBy(selector.Columns(*_g.flds...)...) + if err := selector.Err(); err != nil { + return err + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := _g.build.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} + +// SubscriptionTemplateSelect is the builder for selecting fields of SubscriptionTemplate entities. +type SubscriptionTemplateSelect struct { + *SubscriptionTemplateQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (_s *SubscriptionTemplateSelect) Aggregate(fns ...AggregateFunc) *SubscriptionTemplateSelect { + _s.fns = append(_s.fns, fns...) + return _s +} + +// Scan applies the selector query and scans the result into the given value. +func (_s *SubscriptionTemplateSelect) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, _s.ctx, ent.OpQuerySelect) + if err := _s.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*SubscriptionTemplateQuery, *SubscriptionTemplateSelect](ctx, _s.SubscriptionTemplateQuery, _s, _s.inters, v) +} + +func (_s *SubscriptionTemplateSelect) sqlScan(ctx context.Context, root *SubscriptionTemplateQuery, v any) error { + selector := root.sqlQuery(ctx) + aggregation := make([]string, 0, len(_s.fns)) + for _, fn := range _s.fns { + aggregation = append(aggregation, fn(selector)) + } + switch n := len(*_s.selector.flds); { + case n == 0 && len(aggregation) > 0: + selector.Select(aggregation...) + case n != 0 && len(aggregation) > 0: + selector.AppendSelect(aggregation...) + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := _s.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} diff --git a/pkg/ent/subscriptiontemplate_update.go b/pkg/ent/subscriptiontemplate_update.go new file mode 100644 index 000000000..2a87dfae5 --- /dev/null +++ b/pkg/ent/subscriptiontemplate_update.go @@ -0,0 +1,410 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/GoogleCloudPlatform/scion/pkg/ent/subscriptiontemplate" + "github.com/google/uuid" +) + +// SubscriptionTemplateUpdate is the builder for updating SubscriptionTemplate entities. +type SubscriptionTemplateUpdate struct { + config + hooks []Hook + mutation *SubscriptionTemplateMutation +} + +// Where appends a list predicates to the SubscriptionTemplateUpdate builder. +func (_u *SubscriptionTemplateUpdate) Where(ps ...predicate.SubscriptionTemplate) *SubscriptionTemplateUpdate { + _u.mutation.Where(ps...) + return _u +} + +// SetName sets the "name" field. +func (_u *SubscriptionTemplateUpdate) SetName(v string) *SubscriptionTemplateUpdate { + _u.mutation.SetName(v) + return _u +} + +// SetNillableName sets the "name" field if the given value is not nil. +func (_u *SubscriptionTemplateUpdate) SetNillableName(v *string) *SubscriptionTemplateUpdate { + if v != nil { + _u.SetName(*v) + } + return _u +} + +// SetScope sets the "scope" field. +func (_u *SubscriptionTemplateUpdate) SetScope(v string) *SubscriptionTemplateUpdate { + _u.mutation.SetScope(v) + return _u +} + +// SetNillableScope sets the "scope" field if the given value is not nil. +func (_u *SubscriptionTemplateUpdate) SetNillableScope(v *string) *SubscriptionTemplateUpdate { + if v != nil { + _u.SetScope(*v) + } + return _u +} + +// SetTriggerActivities sets the "trigger_activities" field. +func (_u *SubscriptionTemplateUpdate) SetTriggerActivities(v string) *SubscriptionTemplateUpdate { + _u.mutation.SetTriggerActivities(v) + return _u +} + +// SetNillableTriggerActivities sets the "trigger_activities" field if the given value is not nil. +func (_u *SubscriptionTemplateUpdate) SetNillableTriggerActivities(v *string) *SubscriptionTemplateUpdate { + if v != nil { + _u.SetTriggerActivities(*v) + } + return _u +} + +// SetProjectID sets the "project_id" field. +func (_u *SubscriptionTemplateUpdate) SetProjectID(v uuid.UUID) *SubscriptionTemplateUpdate { + _u.mutation.SetProjectID(v) + return _u +} + +// SetNillableProjectID sets the "project_id" field if the given value is not nil. +func (_u *SubscriptionTemplateUpdate) SetNillableProjectID(v *uuid.UUID) *SubscriptionTemplateUpdate { + if v != nil { + _u.SetProjectID(*v) + } + return _u +} + +// ClearProjectID clears the value of the "project_id" field. +func (_u *SubscriptionTemplateUpdate) ClearProjectID() *SubscriptionTemplateUpdate { + _u.mutation.ClearProjectID() + return _u +} + +// SetCreatedBy sets the "created_by" field. +func (_u *SubscriptionTemplateUpdate) SetCreatedBy(v string) *SubscriptionTemplateUpdate { + _u.mutation.SetCreatedBy(v) + return _u +} + +// SetNillableCreatedBy sets the "created_by" field if the given value is not nil. +func (_u *SubscriptionTemplateUpdate) SetNillableCreatedBy(v *string) *SubscriptionTemplateUpdate { + if v != nil { + _u.SetCreatedBy(*v) + } + return _u +} + +// Mutation returns the SubscriptionTemplateMutation object of the builder. +func (_u *SubscriptionTemplateUpdate) Mutation() *SubscriptionTemplateMutation { + return _u.mutation +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (_u *SubscriptionTemplateUpdate) Save(ctx context.Context) (int, error) { + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *SubscriptionTemplateUpdate) SaveX(ctx context.Context) int { + affected, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (_u *SubscriptionTemplateUpdate) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *SubscriptionTemplateUpdate) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_u *SubscriptionTemplateUpdate) check() error { + if v, ok := _u.mutation.Name(); ok { + if err := subscriptiontemplate.NameValidator(v); err != nil { + return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "SubscriptionTemplate.name": %w`, err)} + } + } + if v, ok := _u.mutation.TriggerActivities(); ok { + if err := subscriptiontemplate.TriggerActivitiesValidator(v); err != nil { + return &ValidationError{Name: "trigger_activities", err: fmt.Errorf(`ent: validator failed for field "SubscriptionTemplate.trigger_activities": %w`, err)} + } + } + if v, ok := _u.mutation.CreatedBy(); ok { + if err := subscriptiontemplate.CreatedByValidator(v); err != nil { + return &ValidationError{Name: "created_by", err: fmt.Errorf(`ent: validator failed for field "SubscriptionTemplate.created_by": %w`, err)} + } + } + return nil +} + +func (_u *SubscriptionTemplateUpdate) sqlSave(ctx context.Context) (_node int, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(subscriptiontemplate.Table, subscriptiontemplate.Columns, sqlgraph.NewFieldSpec(subscriptiontemplate.FieldID, field.TypeUUID)) + if ps := _u.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := _u.mutation.Name(); ok { + _spec.SetField(subscriptiontemplate.FieldName, field.TypeString, value) + } + if value, ok := _u.mutation.Scope(); ok { + _spec.SetField(subscriptiontemplate.FieldScope, field.TypeString, value) + } + if value, ok := _u.mutation.TriggerActivities(); ok { + _spec.SetField(subscriptiontemplate.FieldTriggerActivities, field.TypeString, value) + } + if value, ok := _u.mutation.ProjectID(); ok { + _spec.SetField(subscriptiontemplate.FieldProjectID, field.TypeUUID, value) + } + if _u.mutation.ProjectIDCleared() { + _spec.ClearField(subscriptiontemplate.FieldProjectID, field.TypeUUID) + } + if value, ok := _u.mutation.CreatedBy(); ok { + _spec.SetField(subscriptiontemplate.FieldCreatedBy, field.TypeString, value) + } + if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{subscriptiontemplate.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + _u.mutation.done = true + return _node, nil +} + +// SubscriptionTemplateUpdateOne is the builder for updating a single SubscriptionTemplate entity. +type SubscriptionTemplateUpdateOne struct { + config + fields []string + hooks []Hook + mutation *SubscriptionTemplateMutation +} + +// SetName sets the "name" field. +func (_u *SubscriptionTemplateUpdateOne) SetName(v string) *SubscriptionTemplateUpdateOne { + _u.mutation.SetName(v) + return _u +} + +// SetNillableName sets the "name" field if the given value is not nil. +func (_u *SubscriptionTemplateUpdateOne) SetNillableName(v *string) *SubscriptionTemplateUpdateOne { + if v != nil { + _u.SetName(*v) + } + return _u +} + +// SetScope sets the "scope" field. +func (_u *SubscriptionTemplateUpdateOne) SetScope(v string) *SubscriptionTemplateUpdateOne { + _u.mutation.SetScope(v) + return _u +} + +// SetNillableScope sets the "scope" field if the given value is not nil. +func (_u *SubscriptionTemplateUpdateOne) SetNillableScope(v *string) *SubscriptionTemplateUpdateOne { + if v != nil { + _u.SetScope(*v) + } + return _u +} + +// SetTriggerActivities sets the "trigger_activities" field. +func (_u *SubscriptionTemplateUpdateOne) SetTriggerActivities(v string) *SubscriptionTemplateUpdateOne { + _u.mutation.SetTriggerActivities(v) + return _u +} + +// SetNillableTriggerActivities sets the "trigger_activities" field if the given value is not nil. +func (_u *SubscriptionTemplateUpdateOne) SetNillableTriggerActivities(v *string) *SubscriptionTemplateUpdateOne { + if v != nil { + _u.SetTriggerActivities(*v) + } + return _u +} + +// SetProjectID sets the "project_id" field. +func (_u *SubscriptionTemplateUpdateOne) SetProjectID(v uuid.UUID) *SubscriptionTemplateUpdateOne { + _u.mutation.SetProjectID(v) + return _u +} + +// SetNillableProjectID sets the "project_id" field if the given value is not nil. +func (_u *SubscriptionTemplateUpdateOne) SetNillableProjectID(v *uuid.UUID) *SubscriptionTemplateUpdateOne { + if v != nil { + _u.SetProjectID(*v) + } + return _u +} + +// ClearProjectID clears the value of the "project_id" field. +func (_u *SubscriptionTemplateUpdateOne) ClearProjectID() *SubscriptionTemplateUpdateOne { + _u.mutation.ClearProjectID() + return _u +} + +// SetCreatedBy sets the "created_by" field. +func (_u *SubscriptionTemplateUpdateOne) SetCreatedBy(v string) *SubscriptionTemplateUpdateOne { + _u.mutation.SetCreatedBy(v) + return _u +} + +// SetNillableCreatedBy sets the "created_by" field if the given value is not nil. +func (_u *SubscriptionTemplateUpdateOne) SetNillableCreatedBy(v *string) *SubscriptionTemplateUpdateOne { + if v != nil { + _u.SetCreatedBy(*v) + } + return _u +} + +// Mutation returns the SubscriptionTemplateMutation object of the builder. +func (_u *SubscriptionTemplateUpdateOne) Mutation() *SubscriptionTemplateMutation { + return _u.mutation +} + +// Where appends a list predicates to the SubscriptionTemplateUpdate builder. +func (_u *SubscriptionTemplateUpdateOne) Where(ps ...predicate.SubscriptionTemplate) *SubscriptionTemplateUpdateOne { + _u.mutation.Where(ps...) + return _u +} + +// Select allows selecting one or more fields (columns) of the returned entity. +// The default is selecting all fields defined in the entity schema. +func (_u *SubscriptionTemplateUpdateOne) Select(field string, fields ...string) *SubscriptionTemplateUpdateOne { + _u.fields = append([]string{field}, fields...) + return _u +} + +// Save executes the query and returns the updated SubscriptionTemplate entity. +func (_u *SubscriptionTemplateUpdateOne) Save(ctx context.Context) (*SubscriptionTemplate, error) { + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *SubscriptionTemplateUpdateOne) SaveX(ctx context.Context) *SubscriptionTemplate { + node, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (_u *SubscriptionTemplateUpdateOne) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *SubscriptionTemplateUpdateOne) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_u *SubscriptionTemplateUpdateOne) check() error { + if v, ok := _u.mutation.Name(); ok { + if err := subscriptiontemplate.NameValidator(v); err != nil { + return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "SubscriptionTemplate.name": %w`, err)} + } + } + if v, ok := _u.mutation.TriggerActivities(); ok { + if err := subscriptiontemplate.TriggerActivitiesValidator(v); err != nil { + return &ValidationError{Name: "trigger_activities", err: fmt.Errorf(`ent: validator failed for field "SubscriptionTemplate.trigger_activities": %w`, err)} + } + } + if v, ok := _u.mutation.CreatedBy(); ok { + if err := subscriptiontemplate.CreatedByValidator(v); err != nil { + return &ValidationError{Name: "created_by", err: fmt.Errorf(`ent: validator failed for field "SubscriptionTemplate.created_by": %w`, err)} + } + } + return nil +} + +func (_u *SubscriptionTemplateUpdateOne) sqlSave(ctx context.Context) (_node *SubscriptionTemplate, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(subscriptiontemplate.Table, subscriptiontemplate.Columns, sqlgraph.NewFieldSpec(subscriptiontemplate.FieldID, field.TypeUUID)) + id, ok := _u.mutation.ID() + if !ok { + return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "SubscriptionTemplate.id" for update`)} + } + _spec.Node.ID.Value = id + if fields := _u.fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, subscriptiontemplate.FieldID) + for _, f := range fields { + if !subscriptiontemplate.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + if f != subscriptiontemplate.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, f) + } + } + } + if ps := _u.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := _u.mutation.Name(); ok { + _spec.SetField(subscriptiontemplate.FieldName, field.TypeString, value) + } + if value, ok := _u.mutation.Scope(); ok { + _spec.SetField(subscriptiontemplate.FieldScope, field.TypeString, value) + } + if value, ok := _u.mutation.TriggerActivities(); ok { + _spec.SetField(subscriptiontemplate.FieldTriggerActivities, field.TypeString, value) + } + if value, ok := _u.mutation.ProjectID(); ok { + _spec.SetField(subscriptiontemplate.FieldProjectID, field.TypeUUID, value) + } + if _u.mutation.ProjectIDCleared() { + _spec.ClearField(subscriptiontemplate.FieldProjectID, field.TypeUUID) + } + if value, ok := _u.mutation.CreatedBy(); ok { + _spec.SetField(subscriptiontemplate.FieldCreatedBy, field.TypeString, value) + } + _node = &SubscriptionTemplate{config: _u.config} + _spec.Assign = _node.assignValues + _spec.ScanValues = _node.scanValues + if err = sqlgraph.UpdateNode(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{subscriptiontemplate.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + _u.mutation.done = true + return _node, nil +} diff --git a/pkg/ent/template.go b/pkg/ent/template.go new file mode 100644 index 000000000..eed77d8d0 --- /dev/null +++ b/pkg/ent/template.go @@ -0,0 +1,373 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "fmt" + "strings" + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "github.com/GoogleCloudPlatform/scion/pkg/ent/template" + "github.com/google/uuid" +) + +// Template is the model entity for the Template schema. +type Template struct { + config `json:"-"` + // ID of the ent. + ID uuid.UUID `json:"id,omitempty"` + // Name holds the value of the "name" field. + Name string `json:"name,omitempty"` + // Slug holds the value of the "slug" field. + Slug string `json:"slug,omitempty"` + // DisplayName holds the value of the "display_name" field. + DisplayName string `json:"display_name,omitempty"` + // Description holds the value of the "description" field. + Description string `json:"description,omitempty"` + // Harness holds the value of the "harness" field. + Harness string `json:"harness,omitempty"` + // DefaultHarnessConfig holds the value of the "default_harness_config" field. + DefaultHarnessConfig string `json:"default_harness_config,omitempty"` + // Image holds the value of the "image" field. + Image string `json:"image,omitempty"` + // Config holds the value of the "config" field. + Config string `json:"config,omitempty"` + // ContentHash holds the value of the "content_hash" field. + ContentHash string `json:"content_hash,omitempty"` + // Scope holds the value of the "scope" field. + Scope string `json:"scope,omitempty"` + // ScopeID holds the value of the "scope_id" field. + ScopeID string `json:"scope_id,omitempty"` + // ProjectID holds the value of the "project_id" field. + ProjectID string `json:"project_id,omitempty"` + // StorageURI holds the value of the "storage_uri" field. + StorageURI string `json:"storage_uri,omitempty"` + // StorageBucket holds the value of the "storage_bucket" field. + StorageBucket string `json:"storage_bucket,omitempty"` + // StoragePath holds the value of the "storage_path" field. + StoragePath string `json:"storage_path,omitempty"` + // Files holds the value of the "files" field. + Files string `json:"files,omitempty"` + // BaseTemplate holds the value of the "base_template" field. + BaseTemplate string `json:"base_template,omitempty"` + // Locked holds the value of the "locked" field. + Locked bool `json:"locked,omitempty"` + // Status holds the value of the "status" field. + Status template.Status `json:"status,omitempty"` + // OwnerID holds the value of the "owner_id" field. + OwnerID string `json:"owner_id,omitempty"` + // CreatedBy holds the value of the "created_by" field. + CreatedBy string `json:"created_by,omitempty"` + // UpdatedBy holds the value of the "updated_by" field. + UpdatedBy string `json:"updated_by,omitempty"` + // Visibility holds the value of the "visibility" field. + Visibility string `json:"visibility,omitempty"` + // Created holds the value of the "created" field. + Created time.Time `json:"created,omitempty"` + // Updated holds the value of the "updated" field. + Updated time.Time `json:"updated,omitempty"` + selectValues sql.SelectValues +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*Template) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case template.FieldLocked: + values[i] = new(sql.NullBool) + case template.FieldName, template.FieldSlug, template.FieldDisplayName, template.FieldDescription, template.FieldHarness, template.FieldDefaultHarnessConfig, template.FieldImage, template.FieldConfig, template.FieldContentHash, template.FieldScope, template.FieldScopeID, template.FieldProjectID, template.FieldStorageURI, template.FieldStorageBucket, template.FieldStoragePath, template.FieldFiles, template.FieldBaseTemplate, template.FieldStatus, template.FieldOwnerID, template.FieldCreatedBy, template.FieldUpdatedBy, template.FieldVisibility: + values[i] = new(sql.NullString) + case template.FieldCreated, template.FieldUpdated: + values[i] = new(sql.NullTime) + case template.FieldID: + values[i] = new(uuid.UUID) + default: + values[i] = new(sql.UnknownType) + } + } + return values, nil +} + +// assignValues assigns the values that were returned from sql.Rows (after scanning) +// to the Template fields. +func (_m *Template) assignValues(columns []string, values []any) error { + if m, n := len(values), len(columns); m < n { + return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) + } + for i := range columns { + switch columns[i] { + case template.FieldID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field id", values[i]) + } else if value != nil { + _m.ID = *value + } + case template.FieldName: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field name", values[i]) + } else if value.Valid { + _m.Name = value.String + } + case template.FieldSlug: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field slug", values[i]) + } else if value.Valid { + _m.Slug = value.String + } + case template.FieldDisplayName: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field display_name", values[i]) + } else if value.Valid { + _m.DisplayName = value.String + } + case template.FieldDescription: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field description", values[i]) + } else if value.Valid { + _m.Description = value.String + } + case template.FieldHarness: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field harness", values[i]) + } else if value.Valid { + _m.Harness = value.String + } + case template.FieldDefaultHarnessConfig: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field default_harness_config", values[i]) + } else if value.Valid { + _m.DefaultHarnessConfig = value.String + } + case template.FieldImage: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field image", values[i]) + } else if value.Valid { + _m.Image = value.String + } + case template.FieldConfig: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field config", values[i]) + } else if value.Valid { + _m.Config = value.String + } + case template.FieldContentHash: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field content_hash", values[i]) + } else if value.Valid { + _m.ContentHash = value.String + } + case template.FieldScope: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field scope", values[i]) + } else if value.Valid { + _m.Scope = value.String + } + case template.FieldScopeID: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field scope_id", values[i]) + } else if value.Valid { + _m.ScopeID = value.String + } + case template.FieldProjectID: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field project_id", values[i]) + } else if value.Valid { + _m.ProjectID = value.String + } + case template.FieldStorageURI: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field storage_uri", values[i]) + } else if value.Valid { + _m.StorageURI = value.String + } + case template.FieldStorageBucket: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field storage_bucket", values[i]) + } else if value.Valid { + _m.StorageBucket = value.String + } + case template.FieldStoragePath: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field storage_path", values[i]) + } else if value.Valid { + _m.StoragePath = value.String + } + case template.FieldFiles: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field files", values[i]) + } else if value.Valid { + _m.Files = value.String + } + case template.FieldBaseTemplate: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field base_template", values[i]) + } else if value.Valid { + _m.BaseTemplate = value.String + } + case template.FieldLocked: + if value, ok := values[i].(*sql.NullBool); !ok { + return fmt.Errorf("unexpected type %T for field locked", values[i]) + } else if value.Valid { + _m.Locked = value.Bool + } + case template.FieldStatus: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field status", values[i]) + } else if value.Valid { + _m.Status = template.Status(value.String) + } + case template.FieldOwnerID: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field owner_id", values[i]) + } else if value.Valid { + _m.OwnerID = value.String + } + case template.FieldCreatedBy: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field created_by", values[i]) + } else if value.Valid { + _m.CreatedBy = value.String + } + case template.FieldUpdatedBy: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field updated_by", values[i]) + } else if value.Valid { + _m.UpdatedBy = value.String + } + case template.FieldVisibility: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field visibility", values[i]) + } else if value.Valid { + _m.Visibility = value.String + } + case template.FieldCreated: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field created", values[i]) + } else if value.Valid { + _m.Created = value.Time + } + case template.FieldUpdated: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field updated", values[i]) + } else if value.Valid { + _m.Updated = value.Time + } + default: + _m.selectValues.Set(columns[i], values[i]) + } + } + return nil +} + +// Value returns the ent.Value that was dynamically selected and assigned to the Template. +// This includes values selected through modifiers, order, etc. +func (_m *Template) Value(name string) (ent.Value, error) { + return _m.selectValues.Get(name) +} + +// Update returns a builder for updating this Template. +// Note that you need to call Template.Unwrap() before calling this method if this Template +// was returned from a transaction, and the transaction was committed or rolled back. +func (_m *Template) Update() *TemplateUpdateOne { + return NewTemplateClient(_m.config).UpdateOne(_m) +} + +// Unwrap unwraps the Template entity that was returned from a transaction after it was closed, +// so that all future queries will be executed through the driver which created the transaction. +func (_m *Template) Unwrap() *Template { + _tx, ok := _m.config.driver.(*txDriver) + if !ok { + panic("ent: Template is not a transactional entity") + } + _m.config.driver = _tx.drv + return _m +} + +// String implements the fmt.Stringer. +func (_m *Template) String() string { + var builder strings.Builder + builder.WriteString("Template(") + builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID)) + builder.WriteString("name=") + builder.WriteString(_m.Name) + builder.WriteString(", ") + builder.WriteString("slug=") + builder.WriteString(_m.Slug) + builder.WriteString(", ") + builder.WriteString("display_name=") + builder.WriteString(_m.DisplayName) + builder.WriteString(", ") + builder.WriteString("description=") + builder.WriteString(_m.Description) + builder.WriteString(", ") + builder.WriteString("harness=") + builder.WriteString(_m.Harness) + builder.WriteString(", ") + builder.WriteString("default_harness_config=") + builder.WriteString(_m.DefaultHarnessConfig) + builder.WriteString(", ") + builder.WriteString("image=") + builder.WriteString(_m.Image) + builder.WriteString(", ") + builder.WriteString("config=") + builder.WriteString(_m.Config) + builder.WriteString(", ") + builder.WriteString("content_hash=") + builder.WriteString(_m.ContentHash) + builder.WriteString(", ") + builder.WriteString("scope=") + builder.WriteString(_m.Scope) + builder.WriteString(", ") + builder.WriteString("scope_id=") + builder.WriteString(_m.ScopeID) + builder.WriteString(", ") + builder.WriteString("project_id=") + builder.WriteString(_m.ProjectID) + builder.WriteString(", ") + builder.WriteString("storage_uri=") + builder.WriteString(_m.StorageURI) + builder.WriteString(", ") + builder.WriteString("storage_bucket=") + builder.WriteString(_m.StorageBucket) + builder.WriteString(", ") + builder.WriteString("storage_path=") + builder.WriteString(_m.StoragePath) + builder.WriteString(", ") + builder.WriteString("files=") + builder.WriteString(_m.Files) + builder.WriteString(", ") + builder.WriteString("base_template=") + builder.WriteString(_m.BaseTemplate) + builder.WriteString(", ") + builder.WriteString("locked=") + builder.WriteString(fmt.Sprintf("%v", _m.Locked)) + builder.WriteString(", ") + builder.WriteString("status=") + builder.WriteString(fmt.Sprintf("%v", _m.Status)) + builder.WriteString(", ") + builder.WriteString("owner_id=") + builder.WriteString(_m.OwnerID) + builder.WriteString(", ") + builder.WriteString("created_by=") + builder.WriteString(_m.CreatedBy) + builder.WriteString(", ") + builder.WriteString("updated_by=") + builder.WriteString(_m.UpdatedBy) + builder.WriteString(", ") + builder.WriteString("visibility=") + builder.WriteString(_m.Visibility) + builder.WriteString(", ") + builder.WriteString("created=") + builder.WriteString(_m.Created.Format(time.ANSIC)) + builder.WriteString(", ") + builder.WriteString("updated=") + builder.WriteString(_m.Updated.Format(time.ANSIC)) + builder.WriteByte(')') + return builder.String() +} + +// Templates is a parsable slice of Template. +type Templates []*Template diff --git a/pkg/ent/template/template.go b/pkg/ent/template/template.go new file mode 100644 index 000000000..13052caa1 --- /dev/null +++ b/pkg/ent/template/template.go @@ -0,0 +1,291 @@ +// Code generated by ent, DO NOT EDIT. + +package template + +import ( + "fmt" + "time" + + "entgo.io/ent/dialect/sql" + "github.com/google/uuid" +) + +const ( + // Label holds the string label denoting the template type in the database. + Label = "template" + // FieldID holds the string denoting the id field in the database. + FieldID = "id" + // FieldName holds the string denoting the name field in the database. + FieldName = "name" + // FieldSlug holds the string denoting the slug field in the database. + FieldSlug = "slug" + // FieldDisplayName holds the string denoting the display_name field in the database. + FieldDisplayName = "display_name" + // FieldDescription holds the string denoting the description field in the database. + FieldDescription = "description" + // FieldHarness holds the string denoting the harness field in the database. + FieldHarness = "harness" + // FieldDefaultHarnessConfig holds the string denoting the default_harness_config field in the database. + FieldDefaultHarnessConfig = "default_harness_config" + // FieldImage holds the string denoting the image field in the database. + FieldImage = "image" + // FieldConfig holds the string denoting the config field in the database. + FieldConfig = "config" + // FieldContentHash holds the string denoting the content_hash field in the database. + FieldContentHash = "content_hash" + // FieldScope holds the string denoting the scope field in the database. + FieldScope = "scope" + // FieldScopeID holds the string denoting the scope_id field in the database. + FieldScopeID = "scope_id" + // FieldProjectID holds the string denoting the project_id field in the database. + FieldProjectID = "project_id" + // FieldStorageURI holds the string denoting the storage_uri field in the database. + FieldStorageURI = "storage_uri" + // FieldStorageBucket holds the string denoting the storage_bucket field in the database. + FieldStorageBucket = "storage_bucket" + // FieldStoragePath holds the string denoting the storage_path field in the database. + FieldStoragePath = "storage_path" + // FieldFiles holds the string denoting the files field in the database. + FieldFiles = "files" + // FieldBaseTemplate holds the string denoting the base_template field in the database. + FieldBaseTemplate = "base_template" + // FieldLocked holds the string denoting the locked field in the database. + FieldLocked = "locked" + // FieldStatus holds the string denoting the status field in the database. + FieldStatus = "status" + // FieldOwnerID holds the string denoting the owner_id field in the database. + FieldOwnerID = "owner_id" + // FieldCreatedBy holds the string denoting the created_by field in the database. + FieldCreatedBy = "created_by" + // FieldUpdatedBy holds the string denoting the updated_by field in the database. + FieldUpdatedBy = "updated_by" + // FieldVisibility holds the string denoting the visibility field in the database. + FieldVisibility = "visibility" + // FieldCreated holds the string denoting the created field in the database. + FieldCreated = "created" + // FieldUpdated holds the string denoting the updated field in the database. + FieldUpdated = "updated" + // Table holds the table name of the template in the database. + Table = "templates" +) + +// Columns holds all SQL columns for template fields. +var Columns = []string{ + FieldID, + FieldName, + FieldSlug, + FieldDisplayName, + FieldDescription, + FieldHarness, + FieldDefaultHarnessConfig, + FieldImage, + FieldConfig, + FieldContentHash, + FieldScope, + FieldScopeID, + FieldProjectID, + FieldStorageURI, + FieldStorageBucket, + FieldStoragePath, + FieldFiles, + FieldBaseTemplate, + FieldLocked, + FieldStatus, + FieldOwnerID, + FieldCreatedBy, + FieldUpdatedBy, + FieldVisibility, + FieldCreated, + FieldUpdated, +} + +// ValidColumn reports if the column name is valid (part of the table columns). +func ValidColumn(column string) bool { + for i := range Columns { + if column == Columns[i] { + return true + } + } + return false +} + +var ( + // NameValidator is a validator for the "name" field. It is called by the builders before save. + NameValidator func(string) error + // SlugValidator is a validator for the "slug" field. It is called by the builders before save. + SlugValidator func(string) error + // DefaultScope holds the default value on creation for the "scope" field. + DefaultScope string + // DefaultLocked holds the default value on creation for the "locked" field. + DefaultLocked bool + // DefaultVisibility holds the default value on creation for the "visibility" field. + DefaultVisibility string + // DefaultCreated holds the default value on creation for the "created" field. + DefaultCreated func() time.Time + // DefaultUpdated holds the default value on creation for the "updated" field. + DefaultUpdated func() time.Time + // UpdateDefaultUpdated holds the default value on update for the "updated" field. + UpdateDefaultUpdated func() time.Time + // DefaultID holds the default value on creation for the "id" field. + DefaultID func() uuid.UUID +) + +// Status defines the type for the "status" enum field. +type Status string + +// StatusActive is the default value of the Status enum. +const DefaultStatus = StatusActive + +// Status values. +const ( + StatusPending Status = "pending" + StatusActive Status = "active" + StatusArchived Status = "archived" +) + +func (s Status) String() string { + return string(s) +} + +// StatusValidator is a validator for the "status" field enum values. It is called by the builders before save. +func StatusValidator(s Status) error { + switch s { + case StatusPending, StatusActive, StatusArchived: + return nil + default: + return fmt.Errorf("template: invalid enum value for status field: %q", s) + } +} + +// OrderOption defines the ordering options for the Template queries. +type OrderOption func(*sql.Selector) + +// ByID orders the results by the id field. +func ByID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldID, opts...).ToFunc() +} + +// ByName orders the results by the name field. +func ByName(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldName, opts...).ToFunc() +} + +// BySlug orders the results by the slug field. +func BySlug(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldSlug, opts...).ToFunc() +} + +// ByDisplayName orders the results by the display_name field. +func ByDisplayName(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldDisplayName, opts...).ToFunc() +} + +// ByDescription orders the results by the description field. +func ByDescription(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldDescription, opts...).ToFunc() +} + +// ByHarness orders the results by the harness field. +func ByHarness(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldHarness, opts...).ToFunc() +} + +// ByDefaultHarnessConfig orders the results by the default_harness_config field. +func ByDefaultHarnessConfig(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldDefaultHarnessConfig, opts...).ToFunc() +} + +// ByImage orders the results by the image field. +func ByImage(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldImage, opts...).ToFunc() +} + +// ByConfig orders the results by the config field. +func ByConfig(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldConfig, opts...).ToFunc() +} + +// ByContentHash orders the results by the content_hash field. +func ByContentHash(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldContentHash, opts...).ToFunc() +} + +// ByScope orders the results by the scope field. +func ByScope(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldScope, opts...).ToFunc() +} + +// ByScopeID orders the results by the scope_id field. +func ByScopeID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldScopeID, opts...).ToFunc() +} + +// ByProjectID orders the results by the project_id field. +func ByProjectID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldProjectID, opts...).ToFunc() +} + +// ByStorageURI orders the results by the storage_uri field. +func ByStorageURI(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldStorageURI, opts...).ToFunc() +} + +// ByStorageBucket orders the results by the storage_bucket field. +func ByStorageBucket(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldStorageBucket, opts...).ToFunc() +} + +// ByStoragePath orders the results by the storage_path field. +func ByStoragePath(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldStoragePath, opts...).ToFunc() +} + +// ByFiles orders the results by the files field. +func ByFiles(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldFiles, opts...).ToFunc() +} + +// ByBaseTemplate orders the results by the base_template field. +func ByBaseTemplate(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldBaseTemplate, opts...).ToFunc() +} + +// ByLocked orders the results by the locked field. +func ByLocked(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldLocked, opts...).ToFunc() +} + +// ByStatus orders the results by the status field. +func ByStatus(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldStatus, opts...).ToFunc() +} + +// ByOwnerID orders the results by the owner_id field. +func ByOwnerID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldOwnerID, opts...).ToFunc() +} + +// ByCreatedBy orders the results by the created_by field. +func ByCreatedBy(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCreatedBy, opts...).ToFunc() +} + +// ByUpdatedBy orders the results by the updated_by field. +func ByUpdatedBy(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldUpdatedBy, opts...).ToFunc() +} + +// ByVisibility orders the results by the visibility field. +func ByVisibility(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldVisibility, opts...).ToFunc() +} + +// ByCreated orders the results by the created field. +func ByCreated(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCreated, opts...).ToFunc() +} + +// ByUpdated orders the results by the updated field. +func ByUpdated(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldUpdated, opts...).ToFunc() +} diff --git a/pkg/ent/template/where.go b/pkg/ent/template/where.go new file mode 100644 index 000000000..659342758 --- /dev/null +++ b/pkg/ent/template/where.go @@ -0,0 +1,1826 @@ +// Code generated by ent, DO NOT EDIT. + +package template + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/google/uuid" +) + +// ID filters vertices based on their ID field. +func ID(id uuid.UUID) predicate.Template { + return predicate.Template(sql.FieldEQ(FieldID, id)) +} + +// IDEQ applies the EQ predicate on the ID field. +func IDEQ(id uuid.UUID) predicate.Template { + return predicate.Template(sql.FieldEQ(FieldID, id)) +} + +// IDNEQ applies the NEQ predicate on the ID field. +func IDNEQ(id uuid.UUID) predicate.Template { + return predicate.Template(sql.FieldNEQ(FieldID, id)) +} + +// IDIn applies the In predicate on the ID field. +func IDIn(ids ...uuid.UUID) predicate.Template { + return predicate.Template(sql.FieldIn(FieldID, ids...)) +} + +// IDNotIn applies the NotIn predicate on the ID field. +func IDNotIn(ids ...uuid.UUID) predicate.Template { + return predicate.Template(sql.FieldNotIn(FieldID, ids...)) +} + +// IDGT applies the GT predicate on the ID field. +func IDGT(id uuid.UUID) predicate.Template { + return predicate.Template(sql.FieldGT(FieldID, id)) +} + +// IDGTE applies the GTE predicate on the ID field. +func IDGTE(id uuid.UUID) predicate.Template { + return predicate.Template(sql.FieldGTE(FieldID, id)) +} + +// IDLT applies the LT predicate on the ID field. +func IDLT(id uuid.UUID) predicate.Template { + return predicate.Template(sql.FieldLT(FieldID, id)) +} + +// IDLTE applies the LTE predicate on the ID field. +func IDLTE(id uuid.UUID) predicate.Template { + return predicate.Template(sql.FieldLTE(FieldID, id)) +} + +// Name applies equality check predicate on the "name" field. It's identical to NameEQ. +func Name(v string) predicate.Template { + return predicate.Template(sql.FieldEQ(FieldName, v)) +} + +// Slug applies equality check predicate on the "slug" field. It's identical to SlugEQ. +func Slug(v string) predicate.Template { + return predicate.Template(sql.FieldEQ(FieldSlug, v)) +} + +// DisplayName applies equality check predicate on the "display_name" field. It's identical to DisplayNameEQ. +func DisplayName(v string) predicate.Template { + return predicate.Template(sql.FieldEQ(FieldDisplayName, v)) +} + +// Description applies equality check predicate on the "description" field. It's identical to DescriptionEQ. +func Description(v string) predicate.Template { + return predicate.Template(sql.FieldEQ(FieldDescription, v)) +} + +// Harness applies equality check predicate on the "harness" field. It's identical to HarnessEQ. +func Harness(v string) predicate.Template { + return predicate.Template(sql.FieldEQ(FieldHarness, v)) +} + +// DefaultHarnessConfig applies equality check predicate on the "default_harness_config" field. It's identical to DefaultHarnessConfigEQ. +func DefaultHarnessConfig(v string) predicate.Template { + return predicate.Template(sql.FieldEQ(FieldDefaultHarnessConfig, v)) +} + +// Image applies equality check predicate on the "image" field. It's identical to ImageEQ. +func Image(v string) predicate.Template { + return predicate.Template(sql.FieldEQ(FieldImage, v)) +} + +// Config applies equality check predicate on the "config" field. It's identical to ConfigEQ. +func Config(v string) predicate.Template { + return predicate.Template(sql.FieldEQ(FieldConfig, v)) +} + +// ContentHash applies equality check predicate on the "content_hash" field. It's identical to ContentHashEQ. +func ContentHash(v string) predicate.Template { + return predicate.Template(sql.FieldEQ(FieldContentHash, v)) +} + +// Scope applies equality check predicate on the "scope" field. It's identical to ScopeEQ. +func Scope(v string) predicate.Template { + return predicate.Template(sql.FieldEQ(FieldScope, v)) +} + +// ScopeID applies equality check predicate on the "scope_id" field. It's identical to ScopeIDEQ. +func ScopeID(v string) predicate.Template { + return predicate.Template(sql.FieldEQ(FieldScopeID, v)) +} + +// ProjectID applies equality check predicate on the "project_id" field. It's identical to ProjectIDEQ. +func ProjectID(v string) predicate.Template { + return predicate.Template(sql.FieldEQ(FieldProjectID, v)) +} + +// StorageURI applies equality check predicate on the "storage_uri" field. It's identical to StorageURIEQ. +func StorageURI(v string) predicate.Template { + return predicate.Template(sql.FieldEQ(FieldStorageURI, v)) +} + +// StorageBucket applies equality check predicate on the "storage_bucket" field. It's identical to StorageBucketEQ. +func StorageBucket(v string) predicate.Template { + return predicate.Template(sql.FieldEQ(FieldStorageBucket, v)) +} + +// StoragePath applies equality check predicate on the "storage_path" field. It's identical to StoragePathEQ. +func StoragePath(v string) predicate.Template { + return predicate.Template(sql.FieldEQ(FieldStoragePath, v)) +} + +// Files applies equality check predicate on the "files" field. It's identical to FilesEQ. +func Files(v string) predicate.Template { + return predicate.Template(sql.FieldEQ(FieldFiles, v)) +} + +// BaseTemplate applies equality check predicate on the "base_template" field. It's identical to BaseTemplateEQ. +func BaseTemplate(v string) predicate.Template { + return predicate.Template(sql.FieldEQ(FieldBaseTemplate, v)) +} + +// Locked applies equality check predicate on the "locked" field. It's identical to LockedEQ. +func Locked(v bool) predicate.Template { + return predicate.Template(sql.FieldEQ(FieldLocked, v)) +} + +// OwnerID applies equality check predicate on the "owner_id" field. It's identical to OwnerIDEQ. +func OwnerID(v string) predicate.Template { + return predicate.Template(sql.FieldEQ(FieldOwnerID, v)) +} + +// CreatedBy applies equality check predicate on the "created_by" field. It's identical to CreatedByEQ. +func CreatedBy(v string) predicate.Template { + return predicate.Template(sql.FieldEQ(FieldCreatedBy, v)) +} + +// UpdatedBy applies equality check predicate on the "updated_by" field. It's identical to UpdatedByEQ. +func UpdatedBy(v string) predicate.Template { + return predicate.Template(sql.FieldEQ(FieldUpdatedBy, v)) +} + +// Visibility applies equality check predicate on the "visibility" field. It's identical to VisibilityEQ. +func Visibility(v string) predicate.Template { + return predicate.Template(sql.FieldEQ(FieldVisibility, v)) +} + +// Created applies equality check predicate on the "created" field. It's identical to CreatedEQ. +func Created(v time.Time) predicate.Template { + return predicate.Template(sql.FieldEQ(FieldCreated, v)) +} + +// Updated applies equality check predicate on the "updated" field. It's identical to UpdatedEQ. +func Updated(v time.Time) predicate.Template { + return predicate.Template(sql.FieldEQ(FieldUpdated, v)) +} + +// NameEQ applies the EQ predicate on the "name" field. +func NameEQ(v string) predicate.Template { + return predicate.Template(sql.FieldEQ(FieldName, v)) +} + +// NameNEQ applies the NEQ predicate on the "name" field. +func NameNEQ(v string) predicate.Template { + return predicate.Template(sql.FieldNEQ(FieldName, v)) +} + +// NameIn applies the In predicate on the "name" field. +func NameIn(vs ...string) predicate.Template { + return predicate.Template(sql.FieldIn(FieldName, vs...)) +} + +// NameNotIn applies the NotIn predicate on the "name" field. +func NameNotIn(vs ...string) predicate.Template { + return predicate.Template(sql.FieldNotIn(FieldName, vs...)) +} + +// NameGT applies the GT predicate on the "name" field. +func NameGT(v string) predicate.Template { + return predicate.Template(sql.FieldGT(FieldName, v)) +} + +// NameGTE applies the GTE predicate on the "name" field. +func NameGTE(v string) predicate.Template { + return predicate.Template(sql.FieldGTE(FieldName, v)) +} + +// NameLT applies the LT predicate on the "name" field. +func NameLT(v string) predicate.Template { + return predicate.Template(sql.FieldLT(FieldName, v)) +} + +// NameLTE applies the LTE predicate on the "name" field. +func NameLTE(v string) predicate.Template { + return predicate.Template(sql.FieldLTE(FieldName, v)) +} + +// NameContains applies the Contains predicate on the "name" field. +func NameContains(v string) predicate.Template { + return predicate.Template(sql.FieldContains(FieldName, v)) +} + +// NameHasPrefix applies the HasPrefix predicate on the "name" field. +func NameHasPrefix(v string) predicate.Template { + return predicate.Template(sql.FieldHasPrefix(FieldName, v)) +} + +// NameHasSuffix applies the HasSuffix predicate on the "name" field. +func NameHasSuffix(v string) predicate.Template { + return predicate.Template(sql.FieldHasSuffix(FieldName, v)) +} + +// NameEqualFold applies the EqualFold predicate on the "name" field. +func NameEqualFold(v string) predicate.Template { + return predicate.Template(sql.FieldEqualFold(FieldName, v)) +} + +// NameContainsFold applies the ContainsFold predicate on the "name" field. +func NameContainsFold(v string) predicate.Template { + return predicate.Template(sql.FieldContainsFold(FieldName, v)) +} + +// SlugEQ applies the EQ predicate on the "slug" field. +func SlugEQ(v string) predicate.Template { + return predicate.Template(sql.FieldEQ(FieldSlug, v)) +} + +// SlugNEQ applies the NEQ predicate on the "slug" field. +func SlugNEQ(v string) predicate.Template { + return predicate.Template(sql.FieldNEQ(FieldSlug, v)) +} + +// SlugIn applies the In predicate on the "slug" field. +func SlugIn(vs ...string) predicate.Template { + return predicate.Template(sql.FieldIn(FieldSlug, vs...)) +} + +// SlugNotIn applies the NotIn predicate on the "slug" field. +func SlugNotIn(vs ...string) predicate.Template { + return predicate.Template(sql.FieldNotIn(FieldSlug, vs...)) +} + +// SlugGT applies the GT predicate on the "slug" field. +func SlugGT(v string) predicate.Template { + return predicate.Template(sql.FieldGT(FieldSlug, v)) +} + +// SlugGTE applies the GTE predicate on the "slug" field. +func SlugGTE(v string) predicate.Template { + return predicate.Template(sql.FieldGTE(FieldSlug, v)) +} + +// SlugLT applies the LT predicate on the "slug" field. +func SlugLT(v string) predicate.Template { + return predicate.Template(sql.FieldLT(FieldSlug, v)) +} + +// SlugLTE applies the LTE predicate on the "slug" field. +func SlugLTE(v string) predicate.Template { + return predicate.Template(sql.FieldLTE(FieldSlug, v)) +} + +// SlugContains applies the Contains predicate on the "slug" field. +func SlugContains(v string) predicate.Template { + return predicate.Template(sql.FieldContains(FieldSlug, v)) +} + +// SlugHasPrefix applies the HasPrefix predicate on the "slug" field. +func SlugHasPrefix(v string) predicate.Template { + return predicate.Template(sql.FieldHasPrefix(FieldSlug, v)) +} + +// SlugHasSuffix applies the HasSuffix predicate on the "slug" field. +func SlugHasSuffix(v string) predicate.Template { + return predicate.Template(sql.FieldHasSuffix(FieldSlug, v)) +} + +// SlugEqualFold applies the EqualFold predicate on the "slug" field. +func SlugEqualFold(v string) predicate.Template { + return predicate.Template(sql.FieldEqualFold(FieldSlug, v)) +} + +// SlugContainsFold applies the ContainsFold predicate on the "slug" field. +func SlugContainsFold(v string) predicate.Template { + return predicate.Template(sql.FieldContainsFold(FieldSlug, v)) +} + +// DisplayNameEQ applies the EQ predicate on the "display_name" field. +func DisplayNameEQ(v string) predicate.Template { + return predicate.Template(sql.FieldEQ(FieldDisplayName, v)) +} + +// DisplayNameNEQ applies the NEQ predicate on the "display_name" field. +func DisplayNameNEQ(v string) predicate.Template { + return predicate.Template(sql.FieldNEQ(FieldDisplayName, v)) +} + +// DisplayNameIn applies the In predicate on the "display_name" field. +func DisplayNameIn(vs ...string) predicate.Template { + return predicate.Template(sql.FieldIn(FieldDisplayName, vs...)) +} + +// DisplayNameNotIn applies the NotIn predicate on the "display_name" field. +func DisplayNameNotIn(vs ...string) predicate.Template { + return predicate.Template(sql.FieldNotIn(FieldDisplayName, vs...)) +} + +// DisplayNameGT applies the GT predicate on the "display_name" field. +func DisplayNameGT(v string) predicate.Template { + return predicate.Template(sql.FieldGT(FieldDisplayName, v)) +} + +// DisplayNameGTE applies the GTE predicate on the "display_name" field. +func DisplayNameGTE(v string) predicate.Template { + return predicate.Template(sql.FieldGTE(FieldDisplayName, v)) +} + +// DisplayNameLT applies the LT predicate on the "display_name" field. +func DisplayNameLT(v string) predicate.Template { + return predicate.Template(sql.FieldLT(FieldDisplayName, v)) +} + +// DisplayNameLTE applies the LTE predicate on the "display_name" field. +func DisplayNameLTE(v string) predicate.Template { + return predicate.Template(sql.FieldLTE(FieldDisplayName, v)) +} + +// DisplayNameContains applies the Contains predicate on the "display_name" field. +func DisplayNameContains(v string) predicate.Template { + return predicate.Template(sql.FieldContains(FieldDisplayName, v)) +} + +// DisplayNameHasPrefix applies the HasPrefix predicate on the "display_name" field. +func DisplayNameHasPrefix(v string) predicate.Template { + return predicate.Template(sql.FieldHasPrefix(FieldDisplayName, v)) +} + +// DisplayNameHasSuffix applies the HasSuffix predicate on the "display_name" field. +func DisplayNameHasSuffix(v string) predicate.Template { + return predicate.Template(sql.FieldHasSuffix(FieldDisplayName, v)) +} + +// DisplayNameIsNil applies the IsNil predicate on the "display_name" field. +func DisplayNameIsNil() predicate.Template { + return predicate.Template(sql.FieldIsNull(FieldDisplayName)) +} + +// DisplayNameNotNil applies the NotNil predicate on the "display_name" field. +func DisplayNameNotNil() predicate.Template { + return predicate.Template(sql.FieldNotNull(FieldDisplayName)) +} + +// DisplayNameEqualFold applies the EqualFold predicate on the "display_name" field. +func DisplayNameEqualFold(v string) predicate.Template { + return predicate.Template(sql.FieldEqualFold(FieldDisplayName, v)) +} + +// DisplayNameContainsFold applies the ContainsFold predicate on the "display_name" field. +func DisplayNameContainsFold(v string) predicate.Template { + return predicate.Template(sql.FieldContainsFold(FieldDisplayName, v)) +} + +// DescriptionEQ applies the EQ predicate on the "description" field. +func DescriptionEQ(v string) predicate.Template { + return predicate.Template(sql.FieldEQ(FieldDescription, v)) +} + +// DescriptionNEQ applies the NEQ predicate on the "description" field. +func DescriptionNEQ(v string) predicate.Template { + return predicate.Template(sql.FieldNEQ(FieldDescription, v)) +} + +// DescriptionIn applies the In predicate on the "description" field. +func DescriptionIn(vs ...string) predicate.Template { + return predicate.Template(sql.FieldIn(FieldDescription, vs...)) +} + +// DescriptionNotIn applies the NotIn predicate on the "description" field. +func DescriptionNotIn(vs ...string) predicate.Template { + return predicate.Template(sql.FieldNotIn(FieldDescription, vs...)) +} + +// DescriptionGT applies the GT predicate on the "description" field. +func DescriptionGT(v string) predicate.Template { + return predicate.Template(sql.FieldGT(FieldDescription, v)) +} + +// DescriptionGTE applies the GTE predicate on the "description" field. +func DescriptionGTE(v string) predicate.Template { + return predicate.Template(sql.FieldGTE(FieldDescription, v)) +} + +// DescriptionLT applies the LT predicate on the "description" field. +func DescriptionLT(v string) predicate.Template { + return predicate.Template(sql.FieldLT(FieldDescription, v)) +} + +// DescriptionLTE applies the LTE predicate on the "description" field. +func DescriptionLTE(v string) predicate.Template { + return predicate.Template(sql.FieldLTE(FieldDescription, v)) +} + +// DescriptionContains applies the Contains predicate on the "description" field. +func DescriptionContains(v string) predicate.Template { + return predicate.Template(sql.FieldContains(FieldDescription, v)) +} + +// DescriptionHasPrefix applies the HasPrefix predicate on the "description" field. +func DescriptionHasPrefix(v string) predicate.Template { + return predicate.Template(sql.FieldHasPrefix(FieldDescription, v)) +} + +// DescriptionHasSuffix applies the HasSuffix predicate on the "description" field. +func DescriptionHasSuffix(v string) predicate.Template { + return predicate.Template(sql.FieldHasSuffix(FieldDescription, v)) +} + +// DescriptionIsNil applies the IsNil predicate on the "description" field. +func DescriptionIsNil() predicate.Template { + return predicate.Template(sql.FieldIsNull(FieldDescription)) +} + +// DescriptionNotNil applies the NotNil predicate on the "description" field. +func DescriptionNotNil() predicate.Template { + return predicate.Template(sql.FieldNotNull(FieldDescription)) +} + +// DescriptionEqualFold applies the EqualFold predicate on the "description" field. +func DescriptionEqualFold(v string) predicate.Template { + return predicate.Template(sql.FieldEqualFold(FieldDescription, v)) +} + +// DescriptionContainsFold applies the ContainsFold predicate on the "description" field. +func DescriptionContainsFold(v string) predicate.Template { + return predicate.Template(sql.FieldContainsFold(FieldDescription, v)) +} + +// HarnessEQ applies the EQ predicate on the "harness" field. +func HarnessEQ(v string) predicate.Template { + return predicate.Template(sql.FieldEQ(FieldHarness, v)) +} + +// HarnessNEQ applies the NEQ predicate on the "harness" field. +func HarnessNEQ(v string) predicate.Template { + return predicate.Template(sql.FieldNEQ(FieldHarness, v)) +} + +// HarnessIn applies the In predicate on the "harness" field. +func HarnessIn(vs ...string) predicate.Template { + return predicate.Template(sql.FieldIn(FieldHarness, vs...)) +} + +// HarnessNotIn applies the NotIn predicate on the "harness" field. +func HarnessNotIn(vs ...string) predicate.Template { + return predicate.Template(sql.FieldNotIn(FieldHarness, vs...)) +} + +// HarnessGT applies the GT predicate on the "harness" field. +func HarnessGT(v string) predicate.Template { + return predicate.Template(sql.FieldGT(FieldHarness, v)) +} + +// HarnessGTE applies the GTE predicate on the "harness" field. +func HarnessGTE(v string) predicate.Template { + return predicate.Template(sql.FieldGTE(FieldHarness, v)) +} + +// HarnessLT applies the LT predicate on the "harness" field. +func HarnessLT(v string) predicate.Template { + return predicate.Template(sql.FieldLT(FieldHarness, v)) +} + +// HarnessLTE applies the LTE predicate on the "harness" field. +func HarnessLTE(v string) predicate.Template { + return predicate.Template(sql.FieldLTE(FieldHarness, v)) +} + +// HarnessContains applies the Contains predicate on the "harness" field. +func HarnessContains(v string) predicate.Template { + return predicate.Template(sql.FieldContains(FieldHarness, v)) +} + +// HarnessHasPrefix applies the HasPrefix predicate on the "harness" field. +func HarnessHasPrefix(v string) predicate.Template { + return predicate.Template(sql.FieldHasPrefix(FieldHarness, v)) +} + +// HarnessHasSuffix applies the HasSuffix predicate on the "harness" field. +func HarnessHasSuffix(v string) predicate.Template { + return predicate.Template(sql.FieldHasSuffix(FieldHarness, v)) +} + +// HarnessEqualFold applies the EqualFold predicate on the "harness" field. +func HarnessEqualFold(v string) predicate.Template { + return predicate.Template(sql.FieldEqualFold(FieldHarness, v)) +} + +// HarnessContainsFold applies the ContainsFold predicate on the "harness" field. +func HarnessContainsFold(v string) predicate.Template { + return predicate.Template(sql.FieldContainsFold(FieldHarness, v)) +} + +// DefaultHarnessConfigEQ applies the EQ predicate on the "default_harness_config" field. +func DefaultHarnessConfigEQ(v string) predicate.Template { + return predicate.Template(sql.FieldEQ(FieldDefaultHarnessConfig, v)) +} + +// DefaultHarnessConfigNEQ applies the NEQ predicate on the "default_harness_config" field. +func DefaultHarnessConfigNEQ(v string) predicate.Template { + return predicate.Template(sql.FieldNEQ(FieldDefaultHarnessConfig, v)) +} + +// DefaultHarnessConfigIn applies the In predicate on the "default_harness_config" field. +func DefaultHarnessConfigIn(vs ...string) predicate.Template { + return predicate.Template(sql.FieldIn(FieldDefaultHarnessConfig, vs...)) +} + +// DefaultHarnessConfigNotIn applies the NotIn predicate on the "default_harness_config" field. +func DefaultHarnessConfigNotIn(vs ...string) predicate.Template { + return predicate.Template(sql.FieldNotIn(FieldDefaultHarnessConfig, vs...)) +} + +// DefaultHarnessConfigGT applies the GT predicate on the "default_harness_config" field. +func DefaultHarnessConfigGT(v string) predicate.Template { + return predicate.Template(sql.FieldGT(FieldDefaultHarnessConfig, v)) +} + +// DefaultHarnessConfigGTE applies the GTE predicate on the "default_harness_config" field. +func DefaultHarnessConfigGTE(v string) predicate.Template { + return predicate.Template(sql.FieldGTE(FieldDefaultHarnessConfig, v)) +} + +// DefaultHarnessConfigLT applies the LT predicate on the "default_harness_config" field. +func DefaultHarnessConfigLT(v string) predicate.Template { + return predicate.Template(sql.FieldLT(FieldDefaultHarnessConfig, v)) +} + +// DefaultHarnessConfigLTE applies the LTE predicate on the "default_harness_config" field. +func DefaultHarnessConfigLTE(v string) predicate.Template { + return predicate.Template(sql.FieldLTE(FieldDefaultHarnessConfig, v)) +} + +// DefaultHarnessConfigContains applies the Contains predicate on the "default_harness_config" field. +func DefaultHarnessConfigContains(v string) predicate.Template { + return predicate.Template(sql.FieldContains(FieldDefaultHarnessConfig, v)) +} + +// DefaultHarnessConfigHasPrefix applies the HasPrefix predicate on the "default_harness_config" field. +func DefaultHarnessConfigHasPrefix(v string) predicate.Template { + return predicate.Template(sql.FieldHasPrefix(FieldDefaultHarnessConfig, v)) +} + +// DefaultHarnessConfigHasSuffix applies the HasSuffix predicate on the "default_harness_config" field. +func DefaultHarnessConfigHasSuffix(v string) predicate.Template { + return predicate.Template(sql.FieldHasSuffix(FieldDefaultHarnessConfig, v)) +} + +// DefaultHarnessConfigIsNil applies the IsNil predicate on the "default_harness_config" field. +func DefaultHarnessConfigIsNil() predicate.Template { + return predicate.Template(sql.FieldIsNull(FieldDefaultHarnessConfig)) +} + +// DefaultHarnessConfigNotNil applies the NotNil predicate on the "default_harness_config" field. +func DefaultHarnessConfigNotNil() predicate.Template { + return predicate.Template(sql.FieldNotNull(FieldDefaultHarnessConfig)) +} + +// DefaultHarnessConfigEqualFold applies the EqualFold predicate on the "default_harness_config" field. +func DefaultHarnessConfigEqualFold(v string) predicate.Template { + return predicate.Template(sql.FieldEqualFold(FieldDefaultHarnessConfig, v)) +} + +// DefaultHarnessConfigContainsFold applies the ContainsFold predicate on the "default_harness_config" field. +func DefaultHarnessConfigContainsFold(v string) predicate.Template { + return predicate.Template(sql.FieldContainsFold(FieldDefaultHarnessConfig, v)) +} + +// ImageEQ applies the EQ predicate on the "image" field. +func ImageEQ(v string) predicate.Template { + return predicate.Template(sql.FieldEQ(FieldImage, v)) +} + +// ImageNEQ applies the NEQ predicate on the "image" field. +func ImageNEQ(v string) predicate.Template { + return predicate.Template(sql.FieldNEQ(FieldImage, v)) +} + +// ImageIn applies the In predicate on the "image" field. +func ImageIn(vs ...string) predicate.Template { + return predicate.Template(sql.FieldIn(FieldImage, vs...)) +} + +// ImageNotIn applies the NotIn predicate on the "image" field. +func ImageNotIn(vs ...string) predicate.Template { + return predicate.Template(sql.FieldNotIn(FieldImage, vs...)) +} + +// ImageGT applies the GT predicate on the "image" field. +func ImageGT(v string) predicate.Template { + return predicate.Template(sql.FieldGT(FieldImage, v)) +} + +// ImageGTE applies the GTE predicate on the "image" field. +func ImageGTE(v string) predicate.Template { + return predicate.Template(sql.FieldGTE(FieldImage, v)) +} + +// ImageLT applies the LT predicate on the "image" field. +func ImageLT(v string) predicate.Template { + return predicate.Template(sql.FieldLT(FieldImage, v)) +} + +// ImageLTE applies the LTE predicate on the "image" field. +func ImageLTE(v string) predicate.Template { + return predicate.Template(sql.FieldLTE(FieldImage, v)) +} + +// ImageContains applies the Contains predicate on the "image" field. +func ImageContains(v string) predicate.Template { + return predicate.Template(sql.FieldContains(FieldImage, v)) +} + +// ImageHasPrefix applies the HasPrefix predicate on the "image" field. +func ImageHasPrefix(v string) predicate.Template { + return predicate.Template(sql.FieldHasPrefix(FieldImage, v)) +} + +// ImageHasSuffix applies the HasSuffix predicate on the "image" field. +func ImageHasSuffix(v string) predicate.Template { + return predicate.Template(sql.FieldHasSuffix(FieldImage, v)) +} + +// ImageIsNil applies the IsNil predicate on the "image" field. +func ImageIsNil() predicate.Template { + return predicate.Template(sql.FieldIsNull(FieldImage)) +} + +// ImageNotNil applies the NotNil predicate on the "image" field. +func ImageNotNil() predicate.Template { + return predicate.Template(sql.FieldNotNull(FieldImage)) +} + +// ImageEqualFold applies the EqualFold predicate on the "image" field. +func ImageEqualFold(v string) predicate.Template { + return predicate.Template(sql.FieldEqualFold(FieldImage, v)) +} + +// ImageContainsFold applies the ContainsFold predicate on the "image" field. +func ImageContainsFold(v string) predicate.Template { + return predicate.Template(sql.FieldContainsFold(FieldImage, v)) +} + +// ConfigEQ applies the EQ predicate on the "config" field. +func ConfigEQ(v string) predicate.Template { + return predicate.Template(sql.FieldEQ(FieldConfig, v)) +} + +// ConfigNEQ applies the NEQ predicate on the "config" field. +func ConfigNEQ(v string) predicate.Template { + return predicate.Template(sql.FieldNEQ(FieldConfig, v)) +} + +// ConfigIn applies the In predicate on the "config" field. +func ConfigIn(vs ...string) predicate.Template { + return predicate.Template(sql.FieldIn(FieldConfig, vs...)) +} + +// ConfigNotIn applies the NotIn predicate on the "config" field. +func ConfigNotIn(vs ...string) predicate.Template { + return predicate.Template(sql.FieldNotIn(FieldConfig, vs...)) +} + +// ConfigGT applies the GT predicate on the "config" field. +func ConfigGT(v string) predicate.Template { + return predicate.Template(sql.FieldGT(FieldConfig, v)) +} + +// ConfigGTE applies the GTE predicate on the "config" field. +func ConfigGTE(v string) predicate.Template { + return predicate.Template(sql.FieldGTE(FieldConfig, v)) +} + +// ConfigLT applies the LT predicate on the "config" field. +func ConfigLT(v string) predicate.Template { + return predicate.Template(sql.FieldLT(FieldConfig, v)) +} + +// ConfigLTE applies the LTE predicate on the "config" field. +func ConfigLTE(v string) predicate.Template { + return predicate.Template(sql.FieldLTE(FieldConfig, v)) +} + +// ConfigContains applies the Contains predicate on the "config" field. +func ConfigContains(v string) predicate.Template { + return predicate.Template(sql.FieldContains(FieldConfig, v)) +} + +// ConfigHasPrefix applies the HasPrefix predicate on the "config" field. +func ConfigHasPrefix(v string) predicate.Template { + return predicate.Template(sql.FieldHasPrefix(FieldConfig, v)) +} + +// ConfigHasSuffix applies the HasSuffix predicate on the "config" field. +func ConfigHasSuffix(v string) predicate.Template { + return predicate.Template(sql.FieldHasSuffix(FieldConfig, v)) +} + +// ConfigIsNil applies the IsNil predicate on the "config" field. +func ConfigIsNil() predicate.Template { + return predicate.Template(sql.FieldIsNull(FieldConfig)) +} + +// ConfigNotNil applies the NotNil predicate on the "config" field. +func ConfigNotNil() predicate.Template { + return predicate.Template(sql.FieldNotNull(FieldConfig)) +} + +// ConfigEqualFold applies the EqualFold predicate on the "config" field. +func ConfigEqualFold(v string) predicate.Template { + return predicate.Template(sql.FieldEqualFold(FieldConfig, v)) +} + +// ConfigContainsFold applies the ContainsFold predicate on the "config" field. +func ConfigContainsFold(v string) predicate.Template { + return predicate.Template(sql.FieldContainsFold(FieldConfig, v)) +} + +// ContentHashEQ applies the EQ predicate on the "content_hash" field. +func ContentHashEQ(v string) predicate.Template { + return predicate.Template(sql.FieldEQ(FieldContentHash, v)) +} + +// ContentHashNEQ applies the NEQ predicate on the "content_hash" field. +func ContentHashNEQ(v string) predicate.Template { + return predicate.Template(sql.FieldNEQ(FieldContentHash, v)) +} + +// ContentHashIn applies the In predicate on the "content_hash" field. +func ContentHashIn(vs ...string) predicate.Template { + return predicate.Template(sql.FieldIn(FieldContentHash, vs...)) +} + +// ContentHashNotIn applies the NotIn predicate on the "content_hash" field. +func ContentHashNotIn(vs ...string) predicate.Template { + return predicate.Template(sql.FieldNotIn(FieldContentHash, vs...)) +} + +// ContentHashGT applies the GT predicate on the "content_hash" field. +func ContentHashGT(v string) predicate.Template { + return predicate.Template(sql.FieldGT(FieldContentHash, v)) +} + +// ContentHashGTE applies the GTE predicate on the "content_hash" field. +func ContentHashGTE(v string) predicate.Template { + return predicate.Template(sql.FieldGTE(FieldContentHash, v)) +} + +// ContentHashLT applies the LT predicate on the "content_hash" field. +func ContentHashLT(v string) predicate.Template { + return predicate.Template(sql.FieldLT(FieldContentHash, v)) +} + +// ContentHashLTE applies the LTE predicate on the "content_hash" field. +func ContentHashLTE(v string) predicate.Template { + return predicate.Template(sql.FieldLTE(FieldContentHash, v)) +} + +// ContentHashContains applies the Contains predicate on the "content_hash" field. +func ContentHashContains(v string) predicate.Template { + return predicate.Template(sql.FieldContains(FieldContentHash, v)) +} + +// ContentHashHasPrefix applies the HasPrefix predicate on the "content_hash" field. +func ContentHashHasPrefix(v string) predicate.Template { + return predicate.Template(sql.FieldHasPrefix(FieldContentHash, v)) +} + +// ContentHashHasSuffix applies the HasSuffix predicate on the "content_hash" field. +func ContentHashHasSuffix(v string) predicate.Template { + return predicate.Template(sql.FieldHasSuffix(FieldContentHash, v)) +} + +// ContentHashIsNil applies the IsNil predicate on the "content_hash" field. +func ContentHashIsNil() predicate.Template { + return predicate.Template(sql.FieldIsNull(FieldContentHash)) +} + +// ContentHashNotNil applies the NotNil predicate on the "content_hash" field. +func ContentHashNotNil() predicate.Template { + return predicate.Template(sql.FieldNotNull(FieldContentHash)) +} + +// ContentHashEqualFold applies the EqualFold predicate on the "content_hash" field. +func ContentHashEqualFold(v string) predicate.Template { + return predicate.Template(sql.FieldEqualFold(FieldContentHash, v)) +} + +// ContentHashContainsFold applies the ContainsFold predicate on the "content_hash" field. +func ContentHashContainsFold(v string) predicate.Template { + return predicate.Template(sql.FieldContainsFold(FieldContentHash, v)) +} + +// ScopeEQ applies the EQ predicate on the "scope" field. +func ScopeEQ(v string) predicate.Template { + return predicate.Template(sql.FieldEQ(FieldScope, v)) +} + +// ScopeNEQ applies the NEQ predicate on the "scope" field. +func ScopeNEQ(v string) predicate.Template { + return predicate.Template(sql.FieldNEQ(FieldScope, v)) +} + +// ScopeIn applies the In predicate on the "scope" field. +func ScopeIn(vs ...string) predicate.Template { + return predicate.Template(sql.FieldIn(FieldScope, vs...)) +} + +// ScopeNotIn applies the NotIn predicate on the "scope" field. +func ScopeNotIn(vs ...string) predicate.Template { + return predicate.Template(sql.FieldNotIn(FieldScope, vs...)) +} + +// ScopeGT applies the GT predicate on the "scope" field. +func ScopeGT(v string) predicate.Template { + return predicate.Template(sql.FieldGT(FieldScope, v)) +} + +// ScopeGTE applies the GTE predicate on the "scope" field. +func ScopeGTE(v string) predicate.Template { + return predicate.Template(sql.FieldGTE(FieldScope, v)) +} + +// ScopeLT applies the LT predicate on the "scope" field. +func ScopeLT(v string) predicate.Template { + return predicate.Template(sql.FieldLT(FieldScope, v)) +} + +// ScopeLTE applies the LTE predicate on the "scope" field. +func ScopeLTE(v string) predicate.Template { + return predicate.Template(sql.FieldLTE(FieldScope, v)) +} + +// ScopeContains applies the Contains predicate on the "scope" field. +func ScopeContains(v string) predicate.Template { + return predicate.Template(sql.FieldContains(FieldScope, v)) +} + +// ScopeHasPrefix applies the HasPrefix predicate on the "scope" field. +func ScopeHasPrefix(v string) predicate.Template { + return predicate.Template(sql.FieldHasPrefix(FieldScope, v)) +} + +// ScopeHasSuffix applies the HasSuffix predicate on the "scope" field. +func ScopeHasSuffix(v string) predicate.Template { + return predicate.Template(sql.FieldHasSuffix(FieldScope, v)) +} + +// ScopeEqualFold applies the EqualFold predicate on the "scope" field. +func ScopeEqualFold(v string) predicate.Template { + return predicate.Template(sql.FieldEqualFold(FieldScope, v)) +} + +// ScopeContainsFold applies the ContainsFold predicate on the "scope" field. +func ScopeContainsFold(v string) predicate.Template { + return predicate.Template(sql.FieldContainsFold(FieldScope, v)) +} + +// ScopeIDEQ applies the EQ predicate on the "scope_id" field. +func ScopeIDEQ(v string) predicate.Template { + return predicate.Template(sql.FieldEQ(FieldScopeID, v)) +} + +// ScopeIDNEQ applies the NEQ predicate on the "scope_id" field. +func ScopeIDNEQ(v string) predicate.Template { + return predicate.Template(sql.FieldNEQ(FieldScopeID, v)) +} + +// ScopeIDIn applies the In predicate on the "scope_id" field. +func ScopeIDIn(vs ...string) predicate.Template { + return predicate.Template(sql.FieldIn(FieldScopeID, vs...)) +} + +// ScopeIDNotIn applies the NotIn predicate on the "scope_id" field. +func ScopeIDNotIn(vs ...string) predicate.Template { + return predicate.Template(sql.FieldNotIn(FieldScopeID, vs...)) +} + +// ScopeIDGT applies the GT predicate on the "scope_id" field. +func ScopeIDGT(v string) predicate.Template { + return predicate.Template(sql.FieldGT(FieldScopeID, v)) +} + +// ScopeIDGTE applies the GTE predicate on the "scope_id" field. +func ScopeIDGTE(v string) predicate.Template { + return predicate.Template(sql.FieldGTE(FieldScopeID, v)) +} + +// ScopeIDLT applies the LT predicate on the "scope_id" field. +func ScopeIDLT(v string) predicate.Template { + return predicate.Template(sql.FieldLT(FieldScopeID, v)) +} + +// ScopeIDLTE applies the LTE predicate on the "scope_id" field. +func ScopeIDLTE(v string) predicate.Template { + return predicate.Template(sql.FieldLTE(FieldScopeID, v)) +} + +// ScopeIDContains applies the Contains predicate on the "scope_id" field. +func ScopeIDContains(v string) predicate.Template { + return predicate.Template(sql.FieldContains(FieldScopeID, v)) +} + +// ScopeIDHasPrefix applies the HasPrefix predicate on the "scope_id" field. +func ScopeIDHasPrefix(v string) predicate.Template { + return predicate.Template(sql.FieldHasPrefix(FieldScopeID, v)) +} + +// ScopeIDHasSuffix applies the HasSuffix predicate on the "scope_id" field. +func ScopeIDHasSuffix(v string) predicate.Template { + return predicate.Template(sql.FieldHasSuffix(FieldScopeID, v)) +} + +// ScopeIDIsNil applies the IsNil predicate on the "scope_id" field. +func ScopeIDIsNil() predicate.Template { + return predicate.Template(sql.FieldIsNull(FieldScopeID)) +} + +// ScopeIDNotNil applies the NotNil predicate on the "scope_id" field. +func ScopeIDNotNil() predicate.Template { + return predicate.Template(sql.FieldNotNull(FieldScopeID)) +} + +// ScopeIDEqualFold applies the EqualFold predicate on the "scope_id" field. +func ScopeIDEqualFold(v string) predicate.Template { + return predicate.Template(sql.FieldEqualFold(FieldScopeID, v)) +} + +// ScopeIDContainsFold applies the ContainsFold predicate on the "scope_id" field. +func ScopeIDContainsFold(v string) predicate.Template { + return predicate.Template(sql.FieldContainsFold(FieldScopeID, v)) +} + +// ProjectIDEQ applies the EQ predicate on the "project_id" field. +func ProjectIDEQ(v string) predicate.Template { + return predicate.Template(sql.FieldEQ(FieldProjectID, v)) +} + +// ProjectIDNEQ applies the NEQ predicate on the "project_id" field. +func ProjectIDNEQ(v string) predicate.Template { + return predicate.Template(sql.FieldNEQ(FieldProjectID, v)) +} + +// ProjectIDIn applies the In predicate on the "project_id" field. +func ProjectIDIn(vs ...string) predicate.Template { + return predicate.Template(sql.FieldIn(FieldProjectID, vs...)) +} + +// ProjectIDNotIn applies the NotIn predicate on the "project_id" field. +func ProjectIDNotIn(vs ...string) predicate.Template { + return predicate.Template(sql.FieldNotIn(FieldProjectID, vs...)) +} + +// ProjectIDGT applies the GT predicate on the "project_id" field. +func ProjectIDGT(v string) predicate.Template { + return predicate.Template(sql.FieldGT(FieldProjectID, v)) +} + +// ProjectIDGTE applies the GTE predicate on the "project_id" field. +func ProjectIDGTE(v string) predicate.Template { + return predicate.Template(sql.FieldGTE(FieldProjectID, v)) +} + +// ProjectIDLT applies the LT predicate on the "project_id" field. +func ProjectIDLT(v string) predicate.Template { + return predicate.Template(sql.FieldLT(FieldProjectID, v)) +} + +// ProjectIDLTE applies the LTE predicate on the "project_id" field. +func ProjectIDLTE(v string) predicate.Template { + return predicate.Template(sql.FieldLTE(FieldProjectID, v)) +} + +// ProjectIDContains applies the Contains predicate on the "project_id" field. +func ProjectIDContains(v string) predicate.Template { + return predicate.Template(sql.FieldContains(FieldProjectID, v)) +} + +// ProjectIDHasPrefix applies the HasPrefix predicate on the "project_id" field. +func ProjectIDHasPrefix(v string) predicate.Template { + return predicate.Template(sql.FieldHasPrefix(FieldProjectID, v)) +} + +// ProjectIDHasSuffix applies the HasSuffix predicate on the "project_id" field. +func ProjectIDHasSuffix(v string) predicate.Template { + return predicate.Template(sql.FieldHasSuffix(FieldProjectID, v)) +} + +// ProjectIDIsNil applies the IsNil predicate on the "project_id" field. +func ProjectIDIsNil() predicate.Template { + return predicate.Template(sql.FieldIsNull(FieldProjectID)) +} + +// ProjectIDNotNil applies the NotNil predicate on the "project_id" field. +func ProjectIDNotNil() predicate.Template { + return predicate.Template(sql.FieldNotNull(FieldProjectID)) +} + +// ProjectIDEqualFold applies the EqualFold predicate on the "project_id" field. +func ProjectIDEqualFold(v string) predicate.Template { + return predicate.Template(sql.FieldEqualFold(FieldProjectID, v)) +} + +// ProjectIDContainsFold applies the ContainsFold predicate on the "project_id" field. +func ProjectIDContainsFold(v string) predicate.Template { + return predicate.Template(sql.FieldContainsFold(FieldProjectID, v)) +} + +// StorageURIEQ applies the EQ predicate on the "storage_uri" field. +func StorageURIEQ(v string) predicate.Template { + return predicate.Template(sql.FieldEQ(FieldStorageURI, v)) +} + +// StorageURINEQ applies the NEQ predicate on the "storage_uri" field. +func StorageURINEQ(v string) predicate.Template { + return predicate.Template(sql.FieldNEQ(FieldStorageURI, v)) +} + +// StorageURIIn applies the In predicate on the "storage_uri" field. +func StorageURIIn(vs ...string) predicate.Template { + return predicate.Template(sql.FieldIn(FieldStorageURI, vs...)) +} + +// StorageURINotIn applies the NotIn predicate on the "storage_uri" field. +func StorageURINotIn(vs ...string) predicate.Template { + return predicate.Template(sql.FieldNotIn(FieldStorageURI, vs...)) +} + +// StorageURIGT applies the GT predicate on the "storage_uri" field. +func StorageURIGT(v string) predicate.Template { + return predicate.Template(sql.FieldGT(FieldStorageURI, v)) +} + +// StorageURIGTE applies the GTE predicate on the "storage_uri" field. +func StorageURIGTE(v string) predicate.Template { + return predicate.Template(sql.FieldGTE(FieldStorageURI, v)) +} + +// StorageURILT applies the LT predicate on the "storage_uri" field. +func StorageURILT(v string) predicate.Template { + return predicate.Template(sql.FieldLT(FieldStorageURI, v)) +} + +// StorageURILTE applies the LTE predicate on the "storage_uri" field. +func StorageURILTE(v string) predicate.Template { + return predicate.Template(sql.FieldLTE(FieldStorageURI, v)) +} + +// StorageURIContains applies the Contains predicate on the "storage_uri" field. +func StorageURIContains(v string) predicate.Template { + return predicate.Template(sql.FieldContains(FieldStorageURI, v)) +} + +// StorageURIHasPrefix applies the HasPrefix predicate on the "storage_uri" field. +func StorageURIHasPrefix(v string) predicate.Template { + return predicate.Template(sql.FieldHasPrefix(FieldStorageURI, v)) +} + +// StorageURIHasSuffix applies the HasSuffix predicate on the "storage_uri" field. +func StorageURIHasSuffix(v string) predicate.Template { + return predicate.Template(sql.FieldHasSuffix(FieldStorageURI, v)) +} + +// StorageURIIsNil applies the IsNil predicate on the "storage_uri" field. +func StorageURIIsNil() predicate.Template { + return predicate.Template(sql.FieldIsNull(FieldStorageURI)) +} + +// StorageURINotNil applies the NotNil predicate on the "storage_uri" field. +func StorageURINotNil() predicate.Template { + return predicate.Template(sql.FieldNotNull(FieldStorageURI)) +} + +// StorageURIEqualFold applies the EqualFold predicate on the "storage_uri" field. +func StorageURIEqualFold(v string) predicate.Template { + return predicate.Template(sql.FieldEqualFold(FieldStorageURI, v)) +} + +// StorageURIContainsFold applies the ContainsFold predicate on the "storage_uri" field. +func StorageURIContainsFold(v string) predicate.Template { + return predicate.Template(sql.FieldContainsFold(FieldStorageURI, v)) +} + +// StorageBucketEQ applies the EQ predicate on the "storage_bucket" field. +func StorageBucketEQ(v string) predicate.Template { + return predicate.Template(sql.FieldEQ(FieldStorageBucket, v)) +} + +// StorageBucketNEQ applies the NEQ predicate on the "storage_bucket" field. +func StorageBucketNEQ(v string) predicate.Template { + return predicate.Template(sql.FieldNEQ(FieldStorageBucket, v)) +} + +// StorageBucketIn applies the In predicate on the "storage_bucket" field. +func StorageBucketIn(vs ...string) predicate.Template { + return predicate.Template(sql.FieldIn(FieldStorageBucket, vs...)) +} + +// StorageBucketNotIn applies the NotIn predicate on the "storage_bucket" field. +func StorageBucketNotIn(vs ...string) predicate.Template { + return predicate.Template(sql.FieldNotIn(FieldStorageBucket, vs...)) +} + +// StorageBucketGT applies the GT predicate on the "storage_bucket" field. +func StorageBucketGT(v string) predicate.Template { + return predicate.Template(sql.FieldGT(FieldStorageBucket, v)) +} + +// StorageBucketGTE applies the GTE predicate on the "storage_bucket" field. +func StorageBucketGTE(v string) predicate.Template { + return predicate.Template(sql.FieldGTE(FieldStorageBucket, v)) +} + +// StorageBucketLT applies the LT predicate on the "storage_bucket" field. +func StorageBucketLT(v string) predicate.Template { + return predicate.Template(sql.FieldLT(FieldStorageBucket, v)) +} + +// StorageBucketLTE applies the LTE predicate on the "storage_bucket" field. +func StorageBucketLTE(v string) predicate.Template { + return predicate.Template(sql.FieldLTE(FieldStorageBucket, v)) +} + +// StorageBucketContains applies the Contains predicate on the "storage_bucket" field. +func StorageBucketContains(v string) predicate.Template { + return predicate.Template(sql.FieldContains(FieldStorageBucket, v)) +} + +// StorageBucketHasPrefix applies the HasPrefix predicate on the "storage_bucket" field. +func StorageBucketHasPrefix(v string) predicate.Template { + return predicate.Template(sql.FieldHasPrefix(FieldStorageBucket, v)) +} + +// StorageBucketHasSuffix applies the HasSuffix predicate on the "storage_bucket" field. +func StorageBucketHasSuffix(v string) predicate.Template { + return predicate.Template(sql.FieldHasSuffix(FieldStorageBucket, v)) +} + +// StorageBucketIsNil applies the IsNil predicate on the "storage_bucket" field. +func StorageBucketIsNil() predicate.Template { + return predicate.Template(sql.FieldIsNull(FieldStorageBucket)) +} + +// StorageBucketNotNil applies the NotNil predicate on the "storage_bucket" field. +func StorageBucketNotNil() predicate.Template { + return predicate.Template(sql.FieldNotNull(FieldStorageBucket)) +} + +// StorageBucketEqualFold applies the EqualFold predicate on the "storage_bucket" field. +func StorageBucketEqualFold(v string) predicate.Template { + return predicate.Template(sql.FieldEqualFold(FieldStorageBucket, v)) +} + +// StorageBucketContainsFold applies the ContainsFold predicate on the "storage_bucket" field. +func StorageBucketContainsFold(v string) predicate.Template { + return predicate.Template(sql.FieldContainsFold(FieldStorageBucket, v)) +} + +// StoragePathEQ applies the EQ predicate on the "storage_path" field. +func StoragePathEQ(v string) predicate.Template { + return predicate.Template(sql.FieldEQ(FieldStoragePath, v)) +} + +// StoragePathNEQ applies the NEQ predicate on the "storage_path" field. +func StoragePathNEQ(v string) predicate.Template { + return predicate.Template(sql.FieldNEQ(FieldStoragePath, v)) +} + +// StoragePathIn applies the In predicate on the "storage_path" field. +func StoragePathIn(vs ...string) predicate.Template { + return predicate.Template(sql.FieldIn(FieldStoragePath, vs...)) +} + +// StoragePathNotIn applies the NotIn predicate on the "storage_path" field. +func StoragePathNotIn(vs ...string) predicate.Template { + return predicate.Template(sql.FieldNotIn(FieldStoragePath, vs...)) +} + +// StoragePathGT applies the GT predicate on the "storage_path" field. +func StoragePathGT(v string) predicate.Template { + return predicate.Template(sql.FieldGT(FieldStoragePath, v)) +} + +// StoragePathGTE applies the GTE predicate on the "storage_path" field. +func StoragePathGTE(v string) predicate.Template { + return predicate.Template(sql.FieldGTE(FieldStoragePath, v)) +} + +// StoragePathLT applies the LT predicate on the "storage_path" field. +func StoragePathLT(v string) predicate.Template { + return predicate.Template(sql.FieldLT(FieldStoragePath, v)) +} + +// StoragePathLTE applies the LTE predicate on the "storage_path" field. +func StoragePathLTE(v string) predicate.Template { + return predicate.Template(sql.FieldLTE(FieldStoragePath, v)) +} + +// StoragePathContains applies the Contains predicate on the "storage_path" field. +func StoragePathContains(v string) predicate.Template { + return predicate.Template(sql.FieldContains(FieldStoragePath, v)) +} + +// StoragePathHasPrefix applies the HasPrefix predicate on the "storage_path" field. +func StoragePathHasPrefix(v string) predicate.Template { + return predicate.Template(sql.FieldHasPrefix(FieldStoragePath, v)) +} + +// StoragePathHasSuffix applies the HasSuffix predicate on the "storage_path" field. +func StoragePathHasSuffix(v string) predicate.Template { + return predicate.Template(sql.FieldHasSuffix(FieldStoragePath, v)) +} + +// StoragePathIsNil applies the IsNil predicate on the "storage_path" field. +func StoragePathIsNil() predicate.Template { + return predicate.Template(sql.FieldIsNull(FieldStoragePath)) +} + +// StoragePathNotNil applies the NotNil predicate on the "storage_path" field. +func StoragePathNotNil() predicate.Template { + return predicate.Template(sql.FieldNotNull(FieldStoragePath)) +} + +// StoragePathEqualFold applies the EqualFold predicate on the "storage_path" field. +func StoragePathEqualFold(v string) predicate.Template { + return predicate.Template(sql.FieldEqualFold(FieldStoragePath, v)) +} + +// StoragePathContainsFold applies the ContainsFold predicate on the "storage_path" field. +func StoragePathContainsFold(v string) predicate.Template { + return predicate.Template(sql.FieldContainsFold(FieldStoragePath, v)) +} + +// FilesEQ applies the EQ predicate on the "files" field. +func FilesEQ(v string) predicate.Template { + return predicate.Template(sql.FieldEQ(FieldFiles, v)) +} + +// FilesNEQ applies the NEQ predicate on the "files" field. +func FilesNEQ(v string) predicate.Template { + return predicate.Template(sql.FieldNEQ(FieldFiles, v)) +} + +// FilesIn applies the In predicate on the "files" field. +func FilesIn(vs ...string) predicate.Template { + return predicate.Template(sql.FieldIn(FieldFiles, vs...)) +} + +// FilesNotIn applies the NotIn predicate on the "files" field. +func FilesNotIn(vs ...string) predicate.Template { + return predicate.Template(sql.FieldNotIn(FieldFiles, vs...)) +} + +// FilesGT applies the GT predicate on the "files" field. +func FilesGT(v string) predicate.Template { + return predicate.Template(sql.FieldGT(FieldFiles, v)) +} + +// FilesGTE applies the GTE predicate on the "files" field. +func FilesGTE(v string) predicate.Template { + return predicate.Template(sql.FieldGTE(FieldFiles, v)) +} + +// FilesLT applies the LT predicate on the "files" field. +func FilesLT(v string) predicate.Template { + return predicate.Template(sql.FieldLT(FieldFiles, v)) +} + +// FilesLTE applies the LTE predicate on the "files" field. +func FilesLTE(v string) predicate.Template { + return predicate.Template(sql.FieldLTE(FieldFiles, v)) +} + +// FilesContains applies the Contains predicate on the "files" field. +func FilesContains(v string) predicate.Template { + return predicate.Template(sql.FieldContains(FieldFiles, v)) +} + +// FilesHasPrefix applies the HasPrefix predicate on the "files" field. +func FilesHasPrefix(v string) predicate.Template { + return predicate.Template(sql.FieldHasPrefix(FieldFiles, v)) +} + +// FilesHasSuffix applies the HasSuffix predicate on the "files" field. +func FilesHasSuffix(v string) predicate.Template { + return predicate.Template(sql.FieldHasSuffix(FieldFiles, v)) +} + +// FilesIsNil applies the IsNil predicate on the "files" field. +func FilesIsNil() predicate.Template { + return predicate.Template(sql.FieldIsNull(FieldFiles)) +} + +// FilesNotNil applies the NotNil predicate on the "files" field. +func FilesNotNil() predicate.Template { + return predicate.Template(sql.FieldNotNull(FieldFiles)) +} + +// FilesEqualFold applies the EqualFold predicate on the "files" field. +func FilesEqualFold(v string) predicate.Template { + return predicate.Template(sql.FieldEqualFold(FieldFiles, v)) +} + +// FilesContainsFold applies the ContainsFold predicate on the "files" field. +func FilesContainsFold(v string) predicate.Template { + return predicate.Template(sql.FieldContainsFold(FieldFiles, v)) +} + +// BaseTemplateEQ applies the EQ predicate on the "base_template" field. +func BaseTemplateEQ(v string) predicate.Template { + return predicate.Template(sql.FieldEQ(FieldBaseTemplate, v)) +} + +// BaseTemplateNEQ applies the NEQ predicate on the "base_template" field. +func BaseTemplateNEQ(v string) predicate.Template { + return predicate.Template(sql.FieldNEQ(FieldBaseTemplate, v)) +} + +// BaseTemplateIn applies the In predicate on the "base_template" field. +func BaseTemplateIn(vs ...string) predicate.Template { + return predicate.Template(sql.FieldIn(FieldBaseTemplate, vs...)) +} + +// BaseTemplateNotIn applies the NotIn predicate on the "base_template" field. +func BaseTemplateNotIn(vs ...string) predicate.Template { + return predicate.Template(sql.FieldNotIn(FieldBaseTemplate, vs...)) +} + +// BaseTemplateGT applies the GT predicate on the "base_template" field. +func BaseTemplateGT(v string) predicate.Template { + return predicate.Template(sql.FieldGT(FieldBaseTemplate, v)) +} + +// BaseTemplateGTE applies the GTE predicate on the "base_template" field. +func BaseTemplateGTE(v string) predicate.Template { + return predicate.Template(sql.FieldGTE(FieldBaseTemplate, v)) +} + +// BaseTemplateLT applies the LT predicate on the "base_template" field. +func BaseTemplateLT(v string) predicate.Template { + return predicate.Template(sql.FieldLT(FieldBaseTemplate, v)) +} + +// BaseTemplateLTE applies the LTE predicate on the "base_template" field. +func BaseTemplateLTE(v string) predicate.Template { + return predicate.Template(sql.FieldLTE(FieldBaseTemplate, v)) +} + +// BaseTemplateContains applies the Contains predicate on the "base_template" field. +func BaseTemplateContains(v string) predicate.Template { + return predicate.Template(sql.FieldContains(FieldBaseTemplate, v)) +} + +// BaseTemplateHasPrefix applies the HasPrefix predicate on the "base_template" field. +func BaseTemplateHasPrefix(v string) predicate.Template { + return predicate.Template(sql.FieldHasPrefix(FieldBaseTemplate, v)) +} + +// BaseTemplateHasSuffix applies the HasSuffix predicate on the "base_template" field. +func BaseTemplateHasSuffix(v string) predicate.Template { + return predicate.Template(sql.FieldHasSuffix(FieldBaseTemplate, v)) +} + +// BaseTemplateIsNil applies the IsNil predicate on the "base_template" field. +func BaseTemplateIsNil() predicate.Template { + return predicate.Template(sql.FieldIsNull(FieldBaseTemplate)) +} + +// BaseTemplateNotNil applies the NotNil predicate on the "base_template" field. +func BaseTemplateNotNil() predicate.Template { + return predicate.Template(sql.FieldNotNull(FieldBaseTemplate)) +} + +// BaseTemplateEqualFold applies the EqualFold predicate on the "base_template" field. +func BaseTemplateEqualFold(v string) predicate.Template { + return predicate.Template(sql.FieldEqualFold(FieldBaseTemplate, v)) +} + +// BaseTemplateContainsFold applies the ContainsFold predicate on the "base_template" field. +func BaseTemplateContainsFold(v string) predicate.Template { + return predicate.Template(sql.FieldContainsFold(FieldBaseTemplate, v)) +} + +// LockedEQ applies the EQ predicate on the "locked" field. +func LockedEQ(v bool) predicate.Template { + return predicate.Template(sql.FieldEQ(FieldLocked, v)) +} + +// LockedNEQ applies the NEQ predicate on the "locked" field. +func LockedNEQ(v bool) predicate.Template { + return predicate.Template(sql.FieldNEQ(FieldLocked, v)) +} + +// StatusEQ applies the EQ predicate on the "status" field. +func StatusEQ(v Status) predicate.Template { + return predicate.Template(sql.FieldEQ(FieldStatus, v)) +} + +// StatusNEQ applies the NEQ predicate on the "status" field. +func StatusNEQ(v Status) predicate.Template { + return predicate.Template(sql.FieldNEQ(FieldStatus, v)) +} + +// StatusIn applies the In predicate on the "status" field. +func StatusIn(vs ...Status) predicate.Template { + return predicate.Template(sql.FieldIn(FieldStatus, vs...)) +} + +// StatusNotIn applies the NotIn predicate on the "status" field. +func StatusNotIn(vs ...Status) predicate.Template { + return predicate.Template(sql.FieldNotIn(FieldStatus, vs...)) +} + +// OwnerIDEQ applies the EQ predicate on the "owner_id" field. +func OwnerIDEQ(v string) predicate.Template { + return predicate.Template(sql.FieldEQ(FieldOwnerID, v)) +} + +// OwnerIDNEQ applies the NEQ predicate on the "owner_id" field. +func OwnerIDNEQ(v string) predicate.Template { + return predicate.Template(sql.FieldNEQ(FieldOwnerID, v)) +} + +// OwnerIDIn applies the In predicate on the "owner_id" field. +func OwnerIDIn(vs ...string) predicate.Template { + return predicate.Template(sql.FieldIn(FieldOwnerID, vs...)) +} + +// OwnerIDNotIn applies the NotIn predicate on the "owner_id" field. +func OwnerIDNotIn(vs ...string) predicate.Template { + return predicate.Template(sql.FieldNotIn(FieldOwnerID, vs...)) +} + +// OwnerIDGT applies the GT predicate on the "owner_id" field. +func OwnerIDGT(v string) predicate.Template { + return predicate.Template(sql.FieldGT(FieldOwnerID, v)) +} + +// OwnerIDGTE applies the GTE predicate on the "owner_id" field. +func OwnerIDGTE(v string) predicate.Template { + return predicate.Template(sql.FieldGTE(FieldOwnerID, v)) +} + +// OwnerIDLT applies the LT predicate on the "owner_id" field. +func OwnerIDLT(v string) predicate.Template { + return predicate.Template(sql.FieldLT(FieldOwnerID, v)) +} + +// OwnerIDLTE applies the LTE predicate on the "owner_id" field. +func OwnerIDLTE(v string) predicate.Template { + return predicate.Template(sql.FieldLTE(FieldOwnerID, v)) +} + +// OwnerIDContains applies the Contains predicate on the "owner_id" field. +func OwnerIDContains(v string) predicate.Template { + return predicate.Template(sql.FieldContains(FieldOwnerID, v)) +} + +// OwnerIDHasPrefix applies the HasPrefix predicate on the "owner_id" field. +func OwnerIDHasPrefix(v string) predicate.Template { + return predicate.Template(sql.FieldHasPrefix(FieldOwnerID, v)) +} + +// OwnerIDHasSuffix applies the HasSuffix predicate on the "owner_id" field. +func OwnerIDHasSuffix(v string) predicate.Template { + return predicate.Template(sql.FieldHasSuffix(FieldOwnerID, v)) +} + +// OwnerIDIsNil applies the IsNil predicate on the "owner_id" field. +func OwnerIDIsNil() predicate.Template { + return predicate.Template(sql.FieldIsNull(FieldOwnerID)) +} + +// OwnerIDNotNil applies the NotNil predicate on the "owner_id" field. +func OwnerIDNotNil() predicate.Template { + return predicate.Template(sql.FieldNotNull(FieldOwnerID)) +} + +// OwnerIDEqualFold applies the EqualFold predicate on the "owner_id" field. +func OwnerIDEqualFold(v string) predicate.Template { + return predicate.Template(sql.FieldEqualFold(FieldOwnerID, v)) +} + +// OwnerIDContainsFold applies the ContainsFold predicate on the "owner_id" field. +func OwnerIDContainsFold(v string) predicate.Template { + return predicate.Template(sql.FieldContainsFold(FieldOwnerID, v)) +} + +// CreatedByEQ applies the EQ predicate on the "created_by" field. +func CreatedByEQ(v string) predicate.Template { + return predicate.Template(sql.FieldEQ(FieldCreatedBy, v)) +} + +// CreatedByNEQ applies the NEQ predicate on the "created_by" field. +func CreatedByNEQ(v string) predicate.Template { + return predicate.Template(sql.FieldNEQ(FieldCreatedBy, v)) +} + +// CreatedByIn applies the In predicate on the "created_by" field. +func CreatedByIn(vs ...string) predicate.Template { + return predicate.Template(sql.FieldIn(FieldCreatedBy, vs...)) +} + +// CreatedByNotIn applies the NotIn predicate on the "created_by" field. +func CreatedByNotIn(vs ...string) predicate.Template { + return predicate.Template(sql.FieldNotIn(FieldCreatedBy, vs...)) +} + +// CreatedByGT applies the GT predicate on the "created_by" field. +func CreatedByGT(v string) predicate.Template { + return predicate.Template(sql.FieldGT(FieldCreatedBy, v)) +} + +// CreatedByGTE applies the GTE predicate on the "created_by" field. +func CreatedByGTE(v string) predicate.Template { + return predicate.Template(sql.FieldGTE(FieldCreatedBy, v)) +} + +// CreatedByLT applies the LT predicate on the "created_by" field. +func CreatedByLT(v string) predicate.Template { + return predicate.Template(sql.FieldLT(FieldCreatedBy, v)) +} + +// CreatedByLTE applies the LTE predicate on the "created_by" field. +func CreatedByLTE(v string) predicate.Template { + return predicate.Template(sql.FieldLTE(FieldCreatedBy, v)) +} + +// CreatedByContains applies the Contains predicate on the "created_by" field. +func CreatedByContains(v string) predicate.Template { + return predicate.Template(sql.FieldContains(FieldCreatedBy, v)) +} + +// CreatedByHasPrefix applies the HasPrefix predicate on the "created_by" field. +func CreatedByHasPrefix(v string) predicate.Template { + return predicate.Template(sql.FieldHasPrefix(FieldCreatedBy, v)) +} + +// CreatedByHasSuffix applies the HasSuffix predicate on the "created_by" field. +func CreatedByHasSuffix(v string) predicate.Template { + return predicate.Template(sql.FieldHasSuffix(FieldCreatedBy, v)) +} + +// CreatedByIsNil applies the IsNil predicate on the "created_by" field. +func CreatedByIsNil() predicate.Template { + return predicate.Template(sql.FieldIsNull(FieldCreatedBy)) +} + +// CreatedByNotNil applies the NotNil predicate on the "created_by" field. +func CreatedByNotNil() predicate.Template { + return predicate.Template(sql.FieldNotNull(FieldCreatedBy)) +} + +// CreatedByEqualFold applies the EqualFold predicate on the "created_by" field. +func CreatedByEqualFold(v string) predicate.Template { + return predicate.Template(sql.FieldEqualFold(FieldCreatedBy, v)) +} + +// CreatedByContainsFold applies the ContainsFold predicate on the "created_by" field. +func CreatedByContainsFold(v string) predicate.Template { + return predicate.Template(sql.FieldContainsFold(FieldCreatedBy, v)) +} + +// UpdatedByEQ applies the EQ predicate on the "updated_by" field. +func UpdatedByEQ(v string) predicate.Template { + return predicate.Template(sql.FieldEQ(FieldUpdatedBy, v)) +} + +// UpdatedByNEQ applies the NEQ predicate on the "updated_by" field. +func UpdatedByNEQ(v string) predicate.Template { + return predicate.Template(sql.FieldNEQ(FieldUpdatedBy, v)) +} + +// UpdatedByIn applies the In predicate on the "updated_by" field. +func UpdatedByIn(vs ...string) predicate.Template { + return predicate.Template(sql.FieldIn(FieldUpdatedBy, vs...)) +} + +// UpdatedByNotIn applies the NotIn predicate on the "updated_by" field. +func UpdatedByNotIn(vs ...string) predicate.Template { + return predicate.Template(sql.FieldNotIn(FieldUpdatedBy, vs...)) +} + +// UpdatedByGT applies the GT predicate on the "updated_by" field. +func UpdatedByGT(v string) predicate.Template { + return predicate.Template(sql.FieldGT(FieldUpdatedBy, v)) +} + +// UpdatedByGTE applies the GTE predicate on the "updated_by" field. +func UpdatedByGTE(v string) predicate.Template { + return predicate.Template(sql.FieldGTE(FieldUpdatedBy, v)) +} + +// UpdatedByLT applies the LT predicate on the "updated_by" field. +func UpdatedByLT(v string) predicate.Template { + return predicate.Template(sql.FieldLT(FieldUpdatedBy, v)) +} + +// UpdatedByLTE applies the LTE predicate on the "updated_by" field. +func UpdatedByLTE(v string) predicate.Template { + return predicate.Template(sql.FieldLTE(FieldUpdatedBy, v)) +} + +// UpdatedByContains applies the Contains predicate on the "updated_by" field. +func UpdatedByContains(v string) predicate.Template { + return predicate.Template(sql.FieldContains(FieldUpdatedBy, v)) +} + +// UpdatedByHasPrefix applies the HasPrefix predicate on the "updated_by" field. +func UpdatedByHasPrefix(v string) predicate.Template { + return predicate.Template(sql.FieldHasPrefix(FieldUpdatedBy, v)) +} + +// UpdatedByHasSuffix applies the HasSuffix predicate on the "updated_by" field. +func UpdatedByHasSuffix(v string) predicate.Template { + return predicate.Template(sql.FieldHasSuffix(FieldUpdatedBy, v)) +} + +// UpdatedByIsNil applies the IsNil predicate on the "updated_by" field. +func UpdatedByIsNil() predicate.Template { + return predicate.Template(sql.FieldIsNull(FieldUpdatedBy)) +} + +// UpdatedByNotNil applies the NotNil predicate on the "updated_by" field. +func UpdatedByNotNil() predicate.Template { + return predicate.Template(sql.FieldNotNull(FieldUpdatedBy)) +} + +// UpdatedByEqualFold applies the EqualFold predicate on the "updated_by" field. +func UpdatedByEqualFold(v string) predicate.Template { + return predicate.Template(sql.FieldEqualFold(FieldUpdatedBy, v)) +} + +// UpdatedByContainsFold applies the ContainsFold predicate on the "updated_by" field. +func UpdatedByContainsFold(v string) predicate.Template { + return predicate.Template(sql.FieldContainsFold(FieldUpdatedBy, v)) +} + +// VisibilityEQ applies the EQ predicate on the "visibility" field. +func VisibilityEQ(v string) predicate.Template { + return predicate.Template(sql.FieldEQ(FieldVisibility, v)) +} + +// VisibilityNEQ applies the NEQ predicate on the "visibility" field. +func VisibilityNEQ(v string) predicate.Template { + return predicate.Template(sql.FieldNEQ(FieldVisibility, v)) +} + +// VisibilityIn applies the In predicate on the "visibility" field. +func VisibilityIn(vs ...string) predicate.Template { + return predicate.Template(sql.FieldIn(FieldVisibility, vs...)) +} + +// VisibilityNotIn applies the NotIn predicate on the "visibility" field. +func VisibilityNotIn(vs ...string) predicate.Template { + return predicate.Template(sql.FieldNotIn(FieldVisibility, vs...)) +} + +// VisibilityGT applies the GT predicate on the "visibility" field. +func VisibilityGT(v string) predicate.Template { + return predicate.Template(sql.FieldGT(FieldVisibility, v)) +} + +// VisibilityGTE applies the GTE predicate on the "visibility" field. +func VisibilityGTE(v string) predicate.Template { + return predicate.Template(sql.FieldGTE(FieldVisibility, v)) +} + +// VisibilityLT applies the LT predicate on the "visibility" field. +func VisibilityLT(v string) predicate.Template { + return predicate.Template(sql.FieldLT(FieldVisibility, v)) +} + +// VisibilityLTE applies the LTE predicate on the "visibility" field. +func VisibilityLTE(v string) predicate.Template { + return predicate.Template(sql.FieldLTE(FieldVisibility, v)) +} + +// VisibilityContains applies the Contains predicate on the "visibility" field. +func VisibilityContains(v string) predicate.Template { + return predicate.Template(sql.FieldContains(FieldVisibility, v)) +} + +// VisibilityHasPrefix applies the HasPrefix predicate on the "visibility" field. +func VisibilityHasPrefix(v string) predicate.Template { + return predicate.Template(sql.FieldHasPrefix(FieldVisibility, v)) +} + +// VisibilityHasSuffix applies the HasSuffix predicate on the "visibility" field. +func VisibilityHasSuffix(v string) predicate.Template { + return predicate.Template(sql.FieldHasSuffix(FieldVisibility, v)) +} + +// VisibilityEqualFold applies the EqualFold predicate on the "visibility" field. +func VisibilityEqualFold(v string) predicate.Template { + return predicate.Template(sql.FieldEqualFold(FieldVisibility, v)) +} + +// VisibilityContainsFold applies the ContainsFold predicate on the "visibility" field. +func VisibilityContainsFold(v string) predicate.Template { + return predicate.Template(sql.FieldContainsFold(FieldVisibility, v)) +} + +// CreatedEQ applies the EQ predicate on the "created" field. +func CreatedEQ(v time.Time) predicate.Template { + return predicate.Template(sql.FieldEQ(FieldCreated, v)) +} + +// CreatedNEQ applies the NEQ predicate on the "created" field. +func CreatedNEQ(v time.Time) predicate.Template { + return predicate.Template(sql.FieldNEQ(FieldCreated, v)) +} + +// CreatedIn applies the In predicate on the "created" field. +func CreatedIn(vs ...time.Time) predicate.Template { + return predicate.Template(sql.FieldIn(FieldCreated, vs...)) +} + +// CreatedNotIn applies the NotIn predicate on the "created" field. +func CreatedNotIn(vs ...time.Time) predicate.Template { + return predicate.Template(sql.FieldNotIn(FieldCreated, vs...)) +} + +// CreatedGT applies the GT predicate on the "created" field. +func CreatedGT(v time.Time) predicate.Template { + return predicate.Template(sql.FieldGT(FieldCreated, v)) +} + +// CreatedGTE applies the GTE predicate on the "created" field. +func CreatedGTE(v time.Time) predicate.Template { + return predicate.Template(sql.FieldGTE(FieldCreated, v)) +} + +// CreatedLT applies the LT predicate on the "created" field. +func CreatedLT(v time.Time) predicate.Template { + return predicate.Template(sql.FieldLT(FieldCreated, v)) +} + +// CreatedLTE applies the LTE predicate on the "created" field. +func CreatedLTE(v time.Time) predicate.Template { + return predicate.Template(sql.FieldLTE(FieldCreated, v)) +} + +// UpdatedEQ applies the EQ predicate on the "updated" field. +func UpdatedEQ(v time.Time) predicate.Template { + return predicate.Template(sql.FieldEQ(FieldUpdated, v)) +} + +// UpdatedNEQ applies the NEQ predicate on the "updated" field. +func UpdatedNEQ(v time.Time) predicate.Template { + return predicate.Template(sql.FieldNEQ(FieldUpdated, v)) +} + +// UpdatedIn applies the In predicate on the "updated" field. +func UpdatedIn(vs ...time.Time) predicate.Template { + return predicate.Template(sql.FieldIn(FieldUpdated, vs...)) +} + +// UpdatedNotIn applies the NotIn predicate on the "updated" field. +func UpdatedNotIn(vs ...time.Time) predicate.Template { + return predicate.Template(sql.FieldNotIn(FieldUpdated, vs...)) +} + +// UpdatedGT applies the GT predicate on the "updated" field. +func UpdatedGT(v time.Time) predicate.Template { + return predicate.Template(sql.FieldGT(FieldUpdated, v)) +} + +// UpdatedGTE applies the GTE predicate on the "updated" field. +func UpdatedGTE(v time.Time) predicate.Template { + return predicate.Template(sql.FieldGTE(FieldUpdated, v)) +} + +// UpdatedLT applies the LT predicate on the "updated" field. +func UpdatedLT(v time.Time) predicate.Template { + return predicate.Template(sql.FieldLT(FieldUpdated, v)) +} + +// UpdatedLTE applies the LTE predicate on the "updated" field. +func UpdatedLTE(v time.Time) predicate.Template { + return predicate.Template(sql.FieldLTE(FieldUpdated, v)) +} + +// And groups predicates with the AND operator between them. +func And(predicates ...predicate.Template) predicate.Template { + return predicate.Template(sql.AndPredicates(predicates...)) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.Template) predicate.Template { + return predicate.Template(sql.OrPredicates(predicates...)) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.Template) predicate.Template { + return predicate.Template(sql.NotPredicates(p)) +} diff --git a/pkg/ent/template_create.go b/pkg/ent/template_create.go new file mode 100644 index 000000000..24dc33edd --- /dev/null +++ b/pkg/ent/template_create.go @@ -0,0 +1,2234 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/template" + "github.com/google/uuid" +) + +// TemplateCreate is the builder for creating a Template entity. +type TemplateCreate struct { + config + mutation *TemplateMutation + hooks []Hook + conflict []sql.ConflictOption +} + +// SetName sets the "name" field. +func (_c *TemplateCreate) SetName(v string) *TemplateCreate { + _c.mutation.SetName(v) + return _c +} + +// SetSlug sets the "slug" field. +func (_c *TemplateCreate) SetSlug(v string) *TemplateCreate { + _c.mutation.SetSlug(v) + return _c +} + +// SetDisplayName sets the "display_name" field. +func (_c *TemplateCreate) SetDisplayName(v string) *TemplateCreate { + _c.mutation.SetDisplayName(v) + return _c +} + +// SetNillableDisplayName sets the "display_name" field if the given value is not nil. +func (_c *TemplateCreate) SetNillableDisplayName(v *string) *TemplateCreate { + if v != nil { + _c.SetDisplayName(*v) + } + return _c +} + +// SetDescription sets the "description" field. +func (_c *TemplateCreate) SetDescription(v string) *TemplateCreate { + _c.mutation.SetDescription(v) + return _c +} + +// SetNillableDescription sets the "description" field if the given value is not nil. +func (_c *TemplateCreate) SetNillableDescription(v *string) *TemplateCreate { + if v != nil { + _c.SetDescription(*v) + } + return _c +} + +// SetHarness sets the "harness" field. +func (_c *TemplateCreate) SetHarness(v string) *TemplateCreate { + _c.mutation.SetHarness(v) + return _c +} + +// SetDefaultHarnessConfig sets the "default_harness_config" field. +func (_c *TemplateCreate) SetDefaultHarnessConfig(v string) *TemplateCreate { + _c.mutation.SetDefaultHarnessConfig(v) + return _c +} + +// SetNillableDefaultHarnessConfig sets the "default_harness_config" field if the given value is not nil. +func (_c *TemplateCreate) SetNillableDefaultHarnessConfig(v *string) *TemplateCreate { + if v != nil { + _c.SetDefaultHarnessConfig(*v) + } + return _c +} + +// SetImage sets the "image" field. +func (_c *TemplateCreate) SetImage(v string) *TemplateCreate { + _c.mutation.SetImage(v) + return _c +} + +// SetNillableImage sets the "image" field if the given value is not nil. +func (_c *TemplateCreate) SetNillableImage(v *string) *TemplateCreate { + if v != nil { + _c.SetImage(*v) + } + return _c +} + +// SetConfig sets the "config" field. +func (_c *TemplateCreate) SetConfig(v string) *TemplateCreate { + _c.mutation.SetConfig(v) + return _c +} + +// SetNillableConfig sets the "config" field if the given value is not nil. +func (_c *TemplateCreate) SetNillableConfig(v *string) *TemplateCreate { + if v != nil { + _c.SetConfig(*v) + } + return _c +} + +// SetContentHash sets the "content_hash" field. +func (_c *TemplateCreate) SetContentHash(v string) *TemplateCreate { + _c.mutation.SetContentHash(v) + return _c +} + +// SetNillableContentHash sets the "content_hash" field if the given value is not nil. +func (_c *TemplateCreate) SetNillableContentHash(v *string) *TemplateCreate { + if v != nil { + _c.SetContentHash(*v) + } + return _c +} + +// SetScope sets the "scope" field. +func (_c *TemplateCreate) SetScope(v string) *TemplateCreate { + _c.mutation.SetScope(v) + return _c +} + +// SetNillableScope sets the "scope" field if the given value is not nil. +func (_c *TemplateCreate) SetNillableScope(v *string) *TemplateCreate { + if v != nil { + _c.SetScope(*v) + } + return _c +} + +// SetScopeID sets the "scope_id" field. +func (_c *TemplateCreate) SetScopeID(v string) *TemplateCreate { + _c.mutation.SetScopeID(v) + return _c +} + +// SetNillableScopeID sets the "scope_id" field if the given value is not nil. +func (_c *TemplateCreate) SetNillableScopeID(v *string) *TemplateCreate { + if v != nil { + _c.SetScopeID(*v) + } + return _c +} + +// SetProjectID sets the "project_id" field. +func (_c *TemplateCreate) SetProjectID(v string) *TemplateCreate { + _c.mutation.SetProjectID(v) + return _c +} + +// SetNillableProjectID sets the "project_id" field if the given value is not nil. +func (_c *TemplateCreate) SetNillableProjectID(v *string) *TemplateCreate { + if v != nil { + _c.SetProjectID(*v) + } + return _c +} + +// SetStorageURI sets the "storage_uri" field. +func (_c *TemplateCreate) SetStorageURI(v string) *TemplateCreate { + _c.mutation.SetStorageURI(v) + return _c +} + +// SetNillableStorageURI sets the "storage_uri" field if the given value is not nil. +func (_c *TemplateCreate) SetNillableStorageURI(v *string) *TemplateCreate { + if v != nil { + _c.SetStorageURI(*v) + } + return _c +} + +// SetStorageBucket sets the "storage_bucket" field. +func (_c *TemplateCreate) SetStorageBucket(v string) *TemplateCreate { + _c.mutation.SetStorageBucket(v) + return _c +} + +// SetNillableStorageBucket sets the "storage_bucket" field if the given value is not nil. +func (_c *TemplateCreate) SetNillableStorageBucket(v *string) *TemplateCreate { + if v != nil { + _c.SetStorageBucket(*v) + } + return _c +} + +// SetStoragePath sets the "storage_path" field. +func (_c *TemplateCreate) SetStoragePath(v string) *TemplateCreate { + _c.mutation.SetStoragePath(v) + return _c +} + +// SetNillableStoragePath sets the "storage_path" field if the given value is not nil. +func (_c *TemplateCreate) SetNillableStoragePath(v *string) *TemplateCreate { + if v != nil { + _c.SetStoragePath(*v) + } + return _c +} + +// SetFiles sets the "files" field. +func (_c *TemplateCreate) SetFiles(v string) *TemplateCreate { + _c.mutation.SetFiles(v) + return _c +} + +// SetNillableFiles sets the "files" field if the given value is not nil. +func (_c *TemplateCreate) SetNillableFiles(v *string) *TemplateCreate { + if v != nil { + _c.SetFiles(*v) + } + return _c +} + +// SetBaseTemplate sets the "base_template" field. +func (_c *TemplateCreate) SetBaseTemplate(v string) *TemplateCreate { + _c.mutation.SetBaseTemplate(v) + return _c +} + +// SetNillableBaseTemplate sets the "base_template" field if the given value is not nil. +func (_c *TemplateCreate) SetNillableBaseTemplate(v *string) *TemplateCreate { + if v != nil { + _c.SetBaseTemplate(*v) + } + return _c +} + +// SetLocked sets the "locked" field. +func (_c *TemplateCreate) SetLocked(v bool) *TemplateCreate { + _c.mutation.SetLocked(v) + return _c +} + +// SetNillableLocked sets the "locked" field if the given value is not nil. +func (_c *TemplateCreate) SetNillableLocked(v *bool) *TemplateCreate { + if v != nil { + _c.SetLocked(*v) + } + return _c +} + +// SetStatus sets the "status" field. +func (_c *TemplateCreate) SetStatus(v template.Status) *TemplateCreate { + _c.mutation.SetStatus(v) + return _c +} + +// SetNillableStatus sets the "status" field if the given value is not nil. +func (_c *TemplateCreate) SetNillableStatus(v *template.Status) *TemplateCreate { + if v != nil { + _c.SetStatus(*v) + } + return _c +} + +// SetOwnerID sets the "owner_id" field. +func (_c *TemplateCreate) SetOwnerID(v string) *TemplateCreate { + _c.mutation.SetOwnerID(v) + return _c +} + +// SetNillableOwnerID sets the "owner_id" field if the given value is not nil. +func (_c *TemplateCreate) SetNillableOwnerID(v *string) *TemplateCreate { + if v != nil { + _c.SetOwnerID(*v) + } + return _c +} + +// SetCreatedBy sets the "created_by" field. +func (_c *TemplateCreate) SetCreatedBy(v string) *TemplateCreate { + _c.mutation.SetCreatedBy(v) + return _c +} + +// SetNillableCreatedBy sets the "created_by" field if the given value is not nil. +func (_c *TemplateCreate) SetNillableCreatedBy(v *string) *TemplateCreate { + if v != nil { + _c.SetCreatedBy(*v) + } + return _c +} + +// SetUpdatedBy sets the "updated_by" field. +func (_c *TemplateCreate) SetUpdatedBy(v string) *TemplateCreate { + _c.mutation.SetUpdatedBy(v) + return _c +} + +// SetNillableUpdatedBy sets the "updated_by" field if the given value is not nil. +func (_c *TemplateCreate) SetNillableUpdatedBy(v *string) *TemplateCreate { + if v != nil { + _c.SetUpdatedBy(*v) + } + return _c +} + +// SetVisibility sets the "visibility" field. +func (_c *TemplateCreate) SetVisibility(v string) *TemplateCreate { + _c.mutation.SetVisibility(v) + return _c +} + +// SetNillableVisibility sets the "visibility" field if the given value is not nil. +func (_c *TemplateCreate) SetNillableVisibility(v *string) *TemplateCreate { + if v != nil { + _c.SetVisibility(*v) + } + return _c +} + +// SetCreated sets the "created" field. +func (_c *TemplateCreate) SetCreated(v time.Time) *TemplateCreate { + _c.mutation.SetCreated(v) + return _c +} + +// SetNillableCreated sets the "created" field if the given value is not nil. +func (_c *TemplateCreate) SetNillableCreated(v *time.Time) *TemplateCreate { + if v != nil { + _c.SetCreated(*v) + } + return _c +} + +// SetUpdated sets the "updated" field. +func (_c *TemplateCreate) SetUpdated(v time.Time) *TemplateCreate { + _c.mutation.SetUpdated(v) + return _c +} + +// SetNillableUpdated sets the "updated" field if the given value is not nil. +func (_c *TemplateCreate) SetNillableUpdated(v *time.Time) *TemplateCreate { + if v != nil { + _c.SetUpdated(*v) + } + return _c +} + +// SetID sets the "id" field. +func (_c *TemplateCreate) SetID(v uuid.UUID) *TemplateCreate { + _c.mutation.SetID(v) + return _c +} + +// SetNillableID sets the "id" field if the given value is not nil. +func (_c *TemplateCreate) SetNillableID(v *uuid.UUID) *TemplateCreate { + if v != nil { + _c.SetID(*v) + } + return _c +} + +// Mutation returns the TemplateMutation object of the builder. +func (_c *TemplateCreate) Mutation() *TemplateMutation { + return _c.mutation +} + +// Save creates the Template in the database. +func (_c *TemplateCreate) Save(ctx context.Context) (*Template, error) { + _c.defaults() + return withHooks(ctx, _c.sqlSave, _c.mutation, _c.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (_c *TemplateCreate) SaveX(ctx context.Context) *Template { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *TemplateCreate) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *TemplateCreate) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_c *TemplateCreate) defaults() { + if _, ok := _c.mutation.Scope(); !ok { + v := template.DefaultScope + _c.mutation.SetScope(v) + } + if _, ok := _c.mutation.Locked(); !ok { + v := template.DefaultLocked + _c.mutation.SetLocked(v) + } + if _, ok := _c.mutation.Status(); !ok { + v := template.DefaultStatus + _c.mutation.SetStatus(v) + } + if _, ok := _c.mutation.Visibility(); !ok { + v := template.DefaultVisibility + _c.mutation.SetVisibility(v) + } + if _, ok := _c.mutation.Created(); !ok { + v := template.DefaultCreated() + _c.mutation.SetCreated(v) + } + if _, ok := _c.mutation.Updated(); !ok { + v := template.DefaultUpdated() + _c.mutation.SetUpdated(v) + } + if _, ok := _c.mutation.ID(); !ok { + v := template.DefaultID() + _c.mutation.SetID(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_c *TemplateCreate) check() error { + if _, ok := _c.mutation.Name(); !ok { + return &ValidationError{Name: "name", err: errors.New(`ent: missing required field "Template.name"`)} + } + if v, ok := _c.mutation.Name(); ok { + if err := template.NameValidator(v); err != nil { + return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "Template.name": %w`, err)} + } + } + if _, ok := _c.mutation.Slug(); !ok { + return &ValidationError{Name: "slug", err: errors.New(`ent: missing required field "Template.slug"`)} + } + if v, ok := _c.mutation.Slug(); ok { + if err := template.SlugValidator(v); err != nil { + return &ValidationError{Name: "slug", err: fmt.Errorf(`ent: validator failed for field "Template.slug": %w`, err)} + } + } + if _, ok := _c.mutation.Harness(); !ok { + return &ValidationError{Name: "harness", err: errors.New(`ent: missing required field "Template.harness"`)} + } + if _, ok := _c.mutation.Scope(); !ok { + return &ValidationError{Name: "scope", err: errors.New(`ent: missing required field "Template.scope"`)} + } + if _, ok := _c.mutation.Locked(); !ok { + return &ValidationError{Name: "locked", err: errors.New(`ent: missing required field "Template.locked"`)} + } + if _, ok := _c.mutation.Status(); !ok { + return &ValidationError{Name: "status", err: errors.New(`ent: missing required field "Template.status"`)} + } + if v, ok := _c.mutation.Status(); ok { + if err := template.StatusValidator(v); err != nil { + return &ValidationError{Name: "status", err: fmt.Errorf(`ent: validator failed for field "Template.status": %w`, err)} + } + } + if _, ok := _c.mutation.Visibility(); !ok { + return &ValidationError{Name: "visibility", err: errors.New(`ent: missing required field "Template.visibility"`)} + } + if _, ok := _c.mutation.Created(); !ok { + return &ValidationError{Name: "created", err: errors.New(`ent: missing required field "Template.created"`)} + } + if _, ok := _c.mutation.Updated(); !ok { + return &ValidationError{Name: "updated", err: errors.New(`ent: missing required field "Template.updated"`)} + } + return nil +} + +func (_c *TemplateCreate) sqlSave(ctx context.Context) (*Template, error) { + if err := _c.check(); err != nil { + return nil, err + } + _node, _spec := _c.createSpec() + if err := sqlgraph.CreateNode(ctx, _c.driver, _spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + if _spec.ID.Value != nil { + if id, ok := _spec.ID.Value.(*uuid.UUID); ok { + _node.ID = *id + } else if err := _node.ID.Scan(_spec.ID.Value); err != nil { + return nil, err + } + } + _c.mutation.id = &_node.ID + _c.mutation.done = true + return _node, nil +} + +func (_c *TemplateCreate) createSpec() (*Template, *sqlgraph.CreateSpec) { + var ( + _node = &Template{config: _c.config} + _spec = sqlgraph.NewCreateSpec(template.Table, sqlgraph.NewFieldSpec(template.FieldID, field.TypeUUID)) + ) + _spec.OnConflict = _c.conflict + if id, ok := _c.mutation.ID(); ok { + _node.ID = id + _spec.ID.Value = &id + } + if value, ok := _c.mutation.Name(); ok { + _spec.SetField(template.FieldName, field.TypeString, value) + _node.Name = value + } + if value, ok := _c.mutation.Slug(); ok { + _spec.SetField(template.FieldSlug, field.TypeString, value) + _node.Slug = value + } + if value, ok := _c.mutation.DisplayName(); ok { + _spec.SetField(template.FieldDisplayName, field.TypeString, value) + _node.DisplayName = value + } + if value, ok := _c.mutation.Description(); ok { + _spec.SetField(template.FieldDescription, field.TypeString, value) + _node.Description = value + } + if value, ok := _c.mutation.Harness(); ok { + _spec.SetField(template.FieldHarness, field.TypeString, value) + _node.Harness = value + } + if value, ok := _c.mutation.DefaultHarnessConfig(); ok { + _spec.SetField(template.FieldDefaultHarnessConfig, field.TypeString, value) + _node.DefaultHarnessConfig = value + } + if value, ok := _c.mutation.Image(); ok { + _spec.SetField(template.FieldImage, field.TypeString, value) + _node.Image = value + } + if value, ok := _c.mutation.Config(); ok { + _spec.SetField(template.FieldConfig, field.TypeString, value) + _node.Config = value + } + if value, ok := _c.mutation.ContentHash(); ok { + _spec.SetField(template.FieldContentHash, field.TypeString, value) + _node.ContentHash = value + } + if value, ok := _c.mutation.Scope(); ok { + _spec.SetField(template.FieldScope, field.TypeString, value) + _node.Scope = value + } + if value, ok := _c.mutation.ScopeID(); ok { + _spec.SetField(template.FieldScopeID, field.TypeString, value) + _node.ScopeID = value + } + if value, ok := _c.mutation.ProjectID(); ok { + _spec.SetField(template.FieldProjectID, field.TypeString, value) + _node.ProjectID = value + } + if value, ok := _c.mutation.StorageURI(); ok { + _spec.SetField(template.FieldStorageURI, field.TypeString, value) + _node.StorageURI = value + } + if value, ok := _c.mutation.StorageBucket(); ok { + _spec.SetField(template.FieldStorageBucket, field.TypeString, value) + _node.StorageBucket = value + } + if value, ok := _c.mutation.StoragePath(); ok { + _spec.SetField(template.FieldStoragePath, field.TypeString, value) + _node.StoragePath = value + } + if value, ok := _c.mutation.Files(); ok { + _spec.SetField(template.FieldFiles, field.TypeString, value) + _node.Files = value + } + if value, ok := _c.mutation.BaseTemplate(); ok { + _spec.SetField(template.FieldBaseTemplate, field.TypeString, value) + _node.BaseTemplate = value + } + if value, ok := _c.mutation.Locked(); ok { + _spec.SetField(template.FieldLocked, field.TypeBool, value) + _node.Locked = value + } + if value, ok := _c.mutation.Status(); ok { + _spec.SetField(template.FieldStatus, field.TypeEnum, value) + _node.Status = value + } + if value, ok := _c.mutation.OwnerID(); ok { + _spec.SetField(template.FieldOwnerID, field.TypeString, value) + _node.OwnerID = value + } + if value, ok := _c.mutation.CreatedBy(); ok { + _spec.SetField(template.FieldCreatedBy, field.TypeString, value) + _node.CreatedBy = value + } + if value, ok := _c.mutation.UpdatedBy(); ok { + _spec.SetField(template.FieldUpdatedBy, field.TypeString, value) + _node.UpdatedBy = value + } + if value, ok := _c.mutation.Visibility(); ok { + _spec.SetField(template.FieldVisibility, field.TypeString, value) + _node.Visibility = value + } + if value, ok := _c.mutation.Created(); ok { + _spec.SetField(template.FieldCreated, field.TypeTime, value) + _node.Created = value + } + if value, ok := _c.mutation.Updated(); ok { + _spec.SetField(template.FieldUpdated, field.TypeTime, value) + _node.Updated = value + } + return _node, _spec +} + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.Template.Create(). +// SetName(v). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.TemplateUpsert) { +// SetName(v+v). +// }). +// Exec(ctx) +func (_c *TemplateCreate) OnConflict(opts ...sql.ConflictOption) *TemplateUpsertOne { + _c.conflict = opts + return &TemplateUpsertOne{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.Template.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *TemplateCreate) OnConflictColumns(columns ...string) *TemplateUpsertOne { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &TemplateUpsertOne{ + create: _c, + } +} + +type ( + // TemplateUpsertOne is the builder for "upsert"-ing + // one Template node. + TemplateUpsertOne struct { + create *TemplateCreate + } + + // TemplateUpsert is the "OnConflict" setter. + TemplateUpsert struct { + *sql.UpdateSet + } +) + +// SetName sets the "name" field. +func (u *TemplateUpsert) SetName(v string) *TemplateUpsert { + u.Set(template.FieldName, v) + return u +} + +// UpdateName sets the "name" field to the value that was provided on create. +func (u *TemplateUpsert) UpdateName() *TemplateUpsert { + u.SetExcluded(template.FieldName) + return u +} + +// SetSlug sets the "slug" field. +func (u *TemplateUpsert) SetSlug(v string) *TemplateUpsert { + u.Set(template.FieldSlug, v) + return u +} + +// UpdateSlug sets the "slug" field to the value that was provided on create. +func (u *TemplateUpsert) UpdateSlug() *TemplateUpsert { + u.SetExcluded(template.FieldSlug) + return u +} + +// SetDisplayName sets the "display_name" field. +func (u *TemplateUpsert) SetDisplayName(v string) *TemplateUpsert { + u.Set(template.FieldDisplayName, v) + return u +} + +// UpdateDisplayName sets the "display_name" field to the value that was provided on create. +func (u *TemplateUpsert) UpdateDisplayName() *TemplateUpsert { + u.SetExcluded(template.FieldDisplayName) + return u +} + +// ClearDisplayName clears the value of the "display_name" field. +func (u *TemplateUpsert) ClearDisplayName() *TemplateUpsert { + u.SetNull(template.FieldDisplayName) + return u +} + +// SetDescription sets the "description" field. +func (u *TemplateUpsert) SetDescription(v string) *TemplateUpsert { + u.Set(template.FieldDescription, v) + return u +} + +// UpdateDescription sets the "description" field to the value that was provided on create. +func (u *TemplateUpsert) UpdateDescription() *TemplateUpsert { + u.SetExcluded(template.FieldDescription) + return u +} + +// ClearDescription clears the value of the "description" field. +func (u *TemplateUpsert) ClearDescription() *TemplateUpsert { + u.SetNull(template.FieldDescription) + return u +} + +// SetHarness sets the "harness" field. +func (u *TemplateUpsert) SetHarness(v string) *TemplateUpsert { + u.Set(template.FieldHarness, v) + return u +} + +// UpdateHarness sets the "harness" field to the value that was provided on create. +func (u *TemplateUpsert) UpdateHarness() *TemplateUpsert { + u.SetExcluded(template.FieldHarness) + return u +} + +// SetDefaultHarnessConfig sets the "default_harness_config" field. +func (u *TemplateUpsert) SetDefaultHarnessConfig(v string) *TemplateUpsert { + u.Set(template.FieldDefaultHarnessConfig, v) + return u +} + +// UpdateDefaultHarnessConfig sets the "default_harness_config" field to the value that was provided on create. +func (u *TemplateUpsert) UpdateDefaultHarnessConfig() *TemplateUpsert { + u.SetExcluded(template.FieldDefaultHarnessConfig) + return u +} + +// ClearDefaultHarnessConfig clears the value of the "default_harness_config" field. +func (u *TemplateUpsert) ClearDefaultHarnessConfig() *TemplateUpsert { + u.SetNull(template.FieldDefaultHarnessConfig) + return u +} + +// SetImage sets the "image" field. +func (u *TemplateUpsert) SetImage(v string) *TemplateUpsert { + u.Set(template.FieldImage, v) + return u +} + +// UpdateImage sets the "image" field to the value that was provided on create. +func (u *TemplateUpsert) UpdateImage() *TemplateUpsert { + u.SetExcluded(template.FieldImage) + return u +} + +// ClearImage clears the value of the "image" field. +func (u *TemplateUpsert) ClearImage() *TemplateUpsert { + u.SetNull(template.FieldImage) + return u +} + +// SetConfig sets the "config" field. +func (u *TemplateUpsert) SetConfig(v string) *TemplateUpsert { + u.Set(template.FieldConfig, v) + return u +} + +// UpdateConfig sets the "config" field to the value that was provided on create. +func (u *TemplateUpsert) UpdateConfig() *TemplateUpsert { + u.SetExcluded(template.FieldConfig) + return u +} + +// ClearConfig clears the value of the "config" field. +func (u *TemplateUpsert) ClearConfig() *TemplateUpsert { + u.SetNull(template.FieldConfig) + return u +} + +// SetContentHash sets the "content_hash" field. +func (u *TemplateUpsert) SetContentHash(v string) *TemplateUpsert { + u.Set(template.FieldContentHash, v) + return u +} + +// UpdateContentHash sets the "content_hash" field to the value that was provided on create. +func (u *TemplateUpsert) UpdateContentHash() *TemplateUpsert { + u.SetExcluded(template.FieldContentHash) + return u +} + +// ClearContentHash clears the value of the "content_hash" field. +func (u *TemplateUpsert) ClearContentHash() *TemplateUpsert { + u.SetNull(template.FieldContentHash) + return u +} + +// SetScope sets the "scope" field. +func (u *TemplateUpsert) SetScope(v string) *TemplateUpsert { + u.Set(template.FieldScope, v) + return u +} + +// UpdateScope sets the "scope" field to the value that was provided on create. +func (u *TemplateUpsert) UpdateScope() *TemplateUpsert { + u.SetExcluded(template.FieldScope) + return u +} + +// SetScopeID sets the "scope_id" field. +func (u *TemplateUpsert) SetScopeID(v string) *TemplateUpsert { + u.Set(template.FieldScopeID, v) + return u +} + +// UpdateScopeID sets the "scope_id" field to the value that was provided on create. +func (u *TemplateUpsert) UpdateScopeID() *TemplateUpsert { + u.SetExcluded(template.FieldScopeID) + return u +} + +// ClearScopeID clears the value of the "scope_id" field. +func (u *TemplateUpsert) ClearScopeID() *TemplateUpsert { + u.SetNull(template.FieldScopeID) + return u +} + +// SetProjectID sets the "project_id" field. +func (u *TemplateUpsert) SetProjectID(v string) *TemplateUpsert { + u.Set(template.FieldProjectID, v) + return u +} + +// UpdateProjectID sets the "project_id" field to the value that was provided on create. +func (u *TemplateUpsert) UpdateProjectID() *TemplateUpsert { + u.SetExcluded(template.FieldProjectID) + return u +} + +// ClearProjectID clears the value of the "project_id" field. +func (u *TemplateUpsert) ClearProjectID() *TemplateUpsert { + u.SetNull(template.FieldProjectID) + return u +} + +// SetStorageURI sets the "storage_uri" field. +func (u *TemplateUpsert) SetStorageURI(v string) *TemplateUpsert { + u.Set(template.FieldStorageURI, v) + return u +} + +// UpdateStorageURI sets the "storage_uri" field to the value that was provided on create. +func (u *TemplateUpsert) UpdateStorageURI() *TemplateUpsert { + u.SetExcluded(template.FieldStorageURI) + return u +} + +// ClearStorageURI clears the value of the "storage_uri" field. +func (u *TemplateUpsert) ClearStorageURI() *TemplateUpsert { + u.SetNull(template.FieldStorageURI) + return u +} + +// SetStorageBucket sets the "storage_bucket" field. +func (u *TemplateUpsert) SetStorageBucket(v string) *TemplateUpsert { + u.Set(template.FieldStorageBucket, v) + return u +} + +// UpdateStorageBucket sets the "storage_bucket" field to the value that was provided on create. +func (u *TemplateUpsert) UpdateStorageBucket() *TemplateUpsert { + u.SetExcluded(template.FieldStorageBucket) + return u +} + +// ClearStorageBucket clears the value of the "storage_bucket" field. +func (u *TemplateUpsert) ClearStorageBucket() *TemplateUpsert { + u.SetNull(template.FieldStorageBucket) + return u +} + +// SetStoragePath sets the "storage_path" field. +func (u *TemplateUpsert) SetStoragePath(v string) *TemplateUpsert { + u.Set(template.FieldStoragePath, v) + return u +} + +// UpdateStoragePath sets the "storage_path" field to the value that was provided on create. +func (u *TemplateUpsert) UpdateStoragePath() *TemplateUpsert { + u.SetExcluded(template.FieldStoragePath) + return u +} + +// ClearStoragePath clears the value of the "storage_path" field. +func (u *TemplateUpsert) ClearStoragePath() *TemplateUpsert { + u.SetNull(template.FieldStoragePath) + return u +} + +// SetFiles sets the "files" field. +func (u *TemplateUpsert) SetFiles(v string) *TemplateUpsert { + u.Set(template.FieldFiles, v) + return u +} + +// UpdateFiles sets the "files" field to the value that was provided on create. +func (u *TemplateUpsert) UpdateFiles() *TemplateUpsert { + u.SetExcluded(template.FieldFiles) + return u +} + +// ClearFiles clears the value of the "files" field. +func (u *TemplateUpsert) ClearFiles() *TemplateUpsert { + u.SetNull(template.FieldFiles) + return u +} + +// SetBaseTemplate sets the "base_template" field. +func (u *TemplateUpsert) SetBaseTemplate(v string) *TemplateUpsert { + u.Set(template.FieldBaseTemplate, v) + return u +} + +// UpdateBaseTemplate sets the "base_template" field to the value that was provided on create. +func (u *TemplateUpsert) UpdateBaseTemplate() *TemplateUpsert { + u.SetExcluded(template.FieldBaseTemplate) + return u +} + +// ClearBaseTemplate clears the value of the "base_template" field. +func (u *TemplateUpsert) ClearBaseTemplate() *TemplateUpsert { + u.SetNull(template.FieldBaseTemplate) + return u +} + +// SetLocked sets the "locked" field. +func (u *TemplateUpsert) SetLocked(v bool) *TemplateUpsert { + u.Set(template.FieldLocked, v) + return u +} + +// UpdateLocked sets the "locked" field to the value that was provided on create. +func (u *TemplateUpsert) UpdateLocked() *TemplateUpsert { + u.SetExcluded(template.FieldLocked) + return u +} + +// SetStatus sets the "status" field. +func (u *TemplateUpsert) SetStatus(v template.Status) *TemplateUpsert { + u.Set(template.FieldStatus, v) + return u +} + +// UpdateStatus sets the "status" field to the value that was provided on create. +func (u *TemplateUpsert) UpdateStatus() *TemplateUpsert { + u.SetExcluded(template.FieldStatus) + return u +} + +// SetOwnerID sets the "owner_id" field. +func (u *TemplateUpsert) SetOwnerID(v string) *TemplateUpsert { + u.Set(template.FieldOwnerID, v) + return u +} + +// UpdateOwnerID sets the "owner_id" field to the value that was provided on create. +func (u *TemplateUpsert) UpdateOwnerID() *TemplateUpsert { + u.SetExcluded(template.FieldOwnerID) + return u +} + +// ClearOwnerID clears the value of the "owner_id" field. +func (u *TemplateUpsert) ClearOwnerID() *TemplateUpsert { + u.SetNull(template.FieldOwnerID) + return u +} + +// SetCreatedBy sets the "created_by" field. +func (u *TemplateUpsert) SetCreatedBy(v string) *TemplateUpsert { + u.Set(template.FieldCreatedBy, v) + return u +} + +// UpdateCreatedBy sets the "created_by" field to the value that was provided on create. +func (u *TemplateUpsert) UpdateCreatedBy() *TemplateUpsert { + u.SetExcluded(template.FieldCreatedBy) + return u +} + +// ClearCreatedBy clears the value of the "created_by" field. +func (u *TemplateUpsert) ClearCreatedBy() *TemplateUpsert { + u.SetNull(template.FieldCreatedBy) + return u +} + +// SetUpdatedBy sets the "updated_by" field. +func (u *TemplateUpsert) SetUpdatedBy(v string) *TemplateUpsert { + u.Set(template.FieldUpdatedBy, v) + return u +} + +// UpdateUpdatedBy sets the "updated_by" field to the value that was provided on create. +func (u *TemplateUpsert) UpdateUpdatedBy() *TemplateUpsert { + u.SetExcluded(template.FieldUpdatedBy) + return u +} + +// ClearUpdatedBy clears the value of the "updated_by" field. +func (u *TemplateUpsert) ClearUpdatedBy() *TemplateUpsert { + u.SetNull(template.FieldUpdatedBy) + return u +} + +// SetVisibility sets the "visibility" field. +func (u *TemplateUpsert) SetVisibility(v string) *TemplateUpsert { + u.Set(template.FieldVisibility, v) + return u +} + +// UpdateVisibility sets the "visibility" field to the value that was provided on create. +func (u *TemplateUpsert) UpdateVisibility() *TemplateUpsert { + u.SetExcluded(template.FieldVisibility) + return u +} + +// SetUpdated sets the "updated" field. +func (u *TemplateUpsert) SetUpdated(v time.Time) *TemplateUpsert { + u.Set(template.FieldUpdated, v) + return u +} + +// UpdateUpdated sets the "updated" field to the value that was provided on create. +func (u *TemplateUpsert) UpdateUpdated() *TemplateUpsert { + u.SetExcluded(template.FieldUpdated) + return u +} + +// UpdateNewValues updates the mutable fields using the new values that were set on create except the ID field. +// Using this option is equivalent to using: +// +// client.Template.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(template.FieldID) +// }), +// ). +// Exec(ctx) +func (u *TemplateUpsertOne) UpdateNewValues() *TemplateUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + if _, exists := u.create.mutation.ID(); exists { + s.SetIgnore(template.FieldID) + } + if _, exists := u.create.mutation.Created(); exists { + s.SetIgnore(template.FieldCreated) + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.Template.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *TemplateUpsertOne) Ignore() *TemplateUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *TemplateUpsertOne) DoNothing() *TemplateUpsertOne { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the TemplateCreate.OnConflict +// documentation for more info. +func (u *TemplateUpsertOne) Update(set func(*TemplateUpsert)) *TemplateUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&TemplateUpsert{UpdateSet: update}) + })) + return u +} + +// SetName sets the "name" field. +func (u *TemplateUpsertOne) SetName(v string) *TemplateUpsertOne { + return u.Update(func(s *TemplateUpsert) { + s.SetName(v) + }) +} + +// UpdateName sets the "name" field to the value that was provided on create. +func (u *TemplateUpsertOne) UpdateName() *TemplateUpsertOne { + return u.Update(func(s *TemplateUpsert) { + s.UpdateName() + }) +} + +// SetSlug sets the "slug" field. +func (u *TemplateUpsertOne) SetSlug(v string) *TemplateUpsertOne { + return u.Update(func(s *TemplateUpsert) { + s.SetSlug(v) + }) +} + +// UpdateSlug sets the "slug" field to the value that was provided on create. +func (u *TemplateUpsertOne) UpdateSlug() *TemplateUpsertOne { + return u.Update(func(s *TemplateUpsert) { + s.UpdateSlug() + }) +} + +// SetDisplayName sets the "display_name" field. +func (u *TemplateUpsertOne) SetDisplayName(v string) *TemplateUpsertOne { + return u.Update(func(s *TemplateUpsert) { + s.SetDisplayName(v) + }) +} + +// UpdateDisplayName sets the "display_name" field to the value that was provided on create. +func (u *TemplateUpsertOne) UpdateDisplayName() *TemplateUpsertOne { + return u.Update(func(s *TemplateUpsert) { + s.UpdateDisplayName() + }) +} + +// ClearDisplayName clears the value of the "display_name" field. +func (u *TemplateUpsertOne) ClearDisplayName() *TemplateUpsertOne { + return u.Update(func(s *TemplateUpsert) { + s.ClearDisplayName() + }) +} + +// SetDescription sets the "description" field. +func (u *TemplateUpsertOne) SetDescription(v string) *TemplateUpsertOne { + return u.Update(func(s *TemplateUpsert) { + s.SetDescription(v) + }) +} + +// UpdateDescription sets the "description" field to the value that was provided on create. +func (u *TemplateUpsertOne) UpdateDescription() *TemplateUpsertOne { + return u.Update(func(s *TemplateUpsert) { + s.UpdateDescription() + }) +} + +// ClearDescription clears the value of the "description" field. +func (u *TemplateUpsertOne) ClearDescription() *TemplateUpsertOne { + return u.Update(func(s *TemplateUpsert) { + s.ClearDescription() + }) +} + +// SetHarness sets the "harness" field. +func (u *TemplateUpsertOne) SetHarness(v string) *TemplateUpsertOne { + return u.Update(func(s *TemplateUpsert) { + s.SetHarness(v) + }) +} + +// UpdateHarness sets the "harness" field to the value that was provided on create. +func (u *TemplateUpsertOne) UpdateHarness() *TemplateUpsertOne { + return u.Update(func(s *TemplateUpsert) { + s.UpdateHarness() + }) +} + +// SetDefaultHarnessConfig sets the "default_harness_config" field. +func (u *TemplateUpsertOne) SetDefaultHarnessConfig(v string) *TemplateUpsertOne { + return u.Update(func(s *TemplateUpsert) { + s.SetDefaultHarnessConfig(v) + }) +} + +// UpdateDefaultHarnessConfig sets the "default_harness_config" field to the value that was provided on create. +func (u *TemplateUpsertOne) UpdateDefaultHarnessConfig() *TemplateUpsertOne { + return u.Update(func(s *TemplateUpsert) { + s.UpdateDefaultHarnessConfig() + }) +} + +// ClearDefaultHarnessConfig clears the value of the "default_harness_config" field. +func (u *TemplateUpsertOne) ClearDefaultHarnessConfig() *TemplateUpsertOne { + return u.Update(func(s *TemplateUpsert) { + s.ClearDefaultHarnessConfig() + }) +} + +// SetImage sets the "image" field. +func (u *TemplateUpsertOne) SetImage(v string) *TemplateUpsertOne { + return u.Update(func(s *TemplateUpsert) { + s.SetImage(v) + }) +} + +// UpdateImage sets the "image" field to the value that was provided on create. +func (u *TemplateUpsertOne) UpdateImage() *TemplateUpsertOne { + return u.Update(func(s *TemplateUpsert) { + s.UpdateImage() + }) +} + +// ClearImage clears the value of the "image" field. +func (u *TemplateUpsertOne) ClearImage() *TemplateUpsertOne { + return u.Update(func(s *TemplateUpsert) { + s.ClearImage() + }) +} + +// SetConfig sets the "config" field. +func (u *TemplateUpsertOne) SetConfig(v string) *TemplateUpsertOne { + return u.Update(func(s *TemplateUpsert) { + s.SetConfig(v) + }) +} + +// UpdateConfig sets the "config" field to the value that was provided on create. +func (u *TemplateUpsertOne) UpdateConfig() *TemplateUpsertOne { + return u.Update(func(s *TemplateUpsert) { + s.UpdateConfig() + }) +} + +// ClearConfig clears the value of the "config" field. +func (u *TemplateUpsertOne) ClearConfig() *TemplateUpsertOne { + return u.Update(func(s *TemplateUpsert) { + s.ClearConfig() + }) +} + +// SetContentHash sets the "content_hash" field. +func (u *TemplateUpsertOne) SetContentHash(v string) *TemplateUpsertOne { + return u.Update(func(s *TemplateUpsert) { + s.SetContentHash(v) + }) +} + +// UpdateContentHash sets the "content_hash" field to the value that was provided on create. +func (u *TemplateUpsertOne) UpdateContentHash() *TemplateUpsertOne { + return u.Update(func(s *TemplateUpsert) { + s.UpdateContentHash() + }) +} + +// ClearContentHash clears the value of the "content_hash" field. +func (u *TemplateUpsertOne) ClearContentHash() *TemplateUpsertOne { + return u.Update(func(s *TemplateUpsert) { + s.ClearContentHash() + }) +} + +// SetScope sets the "scope" field. +func (u *TemplateUpsertOne) SetScope(v string) *TemplateUpsertOne { + return u.Update(func(s *TemplateUpsert) { + s.SetScope(v) + }) +} + +// UpdateScope sets the "scope" field to the value that was provided on create. +func (u *TemplateUpsertOne) UpdateScope() *TemplateUpsertOne { + return u.Update(func(s *TemplateUpsert) { + s.UpdateScope() + }) +} + +// SetScopeID sets the "scope_id" field. +func (u *TemplateUpsertOne) SetScopeID(v string) *TemplateUpsertOne { + return u.Update(func(s *TemplateUpsert) { + s.SetScopeID(v) + }) +} + +// UpdateScopeID sets the "scope_id" field to the value that was provided on create. +func (u *TemplateUpsertOne) UpdateScopeID() *TemplateUpsertOne { + return u.Update(func(s *TemplateUpsert) { + s.UpdateScopeID() + }) +} + +// ClearScopeID clears the value of the "scope_id" field. +func (u *TemplateUpsertOne) ClearScopeID() *TemplateUpsertOne { + return u.Update(func(s *TemplateUpsert) { + s.ClearScopeID() + }) +} + +// SetProjectID sets the "project_id" field. +func (u *TemplateUpsertOne) SetProjectID(v string) *TemplateUpsertOne { + return u.Update(func(s *TemplateUpsert) { + s.SetProjectID(v) + }) +} + +// UpdateProjectID sets the "project_id" field to the value that was provided on create. +func (u *TemplateUpsertOne) UpdateProjectID() *TemplateUpsertOne { + return u.Update(func(s *TemplateUpsert) { + s.UpdateProjectID() + }) +} + +// ClearProjectID clears the value of the "project_id" field. +func (u *TemplateUpsertOne) ClearProjectID() *TemplateUpsertOne { + return u.Update(func(s *TemplateUpsert) { + s.ClearProjectID() + }) +} + +// SetStorageURI sets the "storage_uri" field. +func (u *TemplateUpsertOne) SetStorageURI(v string) *TemplateUpsertOne { + return u.Update(func(s *TemplateUpsert) { + s.SetStorageURI(v) + }) +} + +// UpdateStorageURI sets the "storage_uri" field to the value that was provided on create. +func (u *TemplateUpsertOne) UpdateStorageURI() *TemplateUpsertOne { + return u.Update(func(s *TemplateUpsert) { + s.UpdateStorageURI() + }) +} + +// ClearStorageURI clears the value of the "storage_uri" field. +func (u *TemplateUpsertOne) ClearStorageURI() *TemplateUpsertOne { + return u.Update(func(s *TemplateUpsert) { + s.ClearStorageURI() + }) +} + +// SetStorageBucket sets the "storage_bucket" field. +func (u *TemplateUpsertOne) SetStorageBucket(v string) *TemplateUpsertOne { + return u.Update(func(s *TemplateUpsert) { + s.SetStorageBucket(v) + }) +} + +// UpdateStorageBucket sets the "storage_bucket" field to the value that was provided on create. +func (u *TemplateUpsertOne) UpdateStorageBucket() *TemplateUpsertOne { + return u.Update(func(s *TemplateUpsert) { + s.UpdateStorageBucket() + }) +} + +// ClearStorageBucket clears the value of the "storage_bucket" field. +func (u *TemplateUpsertOne) ClearStorageBucket() *TemplateUpsertOne { + return u.Update(func(s *TemplateUpsert) { + s.ClearStorageBucket() + }) +} + +// SetStoragePath sets the "storage_path" field. +func (u *TemplateUpsertOne) SetStoragePath(v string) *TemplateUpsertOne { + return u.Update(func(s *TemplateUpsert) { + s.SetStoragePath(v) + }) +} + +// UpdateStoragePath sets the "storage_path" field to the value that was provided on create. +func (u *TemplateUpsertOne) UpdateStoragePath() *TemplateUpsertOne { + return u.Update(func(s *TemplateUpsert) { + s.UpdateStoragePath() + }) +} + +// ClearStoragePath clears the value of the "storage_path" field. +func (u *TemplateUpsertOne) ClearStoragePath() *TemplateUpsertOne { + return u.Update(func(s *TemplateUpsert) { + s.ClearStoragePath() + }) +} + +// SetFiles sets the "files" field. +func (u *TemplateUpsertOne) SetFiles(v string) *TemplateUpsertOne { + return u.Update(func(s *TemplateUpsert) { + s.SetFiles(v) + }) +} + +// UpdateFiles sets the "files" field to the value that was provided on create. +func (u *TemplateUpsertOne) UpdateFiles() *TemplateUpsertOne { + return u.Update(func(s *TemplateUpsert) { + s.UpdateFiles() + }) +} + +// ClearFiles clears the value of the "files" field. +func (u *TemplateUpsertOne) ClearFiles() *TemplateUpsertOne { + return u.Update(func(s *TemplateUpsert) { + s.ClearFiles() + }) +} + +// SetBaseTemplate sets the "base_template" field. +func (u *TemplateUpsertOne) SetBaseTemplate(v string) *TemplateUpsertOne { + return u.Update(func(s *TemplateUpsert) { + s.SetBaseTemplate(v) + }) +} + +// UpdateBaseTemplate sets the "base_template" field to the value that was provided on create. +func (u *TemplateUpsertOne) UpdateBaseTemplate() *TemplateUpsertOne { + return u.Update(func(s *TemplateUpsert) { + s.UpdateBaseTemplate() + }) +} + +// ClearBaseTemplate clears the value of the "base_template" field. +func (u *TemplateUpsertOne) ClearBaseTemplate() *TemplateUpsertOne { + return u.Update(func(s *TemplateUpsert) { + s.ClearBaseTemplate() + }) +} + +// SetLocked sets the "locked" field. +func (u *TemplateUpsertOne) SetLocked(v bool) *TemplateUpsertOne { + return u.Update(func(s *TemplateUpsert) { + s.SetLocked(v) + }) +} + +// UpdateLocked sets the "locked" field to the value that was provided on create. +func (u *TemplateUpsertOne) UpdateLocked() *TemplateUpsertOne { + return u.Update(func(s *TemplateUpsert) { + s.UpdateLocked() + }) +} + +// SetStatus sets the "status" field. +func (u *TemplateUpsertOne) SetStatus(v template.Status) *TemplateUpsertOne { + return u.Update(func(s *TemplateUpsert) { + s.SetStatus(v) + }) +} + +// UpdateStatus sets the "status" field to the value that was provided on create. +func (u *TemplateUpsertOne) UpdateStatus() *TemplateUpsertOne { + return u.Update(func(s *TemplateUpsert) { + s.UpdateStatus() + }) +} + +// SetOwnerID sets the "owner_id" field. +func (u *TemplateUpsertOne) SetOwnerID(v string) *TemplateUpsertOne { + return u.Update(func(s *TemplateUpsert) { + s.SetOwnerID(v) + }) +} + +// UpdateOwnerID sets the "owner_id" field to the value that was provided on create. +func (u *TemplateUpsertOne) UpdateOwnerID() *TemplateUpsertOne { + return u.Update(func(s *TemplateUpsert) { + s.UpdateOwnerID() + }) +} + +// ClearOwnerID clears the value of the "owner_id" field. +func (u *TemplateUpsertOne) ClearOwnerID() *TemplateUpsertOne { + return u.Update(func(s *TemplateUpsert) { + s.ClearOwnerID() + }) +} + +// SetCreatedBy sets the "created_by" field. +func (u *TemplateUpsertOne) SetCreatedBy(v string) *TemplateUpsertOne { + return u.Update(func(s *TemplateUpsert) { + s.SetCreatedBy(v) + }) +} + +// UpdateCreatedBy sets the "created_by" field to the value that was provided on create. +func (u *TemplateUpsertOne) UpdateCreatedBy() *TemplateUpsertOne { + return u.Update(func(s *TemplateUpsert) { + s.UpdateCreatedBy() + }) +} + +// ClearCreatedBy clears the value of the "created_by" field. +func (u *TemplateUpsertOne) ClearCreatedBy() *TemplateUpsertOne { + return u.Update(func(s *TemplateUpsert) { + s.ClearCreatedBy() + }) +} + +// SetUpdatedBy sets the "updated_by" field. +func (u *TemplateUpsertOne) SetUpdatedBy(v string) *TemplateUpsertOne { + return u.Update(func(s *TemplateUpsert) { + s.SetUpdatedBy(v) + }) +} + +// UpdateUpdatedBy sets the "updated_by" field to the value that was provided on create. +func (u *TemplateUpsertOne) UpdateUpdatedBy() *TemplateUpsertOne { + return u.Update(func(s *TemplateUpsert) { + s.UpdateUpdatedBy() + }) +} + +// ClearUpdatedBy clears the value of the "updated_by" field. +func (u *TemplateUpsertOne) ClearUpdatedBy() *TemplateUpsertOne { + return u.Update(func(s *TemplateUpsert) { + s.ClearUpdatedBy() + }) +} + +// SetVisibility sets the "visibility" field. +func (u *TemplateUpsertOne) SetVisibility(v string) *TemplateUpsertOne { + return u.Update(func(s *TemplateUpsert) { + s.SetVisibility(v) + }) +} + +// UpdateVisibility sets the "visibility" field to the value that was provided on create. +func (u *TemplateUpsertOne) UpdateVisibility() *TemplateUpsertOne { + return u.Update(func(s *TemplateUpsert) { + s.UpdateVisibility() + }) +} + +// SetUpdated sets the "updated" field. +func (u *TemplateUpsertOne) SetUpdated(v time.Time) *TemplateUpsertOne { + return u.Update(func(s *TemplateUpsert) { + s.SetUpdated(v) + }) +} + +// UpdateUpdated sets the "updated" field to the value that was provided on create. +func (u *TemplateUpsertOne) UpdateUpdated() *TemplateUpsertOne { + return u.Update(func(s *TemplateUpsert) { + s.UpdateUpdated() + }) +} + +// Exec executes the query. +func (u *TemplateUpsertOne) Exec(ctx context.Context) error { + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for TemplateCreate.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *TemplateUpsertOne) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} + +// Exec executes the UPSERT query and returns the inserted/updated ID. +func (u *TemplateUpsertOne) ID(ctx context.Context) (id uuid.UUID, err error) { + if u.create.driver.Dialect() == dialect.MySQL { + // In case of "ON CONFLICT", there is no way to get back non-numeric ID + // fields from the database since MySQL does not support the RETURNING clause. + return id, errors.New("ent: TemplateUpsertOne.ID is not supported by MySQL driver. Use TemplateUpsertOne.Exec instead") + } + node, err := u.create.Save(ctx) + if err != nil { + return id, err + } + return node.ID, nil +} + +// IDX is like ID, but panics if an error occurs. +func (u *TemplateUpsertOne) IDX(ctx context.Context) uuid.UUID { + id, err := u.ID(ctx) + if err != nil { + panic(err) + } + return id +} + +// TemplateCreateBulk is the builder for creating many Template entities in bulk. +type TemplateCreateBulk struct { + config + err error + builders []*TemplateCreate + conflict []sql.ConflictOption +} + +// Save creates the Template entities in the database. +func (_c *TemplateCreateBulk) Save(ctx context.Context) ([]*Template, error) { + if _c.err != nil { + return nil, _c.err + } + specs := make([]*sqlgraph.CreateSpec, len(_c.builders)) + nodes := make([]*Template, len(_c.builders)) + mutators := make([]Mutator, len(_c.builders)) + for i := range _c.builders { + func(i int, root context.Context) { + builder := _c.builders[i] + builder.defaults() + var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { + mutation, ok := m.(*TemplateMutation) + if !ok { + return nil, fmt.Errorf("unexpected mutation type %T", m) + } + if err := builder.check(); err != nil { + return nil, err + } + builder.mutation = mutation + var err error + nodes[i], specs[i] = builder.createSpec() + if i < len(mutators)-1 { + _, err = mutators[i+1].Mutate(root, _c.builders[i+1].mutation) + } else { + spec := &sqlgraph.BatchCreateSpec{Nodes: specs} + spec.OnConflict = _c.conflict + // Invoke the actual operation on the latest mutation in the chain. + if err = sqlgraph.BatchCreate(ctx, _c.driver, spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + } + } + if err != nil { + return nil, err + } + mutation.id = &nodes[i].ID + mutation.done = true + return nodes[i], nil + }) + for i := len(builder.hooks) - 1; i >= 0; i-- { + mut = builder.hooks[i](mut) + } + mutators[i] = mut + }(i, ctx) + } + if len(mutators) > 0 { + if _, err := mutators[0].Mutate(ctx, _c.builders[0].mutation); err != nil { + return nil, err + } + } + return nodes, nil +} + +// SaveX is like Save, but panics if an error occurs. +func (_c *TemplateCreateBulk) SaveX(ctx context.Context) []*Template { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *TemplateCreateBulk) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *TemplateCreateBulk) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.Template.CreateBulk(builders...). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.TemplateUpsert) { +// SetName(v+v). +// }). +// Exec(ctx) +func (_c *TemplateCreateBulk) OnConflict(opts ...sql.ConflictOption) *TemplateUpsertBulk { + _c.conflict = opts + return &TemplateUpsertBulk{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.Template.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *TemplateCreateBulk) OnConflictColumns(columns ...string) *TemplateUpsertBulk { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &TemplateUpsertBulk{ + create: _c, + } +} + +// TemplateUpsertBulk is the builder for "upsert"-ing +// a bulk of Template nodes. +type TemplateUpsertBulk struct { + create *TemplateCreateBulk +} + +// UpdateNewValues updates the mutable fields using the new values that +// were set on create. Using this option is equivalent to using: +// +// client.Template.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(template.FieldID) +// }), +// ). +// Exec(ctx) +func (u *TemplateUpsertBulk) UpdateNewValues() *TemplateUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + for _, b := range u.create.builders { + if _, exists := b.mutation.ID(); exists { + s.SetIgnore(template.FieldID) + } + if _, exists := b.mutation.Created(); exists { + s.SetIgnore(template.FieldCreated) + } + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.Template.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *TemplateUpsertBulk) Ignore() *TemplateUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *TemplateUpsertBulk) DoNothing() *TemplateUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the TemplateCreateBulk.OnConflict +// documentation for more info. +func (u *TemplateUpsertBulk) Update(set func(*TemplateUpsert)) *TemplateUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&TemplateUpsert{UpdateSet: update}) + })) + return u +} + +// SetName sets the "name" field. +func (u *TemplateUpsertBulk) SetName(v string) *TemplateUpsertBulk { + return u.Update(func(s *TemplateUpsert) { + s.SetName(v) + }) +} + +// UpdateName sets the "name" field to the value that was provided on create. +func (u *TemplateUpsertBulk) UpdateName() *TemplateUpsertBulk { + return u.Update(func(s *TemplateUpsert) { + s.UpdateName() + }) +} + +// SetSlug sets the "slug" field. +func (u *TemplateUpsertBulk) SetSlug(v string) *TemplateUpsertBulk { + return u.Update(func(s *TemplateUpsert) { + s.SetSlug(v) + }) +} + +// UpdateSlug sets the "slug" field to the value that was provided on create. +func (u *TemplateUpsertBulk) UpdateSlug() *TemplateUpsertBulk { + return u.Update(func(s *TemplateUpsert) { + s.UpdateSlug() + }) +} + +// SetDisplayName sets the "display_name" field. +func (u *TemplateUpsertBulk) SetDisplayName(v string) *TemplateUpsertBulk { + return u.Update(func(s *TemplateUpsert) { + s.SetDisplayName(v) + }) +} + +// UpdateDisplayName sets the "display_name" field to the value that was provided on create. +func (u *TemplateUpsertBulk) UpdateDisplayName() *TemplateUpsertBulk { + return u.Update(func(s *TemplateUpsert) { + s.UpdateDisplayName() + }) +} + +// ClearDisplayName clears the value of the "display_name" field. +func (u *TemplateUpsertBulk) ClearDisplayName() *TemplateUpsertBulk { + return u.Update(func(s *TemplateUpsert) { + s.ClearDisplayName() + }) +} + +// SetDescription sets the "description" field. +func (u *TemplateUpsertBulk) SetDescription(v string) *TemplateUpsertBulk { + return u.Update(func(s *TemplateUpsert) { + s.SetDescription(v) + }) +} + +// UpdateDescription sets the "description" field to the value that was provided on create. +func (u *TemplateUpsertBulk) UpdateDescription() *TemplateUpsertBulk { + return u.Update(func(s *TemplateUpsert) { + s.UpdateDescription() + }) +} + +// ClearDescription clears the value of the "description" field. +func (u *TemplateUpsertBulk) ClearDescription() *TemplateUpsertBulk { + return u.Update(func(s *TemplateUpsert) { + s.ClearDescription() + }) +} + +// SetHarness sets the "harness" field. +func (u *TemplateUpsertBulk) SetHarness(v string) *TemplateUpsertBulk { + return u.Update(func(s *TemplateUpsert) { + s.SetHarness(v) + }) +} + +// UpdateHarness sets the "harness" field to the value that was provided on create. +func (u *TemplateUpsertBulk) UpdateHarness() *TemplateUpsertBulk { + return u.Update(func(s *TemplateUpsert) { + s.UpdateHarness() + }) +} + +// SetDefaultHarnessConfig sets the "default_harness_config" field. +func (u *TemplateUpsertBulk) SetDefaultHarnessConfig(v string) *TemplateUpsertBulk { + return u.Update(func(s *TemplateUpsert) { + s.SetDefaultHarnessConfig(v) + }) +} + +// UpdateDefaultHarnessConfig sets the "default_harness_config" field to the value that was provided on create. +func (u *TemplateUpsertBulk) UpdateDefaultHarnessConfig() *TemplateUpsertBulk { + return u.Update(func(s *TemplateUpsert) { + s.UpdateDefaultHarnessConfig() + }) +} + +// ClearDefaultHarnessConfig clears the value of the "default_harness_config" field. +func (u *TemplateUpsertBulk) ClearDefaultHarnessConfig() *TemplateUpsertBulk { + return u.Update(func(s *TemplateUpsert) { + s.ClearDefaultHarnessConfig() + }) +} + +// SetImage sets the "image" field. +func (u *TemplateUpsertBulk) SetImage(v string) *TemplateUpsertBulk { + return u.Update(func(s *TemplateUpsert) { + s.SetImage(v) + }) +} + +// UpdateImage sets the "image" field to the value that was provided on create. +func (u *TemplateUpsertBulk) UpdateImage() *TemplateUpsertBulk { + return u.Update(func(s *TemplateUpsert) { + s.UpdateImage() + }) +} + +// ClearImage clears the value of the "image" field. +func (u *TemplateUpsertBulk) ClearImage() *TemplateUpsertBulk { + return u.Update(func(s *TemplateUpsert) { + s.ClearImage() + }) +} + +// SetConfig sets the "config" field. +func (u *TemplateUpsertBulk) SetConfig(v string) *TemplateUpsertBulk { + return u.Update(func(s *TemplateUpsert) { + s.SetConfig(v) + }) +} + +// UpdateConfig sets the "config" field to the value that was provided on create. +func (u *TemplateUpsertBulk) UpdateConfig() *TemplateUpsertBulk { + return u.Update(func(s *TemplateUpsert) { + s.UpdateConfig() + }) +} + +// ClearConfig clears the value of the "config" field. +func (u *TemplateUpsertBulk) ClearConfig() *TemplateUpsertBulk { + return u.Update(func(s *TemplateUpsert) { + s.ClearConfig() + }) +} + +// SetContentHash sets the "content_hash" field. +func (u *TemplateUpsertBulk) SetContentHash(v string) *TemplateUpsertBulk { + return u.Update(func(s *TemplateUpsert) { + s.SetContentHash(v) + }) +} + +// UpdateContentHash sets the "content_hash" field to the value that was provided on create. +func (u *TemplateUpsertBulk) UpdateContentHash() *TemplateUpsertBulk { + return u.Update(func(s *TemplateUpsert) { + s.UpdateContentHash() + }) +} + +// ClearContentHash clears the value of the "content_hash" field. +func (u *TemplateUpsertBulk) ClearContentHash() *TemplateUpsertBulk { + return u.Update(func(s *TemplateUpsert) { + s.ClearContentHash() + }) +} + +// SetScope sets the "scope" field. +func (u *TemplateUpsertBulk) SetScope(v string) *TemplateUpsertBulk { + return u.Update(func(s *TemplateUpsert) { + s.SetScope(v) + }) +} + +// UpdateScope sets the "scope" field to the value that was provided on create. +func (u *TemplateUpsertBulk) UpdateScope() *TemplateUpsertBulk { + return u.Update(func(s *TemplateUpsert) { + s.UpdateScope() + }) +} + +// SetScopeID sets the "scope_id" field. +func (u *TemplateUpsertBulk) SetScopeID(v string) *TemplateUpsertBulk { + return u.Update(func(s *TemplateUpsert) { + s.SetScopeID(v) + }) +} + +// UpdateScopeID sets the "scope_id" field to the value that was provided on create. +func (u *TemplateUpsertBulk) UpdateScopeID() *TemplateUpsertBulk { + return u.Update(func(s *TemplateUpsert) { + s.UpdateScopeID() + }) +} + +// ClearScopeID clears the value of the "scope_id" field. +func (u *TemplateUpsertBulk) ClearScopeID() *TemplateUpsertBulk { + return u.Update(func(s *TemplateUpsert) { + s.ClearScopeID() + }) +} + +// SetProjectID sets the "project_id" field. +func (u *TemplateUpsertBulk) SetProjectID(v string) *TemplateUpsertBulk { + return u.Update(func(s *TemplateUpsert) { + s.SetProjectID(v) + }) +} + +// UpdateProjectID sets the "project_id" field to the value that was provided on create. +func (u *TemplateUpsertBulk) UpdateProjectID() *TemplateUpsertBulk { + return u.Update(func(s *TemplateUpsert) { + s.UpdateProjectID() + }) +} + +// ClearProjectID clears the value of the "project_id" field. +func (u *TemplateUpsertBulk) ClearProjectID() *TemplateUpsertBulk { + return u.Update(func(s *TemplateUpsert) { + s.ClearProjectID() + }) +} + +// SetStorageURI sets the "storage_uri" field. +func (u *TemplateUpsertBulk) SetStorageURI(v string) *TemplateUpsertBulk { + return u.Update(func(s *TemplateUpsert) { + s.SetStorageURI(v) + }) +} + +// UpdateStorageURI sets the "storage_uri" field to the value that was provided on create. +func (u *TemplateUpsertBulk) UpdateStorageURI() *TemplateUpsertBulk { + return u.Update(func(s *TemplateUpsert) { + s.UpdateStorageURI() + }) +} + +// ClearStorageURI clears the value of the "storage_uri" field. +func (u *TemplateUpsertBulk) ClearStorageURI() *TemplateUpsertBulk { + return u.Update(func(s *TemplateUpsert) { + s.ClearStorageURI() + }) +} + +// SetStorageBucket sets the "storage_bucket" field. +func (u *TemplateUpsertBulk) SetStorageBucket(v string) *TemplateUpsertBulk { + return u.Update(func(s *TemplateUpsert) { + s.SetStorageBucket(v) + }) +} + +// UpdateStorageBucket sets the "storage_bucket" field to the value that was provided on create. +func (u *TemplateUpsertBulk) UpdateStorageBucket() *TemplateUpsertBulk { + return u.Update(func(s *TemplateUpsert) { + s.UpdateStorageBucket() + }) +} + +// ClearStorageBucket clears the value of the "storage_bucket" field. +func (u *TemplateUpsertBulk) ClearStorageBucket() *TemplateUpsertBulk { + return u.Update(func(s *TemplateUpsert) { + s.ClearStorageBucket() + }) +} + +// SetStoragePath sets the "storage_path" field. +func (u *TemplateUpsertBulk) SetStoragePath(v string) *TemplateUpsertBulk { + return u.Update(func(s *TemplateUpsert) { + s.SetStoragePath(v) + }) +} + +// UpdateStoragePath sets the "storage_path" field to the value that was provided on create. +func (u *TemplateUpsertBulk) UpdateStoragePath() *TemplateUpsertBulk { + return u.Update(func(s *TemplateUpsert) { + s.UpdateStoragePath() + }) +} + +// ClearStoragePath clears the value of the "storage_path" field. +func (u *TemplateUpsertBulk) ClearStoragePath() *TemplateUpsertBulk { + return u.Update(func(s *TemplateUpsert) { + s.ClearStoragePath() + }) +} + +// SetFiles sets the "files" field. +func (u *TemplateUpsertBulk) SetFiles(v string) *TemplateUpsertBulk { + return u.Update(func(s *TemplateUpsert) { + s.SetFiles(v) + }) +} + +// UpdateFiles sets the "files" field to the value that was provided on create. +func (u *TemplateUpsertBulk) UpdateFiles() *TemplateUpsertBulk { + return u.Update(func(s *TemplateUpsert) { + s.UpdateFiles() + }) +} + +// ClearFiles clears the value of the "files" field. +func (u *TemplateUpsertBulk) ClearFiles() *TemplateUpsertBulk { + return u.Update(func(s *TemplateUpsert) { + s.ClearFiles() + }) +} + +// SetBaseTemplate sets the "base_template" field. +func (u *TemplateUpsertBulk) SetBaseTemplate(v string) *TemplateUpsertBulk { + return u.Update(func(s *TemplateUpsert) { + s.SetBaseTemplate(v) + }) +} + +// UpdateBaseTemplate sets the "base_template" field to the value that was provided on create. +func (u *TemplateUpsertBulk) UpdateBaseTemplate() *TemplateUpsertBulk { + return u.Update(func(s *TemplateUpsert) { + s.UpdateBaseTemplate() + }) +} + +// ClearBaseTemplate clears the value of the "base_template" field. +func (u *TemplateUpsertBulk) ClearBaseTemplate() *TemplateUpsertBulk { + return u.Update(func(s *TemplateUpsert) { + s.ClearBaseTemplate() + }) +} + +// SetLocked sets the "locked" field. +func (u *TemplateUpsertBulk) SetLocked(v bool) *TemplateUpsertBulk { + return u.Update(func(s *TemplateUpsert) { + s.SetLocked(v) + }) +} + +// UpdateLocked sets the "locked" field to the value that was provided on create. +func (u *TemplateUpsertBulk) UpdateLocked() *TemplateUpsertBulk { + return u.Update(func(s *TemplateUpsert) { + s.UpdateLocked() + }) +} + +// SetStatus sets the "status" field. +func (u *TemplateUpsertBulk) SetStatus(v template.Status) *TemplateUpsertBulk { + return u.Update(func(s *TemplateUpsert) { + s.SetStatus(v) + }) +} + +// UpdateStatus sets the "status" field to the value that was provided on create. +func (u *TemplateUpsertBulk) UpdateStatus() *TemplateUpsertBulk { + return u.Update(func(s *TemplateUpsert) { + s.UpdateStatus() + }) +} + +// SetOwnerID sets the "owner_id" field. +func (u *TemplateUpsertBulk) SetOwnerID(v string) *TemplateUpsertBulk { + return u.Update(func(s *TemplateUpsert) { + s.SetOwnerID(v) + }) +} + +// UpdateOwnerID sets the "owner_id" field to the value that was provided on create. +func (u *TemplateUpsertBulk) UpdateOwnerID() *TemplateUpsertBulk { + return u.Update(func(s *TemplateUpsert) { + s.UpdateOwnerID() + }) +} + +// ClearOwnerID clears the value of the "owner_id" field. +func (u *TemplateUpsertBulk) ClearOwnerID() *TemplateUpsertBulk { + return u.Update(func(s *TemplateUpsert) { + s.ClearOwnerID() + }) +} + +// SetCreatedBy sets the "created_by" field. +func (u *TemplateUpsertBulk) SetCreatedBy(v string) *TemplateUpsertBulk { + return u.Update(func(s *TemplateUpsert) { + s.SetCreatedBy(v) + }) +} + +// UpdateCreatedBy sets the "created_by" field to the value that was provided on create. +func (u *TemplateUpsertBulk) UpdateCreatedBy() *TemplateUpsertBulk { + return u.Update(func(s *TemplateUpsert) { + s.UpdateCreatedBy() + }) +} + +// ClearCreatedBy clears the value of the "created_by" field. +func (u *TemplateUpsertBulk) ClearCreatedBy() *TemplateUpsertBulk { + return u.Update(func(s *TemplateUpsert) { + s.ClearCreatedBy() + }) +} + +// SetUpdatedBy sets the "updated_by" field. +func (u *TemplateUpsertBulk) SetUpdatedBy(v string) *TemplateUpsertBulk { + return u.Update(func(s *TemplateUpsert) { + s.SetUpdatedBy(v) + }) +} + +// UpdateUpdatedBy sets the "updated_by" field to the value that was provided on create. +func (u *TemplateUpsertBulk) UpdateUpdatedBy() *TemplateUpsertBulk { + return u.Update(func(s *TemplateUpsert) { + s.UpdateUpdatedBy() + }) +} + +// ClearUpdatedBy clears the value of the "updated_by" field. +func (u *TemplateUpsertBulk) ClearUpdatedBy() *TemplateUpsertBulk { + return u.Update(func(s *TemplateUpsert) { + s.ClearUpdatedBy() + }) +} + +// SetVisibility sets the "visibility" field. +func (u *TemplateUpsertBulk) SetVisibility(v string) *TemplateUpsertBulk { + return u.Update(func(s *TemplateUpsert) { + s.SetVisibility(v) + }) +} + +// UpdateVisibility sets the "visibility" field to the value that was provided on create. +func (u *TemplateUpsertBulk) UpdateVisibility() *TemplateUpsertBulk { + return u.Update(func(s *TemplateUpsert) { + s.UpdateVisibility() + }) +} + +// SetUpdated sets the "updated" field. +func (u *TemplateUpsertBulk) SetUpdated(v time.Time) *TemplateUpsertBulk { + return u.Update(func(s *TemplateUpsert) { + s.SetUpdated(v) + }) +} + +// UpdateUpdated sets the "updated" field to the value that was provided on create. +func (u *TemplateUpsertBulk) UpdateUpdated() *TemplateUpsertBulk { + return u.Update(func(s *TemplateUpsert) { + s.UpdateUpdated() + }) +} + +// Exec executes the query. +func (u *TemplateUpsertBulk) Exec(ctx context.Context) error { + if u.create.err != nil { + return u.create.err + } + for i, b := range u.create.builders { + if len(b.conflict) != 0 { + return fmt.Errorf("ent: OnConflict was set for builder %d. Set it on the TemplateCreateBulk instead", i) + } + } + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for TemplateCreateBulk.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *TemplateUpsertBulk) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/pkg/ent/template_delete.go b/pkg/ent/template_delete.go new file mode 100644 index 000000000..5d11d8de1 --- /dev/null +++ b/pkg/ent/template_delete.go @@ -0,0 +1,88 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/GoogleCloudPlatform/scion/pkg/ent/template" +) + +// TemplateDelete is the builder for deleting a Template entity. +type TemplateDelete struct { + config + hooks []Hook + mutation *TemplateMutation +} + +// Where appends a list predicates to the TemplateDelete builder. +func (_d *TemplateDelete) Where(ps ...predicate.Template) *TemplateDelete { + _d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (_d *TemplateDelete) Exec(ctx context.Context) (int, error) { + return withHooks(ctx, _d.sqlExec, _d.mutation, _d.hooks) +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *TemplateDelete) ExecX(ctx context.Context) int { + n, err := _d.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (_d *TemplateDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(template.Table, sqlgraph.NewFieldSpec(template.FieldID, field.TypeUUID)) + if ps := _d.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + affected, err := sqlgraph.DeleteNodes(ctx, _d.driver, _spec) + if err != nil && sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + _d.mutation.done = true + return affected, err +} + +// TemplateDeleteOne is the builder for deleting a single Template entity. +type TemplateDeleteOne struct { + _d *TemplateDelete +} + +// Where appends a list predicates to the TemplateDelete builder. +func (_d *TemplateDeleteOne) Where(ps ...predicate.Template) *TemplateDeleteOne { + _d._d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query. +func (_d *TemplateDeleteOne) Exec(ctx context.Context) error { + n, err := _d._d.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{template.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *TemplateDeleteOne) ExecX(ctx context.Context) { + if err := _d.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/pkg/ent/template_query.go b/pkg/ent/template_query.go new file mode 100644 index 000000000..82f1a5486 --- /dev/null +++ b/pkg/ent/template_query.go @@ -0,0 +1,565 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "fmt" + "math" + + "entgo.io/ent" + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/GoogleCloudPlatform/scion/pkg/ent/template" + "github.com/google/uuid" +) + +// TemplateQuery is the builder for querying Template entities. +type TemplateQuery struct { + config + ctx *QueryContext + order []template.OrderOption + inters []Interceptor + predicates []predicate.Template + modifiers []func(*sql.Selector) + // intermediate query (i.e. traversal path). + sql *sql.Selector + path func(context.Context) (*sql.Selector, error) +} + +// Where adds a new predicate for the TemplateQuery builder. +func (_q *TemplateQuery) Where(ps ...predicate.Template) *TemplateQuery { + _q.predicates = append(_q.predicates, ps...) + return _q +} + +// Limit the number of records to be returned by this query. +func (_q *TemplateQuery) Limit(limit int) *TemplateQuery { + _q.ctx.Limit = &limit + return _q +} + +// Offset to start from. +func (_q *TemplateQuery) Offset(offset int) *TemplateQuery { + _q.ctx.Offset = &offset + return _q +} + +// Unique configures the query builder to filter duplicate records on query. +// By default, unique is set to true, and can be disabled using this method. +func (_q *TemplateQuery) Unique(unique bool) *TemplateQuery { + _q.ctx.Unique = &unique + return _q +} + +// Order specifies how the records should be ordered. +func (_q *TemplateQuery) Order(o ...template.OrderOption) *TemplateQuery { + _q.order = append(_q.order, o...) + return _q +} + +// First returns the first Template entity from the query. +// Returns a *NotFoundError when no Template was found. +func (_q *TemplateQuery) First(ctx context.Context) (*Template, error) { + nodes, err := _q.Limit(1).All(setContextOp(ctx, _q.ctx, ent.OpQueryFirst)) + if err != nil { + return nil, err + } + if len(nodes) == 0 { + return nil, &NotFoundError{template.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (_q *TemplateQuery) FirstX(ctx context.Context) *Template { + node, err := _q.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// FirstID returns the first Template ID from the query. +// Returns a *NotFoundError when no Template ID was found. +func (_q *TemplateQuery) FirstID(ctx context.Context) (id uuid.UUID, err error) { + var ids []uuid.UUID + if ids, err = _q.Limit(1).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryFirstID)); err != nil { + return + } + if len(ids) == 0 { + err = &NotFoundError{template.Label} + return + } + return ids[0], nil +} + +// FirstIDX is like FirstID, but panics if an error occurs. +func (_q *TemplateQuery) FirstIDX(ctx context.Context) uuid.UUID { + id, err := _q.FirstID(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return id +} + +// Only returns a single Template entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one Template entity is found. +// Returns a *NotFoundError when no Template entities are found. +func (_q *TemplateQuery) Only(ctx context.Context) (*Template, error) { + nodes, err := _q.Limit(2).All(setContextOp(ctx, _q.ctx, ent.OpQueryOnly)) + if err != nil { + return nil, err + } + switch len(nodes) { + case 1: + return nodes[0], nil + case 0: + return nil, &NotFoundError{template.Label} + default: + return nil, &NotSingularError{template.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (_q *TemplateQuery) OnlyX(ctx context.Context) *Template { + node, err := _q.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// OnlyID is like Only, but returns the only Template ID in the query. +// Returns a *NotSingularError when more than one Template ID is found. +// Returns a *NotFoundError when no entities are found. +func (_q *TemplateQuery) OnlyID(ctx context.Context) (id uuid.UUID, err error) { + var ids []uuid.UUID + if ids, err = _q.Limit(2).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryOnlyID)); err != nil { + return + } + switch len(ids) { + case 1: + id = ids[0] + case 0: + err = &NotFoundError{template.Label} + default: + err = &NotSingularError{template.Label} + } + return +} + +// OnlyIDX is like OnlyID, but panics if an error occurs. +func (_q *TemplateQuery) OnlyIDX(ctx context.Context) uuid.UUID { + id, err := _q.OnlyID(ctx) + if err != nil { + panic(err) + } + return id +} + +// All executes the query and returns a list of Templates. +func (_q *TemplateQuery) All(ctx context.Context) ([]*Template, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryAll) + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*Template, *TemplateQuery]() + return withInterceptors[[]*Template](ctx, _q, qr, _q.inters) +} + +// AllX is like All, but panics if an error occurs. +func (_q *TemplateQuery) AllX(ctx context.Context) []*Template { + nodes, err := _q.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// IDs executes the query and returns a list of Template IDs. +func (_q *TemplateQuery) IDs(ctx context.Context) (ids []uuid.UUID, err error) { + if _q.ctx.Unique == nil && _q.path != nil { + _q.Unique(true) + } + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryIDs) + if err = _q.Select(template.FieldID).Scan(ctx, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// IDsX is like IDs, but panics if an error occurs. +func (_q *TemplateQuery) IDsX(ctx context.Context) []uuid.UUID { + ids, err := _q.IDs(ctx) + if err != nil { + panic(err) + } + return ids +} + +// Count returns the count of the given query. +func (_q *TemplateQuery) Count(ctx context.Context) (int, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryCount) + if err := _q.prepareQuery(ctx); err != nil { + return 0, err + } + return withInterceptors[int](ctx, _q, querierCount[*TemplateQuery](), _q.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (_q *TemplateQuery) CountX(ctx context.Context) int { + count, err := _q.Count(ctx) + if err != nil { + panic(err) + } + return count +} + +// Exist returns true if the query has elements in the graph. +func (_q *TemplateQuery) Exist(ctx context.Context) (bool, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryExist) + switch _, err := _q.FirstID(ctx); { + case IsNotFound(err): + return false, nil + case err != nil: + return false, fmt.Errorf("ent: check existence: %w", err) + default: + return true, nil + } +} + +// ExistX is like Exist, but panics if an error occurs. +func (_q *TemplateQuery) ExistX(ctx context.Context) bool { + exist, err := _q.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the TemplateQuery builder, including all associated steps. It can be +// used to prepare common query builders and use them differently after the clone is made. +func (_q *TemplateQuery) Clone() *TemplateQuery { + if _q == nil { + return nil + } + return &TemplateQuery{ + config: _q.config, + ctx: _q.ctx.Clone(), + order: append([]template.OrderOption{}, _q.order...), + inters: append([]Interceptor{}, _q.inters...), + predicates: append([]predicate.Template{}, _q.predicates...), + // clone intermediate query. + sql: _q.sql.Clone(), + path: _q.path, + } +} + +// GroupBy is used to group vertices by one or more fields/columns. +// It is often used with aggregate functions, like: count, max, mean, min, sum. +// +// Example: +// +// var v []struct { +// Name string `json:"name,omitempty"` +// Count int `json:"count,omitempty"` +// } +// +// client.Template.Query(). +// GroupBy(template.FieldName). +// Aggregate(ent.Count()). +// Scan(ctx, &v) +func (_q *TemplateQuery) GroupBy(field string, fields ...string) *TemplateGroupBy { + _q.ctx.Fields = append([]string{field}, fields...) + grbuild := &TemplateGroupBy{build: _q} + grbuild.flds = &_q.ctx.Fields + grbuild.label = template.Label + grbuild.scan = grbuild.Scan + return grbuild +} + +// Select allows the selection one or more fields/columns for the given query, +// instead of selecting all fields in the entity. +// +// Example: +// +// var v []struct { +// Name string `json:"name,omitempty"` +// } +// +// client.Template.Query(). +// Select(template.FieldName). +// Scan(ctx, &v) +func (_q *TemplateQuery) Select(fields ...string) *TemplateSelect { + _q.ctx.Fields = append(_q.ctx.Fields, fields...) + sbuild := &TemplateSelect{TemplateQuery: _q} + sbuild.label = template.Label + sbuild.flds, sbuild.scan = &_q.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a TemplateSelect configured with the given aggregations. +func (_q *TemplateQuery) Aggregate(fns ...AggregateFunc) *TemplateSelect { + return _q.Select().Aggregate(fns...) +} + +func (_q *TemplateQuery) prepareQuery(ctx context.Context) error { + for _, inter := range _q.inters { + if inter == nil { + return fmt.Errorf("ent: uninitialized interceptor (forgotten import ent/runtime?)") + } + if trv, ok := inter.(Traverser); ok { + if err := trv.Traverse(ctx, _q); err != nil { + return err + } + } + } + for _, f := range _q.ctx.Fields { + if !template.ValidColumn(f) { + return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + } + if _q.path != nil { + prev, err := _q.path(ctx) + if err != nil { + return err + } + _q.sql = prev + } + return nil +} + +func (_q *TemplateQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*Template, error) { + var ( + nodes = []*Template{} + _spec = _q.querySpec() + ) + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*Template).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &Template{config: _q.config} + nodes = append(nodes, node) + return node.assignValues(columns, values) + } + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } + for i := range hooks { + hooks[i](ctx, _spec) + } + if err := sqlgraph.QueryNodes(ctx, _q.driver, _spec); err != nil { + return nil, err + } + if len(nodes) == 0 { + return nodes, nil + } + return nodes, nil +} + +func (_q *TemplateQuery) sqlCount(ctx context.Context) (int, error) { + _spec := _q.querySpec() + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } + _spec.Node.Columns = _q.ctx.Fields + if len(_q.ctx.Fields) > 0 { + _spec.Unique = _q.ctx.Unique != nil && *_q.ctx.Unique + } + return sqlgraph.CountNodes(ctx, _q.driver, _spec) +} + +func (_q *TemplateQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(template.Table, template.Columns, sqlgraph.NewFieldSpec(template.FieldID, field.TypeUUID)) + _spec.From = _q.sql + if unique := _q.ctx.Unique; unique != nil { + _spec.Unique = *unique + } else if _q.path != nil { + _spec.Unique = true + } + if fields := _q.ctx.Fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, template.FieldID) + for i := range fields { + if fields[i] != template.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, fields[i]) + } + } + } + if ps := _q.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if limit := _q.ctx.Limit; limit != nil { + _spec.Limit = *limit + } + if offset := _q.ctx.Offset; offset != nil { + _spec.Offset = *offset + } + if ps := _q.order; len(ps) > 0 { + _spec.Order = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + return _spec +} + +func (_q *TemplateQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(_q.driver.Dialect()) + t1 := builder.Table(template.Table) + columns := _q.ctx.Fields + if len(columns) == 0 { + columns = template.Columns + } + selector := builder.Select(t1.Columns(columns...)...).From(t1) + if _q.sql != nil { + selector = _q.sql + selector.Select(selector.Columns(columns...)...) + } + if _q.ctx.Unique != nil && *_q.ctx.Unique { + selector.Distinct() + } + for _, m := range _q.modifiers { + m(selector) + } + for _, p := range _q.predicates { + p(selector) + } + for _, p := range _q.order { + p(selector) + } + if offset := _q.ctx.Offset; offset != nil { + // limit is mandatory for offset clause. We start + // with default value, and override it below if needed. + selector.Offset(*offset).Limit(math.MaxInt32) + } + if limit := _q.ctx.Limit; limit != nil { + selector.Limit(*limit) + } + return selector +} + +// ForUpdate locks the selected rows against concurrent updates, and prevent them from being +// updated, deleted or "selected ... for update" by other sessions, until the transaction is +// either committed or rolled-back. +func (_q *TemplateQuery) ForUpdate(opts ...sql.LockOption) *TemplateQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForUpdate(opts...) + }) + return _q +} + +// ForShare behaves similarly to ForUpdate, except that it acquires a shared mode lock +// on any rows that are read. Other sessions can read the rows, but cannot modify them +// until your transaction commits. +func (_q *TemplateQuery) ForShare(opts ...sql.LockOption) *TemplateQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForShare(opts...) + }) + return _q +} + +// TemplateGroupBy is the group-by builder for Template entities. +type TemplateGroupBy struct { + selector + build *TemplateQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (_g *TemplateGroupBy) Aggregate(fns ...AggregateFunc) *TemplateGroupBy { + _g.fns = append(_g.fns, fns...) + return _g +} + +// Scan applies the selector query and scans the result into the given value. +func (_g *TemplateGroupBy) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, _g.build.ctx, ent.OpQueryGroupBy) + if err := _g.build.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*TemplateQuery, *TemplateGroupBy](ctx, _g.build, _g, _g.build.inters, v) +} + +func (_g *TemplateGroupBy) sqlScan(ctx context.Context, root *TemplateQuery, v any) error { + selector := root.sqlQuery(ctx).Select() + aggregation := make([]string, 0, len(_g.fns)) + for _, fn := range _g.fns { + aggregation = append(aggregation, fn(selector)) + } + if len(selector.SelectedColumns()) == 0 { + columns := make([]string, 0, len(*_g.flds)+len(_g.fns)) + for _, f := range *_g.flds { + columns = append(columns, selector.C(f)) + } + columns = append(columns, aggregation...) + selector.Select(columns...) + } + selector.GroupBy(selector.Columns(*_g.flds...)...) + if err := selector.Err(); err != nil { + return err + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := _g.build.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} + +// TemplateSelect is the builder for selecting fields of Template entities. +type TemplateSelect struct { + *TemplateQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (_s *TemplateSelect) Aggregate(fns ...AggregateFunc) *TemplateSelect { + _s.fns = append(_s.fns, fns...) + return _s +} + +// Scan applies the selector query and scans the result into the given value. +func (_s *TemplateSelect) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, _s.ctx, ent.OpQuerySelect) + if err := _s.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*TemplateQuery, *TemplateSelect](ctx, _s.TemplateQuery, _s, _s.inters, v) +} + +func (_s *TemplateSelect) sqlScan(ctx context.Context, root *TemplateQuery, v any) error { + selector := root.sqlQuery(ctx) + aggregation := make([]string, 0, len(_s.fns)) + for _, fn := range _s.fns { + aggregation = append(aggregation, fn(selector)) + } + switch n := len(*_s.selector.flds); { + case n == 0 && len(aggregation) > 0: + selector.Select(aggregation...) + case n != 0 && len(aggregation) > 0: + selector.AppendSelect(aggregation...) + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := _s.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} diff --git a/pkg/ent/template_update.go b/pkg/ent/template_update.go new file mode 100644 index 000000000..c8d12df0c --- /dev/null +++ b/pkg/ent/template_update.go @@ -0,0 +1,1328 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/GoogleCloudPlatform/scion/pkg/ent/template" +) + +// TemplateUpdate is the builder for updating Template entities. +type TemplateUpdate struct { + config + hooks []Hook + mutation *TemplateMutation +} + +// Where appends a list predicates to the TemplateUpdate builder. +func (_u *TemplateUpdate) Where(ps ...predicate.Template) *TemplateUpdate { + _u.mutation.Where(ps...) + return _u +} + +// SetName sets the "name" field. +func (_u *TemplateUpdate) SetName(v string) *TemplateUpdate { + _u.mutation.SetName(v) + return _u +} + +// SetNillableName sets the "name" field if the given value is not nil. +func (_u *TemplateUpdate) SetNillableName(v *string) *TemplateUpdate { + if v != nil { + _u.SetName(*v) + } + return _u +} + +// SetSlug sets the "slug" field. +func (_u *TemplateUpdate) SetSlug(v string) *TemplateUpdate { + _u.mutation.SetSlug(v) + return _u +} + +// SetNillableSlug sets the "slug" field if the given value is not nil. +func (_u *TemplateUpdate) SetNillableSlug(v *string) *TemplateUpdate { + if v != nil { + _u.SetSlug(*v) + } + return _u +} + +// SetDisplayName sets the "display_name" field. +func (_u *TemplateUpdate) SetDisplayName(v string) *TemplateUpdate { + _u.mutation.SetDisplayName(v) + return _u +} + +// SetNillableDisplayName sets the "display_name" field if the given value is not nil. +func (_u *TemplateUpdate) SetNillableDisplayName(v *string) *TemplateUpdate { + if v != nil { + _u.SetDisplayName(*v) + } + return _u +} + +// ClearDisplayName clears the value of the "display_name" field. +func (_u *TemplateUpdate) ClearDisplayName() *TemplateUpdate { + _u.mutation.ClearDisplayName() + return _u +} + +// SetDescription sets the "description" field. +func (_u *TemplateUpdate) SetDescription(v string) *TemplateUpdate { + _u.mutation.SetDescription(v) + return _u +} + +// SetNillableDescription sets the "description" field if the given value is not nil. +func (_u *TemplateUpdate) SetNillableDescription(v *string) *TemplateUpdate { + if v != nil { + _u.SetDescription(*v) + } + return _u +} + +// ClearDescription clears the value of the "description" field. +func (_u *TemplateUpdate) ClearDescription() *TemplateUpdate { + _u.mutation.ClearDescription() + return _u +} + +// SetHarness sets the "harness" field. +func (_u *TemplateUpdate) SetHarness(v string) *TemplateUpdate { + _u.mutation.SetHarness(v) + return _u +} + +// SetNillableHarness sets the "harness" field if the given value is not nil. +func (_u *TemplateUpdate) SetNillableHarness(v *string) *TemplateUpdate { + if v != nil { + _u.SetHarness(*v) + } + return _u +} + +// SetDefaultHarnessConfig sets the "default_harness_config" field. +func (_u *TemplateUpdate) SetDefaultHarnessConfig(v string) *TemplateUpdate { + _u.mutation.SetDefaultHarnessConfig(v) + return _u +} + +// SetNillableDefaultHarnessConfig sets the "default_harness_config" field if the given value is not nil. +func (_u *TemplateUpdate) SetNillableDefaultHarnessConfig(v *string) *TemplateUpdate { + if v != nil { + _u.SetDefaultHarnessConfig(*v) + } + return _u +} + +// ClearDefaultHarnessConfig clears the value of the "default_harness_config" field. +func (_u *TemplateUpdate) ClearDefaultHarnessConfig() *TemplateUpdate { + _u.mutation.ClearDefaultHarnessConfig() + return _u +} + +// SetImage sets the "image" field. +func (_u *TemplateUpdate) SetImage(v string) *TemplateUpdate { + _u.mutation.SetImage(v) + return _u +} + +// SetNillableImage sets the "image" field if the given value is not nil. +func (_u *TemplateUpdate) SetNillableImage(v *string) *TemplateUpdate { + if v != nil { + _u.SetImage(*v) + } + return _u +} + +// ClearImage clears the value of the "image" field. +func (_u *TemplateUpdate) ClearImage() *TemplateUpdate { + _u.mutation.ClearImage() + return _u +} + +// SetConfig sets the "config" field. +func (_u *TemplateUpdate) SetConfig(v string) *TemplateUpdate { + _u.mutation.SetConfig(v) + return _u +} + +// SetNillableConfig sets the "config" field if the given value is not nil. +func (_u *TemplateUpdate) SetNillableConfig(v *string) *TemplateUpdate { + if v != nil { + _u.SetConfig(*v) + } + return _u +} + +// ClearConfig clears the value of the "config" field. +func (_u *TemplateUpdate) ClearConfig() *TemplateUpdate { + _u.mutation.ClearConfig() + return _u +} + +// SetContentHash sets the "content_hash" field. +func (_u *TemplateUpdate) SetContentHash(v string) *TemplateUpdate { + _u.mutation.SetContentHash(v) + return _u +} + +// SetNillableContentHash sets the "content_hash" field if the given value is not nil. +func (_u *TemplateUpdate) SetNillableContentHash(v *string) *TemplateUpdate { + if v != nil { + _u.SetContentHash(*v) + } + return _u +} + +// ClearContentHash clears the value of the "content_hash" field. +func (_u *TemplateUpdate) ClearContentHash() *TemplateUpdate { + _u.mutation.ClearContentHash() + return _u +} + +// SetScope sets the "scope" field. +func (_u *TemplateUpdate) SetScope(v string) *TemplateUpdate { + _u.mutation.SetScope(v) + return _u +} + +// SetNillableScope sets the "scope" field if the given value is not nil. +func (_u *TemplateUpdate) SetNillableScope(v *string) *TemplateUpdate { + if v != nil { + _u.SetScope(*v) + } + return _u +} + +// SetScopeID sets the "scope_id" field. +func (_u *TemplateUpdate) SetScopeID(v string) *TemplateUpdate { + _u.mutation.SetScopeID(v) + return _u +} + +// SetNillableScopeID sets the "scope_id" field if the given value is not nil. +func (_u *TemplateUpdate) SetNillableScopeID(v *string) *TemplateUpdate { + if v != nil { + _u.SetScopeID(*v) + } + return _u +} + +// ClearScopeID clears the value of the "scope_id" field. +func (_u *TemplateUpdate) ClearScopeID() *TemplateUpdate { + _u.mutation.ClearScopeID() + return _u +} + +// SetProjectID sets the "project_id" field. +func (_u *TemplateUpdate) SetProjectID(v string) *TemplateUpdate { + _u.mutation.SetProjectID(v) + return _u +} + +// SetNillableProjectID sets the "project_id" field if the given value is not nil. +func (_u *TemplateUpdate) SetNillableProjectID(v *string) *TemplateUpdate { + if v != nil { + _u.SetProjectID(*v) + } + return _u +} + +// ClearProjectID clears the value of the "project_id" field. +func (_u *TemplateUpdate) ClearProjectID() *TemplateUpdate { + _u.mutation.ClearProjectID() + return _u +} + +// SetStorageURI sets the "storage_uri" field. +func (_u *TemplateUpdate) SetStorageURI(v string) *TemplateUpdate { + _u.mutation.SetStorageURI(v) + return _u +} + +// SetNillableStorageURI sets the "storage_uri" field if the given value is not nil. +func (_u *TemplateUpdate) SetNillableStorageURI(v *string) *TemplateUpdate { + if v != nil { + _u.SetStorageURI(*v) + } + return _u +} + +// ClearStorageURI clears the value of the "storage_uri" field. +func (_u *TemplateUpdate) ClearStorageURI() *TemplateUpdate { + _u.mutation.ClearStorageURI() + return _u +} + +// SetStorageBucket sets the "storage_bucket" field. +func (_u *TemplateUpdate) SetStorageBucket(v string) *TemplateUpdate { + _u.mutation.SetStorageBucket(v) + return _u +} + +// SetNillableStorageBucket sets the "storage_bucket" field if the given value is not nil. +func (_u *TemplateUpdate) SetNillableStorageBucket(v *string) *TemplateUpdate { + if v != nil { + _u.SetStorageBucket(*v) + } + return _u +} + +// ClearStorageBucket clears the value of the "storage_bucket" field. +func (_u *TemplateUpdate) ClearStorageBucket() *TemplateUpdate { + _u.mutation.ClearStorageBucket() + return _u +} + +// SetStoragePath sets the "storage_path" field. +func (_u *TemplateUpdate) SetStoragePath(v string) *TemplateUpdate { + _u.mutation.SetStoragePath(v) + return _u +} + +// SetNillableStoragePath sets the "storage_path" field if the given value is not nil. +func (_u *TemplateUpdate) SetNillableStoragePath(v *string) *TemplateUpdate { + if v != nil { + _u.SetStoragePath(*v) + } + return _u +} + +// ClearStoragePath clears the value of the "storage_path" field. +func (_u *TemplateUpdate) ClearStoragePath() *TemplateUpdate { + _u.mutation.ClearStoragePath() + return _u +} + +// SetFiles sets the "files" field. +func (_u *TemplateUpdate) SetFiles(v string) *TemplateUpdate { + _u.mutation.SetFiles(v) + return _u +} + +// SetNillableFiles sets the "files" field if the given value is not nil. +func (_u *TemplateUpdate) SetNillableFiles(v *string) *TemplateUpdate { + if v != nil { + _u.SetFiles(*v) + } + return _u +} + +// ClearFiles clears the value of the "files" field. +func (_u *TemplateUpdate) ClearFiles() *TemplateUpdate { + _u.mutation.ClearFiles() + return _u +} + +// SetBaseTemplate sets the "base_template" field. +func (_u *TemplateUpdate) SetBaseTemplate(v string) *TemplateUpdate { + _u.mutation.SetBaseTemplate(v) + return _u +} + +// SetNillableBaseTemplate sets the "base_template" field if the given value is not nil. +func (_u *TemplateUpdate) SetNillableBaseTemplate(v *string) *TemplateUpdate { + if v != nil { + _u.SetBaseTemplate(*v) + } + return _u +} + +// ClearBaseTemplate clears the value of the "base_template" field. +func (_u *TemplateUpdate) ClearBaseTemplate() *TemplateUpdate { + _u.mutation.ClearBaseTemplate() + return _u +} + +// SetLocked sets the "locked" field. +func (_u *TemplateUpdate) SetLocked(v bool) *TemplateUpdate { + _u.mutation.SetLocked(v) + return _u +} + +// SetNillableLocked sets the "locked" field if the given value is not nil. +func (_u *TemplateUpdate) SetNillableLocked(v *bool) *TemplateUpdate { + if v != nil { + _u.SetLocked(*v) + } + return _u +} + +// SetStatus sets the "status" field. +func (_u *TemplateUpdate) SetStatus(v template.Status) *TemplateUpdate { + _u.mutation.SetStatus(v) + return _u +} + +// SetNillableStatus sets the "status" field if the given value is not nil. +func (_u *TemplateUpdate) SetNillableStatus(v *template.Status) *TemplateUpdate { + if v != nil { + _u.SetStatus(*v) + } + return _u +} + +// SetOwnerID sets the "owner_id" field. +func (_u *TemplateUpdate) SetOwnerID(v string) *TemplateUpdate { + _u.mutation.SetOwnerID(v) + return _u +} + +// SetNillableOwnerID sets the "owner_id" field if the given value is not nil. +func (_u *TemplateUpdate) SetNillableOwnerID(v *string) *TemplateUpdate { + if v != nil { + _u.SetOwnerID(*v) + } + return _u +} + +// ClearOwnerID clears the value of the "owner_id" field. +func (_u *TemplateUpdate) ClearOwnerID() *TemplateUpdate { + _u.mutation.ClearOwnerID() + return _u +} + +// SetCreatedBy sets the "created_by" field. +func (_u *TemplateUpdate) SetCreatedBy(v string) *TemplateUpdate { + _u.mutation.SetCreatedBy(v) + return _u +} + +// SetNillableCreatedBy sets the "created_by" field if the given value is not nil. +func (_u *TemplateUpdate) SetNillableCreatedBy(v *string) *TemplateUpdate { + if v != nil { + _u.SetCreatedBy(*v) + } + return _u +} + +// ClearCreatedBy clears the value of the "created_by" field. +func (_u *TemplateUpdate) ClearCreatedBy() *TemplateUpdate { + _u.mutation.ClearCreatedBy() + return _u +} + +// SetUpdatedBy sets the "updated_by" field. +func (_u *TemplateUpdate) SetUpdatedBy(v string) *TemplateUpdate { + _u.mutation.SetUpdatedBy(v) + return _u +} + +// SetNillableUpdatedBy sets the "updated_by" field if the given value is not nil. +func (_u *TemplateUpdate) SetNillableUpdatedBy(v *string) *TemplateUpdate { + if v != nil { + _u.SetUpdatedBy(*v) + } + return _u +} + +// ClearUpdatedBy clears the value of the "updated_by" field. +func (_u *TemplateUpdate) ClearUpdatedBy() *TemplateUpdate { + _u.mutation.ClearUpdatedBy() + return _u +} + +// SetVisibility sets the "visibility" field. +func (_u *TemplateUpdate) SetVisibility(v string) *TemplateUpdate { + _u.mutation.SetVisibility(v) + return _u +} + +// SetNillableVisibility sets the "visibility" field if the given value is not nil. +func (_u *TemplateUpdate) SetNillableVisibility(v *string) *TemplateUpdate { + if v != nil { + _u.SetVisibility(*v) + } + return _u +} + +// SetUpdated sets the "updated" field. +func (_u *TemplateUpdate) SetUpdated(v time.Time) *TemplateUpdate { + _u.mutation.SetUpdated(v) + return _u +} + +// Mutation returns the TemplateMutation object of the builder. +func (_u *TemplateUpdate) Mutation() *TemplateMutation { + return _u.mutation +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (_u *TemplateUpdate) Save(ctx context.Context) (int, error) { + _u.defaults() + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *TemplateUpdate) SaveX(ctx context.Context) int { + affected, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (_u *TemplateUpdate) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *TemplateUpdate) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_u *TemplateUpdate) defaults() { + if _, ok := _u.mutation.Updated(); !ok { + v := template.UpdateDefaultUpdated() + _u.mutation.SetUpdated(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_u *TemplateUpdate) check() error { + if v, ok := _u.mutation.Name(); ok { + if err := template.NameValidator(v); err != nil { + return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "Template.name": %w`, err)} + } + } + if v, ok := _u.mutation.Slug(); ok { + if err := template.SlugValidator(v); err != nil { + return &ValidationError{Name: "slug", err: fmt.Errorf(`ent: validator failed for field "Template.slug": %w`, err)} + } + } + if v, ok := _u.mutation.Status(); ok { + if err := template.StatusValidator(v); err != nil { + return &ValidationError{Name: "status", err: fmt.Errorf(`ent: validator failed for field "Template.status": %w`, err)} + } + } + return nil +} + +func (_u *TemplateUpdate) sqlSave(ctx context.Context) (_node int, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(template.Table, template.Columns, sqlgraph.NewFieldSpec(template.FieldID, field.TypeUUID)) + if ps := _u.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := _u.mutation.Name(); ok { + _spec.SetField(template.FieldName, field.TypeString, value) + } + if value, ok := _u.mutation.Slug(); ok { + _spec.SetField(template.FieldSlug, field.TypeString, value) + } + if value, ok := _u.mutation.DisplayName(); ok { + _spec.SetField(template.FieldDisplayName, field.TypeString, value) + } + if _u.mutation.DisplayNameCleared() { + _spec.ClearField(template.FieldDisplayName, field.TypeString) + } + if value, ok := _u.mutation.Description(); ok { + _spec.SetField(template.FieldDescription, field.TypeString, value) + } + if _u.mutation.DescriptionCleared() { + _spec.ClearField(template.FieldDescription, field.TypeString) + } + if value, ok := _u.mutation.Harness(); ok { + _spec.SetField(template.FieldHarness, field.TypeString, value) + } + if value, ok := _u.mutation.DefaultHarnessConfig(); ok { + _spec.SetField(template.FieldDefaultHarnessConfig, field.TypeString, value) + } + if _u.mutation.DefaultHarnessConfigCleared() { + _spec.ClearField(template.FieldDefaultHarnessConfig, field.TypeString) + } + if value, ok := _u.mutation.Image(); ok { + _spec.SetField(template.FieldImage, field.TypeString, value) + } + if _u.mutation.ImageCleared() { + _spec.ClearField(template.FieldImage, field.TypeString) + } + if value, ok := _u.mutation.Config(); ok { + _spec.SetField(template.FieldConfig, field.TypeString, value) + } + if _u.mutation.ConfigCleared() { + _spec.ClearField(template.FieldConfig, field.TypeString) + } + if value, ok := _u.mutation.ContentHash(); ok { + _spec.SetField(template.FieldContentHash, field.TypeString, value) + } + if _u.mutation.ContentHashCleared() { + _spec.ClearField(template.FieldContentHash, field.TypeString) + } + if value, ok := _u.mutation.Scope(); ok { + _spec.SetField(template.FieldScope, field.TypeString, value) + } + if value, ok := _u.mutation.ScopeID(); ok { + _spec.SetField(template.FieldScopeID, field.TypeString, value) + } + if _u.mutation.ScopeIDCleared() { + _spec.ClearField(template.FieldScopeID, field.TypeString) + } + if value, ok := _u.mutation.ProjectID(); ok { + _spec.SetField(template.FieldProjectID, field.TypeString, value) + } + if _u.mutation.ProjectIDCleared() { + _spec.ClearField(template.FieldProjectID, field.TypeString) + } + if value, ok := _u.mutation.StorageURI(); ok { + _spec.SetField(template.FieldStorageURI, field.TypeString, value) + } + if _u.mutation.StorageURICleared() { + _spec.ClearField(template.FieldStorageURI, field.TypeString) + } + if value, ok := _u.mutation.StorageBucket(); ok { + _spec.SetField(template.FieldStorageBucket, field.TypeString, value) + } + if _u.mutation.StorageBucketCleared() { + _spec.ClearField(template.FieldStorageBucket, field.TypeString) + } + if value, ok := _u.mutation.StoragePath(); ok { + _spec.SetField(template.FieldStoragePath, field.TypeString, value) + } + if _u.mutation.StoragePathCleared() { + _spec.ClearField(template.FieldStoragePath, field.TypeString) + } + if value, ok := _u.mutation.Files(); ok { + _spec.SetField(template.FieldFiles, field.TypeString, value) + } + if _u.mutation.FilesCleared() { + _spec.ClearField(template.FieldFiles, field.TypeString) + } + if value, ok := _u.mutation.BaseTemplate(); ok { + _spec.SetField(template.FieldBaseTemplate, field.TypeString, value) + } + if _u.mutation.BaseTemplateCleared() { + _spec.ClearField(template.FieldBaseTemplate, field.TypeString) + } + if value, ok := _u.mutation.Locked(); ok { + _spec.SetField(template.FieldLocked, field.TypeBool, value) + } + if value, ok := _u.mutation.Status(); ok { + _spec.SetField(template.FieldStatus, field.TypeEnum, value) + } + if value, ok := _u.mutation.OwnerID(); ok { + _spec.SetField(template.FieldOwnerID, field.TypeString, value) + } + if _u.mutation.OwnerIDCleared() { + _spec.ClearField(template.FieldOwnerID, field.TypeString) + } + if value, ok := _u.mutation.CreatedBy(); ok { + _spec.SetField(template.FieldCreatedBy, field.TypeString, value) + } + if _u.mutation.CreatedByCleared() { + _spec.ClearField(template.FieldCreatedBy, field.TypeString) + } + if value, ok := _u.mutation.UpdatedBy(); ok { + _spec.SetField(template.FieldUpdatedBy, field.TypeString, value) + } + if _u.mutation.UpdatedByCleared() { + _spec.ClearField(template.FieldUpdatedBy, field.TypeString) + } + if value, ok := _u.mutation.Visibility(); ok { + _spec.SetField(template.FieldVisibility, field.TypeString, value) + } + if value, ok := _u.mutation.Updated(); ok { + _spec.SetField(template.FieldUpdated, field.TypeTime, value) + } + if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{template.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + _u.mutation.done = true + return _node, nil +} + +// TemplateUpdateOne is the builder for updating a single Template entity. +type TemplateUpdateOne struct { + config + fields []string + hooks []Hook + mutation *TemplateMutation +} + +// SetName sets the "name" field. +func (_u *TemplateUpdateOne) SetName(v string) *TemplateUpdateOne { + _u.mutation.SetName(v) + return _u +} + +// SetNillableName sets the "name" field if the given value is not nil. +func (_u *TemplateUpdateOne) SetNillableName(v *string) *TemplateUpdateOne { + if v != nil { + _u.SetName(*v) + } + return _u +} + +// SetSlug sets the "slug" field. +func (_u *TemplateUpdateOne) SetSlug(v string) *TemplateUpdateOne { + _u.mutation.SetSlug(v) + return _u +} + +// SetNillableSlug sets the "slug" field if the given value is not nil. +func (_u *TemplateUpdateOne) SetNillableSlug(v *string) *TemplateUpdateOne { + if v != nil { + _u.SetSlug(*v) + } + return _u +} + +// SetDisplayName sets the "display_name" field. +func (_u *TemplateUpdateOne) SetDisplayName(v string) *TemplateUpdateOne { + _u.mutation.SetDisplayName(v) + return _u +} + +// SetNillableDisplayName sets the "display_name" field if the given value is not nil. +func (_u *TemplateUpdateOne) SetNillableDisplayName(v *string) *TemplateUpdateOne { + if v != nil { + _u.SetDisplayName(*v) + } + return _u +} + +// ClearDisplayName clears the value of the "display_name" field. +func (_u *TemplateUpdateOne) ClearDisplayName() *TemplateUpdateOne { + _u.mutation.ClearDisplayName() + return _u +} + +// SetDescription sets the "description" field. +func (_u *TemplateUpdateOne) SetDescription(v string) *TemplateUpdateOne { + _u.mutation.SetDescription(v) + return _u +} + +// SetNillableDescription sets the "description" field if the given value is not nil. +func (_u *TemplateUpdateOne) SetNillableDescription(v *string) *TemplateUpdateOne { + if v != nil { + _u.SetDescription(*v) + } + return _u +} + +// ClearDescription clears the value of the "description" field. +func (_u *TemplateUpdateOne) ClearDescription() *TemplateUpdateOne { + _u.mutation.ClearDescription() + return _u +} + +// SetHarness sets the "harness" field. +func (_u *TemplateUpdateOne) SetHarness(v string) *TemplateUpdateOne { + _u.mutation.SetHarness(v) + return _u +} + +// SetNillableHarness sets the "harness" field if the given value is not nil. +func (_u *TemplateUpdateOne) SetNillableHarness(v *string) *TemplateUpdateOne { + if v != nil { + _u.SetHarness(*v) + } + return _u +} + +// SetDefaultHarnessConfig sets the "default_harness_config" field. +func (_u *TemplateUpdateOne) SetDefaultHarnessConfig(v string) *TemplateUpdateOne { + _u.mutation.SetDefaultHarnessConfig(v) + return _u +} + +// SetNillableDefaultHarnessConfig sets the "default_harness_config" field if the given value is not nil. +func (_u *TemplateUpdateOne) SetNillableDefaultHarnessConfig(v *string) *TemplateUpdateOne { + if v != nil { + _u.SetDefaultHarnessConfig(*v) + } + return _u +} + +// ClearDefaultHarnessConfig clears the value of the "default_harness_config" field. +func (_u *TemplateUpdateOne) ClearDefaultHarnessConfig() *TemplateUpdateOne { + _u.mutation.ClearDefaultHarnessConfig() + return _u +} + +// SetImage sets the "image" field. +func (_u *TemplateUpdateOne) SetImage(v string) *TemplateUpdateOne { + _u.mutation.SetImage(v) + return _u +} + +// SetNillableImage sets the "image" field if the given value is not nil. +func (_u *TemplateUpdateOne) SetNillableImage(v *string) *TemplateUpdateOne { + if v != nil { + _u.SetImage(*v) + } + return _u +} + +// ClearImage clears the value of the "image" field. +func (_u *TemplateUpdateOne) ClearImage() *TemplateUpdateOne { + _u.mutation.ClearImage() + return _u +} + +// SetConfig sets the "config" field. +func (_u *TemplateUpdateOne) SetConfig(v string) *TemplateUpdateOne { + _u.mutation.SetConfig(v) + return _u +} + +// SetNillableConfig sets the "config" field if the given value is not nil. +func (_u *TemplateUpdateOne) SetNillableConfig(v *string) *TemplateUpdateOne { + if v != nil { + _u.SetConfig(*v) + } + return _u +} + +// ClearConfig clears the value of the "config" field. +func (_u *TemplateUpdateOne) ClearConfig() *TemplateUpdateOne { + _u.mutation.ClearConfig() + return _u +} + +// SetContentHash sets the "content_hash" field. +func (_u *TemplateUpdateOne) SetContentHash(v string) *TemplateUpdateOne { + _u.mutation.SetContentHash(v) + return _u +} + +// SetNillableContentHash sets the "content_hash" field if the given value is not nil. +func (_u *TemplateUpdateOne) SetNillableContentHash(v *string) *TemplateUpdateOne { + if v != nil { + _u.SetContentHash(*v) + } + return _u +} + +// ClearContentHash clears the value of the "content_hash" field. +func (_u *TemplateUpdateOne) ClearContentHash() *TemplateUpdateOne { + _u.mutation.ClearContentHash() + return _u +} + +// SetScope sets the "scope" field. +func (_u *TemplateUpdateOne) SetScope(v string) *TemplateUpdateOne { + _u.mutation.SetScope(v) + return _u +} + +// SetNillableScope sets the "scope" field if the given value is not nil. +func (_u *TemplateUpdateOne) SetNillableScope(v *string) *TemplateUpdateOne { + if v != nil { + _u.SetScope(*v) + } + return _u +} + +// SetScopeID sets the "scope_id" field. +func (_u *TemplateUpdateOne) SetScopeID(v string) *TemplateUpdateOne { + _u.mutation.SetScopeID(v) + return _u +} + +// SetNillableScopeID sets the "scope_id" field if the given value is not nil. +func (_u *TemplateUpdateOne) SetNillableScopeID(v *string) *TemplateUpdateOne { + if v != nil { + _u.SetScopeID(*v) + } + return _u +} + +// ClearScopeID clears the value of the "scope_id" field. +func (_u *TemplateUpdateOne) ClearScopeID() *TemplateUpdateOne { + _u.mutation.ClearScopeID() + return _u +} + +// SetProjectID sets the "project_id" field. +func (_u *TemplateUpdateOne) SetProjectID(v string) *TemplateUpdateOne { + _u.mutation.SetProjectID(v) + return _u +} + +// SetNillableProjectID sets the "project_id" field if the given value is not nil. +func (_u *TemplateUpdateOne) SetNillableProjectID(v *string) *TemplateUpdateOne { + if v != nil { + _u.SetProjectID(*v) + } + return _u +} + +// ClearProjectID clears the value of the "project_id" field. +func (_u *TemplateUpdateOne) ClearProjectID() *TemplateUpdateOne { + _u.mutation.ClearProjectID() + return _u +} + +// SetStorageURI sets the "storage_uri" field. +func (_u *TemplateUpdateOne) SetStorageURI(v string) *TemplateUpdateOne { + _u.mutation.SetStorageURI(v) + return _u +} + +// SetNillableStorageURI sets the "storage_uri" field if the given value is not nil. +func (_u *TemplateUpdateOne) SetNillableStorageURI(v *string) *TemplateUpdateOne { + if v != nil { + _u.SetStorageURI(*v) + } + return _u +} + +// ClearStorageURI clears the value of the "storage_uri" field. +func (_u *TemplateUpdateOne) ClearStorageURI() *TemplateUpdateOne { + _u.mutation.ClearStorageURI() + return _u +} + +// SetStorageBucket sets the "storage_bucket" field. +func (_u *TemplateUpdateOne) SetStorageBucket(v string) *TemplateUpdateOne { + _u.mutation.SetStorageBucket(v) + return _u +} + +// SetNillableStorageBucket sets the "storage_bucket" field if the given value is not nil. +func (_u *TemplateUpdateOne) SetNillableStorageBucket(v *string) *TemplateUpdateOne { + if v != nil { + _u.SetStorageBucket(*v) + } + return _u +} + +// ClearStorageBucket clears the value of the "storage_bucket" field. +func (_u *TemplateUpdateOne) ClearStorageBucket() *TemplateUpdateOne { + _u.mutation.ClearStorageBucket() + return _u +} + +// SetStoragePath sets the "storage_path" field. +func (_u *TemplateUpdateOne) SetStoragePath(v string) *TemplateUpdateOne { + _u.mutation.SetStoragePath(v) + return _u +} + +// SetNillableStoragePath sets the "storage_path" field if the given value is not nil. +func (_u *TemplateUpdateOne) SetNillableStoragePath(v *string) *TemplateUpdateOne { + if v != nil { + _u.SetStoragePath(*v) + } + return _u +} + +// ClearStoragePath clears the value of the "storage_path" field. +func (_u *TemplateUpdateOne) ClearStoragePath() *TemplateUpdateOne { + _u.mutation.ClearStoragePath() + return _u +} + +// SetFiles sets the "files" field. +func (_u *TemplateUpdateOne) SetFiles(v string) *TemplateUpdateOne { + _u.mutation.SetFiles(v) + return _u +} + +// SetNillableFiles sets the "files" field if the given value is not nil. +func (_u *TemplateUpdateOne) SetNillableFiles(v *string) *TemplateUpdateOne { + if v != nil { + _u.SetFiles(*v) + } + return _u +} + +// ClearFiles clears the value of the "files" field. +func (_u *TemplateUpdateOne) ClearFiles() *TemplateUpdateOne { + _u.mutation.ClearFiles() + return _u +} + +// SetBaseTemplate sets the "base_template" field. +func (_u *TemplateUpdateOne) SetBaseTemplate(v string) *TemplateUpdateOne { + _u.mutation.SetBaseTemplate(v) + return _u +} + +// SetNillableBaseTemplate sets the "base_template" field if the given value is not nil. +func (_u *TemplateUpdateOne) SetNillableBaseTemplate(v *string) *TemplateUpdateOne { + if v != nil { + _u.SetBaseTemplate(*v) + } + return _u +} + +// ClearBaseTemplate clears the value of the "base_template" field. +func (_u *TemplateUpdateOne) ClearBaseTemplate() *TemplateUpdateOne { + _u.mutation.ClearBaseTemplate() + return _u +} + +// SetLocked sets the "locked" field. +func (_u *TemplateUpdateOne) SetLocked(v bool) *TemplateUpdateOne { + _u.mutation.SetLocked(v) + return _u +} + +// SetNillableLocked sets the "locked" field if the given value is not nil. +func (_u *TemplateUpdateOne) SetNillableLocked(v *bool) *TemplateUpdateOne { + if v != nil { + _u.SetLocked(*v) + } + return _u +} + +// SetStatus sets the "status" field. +func (_u *TemplateUpdateOne) SetStatus(v template.Status) *TemplateUpdateOne { + _u.mutation.SetStatus(v) + return _u +} + +// SetNillableStatus sets the "status" field if the given value is not nil. +func (_u *TemplateUpdateOne) SetNillableStatus(v *template.Status) *TemplateUpdateOne { + if v != nil { + _u.SetStatus(*v) + } + return _u +} + +// SetOwnerID sets the "owner_id" field. +func (_u *TemplateUpdateOne) SetOwnerID(v string) *TemplateUpdateOne { + _u.mutation.SetOwnerID(v) + return _u +} + +// SetNillableOwnerID sets the "owner_id" field if the given value is not nil. +func (_u *TemplateUpdateOne) SetNillableOwnerID(v *string) *TemplateUpdateOne { + if v != nil { + _u.SetOwnerID(*v) + } + return _u +} + +// ClearOwnerID clears the value of the "owner_id" field. +func (_u *TemplateUpdateOne) ClearOwnerID() *TemplateUpdateOne { + _u.mutation.ClearOwnerID() + return _u +} + +// SetCreatedBy sets the "created_by" field. +func (_u *TemplateUpdateOne) SetCreatedBy(v string) *TemplateUpdateOne { + _u.mutation.SetCreatedBy(v) + return _u +} + +// SetNillableCreatedBy sets the "created_by" field if the given value is not nil. +func (_u *TemplateUpdateOne) SetNillableCreatedBy(v *string) *TemplateUpdateOne { + if v != nil { + _u.SetCreatedBy(*v) + } + return _u +} + +// ClearCreatedBy clears the value of the "created_by" field. +func (_u *TemplateUpdateOne) ClearCreatedBy() *TemplateUpdateOne { + _u.mutation.ClearCreatedBy() + return _u +} + +// SetUpdatedBy sets the "updated_by" field. +func (_u *TemplateUpdateOne) SetUpdatedBy(v string) *TemplateUpdateOne { + _u.mutation.SetUpdatedBy(v) + return _u +} + +// SetNillableUpdatedBy sets the "updated_by" field if the given value is not nil. +func (_u *TemplateUpdateOne) SetNillableUpdatedBy(v *string) *TemplateUpdateOne { + if v != nil { + _u.SetUpdatedBy(*v) + } + return _u +} + +// ClearUpdatedBy clears the value of the "updated_by" field. +func (_u *TemplateUpdateOne) ClearUpdatedBy() *TemplateUpdateOne { + _u.mutation.ClearUpdatedBy() + return _u +} + +// SetVisibility sets the "visibility" field. +func (_u *TemplateUpdateOne) SetVisibility(v string) *TemplateUpdateOne { + _u.mutation.SetVisibility(v) + return _u +} + +// SetNillableVisibility sets the "visibility" field if the given value is not nil. +func (_u *TemplateUpdateOne) SetNillableVisibility(v *string) *TemplateUpdateOne { + if v != nil { + _u.SetVisibility(*v) + } + return _u +} + +// SetUpdated sets the "updated" field. +func (_u *TemplateUpdateOne) SetUpdated(v time.Time) *TemplateUpdateOne { + _u.mutation.SetUpdated(v) + return _u +} + +// Mutation returns the TemplateMutation object of the builder. +func (_u *TemplateUpdateOne) Mutation() *TemplateMutation { + return _u.mutation +} + +// Where appends a list predicates to the TemplateUpdate builder. +func (_u *TemplateUpdateOne) Where(ps ...predicate.Template) *TemplateUpdateOne { + _u.mutation.Where(ps...) + return _u +} + +// Select allows selecting one or more fields (columns) of the returned entity. +// The default is selecting all fields defined in the entity schema. +func (_u *TemplateUpdateOne) Select(field string, fields ...string) *TemplateUpdateOne { + _u.fields = append([]string{field}, fields...) + return _u +} + +// Save executes the query and returns the updated Template entity. +func (_u *TemplateUpdateOne) Save(ctx context.Context) (*Template, error) { + _u.defaults() + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *TemplateUpdateOne) SaveX(ctx context.Context) *Template { + node, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (_u *TemplateUpdateOne) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *TemplateUpdateOne) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_u *TemplateUpdateOne) defaults() { + if _, ok := _u.mutation.Updated(); !ok { + v := template.UpdateDefaultUpdated() + _u.mutation.SetUpdated(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_u *TemplateUpdateOne) check() error { + if v, ok := _u.mutation.Name(); ok { + if err := template.NameValidator(v); err != nil { + return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "Template.name": %w`, err)} + } + } + if v, ok := _u.mutation.Slug(); ok { + if err := template.SlugValidator(v); err != nil { + return &ValidationError{Name: "slug", err: fmt.Errorf(`ent: validator failed for field "Template.slug": %w`, err)} + } + } + if v, ok := _u.mutation.Status(); ok { + if err := template.StatusValidator(v); err != nil { + return &ValidationError{Name: "status", err: fmt.Errorf(`ent: validator failed for field "Template.status": %w`, err)} + } + } + return nil +} + +func (_u *TemplateUpdateOne) sqlSave(ctx context.Context) (_node *Template, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(template.Table, template.Columns, sqlgraph.NewFieldSpec(template.FieldID, field.TypeUUID)) + id, ok := _u.mutation.ID() + if !ok { + return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "Template.id" for update`)} + } + _spec.Node.ID.Value = id + if fields := _u.fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, template.FieldID) + for _, f := range fields { + if !template.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + if f != template.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, f) + } + } + } + if ps := _u.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := _u.mutation.Name(); ok { + _spec.SetField(template.FieldName, field.TypeString, value) + } + if value, ok := _u.mutation.Slug(); ok { + _spec.SetField(template.FieldSlug, field.TypeString, value) + } + if value, ok := _u.mutation.DisplayName(); ok { + _spec.SetField(template.FieldDisplayName, field.TypeString, value) + } + if _u.mutation.DisplayNameCleared() { + _spec.ClearField(template.FieldDisplayName, field.TypeString) + } + if value, ok := _u.mutation.Description(); ok { + _spec.SetField(template.FieldDescription, field.TypeString, value) + } + if _u.mutation.DescriptionCleared() { + _spec.ClearField(template.FieldDescription, field.TypeString) + } + if value, ok := _u.mutation.Harness(); ok { + _spec.SetField(template.FieldHarness, field.TypeString, value) + } + if value, ok := _u.mutation.DefaultHarnessConfig(); ok { + _spec.SetField(template.FieldDefaultHarnessConfig, field.TypeString, value) + } + if _u.mutation.DefaultHarnessConfigCleared() { + _spec.ClearField(template.FieldDefaultHarnessConfig, field.TypeString) + } + if value, ok := _u.mutation.Image(); ok { + _spec.SetField(template.FieldImage, field.TypeString, value) + } + if _u.mutation.ImageCleared() { + _spec.ClearField(template.FieldImage, field.TypeString) + } + if value, ok := _u.mutation.Config(); ok { + _spec.SetField(template.FieldConfig, field.TypeString, value) + } + if _u.mutation.ConfigCleared() { + _spec.ClearField(template.FieldConfig, field.TypeString) + } + if value, ok := _u.mutation.ContentHash(); ok { + _spec.SetField(template.FieldContentHash, field.TypeString, value) + } + if _u.mutation.ContentHashCleared() { + _spec.ClearField(template.FieldContentHash, field.TypeString) + } + if value, ok := _u.mutation.Scope(); ok { + _spec.SetField(template.FieldScope, field.TypeString, value) + } + if value, ok := _u.mutation.ScopeID(); ok { + _spec.SetField(template.FieldScopeID, field.TypeString, value) + } + if _u.mutation.ScopeIDCleared() { + _spec.ClearField(template.FieldScopeID, field.TypeString) + } + if value, ok := _u.mutation.ProjectID(); ok { + _spec.SetField(template.FieldProjectID, field.TypeString, value) + } + if _u.mutation.ProjectIDCleared() { + _spec.ClearField(template.FieldProjectID, field.TypeString) + } + if value, ok := _u.mutation.StorageURI(); ok { + _spec.SetField(template.FieldStorageURI, field.TypeString, value) + } + if _u.mutation.StorageURICleared() { + _spec.ClearField(template.FieldStorageURI, field.TypeString) + } + if value, ok := _u.mutation.StorageBucket(); ok { + _spec.SetField(template.FieldStorageBucket, field.TypeString, value) + } + if _u.mutation.StorageBucketCleared() { + _spec.ClearField(template.FieldStorageBucket, field.TypeString) + } + if value, ok := _u.mutation.StoragePath(); ok { + _spec.SetField(template.FieldStoragePath, field.TypeString, value) + } + if _u.mutation.StoragePathCleared() { + _spec.ClearField(template.FieldStoragePath, field.TypeString) + } + if value, ok := _u.mutation.Files(); ok { + _spec.SetField(template.FieldFiles, field.TypeString, value) + } + if _u.mutation.FilesCleared() { + _spec.ClearField(template.FieldFiles, field.TypeString) + } + if value, ok := _u.mutation.BaseTemplate(); ok { + _spec.SetField(template.FieldBaseTemplate, field.TypeString, value) + } + if _u.mutation.BaseTemplateCleared() { + _spec.ClearField(template.FieldBaseTemplate, field.TypeString) + } + if value, ok := _u.mutation.Locked(); ok { + _spec.SetField(template.FieldLocked, field.TypeBool, value) + } + if value, ok := _u.mutation.Status(); ok { + _spec.SetField(template.FieldStatus, field.TypeEnum, value) + } + if value, ok := _u.mutation.OwnerID(); ok { + _spec.SetField(template.FieldOwnerID, field.TypeString, value) + } + if _u.mutation.OwnerIDCleared() { + _spec.ClearField(template.FieldOwnerID, field.TypeString) + } + if value, ok := _u.mutation.CreatedBy(); ok { + _spec.SetField(template.FieldCreatedBy, field.TypeString, value) + } + if _u.mutation.CreatedByCleared() { + _spec.ClearField(template.FieldCreatedBy, field.TypeString) + } + if value, ok := _u.mutation.UpdatedBy(); ok { + _spec.SetField(template.FieldUpdatedBy, field.TypeString, value) + } + if _u.mutation.UpdatedByCleared() { + _spec.ClearField(template.FieldUpdatedBy, field.TypeString) + } + if value, ok := _u.mutation.Visibility(); ok { + _spec.SetField(template.FieldVisibility, field.TypeString, value) + } + if value, ok := _u.mutation.Updated(); ok { + _spec.SetField(template.FieldUpdated, field.TypeTime, value) + } + _node = &Template{config: _u.config} + _spec.Assign = _node.assignValues + _spec.ScanValues = _node.scanValues + if err = sqlgraph.UpdateNode(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{template.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + _u.mutation.done = true + return _node, nil +} diff --git a/pkg/ent/tx.go b/pkg/ent/tx.go index c0b706ee4..adf201cf2 100644 --- a/pkg/ent/tx.go +++ b/pkg/ent/tx.go @@ -16,16 +16,62 @@ type Tx struct { AccessPolicy *AccessPolicyClient // Agent is the client for interacting with the Agent builders. Agent *AgentClient + // AllowListEntry is the client for interacting with the AllowListEntry builders. + AllowListEntry *AllowListEntryClient + // ApiKey is the client for interacting with the ApiKey builders. + ApiKey *ApiKeyClient + // BrokerJoinToken is the client for interacting with the BrokerJoinToken builders. + BrokerJoinToken *BrokerJoinTokenClient + // BrokerSecret is the client for interacting with the BrokerSecret builders. + BrokerSecret *BrokerSecretClient + // EnvVar is the client for interacting with the EnvVar builders. + EnvVar *EnvVarClient + // GCPServiceAccount is the client for interacting with the GCPServiceAccount builders. + GCPServiceAccount *GCPServiceAccountClient + // GithubInstallation is the client for interacting with the GithubInstallation builders. + GithubInstallation *GithubInstallationClient // Group is the client for interacting with the Group builders. Group *GroupClient // GroupMembership is the client for interacting with the GroupMembership builders. GroupMembership *GroupMembershipClient + // HarnessConfig is the client for interacting with the HarnessConfig builders. + HarnessConfig *HarnessConfigClient + // InviteCode is the client for interacting with the InviteCode builders. + InviteCode *InviteCodeClient + // MaintenanceOperation is the client for interacting with the MaintenanceOperation builders. + MaintenanceOperation *MaintenanceOperationClient + // MaintenanceOperationRun is the client for interacting with the MaintenanceOperationRun builders. + MaintenanceOperationRun *MaintenanceOperationRunClient + // Message is the client for interacting with the Message builders. + Message *MessageClient + // Notification is the client for interacting with the Notification builders. + Notification *NotificationClient + // NotificationSubscription is the client for interacting with the NotificationSubscription builders. + NotificationSubscription *NotificationSubscriptionClient // PolicyBinding is the client for interacting with the PolicyBinding builders. PolicyBinding *PolicyBindingClient // Project is the client for interacting with the Project builders. Project *ProjectClient + // ProjectContributor is the client for interacting with the ProjectContributor builders. + ProjectContributor *ProjectContributorClient + // ProjectSyncState is the client for interacting with the ProjectSyncState builders. + ProjectSyncState *ProjectSyncStateClient + // RuntimeBroker is the client for interacting with the RuntimeBroker builders. + RuntimeBroker *RuntimeBrokerClient + // Schedule is the client for interacting with the Schedule builders. + Schedule *ScheduleClient + // ScheduledEvent is the client for interacting with the ScheduledEvent builders. + ScheduledEvent *ScheduledEventClient + // Secret is the client for interacting with the Secret builders. + Secret *SecretClient + // SubscriptionTemplate is the client for interacting with the SubscriptionTemplate builders. + SubscriptionTemplate *SubscriptionTemplateClient + // Template is the client for interacting with the Template builders. + Template *TemplateClient // User is the client for interacting with the User builders. User *UserClient + // UserAccessToken is the client for interacting with the UserAccessToken builders. + UserAccessToken *UserAccessTokenClient // lazily loaded. client *Client @@ -159,11 +205,34 @@ func (tx *Tx) Client() *Client { func (tx *Tx) init() { tx.AccessPolicy = NewAccessPolicyClient(tx.config) tx.Agent = NewAgentClient(tx.config) + tx.AllowListEntry = NewAllowListEntryClient(tx.config) + tx.ApiKey = NewApiKeyClient(tx.config) + tx.BrokerJoinToken = NewBrokerJoinTokenClient(tx.config) + tx.BrokerSecret = NewBrokerSecretClient(tx.config) + tx.EnvVar = NewEnvVarClient(tx.config) + tx.GCPServiceAccount = NewGCPServiceAccountClient(tx.config) + tx.GithubInstallation = NewGithubInstallationClient(tx.config) tx.Group = NewGroupClient(tx.config) tx.GroupMembership = NewGroupMembershipClient(tx.config) + tx.HarnessConfig = NewHarnessConfigClient(tx.config) + tx.InviteCode = NewInviteCodeClient(tx.config) + tx.MaintenanceOperation = NewMaintenanceOperationClient(tx.config) + tx.MaintenanceOperationRun = NewMaintenanceOperationRunClient(tx.config) + tx.Message = NewMessageClient(tx.config) + tx.Notification = NewNotificationClient(tx.config) + tx.NotificationSubscription = NewNotificationSubscriptionClient(tx.config) tx.PolicyBinding = NewPolicyBindingClient(tx.config) tx.Project = NewProjectClient(tx.config) + tx.ProjectContributor = NewProjectContributorClient(tx.config) + tx.ProjectSyncState = NewProjectSyncStateClient(tx.config) + tx.RuntimeBroker = NewRuntimeBrokerClient(tx.config) + tx.Schedule = NewScheduleClient(tx.config) + tx.ScheduledEvent = NewScheduledEventClient(tx.config) + tx.Secret = NewSecretClient(tx.config) + tx.SubscriptionTemplate = NewSubscriptionTemplateClient(tx.config) + tx.Template = NewTemplateClient(tx.config) tx.User = NewUserClient(tx.config) + tx.UserAccessToken = NewUserAccessTokenClient(tx.config) } // txDriver wraps the given dialect.Tx with a nop dialect.Driver implementation. diff --git a/pkg/ent/user.go b/pkg/ent/user.go index f116e9fcf..de2f7510b 100644 --- a/pkg/ent/user.go +++ b/pkg/ent/user.go @@ -36,6 +36,8 @@ type User struct { Created time.Time `json:"created,omitempty"` // LastLogin holds the value of the "last_login" field. LastLogin *time.Time `json:"last_login,omitempty"` + // LastSeen holds the value of the "last_seen" field. + LastSeen *time.Time `json:"last_seen,omitempty"` // Edges holds the relations/edges for other nodes in the graph. // The values are being populated by the UserQuery when eager-loading is set. Edges UserEdges `json:"edges"` @@ -113,7 +115,7 @@ func (*User) scanValues(columns []string) ([]any, error) { values[i] = new([]byte) case user.FieldEmail, user.FieldDisplayName, user.FieldAvatarURL, user.FieldRole, user.FieldStatus: values[i] = new(sql.NullString) - case user.FieldCreated, user.FieldLastLogin: + case user.FieldCreated, user.FieldLastLogin, user.FieldLastSeen: values[i] = new(sql.NullTime) case user.FieldID: values[i] = new(uuid.UUID) @@ -189,6 +191,13 @@ func (_m *User) assignValues(columns []string, values []any) error { _m.LastLogin = new(time.Time) *_m.LastLogin = value.Time } + case user.FieldLastSeen: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field last_seen", values[i]) + } else if value.Valid { + _m.LastSeen = new(time.Time) + *_m.LastSeen = value.Time + } default: _m.selectValues.Set(columns[i], values[i]) } @@ -275,6 +284,11 @@ func (_m *User) String() string { builder.WriteString("last_login=") builder.WriteString(v.Format(time.ANSIC)) } + builder.WriteString(", ") + if v := _m.LastSeen; v != nil { + builder.WriteString("last_seen=") + builder.WriteString(v.Format(time.ANSIC)) + } builder.WriteByte(')') return builder.String() } diff --git a/pkg/ent/user/user.go b/pkg/ent/user/user.go index 72a6d4e68..89bda5b9d 100644 --- a/pkg/ent/user/user.go +++ b/pkg/ent/user/user.go @@ -32,6 +32,8 @@ const ( FieldCreated = "created" // FieldLastLogin holds the string denoting the last_login field in the database. FieldLastLogin = "last_login" + // FieldLastSeen holds the string denoting the last_seen field in the database. + FieldLastSeen = "last_seen" // EdgeCreatedAgents holds the string denoting the created_agents edge name in mutations. EdgeCreatedAgents = "created_agents" // EdgeOwnedAgents holds the string denoting the owned_agents edge name in mutations. @@ -92,6 +94,7 @@ var Columns = []string{ FieldPreferences, FieldCreated, FieldLastLogin, + FieldLastSeen, } // ValidColumn reports if the column name is valid (part of the table columns). @@ -107,8 +110,6 @@ func ValidColumn(column string) bool { var ( // EmailValidator is a validator for the "email" field. It is called by the builders before save. EmailValidator func(string) error - // DisplayNameValidator is a validator for the "display_name" field. It is called by the builders before save. - DisplayNameValidator func(string) error // DefaultCreated holds the default value on creation for the "created" field. DefaultCreated func() time.Time // DefaultID holds the default value on creation for the "id" field. @@ -211,6 +212,11 @@ func ByLastLogin(opts ...sql.OrderTermOption) OrderOption { return sql.OrderByField(FieldLastLogin, opts...).ToFunc() } +// ByLastSeen orders the results by the last_seen field. +func ByLastSeen(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldLastSeen, opts...).ToFunc() +} + // ByCreatedAgentsCount orders the results by created_agents count. func ByCreatedAgentsCount(opts ...sql.OrderTermOption) OrderOption { return func(s *sql.Selector) { diff --git a/pkg/ent/user/where.go b/pkg/ent/user/where.go index 6bf80afb2..5b6ffd31f 100644 --- a/pkg/ent/user/where.go +++ b/pkg/ent/user/where.go @@ -81,6 +81,11 @@ func LastLogin(v time.Time) predicate.User { return predicate.User(sql.FieldEQ(FieldLastLogin, v)) } +// LastSeen applies equality check predicate on the "last_seen" field. It's identical to LastSeenEQ. +func LastSeen(v time.Time) predicate.User { + return predicate.User(sql.FieldEQ(FieldLastSeen, v)) +} + // EmailEQ applies the EQ predicate on the "email" field. func EmailEQ(v string) predicate.User { return predicate.User(sql.FieldEQ(FieldEmail, v)) @@ -426,6 +431,56 @@ func LastLoginNotNil() predicate.User { return predicate.User(sql.FieldNotNull(FieldLastLogin)) } +// LastSeenEQ applies the EQ predicate on the "last_seen" field. +func LastSeenEQ(v time.Time) predicate.User { + return predicate.User(sql.FieldEQ(FieldLastSeen, v)) +} + +// LastSeenNEQ applies the NEQ predicate on the "last_seen" field. +func LastSeenNEQ(v time.Time) predicate.User { + return predicate.User(sql.FieldNEQ(FieldLastSeen, v)) +} + +// LastSeenIn applies the In predicate on the "last_seen" field. +func LastSeenIn(vs ...time.Time) predicate.User { + return predicate.User(sql.FieldIn(FieldLastSeen, vs...)) +} + +// LastSeenNotIn applies the NotIn predicate on the "last_seen" field. +func LastSeenNotIn(vs ...time.Time) predicate.User { + return predicate.User(sql.FieldNotIn(FieldLastSeen, vs...)) +} + +// LastSeenGT applies the GT predicate on the "last_seen" field. +func LastSeenGT(v time.Time) predicate.User { + return predicate.User(sql.FieldGT(FieldLastSeen, v)) +} + +// LastSeenGTE applies the GTE predicate on the "last_seen" field. +func LastSeenGTE(v time.Time) predicate.User { + return predicate.User(sql.FieldGTE(FieldLastSeen, v)) +} + +// LastSeenLT applies the LT predicate on the "last_seen" field. +func LastSeenLT(v time.Time) predicate.User { + return predicate.User(sql.FieldLT(FieldLastSeen, v)) +} + +// LastSeenLTE applies the LTE predicate on the "last_seen" field. +func LastSeenLTE(v time.Time) predicate.User { + return predicate.User(sql.FieldLTE(FieldLastSeen, v)) +} + +// LastSeenIsNil applies the IsNil predicate on the "last_seen" field. +func LastSeenIsNil() predicate.User { + return predicate.User(sql.FieldIsNull(FieldLastSeen)) +} + +// LastSeenNotNil applies the NotNil predicate on the "last_seen" field. +func LastSeenNotNil() predicate.User { + return predicate.User(sql.FieldNotNull(FieldLastSeen)) +} + // HasCreatedAgents applies the HasEdge predicate on the "created_agents" edge. func HasCreatedAgents() predicate.User { return predicate.User(func(s *sql.Selector) { diff --git a/pkg/ent/user_create.go b/pkg/ent/user_create.go index f07c0d0e1..97c852738 100644 --- a/pkg/ent/user_create.go +++ b/pkg/ent/user_create.go @@ -8,6 +8,8 @@ import ( "fmt" "time" + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" "entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/schema/field" "github.com/GoogleCloudPlatform/scion/pkg/ent/agent" @@ -24,6 +26,7 @@ type UserCreate struct { config mutation *UserMutation hooks []Hook + conflict []sql.ConflictOption } // SetEmail sets the "email" field. @@ -114,6 +117,20 @@ func (_c *UserCreate) SetNillableLastLogin(v *time.Time) *UserCreate { return _c } +// SetLastSeen sets the "last_seen" field. +func (_c *UserCreate) SetLastSeen(v time.Time) *UserCreate { + _c.mutation.SetLastSeen(v) + return _c +} + +// SetNillableLastSeen sets the "last_seen" field if the given value is not nil. +func (_c *UserCreate) SetNillableLastSeen(v *time.Time) *UserCreate { + if v != nil { + _c.SetLastSeen(*v) + } + return _c +} + // SetID sets the "id" field. func (_c *UserCreate) SetID(v uuid.UUID) *UserCreate { _c.mutation.SetID(v) @@ -269,11 +286,6 @@ func (_c *UserCreate) check() error { if _, ok := _c.mutation.DisplayName(); !ok { return &ValidationError{Name: "display_name", err: errors.New(`ent: missing required field "User.display_name"`)} } - if v, ok := _c.mutation.DisplayName(); ok { - if err := user.DisplayNameValidator(v); err != nil { - return &ValidationError{Name: "display_name", err: fmt.Errorf(`ent: validator failed for field "User.display_name": %w`, err)} - } - } if _, ok := _c.mutation.Role(); !ok { return &ValidationError{Name: "role", err: errors.New(`ent: missing required field "User.role"`)} } @@ -324,6 +336,7 @@ func (_c *UserCreate) createSpec() (*User, *sqlgraph.CreateSpec) { _node = &User{config: _c.config} _spec = sqlgraph.NewCreateSpec(user.Table, sqlgraph.NewFieldSpec(user.FieldID, field.TypeUUID)) ) + _spec.OnConflict = _c.conflict if id, ok := _c.mutation.ID(); ok { _node.ID = id _spec.ID.Value = &id @@ -360,6 +373,10 @@ func (_c *UserCreate) createSpec() (*User, *sqlgraph.CreateSpec) { _spec.SetField(user.FieldLastLogin, field.TypeTime, value) _node.LastLogin = &value } + if value, ok := _c.mutation.LastSeen(); ok { + _spec.SetField(user.FieldLastSeen, field.TypeTime, value) + _node.LastSeen = &value + } if nodes := _c.mutation.CreatedAgentsIDs(); len(nodes) > 0 { edge := &sqlgraph.EdgeSpec{ Rel: sqlgraph.O2M, @@ -443,11 +460,410 @@ func (_c *UserCreate) createSpec() (*User, *sqlgraph.CreateSpec) { return _node, _spec } +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.User.Create(). +// SetEmail(v). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.UserUpsert) { +// SetEmail(v+v). +// }). +// Exec(ctx) +func (_c *UserCreate) OnConflict(opts ...sql.ConflictOption) *UserUpsertOne { + _c.conflict = opts + return &UserUpsertOne{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.User.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *UserCreate) OnConflictColumns(columns ...string) *UserUpsertOne { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &UserUpsertOne{ + create: _c, + } +} + +type ( + // UserUpsertOne is the builder for "upsert"-ing + // one User node. + UserUpsertOne struct { + create *UserCreate + } + + // UserUpsert is the "OnConflict" setter. + UserUpsert struct { + *sql.UpdateSet + } +) + +// SetEmail sets the "email" field. +func (u *UserUpsert) SetEmail(v string) *UserUpsert { + u.Set(user.FieldEmail, v) + return u +} + +// UpdateEmail sets the "email" field to the value that was provided on create. +func (u *UserUpsert) UpdateEmail() *UserUpsert { + u.SetExcluded(user.FieldEmail) + return u +} + +// SetDisplayName sets the "display_name" field. +func (u *UserUpsert) SetDisplayName(v string) *UserUpsert { + u.Set(user.FieldDisplayName, v) + return u +} + +// UpdateDisplayName sets the "display_name" field to the value that was provided on create. +func (u *UserUpsert) UpdateDisplayName() *UserUpsert { + u.SetExcluded(user.FieldDisplayName) + return u +} + +// SetAvatarURL sets the "avatar_url" field. +func (u *UserUpsert) SetAvatarURL(v string) *UserUpsert { + u.Set(user.FieldAvatarURL, v) + return u +} + +// UpdateAvatarURL sets the "avatar_url" field to the value that was provided on create. +func (u *UserUpsert) UpdateAvatarURL() *UserUpsert { + u.SetExcluded(user.FieldAvatarURL) + return u +} + +// ClearAvatarURL clears the value of the "avatar_url" field. +func (u *UserUpsert) ClearAvatarURL() *UserUpsert { + u.SetNull(user.FieldAvatarURL) + return u +} + +// SetRole sets the "role" field. +func (u *UserUpsert) SetRole(v user.Role) *UserUpsert { + u.Set(user.FieldRole, v) + return u +} + +// UpdateRole sets the "role" field to the value that was provided on create. +func (u *UserUpsert) UpdateRole() *UserUpsert { + u.SetExcluded(user.FieldRole) + return u +} + +// SetStatus sets the "status" field. +func (u *UserUpsert) SetStatus(v user.Status) *UserUpsert { + u.Set(user.FieldStatus, v) + return u +} + +// UpdateStatus sets the "status" field to the value that was provided on create. +func (u *UserUpsert) UpdateStatus() *UserUpsert { + u.SetExcluded(user.FieldStatus) + return u +} + +// SetPreferences sets the "preferences" field. +func (u *UserUpsert) SetPreferences(v *schema.UserPreferences) *UserUpsert { + u.Set(user.FieldPreferences, v) + return u +} + +// UpdatePreferences sets the "preferences" field to the value that was provided on create. +func (u *UserUpsert) UpdatePreferences() *UserUpsert { + u.SetExcluded(user.FieldPreferences) + return u +} + +// ClearPreferences clears the value of the "preferences" field. +func (u *UserUpsert) ClearPreferences() *UserUpsert { + u.SetNull(user.FieldPreferences) + return u +} + +// SetLastLogin sets the "last_login" field. +func (u *UserUpsert) SetLastLogin(v time.Time) *UserUpsert { + u.Set(user.FieldLastLogin, v) + return u +} + +// UpdateLastLogin sets the "last_login" field to the value that was provided on create. +func (u *UserUpsert) UpdateLastLogin() *UserUpsert { + u.SetExcluded(user.FieldLastLogin) + return u +} + +// ClearLastLogin clears the value of the "last_login" field. +func (u *UserUpsert) ClearLastLogin() *UserUpsert { + u.SetNull(user.FieldLastLogin) + return u +} + +// SetLastSeen sets the "last_seen" field. +func (u *UserUpsert) SetLastSeen(v time.Time) *UserUpsert { + u.Set(user.FieldLastSeen, v) + return u +} + +// UpdateLastSeen sets the "last_seen" field to the value that was provided on create. +func (u *UserUpsert) UpdateLastSeen() *UserUpsert { + u.SetExcluded(user.FieldLastSeen) + return u +} + +// ClearLastSeen clears the value of the "last_seen" field. +func (u *UserUpsert) ClearLastSeen() *UserUpsert { + u.SetNull(user.FieldLastSeen) + return u +} + +// UpdateNewValues updates the mutable fields using the new values that were set on create except the ID field. +// Using this option is equivalent to using: +// +// client.User.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(user.FieldID) +// }), +// ). +// Exec(ctx) +func (u *UserUpsertOne) UpdateNewValues() *UserUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + if _, exists := u.create.mutation.ID(); exists { + s.SetIgnore(user.FieldID) + } + if _, exists := u.create.mutation.Created(); exists { + s.SetIgnore(user.FieldCreated) + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.User.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *UserUpsertOne) Ignore() *UserUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *UserUpsertOne) DoNothing() *UserUpsertOne { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the UserCreate.OnConflict +// documentation for more info. +func (u *UserUpsertOne) Update(set func(*UserUpsert)) *UserUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&UserUpsert{UpdateSet: update}) + })) + return u +} + +// SetEmail sets the "email" field. +func (u *UserUpsertOne) SetEmail(v string) *UserUpsertOne { + return u.Update(func(s *UserUpsert) { + s.SetEmail(v) + }) +} + +// UpdateEmail sets the "email" field to the value that was provided on create. +func (u *UserUpsertOne) UpdateEmail() *UserUpsertOne { + return u.Update(func(s *UserUpsert) { + s.UpdateEmail() + }) +} + +// SetDisplayName sets the "display_name" field. +func (u *UserUpsertOne) SetDisplayName(v string) *UserUpsertOne { + return u.Update(func(s *UserUpsert) { + s.SetDisplayName(v) + }) +} + +// UpdateDisplayName sets the "display_name" field to the value that was provided on create. +func (u *UserUpsertOne) UpdateDisplayName() *UserUpsertOne { + return u.Update(func(s *UserUpsert) { + s.UpdateDisplayName() + }) +} + +// SetAvatarURL sets the "avatar_url" field. +func (u *UserUpsertOne) SetAvatarURL(v string) *UserUpsertOne { + return u.Update(func(s *UserUpsert) { + s.SetAvatarURL(v) + }) +} + +// UpdateAvatarURL sets the "avatar_url" field to the value that was provided on create. +func (u *UserUpsertOne) UpdateAvatarURL() *UserUpsertOne { + return u.Update(func(s *UserUpsert) { + s.UpdateAvatarURL() + }) +} + +// ClearAvatarURL clears the value of the "avatar_url" field. +func (u *UserUpsertOne) ClearAvatarURL() *UserUpsertOne { + return u.Update(func(s *UserUpsert) { + s.ClearAvatarURL() + }) +} + +// SetRole sets the "role" field. +func (u *UserUpsertOne) SetRole(v user.Role) *UserUpsertOne { + return u.Update(func(s *UserUpsert) { + s.SetRole(v) + }) +} + +// UpdateRole sets the "role" field to the value that was provided on create. +func (u *UserUpsertOne) UpdateRole() *UserUpsertOne { + return u.Update(func(s *UserUpsert) { + s.UpdateRole() + }) +} + +// SetStatus sets the "status" field. +func (u *UserUpsertOne) SetStatus(v user.Status) *UserUpsertOne { + return u.Update(func(s *UserUpsert) { + s.SetStatus(v) + }) +} + +// UpdateStatus sets the "status" field to the value that was provided on create. +func (u *UserUpsertOne) UpdateStatus() *UserUpsertOne { + return u.Update(func(s *UserUpsert) { + s.UpdateStatus() + }) +} + +// SetPreferences sets the "preferences" field. +func (u *UserUpsertOne) SetPreferences(v *schema.UserPreferences) *UserUpsertOne { + return u.Update(func(s *UserUpsert) { + s.SetPreferences(v) + }) +} + +// UpdatePreferences sets the "preferences" field to the value that was provided on create. +func (u *UserUpsertOne) UpdatePreferences() *UserUpsertOne { + return u.Update(func(s *UserUpsert) { + s.UpdatePreferences() + }) +} + +// ClearPreferences clears the value of the "preferences" field. +func (u *UserUpsertOne) ClearPreferences() *UserUpsertOne { + return u.Update(func(s *UserUpsert) { + s.ClearPreferences() + }) +} + +// SetLastLogin sets the "last_login" field. +func (u *UserUpsertOne) SetLastLogin(v time.Time) *UserUpsertOne { + return u.Update(func(s *UserUpsert) { + s.SetLastLogin(v) + }) +} + +// UpdateLastLogin sets the "last_login" field to the value that was provided on create. +func (u *UserUpsertOne) UpdateLastLogin() *UserUpsertOne { + return u.Update(func(s *UserUpsert) { + s.UpdateLastLogin() + }) +} + +// ClearLastLogin clears the value of the "last_login" field. +func (u *UserUpsertOne) ClearLastLogin() *UserUpsertOne { + return u.Update(func(s *UserUpsert) { + s.ClearLastLogin() + }) +} + +// SetLastSeen sets the "last_seen" field. +func (u *UserUpsertOne) SetLastSeen(v time.Time) *UserUpsertOne { + return u.Update(func(s *UserUpsert) { + s.SetLastSeen(v) + }) +} + +// UpdateLastSeen sets the "last_seen" field to the value that was provided on create. +func (u *UserUpsertOne) UpdateLastSeen() *UserUpsertOne { + return u.Update(func(s *UserUpsert) { + s.UpdateLastSeen() + }) +} + +// ClearLastSeen clears the value of the "last_seen" field. +func (u *UserUpsertOne) ClearLastSeen() *UserUpsertOne { + return u.Update(func(s *UserUpsert) { + s.ClearLastSeen() + }) +} + +// Exec executes the query. +func (u *UserUpsertOne) Exec(ctx context.Context) error { + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for UserCreate.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *UserUpsertOne) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} + +// Exec executes the UPSERT query and returns the inserted/updated ID. +func (u *UserUpsertOne) ID(ctx context.Context) (id uuid.UUID, err error) { + if u.create.driver.Dialect() == dialect.MySQL { + // In case of "ON CONFLICT", there is no way to get back non-numeric ID + // fields from the database since MySQL does not support the RETURNING clause. + return id, errors.New("ent: UserUpsertOne.ID is not supported by MySQL driver. Use UserUpsertOne.Exec instead") + } + node, err := u.create.Save(ctx) + if err != nil { + return id, err + } + return node.ID, nil +} + +// IDX is like ID, but panics if an error occurs. +func (u *UserUpsertOne) IDX(ctx context.Context) uuid.UUID { + id, err := u.ID(ctx) + if err != nil { + panic(err) + } + return id +} + // UserCreateBulk is the builder for creating many User entities in bulk. type UserCreateBulk struct { config err error builders []*UserCreate + conflict []sql.ConflictOption } // Save creates the User entities in the database. @@ -477,6 +893,7 @@ func (_c *UserCreateBulk) Save(ctx context.Context) ([]*User, error) { _, err = mutators[i+1].Mutate(root, _c.builders[i+1].mutation) } else { spec := &sqlgraph.BatchCreateSpec{Nodes: specs} + spec.OnConflict = _c.conflict // Invoke the actual operation on the latest mutation in the chain. if err = sqlgraph.BatchCreate(ctx, _c.driver, spec); err != nil { if sqlgraph.IsConstraintError(err) { @@ -526,3 +943,260 @@ func (_c *UserCreateBulk) ExecX(ctx context.Context) { panic(err) } } + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.User.CreateBulk(builders...). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.UserUpsert) { +// SetEmail(v+v). +// }). +// Exec(ctx) +func (_c *UserCreateBulk) OnConflict(opts ...sql.ConflictOption) *UserUpsertBulk { + _c.conflict = opts + return &UserUpsertBulk{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.User.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *UserCreateBulk) OnConflictColumns(columns ...string) *UserUpsertBulk { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &UserUpsertBulk{ + create: _c, + } +} + +// UserUpsertBulk is the builder for "upsert"-ing +// a bulk of User nodes. +type UserUpsertBulk struct { + create *UserCreateBulk +} + +// UpdateNewValues updates the mutable fields using the new values that +// were set on create. Using this option is equivalent to using: +// +// client.User.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(user.FieldID) +// }), +// ). +// Exec(ctx) +func (u *UserUpsertBulk) UpdateNewValues() *UserUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + for _, b := range u.create.builders { + if _, exists := b.mutation.ID(); exists { + s.SetIgnore(user.FieldID) + } + if _, exists := b.mutation.Created(); exists { + s.SetIgnore(user.FieldCreated) + } + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.User.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *UserUpsertBulk) Ignore() *UserUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *UserUpsertBulk) DoNothing() *UserUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the UserCreateBulk.OnConflict +// documentation for more info. +func (u *UserUpsertBulk) Update(set func(*UserUpsert)) *UserUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&UserUpsert{UpdateSet: update}) + })) + return u +} + +// SetEmail sets the "email" field. +func (u *UserUpsertBulk) SetEmail(v string) *UserUpsertBulk { + return u.Update(func(s *UserUpsert) { + s.SetEmail(v) + }) +} + +// UpdateEmail sets the "email" field to the value that was provided on create. +func (u *UserUpsertBulk) UpdateEmail() *UserUpsertBulk { + return u.Update(func(s *UserUpsert) { + s.UpdateEmail() + }) +} + +// SetDisplayName sets the "display_name" field. +func (u *UserUpsertBulk) SetDisplayName(v string) *UserUpsertBulk { + return u.Update(func(s *UserUpsert) { + s.SetDisplayName(v) + }) +} + +// UpdateDisplayName sets the "display_name" field to the value that was provided on create. +func (u *UserUpsertBulk) UpdateDisplayName() *UserUpsertBulk { + return u.Update(func(s *UserUpsert) { + s.UpdateDisplayName() + }) +} + +// SetAvatarURL sets the "avatar_url" field. +func (u *UserUpsertBulk) SetAvatarURL(v string) *UserUpsertBulk { + return u.Update(func(s *UserUpsert) { + s.SetAvatarURL(v) + }) +} + +// UpdateAvatarURL sets the "avatar_url" field to the value that was provided on create. +func (u *UserUpsertBulk) UpdateAvatarURL() *UserUpsertBulk { + return u.Update(func(s *UserUpsert) { + s.UpdateAvatarURL() + }) +} + +// ClearAvatarURL clears the value of the "avatar_url" field. +func (u *UserUpsertBulk) ClearAvatarURL() *UserUpsertBulk { + return u.Update(func(s *UserUpsert) { + s.ClearAvatarURL() + }) +} + +// SetRole sets the "role" field. +func (u *UserUpsertBulk) SetRole(v user.Role) *UserUpsertBulk { + return u.Update(func(s *UserUpsert) { + s.SetRole(v) + }) +} + +// UpdateRole sets the "role" field to the value that was provided on create. +func (u *UserUpsertBulk) UpdateRole() *UserUpsertBulk { + return u.Update(func(s *UserUpsert) { + s.UpdateRole() + }) +} + +// SetStatus sets the "status" field. +func (u *UserUpsertBulk) SetStatus(v user.Status) *UserUpsertBulk { + return u.Update(func(s *UserUpsert) { + s.SetStatus(v) + }) +} + +// UpdateStatus sets the "status" field to the value that was provided on create. +func (u *UserUpsertBulk) UpdateStatus() *UserUpsertBulk { + return u.Update(func(s *UserUpsert) { + s.UpdateStatus() + }) +} + +// SetPreferences sets the "preferences" field. +func (u *UserUpsertBulk) SetPreferences(v *schema.UserPreferences) *UserUpsertBulk { + return u.Update(func(s *UserUpsert) { + s.SetPreferences(v) + }) +} + +// UpdatePreferences sets the "preferences" field to the value that was provided on create. +func (u *UserUpsertBulk) UpdatePreferences() *UserUpsertBulk { + return u.Update(func(s *UserUpsert) { + s.UpdatePreferences() + }) +} + +// ClearPreferences clears the value of the "preferences" field. +func (u *UserUpsertBulk) ClearPreferences() *UserUpsertBulk { + return u.Update(func(s *UserUpsert) { + s.ClearPreferences() + }) +} + +// SetLastLogin sets the "last_login" field. +func (u *UserUpsertBulk) SetLastLogin(v time.Time) *UserUpsertBulk { + return u.Update(func(s *UserUpsert) { + s.SetLastLogin(v) + }) +} + +// UpdateLastLogin sets the "last_login" field to the value that was provided on create. +func (u *UserUpsertBulk) UpdateLastLogin() *UserUpsertBulk { + return u.Update(func(s *UserUpsert) { + s.UpdateLastLogin() + }) +} + +// ClearLastLogin clears the value of the "last_login" field. +func (u *UserUpsertBulk) ClearLastLogin() *UserUpsertBulk { + return u.Update(func(s *UserUpsert) { + s.ClearLastLogin() + }) +} + +// SetLastSeen sets the "last_seen" field. +func (u *UserUpsertBulk) SetLastSeen(v time.Time) *UserUpsertBulk { + return u.Update(func(s *UserUpsert) { + s.SetLastSeen(v) + }) +} + +// UpdateLastSeen sets the "last_seen" field to the value that was provided on create. +func (u *UserUpsertBulk) UpdateLastSeen() *UserUpsertBulk { + return u.Update(func(s *UserUpsert) { + s.UpdateLastSeen() + }) +} + +// ClearLastSeen clears the value of the "last_seen" field. +func (u *UserUpsertBulk) ClearLastSeen() *UserUpsertBulk { + return u.Update(func(s *UserUpsert) { + s.ClearLastSeen() + }) +} + +// Exec executes the query. +func (u *UserUpsertBulk) Exec(ctx context.Context) error { + if u.create.err != nil { + return u.create.err + } + for i, b := range u.create.builders { + if len(b.conflict) != 0 { + return fmt.Errorf("ent: OnConflict was set for builder %d. Set it on the UserCreateBulk instead", i) + } + } + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for UserCreateBulk.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *UserUpsertBulk) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/pkg/ent/user_query.go b/pkg/ent/user_query.go index 63c08582d..728e8f97f 100644 --- a/pkg/ent/user_query.go +++ b/pkg/ent/user_query.go @@ -9,6 +9,7 @@ import ( "math" "entgo.io/ent" + "entgo.io/ent/dialect" "entgo.io/ent/dialect/sql" "entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/schema/field" @@ -33,6 +34,7 @@ type UserQuery struct { withOwnedGroups *GroupQuery withMemberships *GroupMembershipQuery withPolicyBindings *PolicyBindingQuery + modifiers []func(*sql.Selector) // intermediate query (i.e. traversal path). sql *sql.Selector path func(context.Context) (*sql.Selector, error) @@ -532,6 +534,9 @@ func (_q *UserQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*User, e node.Edges.loadedTypes = loadedTypes return node.assignValues(columns, values) } + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } for i := range hooks { hooks[i](ctx, _spec) } @@ -747,6 +752,9 @@ func (_q *UserQuery) loadPolicyBindings(ctx context.Context, query *PolicyBindin func (_q *UserQuery) sqlCount(ctx context.Context) (int, error) { _spec := _q.querySpec() + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } _spec.Node.Columns = _q.ctx.Fields if len(_q.ctx.Fields) > 0 { _spec.Unique = _q.ctx.Unique != nil && *_q.ctx.Unique @@ -809,6 +817,9 @@ func (_q *UserQuery) sqlQuery(ctx context.Context) *sql.Selector { if _q.ctx.Unique != nil && *_q.ctx.Unique { selector.Distinct() } + for _, m := range _q.modifiers { + m(selector) + } for _, p := range _q.predicates { p(selector) } @@ -826,6 +837,32 @@ func (_q *UserQuery) sqlQuery(ctx context.Context) *sql.Selector { return selector } +// ForUpdate locks the selected rows against concurrent updates, and prevent them from being +// updated, deleted or "selected ... for update" by other sessions, until the transaction is +// either committed or rolled-back. +func (_q *UserQuery) ForUpdate(opts ...sql.LockOption) *UserQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForUpdate(opts...) + }) + return _q +} + +// ForShare behaves similarly to ForUpdate, except that it acquires a shared mode lock +// on any rows that are read. Other sessions can read the rows, but cannot modify them +// until your transaction commits. +func (_q *UserQuery) ForShare(opts ...sql.LockOption) *UserQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForShare(opts...) + }) + return _q +} + // UserGroupBy is the group-by builder for User entities. type UserGroupBy struct { selector diff --git a/pkg/ent/user_update.go b/pkg/ent/user_update.go index e6b121d90..0b8fdd14a 100644 --- a/pkg/ent/user_update.go +++ b/pkg/ent/user_update.go @@ -142,6 +142,26 @@ func (_u *UserUpdate) ClearLastLogin() *UserUpdate { return _u } +// SetLastSeen sets the "last_seen" field. +func (_u *UserUpdate) SetLastSeen(v time.Time) *UserUpdate { + _u.mutation.SetLastSeen(v) + return _u +} + +// SetNillableLastSeen sets the "last_seen" field if the given value is not nil. +func (_u *UserUpdate) SetNillableLastSeen(v *time.Time) *UserUpdate { + if v != nil { + _u.SetLastSeen(*v) + } + return _u +} + +// ClearLastSeen clears the value of the "last_seen" field. +func (_u *UserUpdate) ClearLastSeen() *UserUpdate { + _u.mutation.ClearLastSeen() + return _u +} + // AddCreatedAgentIDs adds the "created_agents" edge to the Agent entity by IDs. func (_u *UserUpdate) AddCreatedAgentIDs(ids ...uuid.UUID) *UserUpdate { _u.mutation.AddCreatedAgentIDs(ids...) @@ -361,11 +381,6 @@ func (_u *UserUpdate) check() error { return &ValidationError{Name: "email", err: fmt.Errorf(`ent: validator failed for field "User.email": %w`, err)} } } - if v, ok := _u.mutation.DisplayName(); ok { - if err := user.DisplayNameValidator(v); err != nil { - return &ValidationError{Name: "display_name", err: fmt.Errorf(`ent: validator failed for field "User.display_name": %w`, err)} - } - } if v, ok := _u.mutation.Role(); ok { if err := user.RoleValidator(v); err != nil { return &ValidationError{Name: "role", err: fmt.Errorf(`ent: validator failed for field "User.role": %w`, err)} @@ -421,6 +436,12 @@ func (_u *UserUpdate) sqlSave(ctx context.Context) (_node int, err error) { if _u.mutation.LastLoginCleared() { _spec.ClearField(user.FieldLastLogin, field.TypeTime) } + if value, ok := _u.mutation.LastSeen(); ok { + _spec.SetField(user.FieldLastSeen, field.TypeTime, value) + } + if _u.mutation.LastSeenCleared() { + _spec.ClearField(user.FieldLastSeen, field.TypeTime) + } if _u.mutation.CreatedAgentsCleared() { edge := &sqlgraph.EdgeSpec{ Rel: sqlgraph.O2M, @@ -774,6 +795,26 @@ func (_u *UserUpdateOne) ClearLastLogin() *UserUpdateOne { return _u } +// SetLastSeen sets the "last_seen" field. +func (_u *UserUpdateOne) SetLastSeen(v time.Time) *UserUpdateOne { + _u.mutation.SetLastSeen(v) + return _u +} + +// SetNillableLastSeen sets the "last_seen" field if the given value is not nil. +func (_u *UserUpdateOne) SetNillableLastSeen(v *time.Time) *UserUpdateOne { + if v != nil { + _u.SetLastSeen(*v) + } + return _u +} + +// ClearLastSeen clears the value of the "last_seen" field. +func (_u *UserUpdateOne) ClearLastSeen() *UserUpdateOne { + _u.mutation.ClearLastSeen() + return _u +} + // AddCreatedAgentIDs adds the "created_agents" edge to the Agent entity by IDs. func (_u *UserUpdateOne) AddCreatedAgentIDs(ids ...uuid.UUID) *UserUpdateOne { _u.mutation.AddCreatedAgentIDs(ids...) @@ -1006,11 +1047,6 @@ func (_u *UserUpdateOne) check() error { return &ValidationError{Name: "email", err: fmt.Errorf(`ent: validator failed for field "User.email": %w`, err)} } } - if v, ok := _u.mutation.DisplayName(); ok { - if err := user.DisplayNameValidator(v); err != nil { - return &ValidationError{Name: "display_name", err: fmt.Errorf(`ent: validator failed for field "User.display_name": %w`, err)} - } - } if v, ok := _u.mutation.Role(); ok { if err := user.RoleValidator(v); err != nil { return &ValidationError{Name: "role", err: fmt.Errorf(`ent: validator failed for field "User.role": %w`, err)} @@ -1083,6 +1119,12 @@ func (_u *UserUpdateOne) sqlSave(ctx context.Context) (_node *User, err error) { if _u.mutation.LastLoginCleared() { _spec.ClearField(user.FieldLastLogin, field.TypeTime) } + if value, ok := _u.mutation.LastSeen(); ok { + _spec.SetField(user.FieldLastSeen, field.TypeTime, value) + } + if _u.mutation.LastSeenCleared() { + _spec.ClearField(user.FieldLastSeen, field.TypeTime) + } if _u.mutation.CreatedAgentsCleared() { edge := &sqlgraph.EdgeSpec{ Rel: sqlgraph.O2M, diff --git a/pkg/ent/useraccesstoken.go b/pkg/ent/useraccesstoken.go new file mode 100644 index 000000000..40e13b7d7 --- /dev/null +++ b/pkg/ent/useraccesstoken.go @@ -0,0 +1,213 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "fmt" + "strings" + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "github.com/GoogleCloudPlatform/scion/pkg/ent/useraccesstoken" + "github.com/google/uuid" +) + +// UserAccessToken is the model entity for the UserAccessToken schema. +type UserAccessToken struct { + config `json:"-"` + // ID of the ent. + ID uuid.UUID `json:"id,omitempty"` + // UserID holds the value of the "user_id" field. + UserID uuid.UUID `json:"user_id,omitempty"` + // Name holds the value of the "name" field. + Name string `json:"name,omitempty"` + // Prefix holds the value of the "prefix" field. + Prefix string `json:"prefix,omitempty"` + // KeyHash holds the value of the "key_hash" field. + KeyHash string `json:"-"` + // ProjectID holds the value of the "project_id" field. + ProjectID uuid.UUID `json:"project_id,omitempty"` + // Scopes holds the value of the "scopes" field. + Scopes string `json:"scopes,omitempty"` + // Revoked holds the value of the "revoked" field. + Revoked bool `json:"revoked,omitempty"` + // ExpiresAt holds the value of the "expires_at" field. + ExpiresAt *time.Time `json:"expires_at,omitempty"` + // LastUsed holds the value of the "last_used" field. + LastUsed *time.Time `json:"last_used,omitempty"` + // Created holds the value of the "created" field. + Created time.Time `json:"created,omitempty"` + selectValues sql.SelectValues +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*UserAccessToken) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case useraccesstoken.FieldRevoked: + values[i] = new(sql.NullBool) + case useraccesstoken.FieldName, useraccesstoken.FieldPrefix, useraccesstoken.FieldKeyHash, useraccesstoken.FieldScopes: + values[i] = new(sql.NullString) + case useraccesstoken.FieldExpiresAt, useraccesstoken.FieldLastUsed, useraccesstoken.FieldCreated: + values[i] = new(sql.NullTime) + case useraccesstoken.FieldID, useraccesstoken.FieldUserID, useraccesstoken.FieldProjectID: + values[i] = new(uuid.UUID) + default: + values[i] = new(sql.UnknownType) + } + } + return values, nil +} + +// assignValues assigns the values that were returned from sql.Rows (after scanning) +// to the UserAccessToken fields. +func (_m *UserAccessToken) assignValues(columns []string, values []any) error { + if m, n := len(values), len(columns); m < n { + return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) + } + for i := range columns { + switch columns[i] { + case useraccesstoken.FieldID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field id", values[i]) + } else if value != nil { + _m.ID = *value + } + case useraccesstoken.FieldUserID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field user_id", values[i]) + } else if value != nil { + _m.UserID = *value + } + case useraccesstoken.FieldName: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field name", values[i]) + } else if value.Valid { + _m.Name = value.String + } + case useraccesstoken.FieldPrefix: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field prefix", values[i]) + } else if value.Valid { + _m.Prefix = value.String + } + case useraccesstoken.FieldKeyHash: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field key_hash", values[i]) + } else if value.Valid { + _m.KeyHash = value.String + } + case useraccesstoken.FieldProjectID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field project_id", values[i]) + } else if value != nil { + _m.ProjectID = *value + } + case useraccesstoken.FieldScopes: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field scopes", values[i]) + } else if value.Valid { + _m.Scopes = value.String + } + case useraccesstoken.FieldRevoked: + if value, ok := values[i].(*sql.NullBool); !ok { + return fmt.Errorf("unexpected type %T for field revoked", values[i]) + } else if value.Valid { + _m.Revoked = value.Bool + } + case useraccesstoken.FieldExpiresAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field expires_at", values[i]) + } else if value.Valid { + _m.ExpiresAt = new(time.Time) + *_m.ExpiresAt = value.Time + } + case useraccesstoken.FieldLastUsed: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field last_used", values[i]) + } else if value.Valid { + _m.LastUsed = new(time.Time) + *_m.LastUsed = value.Time + } + case useraccesstoken.FieldCreated: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field created", values[i]) + } else if value.Valid { + _m.Created = value.Time + } + default: + _m.selectValues.Set(columns[i], values[i]) + } + } + return nil +} + +// Value returns the ent.Value that was dynamically selected and assigned to the UserAccessToken. +// This includes values selected through modifiers, order, etc. +func (_m *UserAccessToken) Value(name string) (ent.Value, error) { + return _m.selectValues.Get(name) +} + +// Update returns a builder for updating this UserAccessToken. +// Note that you need to call UserAccessToken.Unwrap() before calling this method if this UserAccessToken +// was returned from a transaction, and the transaction was committed or rolled back. +func (_m *UserAccessToken) Update() *UserAccessTokenUpdateOne { + return NewUserAccessTokenClient(_m.config).UpdateOne(_m) +} + +// Unwrap unwraps the UserAccessToken entity that was returned from a transaction after it was closed, +// so that all future queries will be executed through the driver which created the transaction. +func (_m *UserAccessToken) Unwrap() *UserAccessToken { + _tx, ok := _m.config.driver.(*txDriver) + if !ok { + panic("ent: UserAccessToken is not a transactional entity") + } + _m.config.driver = _tx.drv + return _m +} + +// String implements the fmt.Stringer. +func (_m *UserAccessToken) String() string { + var builder strings.Builder + builder.WriteString("UserAccessToken(") + builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID)) + builder.WriteString("user_id=") + builder.WriteString(fmt.Sprintf("%v", _m.UserID)) + builder.WriteString(", ") + builder.WriteString("name=") + builder.WriteString(_m.Name) + builder.WriteString(", ") + builder.WriteString("prefix=") + builder.WriteString(_m.Prefix) + builder.WriteString(", ") + builder.WriteString("key_hash=") + builder.WriteString(", ") + builder.WriteString("project_id=") + builder.WriteString(fmt.Sprintf("%v", _m.ProjectID)) + builder.WriteString(", ") + builder.WriteString("scopes=") + builder.WriteString(_m.Scopes) + builder.WriteString(", ") + builder.WriteString("revoked=") + builder.WriteString(fmt.Sprintf("%v", _m.Revoked)) + builder.WriteString(", ") + if v := _m.ExpiresAt; v != nil { + builder.WriteString("expires_at=") + builder.WriteString(v.Format(time.ANSIC)) + } + builder.WriteString(", ") + if v := _m.LastUsed; v != nil { + builder.WriteString("last_used=") + builder.WriteString(v.Format(time.ANSIC)) + } + builder.WriteString(", ") + builder.WriteString("created=") + builder.WriteString(_m.Created.Format(time.ANSIC)) + builder.WriteByte(')') + return builder.String() +} + +// UserAccessTokens is a parsable slice of UserAccessToken. +type UserAccessTokens []*UserAccessToken diff --git a/pkg/ent/useraccesstoken/useraccesstoken.go b/pkg/ent/useraccesstoken/useraccesstoken.go new file mode 100644 index 000000000..5e6496395 --- /dev/null +++ b/pkg/ent/useraccesstoken/useraccesstoken.go @@ -0,0 +1,139 @@ +// Code generated by ent, DO NOT EDIT. + +package useraccesstoken + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "github.com/google/uuid" +) + +const ( + // Label holds the string label denoting the useraccesstoken type in the database. + Label = "user_access_token" + // FieldID holds the string denoting the id field in the database. + FieldID = "id" + // FieldUserID holds the string denoting the user_id field in the database. + FieldUserID = "user_id" + // FieldName holds the string denoting the name field in the database. + FieldName = "name" + // FieldPrefix holds the string denoting the prefix field in the database. + FieldPrefix = "prefix" + // FieldKeyHash holds the string denoting the key_hash field in the database. + FieldKeyHash = "key_hash" + // FieldProjectID holds the string denoting the project_id field in the database. + FieldProjectID = "project_id" + // FieldScopes holds the string denoting the scopes field in the database. + FieldScopes = "scopes" + // FieldRevoked holds the string denoting the revoked field in the database. + FieldRevoked = "revoked" + // FieldExpiresAt holds the string denoting the expires_at field in the database. + FieldExpiresAt = "expires_at" + // FieldLastUsed holds the string denoting the last_used field in the database. + FieldLastUsed = "last_used" + // FieldCreated holds the string denoting the created field in the database. + FieldCreated = "created" + // Table holds the table name of the useraccesstoken in the database. + Table = "user_access_tokens" +) + +// Columns holds all SQL columns for useraccesstoken fields. +var Columns = []string{ + FieldID, + FieldUserID, + FieldName, + FieldPrefix, + FieldKeyHash, + FieldProjectID, + FieldScopes, + FieldRevoked, + FieldExpiresAt, + FieldLastUsed, + FieldCreated, +} + +// ValidColumn reports if the column name is valid (part of the table columns). +func ValidColumn(column string) bool { + for i := range Columns { + if column == Columns[i] { + return true + } + } + return false +} + +var ( + // NameValidator is a validator for the "name" field. It is called by the builders before save. + NameValidator func(string) error + // PrefixValidator is a validator for the "prefix" field. It is called by the builders before save. + PrefixValidator func(string) error + // KeyHashValidator is a validator for the "key_hash" field. It is called by the builders before save. + KeyHashValidator func(string) error + // ScopesValidator is a validator for the "scopes" field. It is called by the builders before save. + ScopesValidator func(string) error + // DefaultRevoked holds the default value on creation for the "revoked" field. + DefaultRevoked bool + // DefaultCreated holds the default value on creation for the "created" field. + DefaultCreated func() time.Time + // DefaultID holds the default value on creation for the "id" field. + DefaultID func() uuid.UUID +) + +// OrderOption defines the ordering options for the UserAccessToken queries. +type OrderOption func(*sql.Selector) + +// ByID orders the results by the id field. +func ByID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldID, opts...).ToFunc() +} + +// ByUserID orders the results by the user_id field. +func ByUserID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldUserID, opts...).ToFunc() +} + +// ByName orders the results by the name field. +func ByName(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldName, opts...).ToFunc() +} + +// ByPrefix orders the results by the prefix field. +func ByPrefix(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldPrefix, opts...).ToFunc() +} + +// ByKeyHash orders the results by the key_hash field. +func ByKeyHash(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldKeyHash, opts...).ToFunc() +} + +// ByProjectID orders the results by the project_id field. +func ByProjectID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldProjectID, opts...).ToFunc() +} + +// ByScopes orders the results by the scopes field. +func ByScopes(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldScopes, opts...).ToFunc() +} + +// ByRevoked orders the results by the revoked field. +func ByRevoked(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldRevoked, opts...).ToFunc() +} + +// ByExpiresAt orders the results by the expires_at field. +func ByExpiresAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldExpiresAt, opts...).ToFunc() +} + +// ByLastUsed orders the results by the last_used field. +func ByLastUsed(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldLastUsed, opts...).ToFunc() +} + +// ByCreated orders the results by the created field. +func ByCreated(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCreated, opts...).ToFunc() +} diff --git a/pkg/ent/useraccesstoken/where.go b/pkg/ent/useraccesstoken/where.go new file mode 100644 index 000000000..8481dd3ec --- /dev/null +++ b/pkg/ent/useraccesstoken/where.go @@ -0,0 +1,611 @@ +// Code generated by ent, DO NOT EDIT. + +package useraccesstoken + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/google/uuid" +) + +// ID filters vertices based on their ID field. +func ID(id uuid.UUID) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldEQ(FieldID, id)) +} + +// IDEQ applies the EQ predicate on the ID field. +func IDEQ(id uuid.UUID) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldEQ(FieldID, id)) +} + +// IDNEQ applies the NEQ predicate on the ID field. +func IDNEQ(id uuid.UUID) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldNEQ(FieldID, id)) +} + +// IDIn applies the In predicate on the ID field. +func IDIn(ids ...uuid.UUID) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldIn(FieldID, ids...)) +} + +// IDNotIn applies the NotIn predicate on the ID field. +func IDNotIn(ids ...uuid.UUID) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldNotIn(FieldID, ids...)) +} + +// IDGT applies the GT predicate on the ID field. +func IDGT(id uuid.UUID) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldGT(FieldID, id)) +} + +// IDGTE applies the GTE predicate on the ID field. +func IDGTE(id uuid.UUID) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldGTE(FieldID, id)) +} + +// IDLT applies the LT predicate on the ID field. +func IDLT(id uuid.UUID) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldLT(FieldID, id)) +} + +// IDLTE applies the LTE predicate on the ID field. +func IDLTE(id uuid.UUID) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldLTE(FieldID, id)) +} + +// UserID applies equality check predicate on the "user_id" field. It's identical to UserIDEQ. +func UserID(v uuid.UUID) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldEQ(FieldUserID, v)) +} + +// Name applies equality check predicate on the "name" field. It's identical to NameEQ. +func Name(v string) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldEQ(FieldName, v)) +} + +// Prefix applies equality check predicate on the "prefix" field. It's identical to PrefixEQ. +func Prefix(v string) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldEQ(FieldPrefix, v)) +} + +// KeyHash applies equality check predicate on the "key_hash" field. It's identical to KeyHashEQ. +func KeyHash(v string) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldEQ(FieldKeyHash, v)) +} + +// ProjectID applies equality check predicate on the "project_id" field. It's identical to ProjectIDEQ. +func ProjectID(v uuid.UUID) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldEQ(FieldProjectID, v)) +} + +// Scopes applies equality check predicate on the "scopes" field. It's identical to ScopesEQ. +func Scopes(v string) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldEQ(FieldScopes, v)) +} + +// Revoked applies equality check predicate on the "revoked" field. It's identical to RevokedEQ. +func Revoked(v bool) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldEQ(FieldRevoked, v)) +} + +// ExpiresAt applies equality check predicate on the "expires_at" field. It's identical to ExpiresAtEQ. +func ExpiresAt(v time.Time) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldEQ(FieldExpiresAt, v)) +} + +// LastUsed applies equality check predicate on the "last_used" field. It's identical to LastUsedEQ. +func LastUsed(v time.Time) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldEQ(FieldLastUsed, v)) +} + +// Created applies equality check predicate on the "created" field. It's identical to CreatedEQ. +func Created(v time.Time) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldEQ(FieldCreated, v)) +} + +// UserIDEQ applies the EQ predicate on the "user_id" field. +func UserIDEQ(v uuid.UUID) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldEQ(FieldUserID, v)) +} + +// UserIDNEQ applies the NEQ predicate on the "user_id" field. +func UserIDNEQ(v uuid.UUID) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldNEQ(FieldUserID, v)) +} + +// UserIDIn applies the In predicate on the "user_id" field. +func UserIDIn(vs ...uuid.UUID) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldIn(FieldUserID, vs...)) +} + +// UserIDNotIn applies the NotIn predicate on the "user_id" field. +func UserIDNotIn(vs ...uuid.UUID) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldNotIn(FieldUserID, vs...)) +} + +// UserIDGT applies the GT predicate on the "user_id" field. +func UserIDGT(v uuid.UUID) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldGT(FieldUserID, v)) +} + +// UserIDGTE applies the GTE predicate on the "user_id" field. +func UserIDGTE(v uuid.UUID) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldGTE(FieldUserID, v)) +} + +// UserIDLT applies the LT predicate on the "user_id" field. +func UserIDLT(v uuid.UUID) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldLT(FieldUserID, v)) +} + +// UserIDLTE applies the LTE predicate on the "user_id" field. +func UserIDLTE(v uuid.UUID) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldLTE(FieldUserID, v)) +} + +// NameEQ applies the EQ predicate on the "name" field. +func NameEQ(v string) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldEQ(FieldName, v)) +} + +// NameNEQ applies the NEQ predicate on the "name" field. +func NameNEQ(v string) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldNEQ(FieldName, v)) +} + +// NameIn applies the In predicate on the "name" field. +func NameIn(vs ...string) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldIn(FieldName, vs...)) +} + +// NameNotIn applies the NotIn predicate on the "name" field. +func NameNotIn(vs ...string) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldNotIn(FieldName, vs...)) +} + +// NameGT applies the GT predicate on the "name" field. +func NameGT(v string) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldGT(FieldName, v)) +} + +// NameGTE applies the GTE predicate on the "name" field. +func NameGTE(v string) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldGTE(FieldName, v)) +} + +// NameLT applies the LT predicate on the "name" field. +func NameLT(v string) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldLT(FieldName, v)) +} + +// NameLTE applies the LTE predicate on the "name" field. +func NameLTE(v string) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldLTE(FieldName, v)) +} + +// NameContains applies the Contains predicate on the "name" field. +func NameContains(v string) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldContains(FieldName, v)) +} + +// NameHasPrefix applies the HasPrefix predicate on the "name" field. +func NameHasPrefix(v string) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldHasPrefix(FieldName, v)) +} + +// NameHasSuffix applies the HasSuffix predicate on the "name" field. +func NameHasSuffix(v string) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldHasSuffix(FieldName, v)) +} + +// NameEqualFold applies the EqualFold predicate on the "name" field. +func NameEqualFold(v string) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldEqualFold(FieldName, v)) +} + +// NameContainsFold applies the ContainsFold predicate on the "name" field. +func NameContainsFold(v string) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldContainsFold(FieldName, v)) +} + +// PrefixEQ applies the EQ predicate on the "prefix" field. +func PrefixEQ(v string) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldEQ(FieldPrefix, v)) +} + +// PrefixNEQ applies the NEQ predicate on the "prefix" field. +func PrefixNEQ(v string) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldNEQ(FieldPrefix, v)) +} + +// PrefixIn applies the In predicate on the "prefix" field. +func PrefixIn(vs ...string) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldIn(FieldPrefix, vs...)) +} + +// PrefixNotIn applies the NotIn predicate on the "prefix" field. +func PrefixNotIn(vs ...string) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldNotIn(FieldPrefix, vs...)) +} + +// PrefixGT applies the GT predicate on the "prefix" field. +func PrefixGT(v string) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldGT(FieldPrefix, v)) +} + +// PrefixGTE applies the GTE predicate on the "prefix" field. +func PrefixGTE(v string) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldGTE(FieldPrefix, v)) +} + +// PrefixLT applies the LT predicate on the "prefix" field. +func PrefixLT(v string) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldLT(FieldPrefix, v)) +} + +// PrefixLTE applies the LTE predicate on the "prefix" field. +func PrefixLTE(v string) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldLTE(FieldPrefix, v)) +} + +// PrefixContains applies the Contains predicate on the "prefix" field. +func PrefixContains(v string) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldContains(FieldPrefix, v)) +} + +// PrefixHasPrefix applies the HasPrefix predicate on the "prefix" field. +func PrefixHasPrefix(v string) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldHasPrefix(FieldPrefix, v)) +} + +// PrefixHasSuffix applies the HasSuffix predicate on the "prefix" field. +func PrefixHasSuffix(v string) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldHasSuffix(FieldPrefix, v)) +} + +// PrefixEqualFold applies the EqualFold predicate on the "prefix" field. +func PrefixEqualFold(v string) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldEqualFold(FieldPrefix, v)) +} + +// PrefixContainsFold applies the ContainsFold predicate on the "prefix" field. +func PrefixContainsFold(v string) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldContainsFold(FieldPrefix, v)) +} + +// KeyHashEQ applies the EQ predicate on the "key_hash" field. +func KeyHashEQ(v string) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldEQ(FieldKeyHash, v)) +} + +// KeyHashNEQ applies the NEQ predicate on the "key_hash" field. +func KeyHashNEQ(v string) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldNEQ(FieldKeyHash, v)) +} + +// KeyHashIn applies the In predicate on the "key_hash" field. +func KeyHashIn(vs ...string) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldIn(FieldKeyHash, vs...)) +} + +// KeyHashNotIn applies the NotIn predicate on the "key_hash" field. +func KeyHashNotIn(vs ...string) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldNotIn(FieldKeyHash, vs...)) +} + +// KeyHashGT applies the GT predicate on the "key_hash" field. +func KeyHashGT(v string) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldGT(FieldKeyHash, v)) +} + +// KeyHashGTE applies the GTE predicate on the "key_hash" field. +func KeyHashGTE(v string) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldGTE(FieldKeyHash, v)) +} + +// KeyHashLT applies the LT predicate on the "key_hash" field. +func KeyHashLT(v string) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldLT(FieldKeyHash, v)) +} + +// KeyHashLTE applies the LTE predicate on the "key_hash" field. +func KeyHashLTE(v string) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldLTE(FieldKeyHash, v)) +} + +// KeyHashContains applies the Contains predicate on the "key_hash" field. +func KeyHashContains(v string) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldContains(FieldKeyHash, v)) +} + +// KeyHashHasPrefix applies the HasPrefix predicate on the "key_hash" field. +func KeyHashHasPrefix(v string) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldHasPrefix(FieldKeyHash, v)) +} + +// KeyHashHasSuffix applies the HasSuffix predicate on the "key_hash" field. +func KeyHashHasSuffix(v string) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldHasSuffix(FieldKeyHash, v)) +} + +// KeyHashEqualFold applies the EqualFold predicate on the "key_hash" field. +func KeyHashEqualFold(v string) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldEqualFold(FieldKeyHash, v)) +} + +// KeyHashContainsFold applies the ContainsFold predicate on the "key_hash" field. +func KeyHashContainsFold(v string) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldContainsFold(FieldKeyHash, v)) +} + +// ProjectIDEQ applies the EQ predicate on the "project_id" field. +func ProjectIDEQ(v uuid.UUID) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldEQ(FieldProjectID, v)) +} + +// ProjectIDNEQ applies the NEQ predicate on the "project_id" field. +func ProjectIDNEQ(v uuid.UUID) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldNEQ(FieldProjectID, v)) +} + +// ProjectIDIn applies the In predicate on the "project_id" field. +func ProjectIDIn(vs ...uuid.UUID) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldIn(FieldProjectID, vs...)) +} + +// ProjectIDNotIn applies the NotIn predicate on the "project_id" field. +func ProjectIDNotIn(vs ...uuid.UUID) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldNotIn(FieldProjectID, vs...)) +} + +// ProjectIDGT applies the GT predicate on the "project_id" field. +func ProjectIDGT(v uuid.UUID) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldGT(FieldProjectID, v)) +} + +// ProjectIDGTE applies the GTE predicate on the "project_id" field. +func ProjectIDGTE(v uuid.UUID) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldGTE(FieldProjectID, v)) +} + +// ProjectIDLT applies the LT predicate on the "project_id" field. +func ProjectIDLT(v uuid.UUID) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldLT(FieldProjectID, v)) +} + +// ProjectIDLTE applies the LTE predicate on the "project_id" field. +func ProjectIDLTE(v uuid.UUID) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldLTE(FieldProjectID, v)) +} + +// ScopesEQ applies the EQ predicate on the "scopes" field. +func ScopesEQ(v string) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldEQ(FieldScopes, v)) +} + +// ScopesNEQ applies the NEQ predicate on the "scopes" field. +func ScopesNEQ(v string) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldNEQ(FieldScopes, v)) +} + +// ScopesIn applies the In predicate on the "scopes" field. +func ScopesIn(vs ...string) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldIn(FieldScopes, vs...)) +} + +// ScopesNotIn applies the NotIn predicate on the "scopes" field. +func ScopesNotIn(vs ...string) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldNotIn(FieldScopes, vs...)) +} + +// ScopesGT applies the GT predicate on the "scopes" field. +func ScopesGT(v string) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldGT(FieldScopes, v)) +} + +// ScopesGTE applies the GTE predicate on the "scopes" field. +func ScopesGTE(v string) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldGTE(FieldScopes, v)) +} + +// ScopesLT applies the LT predicate on the "scopes" field. +func ScopesLT(v string) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldLT(FieldScopes, v)) +} + +// ScopesLTE applies the LTE predicate on the "scopes" field. +func ScopesLTE(v string) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldLTE(FieldScopes, v)) +} + +// ScopesContains applies the Contains predicate on the "scopes" field. +func ScopesContains(v string) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldContains(FieldScopes, v)) +} + +// ScopesHasPrefix applies the HasPrefix predicate on the "scopes" field. +func ScopesHasPrefix(v string) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldHasPrefix(FieldScopes, v)) +} + +// ScopesHasSuffix applies the HasSuffix predicate on the "scopes" field. +func ScopesHasSuffix(v string) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldHasSuffix(FieldScopes, v)) +} + +// ScopesEqualFold applies the EqualFold predicate on the "scopes" field. +func ScopesEqualFold(v string) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldEqualFold(FieldScopes, v)) +} + +// ScopesContainsFold applies the ContainsFold predicate on the "scopes" field. +func ScopesContainsFold(v string) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldContainsFold(FieldScopes, v)) +} + +// RevokedEQ applies the EQ predicate on the "revoked" field. +func RevokedEQ(v bool) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldEQ(FieldRevoked, v)) +} + +// RevokedNEQ applies the NEQ predicate on the "revoked" field. +func RevokedNEQ(v bool) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldNEQ(FieldRevoked, v)) +} + +// ExpiresAtEQ applies the EQ predicate on the "expires_at" field. +func ExpiresAtEQ(v time.Time) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldEQ(FieldExpiresAt, v)) +} + +// ExpiresAtNEQ applies the NEQ predicate on the "expires_at" field. +func ExpiresAtNEQ(v time.Time) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldNEQ(FieldExpiresAt, v)) +} + +// ExpiresAtIn applies the In predicate on the "expires_at" field. +func ExpiresAtIn(vs ...time.Time) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldIn(FieldExpiresAt, vs...)) +} + +// ExpiresAtNotIn applies the NotIn predicate on the "expires_at" field. +func ExpiresAtNotIn(vs ...time.Time) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldNotIn(FieldExpiresAt, vs...)) +} + +// ExpiresAtGT applies the GT predicate on the "expires_at" field. +func ExpiresAtGT(v time.Time) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldGT(FieldExpiresAt, v)) +} + +// ExpiresAtGTE applies the GTE predicate on the "expires_at" field. +func ExpiresAtGTE(v time.Time) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldGTE(FieldExpiresAt, v)) +} + +// ExpiresAtLT applies the LT predicate on the "expires_at" field. +func ExpiresAtLT(v time.Time) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldLT(FieldExpiresAt, v)) +} + +// ExpiresAtLTE applies the LTE predicate on the "expires_at" field. +func ExpiresAtLTE(v time.Time) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldLTE(FieldExpiresAt, v)) +} + +// ExpiresAtIsNil applies the IsNil predicate on the "expires_at" field. +func ExpiresAtIsNil() predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldIsNull(FieldExpiresAt)) +} + +// ExpiresAtNotNil applies the NotNil predicate on the "expires_at" field. +func ExpiresAtNotNil() predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldNotNull(FieldExpiresAt)) +} + +// LastUsedEQ applies the EQ predicate on the "last_used" field. +func LastUsedEQ(v time.Time) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldEQ(FieldLastUsed, v)) +} + +// LastUsedNEQ applies the NEQ predicate on the "last_used" field. +func LastUsedNEQ(v time.Time) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldNEQ(FieldLastUsed, v)) +} + +// LastUsedIn applies the In predicate on the "last_used" field. +func LastUsedIn(vs ...time.Time) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldIn(FieldLastUsed, vs...)) +} + +// LastUsedNotIn applies the NotIn predicate on the "last_used" field. +func LastUsedNotIn(vs ...time.Time) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldNotIn(FieldLastUsed, vs...)) +} + +// LastUsedGT applies the GT predicate on the "last_used" field. +func LastUsedGT(v time.Time) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldGT(FieldLastUsed, v)) +} + +// LastUsedGTE applies the GTE predicate on the "last_used" field. +func LastUsedGTE(v time.Time) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldGTE(FieldLastUsed, v)) +} + +// LastUsedLT applies the LT predicate on the "last_used" field. +func LastUsedLT(v time.Time) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldLT(FieldLastUsed, v)) +} + +// LastUsedLTE applies the LTE predicate on the "last_used" field. +func LastUsedLTE(v time.Time) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldLTE(FieldLastUsed, v)) +} + +// LastUsedIsNil applies the IsNil predicate on the "last_used" field. +func LastUsedIsNil() predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldIsNull(FieldLastUsed)) +} + +// LastUsedNotNil applies the NotNil predicate on the "last_used" field. +func LastUsedNotNil() predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldNotNull(FieldLastUsed)) +} + +// CreatedEQ applies the EQ predicate on the "created" field. +func CreatedEQ(v time.Time) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldEQ(FieldCreated, v)) +} + +// CreatedNEQ applies the NEQ predicate on the "created" field. +func CreatedNEQ(v time.Time) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldNEQ(FieldCreated, v)) +} + +// CreatedIn applies the In predicate on the "created" field. +func CreatedIn(vs ...time.Time) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldIn(FieldCreated, vs...)) +} + +// CreatedNotIn applies the NotIn predicate on the "created" field. +func CreatedNotIn(vs ...time.Time) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldNotIn(FieldCreated, vs...)) +} + +// CreatedGT applies the GT predicate on the "created" field. +func CreatedGT(v time.Time) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldGT(FieldCreated, v)) +} + +// CreatedGTE applies the GTE predicate on the "created" field. +func CreatedGTE(v time.Time) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldGTE(FieldCreated, v)) +} + +// CreatedLT applies the LT predicate on the "created" field. +func CreatedLT(v time.Time) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldLT(FieldCreated, v)) +} + +// CreatedLTE applies the LTE predicate on the "created" field. +func CreatedLTE(v time.Time) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.FieldLTE(FieldCreated, v)) +} + +// And groups predicates with the AND operator between them. +func And(predicates ...predicate.UserAccessToken) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.AndPredicates(predicates...)) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.UserAccessToken) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.OrPredicates(predicates...)) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.UserAccessToken) predicate.UserAccessToken { + return predicate.UserAccessToken(sql.NotPredicates(p)) +} diff --git a/pkg/ent/useraccesstoken_create.go b/pkg/ent/useraccesstoken_create.go new file mode 100644 index 000000000..8c3828e5a --- /dev/null +++ b/pkg/ent/useraccesstoken_create.go @@ -0,0 +1,1046 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/useraccesstoken" + "github.com/google/uuid" +) + +// UserAccessTokenCreate is the builder for creating a UserAccessToken entity. +type UserAccessTokenCreate struct { + config + mutation *UserAccessTokenMutation + hooks []Hook + conflict []sql.ConflictOption +} + +// SetUserID sets the "user_id" field. +func (_c *UserAccessTokenCreate) SetUserID(v uuid.UUID) *UserAccessTokenCreate { + _c.mutation.SetUserID(v) + return _c +} + +// SetName sets the "name" field. +func (_c *UserAccessTokenCreate) SetName(v string) *UserAccessTokenCreate { + _c.mutation.SetName(v) + return _c +} + +// SetPrefix sets the "prefix" field. +func (_c *UserAccessTokenCreate) SetPrefix(v string) *UserAccessTokenCreate { + _c.mutation.SetPrefix(v) + return _c +} + +// SetKeyHash sets the "key_hash" field. +func (_c *UserAccessTokenCreate) SetKeyHash(v string) *UserAccessTokenCreate { + _c.mutation.SetKeyHash(v) + return _c +} + +// SetProjectID sets the "project_id" field. +func (_c *UserAccessTokenCreate) SetProjectID(v uuid.UUID) *UserAccessTokenCreate { + _c.mutation.SetProjectID(v) + return _c +} + +// SetScopes sets the "scopes" field. +func (_c *UserAccessTokenCreate) SetScopes(v string) *UserAccessTokenCreate { + _c.mutation.SetScopes(v) + return _c +} + +// SetRevoked sets the "revoked" field. +func (_c *UserAccessTokenCreate) SetRevoked(v bool) *UserAccessTokenCreate { + _c.mutation.SetRevoked(v) + return _c +} + +// SetNillableRevoked sets the "revoked" field if the given value is not nil. +func (_c *UserAccessTokenCreate) SetNillableRevoked(v *bool) *UserAccessTokenCreate { + if v != nil { + _c.SetRevoked(*v) + } + return _c +} + +// SetExpiresAt sets the "expires_at" field. +func (_c *UserAccessTokenCreate) SetExpiresAt(v time.Time) *UserAccessTokenCreate { + _c.mutation.SetExpiresAt(v) + return _c +} + +// SetNillableExpiresAt sets the "expires_at" field if the given value is not nil. +func (_c *UserAccessTokenCreate) SetNillableExpiresAt(v *time.Time) *UserAccessTokenCreate { + if v != nil { + _c.SetExpiresAt(*v) + } + return _c +} + +// SetLastUsed sets the "last_used" field. +func (_c *UserAccessTokenCreate) SetLastUsed(v time.Time) *UserAccessTokenCreate { + _c.mutation.SetLastUsed(v) + return _c +} + +// SetNillableLastUsed sets the "last_used" field if the given value is not nil. +func (_c *UserAccessTokenCreate) SetNillableLastUsed(v *time.Time) *UserAccessTokenCreate { + if v != nil { + _c.SetLastUsed(*v) + } + return _c +} + +// SetCreated sets the "created" field. +func (_c *UserAccessTokenCreate) SetCreated(v time.Time) *UserAccessTokenCreate { + _c.mutation.SetCreated(v) + return _c +} + +// SetNillableCreated sets the "created" field if the given value is not nil. +func (_c *UserAccessTokenCreate) SetNillableCreated(v *time.Time) *UserAccessTokenCreate { + if v != nil { + _c.SetCreated(*v) + } + return _c +} + +// SetID sets the "id" field. +func (_c *UserAccessTokenCreate) SetID(v uuid.UUID) *UserAccessTokenCreate { + _c.mutation.SetID(v) + return _c +} + +// SetNillableID sets the "id" field if the given value is not nil. +func (_c *UserAccessTokenCreate) SetNillableID(v *uuid.UUID) *UserAccessTokenCreate { + if v != nil { + _c.SetID(*v) + } + return _c +} + +// Mutation returns the UserAccessTokenMutation object of the builder. +func (_c *UserAccessTokenCreate) Mutation() *UserAccessTokenMutation { + return _c.mutation +} + +// Save creates the UserAccessToken in the database. +func (_c *UserAccessTokenCreate) Save(ctx context.Context) (*UserAccessToken, error) { + _c.defaults() + return withHooks(ctx, _c.sqlSave, _c.mutation, _c.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (_c *UserAccessTokenCreate) SaveX(ctx context.Context) *UserAccessToken { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *UserAccessTokenCreate) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *UserAccessTokenCreate) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_c *UserAccessTokenCreate) defaults() { + if _, ok := _c.mutation.Revoked(); !ok { + v := useraccesstoken.DefaultRevoked + _c.mutation.SetRevoked(v) + } + if _, ok := _c.mutation.Created(); !ok { + v := useraccesstoken.DefaultCreated() + _c.mutation.SetCreated(v) + } + if _, ok := _c.mutation.ID(); !ok { + v := useraccesstoken.DefaultID() + _c.mutation.SetID(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_c *UserAccessTokenCreate) check() error { + if _, ok := _c.mutation.UserID(); !ok { + return &ValidationError{Name: "user_id", err: errors.New(`ent: missing required field "UserAccessToken.user_id"`)} + } + if _, ok := _c.mutation.Name(); !ok { + return &ValidationError{Name: "name", err: errors.New(`ent: missing required field "UserAccessToken.name"`)} + } + if v, ok := _c.mutation.Name(); ok { + if err := useraccesstoken.NameValidator(v); err != nil { + return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "UserAccessToken.name": %w`, err)} + } + } + if _, ok := _c.mutation.Prefix(); !ok { + return &ValidationError{Name: "prefix", err: errors.New(`ent: missing required field "UserAccessToken.prefix"`)} + } + if v, ok := _c.mutation.Prefix(); ok { + if err := useraccesstoken.PrefixValidator(v); err != nil { + return &ValidationError{Name: "prefix", err: fmt.Errorf(`ent: validator failed for field "UserAccessToken.prefix": %w`, err)} + } + } + if _, ok := _c.mutation.KeyHash(); !ok { + return &ValidationError{Name: "key_hash", err: errors.New(`ent: missing required field "UserAccessToken.key_hash"`)} + } + if v, ok := _c.mutation.KeyHash(); ok { + if err := useraccesstoken.KeyHashValidator(v); err != nil { + return &ValidationError{Name: "key_hash", err: fmt.Errorf(`ent: validator failed for field "UserAccessToken.key_hash": %w`, err)} + } + } + if _, ok := _c.mutation.ProjectID(); !ok { + return &ValidationError{Name: "project_id", err: errors.New(`ent: missing required field "UserAccessToken.project_id"`)} + } + if _, ok := _c.mutation.Scopes(); !ok { + return &ValidationError{Name: "scopes", err: errors.New(`ent: missing required field "UserAccessToken.scopes"`)} + } + if v, ok := _c.mutation.Scopes(); ok { + if err := useraccesstoken.ScopesValidator(v); err != nil { + return &ValidationError{Name: "scopes", err: fmt.Errorf(`ent: validator failed for field "UserAccessToken.scopes": %w`, err)} + } + } + if _, ok := _c.mutation.Revoked(); !ok { + return &ValidationError{Name: "revoked", err: errors.New(`ent: missing required field "UserAccessToken.revoked"`)} + } + if _, ok := _c.mutation.Created(); !ok { + return &ValidationError{Name: "created", err: errors.New(`ent: missing required field "UserAccessToken.created"`)} + } + return nil +} + +func (_c *UserAccessTokenCreate) sqlSave(ctx context.Context) (*UserAccessToken, error) { + if err := _c.check(); err != nil { + return nil, err + } + _node, _spec := _c.createSpec() + if err := sqlgraph.CreateNode(ctx, _c.driver, _spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + if _spec.ID.Value != nil { + if id, ok := _spec.ID.Value.(*uuid.UUID); ok { + _node.ID = *id + } else if err := _node.ID.Scan(_spec.ID.Value); err != nil { + return nil, err + } + } + _c.mutation.id = &_node.ID + _c.mutation.done = true + return _node, nil +} + +func (_c *UserAccessTokenCreate) createSpec() (*UserAccessToken, *sqlgraph.CreateSpec) { + var ( + _node = &UserAccessToken{config: _c.config} + _spec = sqlgraph.NewCreateSpec(useraccesstoken.Table, sqlgraph.NewFieldSpec(useraccesstoken.FieldID, field.TypeUUID)) + ) + _spec.OnConflict = _c.conflict + if id, ok := _c.mutation.ID(); ok { + _node.ID = id + _spec.ID.Value = &id + } + if value, ok := _c.mutation.UserID(); ok { + _spec.SetField(useraccesstoken.FieldUserID, field.TypeUUID, value) + _node.UserID = value + } + if value, ok := _c.mutation.Name(); ok { + _spec.SetField(useraccesstoken.FieldName, field.TypeString, value) + _node.Name = value + } + if value, ok := _c.mutation.Prefix(); ok { + _spec.SetField(useraccesstoken.FieldPrefix, field.TypeString, value) + _node.Prefix = value + } + if value, ok := _c.mutation.KeyHash(); ok { + _spec.SetField(useraccesstoken.FieldKeyHash, field.TypeString, value) + _node.KeyHash = value + } + if value, ok := _c.mutation.ProjectID(); ok { + _spec.SetField(useraccesstoken.FieldProjectID, field.TypeUUID, value) + _node.ProjectID = value + } + if value, ok := _c.mutation.Scopes(); ok { + _spec.SetField(useraccesstoken.FieldScopes, field.TypeString, value) + _node.Scopes = value + } + if value, ok := _c.mutation.Revoked(); ok { + _spec.SetField(useraccesstoken.FieldRevoked, field.TypeBool, value) + _node.Revoked = value + } + if value, ok := _c.mutation.ExpiresAt(); ok { + _spec.SetField(useraccesstoken.FieldExpiresAt, field.TypeTime, value) + _node.ExpiresAt = &value + } + if value, ok := _c.mutation.LastUsed(); ok { + _spec.SetField(useraccesstoken.FieldLastUsed, field.TypeTime, value) + _node.LastUsed = &value + } + if value, ok := _c.mutation.Created(); ok { + _spec.SetField(useraccesstoken.FieldCreated, field.TypeTime, value) + _node.Created = value + } + return _node, _spec +} + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.UserAccessToken.Create(). +// SetUserID(v). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.UserAccessTokenUpsert) { +// SetUserID(v+v). +// }). +// Exec(ctx) +func (_c *UserAccessTokenCreate) OnConflict(opts ...sql.ConflictOption) *UserAccessTokenUpsertOne { + _c.conflict = opts + return &UserAccessTokenUpsertOne{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.UserAccessToken.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *UserAccessTokenCreate) OnConflictColumns(columns ...string) *UserAccessTokenUpsertOne { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &UserAccessTokenUpsertOne{ + create: _c, + } +} + +type ( + // UserAccessTokenUpsertOne is the builder for "upsert"-ing + // one UserAccessToken node. + UserAccessTokenUpsertOne struct { + create *UserAccessTokenCreate + } + + // UserAccessTokenUpsert is the "OnConflict" setter. + UserAccessTokenUpsert struct { + *sql.UpdateSet + } +) + +// SetUserID sets the "user_id" field. +func (u *UserAccessTokenUpsert) SetUserID(v uuid.UUID) *UserAccessTokenUpsert { + u.Set(useraccesstoken.FieldUserID, v) + return u +} + +// UpdateUserID sets the "user_id" field to the value that was provided on create. +func (u *UserAccessTokenUpsert) UpdateUserID() *UserAccessTokenUpsert { + u.SetExcluded(useraccesstoken.FieldUserID) + return u +} + +// SetName sets the "name" field. +func (u *UserAccessTokenUpsert) SetName(v string) *UserAccessTokenUpsert { + u.Set(useraccesstoken.FieldName, v) + return u +} + +// UpdateName sets the "name" field to the value that was provided on create. +func (u *UserAccessTokenUpsert) UpdateName() *UserAccessTokenUpsert { + u.SetExcluded(useraccesstoken.FieldName) + return u +} + +// SetPrefix sets the "prefix" field. +func (u *UserAccessTokenUpsert) SetPrefix(v string) *UserAccessTokenUpsert { + u.Set(useraccesstoken.FieldPrefix, v) + return u +} + +// UpdatePrefix sets the "prefix" field to the value that was provided on create. +func (u *UserAccessTokenUpsert) UpdatePrefix() *UserAccessTokenUpsert { + u.SetExcluded(useraccesstoken.FieldPrefix) + return u +} + +// SetKeyHash sets the "key_hash" field. +func (u *UserAccessTokenUpsert) SetKeyHash(v string) *UserAccessTokenUpsert { + u.Set(useraccesstoken.FieldKeyHash, v) + return u +} + +// UpdateKeyHash sets the "key_hash" field to the value that was provided on create. +func (u *UserAccessTokenUpsert) UpdateKeyHash() *UserAccessTokenUpsert { + u.SetExcluded(useraccesstoken.FieldKeyHash) + return u +} + +// SetProjectID sets the "project_id" field. +func (u *UserAccessTokenUpsert) SetProjectID(v uuid.UUID) *UserAccessTokenUpsert { + u.Set(useraccesstoken.FieldProjectID, v) + return u +} + +// UpdateProjectID sets the "project_id" field to the value that was provided on create. +func (u *UserAccessTokenUpsert) UpdateProjectID() *UserAccessTokenUpsert { + u.SetExcluded(useraccesstoken.FieldProjectID) + return u +} + +// SetScopes sets the "scopes" field. +func (u *UserAccessTokenUpsert) SetScopes(v string) *UserAccessTokenUpsert { + u.Set(useraccesstoken.FieldScopes, v) + return u +} + +// UpdateScopes sets the "scopes" field to the value that was provided on create. +func (u *UserAccessTokenUpsert) UpdateScopes() *UserAccessTokenUpsert { + u.SetExcluded(useraccesstoken.FieldScopes) + return u +} + +// SetRevoked sets the "revoked" field. +func (u *UserAccessTokenUpsert) SetRevoked(v bool) *UserAccessTokenUpsert { + u.Set(useraccesstoken.FieldRevoked, v) + return u +} + +// UpdateRevoked sets the "revoked" field to the value that was provided on create. +func (u *UserAccessTokenUpsert) UpdateRevoked() *UserAccessTokenUpsert { + u.SetExcluded(useraccesstoken.FieldRevoked) + return u +} + +// SetExpiresAt sets the "expires_at" field. +func (u *UserAccessTokenUpsert) SetExpiresAt(v time.Time) *UserAccessTokenUpsert { + u.Set(useraccesstoken.FieldExpiresAt, v) + return u +} + +// UpdateExpiresAt sets the "expires_at" field to the value that was provided on create. +func (u *UserAccessTokenUpsert) UpdateExpiresAt() *UserAccessTokenUpsert { + u.SetExcluded(useraccesstoken.FieldExpiresAt) + return u +} + +// ClearExpiresAt clears the value of the "expires_at" field. +func (u *UserAccessTokenUpsert) ClearExpiresAt() *UserAccessTokenUpsert { + u.SetNull(useraccesstoken.FieldExpiresAt) + return u +} + +// SetLastUsed sets the "last_used" field. +func (u *UserAccessTokenUpsert) SetLastUsed(v time.Time) *UserAccessTokenUpsert { + u.Set(useraccesstoken.FieldLastUsed, v) + return u +} + +// UpdateLastUsed sets the "last_used" field to the value that was provided on create. +func (u *UserAccessTokenUpsert) UpdateLastUsed() *UserAccessTokenUpsert { + u.SetExcluded(useraccesstoken.FieldLastUsed) + return u +} + +// ClearLastUsed clears the value of the "last_used" field. +func (u *UserAccessTokenUpsert) ClearLastUsed() *UserAccessTokenUpsert { + u.SetNull(useraccesstoken.FieldLastUsed) + return u +} + +// UpdateNewValues updates the mutable fields using the new values that were set on create except the ID field. +// Using this option is equivalent to using: +// +// client.UserAccessToken.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(useraccesstoken.FieldID) +// }), +// ). +// Exec(ctx) +func (u *UserAccessTokenUpsertOne) UpdateNewValues() *UserAccessTokenUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + if _, exists := u.create.mutation.ID(); exists { + s.SetIgnore(useraccesstoken.FieldID) + } + if _, exists := u.create.mutation.Created(); exists { + s.SetIgnore(useraccesstoken.FieldCreated) + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.UserAccessToken.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *UserAccessTokenUpsertOne) Ignore() *UserAccessTokenUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *UserAccessTokenUpsertOne) DoNothing() *UserAccessTokenUpsertOne { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the UserAccessTokenCreate.OnConflict +// documentation for more info. +func (u *UserAccessTokenUpsertOne) Update(set func(*UserAccessTokenUpsert)) *UserAccessTokenUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&UserAccessTokenUpsert{UpdateSet: update}) + })) + return u +} + +// SetUserID sets the "user_id" field. +func (u *UserAccessTokenUpsertOne) SetUserID(v uuid.UUID) *UserAccessTokenUpsertOne { + return u.Update(func(s *UserAccessTokenUpsert) { + s.SetUserID(v) + }) +} + +// UpdateUserID sets the "user_id" field to the value that was provided on create. +func (u *UserAccessTokenUpsertOne) UpdateUserID() *UserAccessTokenUpsertOne { + return u.Update(func(s *UserAccessTokenUpsert) { + s.UpdateUserID() + }) +} + +// SetName sets the "name" field. +func (u *UserAccessTokenUpsertOne) SetName(v string) *UserAccessTokenUpsertOne { + return u.Update(func(s *UserAccessTokenUpsert) { + s.SetName(v) + }) +} + +// UpdateName sets the "name" field to the value that was provided on create. +func (u *UserAccessTokenUpsertOne) UpdateName() *UserAccessTokenUpsertOne { + return u.Update(func(s *UserAccessTokenUpsert) { + s.UpdateName() + }) +} + +// SetPrefix sets the "prefix" field. +func (u *UserAccessTokenUpsertOne) SetPrefix(v string) *UserAccessTokenUpsertOne { + return u.Update(func(s *UserAccessTokenUpsert) { + s.SetPrefix(v) + }) +} + +// UpdatePrefix sets the "prefix" field to the value that was provided on create. +func (u *UserAccessTokenUpsertOne) UpdatePrefix() *UserAccessTokenUpsertOne { + return u.Update(func(s *UserAccessTokenUpsert) { + s.UpdatePrefix() + }) +} + +// SetKeyHash sets the "key_hash" field. +func (u *UserAccessTokenUpsertOne) SetKeyHash(v string) *UserAccessTokenUpsertOne { + return u.Update(func(s *UserAccessTokenUpsert) { + s.SetKeyHash(v) + }) +} + +// UpdateKeyHash sets the "key_hash" field to the value that was provided on create. +func (u *UserAccessTokenUpsertOne) UpdateKeyHash() *UserAccessTokenUpsertOne { + return u.Update(func(s *UserAccessTokenUpsert) { + s.UpdateKeyHash() + }) +} + +// SetProjectID sets the "project_id" field. +func (u *UserAccessTokenUpsertOne) SetProjectID(v uuid.UUID) *UserAccessTokenUpsertOne { + return u.Update(func(s *UserAccessTokenUpsert) { + s.SetProjectID(v) + }) +} + +// UpdateProjectID sets the "project_id" field to the value that was provided on create. +func (u *UserAccessTokenUpsertOne) UpdateProjectID() *UserAccessTokenUpsertOne { + return u.Update(func(s *UserAccessTokenUpsert) { + s.UpdateProjectID() + }) +} + +// SetScopes sets the "scopes" field. +func (u *UserAccessTokenUpsertOne) SetScopes(v string) *UserAccessTokenUpsertOne { + return u.Update(func(s *UserAccessTokenUpsert) { + s.SetScopes(v) + }) +} + +// UpdateScopes sets the "scopes" field to the value that was provided on create. +func (u *UserAccessTokenUpsertOne) UpdateScopes() *UserAccessTokenUpsertOne { + return u.Update(func(s *UserAccessTokenUpsert) { + s.UpdateScopes() + }) +} + +// SetRevoked sets the "revoked" field. +func (u *UserAccessTokenUpsertOne) SetRevoked(v bool) *UserAccessTokenUpsertOne { + return u.Update(func(s *UserAccessTokenUpsert) { + s.SetRevoked(v) + }) +} + +// UpdateRevoked sets the "revoked" field to the value that was provided on create. +func (u *UserAccessTokenUpsertOne) UpdateRevoked() *UserAccessTokenUpsertOne { + return u.Update(func(s *UserAccessTokenUpsert) { + s.UpdateRevoked() + }) +} + +// SetExpiresAt sets the "expires_at" field. +func (u *UserAccessTokenUpsertOne) SetExpiresAt(v time.Time) *UserAccessTokenUpsertOne { + return u.Update(func(s *UserAccessTokenUpsert) { + s.SetExpiresAt(v) + }) +} + +// UpdateExpiresAt sets the "expires_at" field to the value that was provided on create. +func (u *UserAccessTokenUpsertOne) UpdateExpiresAt() *UserAccessTokenUpsertOne { + return u.Update(func(s *UserAccessTokenUpsert) { + s.UpdateExpiresAt() + }) +} + +// ClearExpiresAt clears the value of the "expires_at" field. +func (u *UserAccessTokenUpsertOne) ClearExpiresAt() *UserAccessTokenUpsertOne { + return u.Update(func(s *UserAccessTokenUpsert) { + s.ClearExpiresAt() + }) +} + +// SetLastUsed sets the "last_used" field. +func (u *UserAccessTokenUpsertOne) SetLastUsed(v time.Time) *UserAccessTokenUpsertOne { + return u.Update(func(s *UserAccessTokenUpsert) { + s.SetLastUsed(v) + }) +} + +// UpdateLastUsed sets the "last_used" field to the value that was provided on create. +func (u *UserAccessTokenUpsertOne) UpdateLastUsed() *UserAccessTokenUpsertOne { + return u.Update(func(s *UserAccessTokenUpsert) { + s.UpdateLastUsed() + }) +} + +// ClearLastUsed clears the value of the "last_used" field. +func (u *UserAccessTokenUpsertOne) ClearLastUsed() *UserAccessTokenUpsertOne { + return u.Update(func(s *UserAccessTokenUpsert) { + s.ClearLastUsed() + }) +} + +// Exec executes the query. +func (u *UserAccessTokenUpsertOne) Exec(ctx context.Context) error { + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for UserAccessTokenCreate.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *UserAccessTokenUpsertOne) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} + +// Exec executes the UPSERT query and returns the inserted/updated ID. +func (u *UserAccessTokenUpsertOne) ID(ctx context.Context) (id uuid.UUID, err error) { + if u.create.driver.Dialect() == dialect.MySQL { + // In case of "ON CONFLICT", there is no way to get back non-numeric ID + // fields from the database since MySQL does not support the RETURNING clause. + return id, errors.New("ent: UserAccessTokenUpsertOne.ID is not supported by MySQL driver. Use UserAccessTokenUpsertOne.Exec instead") + } + node, err := u.create.Save(ctx) + if err != nil { + return id, err + } + return node.ID, nil +} + +// IDX is like ID, but panics if an error occurs. +func (u *UserAccessTokenUpsertOne) IDX(ctx context.Context) uuid.UUID { + id, err := u.ID(ctx) + if err != nil { + panic(err) + } + return id +} + +// UserAccessTokenCreateBulk is the builder for creating many UserAccessToken entities in bulk. +type UserAccessTokenCreateBulk struct { + config + err error + builders []*UserAccessTokenCreate + conflict []sql.ConflictOption +} + +// Save creates the UserAccessToken entities in the database. +func (_c *UserAccessTokenCreateBulk) Save(ctx context.Context) ([]*UserAccessToken, error) { + if _c.err != nil { + return nil, _c.err + } + specs := make([]*sqlgraph.CreateSpec, len(_c.builders)) + nodes := make([]*UserAccessToken, len(_c.builders)) + mutators := make([]Mutator, len(_c.builders)) + for i := range _c.builders { + func(i int, root context.Context) { + builder := _c.builders[i] + builder.defaults() + var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { + mutation, ok := m.(*UserAccessTokenMutation) + if !ok { + return nil, fmt.Errorf("unexpected mutation type %T", m) + } + if err := builder.check(); err != nil { + return nil, err + } + builder.mutation = mutation + var err error + nodes[i], specs[i] = builder.createSpec() + if i < len(mutators)-1 { + _, err = mutators[i+1].Mutate(root, _c.builders[i+1].mutation) + } else { + spec := &sqlgraph.BatchCreateSpec{Nodes: specs} + spec.OnConflict = _c.conflict + // Invoke the actual operation on the latest mutation in the chain. + if err = sqlgraph.BatchCreate(ctx, _c.driver, spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + } + } + if err != nil { + return nil, err + } + mutation.id = &nodes[i].ID + mutation.done = true + return nodes[i], nil + }) + for i := len(builder.hooks) - 1; i >= 0; i-- { + mut = builder.hooks[i](mut) + } + mutators[i] = mut + }(i, ctx) + } + if len(mutators) > 0 { + if _, err := mutators[0].Mutate(ctx, _c.builders[0].mutation); err != nil { + return nil, err + } + } + return nodes, nil +} + +// SaveX is like Save, but panics if an error occurs. +func (_c *UserAccessTokenCreateBulk) SaveX(ctx context.Context) []*UserAccessToken { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *UserAccessTokenCreateBulk) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *UserAccessTokenCreateBulk) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.UserAccessToken.CreateBulk(builders...). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.UserAccessTokenUpsert) { +// SetUserID(v+v). +// }). +// Exec(ctx) +func (_c *UserAccessTokenCreateBulk) OnConflict(opts ...sql.ConflictOption) *UserAccessTokenUpsertBulk { + _c.conflict = opts + return &UserAccessTokenUpsertBulk{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.UserAccessToken.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *UserAccessTokenCreateBulk) OnConflictColumns(columns ...string) *UserAccessTokenUpsertBulk { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &UserAccessTokenUpsertBulk{ + create: _c, + } +} + +// UserAccessTokenUpsertBulk is the builder for "upsert"-ing +// a bulk of UserAccessToken nodes. +type UserAccessTokenUpsertBulk struct { + create *UserAccessTokenCreateBulk +} + +// UpdateNewValues updates the mutable fields using the new values that +// were set on create. Using this option is equivalent to using: +// +// client.UserAccessToken.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(useraccesstoken.FieldID) +// }), +// ). +// Exec(ctx) +func (u *UserAccessTokenUpsertBulk) UpdateNewValues() *UserAccessTokenUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + for _, b := range u.create.builders { + if _, exists := b.mutation.ID(); exists { + s.SetIgnore(useraccesstoken.FieldID) + } + if _, exists := b.mutation.Created(); exists { + s.SetIgnore(useraccesstoken.FieldCreated) + } + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.UserAccessToken.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *UserAccessTokenUpsertBulk) Ignore() *UserAccessTokenUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *UserAccessTokenUpsertBulk) DoNothing() *UserAccessTokenUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the UserAccessTokenCreateBulk.OnConflict +// documentation for more info. +func (u *UserAccessTokenUpsertBulk) Update(set func(*UserAccessTokenUpsert)) *UserAccessTokenUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&UserAccessTokenUpsert{UpdateSet: update}) + })) + return u +} + +// SetUserID sets the "user_id" field. +func (u *UserAccessTokenUpsertBulk) SetUserID(v uuid.UUID) *UserAccessTokenUpsertBulk { + return u.Update(func(s *UserAccessTokenUpsert) { + s.SetUserID(v) + }) +} + +// UpdateUserID sets the "user_id" field to the value that was provided on create. +func (u *UserAccessTokenUpsertBulk) UpdateUserID() *UserAccessTokenUpsertBulk { + return u.Update(func(s *UserAccessTokenUpsert) { + s.UpdateUserID() + }) +} + +// SetName sets the "name" field. +func (u *UserAccessTokenUpsertBulk) SetName(v string) *UserAccessTokenUpsertBulk { + return u.Update(func(s *UserAccessTokenUpsert) { + s.SetName(v) + }) +} + +// UpdateName sets the "name" field to the value that was provided on create. +func (u *UserAccessTokenUpsertBulk) UpdateName() *UserAccessTokenUpsertBulk { + return u.Update(func(s *UserAccessTokenUpsert) { + s.UpdateName() + }) +} + +// SetPrefix sets the "prefix" field. +func (u *UserAccessTokenUpsertBulk) SetPrefix(v string) *UserAccessTokenUpsertBulk { + return u.Update(func(s *UserAccessTokenUpsert) { + s.SetPrefix(v) + }) +} + +// UpdatePrefix sets the "prefix" field to the value that was provided on create. +func (u *UserAccessTokenUpsertBulk) UpdatePrefix() *UserAccessTokenUpsertBulk { + return u.Update(func(s *UserAccessTokenUpsert) { + s.UpdatePrefix() + }) +} + +// SetKeyHash sets the "key_hash" field. +func (u *UserAccessTokenUpsertBulk) SetKeyHash(v string) *UserAccessTokenUpsertBulk { + return u.Update(func(s *UserAccessTokenUpsert) { + s.SetKeyHash(v) + }) +} + +// UpdateKeyHash sets the "key_hash" field to the value that was provided on create. +func (u *UserAccessTokenUpsertBulk) UpdateKeyHash() *UserAccessTokenUpsertBulk { + return u.Update(func(s *UserAccessTokenUpsert) { + s.UpdateKeyHash() + }) +} + +// SetProjectID sets the "project_id" field. +func (u *UserAccessTokenUpsertBulk) SetProjectID(v uuid.UUID) *UserAccessTokenUpsertBulk { + return u.Update(func(s *UserAccessTokenUpsert) { + s.SetProjectID(v) + }) +} + +// UpdateProjectID sets the "project_id" field to the value that was provided on create. +func (u *UserAccessTokenUpsertBulk) UpdateProjectID() *UserAccessTokenUpsertBulk { + return u.Update(func(s *UserAccessTokenUpsert) { + s.UpdateProjectID() + }) +} + +// SetScopes sets the "scopes" field. +func (u *UserAccessTokenUpsertBulk) SetScopes(v string) *UserAccessTokenUpsertBulk { + return u.Update(func(s *UserAccessTokenUpsert) { + s.SetScopes(v) + }) +} + +// UpdateScopes sets the "scopes" field to the value that was provided on create. +func (u *UserAccessTokenUpsertBulk) UpdateScopes() *UserAccessTokenUpsertBulk { + return u.Update(func(s *UserAccessTokenUpsert) { + s.UpdateScopes() + }) +} + +// SetRevoked sets the "revoked" field. +func (u *UserAccessTokenUpsertBulk) SetRevoked(v bool) *UserAccessTokenUpsertBulk { + return u.Update(func(s *UserAccessTokenUpsert) { + s.SetRevoked(v) + }) +} + +// UpdateRevoked sets the "revoked" field to the value that was provided on create. +func (u *UserAccessTokenUpsertBulk) UpdateRevoked() *UserAccessTokenUpsertBulk { + return u.Update(func(s *UserAccessTokenUpsert) { + s.UpdateRevoked() + }) +} + +// SetExpiresAt sets the "expires_at" field. +func (u *UserAccessTokenUpsertBulk) SetExpiresAt(v time.Time) *UserAccessTokenUpsertBulk { + return u.Update(func(s *UserAccessTokenUpsert) { + s.SetExpiresAt(v) + }) +} + +// UpdateExpiresAt sets the "expires_at" field to the value that was provided on create. +func (u *UserAccessTokenUpsertBulk) UpdateExpiresAt() *UserAccessTokenUpsertBulk { + return u.Update(func(s *UserAccessTokenUpsert) { + s.UpdateExpiresAt() + }) +} + +// ClearExpiresAt clears the value of the "expires_at" field. +func (u *UserAccessTokenUpsertBulk) ClearExpiresAt() *UserAccessTokenUpsertBulk { + return u.Update(func(s *UserAccessTokenUpsert) { + s.ClearExpiresAt() + }) +} + +// SetLastUsed sets the "last_used" field. +func (u *UserAccessTokenUpsertBulk) SetLastUsed(v time.Time) *UserAccessTokenUpsertBulk { + return u.Update(func(s *UserAccessTokenUpsert) { + s.SetLastUsed(v) + }) +} + +// UpdateLastUsed sets the "last_used" field to the value that was provided on create. +func (u *UserAccessTokenUpsertBulk) UpdateLastUsed() *UserAccessTokenUpsertBulk { + return u.Update(func(s *UserAccessTokenUpsert) { + s.UpdateLastUsed() + }) +} + +// ClearLastUsed clears the value of the "last_used" field. +func (u *UserAccessTokenUpsertBulk) ClearLastUsed() *UserAccessTokenUpsertBulk { + return u.Update(func(s *UserAccessTokenUpsert) { + s.ClearLastUsed() + }) +} + +// Exec executes the query. +func (u *UserAccessTokenUpsertBulk) Exec(ctx context.Context) error { + if u.create.err != nil { + return u.create.err + } + for i, b := range u.create.builders { + if len(b.conflict) != 0 { + return fmt.Errorf("ent: OnConflict was set for builder %d. Set it on the UserAccessTokenCreateBulk instead", i) + } + } + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for UserAccessTokenCreateBulk.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *UserAccessTokenUpsertBulk) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/pkg/ent/useraccesstoken_delete.go b/pkg/ent/useraccesstoken_delete.go new file mode 100644 index 000000000..e0f4a4d49 --- /dev/null +++ b/pkg/ent/useraccesstoken_delete.go @@ -0,0 +1,88 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/GoogleCloudPlatform/scion/pkg/ent/useraccesstoken" +) + +// UserAccessTokenDelete is the builder for deleting a UserAccessToken entity. +type UserAccessTokenDelete struct { + config + hooks []Hook + mutation *UserAccessTokenMutation +} + +// Where appends a list predicates to the UserAccessTokenDelete builder. +func (_d *UserAccessTokenDelete) Where(ps ...predicate.UserAccessToken) *UserAccessTokenDelete { + _d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (_d *UserAccessTokenDelete) Exec(ctx context.Context) (int, error) { + return withHooks(ctx, _d.sqlExec, _d.mutation, _d.hooks) +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *UserAccessTokenDelete) ExecX(ctx context.Context) int { + n, err := _d.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (_d *UserAccessTokenDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(useraccesstoken.Table, sqlgraph.NewFieldSpec(useraccesstoken.FieldID, field.TypeUUID)) + if ps := _d.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + affected, err := sqlgraph.DeleteNodes(ctx, _d.driver, _spec) + if err != nil && sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + _d.mutation.done = true + return affected, err +} + +// UserAccessTokenDeleteOne is the builder for deleting a single UserAccessToken entity. +type UserAccessTokenDeleteOne struct { + _d *UserAccessTokenDelete +} + +// Where appends a list predicates to the UserAccessTokenDelete builder. +func (_d *UserAccessTokenDeleteOne) Where(ps ...predicate.UserAccessToken) *UserAccessTokenDeleteOne { + _d._d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query. +func (_d *UserAccessTokenDeleteOne) Exec(ctx context.Context) error { + n, err := _d._d.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{useraccesstoken.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *UserAccessTokenDeleteOne) ExecX(ctx context.Context) { + if err := _d.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/pkg/ent/useraccesstoken_query.go b/pkg/ent/useraccesstoken_query.go new file mode 100644 index 000000000..d443fe837 --- /dev/null +++ b/pkg/ent/useraccesstoken_query.go @@ -0,0 +1,565 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "fmt" + "math" + + "entgo.io/ent" + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/GoogleCloudPlatform/scion/pkg/ent/useraccesstoken" + "github.com/google/uuid" +) + +// UserAccessTokenQuery is the builder for querying UserAccessToken entities. +type UserAccessTokenQuery struct { + config + ctx *QueryContext + order []useraccesstoken.OrderOption + inters []Interceptor + predicates []predicate.UserAccessToken + modifiers []func(*sql.Selector) + // intermediate query (i.e. traversal path). + sql *sql.Selector + path func(context.Context) (*sql.Selector, error) +} + +// Where adds a new predicate for the UserAccessTokenQuery builder. +func (_q *UserAccessTokenQuery) Where(ps ...predicate.UserAccessToken) *UserAccessTokenQuery { + _q.predicates = append(_q.predicates, ps...) + return _q +} + +// Limit the number of records to be returned by this query. +func (_q *UserAccessTokenQuery) Limit(limit int) *UserAccessTokenQuery { + _q.ctx.Limit = &limit + return _q +} + +// Offset to start from. +func (_q *UserAccessTokenQuery) Offset(offset int) *UserAccessTokenQuery { + _q.ctx.Offset = &offset + return _q +} + +// Unique configures the query builder to filter duplicate records on query. +// By default, unique is set to true, and can be disabled using this method. +func (_q *UserAccessTokenQuery) Unique(unique bool) *UserAccessTokenQuery { + _q.ctx.Unique = &unique + return _q +} + +// Order specifies how the records should be ordered. +func (_q *UserAccessTokenQuery) Order(o ...useraccesstoken.OrderOption) *UserAccessTokenQuery { + _q.order = append(_q.order, o...) + return _q +} + +// First returns the first UserAccessToken entity from the query. +// Returns a *NotFoundError when no UserAccessToken was found. +func (_q *UserAccessTokenQuery) First(ctx context.Context) (*UserAccessToken, error) { + nodes, err := _q.Limit(1).All(setContextOp(ctx, _q.ctx, ent.OpQueryFirst)) + if err != nil { + return nil, err + } + if len(nodes) == 0 { + return nil, &NotFoundError{useraccesstoken.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (_q *UserAccessTokenQuery) FirstX(ctx context.Context) *UserAccessToken { + node, err := _q.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// FirstID returns the first UserAccessToken ID from the query. +// Returns a *NotFoundError when no UserAccessToken ID was found. +func (_q *UserAccessTokenQuery) FirstID(ctx context.Context) (id uuid.UUID, err error) { + var ids []uuid.UUID + if ids, err = _q.Limit(1).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryFirstID)); err != nil { + return + } + if len(ids) == 0 { + err = &NotFoundError{useraccesstoken.Label} + return + } + return ids[0], nil +} + +// FirstIDX is like FirstID, but panics if an error occurs. +func (_q *UserAccessTokenQuery) FirstIDX(ctx context.Context) uuid.UUID { + id, err := _q.FirstID(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return id +} + +// Only returns a single UserAccessToken entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one UserAccessToken entity is found. +// Returns a *NotFoundError when no UserAccessToken entities are found. +func (_q *UserAccessTokenQuery) Only(ctx context.Context) (*UserAccessToken, error) { + nodes, err := _q.Limit(2).All(setContextOp(ctx, _q.ctx, ent.OpQueryOnly)) + if err != nil { + return nil, err + } + switch len(nodes) { + case 1: + return nodes[0], nil + case 0: + return nil, &NotFoundError{useraccesstoken.Label} + default: + return nil, &NotSingularError{useraccesstoken.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (_q *UserAccessTokenQuery) OnlyX(ctx context.Context) *UserAccessToken { + node, err := _q.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// OnlyID is like Only, but returns the only UserAccessToken ID in the query. +// Returns a *NotSingularError when more than one UserAccessToken ID is found. +// Returns a *NotFoundError when no entities are found. +func (_q *UserAccessTokenQuery) OnlyID(ctx context.Context) (id uuid.UUID, err error) { + var ids []uuid.UUID + if ids, err = _q.Limit(2).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryOnlyID)); err != nil { + return + } + switch len(ids) { + case 1: + id = ids[0] + case 0: + err = &NotFoundError{useraccesstoken.Label} + default: + err = &NotSingularError{useraccesstoken.Label} + } + return +} + +// OnlyIDX is like OnlyID, but panics if an error occurs. +func (_q *UserAccessTokenQuery) OnlyIDX(ctx context.Context) uuid.UUID { + id, err := _q.OnlyID(ctx) + if err != nil { + panic(err) + } + return id +} + +// All executes the query and returns a list of UserAccessTokens. +func (_q *UserAccessTokenQuery) All(ctx context.Context) ([]*UserAccessToken, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryAll) + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*UserAccessToken, *UserAccessTokenQuery]() + return withInterceptors[[]*UserAccessToken](ctx, _q, qr, _q.inters) +} + +// AllX is like All, but panics if an error occurs. +func (_q *UserAccessTokenQuery) AllX(ctx context.Context) []*UserAccessToken { + nodes, err := _q.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// IDs executes the query and returns a list of UserAccessToken IDs. +func (_q *UserAccessTokenQuery) IDs(ctx context.Context) (ids []uuid.UUID, err error) { + if _q.ctx.Unique == nil && _q.path != nil { + _q.Unique(true) + } + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryIDs) + if err = _q.Select(useraccesstoken.FieldID).Scan(ctx, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// IDsX is like IDs, but panics if an error occurs. +func (_q *UserAccessTokenQuery) IDsX(ctx context.Context) []uuid.UUID { + ids, err := _q.IDs(ctx) + if err != nil { + panic(err) + } + return ids +} + +// Count returns the count of the given query. +func (_q *UserAccessTokenQuery) Count(ctx context.Context) (int, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryCount) + if err := _q.prepareQuery(ctx); err != nil { + return 0, err + } + return withInterceptors[int](ctx, _q, querierCount[*UserAccessTokenQuery](), _q.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (_q *UserAccessTokenQuery) CountX(ctx context.Context) int { + count, err := _q.Count(ctx) + if err != nil { + panic(err) + } + return count +} + +// Exist returns true if the query has elements in the graph. +func (_q *UserAccessTokenQuery) Exist(ctx context.Context) (bool, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryExist) + switch _, err := _q.FirstID(ctx); { + case IsNotFound(err): + return false, nil + case err != nil: + return false, fmt.Errorf("ent: check existence: %w", err) + default: + return true, nil + } +} + +// ExistX is like Exist, but panics if an error occurs. +func (_q *UserAccessTokenQuery) ExistX(ctx context.Context) bool { + exist, err := _q.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the UserAccessTokenQuery builder, including all associated steps. It can be +// used to prepare common query builders and use them differently after the clone is made. +func (_q *UserAccessTokenQuery) Clone() *UserAccessTokenQuery { + if _q == nil { + return nil + } + return &UserAccessTokenQuery{ + config: _q.config, + ctx: _q.ctx.Clone(), + order: append([]useraccesstoken.OrderOption{}, _q.order...), + inters: append([]Interceptor{}, _q.inters...), + predicates: append([]predicate.UserAccessToken{}, _q.predicates...), + // clone intermediate query. + sql: _q.sql.Clone(), + path: _q.path, + } +} + +// GroupBy is used to group vertices by one or more fields/columns. +// It is often used with aggregate functions, like: count, max, mean, min, sum. +// +// Example: +// +// var v []struct { +// UserID uuid.UUID `json:"user_id,omitempty"` +// Count int `json:"count,omitempty"` +// } +// +// client.UserAccessToken.Query(). +// GroupBy(useraccesstoken.FieldUserID). +// Aggregate(ent.Count()). +// Scan(ctx, &v) +func (_q *UserAccessTokenQuery) GroupBy(field string, fields ...string) *UserAccessTokenGroupBy { + _q.ctx.Fields = append([]string{field}, fields...) + grbuild := &UserAccessTokenGroupBy{build: _q} + grbuild.flds = &_q.ctx.Fields + grbuild.label = useraccesstoken.Label + grbuild.scan = grbuild.Scan + return grbuild +} + +// Select allows the selection one or more fields/columns for the given query, +// instead of selecting all fields in the entity. +// +// Example: +// +// var v []struct { +// UserID uuid.UUID `json:"user_id,omitempty"` +// } +// +// client.UserAccessToken.Query(). +// Select(useraccesstoken.FieldUserID). +// Scan(ctx, &v) +func (_q *UserAccessTokenQuery) Select(fields ...string) *UserAccessTokenSelect { + _q.ctx.Fields = append(_q.ctx.Fields, fields...) + sbuild := &UserAccessTokenSelect{UserAccessTokenQuery: _q} + sbuild.label = useraccesstoken.Label + sbuild.flds, sbuild.scan = &_q.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a UserAccessTokenSelect configured with the given aggregations. +func (_q *UserAccessTokenQuery) Aggregate(fns ...AggregateFunc) *UserAccessTokenSelect { + return _q.Select().Aggregate(fns...) +} + +func (_q *UserAccessTokenQuery) prepareQuery(ctx context.Context) error { + for _, inter := range _q.inters { + if inter == nil { + return fmt.Errorf("ent: uninitialized interceptor (forgotten import ent/runtime?)") + } + if trv, ok := inter.(Traverser); ok { + if err := trv.Traverse(ctx, _q); err != nil { + return err + } + } + } + for _, f := range _q.ctx.Fields { + if !useraccesstoken.ValidColumn(f) { + return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + } + if _q.path != nil { + prev, err := _q.path(ctx) + if err != nil { + return err + } + _q.sql = prev + } + return nil +} + +func (_q *UserAccessTokenQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*UserAccessToken, error) { + var ( + nodes = []*UserAccessToken{} + _spec = _q.querySpec() + ) + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*UserAccessToken).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &UserAccessToken{config: _q.config} + nodes = append(nodes, node) + return node.assignValues(columns, values) + } + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } + for i := range hooks { + hooks[i](ctx, _spec) + } + if err := sqlgraph.QueryNodes(ctx, _q.driver, _spec); err != nil { + return nil, err + } + if len(nodes) == 0 { + return nodes, nil + } + return nodes, nil +} + +func (_q *UserAccessTokenQuery) sqlCount(ctx context.Context) (int, error) { + _spec := _q.querySpec() + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } + _spec.Node.Columns = _q.ctx.Fields + if len(_q.ctx.Fields) > 0 { + _spec.Unique = _q.ctx.Unique != nil && *_q.ctx.Unique + } + return sqlgraph.CountNodes(ctx, _q.driver, _spec) +} + +func (_q *UserAccessTokenQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(useraccesstoken.Table, useraccesstoken.Columns, sqlgraph.NewFieldSpec(useraccesstoken.FieldID, field.TypeUUID)) + _spec.From = _q.sql + if unique := _q.ctx.Unique; unique != nil { + _spec.Unique = *unique + } else if _q.path != nil { + _spec.Unique = true + } + if fields := _q.ctx.Fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, useraccesstoken.FieldID) + for i := range fields { + if fields[i] != useraccesstoken.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, fields[i]) + } + } + } + if ps := _q.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if limit := _q.ctx.Limit; limit != nil { + _spec.Limit = *limit + } + if offset := _q.ctx.Offset; offset != nil { + _spec.Offset = *offset + } + if ps := _q.order; len(ps) > 0 { + _spec.Order = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + return _spec +} + +func (_q *UserAccessTokenQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(_q.driver.Dialect()) + t1 := builder.Table(useraccesstoken.Table) + columns := _q.ctx.Fields + if len(columns) == 0 { + columns = useraccesstoken.Columns + } + selector := builder.Select(t1.Columns(columns...)...).From(t1) + if _q.sql != nil { + selector = _q.sql + selector.Select(selector.Columns(columns...)...) + } + if _q.ctx.Unique != nil && *_q.ctx.Unique { + selector.Distinct() + } + for _, m := range _q.modifiers { + m(selector) + } + for _, p := range _q.predicates { + p(selector) + } + for _, p := range _q.order { + p(selector) + } + if offset := _q.ctx.Offset; offset != nil { + // limit is mandatory for offset clause. We start + // with default value, and override it below if needed. + selector.Offset(*offset).Limit(math.MaxInt32) + } + if limit := _q.ctx.Limit; limit != nil { + selector.Limit(*limit) + } + return selector +} + +// ForUpdate locks the selected rows against concurrent updates, and prevent them from being +// updated, deleted or "selected ... for update" by other sessions, until the transaction is +// either committed or rolled-back. +func (_q *UserAccessTokenQuery) ForUpdate(opts ...sql.LockOption) *UserAccessTokenQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForUpdate(opts...) + }) + return _q +} + +// ForShare behaves similarly to ForUpdate, except that it acquires a shared mode lock +// on any rows that are read. Other sessions can read the rows, but cannot modify them +// until your transaction commits. +func (_q *UserAccessTokenQuery) ForShare(opts ...sql.LockOption) *UserAccessTokenQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForShare(opts...) + }) + return _q +} + +// UserAccessTokenGroupBy is the group-by builder for UserAccessToken entities. +type UserAccessTokenGroupBy struct { + selector + build *UserAccessTokenQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (_g *UserAccessTokenGroupBy) Aggregate(fns ...AggregateFunc) *UserAccessTokenGroupBy { + _g.fns = append(_g.fns, fns...) + return _g +} + +// Scan applies the selector query and scans the result into the given value. +func (_g *UserAccessTokenGroupBy) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, _g.build.ctx, ent.OpQueryGroupBy) + if err := _g.build.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*UserAccessTokenQuery, *UserAccessTokenGroupBy](ctx, _g.build, _g, _g.build.inters, v) +} + +func (_g *UserAccessTokenGroupBy) sqlScan(ctx context.Context, root *UserAccessTokenQuery, v any) error { + selector := root.sqlQuery(ctx).Select() + aggregation := make([]string, 0, len(_g.fns)) + for _, fn := range _g.fns { + aggregation = append(aggregation, fn(selector)) + } + if len(selector.SelectedColumns()) == 0 { + columns := make([]string, 0, len(*_g.flds)+len(_g.fns)) + for _, f := range *_g.flds { + columns = append(columns, selector.C(f)) + } + columns = append(columns, aggregation...) + selector.Select(columns...) + } + selector.GroupBy(selector.Columns(*_g.flds...)...) + if err := selector.Err(); err != nil { + return err + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := _g.build.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} + +// UserAccessTokenSelect is the builder for selecting fields of UserAccessToken entities. +type UserAccessTokenSelect struct { + *UserAccessTokenQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (_s *UserAccessTokenSelect) Aggregate(fns ...AggregateFunc) *UserAccessTokenSelect { + _s.fns = append(_s.fns, fns...) + return _s +} + +// Scan applies the selector query and scans the result into the given value. +func (_s *UserAccessTokenSelect) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, _s.ctx, ent.OpQuerySelect) + if err := _s.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*UserAccessTokenQuery, *UserAccessTokenSelect](ctx, _s.UserAccessTokenQuery, _s, _s.inters, v) +} + +func (_s *UserAccessTokenSelect) sqlScan(ctx context.Context, root *UserAccessTokenQuery, v any) error { + selector := root.sqlQuery(ctx) + aggregation := make([]string, 0, len(_s.fns)) + for _, fn := range _s.fns { + aggregation = append(aggregation, fn(selector)) + } + switch n := len(*_s.selector.flds); { + case n == 0 && len(aggregation) > 0: + selector.Select(aggregation...) + case n != 0 && len(aggregation) > 0: + selector.AppendSelect(aggregation...) + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := _s.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} diff --git a/pkg/ent/useraccesstoken_update.go b/pkg/ent/useraccesstoken_update.go new file mode 100644 index 000000000..f949cc67c --- /dev/null +++ b/pkg/ent/useraccesstoken_update.go @@ -0,0 +1,575 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/GoogleCloudPlatform/scion/pkg/ent/useraccesstoken" + "github.com/google/uuid" +) + +// UserAccessTokenUpdate is the builder for updating UserAccessToken entities. +type UserAccessTokenUpdate struct { + config + hooks []Hook + mutation *UserAccessTokenMutation +} + +// Where appends a list predicates to the UserAccessTokenUpdate builder. +func (_u *UserAccessTokenUpdate) Where(ps ...predicate.UserAccessToken) *UserAccessTokenUpdate { + _u.mutation.Where(ps...) + return _u +} + +// SetUserID sets the "user_id" field. +func (_u *UserAccessTokenUpdate) SetUserID(v uuid.UUID) *UserAccessTokenUpdate { + _u.mutation.SetUserID(v) + return _u +} + +// SetNillableUserID sets the "user_id" field if the given value is not nil. +func (_u *UserAccessTokenUpdate) SetNillableUserID(v *uuid.UUID) *UserAccessTokenUpdate { + if v != nil { + _u.SetUserID(*v) + } + return _u +} + +// SetName sets the "name" field. +func (_u *UserAccessTokenUpdate) SetName(v string) *UserAccessTokenUpdate { + _u.mutation.SetName(v) + return _u +} + +// SetNillableName sets the "name" field if the given value is not nil. +func (_u *UserAccessTokenUpdate) SetNillableName(v *string) *UserAccessTokenUpdate { + if v != nil { + _u.SetName(*v) + } + return _u +} + +// SetPrefix sets the "prefix" field. +func (_u *UserAccessTokenUpdate) SetPrefix(v string) *UserAccessTokenUpdate { + _u.mutation.SetPrefix(v) + return _u +} + +// SetNillablePrefix sets the "prefix" field if the given value is not nil. +func (_u *UserAccessTokenUpdate) SetNillablePrefix(v *string) *UserAccessTokenUpdate { + if v != nil { + _u.SetPrefix(*v) + } + return _u +} + +// SetKeyHash sets the "key_hash" field. +func (_u *UserAccessTokenUpdate) SetKeyHash(v string) *UserAccessTokenUpdate { + _u.mutation.SetKeyHash(v) + return _u +} + +// SetNillableKeyHash sets the "key_hash" field if the given value is not nil. +func (_u *UserAccessTokenUpdate) SetNillableKeyHash(v *string) *UserAccessTokenUpdate { + if v != nil { + _u.SetKeyHash(*v) + } + return _u +} + +// SetProjectID sets the "project_id" field. +func (_u *UserAccessTokenUpdate) SetProjectID(v uuid.UUID) *UserAccessTokenUpdate { + _u.mutation.SetProjectID(v) + return _u +} + +// SetNillableProjectID sets the "project_id" field if the given value is not nil. +func (_u *UserAccessTokenUpdate) SetNillableProjectID(v *uuid.UUID) *UserAccessTokenUpdate { + if v != nil { + _u.SetProjectID(*v) + } + return _u +} + +// SetScopes sets the "scopes" field. +func (_u *UserAccessTokenUpdate) SetScopes(v string) *UserAccessTokenUpdate { + _u.mutation.SetScopes(v) + return _u +} + +// SetNillableScopes sets the "scopes" field if the given value is not nil. +func (_u *UserAccessTokenUpdate) SetNillableScopes(v *string) *UserAccessTokenUpdate { + if v != nil { + _u.SetScopes(*v) + } + return _u +} + +// SetRevoked sets the "revoked" field. +func (_u *UserAccessTokenUpdate) SetRevoked(v bool) *UserAccessTokenUpdate { + _u.mutation.SetRevoked(v) + return _u +} + +// SetNillableRevoked sets the "revoked" field if the given value is not nil. +func (_u *UserAccessTokenUpdate) SetNillableRevoked(v *bool) *UserAccessTokenUpdate { + if v != nil { + _u.SetRevoked(*v) + } + return _u +} + +// SetExpiresAt sets the "expires_at" field. +func (_u *UserAccessTokenUpdate) SetExpiresAt(v time.Time) *UserAccessTokenUpdate { + _u.mutation.SetExpiresAt(v) + return _u +} + +// SetNillableExpiresAt sets the "expires_at" field if the given value is not nil. +func (_u *UserAccessTokenUpdate) SetNillableExpiresAt(v *time.Time) *UserAccessTokenUpdate { + if v != nil { + _u.SetExpiresAt(*v) + } + return _u +} + +// ClearExpiresAt clears the value of the "expires_at" field. +func (_u *UserAccessTokenUpdate) ClearExpiresAt() *UserAccessTokenUpdate { + _u.mutation.ClearExpiresAt() + return _u +} + +// SetLastUsed sets the "last_used" field. +func (_u *UserAccessTokenUpdate) SetLastUsed(v time.Time) *UserAccessTokenUpdate { + _u.mutation.SetLastUsed(v) + return _u +} + +// SetNillableLastUsed sets the "last_used" field if the given value is not nil. +func (_u *UserAccessTokenUpdate) SetNillableLastUsed(v *time.Time) *UserAccessTokenUpdate { + if v != nil { + _u.SetLastUsed(*v) + } + return _u +} + +// ClearLastUsed clears the value of the "last_used" field. +func (_u *UserAccessTokenUpdate) ClearLastUsed() *UserAccessTokenUpdate { + _u.mutation.ClearLastUsed() + return _u +} + +// Mutation returns the UserAccessTokenMutation object of the builder. +func (_u *UserAccessTokenUpdate) Mutation() *UserAccessTokenMutation { + return _u.mutation +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (_u *UserAccessTokenUpdate) Save(ctx context.Context) (int, error) { + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *UserAccessTokenUpdate) SaveX(ctx context.Context) int { + affected, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (_u *UserAccessTokenUpdate) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *UserAccessTokenUpdate) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_u *UserAccessTokenUpdate) check() error { + if v, ok := _u.mutation.Name(); ok { + if err := useraccesstoken.NameValidator(v); err != nil { + return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "UserAccessToken.name": %w`, err)} + } + } + if v, ok := _u.mutation.Prefix(); ok { + if err := useraccesstoken.PrefixValidator(v); err != nil { + return &ValidationError{Name: "prefix", err: fmt.Errorf(`ent: validator failed for field "UserAccessToken.prefix": %w`, err)} + } + } + if v, ok := _u.mutation.KeyHash(); ok { + if err := useraccesstoken.KeyHashValidator(v); err != nil { + return &ValidationError{Name: "key_hash", err: fmt.Errorf(`ent: validator failed for field "UserAccessToken.key_hash": %w`, err)} + } + } + if v, ok := _u.mutation.Scopes(); ok { + if err := useraccesstoken.ScopesValidator(v); err != nil { + return &ValidationError{Name: "scopes", err: fmt.Errorf(`ent: validator failed for field "UserAccessToken.scopes": %w`, err)} + } + } + return nil +} + +func (_u *UserAccessTokenUpdate) sqlSave(ctx context.Context) (_node int, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(useraccesstoken.Table, useraccesstoken.Columns, sqlgraph.NewFieldSpec(useraccesstoken.FieldID, field.TypeUUID)) + if ps := _u.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := _u.mutation.UserID(); ok { + _spec.SetField(useraccesstoken.FieldUserID, field.TypeUUID, value) + } + if value, ok := _u.mutation.Name(); ok { + _spec.SetField(useraccesstoken.FieldName, field.TypeString, value) + } + if value, ok := _u.mutation.Prefix(); ok { + _spec.SetField(useraccesstoken.FieldPrefix, field.TypeString, value) + } + if value, ok := _u.mutation.KeyHash(); ok { + _spec.SetField(useraccesstoken.FieldKeyHash, field.TypeString, value) + } + if value, ok := _u.mutation.ProjectID(); ok { + _spec.SetField(useraccesstoken.FieldProjectID, field.TypeUUID, value) + } + if value, ok := _u.mutation.Scopes(); ok { + _spec.SetField(useraccesstoken.FieldScopes, field.TypeString, value) + } + if value, ok := _u.mutation.Revoked(); ok { + _spec.SetField(useraccesstoken.FieldRevoked, field.TypeBool, value) + } + if value, ok := _u.mutation.ExpiresAt(); ok { + _spec.SetField(useraccesstoken.FieldExpiresAt, field.TypeTime, value) + } + if _u.mutation.ExpiresAtCleared() { + _spec.ClearField(useraccesstoken.FieldExpiresAt, field.TypeTime) + } + if value, ok := _u.mutation.LastUsed(); ok { + _spec.SetField(useraccesstoken.FieldLastUsed, field.TypeTime, value) + } + if _u.mutation.LastUsedCleared() { + _spec.ClearField(useraccesstoken.FieldLastUsed, field.TypeTime) + } + if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{useraccesstoken.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + _u.mutation.done = true + return _node, nil +} + +// UserAccessTokenUpdateOne is the builder for updating a single UserAccessToken entity. +type UserAccessTokenUpdateOne struct { + config + fields []string + hooks []Hook + mutation *UserAccessTokenMutation +} + +// SetUserID sets the "user_id" field. +func (_u *UserAccessTokenUpdateOne) SetUserID(v uuid.UUID) *UserAccessTokenUpdateOne { + _u.mutation.SetUserID(v) + return _u +} + +// SetNillableUserID sets the "user_id" field if the given value is not nil. +func (_u *UserAccessTokenUpdateOne) SetNillableUserID(v *uuid.UUID) *UserAccessTokenUpdateOne { + if v != nil { + _u.SetUserID(*v) + } + return _u +} + +// SetName sets the "name" field. +func (_u *UserAccessTokenUpdateOne) SetName(v string) *UserAccessTokenUpdateOne { + _u.mutation.SetName(v) + return _u +} + +// SetNillableName sets the "name" field if the given value is not nil. +func (_u *UserAccessTokenUpdateOne) SetNillableName(v *string) *UserAccessTokenUpdateOne { + if v != nil { + _u.SetName(*v) + } + return _u +} + +// SetPrefix sets the "prefix" field. +func (_u *UserAccessTokenUpdateOne) SetPrefix(v string) *UserAccessTokenUpdateOne { + _u.mutation.SetPrefix(v) + return _u +} + +// SetNillablePrefix sets the "prefix" field if the given value is not nil. +func (_u *UserAccessTokenUpdateOne) SetNillablePrefix(v *string) *UserAccessTokenUpdateOne { + if v != nil { + _u.SetPrefix(*v) + } + return _u +} + +// SetKeyHash sets the "key_hash" field. +func (_u *UserAccessTokenUpdateOne) SetKeyHash(v string) *UserAccessTokenUpdateOne { + _u.mutation.SetKeyHash(v) + return _u +} + +// SetNillableKeyHash sets the "key_hash" field if the given value is not nil. +func (_u *UserAccessTokenUpdateOne) SetNillableKeyHash(v *string) *UserAccessTokenUpdateOne { + if v != nil { + _u.SetKeyHash(*v) + } + return _u +} + +// SetProjectID sets the "project_id" field. +func (_u *UserAccessTokenUpdateOne) SetProjectID(v uuid.UUID) *UserAccessTokenUpdateOne { + _u.mutation.SetProjectID(v) + return _u +} + +// SetNillableProjectID sets the "project_id" field if the given value is not nil. +func (_u *UserAccessTokenUpdateOne) SetNillableProjectID(v *uuid.UUID) *UserAccessTokenUpdateOne { + if v != nil { + _u.SetProjectID(*v) + } + return _u +} + +// SetScopes sets the "scopes" field. +func (_u *UserAccessTokenUpdateOne) SetScopes(v string) *UserAccessTokenUpdateOne { + _u.mutation.SetScopes(v) + return _u +} + +// SetNillableScopes sets the "scopes" field if the given value is not nil. +func (_u *UserAccessTokenUpdateOne) SetNillableScopes(v *string) *UserAccessTokenUpdateOne { + if v != nil { + _u.SetScopes(*v) + } + return _u +} + +// SetRevoked sets the "revoked" field. +func (_u *UserAccessTokenUpdateOne) SetRevoked(v bool) *UserAccessTokenUpdateOne { + _u.mutation.SetRevoked(v) + return _u +} + +// SetNillableRevoked sets the "revoked" field if the given value is not nil. +func (_u *UserAccessTokenUpdateOne) SetNillableRevoked(v *bool) *UserAccessTokenUpdateOne { + if v != nil { + _u.SetRevoked(*v) + } + return _u +} + +// SetExpiresAt sets the "expires_at" field. +func (_u *UserAccessTokenUpdateOne) SetExpiresAt(v time.Time) *UserAccessTokenUpdateOne { + _u.mutation.SetExpiresAt(v) + return _u +} + +// SetNillableExpiresAt sets the "expires_at" field if the given value is not nil. +func (_u *UserAccessTokenUpdateOne) SetNillableExpiresAt(v *time.Time) *UserAccessTokenUpdateOne { + if v != nil { + _u.SetExpiresAt(*v) + } + return _u +} + +// ClearExpiresAt clears the value of the "expires_at" field. +func (_u *UserAccessTokenUpdateOne) ClearExpiresAt() *UserAccessTokenUpdateOne { + _u.mutation.ClearExpiresAt() + return _u +} + +// SetLastUsed sets the "last_used" field. +func (_u *UserAccessTokenUpdateOne) SetLastUsed(v time.Time) *UserAccessTokenUpdateOne { + _u.mutation.SetLastUsed(v) + return _u +} + +// SetNillableLastUsed sets the "last_used" field if the given value is not nil. +func (_u *UserAccessTokenUpdateOne) SetNillableLastUsed(v *time.Time) *UserAccessTokenUpdateOne { + if v != nil { + _u.SetLastUsed(*v) + } + return _u +} + +// ClearLastUsed clears the value of the "last_used" field. +func (_u *UserAccessTokenUpdateOne) ClearLastUsed() *UserAccessTokenUpdateOne { + _u.mutation.ClearLastUsed() + return _u +} + +// Mutation returns the UserAccessTokenMutation object of the builder. +func (_u *UserAccessTokenUpdateOne) Mutation() *UserAccessTokenMutation { + return _u.mutation +} + +// Where appends a list predicates to the UserAccessTokenUpdate builder. +func (_u *UserAccessTokenUpdateOne) Where(ps ...predicate.UserAccessToken) *UserAccessTokenUpdateOne { + _u.mutation.Where(ps...) + return _u +} + +// Select allows selecting one or more fields (columns) of the returned entity. +// The default is selecting all fields defined in the entity schema. +func (_u *UserAccessTokenUpdateOne) Select(field string, fields ...string) *UserAccessTokenUpdateOne { + _u.fields = append([]string{field}, fields...) + return _u +} + +// Save executes the query and returns the updated UserAccessToken entity. +func (_u *UserAccessTokenUpdateOne) Save(ctx context.Context) (*UserAccessToken, error) { + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *UserAccessTokenUpdateOne) SaveX(ctx context.Context) *UserAccessToken { + node, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (_u *UserAccessTokenUpdateOne) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *UserAccessTokenUpdateOne) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_u *UserAccessTokenUpdateOne) check() error { + if v, ok := _u.mutation.Name(); ok { + if err := useraccesstoken.NameValidator(v); err != nil { + return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "UserAccessToken.name": %w`, err)} + } + } + if v, ok := _u.mutation.Prefix(); ok { + if err := useraccesstoken.PrefixValidator(v); err != nil { + return &ValidationError{Name: "prefix", err: fmt.Errorf(`ent: validator failed for field "UserAccessToken.prefix": %w`, err)} + } + } + if v, ok := _u.mutation.KeyHash(); ok { + if err := useraccesstoken.KeyHashValidator(v); err != nil { + return &ValidationError{Name: "key_hash", err: fmt.Errorf(`ent: validator failed for field "UserAccessToken.key_hash": %w`, err)} + } + } + if v, ok := _u.mutation.Scopes(); ok { + if err := useraccesstoken.ScopesValidator(v); err != nil { + return &ValidationError{Name: "scopes", err: fmt.Errorf(`ent: validator failed for field "UserAccessToken.scopes": %w`, err)} + } + } + return nil +} + +func (_u *UserAccessTokenUpdateOne) sqlSave(ctx context.Context) (_node *UserAccessToken, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(useraccesstoken.Table, useraccesstoken.Columns, sqlgraph.NewFieldSpec(useraccesstoken.FieldID, field.TypeUUID)) + id, ok := _u.mutation.ID() + if !ok { + return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "UserAccessToken.id" for update`)} + } + _spec.Node.ID.Value = id + if fields := _u.fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, useraccesstoken.FieldID) + for _, f := range fields { + if !useraccesstoken.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + if f != useraccesstoken.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, f) + } + } + } + if ps := _u.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := _u.mutation.UserID(); ok { + _spec.SetField(useraccesstoken.FieldUserID, field.TypeUUID, value) + } + if value, ok := _u.mutation.Name(); ok { + _spec.SetField(useraccesstoken.FieldName, field.TypeString, value) + } + if value, ok := _u.mutation.Prefix(); ok { + _spec.SetField(useraccesstoken.FieldPrefix, field.TypeString, value) + } + if value, ok := _u.mutation.KeyHash(); ok { + _spec.SetField(useraccesstoken.FieldKeyHash, field.TypeString, value) + } + if value, ok := _u.mutation.ProjectID(); ok { + _spec.SetField(useraccesstoken.FieldProjectID, field.TypeUUID, value) + } + if value, ok := _u.mutation.Scopes(); ok { + _spec.SetField(useraccesstoken.FieldScopes, field.TypeString, value) + } + if value, ok := _u.mutation.Revoked(); ok { + _spec.SetField(useraccesstoken.FieldRevoked, field.TypeBool, value) + } + if value, ok := _u.mutation.ExpiresAt(); ok { + _spec.SetField(useraccesstoken.FieldExpiresAt, field.TypeTime, value) + } + if _u.mutation.ExpiresAtCleared() { + _spec.ClearField(useraccesstoken.FieldExpiresAt, field.TypeTime) + } + if value, ok := _u.mutation.LastUsed(); ok { + _spec.SetField(useraccesstoken.FieldLastUsed, field.TypeTime, value) + } + if _u.mutation.LastUsedCleared() { + _spec.ClearField(useraccesstoken.FieldLastUsed, field.TypeTime) + } + _node = &UserAccessToken{config: _u.config} + _spec.Assign = _node.assignValues + _spec.ScanValues = _node.scanValues + if err = sqlgraph.UpdateNode(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{useraccesstoken.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + _u.mutation.done = true + return _node, nil +} diff --git a/pkg/hub/admin_maintenance_test.go b/pkg/hub/admin_maintenance_test.go index 185979206..ca0a0012b 100644 --- a/pkg/hub/admin_maintenance_test.go +++ b/pkg/hub/admin_maintenance_test.go @@ -27,13 +27,12 @@ import ( "time" "github.com/GoogleCloudPlatform/scion/pkg/store" - "github.com/GoogleCloudPlatform/scion/pkg/store/sqlite" "github.com/GoogleCloudPlatform/scion/pkg/util/logging" ) func newTestServerWithStore(t *testing.T) (*Server, store.Store) { t.Helper() - s, err := sqlite.New(":memory:") + s, err := newTestStore(":memory:") if err != nil { t.Fatalf("failed to create sqlite store: %v", err) } @@ -349,7 +348,7 @@ func TestListOperationRuns(t *testing.T) { completed := time.Now().Add(10 * time.Second) for i, status := range []string{"completed", "failed"} { run := &store.MaintenanceOperationRun{ - ID: fmt.Sprintf("run-%d", i), + ID: tid(fmt.Sprintf("run-%d", i)), OperationKey: "pull-images", Status: status, StartedAt: now, @@ -406,7 +405,7 @@ func TestGetOperationRun(t *testing.T) { now := time.Now() completed := now.Add(10 * time.Second) run := &store.MaintenanceOperationRun{ - ID: "run-detail-1", + ID: tid("run-detail-1"), OperationKey: "pull-images", Status: "completed", StartedAt: now, @@ -419,7 +418,7 @@ func TestGetOperationRun(t *testing.T) { } admin := NewAuthenticatedUser("u1", "admin@example.com", "Admin", "admin", "cli") - req := httptest.NewRequest(http.MethodGet, "/api/v1/admin/maintenance/operations/pull-images/runs/run-detail-1", nil) + req := httptest.NewRequest(http.MethodGet, fmt.Sprintf("/api/v1/admin/maintenance/operations/pull-images/runs/%s", tid("run-detail-1")), nil) req = req.WithContext(contextWithIdentity(req.Context(), admin)) rr := httptest.NewRecorder() srv.handleAdminMaintenanceOps(rr, req) @@ -432,7 +431,7 @@ func TestGetOperationRun(t *testing.T) { if err := json.Unmarshal(rr.Body.Bytes(), &resp); err != nil { t.Fatalf("invalid JSON: %v", err) } - if resp["id"] != "run-detail-1" { + if resp["id"] != tid("run-detail-1") { t.Errorf("expected id=run-detail-1, got %v", resp["id"]) } if resp["log"] != "Pulling images...\nDone." { diff --git a/pkg/hub/admin_mode_test.go b/pkg/hub/admin_mode_test.go index 2bde40125..b2fbb2252 100644 --- a/pkg/hub/admin_mode_test.go +++ b/pkg/hub/admin_mode_test.go @@ -150,8 +150,8 @@ func TestAdminModeMiddleware_AgentIdentity(t *testing.T) { mw := adminModeMiddleware(state)(passthrough) agent := &agentIdentityWrapper{&AgentTokenClaims{ - Claims: jwt.Claims{Subject: "agent-1"}, - ProjectID: "project-1", + Claims: jwt.Claims{Subject: tid("agent-1")}, + ProjectID: tid("project-1"), }} req := httptest.NewRequest(http.MethodGet, "/api/v1/agents", nil) req = req.WithContext(contextWithIdentity(req.Context(), agent)) @@ -167,7 +167,7 @@ func TestAdminModeMiddleware_BrokerIdentity(t *testing.T) { state := NewMaintenanceState(true, "") mw := adminModeMiddleware(state)(passthrough) - broker := NewBrokerIdentity("broker-1") + broker := NewBrokerIdentity(tid("broker-1")) req := httptest.NewRequest(http.MethodGet, "/api/v1/agents", nil) ctx := contextWithIdentity(req.Context(), broker) ctx = contextWithBrokerIdentity(ctx, broker) diff --git a/pkg/hub/agenttoken_test.go b/pkg/hub/agenttoken_test.go index 0f4120d6b..3b4ff54d3 100644 --- a/pkg/hub/agenttoken_test.go +++ b/pkg/hub/agenttoken_test.go @@ -118,7 +118,7 @@ func TestAgentTokenService_AgentCreateAndLifecycleScopes(t *testing.T) { require.NoError(t, err) // Generate a token with agent create and lifecycle scopes - token, err := service.GenerateAgentToken("agent-sub", "project-parent", []AgentTokenScope{ + token, err := service.GenerateAgentToken("agent-sub", tid("project-parent"), []AgentTokenScope{ ScopeAgentStatusUpdate, ScopeAgentCreate, ScopeAgentLifecycle, @@ -130,7 +130,7 @@ func TestAgentTokenService_AgentCreateAndLifecycleScopes(t *testing.T) { claims, err := service.ValidateAgentToken(token) require.NoError(t, err) assert.Equal(t, "agent-sub", claims.Subject) - assert.Equal(t, "project-parent", claims.ProjectID) + assert.Equal(t, tid("project-parent"), claims.ProjectID) assert.True(t, claims.HasScope(ScopeAgentStatusUpdate)) assert.True(t, claims.HasScope(ScopeAgentCreate)) assert.True(t, claims.HasScope(ScopeAgentLifecycle)) @@ -396,7 +396,7 @@ func TestGCPTokenScope_InToken(t *testing.T) { gcpScope := GCPTokenScopeForSA(saID) scopes := []AgentTokenScope{ScopeAgentStatusUpdate, gcpScope} - token, err := service.GenerateAgentToken("agent-1", "project-1", scopes, nil) + token, err := service.GenerateAgentToken(tid("agent-1"), tid("project-1"), scopes, nil) require.NoError(t, err) claims, err := service.ValidateAgentToken(token) diff --git a/pkg/hub/audit_gcp_test.go b/pkg/hub/audit_gcp_test.go index ca0b3dfbb..f33a613cd 100644 --- a/pkg/hub/audit_gcp_test.go +++ b/pkg/hub/audit_gcp_test.go @@ -112,8 +112,8 @@ func TestLogAuditLogger_LogGCPTokenEvent(t *testing.T) { // Should not error for success event err := logger.LogGCPTokenEvent(context.Background(), &GCPTokenEvent{ EventType: GCPTokenEventAccessToken, - AgentID: "agent-1", - ProjectID: "project-1", + AgentID: tid("agent-1"), + ProjectID: tid("project-1"), ServiceAccountEmail: "sa@proj.iam.gserviceaccount.com", Success: true, }) @@ -124,8 +124,8 @@ func TestLogAuditLogger_LogGCPTokenEvent(t *testing.T) { // Should not error for failure event err = logger.LogGCPTokenEvent(context.Background(), &GCPTokenEvent{ EventType: GCPTokenEventIdentityToken, - AgentID: "agent-1", - ProjectID: "project-1", + AgentID: tid("agent-1"), + ProjectID: tid("project-1"), ServiceAccountEmail: "sa@proj.iam.gserviceaccount.com", Success: false, FailReason: "permission denied", diff --git a/pkg/hub/authz_integration_test.go b/pkg/hub/authz_integration_test.go index 5ba4e8a5e..ae608796d 100644 --- a/pkg/hub/authz_integration_test.go +++ b/pkg/hub/authz_integration_test.go @@ -38,7 +38,7 @@ func TestEvaluateEndpoint_UserDirectPolicy(t *testing.T) { // Create user require.NoError(t, s.CreateUser(ctx, &store.User{ - ID: "eval-user-1", Email: "eval1@test.com", DisplayName: "Eval User", Role: "member", Status: "active", + ID: tid("eval-user-1"), Email: "eval1@test.com", DisplayName: "Eval User", Role: "member", Status: "active", })) // Create policy via API @@ -58,7 +58,7 @@ func TestEvaluateEndpoint_UserDirectPolicy(t *testing.T) { // Add binding via API bindReq := AddPolicyBindingRequest{ PrincipalType: "user", - PrincipalID: "eval-user-1", + PrincipalID: tid("eval-user-1"), } rec = doRequest(t, srv, http.MethodPost, "/api/v1/policies/"+createdPolicy.ID+"/bindings", bindReq) require.Equal(t, http.StatusCreated, rec.Code, rec.Body.String()) @@ -66,9 +66,9 @@ func TestEvaluateEndpoint_UserDirectPolicy(t *testing.T) { // Evaluate via API evalReq := EvaluateRequest{ PrincipalType: "user", - PrincipalID: "eval-user-1", + PrincipalID: tid("eval-user-1"), ResourceType: "agent", - ResourceID: "agent-1", + ResourceID: tid("agent-1"), Action: "read", } rec = doRequest(t, srv, http.MethodPost, "/api/v1/policies/evaluate", evalReq) @@ -85,12 +85,12 @@ func TestEvaluateEndpoint_DefaultDeny(t *testing.T) { ctx := context.Background() require.NoError(t, s.CreateUser(ctx, &store.User{ - ID: "eval-user-none", Email: "none@test.com", DisplayName: "No Policy", Role: "member", Status: "active", + ID: tid("eval-user-none"), Email: "none@test.com", DisplayName: "No Policy", Role: "member", Status: "active", })) evalReq := EvaluateRequest{ PrincipalType: "user", - PrincipalID: "eval-user-none", + PrincipalID: tid("eval-user-none"), ResourceType: "agent", Action: "delete", } @@ -108,33 +108,33 @@ func TestEvaluateEndpoint_ScopeOverride(t *testing.T) { ctx := context.Background() require.NoError(t, s.CreateUser(ctx, &store.User{ - ID: "eval-user-scope", Email: "scope@test.com", DisplayName: "Scope User", Role: "member", Status: "active", + ID: tid("eval-user-scope"), Email: "scope@test.com", DisplayName: "Scope User", Role: "member", Status: "active", })) // Create hub-level deny hubPolicy := &store.Policy{ - ID: "hub-deny-1", Name: "Hub Deny", ScopeType: "hub", + ID: tid("hub-deny-1"), Name: "Hub Deny", ScopeType: "hub", ResourceType: "agent", Actions: []string{"read"}, Effect: "deny", } require.NoError(t, s.CreatePolicy(ctx, hubPolicy)) require.NoError(t, s.AddPolicyBinding(ctx, &store.PolicyBinding{ - PolicyID: "hub-deny-1", PrincipalType: "user", PrincipalID: "eval-user-scope", + PolicyID: tid("hub-deny-1"), PrincipalType: "user", PrincipalID: tid("eval-user-scope"), })) // Create project-level allow (should override hub deny) projectPolicy := &store.Policy{ - ID: "project-allow-1", Name: "Project Allow", ScopeType: "project", + ID: tid("project-allow-1"), Name: "Project Allow", ScopeType: "project", ScopeID: "project-scope-1", ResourceType: "agent", Actions: []string{"read"}, Effect: "allow", } require.NoError(t, s.CreatePolicy(ctx, projectPolicy)) require.NoError(t, s.AddPolicyBinding(ctx, &store.PolicyBinding{ - PolicyID: "project-allow-1", PrincipalType: "user", PrincipalID: "eval-user-scope", + PolicyID: tid("project-allow-1"), PrincipalType: "user", PrincipalID: tid("eval-user-scope"), })) evalReq := EvaluateRequest{ PrincipalType: "user", - PrincipalID: "eval-user-scope", + PrincipalID: tid("eval-user-scope"), ResourceType: "agent", Action: "read", } @@ -153,26 +153,26 @@ func TestEvaluateEndpoint_AgentPolicy(t *testing.T) { // Create project and agent require.NoError(t, s.CreateProject(ctx, &store.Project{ - ID: "project-eval", Name: "Eval Project", Slug: "project-eval", + ID: tid("project-eval"), Name: "Eval Project", Slug: tid("project-eval"), })) require.NoError(t, s.CreateAgent(ctx, &store.Agent{ - ID: "agent-eval", Slug: "agent-eval", Name: "Eval Agent", - ProjectID: "project-eval", Phase: string(state.PhaseRunning), + ID: tid("agent-eval"), Slug: tid("agent-eval"), Name: "Eval Agent", + ProjectID: tid("project-eval"), Phase: string(state.PhaseRunning), })) // Create and bind policy to agent policy := &store.Policy{ - ID: "agent-policy-eval", Name: "Agent Read", ScopeType: "hub", + ID: tid("agent-policy-eval"), Name: "Agent Read", ScopeType: "hub", ResourceType: "project", Actions: []string{"read"}, Effect: "allow", } require.NoError(t, s.CreatePolicy(ctx, policy)) require.NoError(t, s.AddPolicyBinding(ctx, &store.PolicyBinding{ - PolicyID: "agent-policy-eval", PrincipalType: "agent", PrincipalID: "agent-eval", + PolicyID: tid("agent-policy-eval"), PrincipalType: "agent", PrincipalID: tid("agent-eval"), })) evalReq := EvaluateRequest{ PrincipalType: "agent", - PrincipalID: "agent-eval", + PrincipalID: tid("agent-eval"), ResourceType: "project", Action: "read", } @@ -190,11 +190,11 @@ func TestEvaluateEndpoint_AgentBinding(t *testing.T) { // Create project and agent require.NoError(t, s.CreateProject(ctx, &store.Project{ - ID: "project-bind", Name: "Bind Project", Slug: "project-bind", + ID: tid("project-bind"), Name: "Bind Project", Slug: tid("project-bind"), })) require.NoError(t, s.CreateAgent(ctx, &store.Agent{ - ID: "agent-bind", Slug: "agent-bind", Name: "Bind Agent", - ProjectID: "project-bind", Phase: string(state.PhaseRunning), + ID: tid("agent-bind"), Slug: tid("agent-bind"), Name: "Bind Agent", + ProjectID: tid("project-bind"), Phase: string(state.PhaseRunning), })) // Create policy via API @@ -214,7 +214,7 @@ func TestEvaluateEndpoint_AgentBinding(t *testing.T) { // Bind to agent (tests that "agent" is now a valid principal type) bindReq := AddPolicyBindingRequest{ PrincipalType: "agent", - PrincipalID: "agent-bind", + PrincipalID: tid("agent-bind"), } rec = doRequest(t, srv, http.MethodPost, "/api/v1/policies/"+createdPolicy.ID+"/bindings", bindReq) require.Equal(t, http.StatusCreated, rec.Code, rec.Body.String()) diff --git a/pkg/hub/authz_project_owner_test.go b/pkg/hub/authz_project_owner_test.go index 97a1ab64c..404b3a800 100644 --- a/pkg/hub/authz_project_owner_test.go +++ b/pkg/hub/authz_project_owner_test.go @@ -85,14 +85,14 @@ func TestAuthz_ProjectOwnerBypass_NonCreatorAdminCanDeleteAgent(t *testing.T) { ctx := context.Background() // Bob joins the project as admin (not creator, not direct OwnerID). - bob := makeProjectMemberUser(t, s, project, "user-bob-admin", "Bob Admin", store.GroupMemberRoleAdmin) + bob := makeProjectMemberUser(t, s, project, tid("user-bob-admin"), "Bob Admin", store.GroupMemberRoleAdmin) // Alice creates the agent. require.NoError(t, s.CreateAgent(ctx, &store.Agent{ - ID: "alice-agent-1", Slug: "alice-agent-1", Name: "Alice Agent", + ID: tid("alice-agent-1"), Slug: tid("alice-agent-1"), Name: "Alice Agent", ProjectID: project.ID, OwnerID: alice.ID, Phase: string(state.PhaseRunning), })) - a, err := s.GetAgent(ctx, "alice-agent-1") + a, err := s.GetAgent(ctx, tid("alice-agent-1")) require.NoError(t, err) user := NewAuthenticatedUser(bob.ID, bob.Email, bob.DisplayName, "member", "api") @@ -105,7 +105,7 @@ func TestAuthz_ProjectOwnerBypass_RegularMemberCannotUpdateProject(t *testing.T) srv, s, _, _, project := setupDemoPolicyTest(t) ctx := context.Background() - carol := makeProjectMemberUser(t, s, project, "user-carol-member", "Carol", store.GroupMemberRoleMember) + carol := makeProjectMemberUser(t, s, project, tid("user-carol-member"), "Carol", store.GroupMemberRoleMember) user := NewAuthenticatedUser(carol.ID, carol.Email, carol.DisplayName, "member", "api") decision := srv.authzService.CheckAccess(ctx, user, projectResource(project), ActionUpdate) @@ -116,14 +116,14 @@ func TestAuthz_ProjectOwnerBypass_RegularMemberCannotDeleteOthersAgent(t *testin srv, s, alice, _, project := setupDemoPolicyTest(t) ctx := context.Background() - carol := makeProjectMemberUser(t, s, project, "user-carol-member", "Carol", store.GroupMemberRoleMember) + carol := makeProjectMemberUser(t, s, project, tid("user-carol-member"), "Carol", store.GroupMemberRoleMember) // Alice creates the agent; carol is just a regular member. require.NoError(t, s.CreateAgent(ctx, &store.Agent{ - ID: "alice-agent-2", Slug: "alice-agent-2", Name: "Alice Agent 2", + ID: tid("alice-agent-2"), Slug: tid("alice-agent-2"), Name: "Alice Agent 2", ProjectID: project.ID, OwnerID: alice.ID, Phase: string(state.PhaseRunning), })) - a, err := s.GetAgent(ctx, "alice-agent-2") + a, err := s.GetAgent(ctx, tid("alice-agent-2")) require.NoError(t, err) user := NewAuthenticatedUser(carol.ID, carol.Email, carol.DisplayName, "member", "api") @@ -146,7 +146,7 @@ func TestAuthz_ProjectOwnerBypass_AppliesToProjectMembersGroup(t *testing.T) { srv, s, _, _, project := setupDemoPolicyTest(t) ctx := context.Background() - bob := makeProjectMemberUser(t, s, project, "user-bob-owner", "Bob Owner", store.GroupMemberRoleOwner) + bob := makeProjectMemberUser(t, s, project, tid("user-bob-owner"), "Bob Owner", store.GroupMemberRoleOwner) membersGroup, err := s.GetGroupBySlug(ctx, "project:"+project.Slug+":members") require.NoError(t, err) @@ -165,7 +165,7 @@ func TestCapabilities_ProjectOwnerBypass_ProjectAllActions(t *testing.T) { srv, s, _, _, project := setupDemoPolicyTest(t) ctx := context.Background() - bob := makeProjectMemberUser(t, s, project, "user-bob-cap", "Bob", store.GroupMemberRoleOwner) + bob := makeProjectMemberUser(t, s, project, tid("user-bob-cap"), "Bob", store.GroupMemberRoleOwner) user := NewAuthenticatedUser(bob.ID, bob.Email, bob.DisplayName, "member", "api") caps := srv.authzService.ComputeCapabilities(ctx, user, projectResource(project)) @@ -179,13 +179,13 @@ func TestCapabilities_ProjectOwnerBypass_AgentAllActions(t *testing.T) { srv, s, alice, _, project := setupDemoPolicyTest(t) ctx := context.Background() - bob := makeProjectMemberUser(t, s, project, "user-bob-cap-a", "Bob", store.GroupMemberRoleOwner) + bob := makeProjectMemberUser(t, s, project, tid("user-bob-cap-a"), "Bob", store.GroupMemberRoleOwner) require.NoError(t, s.CreateAgent(ctx, &store.Agent{ - ID: "alice-agent-cap", Slug: "alice-agent-cap", Name: "Alice Agent Cap", + ID: tid("alice-agent-cap"), Slug: tid("alice-agent-cap"), Name: "Alice Agent Cap", ProjectID: project.ID, OwnerID: alice.ID, Phase: string(state.PhaseRunning), })) - a, err := s.GetAgent(ctx, "alice-agent-cap") + a, err := s.GetAgent(ctx, tid("alice-agent-cap")) require.NoError(t, err) user := NewAuthenticatedUser(bob.ID, bob.Email, bob.DisplayName, "member", "api") @@ -200,21 +200,21 @@ func TestCapabilities_ProjectOwnerBypass_BatchAllActions(t *testing.T) { srv, s, alice, _, project := setupDemoPolicyTest(t) ctx := context.Background() - bob := makeProjectMemberUser(t, s, project, "user-bob-batch", "Bob", store.GroupMemberRoleOwner) + bob := makeProjectMemberUser(t, s, project, tid("user-bob-batch"), "Bob", store.GroupMemberRoleOwner) // Two agents: one owned by alice, one by bob. require.NoError(t, s.CreateAgent(ctx, &store.Agent{ - ID: "agent-alice-b", Slug: "agent-alice-b", Name: "AliceB", + ID: tid("agent-alice-b"), Slug: tid("agent-alice-b"), Name: "AliceB", ProjectID: project.ID, OwnerID: alice.ID, Phase: string(state.PhaseRunning), })) require.NoError(t, s.CreateAgent(ctx, &store.Agent{ - ID: "agent-bob-b", Slug: "agent-bob-b", Name: "BobB", + ID: tid("agent-bob-b"), Slug: tid("agent-bob-b"), Name: "BobB", ProjectID: project.ID, OwnerID: bob.ID, Phase: string(state.PhaseRunning), })) - a1, err := s.GetAgent(ctx, "agent-alice-b") + a1, err := s.GetAgent(ctx, tid("agent-alice-b")) require.NoError(t, err) - a2, err := s.GetAgent(ctx, "agent-bob-b") + a2, err := s.GetAgent(ctx, tid("agent-bob-b")) require.NoError(t, err) user := NewAuthenticatedUser(bob.ID, bob.Email, bob.DisplayName, "member", "api") @@ -233,7 +233,7 @@ func TestCapabilities_ProjectOwnerBypass_ScopeAllActions(t *testing.T) { srv, s, _, _, project := setupDemoPolicyTest(t) ctx := context.Background() - bob := makeProjectMemberUser(t, s, project, "user-bob-scope", "Bob", store.GroupMemberRoleOwner) + bob := makeProjectMemberUser(t, s, project, tid("user-bob-scope"), "Bob", store.GroupMemberRoleOwner) user := NewAuthenticatedUser(bob.ID, bob.Email, bob.DisplayName, "member", "api") caps := srv.authzService.ComputeScopeCapabilities(ctx, user, "project", project.ID, "agent") @@ -247,13 +247,13 @@ func TestCapabilities_RegularMember_AgentLimitedActions(t *testing.T) { srv, s, alice, _, project := setupDemoPolicyTest(t) ctx := context.Background() - carol := makeProjectMemberUser(t, s, project, "user-carol-cap", "Carol", store.GroupMemberRoleMember) + carol := makeProjectMemberUser(t, s, project, tid("user-carol-cap"), "Carol", store.GroupMemberRoleMember) require.NoError(t, s.CreateAgent(ctx, &store.Agent{ - ID: "alice-agent-cap2", Slug: "alice-agent-cap2", Name: "Alice Agent Cap2", + ID: tid("alice-agent-cap2"), Slug: tid("alice-agent-cap2"), Name: "Alice Agent Cap2", ProjectID: project.ID, OwnerID: alice.ID, Phase: string(state.PhaseRunning), })) - a, err := s.GetAgent(ctx, "alice-agent-cap2") + a, err := s.GetAgent(ctx, tid("alice-agent-cap2")) require.NoError(t, err) user := NewAuthenticatedUser(carol.ID, carol.Email, carol.DisplayName, "member", "api") @@ -270,7 +270,7 @@ func TestCapabilities_RegularMember_AgentLimitedActions(t *testing.T) { func TestUpdateProject_NonCreatorOwnerAllowed(t *testing.T) { srv, s, _, _, project := setupDemoPolicyTest(t) - bob := makeProjectMemberUser(t, s, project, "user-bob-http-owner", "Bob HTTP", store.GroupMemberRoleOwner) + bob := makeProjectMemberUser(t, s, project, tid("user-bob-http-owner"), "Bob HTTP", store.GroupMemberRoleOwner) body := map[string]string{"description": "updated by bob"} rec := doRequestAsUser(t, srv, bob, http.MethodPatch, "/api/v1/projects/"+project.ID, body) @@ -280,7 +280,7 @@ func TestUpdateProject_NonCreatorOwnerAllowed(t *testing.T) { func TestUpdateProject_RegularMemberDenied(t *testing.T) { srv, s, _, _, project := setupDemoPolicyTest(t) - carol := makeProjectMemberUser(t, s, project, "user-carol-http", "Carol HTTP", store.GroupMemberRoleMember) + carol := makeProjectMemberUser(t, s, project, tid("user-carol-http"), "Carol HTTP", store.GroupMemberRoleMember) body := map[string]string{"description": "updated by carol"} rec := doRequestAsUser(t, srv, carol, http.MethodPatch, "/api/v1/projects/"+project.ID, body) diff --git a/pkg/hub/authz_test.go b/pkg/hub/authz_test.go index 4d05672ed..2831bf221 100644 --- a/pkg/hub/authz_test.go +++ b/pkg/hub/authz_test.go @@ -53,11 +53,11 @@ func TestAuthz_OwnerBypass(t *testing.T) { // Create a user require.NoError(t, s.CreateUser(ctx, &store.User{ - ID: "user-owner", Email: "owner@test.com", DisplayName: "Owner", Role: "member", Status: "active", + ID: tid("user-owner"), Email: "owner@test.com", DisplayName: "Owner", Role: "member", Status: "active", })) - user := NewAuthenticatedUser("user-owner", "owner@test.com", "Owner", "member", "api") - resource := Resource{Type: "agent", ID: "agent-1", OwnerID: "user-owner"} + user := NewAuthenticatedUser(tid("user-owner"), "owner@test.com", "Owner", "member", "api") + resource := Resource{Type: "agent", ID: tid("agent-1"), OwnerID: tid("user-owner")} decision := authz.CheckAccess(ctx, user, resource, ActionDelete) assert.True(t, decision.Allowed) @@ -70,27 +70,27 @@ func TestAuthz_DirectUserPolicy(t *testing.T) { // Create user require.NoError(t, s.CreateUser(ctx, &store.User{ - ID: "user-1", Email: "user1@test.com", DisplayName: "User 1", Role: "member", Status: "active", + ID: tid("user-1"), Email: "user1@test.com", DisplayName: "User 1", Role: "member", Status: "active", })) // Create policy allowing read policy := &store.Policy{ - ID: "policy-1", Name: "Allow Read", ScopeType: "hub", + ID: tid("policy-1"), Name: "Allow Read", ScopeType: "hub", ResourceType: "agent", Actions: []string{"read"}, Effect: "allow", } require.NoError(t, s.CreatePolicy(ctx, policy)) // Bind to user require.NoError(t, s.AddPolicyBinding(ctx, &store.PolicyBinding{ - PolicyID: "policy-1", PrincipalType: "user", PrincipalID: "user-1", + PolicyID: tid("policy-1"), PrincipalType: "user", PrincipalID: tid("user-1"), })) - user := NewAuthenticatedUser("user-1", "user1@test.com", "User 1", "member", "api") - resource := Resource{Type: "agent", ID: "agent-1"} + user := NewAuthenticatedUser(tid("user-1"), "user1@test.com", "User 1", "member", "api") + resource := Resource{Type: "agent", ID: tid("agent-1")} decision := authz.CheckAccess(ctx, user, resource, ActionRead) assert.True(t, decision.Allowed) - assert.Equal(t, "policy-1", decision.PolicyID) + assert.Equal(t, tid("policy-1"), decision.PolicyID) } func TestAuthz_DefaultDeny(t *testing.T) { @@ -98,11 +98,11 @@ func TestAuthz_DefaultDeny(t *testing.T) { ctx := context.Background() require.NoError(t, s.CreateUser(ctx, &store.User{ - ID: "user-nodeny", Email: "nodeny@test.com", DisplayName: "NoDeny", Role: "member", Status: "active", + ID: tid("user-nodeny"), Email: "nodeny@test.com", DisplayName: "NoDeny", Role: "member", Status: "active", })) - user := NewAuthenticatedUser("user-nodeny", "nodeny@test.com", "NoDeny", "member", "api") - resource := Resource{Type: "agent", ID: "agent-1"} + user := NewAuthenticatedUser(tid("user-nodeny"), "nodeny@test.com", "NoDeny", "member", "api") + resource := Resource{Type: "agent", ID: tid("agent-1")} decision := authz.CheckAccess(ctx, user, resource, ActionDelete) assert.False(t, decision.Allowed) @@ -114,24 +114,24 @@ func TestAuthz_DenyEffect(t *testing.T) { ctx := context.Background() require.NoError(t, s.CreateUser(ctx, &store.User{ - ID: "user-deny", Email: "deny@test.com", DisplayName: "Deny", Role: "member", Status: "active", + ID: tid("user-deny"), Email: "deny@test.com", DisplayName: "Deny", Role: "member", Status: "active", })) policy := &store.Policy{ - ID: "policy-deny", Name: "Deny Write", ScopeType: "hub", + ID: tid("policy-deny"), Name: "Deny Write", ScopeType: "hub", ResourceType: "agent", Actions: []string{"update"}, Effect: "deny", } require.NoError(t, s.CreatePolicy(ctx, policy)) require.NoError(t, s.AddPolicyBinding(ctx, &store.PolicyBinding{ - PolicyID: "policy-deny", PrincipalType: "user", PrincipalID: "user-deny", + PolicyID: tid("policy-deny"), PrincipalType: "user", PrincipalID: tid("user-deny"), })) - user := NewAuthenticatedUser("user-deny", "deny@test.com", "Deny", "member", "api") - resource := Resource{Type: "agent", ID: "agent-1"} + user := NewAuthenticatedUser(tid("user-deny"), "deny@test.com", "Deny", "member", "api") + resource := Resource{Type: "agent", ID: tid("agent-1")} decision := authz.CheckAccess(ctx, user, resource, ActionUpdate) assert.False(t, decision.Allowed) - assert.Equal(t, "policy-deny", decision.PolicyID) + assert.Equal(t, tid("policy-deny"), decision.PolicyID) } func TestAuthz_WildcardAction(t *testing.T) { @@ -139,19 +139,19 @@ func TestAuthz_WildcardAction(t *testing.T) { ctx := context.Background() require.NoError(t, s.CreateUser(ctx, &store.User{ - ID: "user-wc", Email: "wc@test.com", DisplayName: "WC", Role: "member", Status: "active", + ID: tid("user-wc"), Email: "wc@test.com", DisplayName: "WC", Role: "member", Status: "active", })) policy := &store.Policy{ - ID: "policy-wc", Name: "Allow All", ScopeType: "hub", + ID: tid("policy-wc"), Name: "Allow All", ScopeType: "hub", ResourceType: "*", Actions: []string{"*"}, Effect: "allow", } require.NoError(t, s.CreatePolicy(ctx, policy)) require.NoError(t, s.AddPolicyBinding(ctx, &store.PolicyBinding{ - PolicyID: "policy-wc", PrincipalType: "user", PrincipalID: "user-wc", + PolicyID: tid("policy-wc"), PrincipalType: "user", PrincipalID: tid("user-wc"), })) - user := NewAuthenticatedUser("user-wc", "wc@test.com", "WC", "member", "api") + user := NewAuthenticatedUser(tid("user-wc"), "wc@test.com", "WC", "member", "api") // Test with different actions and resource types for _, action := range []Action{ActionRead, ActionUpdate, ActionDelete, ActionManage} { @@ -165,37 +165,37 @@ func TestAuthz_ScopeOverride(t *testing.T) { ctx := context.Background() require.NoError(t, s.CreateUser(ctx, &store.User{ - ID: "user-scope", Email: "scope@test.com", DisplayName: "Scope", Role: "member", Status: "active", + ID: tid("user-scope"), Email: "scope@test.com", DisplayName: "Scope", Role: "member", Status: "active", })) // Hub-level deny hubPolicy := &store.Policy{ - ID: "policy-hub-deny", Name: "Hub Deny", ScopeType: "hub", + ID: tid("policy-hub-deny"), Name: "Hub Deny", ScopeType: "hub", ResourceType: "agent", Actions: []string{"read"}, Effect: "deny", Priority: 0, } require.NoError(t, s.CreatePolicy(ctx, hubPolicy)) require.NoError(t, s.AddPolicyBinding(ctx, &store.PolicyBinding{ - PolicyID: "policy-hub-deny", PrincipalType: "user", PrincipalID: "user-scope", + PolicyID: tid("policy-hub-deny"), PrincipalType: "user", PrincipalID: tid("user-scope"), })) // Project-level allow (more specific scope overrides) projectPolicy := &store.Policy{ - ID: "policy-project-allow", Name: "Project Allow", ScopeType: "project", - ScopeID: "project-1", + ID: tid("policy-project-allow"), Name: "Project Allow", ScopeType: "project", + ScopeID: tid("project-1"), ResourceType: "agent", Actions: []string{"read"}, Effect: "allow", Priority: 0, } require.NoError(t, s.CreatePolicy(ctx, projectPolicy)) require.NoError(t, s.AddPolicyBinding(ctx, &store.PolicyBinding{ - PolicyID: "policy-project-allow", PrincipalType: "user", PrincipalID: "user-scope", + PolicyID: tid("policy-project-allow"), PrincipalType: "user", PrincipalID: tid("user-scope"), })) - user := NewAuthenticatedUser("user-scope", "scope@test.com", "Scope", "member", "api") - resource := Resource{Type: "agent", ID: "agent-1", ParentType: "project", ParentID: "project-1"} + user := NewAuthenticatedUser(tid("user-scope"), "scope@test.com", "Scope", "member", "api") + resource := Resource{Type: "agent", ID: tid("agent-1"), ParentType: "project", ParentID: tid("project-1")} decision := authz.CheckAccess(ctx, user, resource, ActionRead) assert.True(t, decision.Allowed) assert.Equal(t, "project", decision.Scope) - assert.Equal(t, "policy-project-allow", decision.PolicyID) + assert.Equal(t, tid("policy-project-allow"), decision.PolicyID) } func TestAuthz_PriorityWithinScope(t *testing.T) { @@ -203,34 +203,34 @@ func TestAuthz_PriorityWithinScope(t *testing.T) { ctx := context.Background() require.NoError(t, s.CreateUser(ctx, &store.User{ - ID: "user-prio", Email: "prio@test.com", DisplayName: "Prio", Role: "member", Status: "active", + ID: tid("user-prio"), Email: "prio@test.com", DisplayName: "Prio", Role: "member", Status: "active", })) // Low priority allow p1 := &store.Policy{ - ID: "policy-low", Name: "Low Priority Allow", ScopeType: "hub", + ID: tid("policy-low"), Name: "Low Priority Allow", ScopeType: "hub", ResourceType: "agent", Actions: []string{"read"}, Effect: "allow", Priority: 0, } // High priority deny (should override) p2 := &store.Policy{ - ID: "policy-high", Name: "High Priority Deny", ScopeType: "hub", + ID: tid("policy-high"), Name: "High Priority Deny", ScopeType: "hub", ResourceType: "agent", Actions: []string{"read"}, Effect: "deny", Priority: 10, } require.NoError(t, s.CreatePolicy(ctx, p1)) require.NoError(t, s.CreatePolicy(ctx, p2)) require.NoError(t, s.AddPolicyBinding(ctx, &store.PolicyBinding{ - PolicyID: "policy-low", PrincipalType: "user", PrincipalID: "user-prio", + PolicyID: tid("policy-low"), PrincipalType: "user", PrincipalID: tid("user-prio"), })) require.NoError(t, s.AddPolicyBinding(ctx, &store.PolicyBinding{ - PolicyID: "policy-high", PrincipalType: "user", PrincipalID: "user-prio", + PolicyID: tid("policy-high"), PrincipalType: "user", PrincipalID: tid("user-prio"), })) - user := NewAuthenticatedUser("user-prio", "prio@test.com", "Prio", "member", "api") - resource := Resource{Type: "agent", ID: "agent-1"} + user := NewAuthenticatedUser(tid("user-prio"), "prio@test.com", "Prio", "member", "api") + resource := Resource{Type: "agent", ID: tid("agent-1")} decision := authz.CheckAccess(ctx, user, resource, ActionRead) assert.False(t, decision.Allowed) - assert.Equal(t, "policy-high", decision.PolicyID) + assert.Equal(t, tid("policy-high"), decision.PolicyID) } func TestAuthz_ConditionLabels(t *testing.T) { @@ -238,11 +238,11 @@ func TestAuthz_ConditionLabels(t *testing.T) { ctx := context.Background() require.NoError(t, s.CreateUser(ctx, &store.User{ - ID: "user-labels", Email: "labels@test.com", DisplayName: "Labels", Role: "member", Status: "active", + ID: tid("user-labels"), Email: "labels@test.com", DisplayName: "Labels", Role: "member", Status: "active", })) policy := &store.Policy{ - ID: "policy-labels", Name: "Label Condition", ScopeType: "hub", + ID: tid("policy-labels"), Name: "Label Condition", ScopeType: "hub", ResourceType: "agent", Actions: []string{"read"}, Effect: "allow", Conditions: &store.PolicyConditions{ Labels: map[string]string{"env": "production", "team": "backend"}, @@ -250,15 +250,15 @@ func TestAuthz_ConditionLabels(t *testing.T) { } require.NoError(t, s.CreatePolicy(ctx, policy)) require.NoError(t, s.AddPolicyBinding(ctx, &store.PolicyBinding{ - PolicyID: "policy-labels", PrincipalType: "user", PrincipalID: "user-labels", + PolicyID: tid("policy-labels"), PrincipalType: "user", PrincipalID: tid("user-labels"), })) - user := NewAuthenticatedUser("user-labels", "labels@test.com", "Labels", "member", "api") + user := NewAuthenticatedUser(tid("user-labels"), "labels@test.com", "Labels", "member", "api") // Matching labels resourceMatch := Resource{ Type: "agent", - ID: "agent-1", + ID: tid("agent-1"), Labels: map[string]string{"env": "production", "team": "backend"}, } decision := authz.CheckAccess(ctx, user, resourceMatch, ActionRead) @@ -267,7 +267,7 @@ func TestAuthz_ConditionLabels(t *testing.T) { // Non-matching labels resourceNoMatch := Resource{ Type: "agent", - ID: "agent-2", + ID: tid("agent-2"), Labels: map[string]string{"env": "staging"}, } decision = authz.CheckAccess(ctx, user, resourceNoMatch, ActionRead) @@ -280,12 +280,12 @@ func TestAuthz_TimeConditions(t *testing.T) { ctx := context.Background() require.NoError(t, s.CreateUser(ctx, &store.User{ - ID: "user-time", Email: "time@test.com", DisplayName: "Time", Role: "member", Status: "active", + ID: tid("user-time"), Email: "time@test.com", DisplayName: "Time", Role: "member", Status: "active", })) past := time.Now().Add(-time.Hour) policy := &store.Policy{ - ID: "policy-expired", Name: "Expired Policy", ScopeType: "hub", + ID: tid("policy-expired"), Name: "Expired Policy", ScopeType: "hub", ResourceType: "agent", Actions: []string{"read"}, Effect: "allow", Conditions: &store.PolicyConditions{ ValidUntil: &past, @@ -293,11 +293,11 @@ func TestAuthz_TimeConditions(t *testing.T) { } require.NoError(t, s.CreatePolicy(ctx, policy)) require.NoError(t, s.AddPolicyBinding(ctx, &store.PolicyBinding{ - PolicyID: "policy-expired", PrincipalType: "user", PrincipalID: "user-time", + PolicyID: tid("policy-expired"), PrincipalType: "user", PrincipalID: tid("user-time"), })) - user := NewAuthenticatedUser("user-time", "time@test.com", "Time", "member", "api") - resource := Resource{Type: "agent", ID: "agent-1"} + user := NewAuthenticatedUser(tid("user-time"), "time@test.com", "Time", "member", "api") + resource := Resource{Type: "agent", ID: tid("agent-1")} decision := authz.CheckAccess(ctx, user, resource, ActionRead) assert.False(t, decision.Allowed) @@ -310,29 +310,29 @@ func TestAuthz_AgentDirectPolicy(t *testing.T) { // Create project and agent require.NoError(t, s.CreateProject(ctx, &store.Project{ - ID: "project-agent-1", Name: "Test Project", Slug: "test-project-agent-1", + ID: tid("project-agent-1"), Name: "Test Project", Slug: "test-project-agent-1", })) require.NoError(t, s.CreateAgent(ctx, &store.Agent{ - ID: "agent-direct", Slug: "agent-direct", Name: "Agent Direct", - ProjectID: "project-agent-1", Phase: string(state.PhaseRunning), + ID: tid("agent-direct"), Slug: tid("agent-direct"), Name: "Agent Direct", + ProjectID: tid("project-agent-1"), Phase: string(state.PhaseRunning), })) // Create and bind policy to agent policy := &store.Policy{ - ID: "policy-agent", Name: "Agent Allow", ScopeType: "hub", + ID: tid("policy-agent"), Name: "Agent Allow", ScopeType: "hub", ResourceType: "project", Actions: []string{"read"}, Effect: "allow", } require.NoError(t, s.CreatePolicy(ctx, policy)) require.NoError(t, s.AddPolicyBinding(ctx, &store.PolicyBinding{ - PolicyID: "policy-agent", PrincipalType: "agent", PrincipalID: "agent-direct", + PolicyID: tid("policy-agent"), PrincipalType: "agent", PrincipalID: tid("agent-direct"), })) - agent := &evaluateAgentIdentity{id: "agent-direct", projectID: "project-agent-1"} - resource := Resource{Type: "project", ID: "project-agent-1"} + agent := &evaluateAgentIdentity{id: tid("agent-direct"), projectID: tid("project-agent-1")} + resource := Resource{Type: "project", ID: tid("project-agent-1")} decision := authz.CheckAccess(ctx, agent, resource, ActionRead) assert.True(t, decision.Allowed) - assert.Equal(t, "policy-agent", decision.PolicyID) + assert.Equal(t, tid("policy-agent"), decision.PolicyID) } func TestAuthz_ActionMismatch(t *testing.T) { @@ -340,20 +340,20 @@ func TestAuthz_ActionMismatch(t *testing.T) { ctx := context.Background() require.NoError(t, s.CreateUser(ctx, &store.User{ - ID: "user-act", Email: "act@test.com", DisplayName: "Act", Role: "member", Status: "active", + ID: tid("user-act"), Email: "act@test.com", DisplayName: "Act", Role: "member", Status: "active", })) policy := &store.Policy{ - ID: "policy-read-only", Name: "Read Only", ScopeType: "hub", + ID: tid("policy-read-only"), Name: "Read Only", ScopeType: "hub", ResourceType: "agent", Actions: []string{"read"}, Effect: "allow", } require.NoError(t, s.CreatePolicy(ctx, policy)) require.NoError(t, s.AddPolicyBinding(ctx, &store.PolicyBinding{ - PolicyID: "policy-read-only", PrincipalType: "user", PrincipalID: "user-act", + PolicyID: tid("policy-read-only"), PrincipalType: "user", PrincipalID: tid("user-act"), })) - user := NewAuthenticatedUser("user-act", "act@test.com", "Act", "member", "api") - resource := Resource{Type: "agent", ID: "agent-1"} + user := NewAuthenticatedUser(tid("user-act"), "act@test.com", "Act", "member", "api") + resource := Resource{Type: "agent", ID: tid("agent-1")} // Read should succeed decision := authz.CheckAccess(ctx, user, resource, ActionRead) @@ -369,19 +369,19 @@ func TestAuthz_ResourceTypeMismatch(t *testing.T) { ctx := context.Background() require.NoError(t, s.CreateUser(ctx, &store.User{ - ID: "user-rt", Email: "rt@test.com", DisplayName: "RT", Role: "member", Status: "active", + ID: tid("user-rt"), Email: "rt@test.com", DisplayName: "RT", Role: "member", Status: "active", })) policy := &store.Policy{ - ID: "policy-agent-only", Name: "Agent Only", ScopeType: "hub", + ID: tid("policy-agent-only"), Name: "Agent Only", ScopeType: "hub", ResourceType: "agent", Actions: []string{"read"}, Effect: "allow", } require.NoError(t, s.CreatePolicy(ctx, policy)) require.NoError(t, s.AddPolicyBinding(ctx, &store.PolicyBinding{ - PolicyID: "policy-agent-only", PrincipalType: "user", PrincipalID: "user-rt", + PolicyID: tid("policy-agent-only"), PrincipalType: "user", PrincipalID: tid("user-rt"), })) - user := NewAuthenticatedUser("user-rt", "rt@test.com", "RT", "member", "api") + user := NewAuthenticatedUser(tid("user-rt"), "rt@test.com", "RT", "member", "api") // Agent resource should match decision := authz.CheckAccess(ctx, user, Resource{Type: "agent", ID: "a1"}, ActionRead) @@ -460,14 +460,14 @@ func TestMatchesResource(t *testing.T) { }, { "project scope matching", - store.Policy{ResourceType: "agent", ScopeType: "project", ScopeID: "project-1"}, - Resource{Type: "agent", ParentType: "project", ParentID: "project-1"}, + store.Policy{ResourceType: "agent", ScopeType: "project", ScopeID: tid("project-1")}, + Resource{Type: "agent", ParentType: "project", ParentID: tid("project-1")}, true, }, { "project scope mismatch", - store.Policy{ResourceType: "agent", ScopeType: "project", ScopeID: "project-1"}, - Resource{Type: "agent", ParentType: "project", ParentID: "project-2"}, + store.Policy{ResourceType: "agent", ScopeType: "project", ScopeID: tid("project-1")}, + Resource{Type: "agent", ParentType: "project", ParentID: tid("project-2")}, false, }, } @@ -491,11 +491,11 @@ func TestAuthz_BrokerDispatch_OwnerAllowed(t *testing.T) { ctx := context.Background() require.NoError(t, s.CreateUser(ctx, &store.User{ - ID: "broker-owner", Email: "owner@test.com", DisplayName: "Owner", Role: "member", Status: "active", + ID: tid("broker-owner"), Email: "owner@test.com", DisplayName: "Owner", Role: "member", Status: "active", })) - user := NewAuthenticatedUser("broker-owner", "owner@test.com", "Owner", "member", "api") - resource := Resource{Type: "broker", ID: "broker-1", OwnerID: "broker-owner"} + user := NewAuthenticatedUser(tid("broker-owner"), "owner@test.com", "Owner", "member", "api") + resource := Resource{Type: "broker", ID: tid("broker-1"), OwnerID: tid("broker-owner")} decision := authz.CheckAccess(ctx, user, resource, ActionDispatch) assert.True(t, decision.Allowed) @@ -507,11 +507,11 @@ func TestAuthz_BrokerDispatch_NonOwnerDenied(t *testing.T) { ctx := context.Background() require.NoError(t, s.CreateUser(ctx, &store.User{ - ID: "other-user", Email: "other@test.com", DisplayName: "Other", Role: "member", Status: "active", + ID: tid("other-user"), Email: "other@test.com", DisplayName: "Other", Role: "member", Status: "active", })) - user := NewAuthenticatedUser("other-user", "other@test.com", "Other", "member", "api") - resource := Resource{Type: "broker", ID: "broker-1", OwnerID: "broker-owner-id"} + user := NewAuthenticatedUser(tid("other-user"), "other@test.com", "Other", "member", "api") + resource := Resource{Type: "broker", ID: tid("broker-1"), OwnerID: tid("broker-owner-id")} decision := authz.CheckAccess(ctx, user, resource, ActionDispatch) assert.False(t, decision.Allowed) @@ -523,7 +523,7 @@ func TestAuthz_BrokerDispatch_AdminAllowed(t *testing.T) { ctx := context.Background() admin := NewAuthenticatedUser("admin-1", "admin@example.com", "Admin", "admin", "api") - resource := Resource{Type: "broker", ID: "broker-1", OwnerID: "someone-else"} + resource := Resource{Type: "broker", ID: tid("broker-1"), OwnerID: "someone-else"} decision := authz.CheckAccess(ctx, admin, resource, ActionDispatch) assert.True(t, decision.Allowed) @@ -535,11 +535,11 @@ func TestAuthz_BrokerCapabilities_Owner(t *testing.T) { ctx := context.Background() require.NoError(t, s.CreateUser(ctx, &store.User{ - ID: "cap-owner", Email: "cap-owner@test.com", DisplayName: "Cap Owner", Role: "member", Status: "active", + ID: tid("cap-owner"), Email: "cap-owner@test.com", DisplayName: "Cap Owner", Role: "member", Status: "active", })) - user := NewAuthenticatedUser("cap-owner", "cap-owner@test.com", "Cap Owner", "member", "api") - resource := Resource{Type: "broker", ID: "broker-cap", OwnerID: "cap-owner"} + user := NewAuthenticatedUser(tid("cap-owner"), "cap-owner@test.com", "Cap Owner", "member", "api") + resource := Resource{Type: "broker", ID: tid("broker-cap"), OwnerID: tid("cap-owner")} caps := authz.ComputeCapabilities(ctx, user, resource) assert.Contains(t, caps.Actions, "dispatch") @@ -553,11 +553,11 @@ func TestAuthz_BrokerCapabilities_NonOwner(t *testing.T) { ctx := context.Background() require.NoError(t, s.CreateUser(ctx, &store.User{ - ID: "cap-nonowner", Email: "nonowner@test.com", DisplayName: "Non Owner", Role: "member", Status: "active", + ID: tid("cap-nonowner"), Email: "nonowner@test.com", DisplayName: "Non Owner", Role: "member", Status: "active", })) - user := NewAuthenticatedUser("cap-nonowner", "nonowner@test.com", "Non Owner", "member", "api") - resource := Resource{Type: "broker", ID: "broker-cap", OwnerID: "someone-else"} + user := NewAuthenticatedUser(tid("cap-nonowner"), "nonowner@test.com", "Non Owner", "member", "api") + resource := Resource{Type: "broker", ID: tid("broker-cap"), OwnerID: "someone-else"} caps := authz.ComputeCapabilities(ctx, user, resource) assert.NotContains(t, caps.Actions, "dispatch") @@ -566,14 +566,14 @@ func TestAuthz_BrokerCapabilities_NonOwner(t *testing.T) { func TestBrokerResource_Helper(t *testing.T) { broker := &store.RuntimeBroker{ - ID: "broker-helper-test", - CreatedBy: "user-123", + ID: tid("broker-helper-test"), + CreatedBy: tid("user-123"), } r := brokerResource(broker) assert.Equal(t, "broker", r.Type) - assert.Equal(t, "broker-helper-test", r.ID) - assert.Equal(t, "user-123", r.OwnerID) + assert.Equal(t, tid("broker-helper-test"), r.ID) + assert.Equal(t, tid("user-123"), r.OwnerID) } // ============================================================================= @@ -587,13 +587,13 @@ func TestCanAccessAsAncestor(t *testing.T) { ancestry []string expected bool }{ - {"root ancestor", "user-1", []string{"user-1"}, true}, - {"intermediate ancestor", "agent-A", []string{"user-1", "agent-A"}, true}, - {"not in ancestry", "user-2", []string{"user-1", "agent-A"}, false}, - {"empty ancestry", "user-1", nil, false}, - {"deep chain", "user-1", []string{"user-1", "agent-A", "agent-B"}, true}, - {"deep chain middle", "agent-A", []string{"user-1", "agent-A", "agent-B"}, true}, - {"deep chain last", "agent-B", []string{"user-1", "agent-A", "agent-B"}, true}, + {"root ancestor", tid("user-1"), []string{tid("user-1")}, true}, + {"intermediate ancestor", tid("agent-A"), []string{tid("user-1"), tid("agent-A")}, true}, + {"not in ancestry", tid("user-2"), []string{tid("user-1"), tid("agent-A")}, false}, + {"empty ancestry", tid("user-1"), nil, false}, + {"deep chain", tid("user-1"), []string{tid("user-1"), tid("agent-A"), tid("agent-B")}, true}, + {"deep chain middle", tid("agent-A"), []string{tid("user-1"), tid("agent-A"), tid("agent-B")}, true}, + {"deep chain last", tid("agent-B"), []string{tid("user-1"), tid("agent-A"), tid("agent-B")}, true}, } for _, tt := range tests { @@ -610,17 +610,17 @@ func TestAuthz_AncestryAccess_UserToAgent(t *testing.T) { // Create user (non-admin, non-owner — ancestry is the only access path) require.NoError(t, s.CreateUser(ctx, &store.User{ - ID: "user-ancestor", Email: "ancestor@test.com", DisplayName: "Ancestor", Role: "member", Status: "active", + ID: tid("user-ancestor"), Email: "ancestor@test.com", DisplayName: "Ancestor", Role: "member", Status: "active", })) - user := NewAuthenticatedUser("user-ancestor", "ancestor@test.com", "Ancestor", "member", "api") + user := NewAuthenticatedUser(tid("user-ancestor"), "ancestor@test.com", "Ancestor", "member", "api") // Resource with user in ancestry but different owner resource := Resource{ Type: "agent", - ID: "agent-grandchild", + ID: tid("agent-grandchild"), OwnerID: "someone-else", - Ancestry: []string{"user-ancestor", "agent-child"}, + Ancestry: []string{tid("user-ancestor"), tid("agent-child")}, } decision := authz.CheckAccess(ctx, user, resource, ActionRead) @@ -634,20 +634,20 @@ func TestAuthz_AncestryAccess_AgentToDescendant(t *testing.T) { // Create project and parent agent require.NoError(t, s.CreateProject(ctx, &store.Project{ - ID: "project-ancestry-1", Name: "Ancestry Project", Slug: "ancestry-project-1", + ID: tid("project-ancestry-1"), Name: "Ancestry Project", Slug: "ancestry-project-1", })) require.NoError(t, s.CreateAgent(ctx, &store.Agent{ - ID: "agent-parent", Slug: "agent-parent", Name: "Parent Agent", - ProjectID: "project-ancestry-1", Phase: string(state.PhaseRunning), + ID: tid("agent-parent"), Slug: tid("agent-parent"), Name: "Parent Agent", + ProjectID: tid("project-ancestry-1"), Phase: string(state.PhaseRunning), })) - agent := &evaluateAgentIdentity{id: "agent-parent", projectID: "project-ancestry-1"} + agent := &evaluateAgentIdentity{id: tid("agent-parent"), projectID: tid("project-ancestry-1")} // Grandchild agent with parent in ancestry resource := Resource{ Type: "agent", - ID: "agent-grandchild", - Ancestry: []string{"user-root", "agent-parent", "agent-child"}, + ID: tid("agent-grandchild"), + Ancestry: []string{tid("user-root"), tid("agent-parent"), tid("agent-child")}, } decision := authz.CheckAccess(ctx, agent, resource, ActionRead) @@ -660,15 +660,15 @@ func TestAuthz_AncestryAccess_NoAncestry(t *testing.T) { ctx := context.Background() require.NoError(t, s.CreateUser(ctx, &store.User{ - ID: "user-no-ancestry", Email: "no-ancestry@test.com", DisplayName: "NoAnc", Role: "member", Status: "active", + ID: tid("user-no-ancestry"), Email: "no-ancestry@test.com", DisplayName: "NoAnc", Role: "member", Status: "active", })) - user := NewAuthenticatedUser("user-no-ancestry", "no-ancestry@test.com", "NoAnc", "member", "api") + user := NewAuthenticatedUser(tid("user-no-ancestry"), "no-ancestry@test.com", "NoAnc", "member", "api") // Resource without ancestry — user is not owner and has no policies resource := Resource{ Type: "agent", - ID: "agent-no-ancestry", + ID: tid("agent-no-ancestry"), OwnerID: "someone-else", } @@ -682,17 +682,17 @@ func TestAuthz_AncestryAccess_NotInChain(t *testing.T) { ctx := context.Background() require.NoError(t, s.CreateUser(ctx, &store.User{ - ID: "user-outsider", Email: "outsider@test.com", DisplayName: "Outsider", Role: "member", Status: "active", + ID: tid("user-outsider"), Email: "outsider@test.com", DisplayName: "Outsider", Role: "member", Status: "active", })) - user := NewAuthenticatedUser("user-outsider", "outsider@test.com", "Outsider", "member", "api") + user := NewAuthenticatedUser(tid("user-outsider"), "outsider@test.com", "Outsider", "member", "api") // Resource with ancestry that doesn't include this user resource := Resource{ Type: "agent", - ID: "agent-other-chain", + ID: tid("agent-other-chain"), OwnerID: "someone-else", - Ancestry: []string{"user-other", "agent-A"}, + Ancestry: []string{tid("user-other"), tid("agent-A")}, } decision := authz.CheckAccess(ctx, user, resource, ActionRead) diff --git a/pkg/hub/bootstrap_test.go b/pkg/hub/bootstrap_test.go index 3e06c06e0..99f1ba499 100644 --- a/pkg/hub/bootstrap_test.go +++ b/pkg/hub/bootstrap_test.go @@ -33,7 +33,6 @@ import ( "github.com/GoogleCloudPlatform/scion/pkg/messages" "github.com/GoogleCloudPlatform/scion/pkg/storage" "github.com/GoogleCloudPlatform/scion/pkg/store" - "github.com/GoogleCloudPlatform/scion/pkg/store/sqlite" "github.com/GoogleCloudPlatform/scion/pkg/transfer" ) @@ -184,7 +183,7 @@ func (d *mockDispatcher) DispatchFinalizeEnv(_ context.Context, _ *store.Agent, // testBootstrapServer creates a test server with storage and dispatcher configured. func testBootstrapServer(t *testing.T) (*Server, store.Store, *mockStorage, *mockDispatcher) { t.Helper() - s, err := sqlite.New(":memory:") + s, err := newTestStore(":memory:") if err != nil { t.Fatalf("failed to create test store: %v", err) } @@ -239,7 +238,7 @@ func setupProjectAndBroker(t *testing.T, s store.Store) (string, string) { ctx := context.Background() broker := &store.RuntimeBroker{ - ID: "broker_bootstrap_test", + ID: tid("broker_bootstrap_test"), Slug: "bootstrap-host", Name: "Bootstrap Host", Status: store.BrokerStatusOnline, @@ -249,7 +248,7 @@ func setupProjectAndBroker(t *testing.T, s store.Store) (string, string) { } project := &store.Project{ - ID: "project_bootstrap_test", + ID: tid("project_bootstrap_test"), Slug: "bootstrap-project", Name: "Bootstrap Project", GitRemote: "https://github.com/test/bootstrap", @@ -410,7 +409,7 @@ func TestCreateAgentWithWorkspaceBootstrap_ExistingFiles(t *testing.T) { func TestCreateAgentWithWorkspaceBootstrap_NoStorage(t *testing.T) { // Create server without storage - s, err := sqlite.New(":memory:") + s, err := newTestStore(":memory:") if err != nil { t.Fatalf("failed to create test store: %v", err) } @@ -489,7 +488,7 @@ func TestCreateAgentWithWorkspaceBootstrap_LocalProvider(t *testing.T) { // Create broker and project broker := &store.RuntimeBroker{ - ID: "broker_local_path_test", + ID: tid("broker_local_path_test"), Slug: "local-path-host", Name: "Local Path Host", Status: store.BrokerStatusOnline, @@ -499,7 +498,7 @@ func TestCreateAgentWithWorkspaceBootstrap_LocalProvider(t *testing.T) { } project := &store.Project{ - ID: "project_local_path_test", + ID: tid("project_local_path_test"), Slug: "local-path-project", Name: "Local Path Project", GitRemote: "https://github.com/test/local-path", @@ -721,11 +720,11 @@ func TestSyncToFinalize_BootstrapMode(t *testing.T) { // Create an agent in provisioning status (simulating post-bootstrap-create) agent := &store.Agent{ - ID: "agent_bootstrap_finalize", + ID: tid("agent_bootstrap_finalize"), Slug: "bootstrap-finalize", Name: "Bootstrap Finalize", ProjectID: projectID, - RuntimeBrokerID: "broker_bootstrap_test", + RuntimeBrokerID: tid("broker_bootstrap_test"), Phase: string(state.PhaseProvisioning), Visibility: store.VisibilityPrivate, AppliedConfig: &store.AgentAppliedConfig{ @@ -737,7 +736,7 @@ func TestSyncToFinalize_BootstrapMode(t *testing.T) { } // Pre-populate the files in mock storage - storagePath := "workspaces/" + projectID + "/agent_bootstrap_finalize" + storagePath := "workspaces/" + projectID + "/" + tid("agent_bootstrap_finalize") stor.objects[storagePath+"/files/main.go"] = &storage.Object{ Name: storagePath + "/files/main.go", } @@ -757,7 +756,7 @@ func TestSyncToFinalize_BootstrapMode(t *testing.T) { Manifest: manifest, } - rec := doBootstrapRequest(t, srv, http.MethodPost, "/api/v1/agents/agent_bootstrap_finalize/workspace/sync-to/finalize", finalizeReq) + rec := doBootstrapRequest(t, srv, http.MethodPost, fmt.Sprintf("/api/v1/agents/%s/workspace/sync-to/finalize", tid("agent_bootstrap_finalize")), finalizeReq) if rec.Code != http.StatusOK { t.Fatalf("expected status 200, got %d: %s", rec.Code, rec.Body.String()) @@ -787,7 +786,7 @@ func TestSyncToFinalize_BootstrapMode(t *testing.T) { } dispatched := disp.dispatchedAgents[0] - if dispatched.ID != "agent_bootstrap_finalize" { + if dispatched.ID != tid("agent_bootstrap_finalize") { t.Errorf("expected dispatched agent ID 'agent_bootstrap_finalize', got %q", dispatched.ID) } @@ -807,11 +806,11 @@ func TestSyncToFinalize_BootstrapMode_MissingFile(t *testing.T) { // Create an agent in provisioning status agent := &store.Agent{ - ID: "agent_bootstrap_missing", + ID: tid("agent_bootstrap_missing"), Slug: "bootstrap-missing", Name: "Bootstrap Missing", ProjectID: projectID, - RuntimeBrokerID: "broker_bootstrap_test", + RuntimeBrokerID: tid("broker_bootstrap_test"), Phase: string(state.PhaseProvisioning), Visibility: store.VisibilityPrivate, } @@ -820,7 +819,7 @@ func TestSyncToFinalize_BootstrapMode_MissingFile(t *testing.T) { } // Only put one file in storage - storagePath := "workspaces/" + projectID + "/agent_bootstrap_missing" + storagePath := "workspaces/" + projectID + "/" + tid("agent_bootstrap_missing") stor.objects[storagePath+"/files/main.go"] = &storage.Object{ Name: storagePath + "/files/main.go", } @@ -837,7 +836,7 @@ func TestSyncToFinalize_BootstrapMode_MissingFile(t *testing.T) { Manifest: manifest, } - rec := doBootstrapRequest(t, srv, http.MethodPost, "/api/v1/agents/agent_bootstrap_missing/workspace/sync-to/finalize", finalizeReq) + rec := doBootstrapRequest(t, srv, http.MethodPost, fmt.Sprintf("/api/v1/agents/%s/workspace/sync-to/finalize", tid("agent_bootstrap_missing")), finalizeReq) if rec.Code != http.StatusBadRequest { t.Errorf("expected status 400, got %d: %s", rec.Code, rec.Body.String()) @@ -851,11 +850,11 @@ func TestSyncToFinalize_RejectsStoppedAgent(t *testing.T) { // Create an agent in stopped status agent := &store.Agent{ - ID: "agent_bootstrap_stopped", + ID: tid("agent_bootstrap_stopped"), Slug: "bootstrap-stopped", Name: "Bootstrap Stopped", ProjectID: projectID, - RuntimeBrokerID: "broker_bootstrap_test", + RuntimeBrokerID: tid("broker_bootstrap_test"), Phase: string(state.PhaseStopped), Visibility: store.VisibilityPrivate, } @@ -869,7 +868,7 @@ func TestSyncToFinalize_RejectsStoppedAgent(t *testing.T) { } finalizeReq := SyncToFinalizeRequest{Manifest: manifest} - rec := doBootstrapRequest(t, srv, http.MethodPost, "/api/v1/agents/agent_bootstrap_stopped/workspace/sync-to/finalize", finalizeReq) + rec := doBootstrapRequest(t, srv, http.MethodPost, fmt.Sprintf("/api/v1/agents/%s/workspace/sync-to/finalize", tid("agent_bootstrap_stopped")), finalizeReq) if rec.Code != http.StatusConflict { t.Errorf("expected status 409, got %d: %s", rec.Code, rec.Body.String()) @@ -878,7 +877,7 @@ func TestSyncToFinalize_RejectsStoppedAgent(t *testing.T) { func TestSyncToFinalize_BootstrapMode_NoDispatcher(t *testing.T) { // Create server without dispatcher - s, err := sqlite.New(":memory:") + s, err := newTestStore(":memory:") if err != nil { t.Fatalf("failed to create test store: %v", err) } @@ -901,11 +900,11 @@ func TestSyncToFinalize_BootstrapMode_NoDispatcher(t *testing.T) { ctx := context.Background() agent := &store.Agent{ - ID: "agent_bootstrap_nodisp", + ID: tid("agent_bootstrap_nodisp"), Slug: "bootstrap-nodisp", Name: "Bootstrap No Dispatcher", ProjectID: projectID, - RuntimeBrokerID: "broker_bootstrap_test", + RuntimeBrokerID: tid("broker_bootstrap_test"), Phase: string(state.PhaseProvisioning), Visibility: store.VisibilityPrivate, } @@ -913,7 +912,7 @@ func TestSyncToFinalize_BootstrapMode_NoDispatcher(t *testing.T) { t.Fatalf("failed to create agent: %v", err) } - storagePath := "workspaces/" + projectID + "/agent_bootstrap_nodisp" + storagePath := "workspaces/" + projectID + "/" + tid("agent_bootstrap_nodisp") stor.objects[storagePath+"/files/main.go"] = &storage.Object{ Name: storagePath + "/files/main.go", } @@ -924,7 +923,7 @@ func TestSyncToFinalize_BootstrapMode_NoDispatcher(t *testing.T) { } finalizeReq := SyncToFinalizeRequest{Manifest: manifest} - rec := doBootstrapRequest(t, srv, http.MethodPost, "/api/v1/agents/agent_bootstrap_nodisp/workspace/sync-to/finalize", finalizeReq) + rec := doBootstrapRequest(t, srv, http.MethodPost, fmt.Sprintf("/api/v1/agents/%s/workspace/sync-to/finalize", tid("agent_bootstrap_nodisp")), finalizeReq) if rec.Code != http.StatusBadGateway { t.Errorf("expected status 502, got %d: %s", rec.Code, rec.Body.String()) @@ -942,7 +941,7 @@ func TestDispatcherPassesWorkspaceStoragePath(t *testing.T) { ID: "agent_with_storage_path", Slug: "storage-path-agent", Name: "Storage Path Agent", - ProjectID: "project_test", + ProjectID: tid("project_test"), RuntimeBrokerID: "broker_test", Phase: string(state.PhaseProvisioning), AppliedConfig: &store.AgentAppliedConfig{ diff --git a/pkg/hub/brokerauth_test.go b/pkg/hub/brokerauth_test.go index 60e86b5e8..2c27a8873 100644 --- a/pkg/hub/brokerauth_test.go +++ b/pkg/hub/brokerauth_test.go @@ -31,14 +31,13 @@ import ( "time" "github.com/GoogleCloudPlatform/scion/pkg/store" - "github.com/GoogleCloudPlatform/scion/pkg/store/sqlite" "github.com/google/uuid" ) func setupTestBrokerAuthService(t *testing.T) (*BrokerAuthService, store.Store) { t.Helper() - s, err := sqlite.New(":memory:") + s, err := newTestStore(":memory:") if err != nil { t.Fatalf("failed to create store: %v", err) } @@ -149,7 +148,7 @@ func TestJoinWithInvalidToken(t *testing.T) { func TestJoinWithExpiredToken(t *testing.T) { // Create service with short token expiry - s, err := sqlite.New(":memory:") + s, err := newTestStore(":memory:") if err != nil { t.Fatalf("failed to create store: %v", err) } @@ -329,7 +328,7 @@ func TestValidateBrokerSignature_InvalidSignature(t *testing.T) { func TestValidateBrokerSignature_ClockSkew(t *testing.T) { // Create service with short clock skew tolerance - s, err := sqlite.New(":memory:") + s, err := newTestStore(":memory:") if err != nil { t.Fatalf("failed to create store: %v", err) } diff --git a/pkg/hub/brokerclient_test.go b/pkg/hub/brokerclient_test.go index 69d901eb9..7f3c3c3ee 100644 --- a/pkg/hub/brokerclient_test.go +++ b/pkg/hub/brokerclient_test.go @@ -27,12 +27,11 @@ import ( "github.com/GoogleCloudPlatform/scion/pkg/apiclient" "github.com/GoogleCloudPlatform/scion/pkg/store" - "github.com/GoogleCloudPlatform/scion/pkg/store/sqlite" ) func TestAuthenticatedBrokerClient_CreateAgent(t *testing.T) { // Create a test store with a broker secret - db, err := sqlite.New(":memory:") + db, err := newTestStore(":memory:") if err != nil { t.Fatalf("failed to create test store: %v", err) } @@ -43,7 +42,7 @@ func TestAuthenticatedBrokerClient_CreateAgent(t *testing.T) { } // Create a test broker - brokerID := "test-host-123" + brokerID := tid("test-host-123") secretKey := []byte("test-secret-key-32-bytes-long!!!") broker := &store.RuntimeBroker{ @@ -101,7 +100,7 @@ func TestAuthenticatedBrokerClient_CreateAgent(t *testing.T) { resp := &RemoteAgentResponse{ Created: true, Agent: &RemoteAgentInfo{ - ID: "agent-1", + ID: tid("agent-1"), Name: "test-agent", Status: "created", }, @@ -116,9 +115,9 @@ func TestAuthenticatedBrokerClient_CreateAgent(t *testing.T) { // Make request req := &RemoteCreateAgentRequest{ - Slug: "agent-1", + Slug: tid("agent-1"), Name: "test-agent", - ProjectID: "project-1", + ProjectID: tid("project-1"), } resp, err := client.CreateAgent(context.Background(), brokerID, server.URL, req) @@ -146,7 +145,7 @@ func TestAuthenticatedBrokerClient_CreateAgent(t *testing.T) { func TestAuthenticatedBrokerClient_StartAgent(t *testing.T) { // Create a test store with a broker secret - db, err := sqlite.New(":memory:") + db, err := newTestStore(":memory:") if err != nil { t.Fatalf("failed to create test store: %v", err) } @@ -157,7 +156,7 @@ func TestAuthenticatedBrokerClient_StartAgent(t *testing.T) { } // Create a test broker - brokerID := "test-host-456" + brokerID := tid("test-host-456") secretKey := []byte("another-secret-key-32-bytes!!!!!") broker := &store.RuntimeBroker{ @@ -235,7 +234,7 @@ func TestAuthenticatedBrokerClient_StartAgent(t *testing.T) { func TestAuthenticatedBrokerClient_MissingSecretFailsClosed(t *testing.T) { // Create a test store without a secret - db, err := sqlite.New(":memory:") + db, err := newTestStore(":memory:") if err != nil { t.Fatalf("failed to create test store: %v", err) } @@ -246,12 +245,12 @@ func TestAuthenticatedBrokerClient_MissingSecretFailsClosed(t *testing.T) { } // Create a test broker without a secret - brokerID := "test-host-no-secret" + brokerID := tid("test-host-no-secret") broker := &store.RuntimeBroker{ ID: brokerID, - Name: "test-host-no-secret", - Slug: "test-host-no-secret", + Name: tid("test-host-no-secret"), + Slug: tid("test-host-no-secret"), Status: store.BrokerStatusOnline, Created: time.Now(), Updated: time.Now(), @@ -274,9 +273,9 @@ func TestAuthenticatedBrokerClient_MissingSecretFailsClosed(t *testing.T) { // Make request - should fail before sending anything req := &RemoteCreateAgentRequest{ - Slug: "agent-1", + Slug: tid("agent-1"), Name: "test-agent", - ProjectID: "project-1", + ProjectID: tid("project-1"), } _, err = client.CreateAgent(context.Background(), brokerID, server.URL, req) @@ -293,7 +292,7 @@ func TestAuthenticatedBrokerClient_MissingSecretFailsClosed(t *testing.T) { func TestAuthenticatedBrokerClient_ExpiredSecretFailsClosed(t *testing.T) { // Create a test store with an expired secret - db, err := sqlite.New(":memory:") + db, err := newTestStore(":memory:") if err != nil { t.Fatalf("failed to create test store: %v", err) } @@ -304,13 +303,13 @@ func TestAuthenticatedBrokerClient_ExpiredSecretFailsClosed(t *testing.T) { } // Create a test broker with expired secret - brokerID := "test-host-expired" + brokerID := tid("test-host-expired") secretKey := []byte("expired-secret-key-32-bytes!!!!!") broker := &store.RuntimeBroker{ ID: brokerID, - Name: "test-host-expired", - Slug: "test-host-expired", + Name: tid("test-host-expired"), + Slug: tid("test-host-expired"), Status: store.BrokerStatusOnline, Created: time.Now(), Updated: time.Now(), @@ -345,9 +344,9 @@ func TestAuthenticatedBrokerClient_ExpiredSecretFailsClosed(t *testing.T) { // Make request - should fail before sending due to expired secret req := &RemoteCreateAgentRequest{ - Slug: "agent-1", + Slug: tid("agent-1"), Name: "test-agent", - ProjectID: "project-1", + ProjectID: tid("project-1"), } _, err = client.CreateAgent(context.Background(), brokerID, server.URL, req) @@ -363,7 +362,7 @@ func TestAuthenticatedBrokerClient_ExpiredSecretFailsClosed(t *testing.T) { } func TestAuthenticatedBrokerClient_StartAgent_InvalidJSONFails(t *testing.T) { - db, err := sqlite.New(":memory:") + db, err := newTestStore(":memory:") if err != nil { t.Fatalf("failed to create test store: %v", err) } @@ -373,11 +372,11 @@ func TestAuthenticatedBrokerClient_StartAgent_InvalidJSONFails(t *testing.T) { t.Fatalf("failed to migrate: %v", err) } - brokerID := "test-host-invalid-json" + brokerID := tid("test-host-invalid-json") broker := &store.RuntimeBroker{ ID: brokerID, - Name: "test-host-invalid-json", - Slug: "test-host-invalid-json", + Name: tid("test-host-invalid-json"), + Slug: tid("test-host-invalid-json"), Status: store.BrokerStatusOnline, Created: time.Now(), Updated: time.Now(), @@ -405,7 +404,7 @@ func TestAuthenticatedBrokerClient_StartAgent_InvalidJSONFails(t *testing.T) { defer server.Close() client := NewAuthenticatedBrokerClient(db, false) - _, err = client.StartAgent(context.Background(), brokerID, server.URL, "agent-1", "", "", "", "", "", nil, nil, nil, nil, false) + _, err = client.StartAgent(context.Background(), brokerID, server.URL, tid("agent-1"), "", "", "", "", "", nil, nil, nil, nil, false) if err == nil { t.Fatal("expected StartAgent to fail on invalid JSON response") } @@ -416,7 +415,7 @@ func TestAuthenticatedBrokerClient_StartAgent_InvalidJSONFails(t *testing.T) { func TestAuthenticatedBrokerClient_AllOperations(t *testing.T) { // Create a test store with a broker secret - db, err := sqlite.New(":memory:") + db, err := newTestStore(":memory:") if err != nil { t.Fatalf("failed to create test store: %v", err) } @@ -427,13 +426,13 @@ func TestAuthenticatedBrokerClient_AllOperations(t *testing.T) { } // Create a test broker - brokerID := "test-host-ops" + brokerID := tid("test-host-ops") secretKey := []byte("ops-test-secret-key-32-bytes!!!!") broker := &store.RuntimeBroker{ ID: brokerID, - Name: "test-host-ops", - Slug: "test-host-ops", + Name: tid("test-host-ops"), + Slug: tid("test-host-ops"), Status: store.BrokerStatusOnline, Created: time.Now(), Updated: time.Now(), diff --git a/pkg/hub/capabilities_test.go b/pkg/hub/capabilities_test.go index d6f7216e8..2c8baff10 100644 --- a/pkg/hub/capabilities_test.go +++ b/pkg/hub/capabilities_test.go @@ -42,11 +42,11 @@ func TestComputeCapabilities_OwnerGetsAllActions(t *testing.T) { ctx := context.Background() require.NoError(t, s.CreateUser(ctx, &store.User{ - ID: "user-owner-cap", Email: "owner-cap@test.com", DisplayName: "Owner", Role: "member", Status: "active", + ID: tid("user-owner-cap"), Email: "owner-cap@test.com", DisplayName: "Owner", Role: "member", Status: "active", })) - user := NewAuthenticatedUser("user-owner-cap", "owner-cap@test.com", "Owner", "member", "api") - resource := Resource{Type: "agent", ID: "agent-1", OwnerID: "user-owner-cap"} + user := NewAuthenticatedUser(tid("user-owner-cap"), "owner-cap@test.com", "Owner", "member", "api") + resource := Resource{Type: "agent", ID: tid("agent-1"), OwnerID: tid("user-owner-cap")} caps := srv.authzService.ComputeCapabilities(ctx, user, resource) assert.Equal(t, []string{"read", "update", "delete", "start", "stop", "message", "attach"}, caps.Actions) @@ -57,20 +57,20 @@ func TestComputeCapabilities_PolicySubset(t *testing.T) { ctx := context.Background() require.NoError(t, s.CreateUser(ctx, &store.User{ - ID: "user-readonly-cap", Email: "readonly-cap@test.com", DisplayName: "ReadOnly", Role: "member", Status: "active", + ID: tid("user-readonly-cap"), Email: "readonly-cap@test.com", DisplayName: "ReadOnly", Role: "member", Status: "active", })) policy := &store.Policy{ - ID: "policy-ro-cap", Name: "Read Only", ScopeType: "hub", + ID: tid("policy-ro-cap"), Name: "Read Only", ScopeType: "hub", ResourceType: "agent", Actions: []string{"read"}, Effect: "allow", } require.NoError(t, s.CreatePolicy(ctx, policy)) require.NoError(t, s.AddPolicyBinding(ctx, &store.PolicyBinding{ - PolicyID: "policy-ro-cap", PrincipalType: "user", PrincipalID: "user-readonly-cap", + PolicyID: tid("policy-ro-cap"), PrincipalType: "user", PrincipalID: tid("user-readonly-cap"), })) - user := NewAuthenticatedUser("user-readonly-cap", "readonly-cap@test.com", "ReadOnly", "member", "api") - resource := Resource{Type: "agent", ID: "agent-1"} + user := NewAuthenticatedUser(tid("user-readonly-cap"), "readonly-cap@test.com", "ReadOnly", "member", "api") + resource := Resource{Type: "agent", ID: tid("agent-1")} caps := srv.authzService.ComputeCapabilities(ctx, user, resource) assert.Equal(t, []string{"read"}, caps.Actions) @@ -81,11 +81,11 @@ func TestComputeCapabilities_DefaultDenyEmpty(t *testing.T) { ctx := context.Background() require.NoError(t, s.CreateUser(ctx, &store.User{ - ID: "user-nopolicy-cap", Email: "nopolicy-cap@test.com", DisplayName: "NoPolicy", Role: "member", Status: "active", + ID: tid("user-nopolicy-cap"), Email: "nopolicy-cap@test.com", DisplayName: "NoPolicy", Role: "member", Status: "active", })) - user := NewAuthenticatedUser("user-nopolicy-cap", "nopolicy-cap@test.com", "NoPolicy", "member", "api") - resource := Resource{Type: "agent", ID: "agent-1"} + user := NewAuthenticatedUser(tid("user-nopolicy-cap"), "nopolicy-cap@test.com", "NoPolicy", "member", "api") + resource := Resource{Type: "agent", ID: tid("agent-1")} caps := srv.authzService.ComputeCapabilities(ctx, user, resource) assert.Equal(t, []string{}, caps.Actions) @@ -97,9 +97,9 @@ func TestComputeCapabilitiesBatch_AdminGetsAll(t *testing.T) { admin := NewAuthenticatedUser("admin-batch", "admin-batch@example.com", "Admin", "admin", "api") resources := []Resource{ - {Type: "agent", ID: "agent-1"}, - {Type: "agent", ID: "agent-2"}, - {Type: "agent", ID: "agent-3"}, + {Type: "agent", ID: tid("agent-1")}, + {Type: "agent", ID: tid("agent-2")}, + {Type: "agent", ID: tid("agent-3")}, } caps := srv.authzService.ComputeCapabilitiesBatch(ctx, admin, resources, "agent") @@ -114,23 +114,23 @@ func TestComputeCapabilitiesBatch_MixedOwnership(t *testing.T) { ctx := context.Background() require.NoError(t, s.CreateUser(ctx, &store.User{ - ID: "user-mixed-cap", Email: "mixed-cap@test.com", DisplayName: "Mixed", Role: "member", Status: "active", + ID: tid("user-mixed-cap"), Email: "mixed-cap@test.com", DisplayName: "Mixed", Role: "member", Status: "active", })) // Policy grants read-only on agents policy := &store.Policy{ - ID: "policy-mixed-cap", Name: "Read Only", ScopeType: "hub", + ID: tid("policy-mixed-cap"), Name: "Read Only", ScopeType: "hub", ResourceType: "agent", Actions: []string{"read"}, Effect: "allow", } require.NoError(t, s.CreatePolicy(ctx, policy)) require.NoError(t, s.AddPolicyBinding(ctx, &store.PolicyBinding{ - PolicyID: "policy-mixed-cap", PrincipalType: "user", PrincipalID: "user-mixed-cap", + PolicyID: tid("policy-mixed-cap"), PrincipalType: "user", PrincipalID: tid("user-mixed-cap"), })) - user := NewAuthenticatedUser("user-mixed-cap", "mixed-cap@test.com", "Mixed", "member", "api") + user := NewAuthenticatedUser(tid("user-mixed-cap"), "mixed-cap@test.com", "Mixed", "member", "api") resources := []Resource{ - {Type: "agent", ID: "agent-owned", OwnerID: "user-mixed-cap"}, // Owned - {Type: "agent", ID: "agent-other", OwnerID: "other-user"}, // Not owned + {Type: "agent", ID: "agent-owned", OwnerID: tid("user-mixed-cap")}, // Owned + {Type: "agent", ID: tid("agent-other"), OwnerID: tid("other-user")}, // Not owned } caps := srv.authzService.ComputeCapabilitiesBatch(ctx, user, resources, "agent") @@ -148,15 +148,15 @@ func TestComputeCapabilities_AncestorGetsAllActions(t *testing.T) { ctx := context.Background() require.NoError(t, s.CreateUser(ctx, &store.User{ - ID: "user-ancestor-cap", Email: "ancestor-cap@test.com", DisplayName: "Ancestor", Role: "member", Status: "active", + ID: tid("user-ancestor-cap"), Email: "ancestor-cap@test.com", DisplayName: "Ancestor", Role: "member", Status: "active", })) - user := NewAuthenticatedUser("user-ancestor-cap", "ancestor-cap@test.com", "Ancestor", "member", "api") + user := NewAuthenticatedUser(tid("user-ancestor-cap"), "ancestor-cap@test.com", "Ancestor", "member", "api") resource := Resource{ Type: "agent", ID: "agent-descendant", OwnerID: "someone-else", - Ancestry: []string{"user-ancestor-cap", "agent-middle"}, + Ancestry: []string{tid("user-ancestor-cap"), "agent-middle"}, } caps := srv.authzService.ComputeCapabilities(ctx, user, resource) @@ -168,13 +168,13 @@ func TestComputeCapabilitiesBatch_AncestryAccess(t *testing.T) { ctx := context.Background() require.NoError(t, s.CreateUser(ctx, &store.User{ - ID: "user-batch-ancestor", Email: "batch-ancestor@test.com", DisplayName: "BatchAnc", Role: "member", Status: "active", + ID: tid("user-batch-ancestor"), Email: "batch-ancestor@test.com", DisplayName: "BatchAnc", Role: "member", Status: "active", })) - user := NewAuthenticatedUser("user-batch-ancestor", "batch-ancestor@test.com", "BatchAnc", "member", "api") + user := NewAuthenticatedUser(tid("user-batch-ancestor"), "batch-ancestor@test.com", "BatchAnc", "member", "api") resources := []Resource{ - {Type: "agent", ID: "agent-descendant-1", OwnerID: "other", Ancestry: []string{"user-batch-ancestor", "agent-A"}}, - {Type: "agent", ID: "agent-unrelated", OwnerID: "other", Ancestry: []string{"other-user"}}, + {Type: "agent", ID: "agent-descendant-1", OwnerID: "other", Ancestry: []string{tid("user-batch-ancestor"), "agent-A"}}, + {Type: "agent", ID: "agent-unrelated", OwnerID: "other", Ancestry: []string{tid("other-user")}}, } caps := srv.authzService.ComputeCapabilitiesBatch(ctx, user, resources, "agent") @@ -202,10 +202,10 @@ func TestComputeScopeCapabilities_NoPolicy(t *testing.T) { ctx := context.Background() require.NoError(t, s.CreateUser(ctx, &store.User{ - ID: "user-noscope-cap", Email: "noscope-cap@test.com", DisplayName: "NoScope", Role: "member", Status: "active", + ID: tid("user-noscope-cap"), Email: "noscope-cap@test.com", DisplayName: "NoScope", Role: "member", Status: "active", })) - user := NewAuthenticatedUser("user-noscope-cap", "noscope-cap@test.com", "NoScope", "member", "api") + user := NewAuthenticatedUser(tid("user-noscope-cap"), "noscope-cap@test.com", "NoScope", "member", "api") caps := srv.authzService.ComputeScopeCapabilities(ctx, user, "", "", "agent") assert.Equal(t, []string{}, caps.Actions) } @@ -312,22 +312,22 @@ func TestComputeCapabilitiesBatch_EmptyList(t *testing.T) { func TestResourceBuilders(t *testing.T) { t.Run("agentResource", func(t *testing.T) { - a := &store.Agent{ID: "a1", OwnerID: "u1", ProjectID: "g1", Labels: map[string]string{"env": "prod"}, Ancestry: []string{"u1"}} + a := &store.Agent{ID: "a1", OwnerID: "u1", ProjectID: tid("g1"), Labels: map[string]string{"env": "prod"}, Ancestry: []string{"u1"}} r := agentResource(a) assert.Equal(t, "agent", r.Type) assert.Equal(t, "a1", r.ID) assert.Equal(t, "u1", r.OwnerID) assert.Equal(t, "project", r.ParentType) - assert.Equal(t, "g1", r.ParentID) + assert.Equal(t, tid("g1"), r.ParentID) assert.Equal(t, "prod", r.Labels["env"]) assert.Equal(t, []string{"u1"}, r.Ancestry) }) t.Run("projectResource", func(t *testing.T) { - g := &store.Project{ID: "g1", OwnerID: "u1"} + g := &store.Project{ID: tid("g1"), OwnerID: "u1"} r := projectResource(g) assert.Equal(t, "project", r.Type) - assert.Equal(t, "g1", r.ID) + assert.Equal(t, tid("g1"), r.ID) assert.Equal(t, "u1", r.OwnerID) }) diff --git a/pkg/hub/capability_marshal_test.go b/pkg/hub/capability_marshal_test.go index b893a242e..c6ca5c153 100644 --- a/pkg/hub/capability_marshal_test.go +++ b/pkg/hub/capability_marshal_test.go @@ -26,8 +26,8 @@ import ( func TestAgentWithCapabilities_MarshalJSON(t *testing.T) { agent := AgentWithCapabilities{ Agent: store.Agent{ - ID: "agent-1", - ProjectID: "project-1", + ID: tid("agent-1"), + ProjectID: tid("project-1"), Name: "my-agent", }, Cap: &Capabilities{ @@ -45,8 +45,8 @@ func TestAgentWithCapabilities_MarshalJSON(t *testing.T) { require.NoError(t, err) // Check embedded Agent fields - assert.Equal(t, "agent-1", m["id"]) - assert.Equal(t, "project-1", m["projectId"]) + assert.Equal(t, tid("agent-1"), m["id"]) + assert.Equal(t, tid("project-1"), m["projectId"]) assert.Equal(t, "my-agent", m["name"]) // Check capability fields @@ -56,7 +56,7 @@ func TestAgentWithCapabilities_MarshalJSON(t *testing.T) { assert.Equal(t, true, m["cloudLogging"]) // Check legacy fields - assert.Equal(t, "project-1", m["groveId"]) + assert.Equal(t, tid("project-1"), m["groveId"]) } func TestProjectWithCapabilities_MarshalJSON(t *testing.T) { @@ -64,7 +64,7 @@ func TestProjectWithCapabilities_MarshalJSON(t *testing.T) { Project: store.Project{ ID: "p-1", Name: "Project 1", - Slug: "project-1", + Slug: tid("project-1"), }, Cap: &Capabilities{ Actions: []string{"write"}, @@ -82,7 +82,7 @@ func TestProjectWithCapabilities_MarshalJSON(t *testing.T) { // Check embedded Project fields assert.Equal(t, "p-1", m["id"]) assert.Equal(t, "Project 1", m["name"]) - assert.Equal(t, "project-1", m["slug"]) + assert.Equal(t, tid("project-1"), m["slug"]) // Check capability fields assert.NotNil(t, m["_capabilities"]) @@ -92,7 +92,7 @@ func TestProjectWithCapabilities_MarshalJSON(t *testing.T) { // Check legacy fields assert.Equal(t, "p-1", m["groveId"]) assert.Equal(t, "Project 1", m["groveName"]) - assert.Equal(t, "project-1", m["grove"]) + assert.Equal(t, tid("project-1"), m["grove"]) } func TestTemplateWithCapabilities_MarshalJSON(t *testing.T) { diff --git a/pkg/hub/controlchannel_test.go b/pkg/hub/controlchannel_test.go index 71b19ce3b..e817cccfd 100644 --- a/pkg/hub/controlchannel_test.go +++ b/pkg/hub/controlchannel_test.go @@ -40,10 +40,10 @@ func TestControlChannelManager_OnDisconnectCallback(t *testing.T) { // Manually add a connection entry so removeConnection has something to remove mgr.mu.Lock() - mgr.connections["broker-1"] = &BrokerConnection{brokerID: "broker-1"} + mgr.connections[tid("broker-1")] = &BrokerConnection{brokerID: tid("broker-1")} mgr.mu.Unlock() - mgr.removeConnection("broker-1") + mgr.removeConnection(tid("broker-1")) // Wait for async callback select { @@ -54,10 +54,10 @@ func TestControlChannelManager_OnDisconnectCallback(t *testing.T) { mu.Lock() defer mu.Unlock() - assert.Equal(t, "broker-1", receivedBrokerID) + assert.Equal(t, tid("broker-1"), receivedBrokerID) // Verify connection was removed - require.False(t, mgr.IsConnected("broker-1")) + require.False(t, mgr.IsConnected(tid("broker-1"))) } func TestControlChannelManager_OnDisconnectCallback_NilSafe(t *testing.T) { @@ -65,11 +65,11 @@ func TestControlChannelManager_OnDisconnectCallback_NilSafe(t *testing.T) { // Don't set any callback - verify removeConnection doesn't panic mgr.mu.Lock() - mgr.connections["broker-2"] = &BrokerConnection{brokerID: "broker-2"} + mgr.connections[tid("broker-2")] = &BrokerConnection{brokerID: tid("broker-2")} mgr.mu.Unlock() // This should not panic - mgr.removeConnection("broker-2") + mgr.removeConnection(tid("broker-2")) - require.False(t, mgr.IsConnected("broker-2")) + require.False(t, mgr.IsConnected(tid("broker-2"))) } diff --git a/pkg/hub/demo_policy_test.go b/pkg/hub/demo_policy_test.go index db461fecd..978a13fd1 100644 --- a/pkg/hub/demo_policy_test.go +++ b/pkg/hub/demo_policy_test.go @@ -68,7 +68,7 @@ func setupDemoPolicyTest(t *testing.T) (*Server, store.Store, *store.User, *stor // Create users alice := &store.User{ - ID: "user-alice", + ID: tid("user-alice"), Email: "alice@test.com", DisplayName: "Alice", Role: store.UserRoleMember, @@ -78,7 +78,7 @@ func setupDemoPolicyTest(t *testing.T) (*Server, store.Store, *store.User, *stor require.NoError(t, s.CreateUser(ctx, alice)) bob := &store.User{ - ID: "user-bob", + ID: tid("user-bob"), Email: "bob@test.com", DisplayName: "Bob", Role: store.UserRoleMember, @@ -93,7 +93,7 @@ func setupDemoPolicyTest(t *testing.T) (*Server, store.Store, *store.User, *stor // Create a project owned by alice project := &store.Project{ - ID: "project-demo", + ID: tid("project-demo"), Name: "Demo Project", Slug: "demo-project", OwnerID: alice.ID, @@ -145,7 +145,7 @@ func TestDemoPolicy_AgentCreate_AdminBypass(t *testing.T) { // Create an admin user (not a project member) admin := &store.User{ - ID: "user-admin", + ID: tid("user-admin"), Email: "admin@test.com", DisplayName: "Admin", Role: store.UserRoleAdmin, @@ -173,8 +173,8 @@ func TestDemoPolicy_AgentDelete_OwnerAllowed(t *testing.T) { // Create an agent owned by alice agent := &store.Agent{ - ID: "agent-del-owner", - Slug: "agent-del-owner", + ID: tid("agent-del-owner"), + Slug: tid("agent-del-owner"), Name: "Agent to Delete", ProjectID: project.ID, OwnerID: alice.ID, @@ -199,8 +199,8 @@ func TestDemoPolicy_AgentDelete_NonOwnerDenied(t *testing.T) { // Create an agent owned by alice agent := &store.Agent{ - ID: "agent-del-nonowner", - Slug: "agent-del-nonowner", + ID: tid("agent-del-nonowner"), + Slug: tid("agent-del-nonowner"), Name: "Agent to Delete", ProjectID: project.ID, OwnerID: alice.ID, @@ -224,7 +224,7 @@ func TestDemoPolicy_AgentDelete_AdminBypass(t *testing.T) { ctx := context.Background() admin := &store.User{ - ID: "user-admin-del", + ID: tid("user-admin-del"), Email: "admin-del@test.com", DisplayName: "Admin", Role: store.UserRoleAdmin, @@ -234,8 +234,8 @@ func TestDemoPolicy_AgentDelete_AdminBypass(t *testing.T) { require.NoError(t, s.CreateUser(ctx, admin)) agent := &store.Agent{ - ID: "agent-del-admin", - Slug: "agent-del-admin", + ID: tid("agent-del-admin"), + Slug: tid("agent-del-admin"), Name: "Agent for Admin Delete", ProjectID: project.ID, OwnerID: alice.ID, @@ -259,8 +259,8 @@ func TestDemoPolicy_AgentDelete_DirectPath_NonOwnerDenied(t *testing.T) { ctx := context.Background() agent := &store.Agent{ - ID: "agent-del-direct", - Slug: "agent-del-direct", + ID: tid("agent-del-direct"), + Slug: tid("agent-del-direct"), Name: "Agent Direct Delete", ProjectID: project.ID, OwnerID: alice.ID, @@ -288,8 +288,8 @@ func TestDemoPolicy_AgentAction_OwnerAllowed(t *testing.T) { ctx := context.Background() agent := &store.Agent{ - ID: "agent-action-owner", - Slug: "agent-action-owner", + ID: tid("agent-action-owner"), + Slug: tid("agent-action-owner"), Name: "Agent Action Test", ProjectID: project.ID, OwnerID: alice.ID, @@ -318,8 +318,8 @@ func TestDemoPolicy_AgentAction_NonOwnerDenied(t *testing.T) { ctx := context.Background() agent := &store.Agent{ - ID: "agent-action-nonowner", - Slug: "agent-action-nonowner", + ID: tid("agent-action-nonowner"), + Slug: tid("agent-action-nonowner"), Name: "Agent Action Test", ProjectID: project.ID, OwnerID: alice.ID, @@ -347,7 +347,7 @@ func TestDemoPolicy_AgentAction_AdminBypass(t *testing.T) { ctx := context.Background() admin := &store.User{ - ID: "user-admin-action", + ID: tid("user-admin-action"), Email: "admin-action@test.com", DisplayName: "Admin", Role: store.UserRoleAdmin, @@ -357,8 +357,8 @@ func TestDemoPolicy_AgentAction_AdminBypass(t *testing.T) { require.NoError(t, s.CreateUser(ctx, admin)) agent := &store.Agent{ - ID: "agent-action-admin", - Slug: "agent-action-admin", + ID: tid("agent-action-admin"), + Slug: tid("agent-action-admin"), Name: "Agent Admin Action", ProjectID: project.ID, OwnerID: alice.ID, @@ -382,8 +382,8 @@ func TestDemoPolicy_AgentAction_DirectPath_NonOwnerDenied(t *testing.T) { ctx := context.Background() agent := &store.Agent{ - ID: "agent-action-direct", - Slug: "agent-action-direct", + ID: tid("agent-action-direct"), + Slug: tid("agent-action-direct"), Name: "Agent Direct Action", ProjectID: project.ID, OwnerID: alice.ID, @@ -466,7 +466,7 @@ func TestDemoPolicy_EndToEnd_ProjectCreatorCanCreateAgent(t *testing.T) { // Create a non-admin user alice := &store.User{ - ID: "user-e2e-alice", + ID: tid("user-e2e-alice"), Email: "e2e-alice@test.com", DisplayName: "E2E Alice", Role: store.UserRoleMember, @@ -504,7 +504,7 @@ func TestDemoPolicy_HubMembershipOnLogin(t *testing.T) { // Create a user and add to hub-members (simulating login) user := &store.User{ - ID: "user-login-test", + ID: tid("user-login-test"), Email: "login@test.com", DisplayName: "Login User", Role: store.UserRoleMember, @@ -535,7 +535,7 @@ func TestDemoPolicy_ProjectRecreation_CreatorCanCreateAgent(t *testing.T) { ctx := context.Background() alice := &store.User{ - ID: "user-recreate-alice", + ID: tid("user-recreate-alice"), Email: "recreate-alice@test.com", DisplayName: "Alice", Role: store.UserRoleMember, @@ -593,7 +593,7 @@ func TestDemoPolicy_ProjectMembersGroupIdempotent(t *testing.T) { ctx := context.Background() alice := &store.User{ - ID: "user-idempotent-alice", + ID: tid("user-idempotent-alice"), Email: "idempotent-alice@test.com", DisplayName: "Alice", Role: store.UserRoleMember, @@ -604,7 +604,7 @@ func TestDemoPolicy_ProjectMembersGroupIdempotent(t *testing.T) { ensureHubMembership(ctx, s, alice.ID) project := &store.Project{ - ID: "project-idempotent", + ID: tid("project-idempotent"), Name: "Idempotent Project", Slug: "idempotent-project", OwnerID: alice.ID, @@ -641,7 +641,7 @@ func TestDemoPolicy_ProjectDeleteCleansUpGroupsAndPolicies(t *testing.T) { ctx := context.Background() alice := &store.User{ - ID: "user-cleanup-alice", + ID: tid("user-cleanup-alice"), Email: "cleanup-alice@test.com", DisplayName: "Alice", Role: store.UserRoleMember, diff --git a/pkg/hub/embedded_broker_test.go b/pkg/hub/embedded_broker_test.go index 576f225e5..5de191323 100644 --- a/pkg/hub/embedded_broker_test.go +++ b/pkg/hub/embedded_broker_test.go @@ -31,7 +31,7 @@ func TestIsEmbeddedBroker(t *testing.T) { srv := &Server{} // Before setting, everything should return false - if srv.isEmbeddedBroker("broker-1") { + if srv.isEmbeddedBroker(tid("broker-1")) { t.Error("expected isEmbeddedBroker to return false before setting") } if srv.isEmbeddedBroker("") { @@ -39,15 +39,15 @@ func TestIsEmbeddedBroker(t *testing.T) { } // Set the embedded broker ID - srv.SetEmbeddedBrokerID("broker-1") + srv.SetEmbeddedBrokerID(tid("broker-1")) // Matching ID should return true - if !srv.isEmbeddedBroker("broker-1") { + if !srv.isEmbeddedBroker(tid("broker-1")) { t.Error("expected isEmbeddedBroker to return true for matching ID") } // Non-matching ID should return false - if srv.isEmbeddedBroker("broker-2") { + if srv.isEmbeddedBroker(tid("broker-2")) { t.Error("expected isEmbeddedBroker to return false for non-matching ID") } @@ -63,7 +63,7 @@ func TestCreateAgent_SkipsGCSSyncForEmbeddedBroker(t *testing.T) { // Create a project (hub-managed: no git remote) project := &store.Project{ - ID: "project-embedded-test", + ID: tid("project-embedded-test"), Name: "embedded-test", Slug: "embedded-test", } @@ -72,10 +72,11 @@ func TestCreateAgent_SkipsGCSSyncForEmbeddedBroker(t *testing.T) { } // Create a runtime broker - brokerID := "embedded-broker-1" + brokerID := tid("embedded-broker-1") broker := &store.RuntimeBroker{ ID: brokerID, Name: "embedded-broker", + Slug: "embedded-broker", Endpoint: "http://localhost:9090", Status: store.BrokerStatusOnline, } diff --git a/pkg/hub/envgather_resolution_test.go b/pkg/hub/envgather_resolution_test.go index 33909cd21..619ad91fb 100644 --- a/pkg/hub/envgather_resolution_test.go +++ b/pkg/hub/envgather_resolution_test.go @@ -33,19 +33,19 @@ func TestResolution_PlainEnvVar(t *testing.T) { memStore := createTestStore(t) broker := &store.RuntimeBroker{ - ID: "broker-res-1", Name: "res-broker", Slug: "res-broker", + ID: tid("broker-res-1"), Name: "res-broker", Slug: "res-broker", Endpoint: "http://localhost:9800", Status: store.BrokerStatusOnline, } if err := memStore.CreateRuntimeBroker(ctx, broker); err != nil { t.Fatal(err) } - project := &store.Project{ID: "project-res-1", Name: "res-project", Slug: "res-project"} + project := &store.Project{ID: tid("project-res-1"), Name: "res-project", Slug: "res-project"} if err := memStore.CreateProject(ctx, project); err != nil { t.Fatal(err) } if err := memStore.AddProjectProvider(ctx, &store.ProjectProvider{ - ProjectID: "project-res-1", BrokerID: "broker-res-1", + ProjectID: tid("project-res-1"), BrokerID: tid("broker-res-1"), BrokerName: "test-broker", }); err != nil { t.Fatal(err) } @@ -69,9 +69,9 @@ func TestResolution_PlainEnvVar(t *testing.T) { ID: "agent-res-1", Name: "res-agent", Slug: "res-agent", - ProjectID: "project-res-1", + ProjectID: tid("project-res-1"), OwnerID: "user-res-1", - RuntimeBrokerID: "broker-res-1", + RuntimeBrokerID: tid("broker-res-1"), AppliedConfig: &store.AgentAppliedConfig{}, } @@ -94,19 +94,19 @@ func TestResolution_SecretUserScope(t *testing.T) { memStore := createTestStore(t) broker := &store.RuntimeBroker{ - ID: "broker-res-2", Name: "res-broker-2", Slug: "res-broker-2", + ID: tid("broker-res-2"), Name: "res-broker-2", Slug: "res-broker-2", Endpoint: "http://localhost:9800", Status: store.BrokerStatusOnline, } if err := memStore.CreateRuntimeBroker(ctx, broker); err != nil { t.Fatal(err) } - project := &store.Project{ID: "project-res-2", Name: "res-project-2", Slug: "res-project-2"} + project := &store.Project{ID: tid("project-res-2"), Name: "res-project-2", Slug: "res-project-2"} if err := memStore.CreateProject(ctx, project); err != nil { t.Fatal(err) } if err := memStore.AddProjectProvider(ctx, &store.ProjectProvider{ - ProjectID: "project-res-2", BrokerID: "broker-res-2", + ProjectID: tid("project-res-2"), BrokerID: tid("broker-res-2"), BrokerName: "test-broker", }); err != nil { t.Fatal(err) } @@ -132,9 +132,9 @@ func TestResolution_SecretUserScope(t *testing.T) { ID: "agent-res-2", Name: "res-agent-2", Slug: "res-agent-2", - ProjectID: "project-res-2", + ProjectID: tid("project-res-2"), OwnerID: "user-res-2", - RuntimeBrokerID: "broker-res-2", + RuntimeBrokerID: tid("broker-res-2"), AppliedConfig: &store.AgentAppliedConfig{}, } @@ -170,19 +170,19 @@ func TestResolution_ProjectEnvVar(t *testing.T) { memStore := createTestStore(t) broker := &store.RuntimeBroker{ - ID: "broker-res-3", Name: "res-broker-3", Slug: "res-broker-3", + ID: tid("broker-res-3"), Name: "res-broker-3", Slug: "res-broker-3", Endpoint: "http://localhost:9800", Status: store.BrokerStatusOnline, } if err := memStore.CreateRuntimeBroker(ctx, broker); err != nil { t.Fatal(err) } - project := &store.Project{ID: "project-res-3", Name: "res-project-3", Slug: "res-project-3"} + project := &store.Project{ID: tid("project-res-3"), Name: "res-project-3", Slug: "res-project-3"} if err := memStore.CreateProject(ctx, project); err != nil { t.Fatal(err) } if err := memStore.AddProjectProvider(ctx, &store.ProjectProvider{ - ProjectID: "project-res-3", BrokerID: "broker-res-3", + ProjectID: tid("project-res-3"), BrokerID: tid("broker-res-3"), BrokerName: "test-broker", }); err != nil { t.Fatal(err) } @@ -193,7 +193,7 @@ func TestResolution_ProjectEnvVar(t *testing.T) { Key: "GROVE_VAR", Value: "project-var-value", Scope: "project", - ScopeID: "project-res-3", + ScopeID: tid("project-res-3"), }) if err != nil { t.Fatal(err) @@ -206,9 +206,9 @@ func TestResolution_ProjectEnvVar(t *testing.T) { ID: "agent-res-3", Name: "res-agent-3", Slug: "res-agent-3", - ProjectID: "project-res-3", + ProjectID: tid("project-res-3"), OwnerID: "user-res-3", - RuntimeBrokerID: "broker-res-3", + RuntimeBrokerID: tid("broker-res-3"), AppliedConfig: &store.AgentAppliedConfig{}, } @@ -233,19 +233,19 @@ func TestResolution_SecretPromotedEnvVar(t *testing.T) { memStore := createTestStore(t) broker := &store.RuntimeBroker{ - ID: "broker-res-4", Name: "res-broker-4", Slug: "res-broker-4", + ID: tid("broker-res-4"), Name: "res-broker-4", Slug: "res-broker-4", Endpoint: "http://localhost:9800", Status: store.BrokerStatusOnline, } if err := memStore.CreateRuntimeBroker(ctx, broker); err != nil { t.Fatal(err) } - project := &store.Project{ID: "project-res-4", Name: "res-project-4", Slug: "res-project-4"} + project := &store.Project{ID: tid("project-res-4"), Name: "res-project-4", Slug: "res-project-4"} if err := memStore.CreateProject(ctx, project); err != nil { t.Fatal(err) } if err := memStore.AddProjectProvider(ctx, &store.ProjectProvider{ - ProjectID: "project-res-4", BrokerID: "broker-res-4", + ProjectID: tid("project-res-4"), BrokerID: tid("broker-res-4"), BrokerName: "test-broker", }); err != nil { t.Fatal(err) } @@ -294,9 +294,9 @@ func TestResolution_SecretPromotedEnvVar(t *testing.T) { ID: "agent-res-4", Name: "res-agent-4", Slug: "res-agent-4", - ProjectID: "project-res-4", + ProjectID: tid("project-res-4"), OwnerID: "user-res-4", - RuntimeBrokerID: "broker-res-4", + RuntimeBrokerID: tid("broker-res-4"), AppliedConfig: &store.AgentAppliedConfig{}, } diff --git a/pkg/hub/envgather_test.go b/pkg/hub/envgather_test.go index 4d8f922b7..d4c844612 100644 --- a/pkg/hub/envgather_test.go +++ b/pkg/hub/envgather_test.go @@ -88,7 +88,7 @@ func TestEnvGather_HubDispatch_AllSatisfied(t *testing.T) { memStore := createTestStore(t) broker := &store.RuntimeBroker{ - ID: "broker-1", + ID: tid("broker-1"), Name: "test-broker", Slug: "test-broker", Endpoint: "http://localhost:9800", @@ -99,7 +99,7 @@ func TestEnvGather_HubDispatch_AllSatisfied(t *testing.T) { } project := &store.Project{ - ID: "project-1", + ID: tid("project-1"), Name: "test-project", Slug: "test-project", } @@ -109,8 +109,8 @@ func TestEnvGather_HubDispatch_AllSatisfied(t *testing.T) { // Add provider so broker can serve this project if err := memStore.AddProjectProvider(ctx, &store.ProjectProvider{ - ProjectID: "project-1", - BrokerID: "broker-1", + ProjectID: tid("project-1"), + BrokerID: tid("broker-1"), BrokerName: "test-broker", }); err != nil { t.Fatal(err) } @@ -119,11 +119,11 @@ func TestEnvGather_HubDispatch_AllSatisfied(t *testing.T) { dispatcher := NewHTTPAgentDispatcherWithClient(memStore, mockClient, true, slog.Default()) agent := &store.Agent{ - ID: "agent-1", + ID: tid("agent-1"), Name: "test-agent", Slug: "test-agent", - ProjectID: "project-1", - RuntimeBrokerID: "broker-1", + ProjectID: tid("project-1"), + RuntimeBrokerID: tid("broker-1"), AppliedConfig: &store.AgentAppliedConfig{ HarnessConfig: "claude", }, @@ -155,7 +155,7 @@ func TestEnvGather_HubDispatch_NeedsGather(t *testing.T) { memStore := createTestStore(t) broker := &store.RuntimeBroker{ - ID: "broker-2", + ID: tid("broker-2"), Name: "test-broker-2", Slug: "test-broker-2", Endpoint: "http://localhost:9800", @@ -167,7 +167,7 @@ func TestEnvGather_HubDispatch_NeedsGather(t *testing.T) { mockClient := &envGatherMockBrokerClient{ gatherReturnEnvReqs: &RemoteEnvRequirementsResponse{ - AgentID: "agent-2", + AgentID: tid("agent-2"), Required: []string{"API_KEY", "SECRET"}, HubHas: []string{"API_KEY"}, Needs: []string{"SECRET"}, @@ -176,11 +176,11 @@ func TestEnvGather_HubDispatch_NeedsGather(t *testing.T) { dispatcher := NewHTTPAgentDispatcherWithClient(memStore, mockClient, true, slog.Default()) agent := &store.Agent{ - ID: "agent-2", + ID: tid("agent-2"), Name: "test-agent-2", Slug: "test-agent-2", - ProjectID: "project-1", - RuntimeBrokerID: "broker-2", + ProjectID: tid("project-1"), + RuntimeBrokerID: tid("broker-2"), AppliedConfig: &store.AgentAppliedConfig{ HarnessConfig: "claude", }, @@ -210,7 +210,7 @@ func TestEnvGather_HubDispatch_FinalizeEnv(t *testing.T) { memStore := createTestStore(t) broker := &store.RuntimeBroker{ - ID: "broker-3", + ID: tid("broker-3"), Name: "test-broker-3", Slug: "test-broker-3", Endpoint: "http://localhost:9800", @@ -224,11 +224,11 @@ func TestEnvGather_HubDispatch_FinalizeEnv(t *testing.T) { dispatcher := NewHTTPAgentDispatcherWithClient(memStore, mockClient, true, slog.Default()) agent := &store.Agent{ - ID: "agent-3", + ID: tid("agent-3"), Name: "test-agent-3", Slug: "test-agent-3", - ProjectID: "project-1", - RuntimeBrokerID: "broker-3", + ProjectID: tid("project-1"), + RuntimeBrokerID: tid("broker-3"), } gatheredEnv := map[string]string{ @@ -257,14 +257,14 @@ func TestEnvGather_HubHandler_202Response(t *testing.T) { ctx := context.Background() // Create project - project := &store.Project{ID: "project-gather", Name: "gather-project", Slug: "gather-project"} + project := &store.Project{ID: tid("project-gather"), Name: "gather-project", Slug: "gather-project"} if err := st.CreateProject(ctx, project); err != nil { t.Fatal(err) } // Create broker broker := &store.RuntimeBroker{ - ID: "broker-gather", Name: "gather-broker", Slug: "gather-broker", + ID: tid("broker-gather"), Name: "gather-broker", Slug: "gather-broker", Endpoint: "http://localhost:9800", Status: store.BrokerStatusOnline, } if err := st.CreateRuntimeBroker(ctx, broker); err != nil { @@ -273,7 +273,7 @@ func TestEnvGather_HubHandler_202Response(t *testing.T) { // Add provider with local path so template can be resolved locally if err := st.AddProjectProvider(ctx, &store.ProjectProvider{ - ProjectID: "project-gather", BrokerID: "broker-gather", + ProjectID: tid("project-gather"), BrokerID: tid("broker-gather"), BrokerName: "test-broker", LocalPath: "/tmp/test-project", }); err != nil { t.Fatal(err) @@ -293,7 +293,7 @@ func TestEnvGather_HubHandler_202Response(t *testing.T) { // Create agent with GatherEnv=true reqBody := map[string]interface{}{ "name": "gather-agent", - "projectId": "project-gather", + "projectId": tid("project-gather"), "template": "claude", "gatherEnv": true, } @@ -333,14 +333,14 @@ func TestEnvGather_HubHandler_ProjectRoute_202Response(t *testing.T) { ctx := context.Background() // Create project - project := &store.Project{ID: "project-gather-route", Name: "gather-route-project", Slug: "gather-route-project"} + project := &store.Project{ID: tid("project-gather-route"), Name: "gather-route-project", Slug: "gather-route-project"} if err := st.CreateProject(ctx, project); err != nil { t.Fatal(err) } // Create broker broker := &store.RuntimeBroker{ - ID: "broker-gather-route", Name: "gather-route-broker", Slug: "gather-route-broker", + ID: tid("broker-gather-route"), Name: "gather-route-broker", Slug: "gather-route-broker", Endpoint: "http://localhost:9800", Status: store.BrokerStatusOnline, } if err := st.CreateRuntimeBroker(ctx, broker); err != nil { @@ -349,7 +349,7 @@ func TestEnvGather_HubHandler_ProjectRoute_202Response(t *testing.T) { // Add provider with local path so template can be resolved locally if err := st.AddProjectProvider(ctx, &store.ProjectProvider{ - ProjectID: "project-gather-route", BrokerID: "broker-gather-route", + ProjectID: tid("project-gather-route"), BrokerID: tid("broker-gather-route"), BrokerName: "test-broker", LocalPath: "/tmp/test-project", }); err != nil { t.Fatal(err) @@ -413,14 +413,14 @@ func TestEnvGather_HubHandler_SubmitEnv(t *testing.T) { ctx := context.Background() // Create project - project := &store.Project{ID: "project-submit", Name: "submit-project", Slug: "submit-project"} + project := &store.Project{ID: tid("project-submit"), Name: "submit-project", Slug: "submit-project"} if err := st.CreateProject(ctx, project); err != nil { t.Fatal(err) } // Create broker broker := &store.RuntimeBroker{ - ID: "broker-submit", Name: "submit-broker", Slug: "submit-broker", + ID: tid("broker-submit"), Name: "submit-broker", Slug: "submit-broker", Endpoint: "http://localhost:9800", Status: store.BrokerStatusOnline, } if err := st.CreateRuntimeBroker(ctx, broker); err != nil { @@ -429,11 +429,11 @@ func TestEnvGather_HubHandler_SubmitEnv(t *testing.T) { // Create agent in provisioning state (as if 202 was already returned) agent := &store.Agent{ - ID: "agent-submit", + ID: tid("agent-submit"), Name: "submit-agent", Slug: "submit-agent", - ProjectID: "project-submit", - RuntimeBrokerID: "broker-submit", + ProjectID: tid("project-submit"), + RuntimeBrokerID: tid("broker-submit"), Phase: string(state.PhaseProvisioning), AppliedConfig: &store.AgentAppliedConfig{ HarnessConfig: "claude", @@ -455,7 +455,7 @@ func TestEnvGather_HubHandler_SubmitEnv(t *testing.T) { }, } - path := "/api/v1/projects/project-submit/agents/submit-agent/env" + path := fmt.Sprintf("/api/v1/projects/%s/agents/submit-agent/env", tid("project-submit")) rec := doRequest(t, srv, http.MethodPost, path, reqBody) if rec.Code != http.StatusOK { @@ -471,7 +471,7 @@ func TestEnvGather_HubHandler_SubmitEnv(t *testing.T) { } // Agent should be updated to running - updated, err := st.GetAgent(ctx, "agent-submit") + updated, err := st.GetAgent(ctx, tid("agent-submit")) if err != nil { t.Fatal(err) } @@ -487,17 +487,17 @@ func TestEnvGather_HubHandler_SubmitEnv_InvalidState(t *testing.T) { ctx := context.Background() // Create project - project := &store.Project{ID: "project-invalid", Name: "invalid-project", Slug: "invalid-project"} + project := &store.Project{ID: tid("project-invalid"), Name: "invalid-project", Slug: "invalid-project"} if err := st.CreateProject(ctx, project); err != nil { t.Fatal(err) } // Create agent in running state (not valid for env submission) agent := &store.Agent{ - ID: "agent-invalid", + ID: tid("agent-invalid"), Name: "invalid-agent", Slug: "invalid-agent", - ProjectID: "project-invalid", + ProjectID: tid("project-invalid"), Phase: string(state.PhaseRunning), } if err := st.CreateAgent(ctx, agent); err != nil { @@ -508,7 +508,7 @@ func TestEnvGather_HubHandler_SubmitEnv_InvalidState(t *testing.T) { "env": map[string]string{"KEY": "value"}, } - path := "/api/v1/projects/project-invalid/agents/invalid-agent/env" + path := fmt.Sprintf("/api/v1/projects/%s/agents/invalid-agent/env", tid("project-invalid")) rec := doRequest(t, srv, http.MethodPost, path, reqBody) if rec.Code != http.StatusConflict { @@ -523,14 +523,14 @@ func TestEnvGather_HubEnvResolution(t *testing.T) { memStore := createTestStore(t) // Create project - project := &store.Project{ID: "project-env", Name: "env-project", Slug: "env-project"} + project := &store.Project{ID: tid("project-env"), Name: "env-project", Slug: "env-project"} if err := memStore.CreateProject(ctx, project); err != nil { t.Fatal(err) } // Create broker broker := &store.RuntimeBroker{ - ID: "broker-env", Name: "env-broker", Slug: "env-broker", + ID: tid("broker-env"), Name: "env-broker", Slug: "env-broker", Endpoint: "http://localhost:9800", Status: store.BrokerStatusOnline, } if err := memStore.CreateRuntimeBroker(ctx, broker); err != nil { @@ -539,11 +539,11 @@ func TestEnvGather_HubEnvResolution(t *testing.T) { // Store env vars in project scope if err := memStore.CreateEnvVar(ctx, &store.EnvVar{ - ID: "env-1", + ID: tid("env-1"), Key: "GROVE_API_KEY", Value: "project-key-value", Scope: "project", - ScopeID: "project-env", + ScopeID: tid("project-env"), }); err != nil { t.Fatal(err) } @@ -555,8 +555,8 @@ func TestEnvGather_HubEnvResolution(t *testing.T) { ID: "agent-env", Name: "env-agent", Slug: "env-agent", - ProjectID: "project-env", - RuntimeBrokerID: "broker-env", + ProjectID: tid("project-env"), + RuntimeBrokerID: tid("broker-env"), AppliedConfig: &store.AgentAppliedConfig{ HarnessConfig: "claude", }, @@ -585,13 +585,13 @@ func TestEnvGather_HubHandler_RetryAfterCancel_GlobalRoute(t *testing.T) { srv, st := testServer(t) ctx := context.Background() - project := &store.Project{ID: "project-retry-global", Name: "retry-global-project", Slug: "retry-global-project"} + project := &store.Project{ID: tid("project-retry-global"), Name: "retry-global-project", Slug: "retry-global-project"} if err := st.CreateProject(ctx, project); err != nil { t.Fatal(err) } broker := &store.RuntimeBroker{ - ID: "broker-retry-global", Name: "retry-global-broker", Slug: "retry-global-broker", + ID: tid("broker-retry-global"), Name: "retry-global-broker", Slug: "retry-global-broker", Endpoint: "http://localhost:9800", Status: store.BrokerStatusOnline, } if err := st.CreateRuntimeBroker(ctx, broker); err != nil { @@ -599,7 +599,7 @@ func TestEnvGather_HubHandler_RetryAfterCancel_GlobalRoute(t *testing.T) { } if err := st.AddProjectProvider(ctx, &store.ProjectProvider{ - ProjectID: "project-retry-global", BrokerID: "broker-retry-global", + ProjectID: tid("project-retry-global"), BrokerID: tid("broker-retry-global"), BrokerName: "test-broker", LocalPath: "/tmp/test-project", }); err != nil { t.Fatal(err) @@ -607,11 +607,11 @@ func TestEnvGather_HubHandler_RetryAfterCancel_GlobalRoute(t *testing.T) { // Simulate a previous cancelled env-gather: agent exists in "provisioning" status staleAgent := &store.Agent{ - ID: "stale-agent-global", + ID: tid("stale-agent-global"), Name: "retry-agent", Slug: "retry-agent", - ProjectID: "project-retry-global", - RuntimeBrokerID: "broker-retry-global", + ProjectID: tid("project-retry-global"), + RuntimeBrokerID: tid("broker-retry-global"), Phase: string(state.PhaseProvisioning), AppliedConfig: &store.AgentAppliedConfig{ HarnessConfig: "claude", @@ -635,7 +635,7 @@ func TestEnvGather_HubHandler_RetryAfterCancel_GlobalRoute(t *testing.T) { // Second create request with GatherEnv=true reqBody := map[string]interface{}{ "name": "retry-agent", - "projectId": "project-retry-global", + "projectId": tid("project-retry-global"), "template": "claude", "gatherEnv": true, } @@ -669,7 +669,7 @@ func TestEnvGather_HubHandler_RetryAfterCancel_GlobalRoute(t *testing.T) { if resp.Agent == nil { t.Fatal("expected agent in response") } - if resp.Agent.ID == "stale-agent-global" { + if resp.Agent.ID == tid("stale-agent-global") { t.Error("expected a new agent ID, got the stale agent ID") } if resp.Agent.Phase != string(state.PhaseProvisioning) { @@ -677,7 +677,7 @@ func TestEnvGather_HubHandler_RetryAfterCancel_GlobalRoute(t *testing.T) { } // The old agent should no longer exist in the store - _, err := st.GetAgent(ctx, "stale-agent-global") + _, err := st.GetAgent(ctx, tid("stale-agent-global")) if err != store.ErrNotFound { t.Errorf("expected stale agent to be deleted, got err=%v", err) } @@ -692,7 +692,7 @@ func TestEnvGather_BuildResponse_SecretScope(t *testing.T) { // Create a user secret for API_KEY if err := st.CreateSecret(ctx, &store.Secret{ - ID: "sec-1", + ID: tid("sec-1"), Key: "API_KEY", EncryptedValue: "encrypted-val", SecretType: store.SecretTypeEnvironment, @@ -711,7 +711,7 @@ func TestEnvGather_BuildResponse_SecretScope(t *testing.T) { ID: "agent-scope-test", Name: "scope-test-agent", OwnerID: "owner-1", - ProjectID: "project-1", + ProjectID: tid("project-1"), } brokerReqs := &RemoteEnvRequirementsResponse{ @@ -756,13 +756,13 @@ func TestEnvGather_SecretInfoRelay(t *testing.T) { srv, st := testServer(t) ctx := context.Background() - project := &store.Project{ID: "project-si-relay", Name: "si-relay-project", Slug: "si-relay-project"} + project := &store.Project{ID: tid("project-si-relay"), Name: "si-relay-project", Slug: "si-relay-project"} if err := st.CreateProject(ctx, project); err != nil { t.Fatal(err) } broker := &store.RuntimeBroker{ - ID: "broker-si-relay", Name: "si-relay-broker", Slug: "si-relay-broker", + ID: tid("broker-si-relay"), Name: "si-relay-broker", Slug: "si-relay-broker", Endpoint: "http://localhost:9800", Status: store.BrokerStatusOnline, } if err := st.CreateRuntimeBroker(ctx, broker); err != nil { @@ -770,7 +770,7 @@ func TestEnvGather_SecretInfoRelay(t *testing.T) { } if err := st.AddProjectProvider(ctx, &store.ProjectProvider{ - ProjectID: "project-si-relay", BrokerID: "broker-si-relay", + ProjectID: tid("project-si-relay"), BrokerID: tid("broker-si-relay"), BrokerName: "test-broker", LocalPath: "/tmp/test-project", }); err != nil { t.Fatal(err) @@ -793,7 +793,7 @@ func TestEnvGather_SecretInfoRelay(t *testing.T) { reqBody := map[string]interface{}{ "name": "si-relay-agent", - "projectId": "project-si-relay", + "projectId": tid("project-si-relay"), "template": "claude", "gatherEnv": true, } @@ -835,13 +835,13 @@ func TestEnvGather_SecretInfoRelayType(t *testing.T) { srv, st := testServer(t) ctx := context.Background() - project := &store.Project{ID: "project-si-type", Name: "si-type-project", Slug: "si-type-project"} + project := &store.Project{ID: tid("project-si-type"), Name: "si-type-project", Slug: "si-type-project"} if err := st.CreateProject(ctx, project); err != nil { t.Fatal(err) } broker := &store.RuntimeBroker{ - ID: "broker-si-type", Name: "si-type-broker", Slug: "si-type-broker", + ID: tid("broker-si-type"), Name: "si-type-broker", Slug: "si-type-broker", Endpoint: "http://localhost:9800", Status: store.BrokerStatusOnline, } if err := st.CreateRuntimeBroker(ctx, broker); err != nil { @@ -849,7 +849,7 @@ func TestEnvGather_SecretInfoRelayType(t *testing.T) { } if err := st.AddProjectProvider(ctx, &store.ProjectProvider{ - ProjectID: "project-si-type", BrokerID: "broker-si-type", + ProjectID: tid("project-si-type"), BrokerID: tid("broker-si-type"), BrokerName: "test-broker", LocalPath: "/tmp/test-project", }); err != nil { t.Fatal(err) @@ -873,7 +873,7 @@ func TestEnvGather_SecretInfoRelayType(t *testing.T) { reqBody := map[string]interface{}{ "name": "si-type-agent", - "projectId": "project-si-type", + "projectId": tid("project-si-type"), "template": "claude", "gatherEnv": true, } @@ -923,13 +923,13 @@ func TestNonGatherEnv_MissingEnvVars_Returns422(t *testing.T) { srv, st := testServer(t) ctx := context.Background() - project := &store.Project{ID: "project-nogather-missing", Name: "nogather-missing-project", Slug: "nogather-missing-project"} + project := &store.Project{ID: tid("project-nogather-missing"), Name: "nogather-missing-project", Slug: "nogather-missing-project"} if err := st.CreateProject(ctx, project); err != nil { t.Fatal(err) } broker := &store.RuntimeBroker{ - ID: "broker-nogather-missing", Name: "nogather-missing-broker", Slug: "nogather-missing-broker", + ID: tid("broker-nogather-missing"), Name: "nogather-missing-broker", Slug: "nogather-missing-broker", Endpoint: "http://localhost:9800", Status: store.BrokerStatusOnline, } if err := st.CreateRuntimeBroker(ctx, broker); err != nil { @@ -937,7 +937,7 @@ func TestNonGatherEnv_MissingEnvVars_Returns422(t *testing.T) { } if err := st.AddProjectProvider(ctx, &store.ProjectProvider{ - ProjectID: "project-nogather-missing", BrokerID: "broker-nogather-missing", + ProjectID: tid("project-nogather-missing"), BrokerID: tid("broker-nogather-missing"), BrokerName: "test-broker", LocalPath: "/tmp/test-project", }); err != nil { t.Fatal(err) @@ -958,7 +958,7 @@ func TestNonGatherEnv_MissingEnvVars_Returns422(t *testing.T) { // Create agent WITHOUT GatherEnv (simulating web/API caller) reqBody := map[string]interface{}{ "name": "nogather-missing-agent", - "projectId": "project-nogather-missing", + "projectId": tid("project-nogather-missing"), "template": "claude", // gatherEnv is NOT set — this is the non-CLI path } @@ -993,7 +993,7 @@ func TestNonGatherEnv_MissingEnvVars_Returns422(t *testing.T) { } // Agent should have been cleaned up from the store - result, err := st.ListAgents(ctx, store.AgentFilter{ProjectID: "project-nogather-missing"}, store.ListOptions{}) + result, err := st.ListAgents(ctx, store.AgentFilter{ProjectID: tid("project-nogather-missing")}, store.ListOptions{}) if err != nil { t.Fatal(err) } @@ -1008,13 +1008,13 @@ func TestNonGatherEnv_MissingEnvVars_ProjectRoute_Returns422(t *testing.T) { srv, st := testServer(t) ctx := context.Background() - project := &store.Project{ID: "project-nogather-route", Name: "nogather-route-project", Slug: "nogather-route-project"} + project := &store.Project{ID: tid("project-nogather-route"), Name: "nogather-route-project", Slug: "nogather-route-project"} if err := st.CreateProject(ctx, project); err != nil { t.Fatal(err) } broker := &store.RuntimeBroker{ - ID: "broker-nogather-route", Name: "nogather-route-broker", Slug: "nogather-route-broker", + ID: tid("broker-nogather-route"), Name: "nogather-route-broker", Slug: "nogather-route-broker", Endpoint: "http://localhost:9800", Status: store.BrokerStatusOnline, } if err := st.CreateRuntimeBroker(ctx, broker); err != nil { @@ -1022,7 +1022,7 @@ func TestNonGatherEnv_MissingEnvVars_ProjectRoute_Returns422(t *testing.T) { } if err := st.AddProjectProvider(ctx, &store.ProjectProvider{ - ProjectID: "project-nogather-route", BrokerID: "broker-nogather-route", + ProjectID: tid("project-nogather-route"), BrokerID: tid("broker-nogather-route"), BrokerName: "test-broker", LocalPath: "/tmp/test-project", }); err != nil { t.Fatal(err) @@ -1063,7 +1063,7 @@ func TestNonGatherEnv_MissingEnvVars_ProjectRoute_Returns422(t *testing.T) { } // Agent should have been cleaned up - result, err := st.ListAgents(ctx, store.AgentFilter{ProjectID: "project-nogather-route"}, store.ListOptions{}) + result, err := st.ListAgents(ctx, store.AgentFilter{ProjectID: tid("project-nogather-route")}, store.ListOptions{}) if err != nil { t.Fatal(err) } @@ -1078,13 +1078,13 @@ func TestNonGatherEnv_AllSatisfied_Returns201(t *testing.T) { srv, st := testServer(t) ctx := context.Background() - project := &store.Project{ID: "project-nogather-ok", Name: "nogather-ok-project", Slug: "nogather-ok-project"} + project := &store.Project{ID: tid("project-nogather-ok"), Name: "nogather-ok-project", Slug: "nogather-ok-project"} if err := st.CreateProject(ctx, project); err != nil { t.Fatal(err) } broker := &store.RuntimeBroker{ - ID: "broker-nogather-ok", Name: "nogather-ok-broker", Slug: "nogather-ok-broker", + ID: tid("broker-nogather-ok"), Name: "nogather-ok-broker", Slug: "nogather-ok-broker", Endpoint: "http://localhost:9800", Status: store.BrokerStatusOnline, } if err := st.CreateRuntimeBroker(ctx, broker); err != nil { @@ -1092,7 +1092,7 @@ func TestNonGatherEnv_AllSatisfied_Returns201(t *testing.T) { } if err := st.AddProjectProvider(ctx, &store.ProjectProvider{ - ProjectID: "project-nogather-ok", BrokerID: "broker-nogather-ok", + ProjectID: tid("project-nogather-ok"), BrokerID: tid("broker-nogather-ok"), BrokerName: "test-broker", LocalPath: "/tmp/test-project", }); err != nil { t.Fatal(err) @@ -1106,7 +1106,7 @@ func TestNonGatherEnv_AllSatisfied_Returns201(t *testing.T) { // Create agent WITHOUT GatherEnv — all env satisfied reqBody := map[string]interface{}{ "name": "nogather-ok-agent", - "projectId": "project-nogather-ok", + "projectId": tid("project-nogather-ok"), "template": "claude", } @@ -1146,13 +1146,13 @@ func TestEnvGather_HubHandler_RetryAfterCancel_ProjectRoute(t *testing.T) { srv, st := testServer(t) ctx := context.Background() - project := &store.Project{ID: "project-retry-route", Name: "retry-route-project", Slug: "retry-route-project"} + project := &store.Project{ID: tid("project-retry-route"), Name: "retry-route-project", Slug: "retry-route-project"} if err := st.CreateProject(ctx, project); err != nil { t.Fatal(err) } broker := &store.RuntimeBroker{ - ID: "broker-retry-route", Name: "retry-route-broker", Slug: "retry-route-broker", + ID: tid("broker-retry-route"), Name: "retry-route-broker", Slug: "retry-route-broker", Endpoint: "http://localhost:9800", Status: store.BrokerStatusOnline, } if err := st.CreateRuntimeBroker(ctx, broker); err != nil { @@ -1160,7 +1160,7 @@ func TestEnvGather_HubHandler_RetryAfterCancel_ProjectRoute(t *testing.T) { } if err := st.AddProjectProvider(ctx, &store.ProjectProvider{ - ProjectID: "project-retry-route", BrokerID: "broker-retry-route", + ProjectID: tid("project-retry-route"), BrokerID: tid("broker-retry-route"), BrokerName: "test-broker", LocalPath: "/tmp/test-project", }); err != nil { t.Fatal(err) @@ -1168,11 +1168,11 @@ func TestEnvGather_HubHandler_RetryAfterCancel_ProjectRoute(t *testing.T) { // Simulate a previous cancelled env-gather: agent exists in "provisioning" status staleAgent := &store.Agent{ - ID: "stale-agent-route", + ID: tid("stale-agent-route"), Name: "retry-route-agent", Slug: "retry-route-agent", - ProjectID: "project-retry-route", - RuntimeBrokerID: "broker-retry-route", + ProjectID: tid("project-retry-route"), + RuntimeBrokerID: tid("broker-retry-route"), Phase: string(state.PhaseProvisioning), AppliedConfig: &store.AgentAppliedConfig{ HarnessConfig: "claude", @@ -1225,7 +1225,7 @@ func TestEnvGather_HubHandler_RetryAfterCancel_ProjectRoute(t *testing.T) { if resp.Agent == nil { t.Fatal("expected agent in response") } - if resp.Agent.ID == "stale-agent-route" { + if resp.Agent.ID == tid("stale-agent-route") { t.Error("expected a new agent ID, got the stale agent ID") } if resp.Agent.Phase != string(state.PhaseProvisioning) { @@ -1233,7 +1233,7 @@ func TestEnvGather_HubHandler_RetryAfterCancel_ProjectRoute(t *testing.T) { } // The old agent should no longer exist in the store - _, err := st.GetAgent(ctx, "stale-agent-route") + _, err := st.GetAgent(ctx, tid("stale-agent-route")) if err != store.ErrNotFound { t.Errorf("expected stale agent to be deleted, got err=%v", err) } @@ -1248,13 +1248,13 @@ func TestProjectRoute_ResolvesUserScopedEnvVars(t *testing.T) { srv, st := testServer(t) ctx := context.Background() - project := &store.Project{ID: "project-owner-env", Name: "owner-env-project", Slug: "owner-env-project"} + project := &store.Project{ID: tid("project-owner-env"), Name: "owner-env-project", Slug: "owner-env-project"} if err := st.CreateProject(ctx, project); err != nil { t.Fatal(err) } broker := &store.RuntimeBroker{ - ID: "broker-owner-env", Name: "owner-env-broker", Slug: "owner-env-broker", + ID: tid("broker-owner-env"), Name: "owner-env-broker", Slug: "owner-env-broker", Endpoint: "http://localhost:9800", Status: store.BrokerStatusOnline, } if err := st.CreateRuntimeBroker(ctx, broker); err != nil { @@ -1262,7 +1262,7 @@ func TestProjectRoute_ResolvesUserScopedEnvVars(t *testing.T) { } if err := st.AddProjectProvider(ctx, &store.ProjectProvider{ - ProjectID: "project-owner-env", BrokerID: "broker-owner-env", + ProjectID: tid("project-owner-env"), BrokerID: tid("broker-owner-env"), BrokerName: "test-broker", LocalPath: "/tmp/test-project", }); err != nil { t.Fatal(err) @@ -1270,7 +1270,7 @@ func TestProjectRoute_ResolvesUserScopedEnvVars(t *testing.T) { // Store a user-scoped env var for the dev-user (dev auth identity) if err := st.CreateEnvVar(ctx, &store.EnvVar{ - ID: "env-owner-1", + ID: tid("env-owner-1"), Key: "GEMINI_API_KEY", Value: "user-scoped-gemini-key", Scope: "user", @@ -1335,13 +1335,13 @@ func TestProjectRoute_ResolvesUserScopedSecrets(t *testing.T) { srv, st := testServer(t) ctx := context.Background() - project := &store.Project{ID: "project-owner-secret", Name: "owner-secret-project", Slug: "owner-secret-project"} + project := &store.Project{ID: tid("project-owner-secret"), Name: "owner-secret-project", Slug: "owner-secret-project"} if err := st.CreateProject(ctx, project); err != nil { t.Fatal(err) } broker := &store.RuntimeBroker{ - ID: "broker-owner-secret", Name: "owner-secret-broker", Slug: "owner-secret-broker", + ID: tid("broker-owner-secret"), Name: "owner-secret-broker", Slug: "owner-secret-broker", Endpoint: "http://localhost:9800", Status: store.BrokerStatusOnline, } if err := st.CreateRuntimeBroker(ctx, broker); err != nil { @@ -1349,7 +1349,7 @@ func TestProjectRoute_ResolvesUserScopedSecrets(t *testing.T) { } if err := st.AddProjectProvider(ctx, &store.ProjectProvider{ - ProjectID: "project-owner-secret", BrokerID: "broker-owner-secret", + ProjectID: tid("project-owner-secret"), BrokerID: tid("broker-owner-secret"), BrokerName: "test-broker", LocalPath: "/tmp/test-project", }); err != nil { t.Fatal(err) diff --git a/pkg/hub/events.go b/pkg/hub/events.go index 3c5e8b33e..37ca6b796 100644 --- a/pkg/hub/events.go +++ b/pkg/hub/events.go @@ -40,6 +40,12 @@ type EventPublisher interface { PublishUserMessage(ctx context.Context, msg *store.Message) PublishAllowListChanged(ctx context.Context, action string, email string) PublishInviteChanged(ctx context.Context, action string, inviteID string, codePrefix string) + // Subscribe returns a channel that receives events matching the given + // subject patterns, along with an unsubscribe function. Patterns use + // NATS-style wildcards: '*' matches a single token, '>' matches the + // remainder. The returned channel is buffered; implementations may drop + // events on a full buffer (backpressure). + Subscribe(patterns ...string) (<-chan Event, func()) Close() } @@ -62,6 +68,13 @@ func (noopEventPublisher) PublishAllowListChanged(_ context.Context, _, _ string func (noopEventPublisher) PublishInviteChanged(_ context.Context, _, _, _ string) {} func (noopEventPublisher) Close() {} +// Subscribe on the no-op publisher returns a nil channel (which blocks forever +// on receive) and a no-op unsubscribe. Callers that need real subscriptions +// must wire a ChannelEventPublisher or PostgresEventPublisher. +func (noopEventPublisher) Subscribe(_ ...string) (<-chan Event, func()) { + return nil, func() {} +} + // Event is a published event with a subject and JSON-encoded data. type Event struct { Subject string @@ -199,9 +212,20 @@ type InviteChangedEvent struct { CodePrefix string `json:"codePrefix,omitempty"` } +// eventBuilder holds the EventPublisher Publish* method implementations shared +// by every publisher backend. Each method marshals a typed event struct and +// hands the (subject, event) pair to sink, which the embedding publisher wires +// to its own delivery mechanism (in-process fan-out for ChannelEventPublisher, +// Postgres NOTIFY for PostgresEventPublisher). Keeping the subject taxonomy in +// one place guarantees both backends publish identical subjects and payloads. +type eventBuilder struct { + sink func(subject string, event interface{}) +} + // ChannelEventPublisher is an in-process event publisher that fans out events // to Go channel subscribers using NATS-style subject matching. type ChannelEventPublisher struct { + eventBuilder mu sync.RWMutex subscribers map[string][]chan Event closed bool @@ -209,9 +233,11 @@ type ChannelEventPublisher struct { // NewChannelEventPublisher creates a new ChannelEventPublisher. func NewChannelEventPublisher() *ChannelEventPublisher { - return &ChannelEventPublisher{ + p := &ChannelEventPublisher{ subscribers: make(map[string][]chan Event), } + p.sink = p.publish + return p } // Subscribe returns a channel that receives events matching the given patterns, @@ -298,7 +324,7 @@ func (p *ChannelEventPublisher) Close() { // PublishAgentStatus publishes an agent status event to both agent-specific // and project-scoped subjects (dual-publish pattern). -func (p *ChannelEventPublisher) PublishAgentStatus(_ context.Context, agent *store.Agent) { +func (p *eventBuilder) PublishAgentStatus(_ context.Context, agent *store.Agent) { evt := AgentStatusEvent{ AgentID: agent.ID, ProjectID: agent.ProjectID, @@ -321,16 +347,16 @@ func (p *ChannelEventPublisher) PublishAgentStatus(_ context.Context, agent *sto if detail != (AgentDetail{}) { evt.Detail = &detail } - p.publish("agent."+agent.ID+".status", evt) + p.sink("agent."+agent.ID+".status", evt) if agent.ProjectID != "" { - p.publish("project."+agent.ProjectID+".agent.status", evt) - p.publish("grove."+agent.ProjectID+".agent.status", evt) + p.sink("project."+agent.ProjectID+".agent.status", evt) + p.sink("grove."+agent.ProjectID+".agent.status", evt) } } // PublishAgentCreated publishes an agent created event to both agent-specific // and project-scoped subjects (dual-publish pattern). -func (p *ChannelEventPublisher) PublishAgentCreated(_ context.Context, agent *store.Agent) { +func (p *eventBuilder) PublishAgentCreated(_ context.Context, agent *store.Agent) { evt := AgentCreatedEvent{ AgentID: agent.ID, ProjectID: agent.ProjectID, @@ -351,63 +377,63 @@ func (p *ChannelEventPublisher) PublishAgentCreated(_ context.Context, agent *st if !agent.Created.IsZero() { evt.Created = agent.Created.Format("2006-01-02T15:04:05Z07:00") } - p.publish("agent."+agent.ID+".created", evt) + p.sink("agent."+agent.ID+".created", evt) if agent.ProjectID != "" { - p.publish("project."+agent.ProjectID+".agent.created", evt) - p.publish("grove."+agent.ProjectID+".agent.created", evt) + p.sink("project."+agent.ProjectID+".agent.created", evt) + p.sink("grove."+agent.ProjectID+".agent.created", evt) } } // PublishAgentDeleted publishes an agent deleted event to both agent-specific // and project-scoped subjects (dual-publish pattern). -func (p *ChannelEventPublisher) PublishAgentDeleted(_ context.Context, agentID, projectID string) { +func (p *eventBuilder) PublishAgentDeleted(_ context.Context, agentID, projectID string) { evt := AgentDeletedEvent{ AgentID: agentID, ProjectID: projectID, GroveID: projectID, } - p.publish("agent."+agentID+".deleted", evt) + p.sink("agent."+agentID+".deleted", evt) if projectID != "" { - p.publish("project."+projectID+".agent.deleted", evt) - p.publish("grove."+projectID+".agent.deleted", evt) + p.sink("project."+projectID+".agent.deleted", evt) + p.sink("grove."+projectID+".agent.deleted", evt) } } // PublishProjectCreated publishes a project created event. -func (p *ChannelEventPublisher) PublishProjectCreated(_ context.Context, project *store.Project) { +func (p *eventBuilder) PublishProjectCreated(_ context.Context, project *store.Project) { evt := ProjectCreatedEvent{ ProjectID: project.ID, GroveID: project.ID, Name: project.Name, Slug: project.Slug, } - p.publish("project."+project.ID+".created", evt) - p.publish("grove."+project.ID+".created", evt) + p.sink("project."+project.ID+".created", evt) + p.sink("grove."+project.ID+".created", evt) } // PublishProjectUpdated publishes a project updated event. -func (p *ChannelEventPublisher) PublishProjectUpdated(_ context.Context, project *store.Project) { +func (p *eventBuilder) PublishProjectUpdated(_ context.Context, project *store.Project) { evt := ProjectUpdatedEvent{ ProjectID: project.ID, GroveID: project.ID, Name: project.Name, } - p.publish("project."+project.ID+".updated", evt) - p.publish("grove."+project.ID+".updated", evt) + p.sink("project."+project.ID+".updated", evt) + p.sink("grove."+project.ID+".updated", evt) } // PublishProjectDeleted publishes a project deleted event. -func (p *ChannelEventPublisher) PublishProjectDeleted(_ context.Context, projectID string) { +func (p *eventBuilder) PublishProjectDeleted(_ context.Context, projectID string) { evt := ProjectDeletedEvent{ ProjectID: projectID, GroveID: projectID, } - p.publish("project."+projectID+".deleted", evt) - p.publish("grove."+projectID+".deleted", evt) + p.sink("project."+projectID+".deleted", evt) + p.sink("grove."+projectID+".deleted", evt) } // PublishBrokerConnected publishes broker connection events, one per project the broker serves. -func (p *ChannelEventPublisher) PublishBrokerConnected(_ context.Context, brokerID, brokerName string, projectIDs []string) { +func (p *eventBuilder) PublishBrokerConnected(_ context.Context, brokerID, brokerName string, projectIDs []string) { for _, pid := range projectIDs { evt := BrokerProjectEvent{ BrokerID: brokerID, @@ -416,13 +442,13 @@ func (p *ChannelEventPublisher) PublishBrokerConnected(_ context.Context, broker GroveID: pid, Status: "online", } - p.publish("project."+pid+".broker.status", evt) - p.publish("grove."+pid+".broker.status", evt) + p.sink("project."+pid+".broker.status", evt) + p.sink("grove."+pid+".broker.status", evt) } } // PublishBrokerDisconnected publishes broker disconnection events, one per project the broker serves. -func (p *ChannelEventPublisher) PublishBrokerDisconnected(_ context.Context, brokerID string, projectIDs []string) { +func (p *eventBuilder) PublishBrokerDisconnected(_ context.Context, brokerID string, projectIDs []string) { for _, pid := range projectIDs { evt := BrokerProjectEvent{ BrokerID: brokerID, @@ -430,22 +456,22 @@ func (p *ChannelEventPublisher) PublishBrokerDisconnected(_ context.Context, bro GroveID: pid, Status: "offline", } - p.publish("project."+pid+".broker.status", evt) - p.publish("grove."+pid+".broker.status", evt) + p.sink("project."+pid+".broker.status", evt) + p.sink("grove."+pid+".broker.status", evt) } } // PublishBrokerStatus publishes a general broker status event. -func (p *ChannelEventPublisher) PublishBrokerStatus(_ context.Context, brokerID, status string) { +func (p *eventBuilder) PublishBrokerStatus(_ context.Context, brokerID, status string) { evt := BrokerStatusEvent{ BrokerID: brokerID, Status: status, } - p.publish("broker."+brokerID+".status", evt) + p.sink("broker."+brokerID+".status", evt) } // PublishNotification publishes a user notification event. -func (p *ChannelEventPublisher) PublishNotification(_ context.Context, notif *store.Notification) { +func (p *eventBuilder) PublishNotification(_ context.Context, notif *store.Notification) { evt := NotificationCreatedEvent{ ID: notif.ID, AgentID: notif.AgentID, @@ -455,30 +481,30 @@ func (p *ChannelEventPublisher) PublishNotification(_ context.Context, notif *st Message: notif.Message, CreatedAt: notif.CreatedAt.Format("2006-01-02T15:04:05.000Z"), } - p.publish("notification.created", evt) + p.sink("notification.created", evt) if notif.ProjectID != "" { - p.publish("project."+notif.ProjectID+".notification", evt) - p.publish("grove."+notif.ProjectID+".notification", evt) + p.sink("project."+notif.ProjectID+".notification", evt) + p.sink("grove."+notif.ProjectID+".notification", evt) } } // PublishAllowListChanged publishes an allow list change event. // Email is intentionally omitted from the event to avoid PII leak via SSE. -func (p *ChannelEventPublisher) PublishAllowListChanged(_ context.Context, action, _ string) { +func (p *eventBuilder) PublishAllowListChanged(_ context.Context, action, _ string) { evt := AllowListChangedEvent{ Action: action, } - p.publish("admin.allowlist.changed", evt) + p.sink("admin.allowlist.changed", evt) } // PublishInviteChanged publishes an invite code change event. -func (p *ChannelEventPublisher) PublishInviteChanged(_ context.Context, action, inviteID, codePrefix string) { +func (p *eventBuilder) PublishInviteChanged(_ context.Context, action, inviteID, codePrefix string) { evt := InviteChangedEvent{ Action: action, InviteID: inviteID, CodePrefix: codePrefix, } - p.publish("admin.invite.changed", evt) + p.sink("admin.invite.changed", evt) } // PublishUserMessage publishes a user.message event when a message involving @@ -493,7 +519,7 @@ func (p *ChannelEventPublisher) PublishInviteChanged(_ context.Context, action, // (only when the recipient is a user) // - agent..message — per-agent conversation streams (both // directions; subscribers filter by user participation themselves) -func (p *ChannelEventPublisher) PublishUserMessage(_ context.Context, msg *store.Message) { +func (p *eventBuilder) PublishUserMessage(_ context.Context, msg *store.Message) { evt := UserMessageEvent{ ID: msg.ID, ProjectID: msg.ProjectID, @@ -516,14 +542,14 @@ func (p *ChannelEventPublisher) PublishUserMessage(_ context.Context, msg *store // count by mixing user→agent prompts with agent→user replies. recipientIsUser := strings.HasPrefix(msg.Recipient, "user:") if recipientIsUser && msg.RecipientID != "" { - p.publish("user."+msg.RecipientID+".message", evt) + p.sink("user."+msg.RecipientID+".message", evt) } if recipientIsUser && msg.ProjectID != "" { - p.publish("project."+msg.ProjectID+".user.message", evt) - p.publish("grove."+msg.ProjectID+".user.message", evt) + p.sink("project."+msg.ProjectID+".user.message", evt) + p.sink("grove."+msg.ProjectID+".user.message", evt) } if msg.AgentID != "" { - p.publish("agent."+msg.AgentID+".message", evt) + p.sink("agent."+msg.AgentID+".message", evt) } } diff --git a/pkg/hub/events_integration_test.go b/pkg/hub/events_integration_test.go index 3cc190e9e..4115c7feb 100644 --- a/pkg/hub/events_integration_test.go +++ b/pkg/hub/events_integration_test.go @@ -77,7 +77,7 @@ func setupEventTestServer(t *testing.T) (*Server, store.Store, *ChannelEventPubl t.Cleanup(func() { pub.Close() }) project := &store.Project{ - ID: "project-evt", + ID: tid("project-evt"), Name: "Event Test Project", Slug: "event-test-project", Visibility: store.VisibilityPrivate, @@ -85,7 +85,7 @@ func setupEventTestServer(t *testing.T) (*Server, store.Store, *ChannelEventPubl require.NoError(t, s.CreateProject(ctx, project)) broker := &store.RuntimeBroker{ - ID: "broker-evt", + ID: tid("broker-evt"), Name: "Event Test Broker", Slug: "event-test-broker", Status: store.BrokerStatusOnline, @@ -140,8 +140,8 @@ func TestEventPublisher_DeleteAgentEmitsEvent(t *testing.T) { ctx := context.Background() agent := &store.Agent{ - ID: "agent-evt-del", - Slug: "agent-evt-del", + ID: tid("agent-evt-del"), + Slug: tid("agent-evt-del"), Name: "Delete Me", ProjectID: project.ID, Phase: string(state.PhaseRunning), @@ -153,7 +153,7 @@ func TestEventPublisher_DeleteAgentEmitsEvent(t *testing.T) { defer unsub() // Delete agent via API - rec := doRequest(t, srv, http.MethodDelete, "/api/v1/agents/agent-evt-del", nil) + rec := doRequest(t, srv, http.MethodDelete, "/api/v1/agents/"+agent.ID, nil) require.Equal(t, http.StatusNoContent, rec.Code) select { @@ -161,7 +161,7 @@ func TestEventPublisher_DeleteAgentEmitsEvent(t *testing.T) { assert.Equal(t, "project."+project.ID+".agent.deleted", evt.Subject) var data AgentDeletedEvent require.NoError(t, json.Unmarshal(evt.Data, &data)) - assert.Equal(t, "agent-evt-del", data.AgentID) + assert.Equal(t, tid("agent-evt-del"), data.AgentID) assert.Equal(t, project.ID, data.ProjectID) case <-time.After(2 * time.Second): t.Fatal("timeout waiting for agent deleted event") diff --git a/pkg/hub/events_postgres.go b/pkg/hub/events_postgres.go new file mode 100644 index 000000000..af4afb6dc --- /dev/null +++ b/pkg/hub/events_postgres.go @@ -0,0 +1,744 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package hub + +import ( + "context" + "crypto/sha256" + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "log/slog" + "strings" + "sync" + "time" + + "github.com/google/uuid" + "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgconn" + "github.com/jackc/pgx/v5/pgxpool" + "go.opentelemetry.io/otel/attribute" + + "github.com/GoogleCloudPlatform/scion/pkg/observability/dbmetrics" +) + +// PostgresEventPublisher is an EventPublisher backed by PostgreSQL LISTEN/NOTIFY. +// It delivers events across replicas: a NOTIFY issued on one hub instance is +// received by the listener goroutine on every instance (including the +// publisher), which fans the event out to that instance's in-process +// subscribers using the same NATS-style subject matching as +// ChannelEventPublisher. +// +// Channel model — per grove plus a global channel (flat exact-match, since +// Postgres channels do not support wildcards): +// +// - Grove-scoped subjects ("project..*" / "grove..*") are published +// to a per-grove channel (scion_ev_g_) AND to the global channel. The +// per-grove channel lets a replica that only watches a specific grove (e.g. +// a browser SSE stream) LISTEN on just that channel instead of the firehose. +// - All other subjects ("agent.*", "user.*", "broker.*", "admin.*", +// "notification.*") are published to the global channel only. +// - Subscriptions with a concrete grove id resolve to that grove's channel; +// everything else (grove-spanning wildcards used by the notification +// dispatcher and message-broker proxy, and non-grove subjects) resolves to +// the global channel. Each subscriber's patterns are grouped by the channel +// they resolve to, so an event arriving on a channel is only matched against +// the patterns that opted into that channel — no double delivery. +// +// Delivery is performed exclusively by the listener (events are not fanned out +// locally at publish time). This gives transactional publish semantics for free +// with PublishTx: a NOTIFY enrolled in a transaction that rolls back is never +// sent, so subscribers — local or remote — never observe it. +// +// Payloads larger than the Postgres 8000-byte NOTIFY limit are stored in the +// scion_event_payloads table and the NOTIFY carries a reference id; the listener +// refetches the payload on receipt (reference-and-refetch). A background +// goroutine purges old payload rows on a TTL so multiple replicas can each +// refetch the same oversized event. +type PostgresEventPublisher struct { + eventBuilder + + pool *pgxpool.Pool + dsn string + metrics dbmetrics.Recorder + log *slog.Logger + + ctx context.Context + cancel context.CancelFunc + wg sync.WaitGroup + + mu sync.RWMutex + // subs maps a Postgres channel -> subscriber -> the subset of that + // subscriber's patterns that resolve to this channel. + subs map[string]map[*pgSubscription][]string + // desired counts how many subscriptions need each channel LISTENed. + desired map[string]int + closed bool +} + +// pgSubscription is a single Subscribe registration. +type pgSubscription struct { + ch chan Event + once sync.Once +} + +// pgEnvelope is the JSON wire format carried in a NOTIFY payload. The event type +// is included so out-of-process consumers can route without re-deriving it from +// the subject. +type pgEnvelope struct { + Type string `json:"type"` + Subject string `json:"subject"` + Data json.RawMessage `json:"data,omitempty"` + Ref string `json:"ref,omitempty"` // payload-table id when oversized + TS int64 `json:"ts,omitempty"` // publish time, unix nanos (for latency) +} + +const ( + pgChannelPrefix = "scion_ev_" + pgGlobalChannel = "scion_ev_global" + // pgNotifyMaxPayload is the threshold above which an event is offloaded to + // the payload table. Postgres rejects NOTIFY payloads of 8000 bytes or more; + // the margin leaves room for the envelope wrapping around the data. + pgNotifyMaxPayload = 7000 + // maxPGIdentifier is the Postgres identifier length limit (NAMEDATALEN-1). + maxPGIdentifier = 63 + // listenPollInterval bounds how long the listener blocks in + // WaitForNotification before waking to apply pending LISTEN/UNLISTEN changes. + // A WaitForNotification deadline does not invalidate the connection (pgconn + // treats a read timeout as recoverable), so this is a cheap idle poll. + listenPollInterval = time.Second + // payloadTTL is how long oversized payloads are retained for refetch. + payloadTTL = 60 * time.Second + // publishTimeout bounds a single autocommit publish (Publish* methods). These + // run synchronously on the caller's goroutine — typically a request handler + // right after a CRUD write — and acquire a connection from the event pool. On + // an undersized / connection-starved instance (see CONNECTION-BUDGET.md) that + // acquire could otherwise block indefinitely, stalling the handler and + // silently never emitting the NOTIFY. Bounding it converts that failure mode + // into a logged error and a dropped event (publishing is fire-and-forget), + // keeping CRUD responsive. The transactional path (PublishTx) is unaffected: + // it uses the caller's context and transaction. + publishTimeout = 5 * time.Second +) + +// pgExecutor is satisfied by both *pgxpool.Pool and pgx.Tx, letting the publish +// path run either against an autocommit pool connection or inside a caller's +// transaction. +type pgExecutor interface { + Exec(ctx context.Context, sql string, args ...any) (pgconn.CommandTag, error) +} + +// compile-time check that PostgresEventPublisher satisfies EventPublisher. +var _ EventPublisher = (*PostgresEventPublisher)(nil) + +// NewPostgresEventPublisher connects to Postgres at dsn, ensures the +// payload-offload table exists, and starts the listener and maintenance +// goroutines. If metrics is nil a disabled (no-op) recorder is used. +func NewPostgresEventPublisher(ctx context.Context, dsn string, metrics dbmetrics.Recorder, log *slog.Logger) (*PostgresEventPublisher, error) { + if metrics == nil { + metrics = dbmetrics.NewDisabled() + } + if log == nil { + log = slog.Default() + } + + poolCfg, err := pgxpool.ParseConfig(dsn) + if err != nil { + return nil, fmt.Errorf("parsing postgres event dsn: %w", err) + } + applyEventPoolKeepalives(poolCfg) + + pool, err := pgxpool.NewWithConfig(ctx, poolCfg) + if err != nil { + return nil, fmt.Errorf("creating postgres event pool: %w", err) + } + if err := pool.Ping(ctx); err != nil { + pool.Close() + return nil, fmt.Errorf("pinging postgres for events: %w", err) + } + + pubCtx, cancel := context.WithCancel(context.Background()) + p := &PostgresEventPublisher{ + pool: pool, + dsn: dsn, + metrics: metrics, + log: log, + ctx: pubCtx, + cancel: cancel, + subs: make(map[string]map[*pgSubscription][]string), + desired: make(map[string]int), + } + p.sink = p.publish + + if err := p.ensurePayloadTable(ctx); err != nil { + cancel() + pool.Close() + return nil, err + } + + p.wg.Add(2) + go p.runListener() + go p.runMaintenance() + + log.Info("Postgres event publisher started") + return p, nil +} + +// ensurePayloadTable creates the oversized-payload offload table if absent. +func (p *PostgresEventPublisher) ensurePayloadTable(ctx context.Context) error { + const ddl = ` +CREATE TABLE IF NOT EXISTS scion_event_payloads ( + id UUID PRIMARY KEY, + subject TEXT NOT NULL, + data BYTEA NOT NULL, + created_at TIMESTAMPTZ NOT NULL DEFAULT now() +); +CREATE INDEX IF NOT EXISTS scion_event_payloads_created_at_idx + ON scion_event_payloads (created_at);` + if _, err := p.pool.Exec(ctx, ddl); err != nil { + return fmt.Errorf("creating scion_event_payloads table: %w", err) + } + return nil +} + +// publish is the sink wired into eventBuilder. It marshals and NOTIFYs on the +// pool (autocommit). Errors are logged rather than returned because the +// EventPublisher Publish* methods are fire-and-forget. +func (p *PostgresEventPublisher) publish(subject string, event interface{}) { + // Bound the publish so a saturated event pool surfaces a logged error instead + // of blocking the calling (often request-handler) goroutine forever. See + // publishTimeout. + ctx, cancel := context.WithTimeout(p.ctx, publishTimeout) + defer cancel() + if err := p.buildAndNotify(ctx, p.pool, subject, event); err != nil { + p.log.Error("Failed to publish event via NOTIFY", "subject", subject, "error", err) + } +} + +// PublishTx publishes an event using a caller-supplied executor, giving an +// atomic write+publish when that executor is a transaction (pgx.Tx satisfies +// pgExecutor): the NOTIFY is enrolled in the transaction and only delivered if +// it commits. If the transaction rolls back, no subscriber (local or remote) +// observes the event. +func (p *PostgresEventPublisher) PublishTx(ctx context.Context, tx pgExecutor, subject string, event interface{}) error { + return p.buildAndNotify(ctx, tx, subject, event) +} + +// buildAndNotify marshals event into an envelope, offloading the data to the +// payload table when it would exceed the NOTIFY size limit, and issues one +// NOTIFY per destination channel via exec. +func (p *PostgresEventPublisher) buildAndNotify(ctx context.Context, exec pgExecutor, subject string, event interface{}) error { + data, err := json.Marshal(event) + if err != nil { + return fmt.Errorf("marshaling event %s: %w", subject, err) + } + + env := pgEnvelope{ + Type: eventTypeName(event), + Subject: subject, + Data: data, + TS: time.Now().UnixNano(), + } + payload, err := json.Marshal(env) + if err != nil { + return fmt.Errorf("marshaling envelope %s: %w", subject, err) + } + + scope := channelScope(subject) + p.metrics.RecordPayloadSize(ctx, int64(len(payload)), attribute.String("scope", scope)) + + if len(payload) > pgNotifyMaxPayload { + id := uuid.NewString() + if _, err := exec.Exec(ctx, + `INSERT INTO scion_event_payloads (id, subject, data) VALUES ($1, $2, $3)`, + id, subject, data, + ); err != nil { + return fmt.Errorf("storing oversized payload for %s: %w", subject, err) + } + env.Data = nil + env.Ref = id + payload, err = json.Marshal(env) + if err != nil { + return fmt.Errorf("marshaling oversized envelope %s: %w", subject, err) + } + } + + for _, channel := range channelsForSubject(subject) { + if _, err := exec.Exec(ctx, `SELECT pg_notify($1, $2)`, channel, string(payload)); err != nil { + return fmt.Errorf("pg_notify on %s: %w", channel, err) + } + } + + p.metrics.IncPublished(ctx, 1, attribute.String("scope", scope)) + return nil +} + +// Subscribe registers patterns and returns a buffered channel plus an +// unsubscribe function. Patterns use NATS-style wildcards; matching is performed +// against the subject of each received event. The listener begins LISTENing on +// any newly-needed Postgres channels within listenPollInterval. +func (p *PostgresEventPublisher) Subscribe(patterns ...string) (<-chan Event, func()) { + ch := make(chan Event, 64) + sub := &pgSubscription{ch: ch} + + // Group patterns by the Postgres channel they resolve to. + byChannel := make(map[string][]string) + for _, pattern := range patterns { + for _, channel := range channelsForPattern(pattern) { + byChannel[channel] = append(byChannel[channel], pattern) + } + } + + p.mu.Lock() + if p.closed { + p.mu.Unlock() + close(ch) + return ch, func() {} + } + for channel, pats := range byChannel { + if p.subs[channel] == nil { + p.subs[channel] = make(map[*pgSubscription][]string) + } + p.subs[channel][sub] = pats + p.desired[channel]++ + } + p.mu.Unlock() + + unsubscribe := func() { + sub.once.Do(func() { + p.mu.Lock() + for channel := range byChannel { + if m := p.subs[channel]; m != nil { + delete(m, sub) + if len(m) == 0 { + delete(p.subs, channel) + } + } + if p.desired[channel] > 0 { + p.desired[channel]-- + if p.desired[channel] == 0 { + delete(p.desired, channel) + } + } + } + p.mu.Unlock() + close(ch) + }) + } + + return ch, unsubscribe +} + +// Close stops the background goroutines, closes the pool, and closes all +// subscriber channels. +func (p *PostgresEventPublisher) Close() { + p.mu.Lock() + if p.closed { + p.mu.Unlock() + return + } + p.closed = true + p.mu.Unlock() + + p.cancel() + p.wg.Wait() + p.pool.Close() + + p.mu.Lock() + defer p.mu.Unlock() + seen := make(map[*pgSubscription]bool) + for _, m := range p.subs { + for sub := range m { + if !seen[sub] { + sub.once.Do(func() { close(sub.ch) }) + seen[sub] = true + } + } + } + p.subs = make(map[string]map[*pgSubscription][]string) + p.desired = make(map[string]int) +} + +// runListener maintains a dedicated connection that LISTENs on the desired +// channels and dispatches received notifications. It reconnects with backoff and +// re-LISTENs (resubscribes) after any connection loss. +func (p *PostgresEventPublisher) runListener() { + defer p.wg.Done() + + const ( + minBackoff = 250 * time.Millisecond + maxBackoff = 10 * time.Second + ) + backoff := minBackoff + + for { + if p.ctx.Err() != nil { + return + } + + conn, err := p.connectListener(p.ctx) + if err != nil { + if p.ctx.Err() != nil { + return + } + p.log.Warn("Event listener connect failed, retrying", "error", err, "backoff", backoff) + if !p.sleep(backoff) { + return + } + backoff = nextBackoff(backoff, maxBackoff) + continue + } + + p.log.Info("Event listener connected") + backoff = minBackoff + + // active tracks channels currently LISTENed on this connection. A fresh + // connection starts empty, so listenLoop re-LISTENs every desired + // channel (resubscribe). + active := make(map[string]bool) + loopErr := p.listenLoop(conn, active) + conn.Close(context.Background()) + + if p.ctx.Err() != nil { + return + } + + // Unexpected connection loss: count a reconnect and retry. + p.metrics.IncListenerReconnects(p.ctx, 1) + p.log.Warn("Event listener connection lost, reconnecting", "error", loopErr, "backoff", backoff) + if !p.sleep(backoff) { + return + } + backoff = nextBackoff(backoff, maxBackoff) + } +} + +// connectListener opens the dedicated listener connection with TCP keepalives and +// a connect timeout applied, so the long-lived (mostly idle) LISTEN connection +// detects a silently dropped peer instead of blocking forever in +// WaitForNotification on a dead socket. +func (p *PostgresEventPublisher) connectListener(ctx context.Context) (*pgx.Conn, error) { + cc, err := pgx.ParseConfig(p.dsn) + if err != nil { + return nil, fmt.Errorf("parsing listener dsn: %w", err) + } + applyConnKeepalives(cc) + return pgx.ConnectConfig(ctx, cc) +} + +// listenLoop applies pending subscription changes and waits for notifications on +// conn until the context is canceled or the connection fails. A returned error +// other than context cancellation signals the caller to reconnect. +func (p *PostgresEventPublisher) listenLoop(conn *pgx.Conn, active map[string]bool) error { + for { + if p.ctx.Err() != nil { + return p.ctx.Err() + } + + desired := p.snapshotDesired() + for channel := range desired { + if !active[channel] { + if err := execListen(p.ctx, conn, "LISTEN", channel); err != nil { + return fmt.Errorf("LISTEN %s: %w", channel, err) + } + active[channel] = true + } + } + for channel := range active { + if !desired[channel] { + if err := execListen(p.ctx, conn, "UNLISTEN", channel); err != nil { + return fmt.Errorf("UNLISTEN %s: %w", channel, err) + } + delete(active, channel) + } + } + + waitCtx, cancel := context.WithTimeout(p.ctx, listenPollInterval) + notif, err := conn.WaitForNotification(waitCtx) + cancel() + if err != nil { + // A poll-interval deadline is expected; loop to reapply subscriptions. + if errors.Is(err, context.DeadlineExceeded) { + continue + } + // Context canceled (shutdown) or a real connection error. + return err + } + + p.handleNotification(notif.Channel, notif.Payload) + } +} + +// handleNotification decodes a NOTIFY payload (refetching oversized payloads), +// records latency, and fans the event out to subscribers of its channel. +func (p *PostgresEventPublisher) handleNotification(channel, payload string) { + var env pgEnvelope + if err := json.Unmarshal([]byte(payload), &env); err != nil { + p.log.Error("Failed to decode NOTIFY payload", "channel", channel, "error", err) + p.metrics.IncDropped(p.ctx, 1, attribute.String("reason", "decode")) + return + } + + data := []byte(env.Data) + if env.Ref != "" { + fetched, err := p.refetchPayload(env.Ref) + if err != nil { + p.log.Error("Failed to refetch oversized payload", "ref", env.Ref, "subject", env.Subject, "error", err) + p.metrics.IncDropped(p.ctx, 1, attribute.String("reason", "refetch")) + return + } + data = fetched + } + + if env.TS != 0 && p.metrics.Enabled() { + ms := float64(time.Now().UnixNano()-env.TS) / float64(time.Millisecond) + p.metrics.RecordPublishToDeliverLatency(p.ctx, ms, attribute.String("scope", channelScope(env.Subject))) + } + + p.fanout(channel, Event{Subject: env.Subject, Data: data}) +} + +// refetchPayload loads an oversized payload by reference id. Rows are not deleted +// here so every replica can refetch the same event; a TTL sweep reclaims them. +func (p *PostgresEventPublisher) refetchPayload(ref string) ([]byte, error) { + var data []byte + err := p.pool.QueryRow(p.ctx, `SELECT data FROM scion_event_payloads WHERE id = $1`, ref).Scan(&data) + if err != nil { + return nil, err + } + return data, nil +} + +// fanout delivers evt to every subscriber of channel whose patterns (scoped to +// that channel) match the event subject. Sends are non-blocking; a full +// subscriber buffer drops the event (backpressure). +func (p *PostgresEventPublisher) fanout(channel string, evt Event) { + p.mu.RLock() + defer p.mu.RUnlock() + + for sub, patterns := range p.subs[channel] { + if !anyPatternMatches(patterns, evt.Subject) { + continue + } + select { + case sub.ch <- evt: + p.metrics.IncDelivered(p.ctx, 1, attribute.String("scope", channelScope(evt.Subject))) + default: + p.metrics.IncDropped(p.ctx, 1, attribute.String("reason", "full_buffer")) + } + } +} + +// snapshotDesired returns a copy of the set of channels that should be LISTENed. +func (p *PostgresEventPublisher) snapshotDesired() map[string]bool { + p.mu.RLock() + defer p.mu.RUnlock() + out := make(map[string]bool, len(p.desired)) + for channel := range p.desired { + out[channel] = true + } + return out +} + +// runMaintenance periodically purges expired oversized payloads and reports +// connection-pool gauges. +func (p *PostgresEventPublisher) runMaintenance() { + defer p.wg.Done() + ticker := time.NewTicker(payloadTTL / 2) + defer ticker.Stop() + + for { + select { + case <-p.ctx.Done(): + return + case <-ticker.C: + if _, err := p.pool.Exec(p.ctx, + `DELETE FROM scion_event_payloads WHERE created_at < now() - $1::interval`, + fmt.Sprintf("%d seconds", int(payloadTTL.Seconds())), + ); err != nil && p.ctx.Err() == nil { + p.log.Warn("Failed to purge expired event payloads", "error", err) + } + p.observePoolStats() + } + } +} + +// observePoolStats records a snapshot of the pgx pool gauges. +func (p *PostgresEventPublisher) observePoolStats() { + if !p.metrics.Enabled() { + return + } + s := p.pool.Stat() + p.metrics.ObservePoolStats(p.ctx, dbmetrics.PoolStats{ + Active: int64(s.AcquiredConns()), + Idle: int64(s.IdleConns()), + Waiting: int64(s.EmptyAcquireCount()), + Max: int64(s.MaxConns()), + }) +} + +// sleep waits for d or until the publisher context is canceled. It reports false +// if the context was canceled. +func (p *PostgresEventPublisher) sleep(d time.Duration) bool { + t := time.NewTimer(d) + defer t.Stop() + select { + case <-p.ctx.Done(): + return false + case <-t.C: + return true + } +} + +// eventConnectTimeout bounds a single connection attempt for the event pool and +// listener, so a network black-hole surfaces as a retryable error instead of a +// hang. +const eventConnectTimeout = 10 * time.Second + +// applyEventPoolKeepalives attaches TCP keepalive GUCs and a connect timeout to +// the event pool's per-connection config, and bounds idle/total connection age. +// CloudSQL (and NAT gateways) silently drop idle connections; keepalives let the +// kernel detect a dead peer and the idle/lifetime caps recycle connections before +// the remote does, so the listener and publishers don't stall on a dead socket. +func applyEventPoolKeepalives(cfg *pgxpool.Config) { + applyConnKeepalives(cfg.ConnConfig) + // Recycle idle event-pool connections well before CloudSQL's ~10m idle + // timeout, and bound total connection age. + if cfg.MaxConnIdleTime == 0 { + cfg.MaxConnIdleTime = 5 * time.Minute + } + if cfg.MaxConnLifetime == 0 { + cfg.MaxConnLifetime = 30 * time.Minute + } +} + +// applyConnKeepalives sets the connect timeout and server-side TCP keepalive GUCs +// on a single pgx connection config. Existing RuntimeParams are not overwritten so +// an explicit DSN setting wins. Values: probe after 60s idle, every 15s, give up +// after 4 missed probes (~2 min to detect a dead peer). +func applyConnKeepalives(cc *pgx.ConnConfig) { + if cc == nil { + return + } + if cc.ConnectTimeout == 0 { + cc.ConnectTimeout = eventConnectTimeout + } + if cc.RuntimeParams == nil { + cc.RuntimeParams = make(map[string]string) + } + defaults := map[string]string{ + "tcp_keepalives_idle": "60", + "tcp_keepalives_interval": "15", + "tcp_keepalives_count": "4", + } + for k, v := range defaults { + if _, ok := cc.RuntimeParams[k]; !ok { + cc.RuntimeParams[k] = v + } + } +} + +// --- helpers (pure functions; no receiver state) --- + +// execListen runs a LISTEN or UNLISTEN for channel, quoting the identifier so +// case and special characters (e.g. UUID hyphens) match the pg_notify channel. +func execListen(ctx context.Context, conn *pgx.Conn, verb, channel string) error { + cctx, cancel := context.WithTimeout(ctx, 5*time.Second) + defer cancel() + quoted := `"` + strings.ReplaceAll(channel, `"`, `""`) + `"` + _, err := conn.Exec(cctx, verb+" "+quoted) + return err +} + +// channelsForSubject returns the Postgres channels a subject is published to. +func channelsForSubject(subject string) []string { + if gc := groveChannelForSubject(subject); gc != "" { + return []string{gc, pgGlobalChannel} + } + return []string{pgGlobalChannel} +} + +// channelsForPattern returns the Postgres channels a subscription pattern needs. +// A concrete grove/project pattern resolves to that grove's channel; everything +// else (wildcard grove, or non-grove subjects) resolves to the global channel. +func channelsForPattern(pattern string) []string { + parts := strings.SplitN(pattern, ".", 3) + if len(parts) >= 2 && (parts[0] == "project" || parts[0] == "grove") && isConcreteToken(parts[1]) { + return []string{groveChannel(parts[1])} + } + return []string{pgGlobalChannel} +} + +// groveChannelForSubject returns the per-grove channel for a grove-scoped +// subject, or "" if the subject is not grove-scoped. +func groveChannelForSubject(subject string) string { + parts := strings.SplitN(subject, ".", 3) + if len(parts) >= 2 && (parts[0] == "project" || parts[0] == "grove") { + return groveChannel(parts[1]) + } + return "" +} + +// groveChannel builds the Postgres channel name for a grove id, hashing the id +// if the resulting identifier would exceed the Postgres length limit. +func groveChannel(id string) string { + name := pgChannelPrefix + "g_" + id + if len(name) <= maxPGIdentifier { + return name + } + sum := sha256.Sum256([]byte(id)) + return pgChannelPrefix + "g_" + hex.EncodeToString(sum[:])[:32] +} + +// channelScope returns a low-cardinality label ("grove" or "global") for the +// channel a subject maps to, suitable for use as a metric attribute. +func channelScope(subject string) string { + if groveChannelForSubject(subject) != "" { + return "grove" + } + return "global" +} + +func isConcreteToken(t string) bool { return t != "" && t != "*" && t != ">" } + +// anyPatternMatches reports whether any pattern matches the subject. +func anyPatternMatches(patterns []string, subject string) bool { + for _, pattern := range patterns { + if subjectMatchesPattern(pattern, subject) { + return true + } + } + return false +} + +// eventTypeName returns the bare Go type name of an event value (e.g. +// "AgentStatusEvent"), used as the envelope type tag. +func eventTypeName(event interface{}) string { + t := fmt.Sprintf("%T", event) + if i := strings.LastIndex(t, "."); i >= 0 { + t = t[i+1:] + } + return t +} + +// nextBackoff doubles d up to max. +func nextBackoff(d, max time.Duration) time.Duration { + d *= 2 + if d > max { + return max + } + return d +} diff --git a/pkg/hub/events_postgres_test.go b/pkg/hub/events_postgres_test.go new file mode 100644 index 000000000..6309a08c8 --- /dev/null +++ b/pkg/hub/events_postgres_test.go @@ -0,0 +1,737 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package hub + +import ( + "context" + "encoding/json" + "log/slog" + "net/http" + "os" + "strings" + "sync" + "testing" + "time" + + "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgconn" + "go.opentelemetry.io/otel/attribute" + + "github.com/GoogleCloudPlatform/scion/pkg/observability/dbmetrics" + "github.com/GoogleCloudPlatform/scion/pkg/store" +) + +func mkProject(id string) *store.Project { + return &store.Project{ID: id, Name: id, Slug: id, Created: time.Now()} +} + +func mkMessage(agentID, msg string) *store.Message { + return &store.Message{ + ID: "msg-" + agentID, + AgentID: agentID, + Sender: "agent:" + agentID, + Recipient: "agent:" + agentID, + Msg: msg, + Type: "instruction", + CreatedAt: time.Now(), + } +} + +// --- test doubles --- + +// recExec records Exec calls so publish-path tests can assert the SQL and +// arguments without a real database. +type recExec struct { + mu sync.Mutex + calls []recCall +} + +type recCall struct { + sql string + args []any +} + +func (e *recExec) Exec(_ context.Context, sql string, args ...any) (pgconn.CommandTag, error) { + e.mu.Lock() + defer e.mu.Unlock() + e.calls = append(e.calls, recCall{sql: sql, args: args}) + return pgconn.CommandTag{}, nil +} + +func (e *recExec) notifyCalls() []recCall { + e.mu.Lock() + defer e.mu.Unlock() + var out []recCall + for _, c := range e.calls { + if strings.Contains(c.sql, "pg_notify") { + out = append(out, c) + } + } + return out +} + +func (e *recExec) inserts() []recCall { + e.mu.Lock() + defer e.mu.Unlock() + var out []recCall + for _, c := range e.calls { + if strings.Contains(c.sql, "INSERT INTO scion_event_payloads") { + out = append(out, c) + } + } + return out +} + +// countingRecorder is a dbmetrics.Recorder that tallies calls for assertions. +type countingRecorder struct { + mu sync.Mutex + published int64 + delivered int64 + dropped int64 + reconnects int64 + payloadSizes []int64 + latencies []float64 + poolObserved int + enabledReturns bool +} + +func (r *countingRecorder) RecordPublishToDeliverLatency(_ context.Context, ms float64, _ ...attribute.KeyValue) { + r.mu.Lock() + defer r.mu.Unlock() + r.latencies = append(r.latencies, ms) +} +func (r *countingRecorder) IncPublished(_ context.Context, n int64, _ ...attribute.KeyValue) { + r.mu.Lock() + defer r.mu.Unlock() + r.published += n +} +func (r *countingRecorder) IncDelivered(_ context.Context, n int64, _ ...attribute.KeyValue) { + r.mu.Lock() + defer r.mu.Unlock() + r.delivered += n +} +func (r *countingRecorder) IncDropped(_ context.Context, n int64, _ ...attribute.KeyValue) { + r.mu.Lock() + defer r.mu.Unlock() + r.dropped += n +} +func (r *countingRecorder) ObserveSubscriberLag(_ context.Context, _ int64, _ ...attribute.KeyValue) { +} +func (r *countingRecorder) IncListenerReconnects(_ context.Context, n int64, _ ...attribute.KeyValue) { + r.mu.Lock() + defer r.mu.Unlock() + r.reconnects += n +} +func (r *countingRecorder) RecordPayloadSize(_ context.Context, bytes int64, _ ...attribute.KeyValue) { + r.mu.Lock() + defer r.mu.Unlock() + r.payloadSizes = append(r.payloadSizes, bytes) +} +func (r *countingRecorder) ObservePoolStats(_ context.Context, _ dbmetrics.PoolStats, _ ...attribute.KeyValue) { + r.mu.Lock() + defer r.mu.Unlock() + r.poolObserved++ +} +func (r *countingRecorder) Enabled() bool { return r.enabledReturns } + +func (r *countingRecorder) snapshot() (pub, del, drop, recon int64) { + r.mu.Lock() + defer r.mu.Unlock() + return r.published, r.delivered, r.dropped, r.reconnects +} + +// newTestPostgresPublisher builds a publisher with no live connection or +// goroutines, suitable for exercising the pure routing/registry/publish logic. +func newTestPostgresPublisher(rec dbmetrics.Recorder) *PostgresEventPublisher { + if rec == nil { + rec = dbmetrics.NewDisabled() + } + p := &PostgresEventPublisher{ + metrics: rec, + log: slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: slog.LevelError})), + ctx: context.Background(), + subs: make(map[string]map[*pgSubscription][]string), + desired: make(map[string]int), + } + p.sink = p.publish + return p +} + +// --- pure helper tests (no database required) --- + +func TestChannelsForSubject(t *testing.T) { + tests := []struct { + subject string + want []string + }{ + {"project.G1.agent.status", []string{groveChannel("G1"), pgGlobalChannel}}, + {"grove.G2.notification", []string{groveChannel("G2"), pgGlobalChannel}}, + {"agent.A1.status", []string{pgGlobalChannel}}, + {"user.U1.message", []string{pgGlobalChannel}}, + {"broker.B1.status", []string{pgGlobalChannel}}, + {"admin.allowlist.changed", []string{pgGlobalChannel}}, + {"notification.created", []string{pgGlobalChannel}}, + } + for _, tt := range tests { + got := channelsForSubject(tt.subject) + if strings.Join(got, ",") != strings.Join(tt.want, ",") { + t.Errorf("channelsForSubject(%q) = %v, want %v", tt.subject, got, tt.want) + } + } +} + +func TestChannelsForPattern(t *testing.T) { + tests := []struct { + pattern string + want []string + }{ + {"project.G1.>", []string{groveChannel("G1")}}, + {"grove.G2.agent.status", []string{groveChannel("G2")}}, + {"project.>.agent.status", []string{pgGlobalChannel}}, // spanning wildcard + {"project.*.agent.status", []string{pgGlobalChannel}}, // single-token wildcard grove + {"agent.A1.message", []string{pgGlobalChannel}}, + {"notification.created", []string{pgGlobalChannel}}, + } + for _, tt := range tests { + got := channelsForPattern(tt.pattern) + if strings.Join(got, ",") != strings.Join(tt.want, ",") { + t.Errorf("channelsForPattern(%q) = %v, want %v", tt.pattern, got, tt.want) + } + } +} + +func TestGroveChannel_BoundedLength(t *testing.T) { + long := strings.Repeat("x", 200) + got := groveChannel(long) + if len(got) > maxPGIdentifier { + t.Errorf("groveChannel(long) length = %d, want <= %d", len(got), maxPGIdentifier) + } + // Deterministic. + if got != groveChannel(long) { + t.Errorf("groveChannel not deterministic") + } + // A normal UUID-length id is passed through unhashed. + uuidLike := "11111111-2222-3333-4444-555555555555" + if groveChannel(uuidLike) != pgChannelPrefix+"g_"+uuidLike { + t.Errorf("groveChannel(uuid) = %q, want passthrough", groveChannel(uuidLike)) + } +} + +func TestEventTypeName(t *testing.T) { + if got := eventTypeName(AgentStatusEvent{}); got != "AgentStatusEvent" { + t.Errorf("eventTypeName = %q, want AgentStatusEvent", got) + } +} + +// --- registry / fan-out tests (no database required) --- + +// TestPostgresFanout_ScopedSubscriberNoDoubleDelivery verifies a grove-scoped +// subscriber receives grove events on the grove channel exactly once and is not +// also matched on the global channel (which carries a mirror of grove events). +func TestPostgresFanout_ScopedSubscriberNoDoubleDelivery(t *testing.T) { + p := newTestPostgresPublisher(nil) + ch, unsub := p.Subscribe("project.G1.>") + defer unsub() + + evt := Event{Subject: "project.G1.agent.status", Data: []byte(`{}`)} + + // Delivered on the grove channel. + p.fanout(groveChannel("G1"), evt) + select { + case got := <-ch: + if got.Subject != evt.Subject { + t.Fatalf("got subject %q", got.Subject) + } + case <-time.After(time.Second): + t.Fatal("expected delivery on grove channel") + } + + // NOT delivered again on the global channel: the subscriber's patterns do + // not resolve to the global channel. + p.fanout(pgGlobalChannel, evt) + select { + case got := <-ch: + t.Fatalf("unexpected duplicate delivery on global channel: %q", got.Subject) + case <-time.After(100 * time.Millisecond): + } +} + +// TestPostgresFanout_SpanningSubscriber verifies a grove-spanning subscriber +// (e.g. the notification dispatcher) receives grove events via the global +// channel and not via the per-grove channel. +func TestPostgresFanout_SpanningSubscriber(t *testing.T) { + p := newTestPostgresPublisher(nil) + ch, unsub := p.Subscribe("project.>.agent.status") + defer unsub() + + evt := Event{Subject: "project.G9.agent.status", Data: []byte(`{}`)} + + p.fanout(pgGlobalChannel, evt) + select { + case got := <-ch: + if got.Subject != evt.Subject { + t.Fatalf("got subject %q", got.Subject) + } + case <-time.After(time.Second): + t.Fatal("expected delivery on global channel for spanning subscriber") + } + + // The per-grove channel must not deliver to a spanning subscriber. + p.fanout(groveChannel("G9"), evt) + select { + case got := <-ch: + t.Fatalf("unexpected delivery on grove channel: %q", got.Subject) + case <-time.After(100 * time.Millisecond): + } +} + +// TestPostgresFanout_MixedPatternsNoDuplicate verifies that a single Subscribe +// call mixing a grove-scoped and a non-grove pattern never double-delivers an +// event that happens to be mirrored onto both channels. +func TestPostgresFanout_MixedPatternsNoDuplicate(t *testing.T) { + p := newTestPostgresPublisher(nil) + ch, unsub := p.Subscribe("project.G1.agent.status", "agent.A1.message") + defer unsub() + + evt := Event{Subject: "project.G1.agent.status", Data: []byte(`{}`)} + + // On the global channel, only the agent.A1.message pattern is active, which + // does not match the project subject -> no delivery here. + p.fanout(pgGlobalChannel, evt) + // On the grove channel, the project pattern matches -> exactly one delivery. + p.fanout(groveChannel("G1"), evt) + + received := 0 + for { + select { + case <-ch: + received++ + case <-time.After(150 * time.Millisecond): + if received != 1 { + t.Fatalf("expected exactly 1 delivery, got %d", received) + } + return + } + } +} + +func TestPostgresSubscribe_Unsubscribe(t *testing.T) { + p := newTestPostgresPublisher(nil) + ch, unsub := p.Subscribe("project.G1.>") + + gc := groveChannel("G1") + if p.desired[gc] != 1 { + t.Fatalf("desired[%s] = %d, want 1", gc, p.desired[gc]) + } + unsub() + if _, ok := p.desired[gc]; ok { + t.Fatalf("desired[%s] should be cleared after unsubscribe", gc) + } + // Channel must be closed. + if _, ok := <-ch; ok { + t.Fatal("subscriber channel should be closed after unsubscribe") + } + // Double unsubscribe is safe. + unsub() +} + +// --- publish-path tests using a fake executor (no database required) --- + +func TestBuildAndNotify_SmallPayload(t *testing.T) { + rec := &countingRecorder{} + p := newTestPostgresPublisher(rec) + exec := &recExec{} + + err := p.buildAndNotify(context.Background(), exec, "project.G1.agent.status", AgentStatusEvent{AgentID: "a1"}) + if err != nil { + t.Fatalf("buildAndNotify: %v", err) + } + + // Grove subject -> NOTIFY on grove channel and global channel. + notifies := exec.notifyCalls() + if len(notifies) != 2 { + t.Fatalf("expected 2 pg_notify calls, got %d", len(notifies)) + } + gotChannels := map[string]bool{} + for _, c := range notifies { + gotChannels[c.args[0].(string)] = true + // Payload should carry the inline data and the event type. + var env pgEnvelope + if err := json.Unmarshal([]byte(c.args[1].(string)), &env); err != nil { + t.Fatalf("decode payload: %v", err) + } + if env.Ref != "" { + t.Fatalf("small payload should not be offloaded; got ref %q", env.Ref) + } + if env.Type != "AgentStatusEvent" { + t.Fatalf("envelope type = %q", env.Type) + } + if len(env.Data) == 0 { + t.Fatal("envelope data should be inline") + } + } + if !gotChannels[groveChannel("G1")] || !gotChannels[pgGlobalChannel] { + t.Fatalf("notify channels = %v", gotChannels) + } + if len(exec.inserts()) != 0 { + t.Fatalf("small payload must not INSERT into payload table") + } + if pub, _, _, _ := rec.snapshot(); pub != 1 { + t.Fatalf("published metric = %d, want 1", pub) + } +} + +func TestBuildAndNotify_OversizedPayloadOffloaded(t *testing.T) { + p := newTestPostgresPublisher(nil) + exec := &recExec{} + + // A message larger than the NOTIFY threshold forces reference-and-refetch. + big := strings.Repeat("z", pgNotifyMaxPayload+500) + err := p.buildAndNotify(context.Background(), exec, "agent.A1.message", UserMessageEvent{ID: "m1", Msg: big}) + if err != nil { + t.Fatalf("buildAndNotify: %v", err) + } + + if got := len(exec.inserts()); got != 1 { + t.Fatalf("oversized payload should INSERT once, got %d", got) + } + notifies := exec.notifyCalls() + if len(notifies) != 1 { // non-grove subject -> global only + t.Fatalf("expected 1 pg_notify, got %d", len(notifies)) + } + var env pgEnvelope + if err := json.Unmarshal([]byte(notifies[0].args[1].(string)), &env); err != nil { + t.Fatalf("decode payload: %v", err) + } + if env.Ref == "" { + t.Fatal("oversized envelope should carry a ref") + } + if len(env.Data) != 0 { + t.Fatal("oversized envelope must not inline data") + } + if len(notifies[0].args[1].(string)) > pgNotifyMaxPayload { + t.Fatalf("reference envelope still exceeds NOTIFY limit: %d bytes", len(notifies[0].args[1].(string))) + } +} + +// TestPublishTx_UsesProvidedExecutor verifies the transactional publish path +// enrolls the NOTIFY on the caller's transaction (the fake executor) rather than +// the pool, which is what gives rollback==no-deliver semantics at the DB layer. +func TestPublishTx_UsesProvidedExecutor(t *testing.T) { + p := newTestPostgresPublisher(nil) + tx := &recExec{} + + if err := p.PublishTx(context.Background(), tx, "grove.G1.created", ProjectCreatedEvent{ProjectID: "G1"}); err != nil { + t.Fatalf("PublishTx: %v", err) + } + if len(tx.notifyCalls()) == 0 { + t.Fatal("PublishTx should issue pg_notify on the provided transaction") + } +} + +func TestHandleNotification_InlineDeliversAndRecordsMetrics(t *testing.T) { + rec := &countingRecorder{enabledReturns: true} + p := newTestPostgresPublisher(rec) + ch, unsub := p.Subscribe("agent.A1.status") + defer unsub() + + data, _ := json.Marshal(AgentStatusEvent{AgentID: "A1", Phase: "running"}) + env := pgEnvelope{Type: "AgentStatusEvent", Subject: "agent.A1.status", Data: data, TS: time.Now().Add(-5 * time.Millisecond).UnixNano()} + payload, _ := json.Marshal(env) + + p.handleNotification(pgGlobalChannel, string(payload)) + + select { + case got := <-ch: + if got.Subject != "agent.A1.status" { + t.Fatalf("subject = %q", got.Subject) + } + case <-time.After(time.Second): + t.Fatal("expected delivery") + } + + if _, del, _, _ := rec.snapshot(); del != 1 { + t.Fatalf("delivered metric = %d, want 1", del) + } + rec.mu.Lock() + gotLatency := len(rec.latencies) + rec.mu.Unlock() + if gotLatency != 1 { + t.Fatalf("expected 1 latency sample, got %d", gotLatency) + } +} + +func TestHandleNotification_FullBufferDropsAndCounts(t *testing.T) { + rec := &countingRecorder{} + p := newTestPostgresPublisher(rec) + // Register a subscriber with a tiny buffer by reaching into the registry. + sub := &pgSubscription{ch: make(chan Event, 1)} + p.subs[pgGlobalChannel] = map[*pgSubscription][]string{sub: {"agent.>"}} + + evt := Event{Subject: "agent.A1.status", Data: []byte(`{}`)} + p.fanout(pgGlobalChannel, evt) // fills the buffer (delivered) + p.fanout(pgGlobalChannel, evt) // dropped (buffer full) + + _, del, drop, _ := rec.snapshot() + if del != 1 || drop != 1 { + t.Fatalf("delivered=%d dropped=%d, want 1 and 1", del, drop) + } +} + +// --- integration tests (require a live Postgres via SCION_TEST_POSTGRES_DSN) --- + +func requirePostgres(t *testing.T) string { + t.Helper() + dsn := os.Getenv("SCION_TEST_POSTGRES_DSN") + if dsn == "" { + t.Skip("set SCION_TEST_POSTGRES_DSN to run Postgres LISTEN/NOTIFY integration tests") + } + return dsn +} + +// TestPostgresIntegration_CrossReplicaDelivery starts two independent publishers +// against the same database (simulating two hub replicas) and asserts an event +// published on one is delivered to a subscriber on the other. +func TestPostgresIntegration_CrossReplicaDelivery(t *testing.T) { + dsn := requirePostgres(t) + ctx := context.Background() + + a, err := NewPostgresEventPublisher(ctx, dsn, dbmetrics.NewDisabled(), nil) + if err != nil { + t.Fatalf("publisher A: %v", err) + } + defer a.Close() + b, err := NewPostgresEventPublisher(ctx, dsn, dbmetrics.NewDisabled(), nil) + if err != nil { + t.Fatalf("publisher B: %v", err) + } + defer b.Close() + + pid := "proj-" + strings.ReplaceAll(time.Now().Format("150405.000000"), ".", "") + ch, unsub := b.Subscribe("project." + pid + ".>") + defer unsub() + + // Give B's listener time to LISTEN on the grove channel. + time.Sleep(2 * listenPollInterval) + + a.PublishProjectCreated(ctx, mkProject(pid)) + + select { + case got := <-ch: + if !strings.HasPrefix(got.Subject, "project."+pid) { + t.Fatalf("unexpected subject %q", got.Subject) + } + case <-time.After(5 * time.Second): + t.Fatal("cross-replica event not delivered") + } +} + +// TestPostgresIntegration_OversizedRoundTrip verifies an event larger than the +// NOTIFY limit is delivered intact via reference-and-refetch. +func TestPostgresIntegration_OversizedRoundTrip(t *testing.T) { + dsn := requirePostgres(t) + ctx := context.Background() + + pub, err := NewPostgresEventPublisher(ctx, dsn, dbmetrics.NewDisabled(), nil) + if err != nil { + t.Fatalf("publisher: %v", err) + } + defer pub.Close() + + aid := "agent-" + strings.ReplaceAll(time.Now().Format("150405.000000"), ".", "") + ch, unsub := pub.Subscribe("agent." + aid + ".message") + defer unsub() + time.Sleep(2 * listenPollInterval) + + big := strings.Repeat("Q", pgNotifyMaxPayload+2048) + pub.PublishUserMessage(ctx, mkMessage(aid, big)) + + select { + case got := <-ch: + var evt UserMessageEvent + if err := json.Unmarshal(got.Data, &evt); err != nil { + t.Fatalf("decode delivered event: %v", err) + } + if evt.Msg != big { + t.Fatalf("oversized payload not delivered intact: got %d bytes, want %d", len(evt.Msg), len(big)) + } + case <-time.After(5 * time.Second): + t.Fatal("oversized event not delivered") + } +} + +// TestPostgresIntegration_TransactionalRollback verifies a NOTIFY enrolled in a +// rolled-back transaction is never delivered, while a committed one is. +func TestPostgresIntegration_TransactionalRollback(t *testing.T) { + dsn := requirePostgres(t) + ctx := context.Background() + + pub, err := NewPostgresEventPublisher(ctx, dsn, dbmetrics.NewDisabled(), nil) + if err != nil { + t.Fatalf("publisher: %v", err) + } + defer pub.Close() + + pid := "txn-" + strings.ReplaceAll(time.Now().Format("150405.000000"), ".", "") + ch, unsub := pub.Subscribe("project." + pid + ".>") + defer unsub() + time.Sleep(2 * listenPollInterval) + + // Rolled-back publish: must NOT be delivered. + txRollback, err := pub.pool.Begin(ctx) + if err != nil { + t.Fatalf("begin: %v", err) + } + if err := pub.PublishTx(ctx, txRollback, "project."+pid+".updated", ProjectUpdatedEvent{ProjectID: pid, Name: "rolled-back"}); err != nil { + t.Fatalf("PublishTx: %v", err) + } + if err := txRollback.Rollback(ctx); err != nil { + t.Fatalf("rollback: %v", err) + } + + select { + case got := <-ch: + t.Fatalf("rolled-back event was delivered: %q", got.Subject) + case <-time.After(2 * time.Second): + // expected: nothing delivered + } + + // Committed publish: must be delivered. + txCommit, err := pub.pool.Begin(ctx) + if err != nil { + t.Fatalf("begin: %v", err) + } + if err := pub.PublishTx(ctx, txCommit, "project."+pid+".updated", ProjectUpdatedEvent{ProjectID: pid, Name: "committed"}); err != nil { + t.Fatalf("PublishTx: %v", err) + } + if err := txCommit.Commit(ctx); err != nil { + t.Fatalf("commit: %v", err) + } + + select { + case got := <-ch: + if got.Subject != "project."+pid+".updated" { + t.Fatalf("subject = %q", got.Subject) + } + case <-time.After(5 * time.Second): + t.Fatal("committed event not delivered") + } +} + +// TestPostgresIntegration_HandlerCreateProjectEmitsNotify exercises the full +// production publish path end-to-end: an HTTP project-create request handled by +// the Hub server calls s.events.PublishProjectCreated on a real +// PostgresEventPublisher, which must emit a pg_notify observable by an +// independent raw LISTEN connection. This is the exact capability the +// multi-replica live test probed with psql (create project => NOTIFY on +// scion_ev_global); it guards against regressions in the cmd-level wiring that +// connects the handler's s.events to the Postgres backend. +func TestPostgresIntegration_HandlerCreateProjectEmitsNotify(t *testing.T) { + dsn := requirePostgres(t) + ctx := context.Background() + + srv, _ := testServer(t) + pub, err := NewPostgresEventPublisher(ctx, dsn, dbmetrics.NewDisabled(), nil) + if err != nil { + t.Fatalf("publisher: %v", err) + } + defer pub.Close() + srv.SetEventPublisher(pub) + + // Independent raw LISTEN on the global channel — bypasses the publisher's own + // listener/subscription machinery, mirroring the psql probe from the live test. + lconn, err := pgx.Connect(ctx, dsn) + if err != nil { + t.Fatalf("listen conn: %v", err) + } + defer lconn.Close(context.Background()) + if _, err := lconn.Exec(ctx, `LISTEN scion_ev_global`); err != nil { + t.Fatalf("LISTEN: %v", err) + } + + rec := doRequest(t, srv, http.MethodPost, "/api/v1/projects", map[string]interface{}{ + "name": "pg-notify-wiring-" + strings.ReplaceAll(time.Now().Format("150405.000000"), ".", ""), + }) + if rec.Code != http.StatusCreated { + t.Fatalf("create project: code=%d body=%s", rec.Code, rec.Body.String()) + } + + // Expect a project..created NOTIFY on the global channel. + deadline := time.Now().Add(5 * time.Second) + for { + wctx, cancel := context.WithTimeout(ctx, time.Until(deadline)) + n, werr := lconn.WaitForNotification(wctx) + cancel() + if werr != nil { + t.Fatalf("no NOTIFY observed for handler-driven project create (publish path not wired): %v", werr) + } + if strings.Contains(n.Payload, ".created") { + return // success: handler -> s.events -> pg_notify works + } + } +} + +// TestPostgresIntegration_ReconnectResubscribe terminates the listener's backend +// connection and verifies the publisher reconnects, re-LISTENs, and resumes +// delivery, incrementing the reconnect metric. +func TestPostgresIntegration_ReconnectResubscribe(t *testing.T) { + dsn := requirePostgres(t) + ctx := context.Background() + + rec := &countingRecorder{enabledReturns: true} + pub, err := NewPostgresEventPublisher(ctx, dsn, rec, nil) + if err != nil { + t.Fatalf("publisher: %v", err) + } + defer pub.Close() + + pid := "rc-" + strings.ReplaceAll(time.Now().Format("150405.000000"), ".", "") + ch, unsub := pub.Subscribe("project." + pid + ".>") + defer unsub() + time.Sleep(2 * listenPollInterval) + + // Forcibly terminate all LISTENing backends for this database, dropping the + // listener connection and forcing a reconnect. + if _, err := pub.pool.Exec(ctx, + `SELECT pg_terminate_backend(pid) FROM pg_stat_activity + WHERE query ILIKE 'LISTEN %' AND pid <> pg_backend_pid()`); err != nil { + t.Fatalf("terminate backends: %v", err) + } + + // Wait for reconnect + resubscribe. + deadline := time.Now().Add(15 * time.Second) + for time.Now().Before(deadline) { + if _, _, _, recon := rec.snapshot(); recon > 0 { + break + } + time.Sleep(200 * time.Millisecond) + } + if _, _, _, recon := rec.snapshot(); recon == 0 { + t.Fatal("expected a listener reconnect to be recorded") + } + + // Allow the resubscribe poll to re-LISTEN, then verify delivery resumes. + time.Sleep(2 * listenPollInterval) + pub.PublishProjectCreated(ctx, mkProject(pid)) + + select { + case got := <-ch: + if !strings.HasPrefix(got.Subject, "project."+pid) { + t.Fatalf("unexpected subject %q", got.Subject) + } + case <-time.After(5 * time.Second): + t.Fatal("delivery did not resume after reconnect") + } +} diff --git a/pkg/hub/gcp_ratelimit_test.go b/pkg/hub/gcp_ratelimit_test.go index 44c69b223..831ed170e 100644 --- a/pkg/hub/gcp_ratelimit_test.go +++ b/pkg/hub/gcp_ratelimit_test.go @@ -25,18 +25,18 @@ func TestGCPTokenRateLimiter_Allow(t *testing.T) { // First 5 requests should be allowed (burst) for i := 0; i < 5; i++ { - if !rl.Allow("agent-1") { + if !rl.Allow(tid("agent-1")) { t.Fatalf("request %d should be allowed", i) } } // 6th request should be denied (burst exhausted) - if rl.Allow("agent-1") { + if rl.Allow(tid("agent-1")) { t.Fatal("6th request should be denied") } // Different agent should still be allowed - if !rl.Allow("agent-2") { + if !rl.Allow(tid("agent-2")) { t.Fatal("different agent should be allowed") } } @@ -44,18 +44,18 @@ func TestGCPTokenRateLimiter_Allow(t *testing.T) { func TestGCPTokenRateLimiter_Refill(t *testing.T) { rl := NewGCPTokenRateLimiter(100, 1) // 100/sec, burst 1 - if !rl.Allow("agent-1") { + if !rl.Allow(tid("agent-1")) { t.Fatal("first request should be allowed") } - if rl.Allow("agent-1") { + if rl.Allow(tid("agent-1")) { t.Fatal("second request should be denied") } // Wait for refill time.Sleep(20 * time.Millisecond) - if !rl.Allow("agent-1") { + if !rl.Allow(tid("agent-1")) { t.Fatal("request after refill should be allowed") } } @@ -68,14 +68,14 @@ func TestGCPTokenRateLimiter_CleanupExitsOnCancel(t *testing.T) { rl.StartCleanup(ctx) // Use the limiter - rl.Allow("agent-1") + rl.Allow(tid("agent-1")) // Cancel and verify goroutine exits (no hang) cancel() time.Sleep(100 * time.Millisecond) // Limiter should still work after cleanup goroutine exits - if !rl.Allow("agent-2") { + if !rl.Allow(tid("agent-2")) { t.Fatal("limiter should still work after cleanup exits") } } diff --git a/pkg/hub/handlers_agent_test.go b/pkg/hub/handlers_agent_test.go index e445ac367..ac30b4499 100644 --- a/pkg/hub/handlers_agent_test.go +++ b/pkg/hub/handlers_agent_test.go @@ -30,7 +30,7 @@ import ( "github.com/GoogleCloudPlatform/scion/pkg/api" "github.com/GoogleCloudPlatform/scion/pkg/messages" "github.com/GoogleCloudPlatform/scion/pkg/store" - "github.com/GoogleCloudPlatform/scion/pkg/store/sqlite" + "github.com/GoogleCloudPlatform/scion/pkg/store/entadapter" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -41,7 +41,7 @@ func TestAgentStatusUpdate_Authorization(t *testing.T) { // Create a project project := &store.Project{ - ID: "project-1", + ID: tid("project-1"), Name: "Test Project", Slug: "test-project", } @@ -49,7 +49,7 @@ func TestAgentStatusUpdate_Authorization(t *testing.T) { // Create two agents agent1 := &store.Agent{ - ID: "agent-1", + ID: tid("agent-1"), Slug: "agent-1-slug", Name: "Agent 1", ProjectID: project.ID, @@ -58,7 +58,7 @@ func TestAgentStatusUpdate_Authorization(t *testing.T) { require.NoError(t, s.CreateAgent(ctx, agent1)) agent2 := &store.Agent{ - ID: "agent-2", + ID: tid("agent-2"), Slug: "agent-2-slug", Name: "Agent 2", ProjectID: project.ID, @@ -80,7 +80,7 @@ func TestAgentStatusUpdate_Authorization(t *testing.T) { Message: "Waiting for user input", } body, _ := json.Marshal(status) - req := httptest.NewRequest(http.MethodPost, "/api/v1/agents/agent-1/status", bytes.NewReader(body)) + req := httptest.NewRequest(http.MethodPost, "/api/v1/agents/"+agent1.ID+"/status", bytes.NewReader(body)) req.Header.Set("X-Scion-Agent-Token", token1) req.Header.Set("Content-Type", "application/json") @@ -101,7 +101,7 @@ func TestAgentStatusUpdate_Authorization(t *testing.T) { Phase: "error", } body, _ := json.Marshal(status) - req := httptest.NewRequest(http.MethodPost, "/api/v1/agents/agent-2/status", bytes.NewReader(body)) + req := httptest.NewRequest(http.MethodPost, "/api/v1/agents/"+agent2.ID+"/status", bytes.NewReader(body)) req.Header.Set("X-Scion-Agent-Token", token1) req.Header.Set("Content-Type", "application/json") @@ -112,7 +112,7 @@ func TestAgentStatusUpdate_Authorization(t *testing.T) { }) t.Run("Agent 1 cannot perform lifecycle actions", func(t *testing.T) { - req := httptest.NewRequest(http.MethodPost, "/api/v1/agents/agent-1/stop", nil) + req := httptest.NewRequest(http.MethodPost, "/api/v1/agents/"+agent1.ID+"/stop", nil) req.Header.Set("X-Scion-Agent-Token", token1) rec := httptest.NewRecorder() @@ -126,7 +126,7 @@ func TestAgentStatusUpdate_Authorization(t *testing.T) { Phase: "running", } body, _ := json.Marshal(status) - req := httptest.NewRequest(http.MethodPost, "/api/v1/agents/agent-1/status", bytes.NewReader(body)) + req := httptest.NewRequest(http.MethodPost, "/api/v1/agents/"+agent1.ID+"/status", bytes.NewReader(body)) req.Header.Set("Authorization", "Bearer "+testDevToken) req.Header.Set("Content-Type", "application/json") @@ -147,7 +147,7 @@ func TestAgentStatusUpdate_Heartbeat(t *testing.T) { // Create a project project := &store.Project{ - ID: "project-h", + ID: tid("project-h"), Name: "Heartbeat Project", Slug: "heartbeat-project", } @@ -155,7 +155,7 @@ func TestAgentStatusUpdate_Heartbeat(t *testing.T) { // Create an agent agent := &store.Agent{ - ID: "agent-h", + ID: tid("agent-h"), Slug: "agent-h-slug", Name: "Agent Heartbeat", ProjectID: project.ID, @@ -177,7 +177,7 @@ func TestAgentStatusUpdate_Heartbeat(t *testing.T) { Heartbeat: true, } body, _ := json.Marshal(status) - req := httptest.NewRequest(http.MethodPost, "/api/v1/agents/agent-h/status", bytes.NewReader(body)) + req := httptest.NewRequest(http.MethodPost, "/api/v1/agents/"+agent.ID+"/status", bytes.NewReader(body)) req.Header.Set("Authorization", "Bearer "+testDevToken) req.Header.Set("Content-Type", "application/json") @@ -198,14 +198,14 @@ func setupOfflineBrokerAgent(t *testing.T, s store.Store, suffix string) (*store ctx := context.Background() project := &store.Project{ - ID: fmt.Sprintf("project-offline-%s", suffix), + ID: tid(fmt.Sprintf("project-offline-%s", suffix)), Name: fmt.Sprintf("Offline Project %s", suffix), Slug: fmt.Sprintf("offline-project-%s", suffix), } require.NoError(t, s.CreateProject(ctx, project)) broker := &store.RuntimeBroker{ - ID: fmt.Sprintf("broker-offline-%s", suffix), + ID: tid(fmt.Sprintf("broker-offline-%s", suffix)), Name: fmt.Sprintf("Offline Broker %s", suffix), Slug: fmt.Sprintf("offline-broker-%s", suffix), Status: store.BrokerStatusOffline, @@ -213,7 +213,7 @@ func setupOfflineBrokerAgent(t *testing.T, s store.Store, suffix string) (*store require.NoError(t, s.CreateRuntimeBroker(ctx, broker)) agent := &store.Agent{ - ID: fmt.Sprintf("agent-offline-%s", suffix), + ID: tid(fmt.Sprintf("agent-offline-%s", suffix)), Slug: fmt.Sprintf("agent-offline-%s-slug", suffix), Name: fmt.Sprintf("Agent Offline %s", suffix), ProjectID: project.ID, @@ -244,14 +244,14 @@ func TestDeleteAgent_NoBroker(t *testing.T) { ctx := context.Background() project := &store.Project{ - ID: "project-nobroker", + ID: tid("project-nobroker"), Name: "No Broker Project", Slug: "no-broker-project", } require.NoError(t, s.CreateProject(ctx, project)) agent := &store.Agent{ - ID: "agent-nobroker", + ID: tid("agent-nobroker"), Slug: "agent-nobroker-slug", Name: "Agent No Broker", ProjectID: project.ID, @@ -290,14 +290,14 @@ func setupOnlineBrokerAgent(t *testing.T, s store.Store, suffix string) (*store. ctx := context.Background() project := &store.Project{ - ID: fmt.Sprintf("project-online-%s", suffix), + ID: tid(fmt.Sprintf("project-online-%s", suffix)), Name: fmt.Sprintf("Online Project %s", suffix), Slug: fmt.Sprintf("online-project-%s", suffix), } require.NoError(t, s.CreateProject(ctx, project)) broker := &store.RuntimeBroker{ - ID: fmt.Sprintf("broker-online-%s", suffix), + ID: tid(fmt.Sprintf("broker-online-%s", suffix)), Name: fmt.Sprintf("Online Broker %s", suffix), Slug: fmt.Sprintf("online-broker-%s", suffix), Status: store.BrokerStatusOnline, @@ -306,7 +306,7 @@ func setupOnlineBrokerAgent(t *testing.T, s store.Store, suffix string) (*store. require.NoError(t, s.CreateRuntimeBroker(ctx, broker)) agent := &store.Agent{ - ID: fmt.Sprintf("agent-online-%s", suffix), + ID: tid(fmt.Sprintf("agent-online-%s", suffix)), Slug: fmt.Sprintf("agent-online-%s-slug", suffix), Name: fmt.Sprintf("Agent Online %s", suffix), ProjectID: project.ID, @@ -424,7 +424,7 @@ func TestAgentCreateAgent_WithScope(t *testing.T) { // Create a project project := &store.Project{ - ID: "project-parent", + ID: tid("project-parent"), Name: "Parent Project", Slug: "parent-project", } @@ -432,7 +432,7 @@ func TestAgentCreateAgent_WithScope(t *testing.T) { // Create a runtime broker and provider for the project broker := &store.RuntimeBroker{ - ID: "broker-parent", + ID: tid("broker-parent"), Name: "Parent Broker", Slug: "parent-broker", Status: store.BrokerStatusOnline, @@ -451,10 +451,12 @@ func TestAgentCreateAgent_WithScope(t *testing.T) { project.DefaultRuntimeBrokerID = broker.ID require.NoError(t, s.UpdateProject(ctx, project)) - // Create the calling agent + // Create the calling agent. The created sub-agent's created_by/owner_id FK + // references the users table, so seed a user sharing the caller's ID. + permSeedUser(t, ctx, s, tid("agent-caller")) callingAgent := &store.Agent{ - ID: "agent-caller", - Slug: "agent-caller", + ID: tid("agent-caller"), + Slug: tid("agent-caller"), Name: "Calling Agent", ProjectID: project.ID, Phase: string(state.PhaseRunning), @@ -498,7 +500,7 @@ func TestAgentCreateAgent_WithScope(t *testing.T) { t.Run("Agent with project:agent:create scope rejected for different project", func(t *testing.T) { // Create another project otherProject := &store.Project{ - ID: "project-other", + ID: tid("project-other"), Name: "Other Project", Slug: "other-project", } @@ -552,7 +554,7 @@ func TestAgentLifecycle_WithScope(t *testing.T) { // Create a project project := &store.Project{ - ID: "project-lc", + ID: tid("project-lc"), Name: "Lifecycle Project", Slug: "lifecycle-project", } @@ -560,8 +562,8 @@ func TestAgentLifecycle_WithScope(t *testing.T) { // Create the calling agent callingAgent := &store.Agent{ - ID: "agent-lc-caller", - Slug: "agent-lc-caller", + ID: tid("agent-lc-caller"), + Slug: tid("agent-lc-caller"), Name: "Lifecycle Caller", ProjectID: project.ID, Phase: string(state.PhaseRunning), @@ -570,8 +572,8 @@ func TestAgentLifecycle_WithScope(t *testing.T) { // Create a target agent in the same project targetAgent := &store.Agent{ - ID: "agent-lc-target", - Slug: "agent-lc-target", + ID: tid("agent-lc-target"), + Slug: tid("agent-lc-target"), Name: "Lifecycle Target", ProjectID: project.ID, Phase: string(state.PhaseRunning), @@ -601,15 +603,15 @@ func TestAgentLifecycle_WithScope(t *testing.T) { t.Run("Agent with project:agent:lifecycle scope rejected for cross-project lifecycle", func(t *testing.T) { // Create another project and agent otherProject := &store.Project{ - ID: "project-lc-other", + ID: tid("project-lc-other"), Name: "Other LC Project", Slug: "other-lc-project", } require.NoError(t, s.CreateProject(ctx, otherProject)) otherAgent := &store.Agent{ - ID: "agent-lc-other", - Slug: "agent-lc-other", + ID: tid("agent-lc-other"), + Slug: tid("agent-lc-other"), Name: "Other LC Agent", ProjectID: otherProject.ID, Phase: string(state.PhaseRunning), @@ -654,14 +656,14 @@ func TestAgentGetAgent_ProjectIsolation(t *testing.T) { // Create two projects project1 := &store.Project{ - ID: "project-get1", + ID: tid("project-get1"), Name: "Get Project 1", Slug: "get-project-1", } require.NoError(t, s.CreateProject(ctx, project1)) project2 := &store.Project{ - ID: "project-get2", + ID: tid("project-get2"), Name: "Get Project 2", Slug: "get-project-2", } @@ -669,8 +671,8 @@ func TestAgentGetAgent_ProjectIsolation(t *testing.T) { // Create agents in each project agent1 := &store.Agent{ - ID: "agent-get-caller", - Slug: "agent-get-caller", + ID: tid("agent-get-caller"), + Slug: tid("agent-get-caller"), Name: "Get Caller", ProjectID: project1.ID, Phase: string(state.PhaseRunning), @@ -678,8 +680,8 @@ func TestAgentGetAgent_ProjectIsolation(t *testing.T) { require.NoError(t, s.CreateAgent(ctx, agent1)) agent2SameProject := &store.Agent{ - ID: "agent-get-same", - Slug: "agent-get-same", + ID: tid("agent-get-same"), + Slug: tid("agent-get-same"), Name: "Same Project Agent", ProjectID: project1.ID, Phase: string(state.PhaseRunning), @@ -687,8 +689,8 @@ func TestAgentGetAgent_ProjectIsolation(t *testing.T) { require.NoError(t, s.CreateAgent(ctx, agent2SameProject)) agentOtherProject := &store.Agent{ - ID: "agent-get-other", - Slug: "agent-get-other", + ID: tid("agent-get-other"), + Slug: tid("agent-get-other"), Name: "Other Project Agent", ProjectID: project2.ID, Phase: string(state.PhaseRunning), @@ -840,14 +842,14 @@ func setupCreateAgentServer(t *testing.T, disp AgentDispatcher) (*Server, store. ctx := context.Background() project := &store.Project{ - ID: "project-create", + ID: tid("project-create"), Name: "Create Test Project", Slug: "create-test-project", } require.NoError(t, s.CreateProject(ctx, project)) broker := &store.RuntimeBroker{ - ID: "broker-create", + ID: tid("broker-create"), Name: "Create Test Broker", Slug: "create-test-broker", Status: store.BrokerStatusOnline, @@ -1071,11 +1073,11 @@ func TestCreateAgent_RestartFromProvisioningStatus(t *testing.T) { // Pre-create an agent stuck in "provisioning" status (simulating Bug 1) stuckAgent := &store.Agent{ - ID: "agent-stuck-prov", + ID: tid("agent-stuck-prov"), Slug: "stuck-agent", Name: "stuck-agent", ProjectID: project.ID, - RuntimeBrokerID: "broker-create", + RuntimeBrokerID: tid("broker-create"), Phase: string(state.PhaseProvisioning), } require.NoError(t, s.CreateAgent(ctx, stuckAgent)) @@ -1103,11 +1105,11 @@ func TestCreateAgent_RestartFromPendingStatus(t *testing.T) { // Pre-create an agent in "pending" status pendingAgent := &store.Agent{ - ID: "agent-pending", + ID: tid("agent-pending"), Slug: "pending-agent", Name: "pending-agent", ProjectID: project.ID, - RuntimeBrokerID: "broker-create", + RuntimeBrokerID: tid("broker-create"), Phase: string(state.PhaseCreated), } require.NoError(t, s.CreateAgent(ctx, pendingAgent)) @@ -1130,11 +1132,11 @@ func TestCreateAgent_RecreateFromRunningStatus(t *testing.T) { // Pre-create an agent in "running" status (stale — container may have died) runningAgent := &store.Agent{ - ID: "agent-running-stale", + ID: tid("agent-running-stale"), Slug: "running-agent", Name: "running-agent", ProjectID: project.ID, - RuntimeBrokerID: "broker-create", + RuntimeBrokerID: tid("broker-create"), Phase: string(state.PhaseRunning), } require.NoError(t, s.CreateAgent(ctx, runningAgent)) @@ -1150,7 +1152,7 @@ func TestCreateAgent_RecreateFromRunningStatus(t *testing.T) { "re-creating agent from running status should succeed with 201") // Old agent should be deleted - _, err := s.GetAgent(ctx, "agent-running-stale") + _, err := s.GetAgent(ctx, tid("agent-running-stale")) assert.ErrorIs(t, err, store.ErrNotFound, "old agent should be deleted") // Dispatcher should have been asked to delete @@ -1160,7 +1162,7 @@ func TestCreateAgent_RecreateFromRunningStatus(t *testing.T) { var resp CreateAgentResponse require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp)) require.NotNil(t, resp.Agent) - assert.NotEqual(t, "agent-running-stale", resp.Agent.ID, "new agent should have a different ID") + assert.NotEqual(t, tid("agent-running-stale"), resp.Agent.ID, "new agent should have a different ID") assert.Equal(t, string(state.PhaseRunning), resp.Agent.Phase) } @@ -1171,11 +1173,11 @@ func TestCreateAgent_RecreateFromErrorStatus(t *testing.T) { // Pre-create an agent in "error" status errorAgent := &store.Agent{ - ID: "agent-errored", + ID: tid("agent-errored"), Slug: "error-agent", Name: "error-agent", ProjectID: project.ID, - RuntimeBrokerID: "broker-create", + RuntimeBrokerID: tid("broker-create"), Phase: string(state.PhaseError), } require.NoError(t, s.CreateAgent(ctx, errorAgent)) @@ -1191,7 +1193,7 @@ func TestCreateAgent_RecreateFromErrorStatus(t *testing.T) { "re-creating agent from error status should succeed with 201") // Old agent should be deleted - _, err := s.GetAgent(ctx, "agent-errored") + _, err := s.GetAgent(ctx, tid("agent-errored")) assert.ErrorIs(t, err, store.ErrNotFound, "old errored agent should be deleted") } @@ -1202,11 +1204,11 @@ func TestCreateAgent_RecreateFromStoppedStatus(t *testing.T) { // Pre-create an agent in "stopped" status stoppedAgent := &store.Agent{ - ID: "agent-stopped", + ID: tid("agent-stopped"), Slug: "stopped-agent", Name: "stopped-agent", ProjectID: project.ID, - RuntimeBrokerID: "broker-create", + RuntimeBrokerID: tid("broker-create"), Phase: string(state.PhaseStopped), } require.NoError(t, s.CreateAgent(ctx, stoppedAgent)) @@ -1220,7 +1222,7 @@ func TestCreateAgent_RecreateFromStoppedStatus(t *testing.T) { require.Equal(t, http.StatusCreated, rec.Code, "re-creating agent from stopped status should succeed with 201") - _, err := s.GetAgent(ctx, "agent-stopped") + _, err := s.GetAgent(ctx, tid("agent-stopped")) assert.ErrorIs(t, err, store.ErrNotFound, "old stopped agent should be deleted") } @@ -1308,7 +1310,7 @@ func TestAgentCreate_LocalTemplateWithLocalBroker(t *testing.T) { // Create a runtime broker broker := &store.RuntimeBroker{ - ID: "broker_local_tpl", + ID: tid("broker_local_tpl"), Slug: "local-tpl-broker", Name: "Local Template Broker", Status: store.BrokerStatusOnline, @@ -1317,7 +1319,7 @@ func TestAgentCreate_LocalTemplateWithLocalBroker(t *testing.T) { // Create a project with default runtime broker project := &store.Project{ - ID: "project_local_tpl", + ID: tid("project_local_tpl"), Slug: "local-tpl-project", Name: "Local Template Project", GitRemote: "github.com/test/local-tpl", @@ -1370,7 +1372,7 @@ func TestAgentCreate_LocalTemplateWithRemoteBroker(t *testing.T) { // Create a runtime broker broker := &store.RuntimeBroker{ - ID: "broker_remote_tpl", + ID: tid("broker_remote_tpl"), Slug: "remote-tpl-broker", Name: "Remote Template Broker", Status: store.BrokerStatusOnline, @@ -1379,7 +1381,7 @@ func TestAgentCreate_LocalTemplateWithRemoteBroker(t *testing.T) { // Create a project project := &store.Project{ - ID: "project_remote_tpl", + ID: tid("project_remote_tpl"), Slug: "remote-tpl-project", Name: "Remote Template Project", GitRemote: "github.com/test/remote-tpl", @@ -1421,7 +1423,7 @@ func TestAgentCreate_LocalTemplateNoBroker(t *testing.T) { // Create a project WITHOUT a default runtime broker project := &store.Project{ - ID: "project_no_broker_tpl", + ID: tid("project_no_broker_tpl"), Slug: "no-broker-tpl-project", Name: "No Broker Template Project", GitRemote: "github.com/test/no-broker-tpl", @@ -1480,14 +1482,14 @@ func TestListAgents_ServerTimeIncluded(t *testing.T) { // Create a project and agent project := &store.Project{ - ID: "project-servertime", + ID: tid("project-servertime"), Name: "ServerTime Project", Slug: "servertime-project", } require.NoError(t, s.CreateProject(ctx, project)) agent := &store.Agent{ - ID: "agent-servertime", + ID: tid("agent-servertime"), Slug: "agent-servertime-slug", Name: "ServerTime Agent", ProjectID: project.ID, @@ -1520,7 +1522,7 @@ func TestListProjectAgents_ServerTimeIncluded(t *testing.T) { // Create a project project := &store.Project{ - ID: "project-servertime-g", + ID: tid("project-servertime-g"), Name: "ServerTime Project G", Slug: "servertime-project-g", } @@ -1609,7 +1611,7 @@ func TestCreateAgent_GitAnchoredProjectPopulatesGitClone(t *testing.T) { // Create a project with GitRemote and labels gitProject := &store.Project{ - ID: "project-git", + ID: tid("project-git"), Name: "Git Project", Slug: "git-project", GitRemote: "github.com/example/myrepo", @@ -1617,14 +1619,14 @@ func TestCreateAgent_GitAnchoredProjectPopulatesGitClone(t *testing.T) { "scion.dev/clone-url": "https://github.com/example/myrepo.git", "scion.dev/default-branch": "develop", }, - DefaultRuntimeBrokerID: "broker-create", + DefaultRuntimeBrokerID: tid("broker-create"), } require.NoError(t, s.CreateProject(ctx, gitProject)) // Add project provider provider := &store.ProjectProvider{ ProjectID: gitProject.ID, - BrokerID: "broker-create", + BrokerID: tid("broker-create"), BrokerName: "Create Test Broker", Status: store.BrokerStatusOnline, } @@ -1683,7 +1685,7 @@ func TestCreateProjectAgent_GitAnchoredProjectPopulatesGitClone(t *testing.T) { // Create a project with GitRemote and labels gitProject := &store.Project{ - ID: "project-git-scoped", + ID: tid("project-git-scoped"), Name: "Git Project Scoped", Slug: "git-project-scoped", GitRemote: "github.com/example/myrepo", @@ -1691,14 +1693,14 @@ func TestCreateProjectAgent_GitAnchoredProjectPopulatesGitClone(t *testing.T) { "scion.dev/clone-url": "https://github.com/example/myrepo.git", "scion.dev/default-branch": "develop", }, - DefaultRuntimeBrokerID: "broker-create", + DefaultRuntimeBrokerID: tid("broker-create"), } require.NoError(t, s.CreateProject(ctx, gitProject)) // Add project provider provider := &store.ProjectProvider{ ProjectID: gitProject.ID, - BrokerID: "broker-create", + BrokerID: tid("broker-create"), BrokerName: "Create Test Broker", Status: store.BrokerStatusOnline, } @@ -1760,20 +1762,20 @@ func TestCreateAgent_GitProjectCloneURLFallback(t *testing.T) { // Create a project with GitRemote but WITHOUT the scion.dev/clone-url label. // The URL should be constructed from gitRemote as "https://.git". gitProject := &store.Project{ - ID: "project-git-fallback-url", + ID: tid("project-git-fallback-url"), Name: "Git Project Fallback URL", Slug: "git-project-fallback-url", GitRemote: "github.com/example/fallback-repo", Labels: map[string]string{ "scion.dev/default-branch": "develop", }, - DefaultRuntimeBrokerID: "broker-create", + DefaultRuntimeBrokerID: tid("broker-create"), } require.NoError(t, s.CreateProject(ctx, gitProject)) provider := &store.ProjectProvider{ ProjectID: gitProject.ID, - BrokerID: "broker-create", + BrokerID: tid("broker-create"), BrokerName: "Create Test Broker", Status: store.BrokerStatusOnline, } @@ -1811,7 +1813,7 @@ func TestCreateAgent_GitProjectSchemelessCloneURL(t *testing.T) { // Create a project where clone-url label is set but missing https:// scheme // (as can happen when the web UI stores raw user input). gitProject := &store.Project{ - ID: "project-git-schemeless", + ID: tid("project-git-schemeless"), Name: "Git Project Schemeless", Slug: "git-project-schemeless", GitRemote: "github.com/example/schemeless-repo", @@ -1819,13 +1821,13 @@ func TestCreateAgent_GitProjectSchemelessCloneURL(t *testing.T) { "scion.dev/clone-url": "github.com/example/schemeless-repo", "scion.dev/default-branch": "main", }, - DefaultRuntimeBrokerID: "broker-create", + DefaultRuntimeBrokerID: tid("broker-create"), } require.NoError(t, s.CreateProject(ctx, gitProject)) provider := &store.ProjectProvider{ ProjectID: gitProject.ID, - BrokerID: "broker-create", + BrokerID: tid("broker-create"), BrokerName: "Create Test Broker", Status: store.BrokerStatusOnline, } @@ -1862,20 +1864,20 @@ func TestCreateAgent_GitProjectDefaultBranchFallback(t *testing.T) { // Create a project with GitRemote and clone-url label but WITHOUT default-branch. // The branch should default to "main". gitProject := &store.Project{ - ID: "project-git-fallback-branch", + ID: tid("project-git-fallback-branch"), Name: "Git Project Fallback Branch", Slug: "git-project-fallback-branch", GitRemote: "github.com/example/branch-repo", Labels: map[string]string{ "scion.dev/clone-url": "https://github.com/example/branch-repo.git", }, - DefaultRuntimeBrokerID: "broker-create", + DefaultRuntimeBrokerID: tid("broker-create"), } require.NoError(t, s.CreateProject(ctx, gitProject)) provider := &store.ProjectProvider{ ProjectID: gitProject.ID, - BrokerID: "broker-create", + BrokerID: tid("broker-create"), BrokerName: "Create Test Broker", Status: store.BrokerStatusOnline, } @@ -2067,15 +2069,15 @@ func TestListAgents_HarnessConfigEnriched(t *testing.T) { ctx := context.Background() project := &store.Project{ - ID: "project-harness-enrich", + ID: tid("project-harness-enrich"), Name: "Harness Enrichment Project", Slug: "harness-enrich-project", } require.NoError(t, s.CreateProject(ctx, project)) agent := &store.Agent{ - ID: "agent-harness-enrich", - Slug: "agent-harness-enrich", + ID: tid("agent-harness-enrich"), + Slug: tid("agent-harness-enrich"), Name: "Harness Agent", ProjectID: project.ID, Phase: string(state.PhaseRunning), @@ -2122,15 +2124,15 @@ func TestGetAgent_HarnessConfigEnriched(t *testing.T) { ctx := context.Background() project := &store.Project{ - ID: "project-harness-get", + ID: tid("project-harness-get"), Name: "Harness Get Project", Slug: "harness-get-project", } require.NoError(t, s.CreateProject(ctx, project)) agent := &store.Agent{ - ID: "agent-harness-get", - Slug: "agent-harness-get", + ID: tid("agent-harness-get"), + Slug: tid("agent-harness-get"), Name: "Harness Get Agent", ProjectID: project.ID, Phase: string(state.PhaseRunning), @@ -2233,21 +2235,22 @@ func TestHeartbeat_BackfillsProfile(t *testing.T) { ctx := context.Background() project := &store.Project{ - ID: "project-profile-hb", + ID: tid("project-profile-hb"), Name: "Profile HB Project", Slug: "profile-hb-project", } require.NoError(t, s.CreateProject(ctx, project)) broker := &store.RuntimeBroker{ - ID: "broker-profile-hb", + ID: tid("broker-profile-hb"), Name: "Profile HB Broker", + Slug: "profile-hb-broker", Status: store.BrokerStatusOnline, } require.NoError(t, s.CreateRuntimeBroker(ctx, broker)) agent := &store.Agent{ - ID: "agent-profile-hb", + ID: tid("agent-profile-hb"), Slug: "profile-hb-agent", Name: "Profile HB Agent", ProjectID: project.ID, @@ -2299,7 +2302,7 @@ func TestCreateAgent_HarnessNotTemplateUUID(t *testing.T) { // the template to be resolved locally by the broker. require.NoError(t, s.AddProjectProvider(ctx, &store.ProjectProvider{ ProjectID: project.ID, - BrokerID: "broker-create", + BrokerID: tid("broker-create"), BrokerName: "Create Test Broker", LocalPath: "/some/local/path", Status: "online", @@ -2337,11 +2340,11 @@ func TestCreateProjectAgent_RecreateFromRunningStatus(t *testing.T) { ctx := context.Background() runningAgent := &store.Agent{ - ID: "project-agent-running", + ID: tid("project-agent-running"), Slug: "running-project-agent", Name: "running-project-agent", ProjectID: project.ID, - RuntimeBrokerID: "broker-create", + RuntimeBrokerID: tid("broker-create"), Phase: string(state.PhaseRunning), } require.NoError(t, s.CreateAgent(ctx, runningAgent)) @@ -2356,7 +2359,7 @@ func TestCreateProjectAgent_RecreateFromRunningStatus(t *testing.T) { require.Equal(t, http.StatusCreated, rec.Code, "re-creating a running project agent should succeed with 201") - _, err := s.GetAgent(ctx, "project-agent-running") + _, err := s.GetAgent(ctx, tid("project-agent-running")) assert.ErrorIs(t, err, store.ErrNotFound, "old running agent should be deleted") assert.True(t, disp.deleteCalled, "dispatcher should have been asked to delete old agent") @@ -2364,7 +2367,7 @@ func TestCreateProjectAgent_RecreateFromRunningStatus(t *testing.T) { var resp CreateAgentResponse require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &resp)) require.NotNil(t, resp.Agent) - assert.NotEqual(t, "project-agent-running", resp.Agent.ID) + assert.NotEqual(t, tid("project-agent-running"), resp.Agent.ID) assert.Equal(t, string(state.PhaseRunning), resp.Agent.Phase) } @@ -2374,11 +2377,11 @@ func TestCreateProjectAgent_RecreateFromStoppedStatus(t *testing.T) { ctx := context.Background() stoppedAgent := &store.Agent{ - ID: "project-agent-stopped", + ID: tid("project-agent-stopped"), Slug: "stopped-project-agent", Name: "stopped-project-agent", ProjectID: project.ID, - RuntimeBrokerID: "broker-create", + RuntimeBrokerID: tid("broker-create"), Phase: string(state.PhaseStopped), } require.NoError(t, s.CreateAgent(ctx, stoppedAgent)) @@ -2393,7 +2396,7 @@ func TestCreateProjectAgent_RecreateFromStoppedStatus(t *testing.T) { require.Equal(t, http.StatusCreated, rec.Code, "re-creating a stopped project agent should succeed with 201") - _, err := s.GetAgent(ctx, "project-agent-stopped") + _, err := s.GetAgent(ctx, tid("project-agent-stopped")) assert.ErrorIs(t, err, store.ErrNotFound, "old stopped agent should be deleted") } @@ -2439,11 +2442,11 @@ func TestCreateProjectAgent_RecreateFromErrorStatus(t *testing.T) { ctx := context.Background() errorAgent := &store.Agent{ - ID: "project-agent-errored", + ID: tid("project-agent-errored"), Slug: "errored-project-agent", Name: "errored-project-agent", ProjectID: project.ID, - RuntimeBrokerID: "broker-create", + RuntimeBrokerID: tid("broker-create"), Phase: string(state.PhaseError), } require.NoError(t, s.CreateAgent(ctx, errorAgent)) @@ -2458,7 +2461,7 @@ func TestCreateProjectAgent_RecreateFromErrorStatus(t *testing.T) { require.Equal(t, http.StatusCreated, rec.Code, "re-creating an errored project agent should succeed with 201") - _, err := s.GetAgent(ctx, "project-agent-errored") + _, err := s.GetAgent(ctx, tid("project-agent-errored")) assert.ErrorIs(t, err, store.ErrNotFound, "old errored agent should be deleted") } @@ -2468,11 +2471,11 @@ func TestCreateProjectAgent_RestartFromProvisioningStatus(t *testing.T) { ctx := context.Background() provAgent := &store.Agent{ - ID: "project-agent-prov", + ID: tid("project-agent-prov"), Slug: "prov-project-agent", Name: "prov-project-agent", ProjectID: project.ID, - RuntimeBrokerID: "broker-create", + RuntimeBrokerID: tid("broker-create"), Phase: string(state.PhaseProvisioning), } require.NoError(t, s.CreateAgent(ctx, provAgent)) @@ -2499,11 +2502,11 @@ func TestCreateProjectAgent_RestartFromPendingStatus(t *testing.T) { ctx := context.Background() pendingAgent := &store.Agent{ - ID: "project-agent-pending", + ID: tid("project-agent-pending"), Slug: "pending-project-agent", Name: "pending-project-agent", ProjectID: project.ID, - RuntimeBrokerID: "broker-create", + RuntimeBrokerID: tid("broker-create"), Phase: string(state.PhaseCreated), } require.NoError(t, s.CreateAgent(ctx, pendingAgent)) @@ -2529,11 +2532,11 @@ func TestCreateProjectAgent_ConfigUpdateOnRestart(t *testing.T) { ctx := context.Background() existingAgent := &store.Agent{ - ID: "project-agent-config", + ID: tid("project-agent-config"), Slug: "config-project-agent", Name: "config-project-agent", ProjectID: project.ID, - RuntimeBrokerID: "broker-create", + RuntimeBrokerID: tid("broker-create"), Phase: string(state.PhaseCreated), AppliedConfig: &store.AgentAppliedConfig{ Task: "old task", @@ -2573,7 +2576,7 @@ func TestCreateProjectAgent_BrokerIDRecovery(t *testing.T) { // Pre-create agent with empty RuntimeBrokerID (simulates agent created // before a broker was registered). existingAgent := &store.Agent{ - ID: "project-agent-no-broker", + ID: tid("project-agent-no-broker"), Slug: "no-broker-project-agent", Name: "no-broker-project-agent", ProjectID: project.ID, @@ -2598,7 +2601,7 @@ func TestCreateProjectAgent_BrokerIDRecovery(t *testing.T) { persisted, err := s.GetAgent(ctx, resp.Agent.ID) require.NoError(t, err) - assert.Equal(t, "broker-create", persisted.RuntimeBrokerID, + assert.Equal(t, tid("broker-create"), persisted.RuntimeBrokerID, "RuntimeBrokerID should be recovered from resolved broker") } @@ -2608,7 +2611,7 @@ func TestCreateAgent_BrokerIDRecovery(t *testing.T) { ctx := context.Background() existingAgent := &store.Agent{ - ID: "agent-no-broker", + ID: tid("agent-no-broker"), Slug: "no-broker-agent", Name: "no-broker-agent", ProjectID: project.ID, @@ -2632,7 +2635,7 @@ func TestCreateAgent_BrokerIDRecovery(t *testing.T) { persisted, err := s.GetAgent(ctx, resp.Agent.ID) require.NoError(t, err) - assert.Equal(t, "broker-create", persisted.RuntimeBrokerID, + assert.Equal(t, tid("broker-create"), persisted.RuntimeBrokerID, "RuntimeBrokerID should be recovered from resolved broker") } @@ -2645,11 +2648,11 @@ func TestCreateAgent_CleanupModeStrictFailsOnBrokerDeleteError(t *testing.T) { ctx := context.Background() existingAgent := &store.Agent{ - ID: "agent-stale-strict", + ID: tid("agent-stale-strict"), Slug: "stale-strict-agent", Name: "stale-strict-agent", ProjectID: project.ID, - RuntimeBrokerID: "broker-create", + RuntimeBrokerID: tid("broker-create"), Phase: string(state.PhaseRunning), } require.NoError(t, s.CreateAgent(ctx, existingAgent)) @@ -2676,11 +2679,11 @@ func TestCreateAgent_CleanupModeForceContinuesOnBrokerDeleteError(t *testing.T) ctx := context.Background() existingAgent := &store.Agent{ - ID: "agent-stale-force", + ID: tid("agent-stale-force"), Slug: "stale-force-agent", Name: "stale-force-agent", ProjectID: project.ID, - RuntimeBrokerID: "broker-create", + RuntimeBrokerID: tid("broker-create"), Phase: string(state.PhaseRunning), } require.NoError(t, s.CreateAgent(ctx, existingAgent)) @@ -2717,14 +2720,14 @@ func TestCreateAgent_NotifyCreatesSubscription(t *testing.T) { // Create project and broker infrastructure project := &store.Project{ - ID: "project-notify", + ID: tid("project-notify"), Name: "Notify Project", Slug: "notify-project", } require.NoError(t, s.CreateProject(ctx, project)) broker := &store.RuntimeBroker{ - ID: "broker-notify", + ID: tid("broker-notify"), Name: "Notify Broker", Slug: "notify-broker", Status: store.BrokerStatusOnline, @@ -2739,9 +2742,11 @@ func TestCreateAgent_NotifyCreatesSubscription(t *testing.T) { project.DefaultRuntimeBrokerID = broker.ID require.NoError(t, s.UpdateProject(ctx, project)) - // Create the calling agent (the one that will subscribe to notifications) + // Create the calling agent (the one that will subscribe to notifications). + // The created sub-agent's created_by/owner_id FK references the users table. + permSeedUser(t, ctx, s, tid("agent-lead")) callingAgent := &store.Agent{ - ID: "agent-lead", + ID: tid("agent-lead"), Slug: "lead-agent", Name: "Lead Agent", ProjectID: project.ID, @@ -2855,14 +2860,14 @@ func TestCreateAgent_NotifySubscriptionCascadeOnDelete(t *testing.T) { ctx := context.Background() project := &store.Project{ - ID: "project-cascade", + ID: tid("project-cascade"), Name: "Cascade Project", Slug: "cascade-project", } require.NoError(t, s.CreateProject(ctx, project)) broker := &store.RuntimeBroker{ - ID: "broker-cascade", + ID: tid("broker-cascade"), Name: "Cascade Broker", Slug: "cascade-broker", Status: store.BrokerStatusOnline, @@ -2877,8 +2882,9 @@ func TestCreateAgent_NotifySubscriptionCascadeOnDelete(t *testing.T) { project.DefaultRuntimeBrokerID = broker.ID require.NoError(t, s.UpdateProject(ctx, project)) + permSeedUser(t, ctx, s, tid("agent-cascade-lead")) callingAgent := &store.Agent{ - ID: "agent-cascade-lead", + ID: tid("agent-cascade-lead"), Slug: "cascade-lead", Name: "Cascade Lead", ProjectID: project.ID, @@ -2935,17 +2941,17 @@ func TestBrokerHeartbeat_PublishesActivitySSE(t *testing.T) { srv.SetEventPublisher(pub) // Create project, broker, and agent - project := &store.Project{ID: "project-hb-sse", Name: "HB SSE Project", Slug: "hb-sse-project"} + project := &store.Project{ID: tid("project-hb-sse"), Name: "HB SSE Project", Slug: "hb-sse-project"} require.NoError(t, s.CreateProject(ctx, project)) broker := &store.RuntimeBroker{ - ID: "broker-hb-sse", Name: "HB SSE Broker", Slug: "hb-sse-broker", + ID: tid("broker-hb-sse"), Name: "HB SSE Broker", Slug: "hb-sse-broker", Status: store.BrokerStatusOnline, } require.NoError(t, s.CreateRuntimeBroker(ctx, broker)) agent := &store.Agent{ - ID: "agent-hb-sse", Slug: "agent-hb-slug", Name: "HB SSE Agent", + ID: tid("agent-hb-sse"), Slug: "agent-hb-slug", Name: "HB SSE Agent", ProjectID: project.ID, RuntimeBrokerID: broker.ID, Phase: string(state.PhaseRunning), } @@ -2995,17 +3001,17 @@ func TestBrokerHeartbeat_RepeatedActivityDoesNotRefreshLastActivityEvent(t *test ctx := context.Background() // Create project, broker, and agent - project := &store.Project{ID: "project-stall-hb", Name: "Stall HB Project", Slug: "stall-hb-project"} + project := &store.Project{ID: tid("project-stall-hb"), Name: "Stall HB Project", Slug: "stall-hb-project"} require.NoError(t, s.CreateProject(ctx, project)) broker := &store.RuntimeBroker{ - ID: "broker-stall-hb", Name: "Stall HB Broker", Slug: "stall-hb-broker", + ID: tid("broker-stall-hb"), Name: "Stall HB Broker", Slug: "stall-hb-broker", Status: store.BrokerStatusOnline, } require.NoError(t, s.CreateRuntimeBroker(ctx, broker)) agent := &store.Agent{ - ID: "agent-stall-hb", Slug: "stall-hb-slug", Name: "Stall HB Agent", + ID: tid("agent-stall-hb"), Slug: "stall-hb-slug", Name: "Stall HB Agent", ProjectID: project.ID, RuntimeBrokerID: broker.ID, Phase: string(state.PhaseRunning), } @@ -3035,7 +3041,7 @@ func TestBrokerHeartbeat_RepeatedActivityDoesNotRefreshLastActivityEvent(t *test // Backdate last_activity_event to simulate time passing pastTime := time.Now().Add(-10 * time.Minute) - db := s.(*sqlite.SQLiteStore).DB() + db := s.(*entadapter.CompositeStore).DB() _, err = db.ExecContext(ctx, "UPDATE agents SET last_activity_event = ? WHERE id = ?", pastTime, agent.ID) require.NoError(t, err) @@ -3091,17 +3097,17 @@ func TestBrokerHeartbeat_StalledAgentNotOverwrittenBySameActivity(t *testing.T) ctx := context.Background() // Create project, broker, and agent - project := &store.Project{ID: "project-stall-keep", Name: "Stall Keep Project", Slug: "stall-keep-project"} + project := &store.Project{ID: tid("project-stall-keep"), Name: "Stall Keep Project", Slug: "stall-keep-project"} require.NoError(t, s.CreateProject(ctx, project)) broker := &store.RuntimeBroker{ - ID: "broker-stall-keep", Name: "Stall Keep Broker", Slug: "stall-keep-broker", + ID: tid("broker-stall-keep"), Name: "Stall Keep Broker", Slug: "stall-keep-broker", Status: store.BrokerStatusOnline, } require.NoError(t, s.CreateRuntimeBroker(ctx, broker)) agent := &store.Agent{ - ID: "agent-stall-keep", Slug: "stall-keep-slug", Name: "Stall Keep Agent", + ID: tid("agent-stall-keep"), Slug: "stall-keep-slug", Name: "Stall Keep Agent", ProjectID: project.ID, RuntimeBrokerID: broker.ID, Phase: string(state.PhaseRunning), } @@ -3114,7 +3120,7 @@ func TestBrokerHeartbeat_StalledAgentNotOverwrittenBySameActivity(t *testing.T) })) // Simulate stalled detection: mark agent stalled with stalled_from_activity = thinking - db := s.(*sqlite.SQLiteStore).DB() + db := s.(*entadapter.CompositeStore).DB() staleActivity := time.Now().Add(-10 * time.Minute) _, err := db.ExecContext(ctx, "UPDATE agents SET activity = 'stalled', stalled_from_activity = 'thinking', last_activity_event = ?, last_seen = ? WHERE id = ?", @@ -3149,17 +3155,17 @@ func TestBrokerHeartbeat_StalledAgentRecoveredByNewActivity(t *testing.T) { ctx := context.Background() // Create project, broker, and agent - project := &store.Project{ID: "project-stall-recover", Name: "Stall Recover Project", Slug: "stall-recover-project"} + project := &store.Project{ID: tid("project-stall-recover"), Name: "Stall Recover Project", Slug: "stall-recover-project"} require.NoError(t, s.CreateProject(ctx, project)) broker := &store.RuntimeBroker{ - ID: "broker-stall-recover", Name: "Stall Recover Broker", Slug: "stall-recover-broker", + ID: tid("broker-stall-recover"), Name: "Stall Recover Broker", Slug: "stall-recover-broker", Status: store.BrokerStatusOnline, } require.NoError(t, s.CreateRuntimeBroker(ctx, broker)) agent := &store.Agent{ - ID: "agent-stall-recover", Slug: "stall-recover-slug", Name: "Stall Recover Agent", + ID: tid("agent-stall-recover"), Slug: "stall-recover-slug", Name: "Stall Recover Agent", ProjectID: project.ID, RuntimeBrokerID: broker.ID, Phase: string(state.PhaseRunning), } @@ -3172,7 +3178,7 @@ func TestBrokerHeartbeat_StalledAgentRecoveredByNewActivity(t *testing.T) { })) // Simulate stalled detection: mark agent stalled with stalled_from_activity = thinking - db := s.(*sqlite.SQLiteStore).DB() + db := s.(*entadapter.CompositeStore).DB() staleActivity := time.Now().Add(-10 * time.Minute) _, err := db.ExecContext(ctx, "UPDATE agents SET activity = 'stalled', stalled_from_activity = 'thinking', last_activity_event = ?, last_seen = ? WHERE id = ?", @@ -3206,17 +3212,17 @@ func TestBrokerHeartbeat_StalledWorkingAgentNotOverwrittenBySameActivity(t *test srv, s := testServer(t) ctx := context.Background() - project := &store.Project{ID: "project-stall-working", Name: "Stall Working Project", Slug: "stall-working-project"} + project := &store.Project{ID: tid("project-stall-working"), Name: "Stall Working Project", Slug: "stall-working-project"} require.NoError(t, s.CreateProject(ctx, project)) broker := &store.RuntimeBroker{ - ID: "broker-stall-working", Name: "Stall Working Broker", Slug: "stall-working-broker", + ID: tid("broker-stall-working"), Name: "Stall Working Broker", Slug: "stall-working-broker", Status: store.BrokerStatusOnline, } require.NoError(t, s.CreateRuntimeBroker(ctx, broker)) agent := &store.Agent{ - ID: "agent-stall-working", Slug: "stall-working-slug", Name: "Stall Working Agent", + ID: tid("agent-stall-working"), Slug: "stall-working-slug", Name: "Stall Working Agent", ProjectID: project.ID, RuntimeBrokerID: broker.ID, Phase: string(state.PhaseRunning), } @@ -3229,7 +3235,7 @@ func TestBrokerHeartbeat_StalledWorkingAgentNotOverwrittenBySameActivity(t *test })) // Simulate stalled detection: mark agent stalled with stalled_from_activity = working - db := s.(*sqlite.SQLiteStore).DB() + db := s.(*entadapter.CompositeStore).DB() staleActivity := time.Now().Add(-10 * time.Minute) _, err := db.ExecContext(ctx, "UPDATE agents SET activity = 'stalled', stalled_from_activity = 'working', last_activity_event = ?, last_seen = ? WHERE id = ?", @@ -3262,17 +3268,17 @@ func TestBrokerHeartbeat_DoesNotRevertStoppedAgent(t *testing.T) { srv, s := testServer(t) ctx := context.Background() - project := &store.Project{ID: "project-stop-revert", Name: "Stop Revert Project", Slug: "stop-revert-project"} + project := &store.Project{ID: tid("project-stop-revert"), Name: "Stop Revert Project", Slug: "stop-revert-project"} require.NoError(t, s.CreateProject(ctx, project)) broker := &store.RuntimeBroker{ - ID: "broker-stop-revert", Name: "Stop Revert Broker", Slug: "stop-revert-broker", + ID: tid("broker-stop-revert"), Name: "Stop Revert Broker", Slug: "stop-revert-broker", Status: store.BrokerStatusOnline, } require.NoError(t, s.CreateRuntimeBroker(ctx, broker)) agent := &store.Agent{ - ID: "agent-stop-revert", Slug: "stop-revert-slug", Name: "Stop Revert Agent", + ID: tid("agent-stop-revert"), Slug: "stop-revert-slug", Name: "Stop Revert Agent", ProjectID: project.ID, RuntimeBrokerID: broker.ID, Phase: string(state.PhaseRunning), } @@ -3320,17 +3326,17 @@ func TestBrokerHeartbeat_DoesNotRevertStoppedAgent_LegacyPath(t *testing.T) { srv, s := testServer(t) ctx := context.Background() - project := &store.Project{ID: "project-stop-legacy", Name: "Stop Legacy Project", Slug: "stop-legacy-project"} + project := &store.Project{ID: tid("project-stop-legacy"), Name: "Stop Legacy Project", Slug: "stop-legacy-project"} require.NoError(t, s.CreateProject(ctx, project)) broker := &store.RuntimeBroker{ - ID: "broker-stop-legacy", Name: "Stop Legacy Broker", Slug: "stop-legacy-broker", + ID: tid("broker-stop-legacy"), Name: "Stop Legacy Broker", Slug: "stop-legacy-broker", Status: store.BrokerStatusOnline, } require.NoError(t, s.CreateRuntimeBroker(ctx, broker)) agent := &store.Agent{ - ID: "agent-stop-legacy", Slug: "stop-legacy-slug", Name: "Stop Legacy Agent", + ID: tid("agent-stop-legacy"), Slug: "stop-legacy-slug", Name: "Stop Legacy Agent", ProjectID: project.ID, RuntimeBrokerID: broker.ID, Phase: string(state.PhaseRunning), } @@ -3368,17 +3374,17 @@ func TestBrokerHeartbeat_PropagatesTerminalActivityOnStoppedAgent(t *testing.T) srv, s := testServer(t) ctx := context.Background() - project := &store.Project{ID: "proj-crash-hb", Name: "Crash HB Project", Slug: "crash-hb-proj"} + project := &store.Project{ID: tid("proj-crash-hb"), Name: "Crash HB Project", Slug: "crash-hb-proj"} require.NoError(t, s.CreateProject(ctx, project)) broker := &store.RuntimeBroker{ - ID: "broker-crash-hb", Name: "Crash HB Broker", Slug: "crash-hb-broker", + ID: tid("broker-crash-hb"), Name: "Crash HB Broker", Slug: "crash-hb-broker", Status: store.BrokerStatusOnline, } require.NoError(t, s.CreateRuntimeBroker(ctx, broker)) agent := &store.Agent{ - ID: "agent-crash-hb", Slug: "crash-hb-slug", Name: "Crash HB Agent", + ID: tid("agent-crash-hb"), Slug: "crash-hb-slug", Name: "Crash HB Agent", ProjectID: project.ID, RuntimeBrokerID: broker.ID, Phase: string(state.PhaseStopped), } @@ -3417,17 +3423,17 @@ func TestBrokerHeartbeat_DoesNotOverwriteTerminalActivityWithNonTerminal(t *test srv, s := testServer(t) ctx := context.Background() - project := &store.Project{ID: "proj-term-guard", Name: "Term Guard Project", Slug: "term-guard-proj"} + project := &store.Project{ID: tid("proj-term-guard"), Name: "Term Guard Project", Slug: "term-guard-proj"} require.NoError(t, s.CreateProject(ctx, project)) broker := &store.RuntimeBroker{ - ID: "broker-term-guard", Name: "Term Guard Broker", Slug: "term-guard-broker", + ID: tid("broker-term-guard"), Name: "Term Guard Broker", Slug: "term-guard-broker", Status: store.BrokerStatusOnline, } require.NoError(t, s.CreateRuntimeBroker(ctx, broker)) agent := &store.Agent{ - ID: "agent-term-guard", Slug: "term-guard-slug", Name: "Term Guard Agent", + ID: tid("agent-term-guard"), Slug: "term-guard-slug", Name: "Term Guard Agent", ProjectID: project.ID, RuntimeBrokerID: broker.ID, Phase: string(state.PhaseStopped), Activity: string(state.ActivityCrashed), @@ -3464,11 +3470,11 @@ func TestCreateAgent_RestartCreatesNotificationSubscription(t *testing.T) { // Pre-create an agent in "created" phase (provisioned but not started) existingAgent := &store.Agent{ - ID: "agent-notify-restart", + ID: tid("agent-notify-restart"), Slug: "notify-agent", Name: "notify-agent", ProjectID: project.ID, - RuntimeBrokerID: "broker-create", + RuntimeBrokerID: tid("broker-create"), Phase: string(state.PhaseCreated), } require.NoError(t, s.CreateAgent(ctx, existingAgent)) @@ -3502,11 +3508,11 @@ func TestCreateAgent_RestartNoSubscriptionWithoutNotify(t *testing.T) { // Pre-create an agent in "created" phase existingAgent := &store.Agent{ - ID: "agent-no-notify", + ID: tid("agent-no-notify"), Slug: "no-notify-agent", Name: "no-notify-agent", ProjectID: project.ID, - RuntimeBrokerID: "broker-create", + RuntimeBrokerID: tid("broker-create"), Phase: string(state.PhaseCreated), } require.NoError(t, s.CreateAgent(ctx, existingAgent)) @@ -3531,14 +3537,14 @@ func TestHandleAgentMessage_PlainTextBuildsStructuredMessage(t *testing.T) { ctx := context.Background() project := &store.Project{ - ID: "project-msg", + ID: tid("project-msg"), Name: "Msg Test Project", Slug: "msg-test-project", } require.NoError(t, s.CreateProject(ctx, project)) broker := &store.RuntimeBroker{ - ID: "broker-msg", + ID: tid("broker-msg"), Name: "Msg Test Broker", Slug: "msg-test-broker", Status: store.BrokerStatusOnline, @@ -3553,8 +3559,8 @@ func TestHandleAgentMessage_PlainTextBuildsStructuredMessage(t *testing.T) { })) agent := &store.Agent{ - ID: "agent-msg-1", - Slug: "agent-msg-1", + ID: tid("agent-msg-1"), + Slug: tid("agent-msg-1"), Name: "Msg Agent", ProjectID: project.ID, RuntimeBrokerID: broker.ID, @@ -3597,14 +3603,14 @@ func TestHandleAgentMessage_StructuredMessagePopulatesSender(t *testing.T) { ctx := context.Background() project := &store.Project{ - ID: "project-msg-sender", + ID: tid("project-msg-sender"), Name: "Msg Sender Project", Slug: "msg-sender-project", } require.NoError(t, s.CreateProject(ctx, project)) broker := &store.RuntimeBroker{ - ID: "broker-msg-sender", + ID: tid("broker-msg-sender"), Name: "Msg Sender Broker", Slug: "msg-sender-broker", Status: store.BrokerStatusOnline, @@ -3618,8 +3624,8 @@ func TestHandleAgentMessage_StructuredMessagePopulatesSender(t *testing.T) { })) agent := &store.Agent{ - ID: "agent-msg-sender-1", - Slug: "agent-msg-sender-1", + ID: tid("agent-msg-sender-1"), + Slug: tid("agent-msg-sender-1"), Name: "Msg Sender Agent", ProjectID: project.ID, RuntimeBrokerID: broker.ID, @@ -3659,14 +3665,14 @@ func TestHandleAgentMessage_NotifyCreatesSubscription(t *testing.T) { ctx := context.Background() project := &store.Project{ - ID: "project-msg-notify", + ID: tid("project-msg-notify"), Name: "Msg Notify Project", Slug: "msg-notify-project", } require.NoError(t, s.CreateProject(ctx, project)) broker := &store.RuntimeBroker{ - ID: "broker-msg-notify", + ID: tid("broker-msg-notify"), Name: "Msg Notify Broker", Slug: "msg-notify-broker", Status: store.BrokerStatusOnline, @@ -3680,8 +3686,8 @@ func TestHandleAgentMessage_NotifyCreatesSubscription(t *testing.T) { })) agent := &store.Agent{ - ID: "agent-msg-notify-1", - Slug: "agent-msg-notify-1", + ID: tid("agent-msg-notify-1"), + Slug: tid("agent-msg-notify-1"), Name: "Msg Notify Agent", ProjectID: project.ID, RuntimeBrokerID: broker.ID, @@ -3719,14 +3725,14 @@ func TestHandleAgentMessage_NoNotifyNoSubscription(t *testing.T) { ctx := context.Background() project := &store.Project{ - ID: "project-msg-no-notify", + ID: tid("project-msg-no-notify"), Name: "Msg No Notify Project", Slug: "msg-no-notify-project", } require.NoError(t, s.CreateProject(ctx, project)) broker := &store.RuntimeBroker{ - ID: "broker-msg-no-notify", + ID: tid("broker-msg-no-notify"), Name: "Msg No Notify Broker", Slug: "msg-no-notify-broker", Status: store.BrokerStatusOnline, @@ -3740,8 +3746,8 @@ func TestHandleAgentMessage_NoNotifyNoSubscription(t *testing.T) { })) agent := &store.Agent{ - ID: "agent-msg-no-notify-1", - Slug: "agent-msg-no-notify-1", + ID: tid("agent-msg-no-notify-1"), + Slug: tid("agent-msg-no-notify-1"), Name: "Msg No Notify Agent", ProjectID: project.ID, RuntimeBrokerID: broker.ID, @@ -3772,15 +3778,15 @@ func TestHandleAgentMessage_NoDispatcher_Returns503(t *testing.T) { ctx := context.Background() project := &store.Project{ - ID: "project-msg-503", + ID: tid("project-msg-503"), Name: "Msg 503 Project", Slug: "msg-503-project", } require.NoError(t, s.CreateProject(ctx, project)) agent := &store.Agent{ - ID: "agent-msg-503", - Slug: "agent-msg-503", + ID: tid("agent-msg-503"), + Slug: tid("agent-msg-503"), Name: "Msg 503 Agent", ProjectID: project.ID, Phase: string(state.PhaseRunning), @@ -3803,15 +3809,15 @@ func TestHandleAgentMessage_NoBrokerID_Returns503(t *testing.T) { ctx := context.Background() project := &store.Project{ - ID: "project-msg-503-nobroker", + ID: tid("project-msg-503-nobroker"), Name: "Msg 503 NoBroker Project", Slug: "msg-503-nobroker-project", } require.NoError(t, s.CreateProject(ctx, project)) agent := &store.Agent{ - ID: "agent-msg-503-nobroker", - Slug: "agent-msg-503-nobroker", + ID: tid("agent-msg-503-nobroker"), + Slug: tid("agent-msg-503-nobroker"), Name: "Msg 503 NoBroker Agent", ProjectID: project.ID, Phase: string(state.PhaseRunning), @@ -3877,14 +3883,14 @@ func TestCreateAgent_GCPIdentityAssign(t *testing.T) { // Register and verify a GCP service account sa := &store.GCPServiceAccount{ - ID: "sa-assign-1", + ID: tid("sa-assign-1"), Scope: store.ScopeProject, ScopeID: project.ID, Email: "worker@project.iam.gserviceaccount.com", - ProjectID: "my-project", + ProjectID: tid("my-project"), Verified: true, VerifiedAt: time.Now(), - CreatedBy: "user-1", + CreatedBy: tid("user-1"), CreatedAt: time.Now(), } require.NoError(t, s.CreateGCPServiceAccount(ctx, sa)) @@ -4033,13 +4039,13 @@ func TestCreateAgent_GCPIdentityAssignUnverifiedSA(t *testing.T) { ctx := context.Background() sa := &store.GCPServiceAccount{ - ID: "sa-unverified-1", + ID: tid("sa-unverified-1"), Scope: store.ScopeProject, ScopeID: project.ID, Email: "unverified@project.iam.gserviceaccount.com", - ProjectID: "my-project", + ProjectID: tid("my-project"), Verified: false, - CreatedBy: "user-1", + CreatedBy: tid("user-1"), CreatedAt: time.Now(), } require.NoError(t, s.CreateGCPServiceAccount(ctx, sa)) @@ -4061,14 +4067,14 @@ func TestCreateAgent_GCPIdentityAssignWrongProject(t *testing.T) { ctx := context.Background() sa := &store.GCPServiceAccount{ - ID: "sa-other-project-1", + ID: tid("sa-other-project-1"), Scope: store.ScopeProject, ScopeID: "other-project-id", Email: "other@project.iam.gserviceaccount.com", - ProjectID: "my-project", + ProjectID: tid("my-project"), Verified: true, VerifiedAt: time.Now(), - CreatedBy: "user-1", + CreatedBy: tid("user-1"), CreatedAt: time.Now(), } require.NoError(t, s.CreateGCPServiceAccount(ctx, sa)) @@ -4121,7 +4127,7 @@ func TestCreateAgent_GCPPassthrough_BrokerOwnerAllowed(t *testing.T) { // Create a user who owns the broker owner := &store.User{ - ID: "user-broker-owner", + ID: tid("user-broker-owner"), Email: "owner@test.com", DisplayName: "Broker Owner", Role: store.UserRoleMember, @@ -4133,7 +4139,7 @@ func TestCreateAgent_GCPPassthrough_BrokerOwnerAllowed(t *testing.T) { // Create a project owned by the broker owner with proper policies project := &store.Project{ - ID: "project-pt-owner", + ID: tid("project-pt-owner"), Name: "Passthrough Owner Project", Slug: "passthrough-owner-project", OwnerID: owner.ID, @@ -4146,7 +4152,7 @@ func TestCreateAgent_GCPPassthrough_BrokerOwnerAllowed(t *testing.T) { // Create a broker owned by the same user broker := &store.RuntimeBroker{ - ID: "broker-pt-owner", + ID: tid("broker-pt-owner"), Name: "Owner Broker", Slug: "owner-broker", Status: store.BrokerStatusOnline, @@ -4186,7 +4192,7 @@ func TestCreateAgent_GCPPassthrough_NonOwnerDenied(t *testing.T) { // Create the broker owner owner := &store.User{ - ID: "user-broker-owner-2", + ID: tid("user-broker-owner-2"), Email: "owner2@test.com", DisplayName: "Broker Owner 2", Role: store.UserRoleMember, @@ -4197,7 +4203,7 @@ func TestCreateAgent_GCPPassthrough_NonOwnerDenied(t *testing.T) { // Create a non-owner user nonOwner := &store.User{ - ID: "user-non-owner", + ID: tid("user-non-owner"), Email: "nonowner@test.com", DisplayName: "Non Owner", Role: store.UserRoleMember, @@ -4209,7 +4215,7 @@ func TestCreateAgent_GCPPassthrough_NonOwnerDenied(t *testing.T) { // Create a project where the non-owner is a member project := &store.Project{ - ID: "project-pt-nonowner", + ID: tid("project-pt-nonowner"), Name: "Passthrough NonOwner Project", Slug: "passthrough-nonowner-project", OwnerID: nonOwner.ID, @@ -4222,7 +4228,7 @@ func TestCreateAgent_GCPPassthrough_NonOwnerDenied(t *testing.T) { // Create a broker owned by a DIFFERENT user broker := &store.RuntimeBroker{ - ID: "broker-pt-nonowner", + ID: tid("broker-pt-nonowner"), Name: "Other Broker", Slug: "other-broker", Status: store.BrokerStatusOnline, @@ -4261,7 +4267,7 @@ func TestCreateAgent_GCPPassthrough_AdminAllowed(t *testing.T) { ctx := context.Background() brokerOwner := &store.User{ - ID: "user-broker-owner-3", + ID: tid("user-broker-owner-3"), Email: "owner3@test.com", DisplayName: "Broker Owner 3", Role: store.UserRoleMember, @@ -4271,7 +4277,7 @@ func TestCreateAgent_GCPPassthrough_AdminAllowed(t *testing.T) { require.NoError(t, s.CreateUser(ctx, brokerOwner)) adminUser := &store.User{ - ID: "user-admin-pt", + ID: tid("user-admin-pt"), Email: "admin@test.com", DisplayName: "Admin User", Role: store.UserRoleAdmin, @@ -4282,7 +4288,7 @@ func TestCreateAgent_GCPPassthrough_AdminAllowed(t *testing.T) { ensureHubMembership(ctx, s, adminUser.ID) project := &store.Project{ - ID: "project-pt-admin", + ID: tid("project-pt-admin"), Name: "Passthrough Admin Project", Slug: "passthrough-admin-project", OwnerID: adminUser.ID, @@ -4295,7 +4301,7 @@ func TestCreateAgent_GCPPassthrough_AdminAllowed(t *testing.T) { // Broker owned by someone else broker := &store.RuntimeBroker{ - ID: "broker-pt-admin", + ID: tid("broker-pt-admin"), Name: "Admin Test Broker", Slug: "admin-test-broker", Status: store.BrokerStatusOnline, @@ -4330,14 +4336,14 @@ func TestCreateAgent_GCPIdentityBlockOverridesProjectDefault(t *testing.T) { // Register and verify a GCP service account sa := &store.GCPServiceAccount{ - ID: "sa-project-default", + ID: tid("sa-project-default"), Scope: store.ScopeProject, ScopeID: project.ID, Email: "project-default@project.iam.gserviceaccount.com", - ProjectID: "my-project", + ProjectID: tid("my-project"), Verified: true, VerifiedAt: time.Now(), - CreatedBy: "user-1", + CreatedBy: tid("user-1"), CreatedAt: time.Now(), } require.NoError(t, s.CreateGCPServiceAccount(ctx, sa)) @@ -4376,14 +4382,14 @@ func TestCreateAgent_GCPIdentityProjectDefaultApplied(t *testing.T) { // Register and verify a GCP service account sa := &store.GCPServiceAccount{ - ID: "sa-project-applied", + ID: tid("sa-project-applied"), Scope: store.ScopeProject, ScopeID: project.ID, Email: "project-applied@project.iam.gserviceaccount.com", - ProjectID: "my-project", + ProjectID: tid("my-project"), Verified: true, VerifiedAt: time.Now(), - CreatedBy: "user-1", + CreatedBy: tid("user-1"), CreatedAt: time.Now(), } require.NoError(t, s.CreateGCPServiceAccount(ctx, sa)) @@ -4416,13 +4422,13 @@ func TestPreserveTerminalPhase(t *testing.T) { srv, s := testServer(t) ctx := context.Background() - project := &store.Project{ID: "project-tp", Name: "TP Project", Slug: "tp-project"} + project := &store.Project{ID: tid("project-tp"), Name: "TP Project", Slug: "tp-project"} require.NoError(t, s.CreateProject(ctx, project)) t.Run("preserves error phase", func(t *testing.T) { agent := &store.Agent{ - ID: "agent-tp-error", - Slug: "agent-tp-error", + ID: tid("agent-tp-error"), + Slug: tid("agent-tp-error"), Name: "TP Error Agent", ProjectID: project.ID, Phase: string(state.PhaseCreated), @@ -4448,8 +4454,8 @@ func TestPreserveTerminalPhase(t *testing.T) { t.Run("preserves stopped phase", func(t *testing.T) { agent := &store.Agent{ - ID: "agent-tp-stopped", - Slug: "agent-tp-stopped", + ID: tid("agent-tp-stopped"), + Slug: tid("agent-tp-stopped"), Name: "TP Stopped Agent", ProjectID: project.ID, Phase: string(state.PhaseCreated), @@ -4468,8 +4474,8 @@ func TestPreserveTerminalPhase(t *testing.T) { t.Run("does not overwrite non-terminal phase", func(t *testing.T) { agent := &store.Agent{ - ID: "agent-tp-running", - Slug: "agent-tp-running", + ID: tid("agent-tp-running"), + Slug: tid("agent-tp-running"), Name: "TP Running Agent", ProjectID: project.ID, Phase: string(state.PhaseCreated), @@ -4492,17 +4498,17 @@ func TestListAgents_GlobalEndpointReturnsAllAgents(t *testing.T) { ctx := context.Background() // Create two projects with agents in each - project1 := &store.Project{ID: "project-global-1", Name: "Project One", Slug: "project-one"} - project2 := &store.Project{ID: "project-global-2", Name: "Project Two", Slug: "project-two"} + project1 := &store.Project{ID: tid("project-global-1"), Name: "Project One", Slug: "project-one"} + project2 := &store.Project{ID: tid("project-global-2"), Name: "Project Two", Slug: "project-two"} require.NoError(t, s.CreateProject(ctx, project1)) require.NoError(t, s.CreateProject(ctx, project2)) agent1 := &store.Agent{ - ID: "agent-g1", Slug: "agent-g1", Name: "Agent G1", + ID: tid("agent-g1"), Slug: tid("agent-g1"), Name: "Agent G1", ProjectID: project1.ID, Phase: string(state.PhaseRunning), } agent2 := &store.Agent{ - ID: "agent-g2", Slug: "agent-g2", Name: "Agent G2", + ID: tid("agent-g2"), Slug: tid("agent-g2"), Name: "Agent G2", ProjectID: project2.ID, Phase: string(state.PhaseCreated), } require.NoError(t, s.CreateAgent(ctx, agent1)) @@ -4545,14 +4551,14 @@ func TestHandleAgentExec_DispatchesToRuntimeBroker(t *testing.T) { ctx := context.Background() project := &store.Project{ - ID: "project-exec", + ID: tid("project-exec"), Name: "Exec Project", Slug: "exec-project", } require.NoError(t, s.CreateProject(ctx, project)) broker := &store.RuntimeBroker{ - ID: "broker-exec", + ID: tid("broker-exec"), Name: "Exec Broker", Slug: "exec-broker", Status: store.BrokerStatusOnline, @@ -4566,8 +4572,8 @@ func TestHandleAgentExec_DispatchesToRuntimeBroker(t *testing.T) { })) agent := &store.Agent{ - ID: "agent-exec-1", - Slug: "agent-exec-1", + ID: tid("agent-exec-1"), + Slug: tid("agent-exec-1"), Name: "Exec Agent", ProjectID: project.ID, RuntimeBrokerID: broker.ID, @@ -4597,14 +4603,14 @@ func TestHandleProjectAgentExec_DispatchesToRuntimeBroker(t *testing.T) { ctx := context.Background() project := &store.Project{ - ID: "project-exec-project-route", + ID: tid("project-exec-project-route"), Name: "Exec Project Route", Slug: "exec-project-route", } require.NoError(t, s.CreateProject(ctx, project)) broker := &store.RuntimeBroker{ - ID: "broker-exec-project-route", + ID: tid("broker-exec-project-route"), Name: "Exec Broker Project Route", Slug: "exec-broker-project-route", Status: store.BrokerStatusOnline, @@ -4618,8 +4624,8 @@ func TestHandleProjectAgentExec_DispatchesToRuntimeBroker(t *testing.T) { })) agent := &store.Agent{ - ID: "agent-exec-project-route", - Slug: "agent-exec-project-route", + ID: tid("agent-exec-project-route"), + Slug: tid("agent-exec-project-route"), Name: "Exec Agent Project Route", ProjectID: project.ID, RuntimeBrokerID: broker.ID, diff --git a/pkg/hub/handlers_auth_test.go b/pkg/hub/handlers_auth_test.go index 42edc3ee6..ede58f8bf 100644 --- a/pkg/hub/handlers_auth_test.go +++ b/pkg/hub/handlers_auth_test.go @@ -161,7 +161,7 @@ func TestAuthMe(t *testing.T) { // Create a user user := &store.User{ - ID: "user_123", + ID: tid("user_123"), Email: "me@example.com", DisplayName: "Me", Role: "admin", diff --git a/pkg/hub/handlers_authz_remediation_test.go b/pkg/hub/handlers_authz_remediation_test.go index 9e7ec0ad8..0a86fa4c7 100644 --- a/pkg/hub/handlers_authz_remediation_test.go +++ b/pkg/hub/handlers_authz_remediation_test.go @@ -34,7 +34,7 @@ func grantUserActionOnResource(t *testing.T, s store.Store, userID, resourceType ctx := context.Background() policy := &store.Policy{ - ID: "policy-" + userID + "-" + resourceType + "-" + resourceID + "-" + string(action), + ID: tid("policy-" + userID + "-" + resourceType + "-" + resourceID + "-" + string(action)), Name: "Allow " + string(action) + " on " + resourceType + " " + resourceID, ScopeType: store.PolicyScopeHub, ResourceType: resourceType, @@ -57,7 +57,7 @@ func TestAuthzRemediation_ListEndpointsFilterUnauthorizedItems(t *testing.T) { ctx := context.Background() member := &store.User{ - ID: "member-list-authz", + ID: tid("member-list-authz"), Email: "member-list-authz@example.com", DisplayName: "Member List Authz", Role: store.UserRoleMember, @@ -66,8 +66,12 @@ func TestAuthzRemediation_ListEndpointsFilterUnauthorizedItems(t *testing.T) { } require.NoError(t, s.CreateUser(ctx, member)) + // The projects/agents below are owned by this user; agent owner_id is an FK + // to the users table, so the owner must exist. + permSeedUser(t, ctx, s, tid("owner-outside-user")) + visibleUser := &store.User{ - ID: "visible-user-authz", + ID: tid("visible-user-authz"), Email: "visible-user-authz@example.com", DisplayName: "Visible User", Role: store.UserRoleMember, @@ -77,7 +81,7 @@ func TestAuthzRemediation_ListEndpointsFilterUnauthorizedItems(t *testing.T) { require.NoError(t, s.CreateUser(ctx, visibleUser)) hiddenUser := &store.User{ - ID: "hidden-user-authz", + ID: tid("hidden-user-authz"), Email: "hidden-user-authz@example.com", DisplayName: "Hidden User", Role: store.UserRoleMember, @@ -87,61 +91,63 @@ func TestAuthzRemediation_ListEndpointsFilterUnauthorizedItems(t *testing.T) { require.NoError(t, s.CreateUser(ctx, hiddenUser)) visibleProject := &store.Project{ - ID: "project-visible-authz", - Slug: "project-visible-authz", + ID: tid("project-visible-authz"), + Slug: tid("project-visible-authz"), Name: "Visible Project", - OwnerID: "owner-outside-user", - CreatedBy: "owner-outside-user", + OwnerID: tid("owner-outside-user"), + CreatedBy: tid("owner-outside-user"), Created: time.Now(), Updated: time.Now(), } require.NoError(t, s.CreateProject(ctx, visibleProject)) hiddenProject := &store.Project{ - ID: "project-hidden-authz", - Slug: "project-hidden-authz", + ID: tid("project-hidden-authz"), + Slug: tid("project-hidden-authz"), Name: "Hidden Project", - OwnerID: "owner-outside-user", - CreatedBy: "owner-outside-user", + OwnerID: tid("owner-outside-user"), + CreatedBy: tid("owner-outside-user"), Created: time.Now(), Updated: time.Now(), } require.NoError(t, s.CreateProject(ctx, hiddenProject)) visibleBroker := &store.RuntimeBroker{ - ID: "broker-visible-authz", + ID: tid("broker-visible-authz"), Name: "Visible Broker", + Slug: "broker-visible-authz", Endpoint: "http://broker-visible", Status: store.BrokerStatusOnline, - CreatedBy: "owner-outside-user", + CreatedBy: tid("owner-outside-user"), } require.NoError(t, s.CreateRuntimeBroker(ctx, visibleBroker)) hiddenBroker := &store.RuntimeBroker{ - ID: "broker-hidden-authz", + ID: tid("broker-hidden-authz"), Name: "Hidden Broker", + Slug: "broker-hidden-authz", Endpoint: "http://broker-hidden", Status: store.BrokerStatusOnline, - CreatedBy: "owner-outside-user", + CreatedBy: tid("owner-outside-user"), } require.NoError(t, s.CreateRuntimeBroker(ctx, hiddenBroker)) visibleAgent := &store.Agent{ - ID: "agent-visible-authz", - Slug: "agent-visible-authz", + ID: tid("agent-visible-authz"), + Slug: tid("agent-visible-authz"), Name: "Visible Agent", ProjectID: visibleProject.ID, - OwnerID: "owner-outside-user", + OwnerID: tid("owner-outside-user"), Phase: string(state.PhaseRunning), } require.NoError(t, s.CreateAgent(ctx, visibleAgent)) hiddenAgent := &store.Agent{ - ID: "agent-hidden-authz", - Slug: "agent-hidden-authz", + ID: tid("agent-hidden-authz"), + Slug: tid("agent-hidden-authz"), Name: "Hidden Agent", ProjectID: hiddenProject.ID, - OwnerID: "owner-outside-user", + OwnerID: tid("owner-outside-user"), Phase: string(state.PhaseRunning), } require.NoError(t, s.CreateAgent(ctx, hiddenAgent)) @@ -189,7 +195,7 @@ func TestAuthzRemediation_AgentAndWorkspaceRoutesEnforceResourcePermissions(t *t ctx := context.Background() member := &store.User{ - ID: "member-workspace-authz", + ID: tid("member-workspace-authz"), Email: "member-workspace-authz@example.com", DisplayName: "Member Workspace Authz", Role: store.UserRoleMember, @@ -198,23 +204,27 @@ func TestAuthzRemediation_AgentAndWorkspaceRoutesEnforceResourcePermissions(t *t } require.NoError(t, s.CreateUser(ctx, member)) + // The project/agent below are owned by this user; agent owner_id is an FK + // to the users table, so the owner must exist. + permSeedUser(t, ctx, s, tid("owner-outside-user")) + project := &store.Project{ - ID: "project-workspace-authz", - Slug: "project-workspace-authz", + ID: tid("project-workspace-authz"), + Slug: tid("project-workspace-authz"), Name: "Workspace Project", - OwnerID: "owner-outside-user", - CreatedBy: "owner-outside-user", + OwnerID: tid("owner-outside-user"), + CreatedBy: tid("owner-outside-user"), Created: time.Now(), Updated: time.Now(), } require.NoError(t, s.CreateProject(ctx, project)) agent := &store.Agent{ - ID: "agent-workspace-authz", - Slug: "agent-workspace-authz", + ID: tid("agent-workspace-authz"), + Slug: tid("agent-workspace-authz"), Name: "Workspace Agent", ProjectID: project.ID, - OwnerID: "owner-outside-user", + OwnerID: tid("owner-outside-user"), Phase: string(state.PhaseStopped), } require.NoError(t, s.CreateAgent(ctx, agent)) diff --git a/pkg/hub/handlers_broker_test.go b/pkg/hub/handlers_broker_test.go index 2cbfbe2b9..473e50d92 100644 --- a/pkg/hub/handlers_broker_test.go +++ b/pkg/hub/handlers_broker_test.go @@ -42,7 +42,7 @@ func setupBrokerAuthzTest(t *testing.T) (srv *Server, s store.Store, alice, bob, ctx := context.Background() alice = &store.User{ - ID: "user-broker-alice", + ID: tid("user-broker-alice"), Email: "broker-alice@test.com", DisplayName: "Alice", Role: store.UserRoleMember, @@ -52,7 +52,7 @@ func setupBrokerAuthzTest(t *testing.T) (srv *Server, s store.Store, alice, bob, require.NoError(t, s.CreateUser(ctx, alice)) bob = &store.User{ - ID: "user-broker-bob", + ID: tid("user-broker-bob"), Email: "broker-bob@test.com", DisplayName: "Bob", Role: store.UserRoleMember, @@ -62,7 +62,7 @@ func setupBrokerAuthzTest(t *testing.T) (srv *Server, s store.Store, alice, bob, require.NoError(t, s.CreateUser(ctx, bob)) admin = &store.User{ - ID: "user-broker-admin", + ID: tid("user-broker-admin"), Email: "broker-admin@test.com", DisplayName: "Admin", Role: store.UserRoleAdmin, @@ -77,7 +77,7 @@ func setupBrokerAuthzTest(t *testing.T) (srv *Server, s store.Store, alice, bob, // Create a project owned by alice project = &store.Project{ - ID: "project-broker-test", + ID: tid("project-broker-test"), Name: "Broker Test Project", Slug: "broker-test-project", OwnerID: alice.ID, @@ -100,7 +100,7 @@ func setupBrokerAuthzTest(t *testing.T) (srv *Server, s store.Store, alice, bob, // Create a broker owned by alice directly in the store broker = &store.RuntimeBroker{ - ID: "broker-alice-owned", + ID: tid("broker-alice-owned"), Name: "Alice Broker", Slug: "alice-broker", Status: store.BrokerStatusOnline, @@ -361,7 +361,7 @@ func TestAgentCreate_BrokerResolution(t *testing.T) { // Create a runtime broker broker := &store.RuntimeBroker{ - ID: "broker_id_123", + ID: tid("broker_id_123"), Name: "My Laptop", Slug: "my-laptop", Status: store.BrokerStatusOnline, @@ -370,7 +370,7 @@ func TestAgentCreate_BrokerResolution(t *testing.T) { // Create a project project := &store.Project{ - ID: "project_1", + ID: tid("project_1"), Slug: "test-project", Name: "Test Project", Created: time.Now(), @@ -391,14 +391,14 @@ func TestAgentCreate_BrokerResolution(t *testing.T) { body := map[string]interface{}{ "name": "Agent ID", "projectId": project.ID, - "runtimeBrokerId": "broker_id_123", + "runtimeBrokerId": tid("broker_id_123"), } rec := doRequest(t, srv, http.MethodPost, "/api/v1/agents", body) assert.Equal(t, http.StatusCreated, rec.Code) var resp CreateAgentResponse require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp)) - assert.Equal(t, "broker_id_123", resp.Agent.RuntimeBrokerID) + assert.Equal(t, tid("broker_id_123"), resp.Agent.RuntimeBrokerID) }) t.Run("Resolve by Name", func(t *testing.T) { @@ -412,7 +412,7 @@ func TestAgentCreate_BrokerResolution(t *testing.T) { var resp CreateAgentResponse require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp)) - assert.Equal(t, "broker_id_123", resp.Agent.RuntimeBrokerID) + assert.Equal(t, tid("broker_id_123"), resp.Agent.RuntimeBrokerID) }) t.Run("Resolve by Slug", func(t *testing.T) { @@ -426,7 +426,7 @@ func TestAgentCreate_BrokerResolution(t *testing.T) { var resp CreateAgentResponse require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp)) - assert.Equal(t, "broker_id_123", resp.Agent.RuntimeBrokerID) + assert.Equal(t, tid("broker_id_123"), resp.Agent.RuntimeBrokerID) }) t.Run("Invalid broker", func(t *testing.T) { diff --git a/pkg/hub/handlers_envsecret_authz_test.go b/pkg/hub/handlers_envsecret_authz_test.go index f4f6390f3..a6543d25e 100644 --- a/pkg/hub/handlers_envsecret_authz_test.go +++ b/pkg/hub/handlers_envsecret_authz_test.go @@ -85,7 +85,7 @@ func TestEnvVar_UserScope_MemberAccess(t *testing.T) { ctx := context.Background() member := &store.User{ - ID: "member-env-1", + ID: tid("member-env-1"), Email: "member-env@example.com", DisplayName: "Test Member", Role: store.UserRoleMember, @@ -107,7 +107,7 @@ func TestEnvVar_UserScope_MemberAccess(t *testing.T) { t.Fatalf("failed to decode response: %v", err) } - if resp.ScopeID != "member-env-1" { + if resp.ScopeID != tid("member-env-1") { t.Errorf("expected scopeId 'member-env-1', got %q", resp.ScopeID) } } @@ -177,11 +177,11 @@ func TestEnvVar_UserScope_MemberIsolation(t *testing.T) { ctx := context.Background() userA := &store.User{ - ID: "user-iso-a", Email: "a@example.com", DisplayName: "User A", + ID: tid("user-iso-a"), Email: "a@example.com", DisplayName: "User A", Role: store.UserRoleMember, Status: "active", Created: time.Now(), } userB := &store.User{ - ID: "user-iso-b", Email: "b@example.com", DisplayName: "User B", + ID: tid("user-iso-b"), Email: "b@example.com", DisplayName: "User B", Role: store.UserRoleMember, Status: "active", Created: time.Now(), } if err := s.CreateUser(ctx, userA); err != nil { @@ -230,7 +230,7 @@ func TestEnvVar_ProjectScope_OwnerAccess(t *testing.T) { ctx := context.Background() owner := &store.User{ - ID: "project-owner-1", Email: "owner@example.com", DisplayName: "Owner", + ID: tid("project-owner-1"), Email: "owner@example.com", DisplayName: "Owner", Role: store.UserRoleMember, Status: "active", Created: time.Now(), } if err := s.CreateUser(ctx, owner); err != nil { @@ -238,10 +238,10 @@ func TestEnvVar_ProjectScope_OwnerAccess(t *testing.T) { } project := &store.Project{ - ID: "project_env_owner", + ID: tid("project_env_owner"), Name: "Owner Test Project", Slug: "owner-test-project", - OwnerID: "project-owner-1", + OwnerID: tid("project-owner-1"), Created: time.Now(), Updated: time.Now(), } @@ -268,7 +268,7 @@ func TestEnvVar_ProjectScope_NonOwnerDenied(t *testing.T) { ctx := context.Background() nonOwner := &store.User{ - ID: "non-owner-1", Email: "nonowner@example.com", DisplayName: "Non-Owner", + ID: tid("non-owner-1"), Email: "nonowner@example.com", DisplayName: "Non-Owner", Role: store.UserRoleMember, Status: "active", Created: time.Now(), } if err := s.CreateUser(ctx, nonOwner); err != nil { @@ -276,7 +276,7 @@ func TestEnvVar_ProjectScope_NonOwnerDenied(t *testing.T) { } project := &store.Project{ - ID: "project_env_notown", + ID: tid("project_env_notown"), Name: "Not Owned Project", Slug: "not-owned-project", OwnerID: "someone-else", @@ -299,7 +299,7 @@ func TestEnvVar_ProjectScope_AdminAccess(t *testing.T) { ctx := context.Background() project := &store.Project{ - ID: "project_env_admin", + ID: tid("project_env_admin"), Name: "Admin Test Project", Slug: "admin-test-project", OwnerID: "someone-else", @@ -322,7 +322,7 @@ func TestEnvVar_ProjectScope_AgentReadOwnProject(t *testing.T) { ctx := context.Background() project := &store.Project{ - ID: "project_agent_env", + ID: tid("project_agent_env"), Name: "Agent Project", Slug: "agent-project", Created: time.Now(), @@ -333,7 +333,7 @@ func TestEnvVar_ProjectScope_AgentReadOwnProject(t *testing.T) { } agent := &store.Agent{ - ID: "agent_env_test", + ID: tid("agent_env_test"), Slug: "env-test-agent", Name: "Env Test Agent", ProjectID: project.ID, @@ -363,11 +363,11 @@ func TestEnvVar_ProjectScope_AgentOtherProjectDenied(t *testing.T) { ctx := context.Background() project1 := &store.Project{ - ID: "project_agent_own", Name: "Agent's Project", Slug: "agents-project", + ID: tid("project_agent_own"), Name: "Agent's Project", Slug: "agents-project", Created: time.Now(), Updated: time.Now(), } project2 := &store.Project{ - ID: "project_agent_other", Name: "Other Project", Slug: "other-project", + ID: tid("project_agent_other"), Name: "Other Project", Slug: "other-project", Created: time.Now(), Updated: time.Now(), } if err := s.CreateProject(ctx, project1); err != nil { @@ -378,7 +378,7 @@ func TestEnvVar_ProjectScope_AgentOtherProjectDenied(t *testing.T) { } agent := &store.Agent{ - ID: "agent_other_project", Slug: "other-project-agent", Name: "Other Project Agent", + ID: tid("agent_other_project"), Slug: "other-project-agent", Name: "Other Project Agent", ProjectID: project1.ID, Phase: string(state.PhaseRunning), StateVersion: 1, Created: time.Now(), Updated: time.Now(), } @@ -403,7 +403,7 @@ func TestEnvVar_ProjectScope_AgentWriteDenied(t *testing.T) { ctx := context.Background() project := &store.Project{ - ID: "project_agent_nowrite", Name: "Agent No Write Project", Slug: "agent-nowrite-project", + ID: tid("project_agent_nowrite"), Name: "Agent No Write Project", Slug: "agent-nowrite-project", Created: time.Now(), Updated: time.Now(), } if err := s.CreateProject(ctx, project); err != nil { @@ -411,7 +411,7 @@ func TestEnvVar_ProjectScope_AgentWriteDenied(t *testing.T) { } agent := &store.Agent{ - ID: "agent_nowrite", Slug: "nowrite-agent", Name: "No Write Agent", + ID: tid("agent_nowrite"), Slug: "nowrite-agent", Name: "No Write Agent", ProjectID: project.ID, Phase: string(state.PhaseRunning), StateVersion: 1, Created: time.Now(), Updated: time.Now(), } @@ -441,7 +441,7 @@ func TestEnvVar_BrokerScope_AdminAccess(t *testing.T) { ctx := context.Background() broker := &store.RuntimeBroker{ - ID: "broker_env_admin", Name: "Env Admin Broker", Slug: "env-admin-broker", + ID: tid("broker_env_admin"), Name: "Env Admin Broker", Slug: "env-admin-broker", Status: store.BrokerStatusOnline, Created: time.Now(), Updated: time.Now(), } if err := s.CreateRuntimeBroker(ctx, broker); err != nil { @@ -486,7 +486,7 @@ func TestSecret_UserScope_AdminDoesNotSeeOtherUserSecrets(t *testing.T) { // Create a member user and store a secret scoped to them. member := &store.User{ - ID: "member-other-1", Email: "other@example.com", DisplayName: "Other User", + ID: tid("member-other-1"), Email: "other@example.com", DisplayName: "Other User", Role: store.UserRoleMember, Status: "active", Created: time.Now(), } if err := s.CreateUser(ctx, member); err != nil { @@ -495,7 +495,7 @@ func TestSecret_UserScope_AdminDoesNotSeeOtherUserSecrets(t *testing.T) { // Store a secret as the member. if err := s.CreateSecret(ctx, &store.Secret{ - ID: "sec-other-1", Key: "MEMBER_KEY", Scope: store.ScopeUser, + ID: tid("sec-other-1"), Key: "MEMBER_KEY", Scope: store.ScopeUser, ScopeID: member.ID, SecretType: store.SecretTypeEnvironment, EncryptedValue: "val", Version: 1, Created: time.Now(), Updated: time.Now(), }); err != nil { @@ -526,7 +526,7 @@ func TestSecret_UserScope_MemberAccess(t *testing.T) { ctx := context.Background() member := &store.User{ - ID: "member-sec-1", Email: "member-sec@example.com", DisplayName: "Test Member", + ID: tid("member-sec-1"), Email: "member-sec@example.com", DisplayName: "Test Member", Role: store.UserRoleMember, Status: "active", Created: time.Now(), } if err := s.CreateUser(ctx, member); err != nil { @@ -543,7 +543,7 @@ func TestSecret_UserScope_MemberAccess(t *testing.T) { t.Fatalf("failed to decode response: %v", err) } - if resp.ScopeID != "member-sec-1" { + if resp.ScopeID != tid("member-sec-1") { t.Errorf("expected scopeId 'member-sec-1', got %q", resp.ScopeID) } } @@ -590,7 +590,7 @@ func TestSecret_ProjectScope_OwnerAccess(t *testing.T) { ctx := context.Background() owner := &store.User{ - ID: "project-sec-owner", Email: "secowner@example.com", DisplayName: "Owner", + ID: tid("project-sec-owner"), Email: "secowner@example.com", DisplayName: "Owner", Role: store.UserRoleMember, Status: "active", Created: time.Now(), } if err := s.CreateUser(ctx, owner); err != nil { @@ -598,8 +598,8 @@ func TestSecret_ProjectScope_OwnerAccess(t *testing.T) { } project := &store.Project{ - ID: "project_sec_owner", Name: "Secret Owner Project", Slug: "secret-owner-project", - OwnerID: "project-sec-owner", Created: time.Now(), Updated: time.Now(), + ID: tid("project_sec_owner"), Name: "Secret Owner Project", Slug: "secret-owner-project", + OwnerID: tid("project-sec-owner"), Created: time.Now(), Updated: time.Now(), } if err := s.CreateProject(ctx, project); err != nil { t.Fatalf("failed to create project: %v", err) @@ -617,7 +617,7 @@ func TestSecret_ProjectScope_NonOwnerDenied(t *testing.T) { ctx := context.Background() nonOwner := &store.User{ - ID: "non-sec-owner", Email: "nonsecowner@example.com", DisplayName: "Non-Owner", + ID: tid("non-sec-owner"), Email: "nonsecowner@example.com", DisplayName: "Non-Owner", Role: store.UserRoleMember, Status: "active", Created: time.Now(), } if err := s.CreateUser(ctx, nonOwner); err != nil { @@ -625,7 +625,7 @@ func TestSecret_ProjectScope_NonOwnerDenied(t *testing.T) { } project := &store.Project{ - ID: "project_sec_notown", Name: "Not Owned Secret Project", Slug: "not-owned-secret-project", + ID: tid("project_sec_notown"), Name: "Not Owned Secret Project", Slug: "not-owned-secret-project", OwnerID: "someone-else", Created: time.Now(), Updated: time.Now(), } if err := s.CreateProject(ctx, project); err != nil { @@ -644,7 +644,7 @@ func TestSecret_ProjectScope_AgentReadOwnProject(t *testing.T) { ctx := context.Background() project := &store.Project{ - ID: "project_agent_sec", Name: "Agent Secret Project", Slug: "agent-secret-project", + ID: tid("project_agent_sec"), Name: "Agent Secret Project", Slug: "agent-secret-project", Created: time.Now(), Updated: time.Now(), } if err := s.CreateProject(ctx, project); err != nil { @@ -652,7 +652,7 @@ func TestSecret_ProjectScope_AgentReadOwnProject(t *testing.T) { } agent := &store.Agent{ - ID: "agent_sec_test", Slug: "sec-test-agent", Name: "Secret Test Agent", + ID: tid("agent_sec_test"), Slug: "sec-test-agent", Name: "Secret Test Agent", ProjectID: project.ID, Phase: string(state.PhaseRunning), StateVersion: 1, Created: time.Now(), Updated: time.Now(), } @@ -677,7 +677,7 @@ func TestSecret_ProjectScope_AgentWriteDenied(t *testing.T) { ctx := context.Background() project := &store.Project{ - ID: "project_agent_sec_nowrite", Name: "Agent Secret No Write", Slug: "agent-sec-nowrite-project", + ID: tid("project_agent_sec_nowrite"), Name: "Agent Secret No Write", Slug: "agent-sec-nowrite-project", Created: time.Now(), Updated: time.Now(), } if err := s.CreateProject(ctx, project); err != nil { @@ -685,7 +685,7 @@ func TestSecret_ProjectScope_AgentWriteDenied(t *testing.T) { } agent := &store.Agent{ - ID: "agent_sec_nowrite", Slug: "sec-nowrite-agent", Name: "Secret No Write Agent", + ID: tid("agent_sec_nowrite"), Slug: "sec-nowrite-agent", Name: "Secret No Write Agent", ProjectID: project.ID, Phase: string(state.PhaseRunning), StateVersion: 1, Created: time.Now(), Updated: time.Now(), } @@ -714,7 +714,7 @@ func TestEnvVar_HubEndpoint_ProjectScope_Authorized(t *testing.T) { ctx := context.Background() project := &store.Project{ - ID: "project_hub_env", Name: "Hub Env Project", Slug: "hub-env-project", + ID: tid("project_hub_env"), Name: "Hub Env Project", Slug: "hub-env-project", Created: time.Now(), Updated: time.Now(), } if err := s.CreateProject(ctx, project); err != nil { @@ -733,7 +733,7 @@ func TestEnvVar_HubEndpoint_ProjectScope_NonOwnerDenied(t *testing.T) { ctx := context.Background() member := &store.User{ - ID: "hub-env-member", Email: "hubenvmember@example.com", DisplayName: "Member", + ID: tid("hub-env-member"), Email: "hubenvmember@example.com", DisplayName: "Member", Role: store.UserRoleMember, Status: "active", Created: time.Now(), } if err := s.CreateUser(ctx, member); err != nil { @@ -741,7 +741,7 @@ func TestEnvVar_HubEndpoint_ProjectScope_NonOwnerDenied(t *testing.T) { } project := &store.Project{ - ID: "project_hub_env_deny", Name: "Hub Env Deny Project", Slug: "hub-env-deny-project", + ID: tid("project_hub_env_deny"), Name: "Hub Env Deny Project", Slug: "hub-env-deny-project", OwnerID: "someone-else", Created: time.Now(), Updated: time.Now(), } if err := s.CreateProject(ctx, project); err != nil { @@ -801,7 +801,7 @@ func TestEnvVar_UnifiedList_MergesSecrets(t *testing.T) { // Create a secret directly in the store with type "environment" if err := s.CreateSecret(ctx, &store.Secret{ - ID: "sec-env-1", + ID: tid("sec-env-1"), Key: "SECRET_ENV_VAR", EncryptedValue: "encrypted-val", SecretType: store.SecretTypeEnvironment, @@ -864,7 +864,7 @@ func TestEnvVar_UnifiedList_Deduplication(t *testing.T) { // Also create a secret with the same key if err := s.CreateSecret(ctx, &store.Secret{ - ID: "sec-dup-1", + ID: tid("sec-dup-1"), Key: "DUPED_KEY", EncryptedValue: "secret-value", SecretType: store.SecretTypeEnvironment, @@ -905,7 +905,7 @@ func TestEnvVar_FallbackGet_FromSecretBackend(t *testing.T) { // Create a secret (no plain env var) if err := s.CreateSecret(ctx, &store.Secret{ - ID: "sec-get-1", + ID: tid("sec-get-1"), Key: "ONLY_SECRET", EncryptedValue: "secret-val", SecretType: store.SecretTypeEnvironment, @@ -946,7 +946,7 @@ func TestEnvVar_FallbackDelete_FromSecretBackend(t *testing.T) { // Create a secret (no plain env var) if err := s.CreateSecret(ctx, &store.Secret{ - ID: "sec-del-1", + ID: tid("sec-del-1"), Key: "DEL_SECRET", EncryptedValue: "secret-val", SecretType: store.SecretTypeEnvironment, @@ -1010,7 +1010,7 @@ func TestEnvVar_NonEnvironmentSecrets_NotMerged(t *testing.T) { // Create a secret with type "variable" (not "environment") if err := s.CreateSecret(ctx, &store.Secret{ - ID: "sec-var-1", + ID: tid("sec-var-1"), Key: "VARIABLE_SECRET", EncryptedValue: "var-val", SecretType: store.SecretTypeVariable, @@ -1043,7 +1043,7 @@ func TestEnvVar_ProjectScope_SecretPromotion_Succeeds(t *testing.T) { ctx := context.Background() project := &store.Project{ - ID: "project_promo_test", Name: "Promo Project", Slug: "promo-project", + ID: tid("project_promo_test"), Name: "Promo Project", Slug: "promo-project", OwnerID: DevUserID, Created: time.Now(), Updated: time.Now(), } if err := s.CreateProject(ctx, project); err != nil { @@ -1063,7 +1063,7 @@ func TestEnvVar_ProjectScope_UnifiedList(t *testing.T) { ctx := context.Background() project := &store.Project{ - ID: "project_unified_list", Name: "Unified Project", Slug: "unified-project", + ID: tid("project_unified_list"), Name: "Unified Project", Slug: "unified-project", OwnerID: DevUserID, Created: time.Now(), Updated: time.Now(), } if err := s.CreateProject(ctx, project); err != nil { @@ -1079,7 +1079,7 @@ func TestEnvVar_ProjectScope_UnifiedList(t *testing.T) { // Create an environment secret in the project scope directly if err := s.CreateSecret(ctx, &store.Secret{ - ID: "sec-project-env-1", + ID: tid("sec-project-env-1"), Key: "GROVE_SECRET_VAR", EncryptedValue: "project-secret-val", SecretType: store.SecretTypeEnvironment, @@ -1123,7 +1123,7 @@ func TestEnvVar_ProjectScope_FallbackGet(t *testing.T) { ctx := context.Background() project := &store.Project{ - ID: "project_fallback_get", Name: "Fallback Get Project", Slug: "fallback-get-project", + ID: tid("project_fallback_get"), Name: "Fallback Get Project", Slug: "fallback-get-project", OwnerID: DevUserID, Created: time.Now(), Updated: time.Now(), } if err := s.CreateProject(ctx, project); err != nil { @@ -1131,7 +1131,7 @@ func TestEnvVar_ProjectScope_FallbackGet(t *testing.T) { } if err := s.CreateSecret(ctx, &store.Secret{ - ID: "sec-project-fb-1", + ID: tid("sec-project-fb-1"), Key: "GROVE_ONLY_SEC", EncryptedValue: "secret-val", SecretType: store.SecretTypeEnvironment, @@ -1160,7 +1160,7 @@ func TestEnvVar_ProjectScope_FallbackDelete(t *testing.T) { ctx := context.Background() project := &store.Project{ - ID: "project_fallback_del", Name: "Fallback Del Project", Slug: "fallback-del-project", + ID: tid("project_fallback_del"), Name: "Fallback Del Project", Slug: "fallback-del-project", OwnerID: DevUserID, Created: time.Now(), Updated: time.Now(), } if err := s.CreateProject(ctx, project); err != nil { @@ -1168,7 +1168,7 @@ func TestEnvVar_ProjectScope_FallbackDelete(t *testing.T) { } if err := s.CreateSecret(ctx, &store.Secret{ - ID: "sec-project-del-1", + ID: tid("sec-project-del-1"), Key: "GROVE_DEL_SEC", EncryptedValue: "secret-val", SecretType: store.SecretTypeEnvironment, @@ -1260,7 +1260,7 @@ func TestEnvVar_HubScope_MemberReadForbidden(t *testing.T) { ctx := context.Background() member := &store.User{ - ID: "hub-env-member-1", Email: "hub-member@example.com", DisplayName: "Hub Member", + ID: tid("hub-env-member-1"), Email: "hub-member@example.com", DisplayName: "Hub Member", Role: store.UserRoleMember, Status: "active", Created: time.Now(), } if err := s.CreateUser(ctx, member); err != nil { @@ -1286,7 +1286,7 @@ func TestEnvVar_HubScope_MemberWriteForbidden(t *testing.T) { ctx := context.Background() member := &store.User{ - ID: "hub-env-member-2", Email: "hub-member2@example.com", DisplayName: "Hub Member 2", + ID: tid("hub-env-member-2"), Email: "hub-member2@example.com", DisplayName: "Hub Member 2", Role: store.UserRoleMember, Status: "active", Created: time.Now(), } if err := s.CreateUser(ctx, member); err != nil { @@ -1306,7 +1306,7 @@ func TestEnvVar_HubScope_MemberDeleteForbidden(t *testing.T) { ctx := context.Background() member := &store.User{ - ID: "hub-env-member-3", Email: "hub-member3@example.com", DisplayName: "Hub Member 3", + ID: tid("hub-env-member-3"), Email: "hub-member3@example.com", DisplayName: "Hub Member 3", Role: store.UserRoleMember, Status: "active", Created: time.Now(), } if err := s.CreateUser(ctx, member); err != nil { @@ -1332,7 +1332,7 @@ func TestEnvVar_HubScope_AgentCanRead(t *testing.T) { ctx := context.Background() project := &store.Project{ - ID: "project_hub_agent", Name: "Hub Agent Project", Slug: "hub-agent-project", + ID: tid("project_hub_agent"), Name: "Hub Agent Project", Slug: "hub-agent-project", Created: time.Now(), Updated: time.Now(), } if err := s.CreateProject(ctx, project); err != nil { @@ -1340,7 +1340,7 @@ func TestEnvVar_HubScope_AgentCanRead(t *testing.T) { } agent := &store.Agent{ - ID: "agent_hub_read", Slug: "hub-read-agent", Name: "Hub Read Agent", + ID: tid("agent_hub_read"), Slug: "hub-read-agent", Name: "Hub Read Agent", ProjectID: project.ID, Phase: string(state.PhaseRunning), StateVersion: 1, Created: time.Now(), Updated: time.Now(), } @@ -1405,7 +1405,7 @@ func TestSecret_HubScope_MemberReadForbidden(t *testing.T) { ctx := context.Background() member := &store.User{ - ID: "hub-sec-member-1", Email: "hubsecmember@example.com", DisplayName: "Hub Sec Member", + ID: tid("hub-sec-member-1"), Email: "hubsecmember@example.com", DisplayName: "Hub Sec Member", Role: store.UserRoleMember, Status: "active", Created: time.Now(), } if err := s.CreateUser(ctx, member); err != nil { @@ -1432,7 +1432,7 @@ func TestSecret_HubScope_MemberWriteForbidden(t *testing.T) { ctx := context.Background() member := &store.User{ - ID: "hub-sec-member-2", Email: "hubsecmember2@example.com", DisplayName: "Hub Sec Member 2", + ID: tid("hub-sec-member-2"), Email: "hubsecmember2@example.com", DisplayName: "Hub Sec Member 2", Role: store.UserRoleMember, Status: "active", Created: time.Now(), } if err := s.CreateUser(ctx, member); err != nil { diff --git a/pkg/hub/handlers_gcp_identity_test.go b/pkg/hub/handlers_gcp_identity_test.go index 4e7a90e15..ccbdd8966 100644 --- a/pkg/hub/handlers_gcp_identity_test.go +++ b/pkg/hub/handlers_gcp_identity_test.go @@ -562,7 +562,7 @@ func setupGCPAuthzTest(t *testing.T) (*Server, store.Store, *store.User, *store. ctx := context.Background() owner := &store.User{ - ID: "user-gcp-owner", + ID: tid("user-gcp-owner"), Email: "gcp-owner@test.com", DisplayName: "GCP Owner", Role: store.UserRoleMember, @@ -570,7 +570,7 @@ func setupGCPAuthzTest(t *testing.T) (*Server, store.Store, *store.User, *store. Created: time.Now(), } member := &store.User{ - ID: "user-gcp-member", + ID: tid("user-gcp-member"), Email: "gcp-member@test.com", DisplayName: "GCP Member", Role: store.UserRoleMember, @@ -578,7 +578,7 @@ func setupGCPAuthzTest(t *testing.T) (*Server, store.Store, *store.User, *store. Created: time.Now(), } outsider := &store.User{ - ID: "user-gcp-outsider", + ID: tid("user-gcp-outsider"), Email: "gcp-outsider@test.com", DisplayName: "GCP Outsider", Role: store.UserRoleMember, @@ -591,7 +591,7 @@ func setupGCPAuthzTest(t *testing.T) (*Server, store.Store, *store.User, *store. } project := &store.Project{ - ID: "project-gcp-authz", + ID: tid("project-gcp-authz"), Name: "GCP Authz Project", Slug: "gcp-authz-project", OwnerID: owner.ID, @@ -622,7 +622,7 @@ func TestGCPSA_Create_ProjectOwnerAllowed(t *testing.T) { rec := doRequestAsUser(t, srv, owner, http.MethodPost, fmt.Sprintf("/api/v1/projects/%s/gcp-service-accounts", project.ID), - map[string]string{"email": "sa@proj.iam.gserviceaccount.com", "projectId": "proj"}) + map[string]string{"email": "sa@proj.iam.gserviceaccount.com", "projectId": tid("proj")}) require.Equal(t, http.StatusCreated, rec.Code, "project owner should be able to create SA; got: %s", rec.Body.String()) } @@ -632,7 +632,7 @@ func TestGCPSA_Create_MemberDenied(t *testing.T) { rec := doRequestAsUser(t, srv, member, http.MethodPost, fmt.Sprintf("/api/v1/projects/%s/gcp-service-accounts", project.ID), - map[string]string{"email": "sa@proj.iam.gserviceaccount.com", "projectId": "proj"}) + map[string]string{"email": "sa@proj.iam.gserviceaccount.com", "projectId": tid("proj")}) require.Equal(t, http.StatusForbidden, rec.Code, "project member should not be able to create SA; got: %s", rec.Body.String()) } @@ -642,7 +642,7 @@ func TestGCPSA_Create_OutsiderDenied(t *testing.T) { rec := doRequestAsUser(t, srv, outsider, http.MethodPost, fmt.Sprintf("/api/v1/projects/%s/gcp-service-accounts", project.ID), - map[string]string{"email": "sa@proj.iam.gserviceaccount.com", "projectId": "proj"}) + map[string]string{"email": "sa@proj.iam.gserviceaccount.com", "projectId": tid("proj")}) require.Equal(t, http.StatusForbidden, rec.Code, "outsider should not be able to create SA; got: %s", rec.Body.String()) } @@ -652,11 +652,11 @@ func TestGCPSA_Delete_ProjectOwnerAllowed(t *testing.T) { ctx := context.Background() sa := &store.GCPServiceAccount{ - ID: "sa-del-owner", + ID: tid("sa-del-owner"), Scope: store.ScopeProject, ScopeID: project.ID, Email: "del-owner@proj.iam.gserviceaccount.com", - ProjectID: "proj", + ProjectID: tid("proj"), CreatedBy: owner.ID, CreatedAt: time.Now(), } @@ -673,11 +673,11 @@ func TestGCPSA_Delete_MemberDenied(t *testing.T) { ctx := context.Background() sa := &store.GCPServiceAccount{ - ID: "sa-del-member", + ID: tid("sa-del-member"), Scope: store.ScopeProject, ScopeID: project.ID, Email: "del-member@proj.iam.gserviceaccount.com", - ProjectID: "proj", + ProjectID: tid("proj"), CreatedBy: owner.ID, CreatedAt: time.Now(), } @@ -724,11 +724,11 @@ func TestGCPSA_Verify_ProjectOwnerAllowed(t *testing.T) { ctx := context.Background() sa := &store.GCPServiceAccount{ - ID: "sa-verify-owner", + ID: tid("sa-verify-owner"), Scope: store.ScopeProject, ScopeID: project.ID, Email: "verify@proj.iam.gserviceaccount.com", - ProjectID: "proj", + ProjectID: tid("proj"), CreatedBy: owner.ID, CreatedAt: time.Now(), } @@ -746,11 +746,11 @@ func TestGCPSA_Verify_MemberDenied(t *testing.T) { ctx := context.Background() sa := &store.GCPServiceAccount{ - ID: "sa-verify-member", + ID: tid("sa-verify-member"), Scope: store.ScopeProject, ScopeID: project.ID, Email: "verify-m@proj.iam.gserviceaccount.com", - ProjectID: "proj", + ProjectID: tid("proj"), CreatedBy: owner.ID, CreatedAt: time.Now(), } diff --git a/pkg/hub/handlers_github_app_test.go b/pkg/hub/handlers_github_app_test.go index 9041aa261..fe76df562 100644 --- a/pkg/hub/handlers_github_app_test.go +++ b/pkg/hub/handlers_github_app_test.go @@ -188,7 +188,7 @@ func TestHandleProjectGitHubInstallation(t *testing.T) { // Create a project project := &store.Project{ - ID: "project_gh_test", + ID: tid("project_gh_test"), Slug: "gh-test-project", Name: "GH Test Project", GitRemote: "https://github.com/acme/widgets", @@ -213,7 +213,7 @@ func TestHandleProjectGitHubInstallation(t *testing.T) { } // Associate project with installation - rec := doRequest(t, srv, http.MethodPut, "/api/v1/projects/project_gh_test/github-installation", map[string]interface{}{ + rec := doRequest(t, srv, http.MethodPut, fmt.Sprintf("/api/v1/projects/%s/github-installation", tid("project_gh_test")), map[string]interface{}{ "installation_id": 54321, }) if rec.Code != http.StatusOK { @@ -221,7 +221,7 @@ func TestHandleProjectGitHubInstallation(t *testing.T) { } // Verify project has installation ID - updatedProject, err := s.GetProject(ctx, "project_gh_test") + updatedProject, err := s.GetProject(ctx, tid("project_gh_test")) if err != nil { t.Fatalf("failed to get project: %v", err) } @@ -233,19 +233,19 @@ func TestHandleProjectGitHubInstallation(t *testing.T) { } // Get status - rec = doRequest(t, srv, http.MethodGet, "/api/v1/projects/project_gh_test/github-status", nil) + rec = doRequest(t, srv, http.MethodGet, fmt.Sprintf("/api/v1/projects/%s/github-status", tid("project_gh_test")), nil) if rec.Code != http.StatusOK { t.Fatalf("expected 200, got %d: %s", rec.Code, rec.Body.String()) } // Remove association - rec = doRequest(t, srv, http.MethodDelete, "/api/v1/projects/project_gh_test/github-installation", nil) + rec = doRequest(t, srv, http.MethodDelete, fmt.Sprintf("/api/v1/projects/%s/github-installation", tid("project_gh_test")), nil) if rec.Code != http.StatusNoContent { t.Fatalf("expected 204, got %d: %s", rec.Code, rec.Body.String()) } // Verify removed - clearedProject, err := s.GetProject(ctx, "project_gh_test") + clearedProject, err := s.GetProject(ctx, tid("project_gh_test")) if err != nil { t.Fatalf("failed to get project: %v", err) } @@ -259,7 +259,7 @@ func TestHandleProjectGitHubStatus_PostNoInstallation(t *testing.T) { ctx := context.Background() project := &store.Project{ - ID: "project_gh_status_check", Slug: "gh-status-check", Name: "GH Status Check", + ID: tid("project_gh_status_check"), Slug: "gh-status-check", Name: "GH Status Check", GitRemote: "https://github.com/acme/widgets", Created: time.Now(), Updated: time.Now(), Visibility: "private", } @@ -268,7 +268,7 @@ func TestHandleProjectGitHubStatus_PostNoInstallation(t *testing.T) { } // POST without installation should return 400 - rec := doRequest(t, srv, http.MethodPost, "/api/v1/projects/project_gh_status_check/github-status", nil) + rec := doRequest(t, srv, http.MethodPost, fmt.Sprintf("/api/v1/projects/%s/github-status", tid("project_gh_status_check")), nil) if rec.Code != http.StatusBadRequest { t.Errorf("expected 400 for project without installation, got %d: %s", rec.Code, rec.Body.String()) } @@ -292,7 +292,7 @@ func TestHandleProjectGitHubStatus_PostWithInstallation(t *testing.T) { } project := &store.Project{ - ID: "project_gh_status_check2", Slug: "gh-status-check2", Name: "GH Status Check 2", + ID: tid("project_gh_status_check2"), Slug: "gh-status-check2", Name: "GH Status Check 2", GitRemote: "https://github.com/acme/widgets", Created: time.Now(), Updated: time.Now(), Visibility: "private", } @@ -308,7 +308,7 @@ func TestHandleProjectGitHubStatus_PostWithInstallation(t *testing.T) { // POST should succeed (though minting will fail because no GitHub App // is configured — the endpoint should still return 200 with the error // captured in the response and project status updated to error) - rec := doRequest(t, srv, http.MethodPost, "/api/v1/projects/project_gh_status_check2/github-status", nil) + rec := doRequest(t, srv, http.MethodPost, fmt.Sprintf("/api/v1/projects/%s/github-status", tid("project_gh_status_check2")), nil) if rec.Code != http.StatusOK { t.Fatalf("expected 200, got %d: %s", rec.Code, rec.Body.String()) } @@ -338,14 +338,14 @@ func TestHandleProjectGitHubInstallation_NotFoundInstallation(t *testing.T) { ctx := context.Background() project := &store.Project{ - ID: "project_gh_notfound", Slug: "gh-nf", Name: "GH NF", + ID: tid("project_gh_notfound"), Slug: "gh-nf", Name: "GH NF", Created: time.Now(), Updated: time.Now(), Visibility: "private", } if err := s.CreateProject(ctx, project); err != nil { t.Fatalf("failed to create project: %v", err) } - rec := doRequest(t, srv, http.MethodPut, "/api/v1/projects/project_gh_notfound/github-installation", map[string]interface{}{ + rec := doRequest(t, srv, http.MethodPut, fmt.Sprintf("/api/v1/projects/%s/github-installation", tid("project_gh_notfound")), map[string]interface{}{ "installation_id": 99999, }) if rec.Code != http.StatusNotFound { @@ -362,7 +362,7 @@ func TestHandleProjectGitHubPermissions(t *testing.T) { ctx := context.Background() project := &store.Project{ - ID: "project_gh_perms", Slug: "gh-perms", Name: "GH Perms", + ID: tid("project_gh_perms"), Slug: "gh-perms", Name: "GH Perms", Created: time.Now(), Updated: time.Now(), Visibility: "private", } if err := s.CreateProject(ctx, project); err != nil { @@ -370,7 +370,7 @@ func TestHandleProjectGitHubPermissions(t *testing.T) { } // Get defaults - rec := doRequest(t, srv, http.MethodGet, "/api/v1/projects/project_gh_perms/github-permissions", nil) + rec := doRequest(t, srv, http.MethodGet, fmt.Sprintf("/api/v1/projects/%s/github-permissions", tid("project_gh_perms")), nil) if rec.Code != http.StatusOK { t.Fatalf("expected 200, got %d", rec.Code) } @@ -384,7 +384,7 @@ func TestHandleProjectGitHubPermissions(t *testing.T) { } // Set custom permissions - rec = doRequest(t, srv, http.MethodPut, "/api/v1/projects/project_gh_perms/github-permissions", map[string]interface{}{ + rec = doRequest(t, srv, http.MethodPut, fmt.Sprintf("/api/v1/projects/%s/github-permissions", tid("project_gh_perms")), map[string]interface{}{ "contents": "read", "metadata": "read", }) @@ -393,7 +393,7 @@ func TestHandleProjectGitHubPermissions(t *testing.T) { } // Verify stored - updatedProject, err := s.GetProject(ctx, "project_gh_perms") + updatedProject, err := s.GetProject(ctx, tid("project_gh_perms")) if err != nil { t.Fatalf("failed to get project: %v", err) } @@ -402,12 +402,12 @@ func TestHandleProjectGitHubPermissions(t *testing.T) { } // Reset to defaults - rec = doRequest(t, srv, http.MethodDelete, "/api/v1/projects/project_gh_perms/github-permissions", nil) + rec = doRequest(t, srv, http.MethodDelete, fmt.Sprintf("/api/v1/projects/%s/github-permissions", tid("project_gh_perms")), nil) if rec.Code != http.StatusNoContent { t.Fatalf("expected 204, got %d", rec.Code) } - clearedProject, err := s.GetProject(ctx, "project_gh_perms") + clearedProject, err := s.GetProject(ctx, tid("project_gh_perms")) if err != nil { t.Fatalf("failed to get project: %v", err) } @@ -449,7 +449,7 @@ func TestHandleAgentGitHubTokenRefresh_NoAuth(t *testing.T) { // Create a project and agent project := &store.Project{ - ID: "project_gh_refresh", + ID: tid("project_gh_refresh"), Name: "Test Project", Slug: "test-project", } @@ -458,7 +458,7 @@ func TestHandleAgentGitHubTokenRefresh_NoAuth(t *testing.T) { } agent := &store.Agent{ - ID: "agent_gh_refresh", + ID: tid("agent_gh_refresh"), Name: "test-agent", Slug: "test-agent", ProjectID: project.ID, @@ -481,7 +481,7 @@ func TestHandleAgentGitHubTokenRefresh_DevAuth(t *testing.T) { // Create a project and agent project := &store.Project{ - ID: "project_gh_refresh2", + ID: tid("project_gh_refresh2"), Name: "Test Project 2", Slug: "test-project-2", } @@ -490,7 +490,7 @@ func TestHandleAgentGitHubTokenRefresh_DevAuth(t *testing.T) { } agent := &store.Agent{ - ID: "agent_gh_refresh2", + ID: tid("agent_gh_refresh2"), Name: "test-agent-2", Slug: "test-agent-2", ProjectID: project.ID, @@ -513,7 +513,7 @@ func TestHandleAgentGitHubTokenRefresh_SelfAccess(t *testing.T) { // Create a project and agent project := &store.Project{ - ID: "project_gh_refresh3", + ID: tid("project_gh_refresh3"), Name: "Test Project 3", Slug: "test-project-3", } @@ -522,7 +522,7 @@ func TestHandleAgentGitHubTokenRefresh_SelfAccess(t *testing.T) { } agent := &store.Agent{ - ID: "agent_gh_refresh3", + ID: tid("agent_gh_refresh3"), Name: "test-agent-3", Slug: "test-agent-3", ProjectID: project.ID, @@ -537,7 +537,7 @@ func TestHandleAgentGitHubTokenRefresh_SelfAccess(t *testing.T) { // Generate an agent token with refresh scope agentToken, err := srv.agentTokenService.GenerateAgentToken( - "agent_gh_refresh3", project.ID, + tid("agent_gh_refresh3"), project.ID, []AgentTokenScope{ScopeAgentTokenRefresh}, nil) if err != nil { t.Fatalf("failed to generate agent token: %v", err) @@ -557,7 +557,7 @@ func TestHandleAgentGitHubTokenRefresh_NoInstallation(t *testing.T) { // Create a project WITHOUT a GitHub App installation project := &store.Project{ - ID: "project_gh_refresh4", + ID: tid("project_gh_refresh4"), Name: "Test Project 4", Slug: "test-project-4", } @@ -566,7 +566,7 @@ func TestHandleAgentGitHubTokenRefresh_NoInstallation(t *testing.T) { } agent := &store.Agent{ - ID: "agent_gh_refresh4", + ID: tid("agent_gh_refresh4"), Name: "test-agent-4", Slug: "test-agent-4", ProjectID: project.ID, @@ -580,7 +580,7 @@ func TestHandleAgentGitHubTokenRefresh_NoInstallation(t *testing.T) { } agentToken, err := srv.agentTokenService.GenerateAgentToken( - "agent_gh_refresh4", project.ID, + tid("agent_gh_refresh4"), project.ID, []AgentTokenScope{ScopeAgentTokenRefresh}, nil) if err != nil { t.Fatalf("failed to generate agent token: %v", err) diff --git a/pkg/hub/handlers_github_app_webhook_test.go b/pkg/hub/handlers_github_app_webhook_test.go index 4170f82f5..262a6b670 100644 --- a/pkg/hub/handlers_github_app_webhook_test.go +++ b/pkg/hub/handlers_github_app_webhook_test.go @@ -152,7 +152,7 @@ func TestHandleGitHubWebhook_InstallationCreated(t *testing.T) { // Create a project with a matching git remote project := &store.Project{ - ID: "project-1", + ID: tid("project-1"), Name: "Test Project", Slug: "test-project", GitRemote: "https://github.com/acme/widgets.git", @@ -205,7 +205,7 @@ func TestHandleGitHubWebhook_InstallationCreated(t *testing.T) { } // Verify project was auto-associated - updatedProject, err := s.GetProject(ctx, "project-1") + updatedProject, err := s.GetProject(ctx, tid("project-1")) if err != nil { t.Fatalf("failed to get project: %v", err) } @@ -242,7 +242,7 @@ func TestHandleGitHubWebhook_InstallationDeleted(t *testing.T) { // Create a project associated with the installation project := &store.Project{ - ID: "project-1", + ID: tid("project-1"), Name: "Test Project", Slug: "test-project", GitRemote: "https://github.com/acme/widgets.git", @@ -286,7 +286,7 @@ func TestHandleGitHubWebhook_InstallationDeleted(t *testing.T) { } // Verify project was set to error state - updatedProject, _ := s.GetProject(ctx, "project-1") + updatedProject, _ := s.GetProject(ctx, tid("project-1")) if updatedProject.GitHubAppStatus == nil || updatedProject.GitHubAppStatus.State != store.GitHubAppStateError { t.Errorf("expected project error state, got %v", updatedProject.GitHubAppStatus) } @@ -310,7 +310,7 @@ func TestHandleGitHubWebhook_InstallationReposRemoved(t *testing.T) { } project := &store.Project{ - ID: "project-1", + ID: tid("project-1"), Name: "Test Project", Slug: "test-project", GitRemote: "https://github.com/acme/widgets.git", @@ -351,7 +351,7 @@ func TestHandleGitHubWebhook_InstallationReposRemoved(t *testing.T) { } // Verify project was set to error - updatedProject, _ := s.GetProject(ctx, "project-1") + updatedProject, _ := s.GetProject(ctx, tid("project-1")) if updatedProject.GitHubAppStatus == nil || updatedProject.GitHubAppStatus.State != store.GitHubAppStateError { t.Errorf("expected error state, got %v", updatedProject.GitHubAppStatus) } @@ -390,10 +390,10 @@ func TestMatchProjectsToInstallation(t *testing.T) { // Create projects with different git remotes projects := []*store.Project{ - {ID: "g1", Name: "G1", Slug: "g1", GitRemote: "https://github.com/acme/widgets.git", Created: time.Now(), Updated: time.Now()}, - {ID: "g2", Name: "G2", Slug: "g2", GitRemote: "https://github.com/acme/api.git", Created: time.Now(), Updated: time.Now()}, - {ID: "g3", Name: "G3", Slug: "g3", GitRemote: "https://github.com/other/repo.git", Created: time.Now(), Updated: time.Now()}, - {ID: "g4", Name: "G4", Slug: "g4", Created: time.Now(), Updated: time.Now()}, // No git remote + {ID: tid("g1"), Name: "G1", Slug: tid("g1"), GitRemote: "https://github.com/acme/widgets.git", Created: time.Now(), Updated: time.Now()}, + {ID: tid("g2"), Name: "G2", Slug: tid("g2"), GitRemote: "https://github.com/acme/api.git", Created: time.Now(), Updated: time.Now()}, + {ID: tid("g3"), Name: "G3", Slug: tid("g3"), GitRemote: "https://github.com/other/repo.git", Created: time.Now(), Updated: time.Now()}, + {ID: tid("g4"), Name: "G4", Slug: tid("g4"), Created: time.Now(), Updated: time.Now()}, // No git remote } for _, g := range projects { @@ -418,7 +418,7 @@ func TestMatchProjectsToInstallation(t *testing.T) { } // Verify both matching projects were associated - for _, gID := range []string{"g1", "g2"} { + for _, gID := range []string{tid("g1"), tid("g2")} { project, _ := s.GetProject(ctx, gID) if project.GitHubInstallationID == nil { t.Errorf("project %s should be associated with installation", gID) @@ -428,13 +428,13 @@ func TestMatchProjectsToInstallation(t *testing.T) { } // Verify non-matching project was NOT associated - g3, _ := s.GetProject(ctx, "g3") + g3, _ := s.GetProject(ctx, tid("g3")) if g3.GitHubInstallationID != nil { t.Error("project g3 should not be associated") } // Verify no-remote project was NOT associated - g4, _ := s.GetProject(ctx, "g4") + g4, _ := s.GetProject(ctx, tid("g4")) if g4.GitHubInstallationID != nil { t.Error("project g4 should not be associated") } @@ -456,9 +456,9 @@ func TestMatchProjectsToInstallation_SkipsAlreadyAssociated(t *testing.T) { } project := &store.Project{ - ID: "g1", + ID: tid("g1"), Name: "G1", - Slug: "g1", + Slug: tid("g1"), GitRemote: "https://github.com/acme/widgets.git", GitHubInstallationID: &otherInstallation, Created: time.Now(), @@ -479,7 +479,7 @@ func TestMatchProjectsToInstallation_SkipsAlreadyAssociated(t *testing.T) { } // Verify project still has the original installation - updatedProject, _ := s.GetProject(ctx, "g1") + updatedProject, _ := s.GetProject(ctx, tid("g1")) if *updatedProject.GitHubInstallationID != 99999 { t.Errorf("project should still have original installation") } @@ -593,7 +593,7 @@ func TestWebhook_PublishesProjectUpdatedOnInstallationDeleted(t *testing.T) { // Create a project associated with the installation project := &store.Project{ - ID: "project-event-1", + ID: tid("project-event-1"), Name: "Event Test Project", Slug: "event-test-project", GitRemote: "https://github.com/acme/widgets.git", @@ -635,7 +635,7 @@ func TestWebhook_PublishesProjectUpdatedOnInstallationDeleted(t *testing.T) { if len(updates) != 1 { t.Fatalf("expected 1 project updated event, got %d", len(updates)) } - if updates[0].ID != "project-event-1" { + if updates[0].ID != tid("project-event-1") { t.Errorf("expected project ID project-event-1, got %s", updates[0].ID) } if updates[0].GitHubAppStatus == nil || updates[0].GitHubAppStatus.State != store.GitHubAppStateError { @@ -664,7 +664,7 @@ func TestWebhook_PublishesProjectUpdatedOnRepoRemoved(t *testing.T) { } project := &store.Project{ - ID: "project-event-2", + ID: tid("project-event-2"), Name: "Event Test Project 2", Slug: "event-test-project-2", GitRemote: "https://github.com/acme/widgets.git", @@ -702,7 +702,7 @@ func TestWebhook_PublishesProjectUpdatedOnRepoRemoved(t *testing.T) { if len(updates) != 1 { t.Fatalf("expected 1 project updated event, got %d", len(updates)) } - if updates[0].ID != "project-event-2" { + if updates[0].ID != tid("project-event-2") { t.Errorf("expected project ID project-event-2, got %s", updates[0].ID) } if updates[0].GitHubAppStatus == nil || updates[0].GitHubAppStatus.State != store.GitHubAppStateError { @@ -719,7 +719,7 @@ func TestWebhook_PublishesProjectUpdatedOnAutoMatch(t *testing.T) { // Create a project with a matching git remote but no installation yet project := &store.Project{ - ID: "project-event-3", + ID: tid("project-event-3"), Name: "Event Test Project 3", Slug: "event-test-project-3", GitRemote: "https://github.com/acme/widgets.git", @@ -762,7 +762,7 @@ func TestWebhook_PublishesProjectUpdatedOnAutoMatch(t *testing.T) { if len(updates) != 1 { t.Fatalf("expected 1 project updated event from auto-match, got %d", len(updates)) } - if updates[0].ID != "project-event-3" { + if updates[0].ID != tid("project-event-3") { t.Errorf("expected project ID project-event-3, got %s", updates[0].ID) } if updates[0].GitHubInstallationID == nil || *updates[0].GitHubInstallationID != 12345 { diff --git a/pkg/hub/handlers_logs_test.go b/pkg/hub/handlers_logs_test.go index eac73d122..6c3693d8a 100644 --- a/pkg/hub/handlers_logs_test.go +++ b/pkg/hub/handlers_logs_test.go @@ -43,6 +43,7 @@ func createTestAgent(t *testing.T, s store.Store) *store.Agent { project := &store.Project{ ID: api.NewUUID(), Name: "test-project-" + api.NewUUID()[:8], + Slug: "test-project-" + api.NewUUID()[:8], } if err := s.CreateProject(ctx, project); err != nil { t.Fatalf("CreateProject: %v", err) @@ -51,6 +52,7 @@ func createTestAgent(t *testing.T, s store.Store) *store.Agent { agent := &store.Agent{ ID: api.NewUUID(), Name: "test-agent-" + api.NewUUID()[:8], + Slug: "test-agent-" + api.NewUUID()[:8], ProjectID: project.ID, } if err := s.CreateAgent(ctx, agent); err != nil { diff --git a/pkg/hub/handlers_messages.go b/pkg/hub/handlers_messages.go index 54a5db948..767e908d5 100644 --- a/pkg/hub/handlers_messages.go +++ b/pkg/hub/handlers_messages.go @@ -216,15 +216,16 @@ func (s *Server) handleAgentMessagesStream(w http.ResponseWriter, r *http.Reques return } - // The event bus is an interface; only ChannelEventPublisher supports - // subscription. Check this before hitting the store so noop-publisher - // hubs fail fast without a wasted DB roundtrip. - ep, ok := s.events.(*ChannelEventPublisher) - if !ok { + // Real-time streaming requires a subscription-capable EventPublisher + // (ChannelEventPublisher or PostgresEventPublisher). The no-op publisher + // returns a nil channel, so fail fast before hitting the store to avoid a + // wasted DB roundtrip on hubs without a configured publisher. + if _, isNoop := s.events.(noopEventPublisher); isNoop || s.events == nil { writeError(w, http.StatusNotImplemented, "not_implemented", "Real-time message streaming is not available on this hub", nil) return } + ep := s.events ctx := r.Context() user := GetUserIdentityFromContext(ctx) diff --git a/pkg/hub/handlers_notifications_test.go b/pkg/hub/handlers_notifications_test.go index 9076bc04d..55f0f1f8f 100644 --- a/pkg/hub/handlers_notifications_test.go +++ b/pkg/hub/handlers_notifications_test.go @@ -19,6 +19,7 @@ package hub import ( "context" "encoding/json" + "fmt" "net/http" "net/http/httptest" "testing" @@ -39,14 +40,14 @@ func setupNotificationHandlerTest(t *testing.T) (*Server, store.Store, string) { ctx := context.Background() project := &store.Project{ - ID: "project-notif-handler", + ID: tid("project-notif-handler"), Name: "Notif Handler Project", Slug: "notif-handler-project", } require.NoError(t, s.CreateProject(ctx, project)) agent := &store.Agent{ - ID: "agent-watched", + ID: tid("agent-watched"), Slug: "watched-agent", Name: "Watched Agent", ProjectID: project.ID, @@ -174,7 +175,7 @@ func TestHandleNotifications_AcknowledgeAll(t *testing.T) { func TestHandleNotifications_AcknowledgeNotFound(t *testing.T) { srv, _, _ := setupNotificationHandlerTest(t) - rec := doRequest(t, srv, http.MethodPost, "/api/v1/notifications/nonexistent-id/ack", nil) + rec := doRequest(t, srv, http.MethodPost, "/api/v1/notifications/"+tid("nonexistent-id")+"/ack", nil) assert.Equal(t, http.StatusNotFound, rec.Code) } @@ -184,14 +185,14 @@ func TestHandleNotifications_RejectAgentToken(t *testing.T) { // Create an agent and generate a token for it project := &store.Project{ - ID: "project-agent-auth", + ID: tid("project-agent-auth"), Name: "Agent Auth Project", Slug: "agent-auth-project", } _ = s.CreateProject(ctx, project) agent := &store.Agent{ - ID: "agent-auth-test", + ID: tid("agent-auth-test"), Slug: "auth-agent", Name: "Auth Agent", ProjectID: project.ID, @@ -227,14 +228,14 @@ func TestHandleNotifications_FilterByAgent(t *testing.T) { srv, s, _ := setupNotificationHandlerTest(t) ctx := context.Background() - // The setup already created "agent-watched" with user notifications for DevUserID. - // Create a second agent that watches "agent-watched", so "agent-watched" is the + // The setup already created tid("agent-watched") with user notifications for DevUserID. + // Create a second agent that watches tid("agent-watched"), so tid("agent-watched") is the // subscriber (simulating notifications sent TO the watched agent). agent2 := &store.Agent{ - ID: "agent-other", - Slug: "other-agent", + ID: tid("agent-other"), + Slug: tid("other-agent"), Name: "Other Agent", - ProjectID: "project-notif-handler", + ProjectID: tid("project-notif-handler"), Phase: string(state.PhaseRunning), } require.NoError(t, s.CreateAgent(ctx, agent2)) @@ -243,10 +244,10 @@ func TestHandleNotifications_FilterByAgent(t *testing.T) { sub2 := &store.NotificationSubscription{ ID: api.NewUUID(), Scope: store.SubscriptionScopeAgent, - AgentID: "agent-other", + AgentID: tid("agent-other"), SubscriberType: store.SubscriberTypeAgent, - SubscriberID: "agent-watched", - ProjectID: "project-notif-handler", + SubscriberID: tid("agent-watched"), + ProjectID: tid("project-notif-handler"), TriggerActivities: []string{"COMPLETED"}, CreatedAt: time.Now(), CreatedBy: "test", @@ -257,10 +258,10 @@ func TestHandleNotifications_FilterByAgent(t *testing.T) { agentNotif := &store.Notification{ ID: api.NewUUID(), SubscriptionID: sub2.ID, - AgentID: "agent-other", - ProjectID: "project-notif-handler", + AgentID: tid("agent-other"), + ProjectID: tid("project-notif-handler"), SubscriberType: store.SubscriberTypeAgent, - SubscriberID: "agent-watched", + SubscriberID: tid("agent-watched"), Status: "COMPLETED", Message: "agent-other completed (to agent-watched)", Dispatched: true, @@ -270,7 +271,7 @@ func TestHandleNotifications_FilterByAgent(t *testing.T) { require.NoError(t, s.CreateNotification(ctx, agentNotif)) // GET with agentId filter - rec := doRequest(t, srv, http.MethodGet, "/api/v1/notifications?agentId=agent-watched", nil) + rec := doRequest(t, srv, http.MethodGet, fmt.Sprintf("/api/v1/notifications?agentId=%s", tid("agent-watched")), nil) assert.Equal(t, http.StatusOK, rec.Code) var resp struct { @@ -280,19 +281,19 @@ func TestHandleNotifications_FilterByAgent(t *testing.T) { require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp)) // User notifications: 1 unacknowledged for this agent (notif1 from setup) - assert.Len(t, resp.UserNotifications, 1) + require.Len(t, resp.UserNotifications, 1) assert.Equal(t, "COMPLETED", resp.UserNotifications[0].Status) // Agent notifications: notifications sent TO agent-watched - assert.Len(t, resp.AgentNotifications, 1) - assert.Equal(t, "agent-watched", resp.AgentNotifications[0].SubscriberID) + require.Len(t, resp.AgentNotifications, 1) + assert.Equal(t, tid("agent-watched"), resp.AgentNotifications[0].SubscriberID) } func TestHandleNotifications_FilterByAgent_NoResults(t *testing.T) { srv, _, _ := setupNotificationHandlerTest(t) // Query for an agent with no notifications - rec := doRequest(t, srv, http.MethodGet, "/api/v1/notifications?agentId=nonexistent-agent", nil) + rec := doRequest(t, srv, http.MethodGet, fmt.Sprintf("/api/v1/notifications?agentId=%s", tid("nonexistent-agent")), nil) assert.Equal(t, http.StatusOK, rec.Code) var resp struct { @@ -323,7 +324,7 @@ func setupProjectWithBroker(t *testing.T, s store.Store, projectID, projectName ctx := context.Background() broker := &store.RuntimeBroker{ - ID: "broker-" + projectID, + ID: tid("broker-" + projectID), Name: "Test Broker", Slug: "test-broker-" + projectID, Status: store.BrokerStatusOnline, @@ -331,7 +332,7 @@ func setupProjectWithBroker(t *testing.T, s store.Store, projectID, projectName require.NoError(t, s.CreateRuntimeBroker(ctx, broker)) project := &store.Project{ - ID: projectID, + ID: tid(projectID), Name: projectName, Slug: projectID, } @@ -416,8 +417,8 @@ func TestHandleSubscriptions_CreateAgentScoped(t *testing.T) { req := createSubscriptionRequest{ Scope: "agent", - AgentID: "agent-watched", - ProjectID: "project-notif-handler", + AgentID: tid("agent-watched"), + ProjectID: tid("project-notif-handler"), TriggerActivities: []string{"COMPLETED", "WAITING_FOR_INPUT"}, } rec := doRequest(t, srv, http.MethodPost, "/api/v1/notifications/subscriptions", req) @@ -429,8 +430,8 @@ func TestHandleSubscriptions_CreateAgentScoped(t *testing.T) { var sub store.NotificationSubscription require.NoError(t, json.NewDecoder(rec.Body).Decode(&sub)) assert.Equal(t, "agent", sub.Scope) - assert.Equal(t, "agent-watched", sub.AgentID) - assert.Equal(t, "project-notif-handler", sub.ProjectID) + assert.Equal(t, tid("agent-watched"), sub.AgentID) + assert.Equal(t, tid("project-notif-handler"), sub.ProjectID) // Verify in store subs, err := s.GetSubscriptionsForSubscriber(context.Background(), store.SubscriberTypeUser, DevUserID) @@ -443,7 +444,7 @@ func TestHandleSubscriptions_CreateProjectScoped(t *testing.T) { req := createSubscriptionRequest{ Scope: "project", - ProjectID: "project-notif-handler", + ProjectID: tid("project-notif-handler"), TriggerActivities: []string{"COMPLETED"}, } rec := doRequest(t, srv, http.MethodPost, "/api/v1/notifications/subscriptions", req) @@ -453,7 +454,7 @@ func TestHandleSubscriptions_CreateProjectScoped(t *testing.T) { require.NoError(t, json.NewDecoder(rec.Body).Decode(&sub)) assert.Equal(t, "project", sub.Scope) assert.Empty(t, sub.AgentID) - assert.Equal(t, "project-notif-handler", sub.ProjectID) + assert.Equal(t, tid("project-notif-handler"), sub.ProjectID) } func TestHandleSubscriptions_CreateValidation(t *testing.T) { @@ -484,7 +485,7 @@ func TestHandleSubscriptions_List(t *testing.T) { // Create a project-scoped subscription createReq := createSubscriptionRequest{ Scope: "project", - ProjectID: "project-notif-handler", + ProjectID: tid("project-notif-handler"), TriggerActivities: []string{"COMPLETED"}, } rec := doRequest(t, srv, http.MethodPost, "/api/v1/notifications/subscriptions", createReq) @@ -515,7 +516,7 @@ func TestHandleSubscriptions_Delete(t *testing.T) { // Create a new subscription to delete createReq := createSubscriptionRequest{ Scope: "project", - ProjectID: "project-notif-handler", + ProjectID: tid("project-notif-handler"), TriggerActivities: []string{"COMPLETED"}, } rec := doRequest(t, srv, http.MethodPost, "/api/v1/notifications/subscriptions", createReq) diff --git a/pkg/hub/handlers_permissions_test.go b/pkg/hub/handlers_permissions_test.go index 257a2a6d4..d9926b021 100644 --- a/pkg/hub/handlers_permissions_test.go +++ b/pkg/hub/handlers_permissions_test.go @@ -20,6 +20,7 @@ import ( "bytes" "context" "encoding/json" + "errors" "net/http" "net/http/httptest" "testing" @@ -28,6 +29,62 @@ import ( "github.com/GoogleCloudPlatform/scion/pkg/store" ) +// permSeedUser ensures a user row exists so that group-membership / policy-binding +// foreign keys resolve. The Ent store enforces user/agent FK edges that the +// former raw-SQL store did not, so fixtures must create referenced principals. +func permSeedUser(t *testing.T, ctx context.Context, s store.Store, id string) { + t.Helper() + err := s.CreateUser(ctx, &store.User{ + ID: id, Email: id + "@example.com", DisplayName: "Seed User", + Role: store.UserRoleMember, Status: "active", Created: time.Now(), + }) + if err != nil && !errors.Is(err, store.ErrAlreadyExists) { + t.Fatalf("seed user %s: %v", id, err) + } +} + +// permSeedAgent ensures an agent (and its required project) exists so that +// membership / binding foreign keys resolve. +func permSeedAgent(t *testing.T, ctx context.Context, s store.Store, id string) { + t.Helper() + projectID := tid("perm-agent-project") + _ = s.CreateProject(ctx, &store.Project{ID: projectID, Name: "Perm Agent Project", Slug: "perm-agent-project"}) + err := s.CreateAgent(ctx, &store.Agent{ + ID: id, Name: "Seed Agent", Slug: "seed-agent-" + id[:8], + ProjectID: projectID, Phase: "stopped", Visibility: store.VisibilityPrivate, + }) + if err != nil && !errors.Is(err, store.ErrAlreadyExists) { + t.Fatalf("seed agent %s: %v", id, err) + } +} + +// permSeedMember seeds the user or agent referenced by a group membership. +func permSeedMember(t *testing.T, ctx context.Context, s store.Store, m *store.GroupMember) { + t.Helper() + if m.MemberID == "" { + return + } + if m.MemberType == store.GroupMemberTypeAgent { + permSeedAgent(t, ctx, s, m.MemberID) + } else { + permSeedUser(t, ctx, s, m.MemberID) + } +} + +// permSeedPrincipal seeds the user or agent referenced by a policy binding. +// Group principals are created by the test itself, so they are skipped. +func permSeedPrincipal(t *testing.T, ctx context.Context, s store.Store, principalType, principalID string) { + t.Helper() + if principalID == "" || principalType == "group" { + return + } + if principalType == "agent" { + permSeedAgent(t, ctx, s, principalID) + } else { + permSeedUser(t, ctx, s, principalID) + } +} + // ============================================================================ // Group Endpoint Tests // ============================================================================ @@ -39,12 +96,15 @@ func TestGroupList(t *testing.T) { // Create some test groups for i := 0; i < 3; i++ { group := &store.Group{ - ID: "group_" + string(rune('a'+i)), + ID: tid("group_" + string(rune('a'+i))), Name: "Test Group " + string(rune('A'+i)), - Slug: "test-group-" + string(rune('a'+i)), + Slug: tid("test-group-" + string(rune('a'+i))), Created: time.Now(), Updated: time.Now(), } + if group.OwnerID != "" { + permSeedUser(t, ctx, s, group.OwnerID) + } if err := s.CreateGroup(ctx, group); err != nil { t.Fatalf("failed to create group: %v", err) } @@ -120,13 +180,16 @@ func TestGroupGet(t *testing.T) { ctx := context.Background() group := &store.Group{ - ID: "group_xyz123", + ID: tid("group_xyz123"), Name: "Test Group", Slug: "test-group", Description: "A test group", Created: time.Now(), Updated: time.Now(), } + if group.OwnerID != "" { + permSeedUser(t, ctx, s, group.OwnerID) + } if err := s.CreateGroup(ctx, group); err != nil { t.Fatalf("failed to create group: %v", err) } @@ -160,12 +223,15 @@ func TestGroupUpdate(t *testing.T) { ctx := context.Background() group := &store.Group{ - ID: "group_upd123", + ID: tid("group_upd123"), Name: "Original Name", Slug: "original-name", Created: time.Now(), Updated: time.Now(), } + if group.OwnerID != "" { + permSeedUser(t, ctx, s, group.OwnerID) + } if err := s.CreateGroup(ctx, group); err != nil { t.Fatalf("failed to create group: %v", err) } @@ -199,12 +265,15 @@ func TestGroupDelete(t *testing.T) { ctx := context.Background() group := &store.Group{ - ID: "group_del123", + ID: tid("group_del123"), Name: "Delete Me", Slug: "delete-me", Created: time.Now(), Updated: time.Now(), } + if group.OwnerID != "" { + permSeedUser(t, ctx, s, group.OwnerID) + } if err := s.CreateGroup(ctx, group); err != nil { t.Fatalf("failed to create group: %v", err) } @@ -227,19 +296,22 @@ func TestGroupMembersAdd(t *testing.T) { ctx := context.Background() group := &store.Group{ - ID: "group_mem123", + ID: tid("group_mem123"), Name: "Test Group", Slug: "test-group", Created: time.Now(), Updated: time.Now(), } + if group.OwnerID != "" { + permSeedUser(t, ctx, s, group.OwnerID) + } if err := s.CreateGroup(ctx, group); err != nil { t.Fatalf("failed to create group: %v", err) } // Create the user to be added as a member user := &store.User{ - ID: "user_abc123", + ID: tid("user_abc123"), Email: "user@example.com", DisplayName: "Test User", Role: "member", @@ -252,7 +324,7 @@ func TestGroupMembersAdd(t *testing.T) { body := AddGroupMemberRequest{ MemberType: "user", - MemberID: "user_abc123", + MemberID: tid("user_abc123"), Role: "member", } @@ -267,7 +339,7 @@ func TestGroupMembersAdd(t *testing.T) { t.Fatalf("failed to decode response: %v", err) } - if resp.MemberID != "user_abc123" { + if resp.MemberID != tid("user_abc123") { t.Errorf("expected memberId 'user_abc123', got %q", resp.MemberID) } if resp.DisplayName != "Test User" { @@ -280,19 +352,22 @@ func TestGroupMembersAddByEmail(t *testing.T) { ctx := context.Background() group := &store.Group{ - ID: "group_email123", + ID: tid("group_email123"), Name: "Test Group Email", Slug: "test-group-email", Created: time.Now(), Updated: time.Now(), } + if group.OwnerID != "" { + permSeedUser(t, ctx, s, group.OwnerID) + } if err := s.CreateGroup(ctx, group); err != nil { t.Fatalf("failed to create group: %v", err) } // Create the user user := &store.User{ - ID: "user_email_test", + ID: tid("user_email_test"), Email: "alice@example.com", DisplayName: "Alice", Role: "member", @@ -322,7 +397,7 @@ func TestGroupMembersAddByEmail(t *testing.T) { } // Should resolve email to user ID - if resp.MemberID != "user_email_test" { + if resp.MemberID != tid("user_email_test") { t.Errorf("expected memberId 'user_email_test', got %q", resp.MemberID) } if resp.DisplayName != "Alice" { @@ -335,12 +410,15 @@ func TestGroupMembersAddByEmail_NotFound(t *testing.T) { ctx := context.Background() group := &store.Group{ - ID: "group_email_nf", + ID: tid("group_email_nf"), Name: "Test Group", Slug: "test-group-email-nf", Created: time.Now(), Updated: time.Now(), } + if group.OwnerID != "" { + permSeedUser(t, ctx, s, group.OwnerID) + } if err := s.CreateGroup(ctx, group); err != nil { t.Fatalf("failed to create group: %v", err) } @@ -363,22 +441,28 @@ func TestGroupMembersAddGroupBySlug(t *testing.T) { ctx := context.Background() parentGroup := &store.Group{ - ID: "parent_grp", + ID: tid("parent_grp"), Name: "Parent Group", Slug: "parent-group", Created: time.Now(), Updated: time.Now(), } childGroup := &store.Group{ - ID: "child_grp", + ID: tid("child_grp"), Name: "Child Group", Slug: "child-group", Created: time.Now(), Updated: time.Now(), } + if parentGroup.OwnerID != "" { + permSeedUser(t, ctx, s, parentGroup.OwnerID) + } if err := s.CreateGroup(ctx, parentGroup); err != nil { t.Fatalf("failed to create parent group: %v", err) } + if childGroup.OwnerID != "" { + permSeedUser(t, ctx, s, childGroup.OwnerID) + } if err := s.CreateGroup(ctx, childGroup); err != nil { t.Fatalf("failed to create child group: %v", err) } @@ -402,7 +486,7 @@ func TestGroupMembersAddGroupBySlug(t *testing.T) { } // Should resolve slug to group ID - if resp.MemberID != "child_grp" { + if resp.MemberID != tid("child_grp") { t.Errorf("expected memberId 'child_grp', got %q", resp.MemberID) } if resp.DisplayName != "Child Group" { @@ -415,12 +499,15 @@ func TestGroupMembersList(t *testing.T) { ctx := context.Background() group := &store.Group{ - ID: "group_lst123", + ID: tid("group_lst123"), Name: "Test Group", Slug: "test-group-list", Created: time.Now(), Updated: time.Now(), } + if group.OwnerID != "" { + permSeedUser(t, ctx, s, group.OwnerID) + } if err := s.CreateGroup(ctx, group); err != nil { t.Fatalf("failed to create group: %v", err) } @@ -430,10 +517,11 @@ func TestGroupMembersList(t *testing.T) { member := &store.GroupMember{ GroupID: group.ID, MemberType: "user", - MemberID: "user_" + string(rune('a'+i)), + MemberID: tid("user_" + string(rune('a'+i))), Role: "member", AddedAt: time.Now(), } + permSeedMember(t, ctx, s, member) if err := s.AddGroupMember(ctx, member); err != nil { t.Fatalf("failed to add member: %v", err) } @@ -460,12 +548,15 @@ func TestGroupMemberRemove(t *testing.T) { ctx := context.Background() group := &store.Group{ - ID: "group_rem123", + ID: tid("group_rem123"), Name: "Test Group", Slug: "test-group-remove", Created: time.Now(), Updated: time.Now(), } + if group.OwnerID != "" { + permSeedUser(t, ctx, s, group.OwnerID) + } if err := s.CreateGroup(ctx, group); err != nil { t.Fatalf("failed to create group: %v", err) } @@ -473,22 +564,23 @@ func TestGroupMemberRemove(t *testing.T) { member := &store.GroupMember{ GroupID: group.ID, MemberType: "user", - MemberID: "user_remove", + MemberID: tid("user_remove"), Role: "member", AddedAt: time.Now(), } + permSeedMember(t, ctx, s, member) if err := s.AddGroupMember(ctx, member); err != nil { t.Fatalf("failed to add member: %v", err) } - rec := doRequest(t, srv, http.MethodDelete, "/api/v1/groups/"+group.ID+"/members/user/user_remove", nil) + rec := doRequest(t, srv, http.MethodDelete, "/api/v1/groups/"+group.ID+"/members/user/"+tid("user_remove"), nil) if rec.Code != http.StatusNoContent { t.Errorf("expected status 204, got %d: %s", rec.Code, rec.Body.String()) } // Verify removed - _, err := s.GetGroupMembership(ctx, group.ID, "user", "user_remove") + _, err := s.GetGroupMembership(ctx, group.ID, "user", tid("user_remove")) if err != store.ErrNotFound { t.Errorf("expected ErrNotFound, got %v", err) } @@ -500,22 +592,28 @@ func TestGroupCycleDetection(t *testing.T) { // Create two groups groupA := &store.Group{ - ID: "group_a", + ID: tid("group_a"), Name: "Group A", Slug: "group-a", Created: time.Now(), Updated: time.Now(), } groupB := &store.Group{ - ID: "group_b", + ID: tid("group_b"), Name: "Group B", Slug: "group-b", Created: time.Now(), Updated: time.Now(), } + if groupA.OwnerID != "" { + permSeedUser(t, ctx, s, groupA.OwnerID) + } if err := s.CreateGroup(ctx, groupA); err != nil { t.Fatalf("failed to create group A: %v", err) } + if groupB.OwnerID != "" { + permSeedUser(t, ctx, s, groupB.OwnerID) + } if err := s.CreateGroup(ctx, groupB); err != nil { t.Fatalf("failed to create group B: %v", err) } @@ -549,7 +647,7 @@ func TestGroupMembersAddAgent(t *testing.T) { // Create a project for the agent project := &store.Project{ - ID: "project_agent_test", + ID: tid("project_agent_test"), Name: "Test Project", Slug: "test-project-agent", } @@ -559,8 +657,9 @@ func TestGroupMembersAddAgent(t *testing.T) { // Create the agent agent := &store.Agent{ - ID: "agent_abc123", + ID: tid("agent_abc123"), Name: "Test Agent", + Slug: "test-agent-abc123", ProjectID: project.ID, } if err := s.CreateAgent(ctx, agent); err != nil { @@ -568,19 +667,22 @@ func TestGroupMembersAddAgent(t *testing.T) { } group := &store.Group{ - ID: "group_agent123", + ID: tid("group_agent123"), Name: "Test Group", Slug: "test-group-agent", Created: time.Now(), Updated: time.Now(), } + if group.OwnerID != "" { + permSeedUser(t, ctx, s, group.OwnerID) + } if err := s.CreateGroup(ctx, group); err != nil { t.Fatalf("failed to create group: %v", err) } body := AddGroupMemberRequest{ MemberType: "agent", - MemberID: "agent_abc123", + MemberID: tid("agent_abc123"), Role: "member", } @@ -598,7 +700,7 @@ func TestGroupMembersAddAgent(t *testing.T) { if resp.MemberType != "agent" { t.Errorf("expected memberType 'agent', got %q", resp.MemberType) } - if resp.MemberID != "agent_abc123" { + if resp.MemberID != tid("agent_abc123") { t.Errorf("expected memberId 'agent_abc123', got %q", resp.MemberID) } if resp.DisplayName != "Test Agent" { @@ -611,12 +713,15 @@ func TestGroupMemberRemoveAgent(t *testing.T) { ctx := context.Background() group := &store.Group{ - ID: "group_rmagent", + ID: tid("group_rmagent"), Name: "Test Group", Slug: "test-group-rm-agent", Created: time.Now(), Updated: time.Now(), } + if group.OwnerID != "" { + permSeedUser(t, ctx, s, group.OwnerID) + } if err := s.CreateGroup(ctx, group); err != nil { t.Fatalf("failed to create group: %v", err) } @@ -624,22 +729,23 @@ func TestGroupMemberRemoveAgent(t *testing.T) { member := &store.GroupMember{ GroupID: group.ID, MemberType: "agent", - MemberID: "agent_remove", + MemberID: tid("agent_remove"), Role: "member", AddedAt: time.Now(), } + permSeedMember(t, ctx, s, member) if err := s.AddGroupMember(ctx, member); err != nil { t.Fatalf("failed to add member: %v", err) } - rec := doRequest(t, srv, http.MethodDelete, "/api/v1/groups/"+group.ID+"/members/agent/agent_remove", nil) + rec := doRequest(t, srv, http.MethodDelete, "/api/v1/groups/"+group.ID+"/members/agent/"+tid("agent_remove"), nil) if rec.Code != http.StatusNoContent { t.Errorf("expected status 204, got %d: %s", rec.Code, rec.Body.String()) } // Verify removed - _, err := s.GetGroupMembership(ctx, group.ID, "agent", "agent_remove") + _, err := s.GetGroupMembership(ctx, group.ID, "agent", tid("agent_remove")) if err != store.ErrNotFound { t.Errorf("expected ErrNotFound, got %v", err) } @@ -701,7 +807,7 @@ func TestGroupListWithGroupTypeFilter(t *testing.T) { // Create groups with different (or default) types g1 := &store.Group{ - ID: "group_explicit_1", + ID: tid("group_explicit_1"), Name: "Explicit 1", Slug: "explicit-1", GroupType: "explicit", @@ -709,7 +815,7 @@ func TestGroupListWithGroupTypeFilter(t *testing.T) { Updated: time.Now(), } g2 := &store.Group{ - ID: "group_explicit_2", + ID: tid("group_explicit_2"), Name: "Explicit 2", Slug: "explicit-2", GroupType: "explicit", @@ -717,6 +823,9 @@ func TestGroupListWithGroupTypeFilter(t *testing.T) { Updated: time.Now(), } for _, g := range []*store.Group{g1, g2} { + if g.OwnerID != "" { + permSeedUser(t, ctx, s, g.OwnerID) + } if err := s.CreateGroup(ctx, g); err != nil { t.Fatalf("failed to create group: %v", err) } @@ -786,7 +895,7 @@ func TestGroupUpdateAuthz_OwnerAllowed(t *testing.T) { ctx := context.Background() owner := &store.User{ - ID: "user_owner_upd", + ID: tid("user_owner_upd"), Email: "owner@example.com", DisplayName: "Owner", Role: "member", @@ -798,13 +907,16 @@ func TestGroupUpdateAuthz_OwnerAllowed(t *testing.T) { } group := &store.Group{ - ID: "group_authz_upd", + ID: tid("group_authz_upd"), Name: "Owned Group", Slug: "owned-group-upd", OwnerID: owner.ID, Created: time.Now(), Updated: time.Now(), } + if group.OwnerID != "" { + permSeedUser(t, ctx, s, group.OwnerID) + } if err := s.CreateGroup(ctx, group); err != nil { t.Fatalf("failed to create group: %v", err) } @@ -822,7 +934,7 @@ func TestGroupUpdateAuthz_NonOwnerDenied(t *testing.T) { ctx := context.Background() other := &store.User{ - ID: "user_other_upd", + ID: tid("user_other_upd"), Email: "other@example.com", DisplayName: "Other", Role: "member", @@ -834,13 +946,16 @@ func TestGroupUpdateAuthz_NonOwnerDenied(t *testing.T) { } group := &store.Group{ - ID: "group_authz_upd2", + ID: tid("group_authz_upd2"), Name: "Someone Else Group", Slug: "someone-else-upd", - OwnerID: "user_someone_else", + OwnerID: tid("user_someone_else"), Created: time.Now(), Updated: time.Now(), } + if group.OwnerID != "" { + permSeedUser(t, ctx, s, group.OwnerID) + } if err := s.CreateGroup(ctx, group); err != nil { t.Fatalf("failed to create group: %v", err) } @@ -858,7 +973,7 @@ func TestGroupDeleteAuthz_NonOwnerDenied(t *testing.T) { ctx := context.Background() other := &store.User{ - ID: "user_other_del", + ID: tid("user_other_del"), Email: "other-del@example.com", DisplayName: "Other", Role: "member", @@ -870,13 +985,16 @@ func TestGroupDeleteAuthz_NonOwnerDenied(t *testing.T) { } group := &store.Group{ - ID: "group_authz_del", + ID: tid("group_authz_del"), Name: "Protected Group", Slug: "protected-group", - OwnerID: "user_someone_else", + OwnerID: tid("user_someone_else"), Created: time.Now(), Updated: time.Now(), } + if group.OwnerID != "" { + permSeedUser(t, ctx, s, group.OwnerID) + } if err := s.CreateGroup(ctx, group); err != nil { t.Fatalf("failed to create group: %v", err) } @@ -893,7 +1011,7 @@ func TestGroupAddMemberAuthz_OwnerAllowed(t *testing.T) { ctx := context.Background() owner := &store.User{ - ID: "user_owner_add", + ID: tid("user_owner_add"), Email: "owner-add@example.com", DisplayName: "Owner", Role: "member", @@ -901,7 +1019,7 @@ func TestGroupAddMemberAuthz_OwnerAllowed(t *testing.T) { Created: time.Now(), } memberUser := &store.User{ - ID: "user_to_add", + ID: tid("user_to_add"), Email: "toadd@example.com", DisplayName: "To Add", Role: "member", @@ -915,13 +1033,16 @@ func TestGroupAddMemberAuthz_OwnerAllowed(t *testing.T) { } group := &store.Group{ - ID: "group_authz_add", + ID: tid("group_authz_add"), Name: "Owned Group", Slug: "owned-group-add", OwnerID: owner.ID, Created: time.Now(), Updated: time.Now(), } + if group.OwnerID != "" { + permSeedUser(t, ctx, s, group.OwnerID) + } if err := s.CreateGroup(ctx, group); err != nil { t.Fatalf("failed to create group: %v", err) } @@ -943,7 +1064,7 @@ func TestGroupAddMemberAuthz_NonOwnerDenied(t *testing.T) { ctx := context.Background() other := &store.User{ - ID: "user_other_add", + ID: tid("user_other_add"), Email: "other-add@example.com", DisplayName: "Other", Role: "member", @@ -955,13 +1076,16 @@ func TestGroupAddMemberAuthz_NonOwnerDenied(t *testing.T) { } group := &store.Group{ - ID: "group_authz_add2", + ID: tid("group_authz_add2"), Name: "Protected Group", Slug: "protected-group-add", - OwnerID: "user_someone_else", + OwnerID: tid("user_someone_else"), Created: time.Now(), Updated: time.Now(), } + if group.OwnerID != "" { + permSeedUser(t, ctx, s, group.OwnerID) + } if err := s.CreateGroup(ctx, group); err != nil { t.Fatalf("failed to create group: %v", err) } @@ -983,7 +1107,7 @@ func TestGroupRemoveMemberAuthz_NonOwnerDenied(t *testing.T) { ctx := context.Background() other := &store.User{ - ID: "user_other_rm", + ID: tid("user_other_rm"), Email: "other-rm@example.com", DisplayName: "Other", Role: "member", @@ -995,13 +1119,16 @@ func TestGroupRemoveMemberAuthz_NonOwnerDenied(t *testing.T) { } group := &store.Group{ - ID: "group_authz_rm", + ID: tid("group_authz_rm"), Name: "Protected Group", Slug: "protected-group-rm", - OwnerID: "user_someone_else", + OwnerID: tid("user_someone_else"), Created: time.Now(), Updated: time.Now(), } + if group.OwnerID != "" { + permSeedUser(t, ctx, s, group.OwnerID) + } if err := s.CreateGroup(ctx, group); err != nil { t.Fatalf("failed to create group: %v", err) } @@ -1010,15 +1137,16 @@ func TestGroupRemoveMemberAuthz_NonOwnerDenied(t *testing.T) { member := &store.GroupMember{ GroupID: group.ID, MemberType: "user", - MemberID: "user_existing", + MemberID: tid("user_existing"), Role: "member", AddedAt: time.Now(), } + permSeedMember(t, ctx, s, member) if err := s.AddGroupMember(ctx, member); err != nil { t.Fatalf("failed to add member: %v", err) } - rec := doGroupRequestAsUser(t, srv, other, http.MethodDelete, "/api/v1/groups/"+group.ID+"/members/user/user_existing", nil) + rec := doGroupRequestAsUser(t, srv, other, http.MethodDelete, "/api/v1/groups/"+group.ID+"/members/user/"+tid("user_existing"), nil) if rec.Code != http.StatusForbidden { t.Errorf("expected 403 for non-owner remove member, got %d: %s", rec.Code, rec.Body.String()) @@ -1030,7 +1158,7 @@ func TestGroupRemoveMemberAuthz_OwnerAllowed(t *testing.T) { ctx := context.Background() owner := &store.User{ - ID: "user_owner_rm", + ID: tid("user_owner_rm"), Email: "owner-rm@example.com", DisplayName: "Owner", Role: "member", @@ -1042,13 +1170,16 @@ func TestGroupRemoveMemberAuthz_OwnerAllowed(t *testing.T) { } group := &store.Group{ - ID: "group_authz_rm2", + ID: tid("group_authz_rm2"), Name: "Owned Group", Slug: "owned-group-rm", OwnerID: owner.ID, Created: time.Now(), Updated: time.Now(), } + if group.OwnerID != "" { + permSeedUser(t, ctx, s, group.OwnerID) + } if err := s.CreateGroup(ctx, group); err != nil { t.Fatalf("failed to create group: %v", err) } @@ -1056,15 +1187,16 @@ func TestGroupRemoveMemberAuthz_OwnerAllowed(t *testing.T) { member := &store.GroupMember{ GroupID: group.ID, MemberType: "user", - MemberID: "user_to_remove", + MemberID: tid("user_to_remove"), Role: "member", AddedAt: time.Now(), } + permSeedMember(t, ctx, s, member) if err := s.AddGroupMember(ctx, member); err != nil { t.Fatalf("failed to add member: %v", err) } - rec := doGroupRequestAsUser(t, srv, owner, http.MethodDelete, "/api/v1/groups/"+group.ID+"/members/user/user_to_remove", nil) + rec := doGroupRequestAsUser(t, srv, owner, http.MethodDelete, "/api/v1/groups/"+group.ID+"/members/user/"+tid("user_to_remove"), nil) if rec.Code != http.StatusNoContent { t.Errorf("expected 204 for owner remove member, got %d: %s", rec.Code, rec.Body.String()) @@ -1082,7 +1214,7 @@ func TestPolicyList(t *testing.T) { // Create some test policies for i := 0; i < 3; i++ { policy := &store.Policy{ - ID: "policy_" + string(rune('a'+i)), + ID: tid("policy_" + string(rune('a'+i))), Name: "Test Policy " + string(rune('A'+i)), ScopeType: "hub", ResourceType: "*", @@ -1196,7 +1328,7 @@ func TestPolicyGet(t *testing.T) { ctx := context.Background() policy := &store.Policy{ - ID: "policy_get123", + ID: tid("policy_get123"), Name: "Test Policy", ScopeType: "hub", ResourceType: "*", @@ -1230,7 +1362,7 @@ func TestPolicyUpdate(t *testing.T) { ctx := context.Background() policy := &store.Policy{ - ID: "policy_upd123", + ID: tid("policy_upd123"), Name: "Original Policy", ScopeType: "hub", ResourceType: "*", @@ -1279,7 +1411,7 @@ func TestPolicyDelete(t *testing.T) { ctx := context.Background() policy := &store.Policy{ - ID: "policy_del123", + ID: tid("policy_del123"), Name: "Delete Me", ScopeType: "hub", ResourceType: "*", @@ -1310,7 +1442,7 @@ func TestPolicyBindingsAdd(t *testing.T) { ctx := context.Background() policy := &store.Policy{ - ID: "policy_bind123", + ID: tid("policy_bind123"), Name: "Test Policy", ScopeType: "hub", ResourceType: "*", @@ -1325,8 +1457,9 @@ func TestPolicyBindingsAdd(t *testing.T) { body := AddPolicyBindingRequest{ PrincipalType: "user", - PrincipalID: "user_abc123", + PrincipalID: tid("user_abc123"), } + permSeedUser(t, ctx, s, tid("user_abc123")) rec := doRequest(t, srv, http.MethodPost, "/api/v1/policies/"+policy.ID+"/bindings", body) @@ -1339,7 +1472,7 @@ func TestPolicyBindingsAdd(t *testing.T) { t.Fatalf("failed to decode response: %v", err) } - if resp.PrincipalID != "user_abc123" { + if resp.PrincipalID != tid("user_abc123") { t.Errorf("expected principalId 'user_abc123', got %q", resp.PrincipalID) } } @@ -1349,7 +1482,7 @@ func TestPolicyBindingsList(t *testing.T) { ctx := context.Background() policy := &store.Policy{ - ID: "policy_blst123", + ID: tid("policy_blst123"), Name: "Test Policy", ScopeType: "hub", ResourceType: "*", @@ -1367,8 +1500,9 @@ func TestPolicyBindingsList(t *testing.T) { binding := &store.PolicyBinding{ PolicyID: policy.ID, PrincipalType: "user", - PrincipalID: "user_" + string(rune('a'+i)), + PrincipalID: tid("user_" + string(rune('a'+i))), } + permSeedPrincipal(t, ctx, s, binding.PrincipalType, binding.PrincipalID) if err := s.AddPolicyBinding(ctx, binding); err != nil { t.Fatalf("failed to add binding: %v", err) } @@ -1395,7 +1529,7 @@ func TestPolicyBindingRemove(t *testing.T) { ctx := context.Background() policy := &store.Policy{ - ID: "policy_brem123", + ID: tid("policy_brem123"), Name: "Test Policy", ScopeType: "hub", ResourceType: "*", @@ -1411,13 +1545,14 @@ func TestPolicyBindingRemove(t *testing.T) { binding := &store.PolicyBinding{ PolicyID: policy.ID, PrincipalType: "user", - PrincipalID: "user_remove", + PrincipalID: tid("user_remove"), } + permSeedPrincipal(t, ctx, s, binding.PrincipalType, binding.PrincipalID) if err := s.AddPolicyBinding(ctx, binding); err != nil { t.Fatalf("failed to add binding: %v", err) } - rec := doRequest(t, srv, http.MethodDelete, "/api/v1/policies/"+policy.ID+"/bindings/user/user_remove", nil) + rec := doRequest(t, srv, http.MethodDelete, "/api/v1/policies/"+policy.ID+"/bindings/user/"+tid("user_remove"), nil) if rec.Code != http.StatusNoContent { t.Errorf("expected status 204, got %d: %s", rec.Code, rec.Body.String()) @@ -1445,21 +1580,21 @@ func TestGetEffectiveGroups(t *testing.T) { // Create a group hierarchy: A contains B, B contains C // User is a member of C, should also be effective member of B and A groupA := &store.Group{ - ID: "group_eff_a", + ID: tid("group_eff_a"), Name: "Group A", Slug: "group-eff-a", Created: time.Now(), Updated: time.Now(), } groupB := &store.Group{ - ID: "group_eff_b", + ID: tid("group_eff_b"), Name: "Group B", Slug: "group-eff-b", Created: time.Now(), Updated: time.Now(), } groupC := &store.Group{ - ID: "group_eff_c", + ID: tid("group_eff_c"), Name: "Group C", Slug: "group-eff-c", Created: time.Now(), @@ -1467,6 +1602,9 @@ func TestGetEffectiveGroups(t *testing.T) { } for _, g := range []*store.Group{groupA, groupB, groupC} { + if g.OwnerID != "" { + permSeedUser(t, ctx, s, g.OwnerID) + } if err := s.CreateGroup(ctx, g); err != nil { t.Fatalf("failed to create group %s: %v", g.ID, err) } @@ -1495,10 +1633,17 @@ func TestGetEffectiveGroups(t *testing.T) { } // User is member of C + permSeedMember(t, ctx, s, &store.GroupMember{ + GroupID: groupC.ID, + MemberType: "user", + MemberID: tid("test_user"), + Role: "member", + AddedAt: time.Now(), + }) if err := s.AddGroupMember(ctx, &store.GroupMember{ GroupID: groupC.ID, MemberType: "user", - MemberID: "test_user", + MemberID: tid("test_user"), Role: "member", AddedAt: time.Now(), }); err != nil { @@ -1506,7 +1651,7 @@ func TestGetEffectiveGroups(t *testing.T) { } // Get effective groups for user - effectiveGroups, err := s.GetEffectiveGroups(ctx, "test_user") + effectiveGroups, err := s.GetEffectiveGroups(ctx, tid("test_user")) if err != nil { t.Fatalf("failed to get effective groups: %v", err) } @@ -1535,7 +1680,7 @@ func TestGetPoliciesForPrincipal(t *testing.T) { // Create a policy policy := &store.Policy{ - ID: "policy_forprinc", + ID: tid("policy_forprinc"), Name: "Test Policy", ScopeType: "hub", ResourceType: "*", @@ -1549,16 +1694,17 @@ func TestGetPoliciesForPrincipal(t *testing.T) { } // Bind to user + permSeedPrincipal(t, ctx, s, "user", tid("test_user")) if err := s.AddPolicyBinding(ctx, &store.PolicyBinding{ PolicyID: policy.ID, PrincipalType: "user", - PrincipalID: "test_user", + PrincipalID: tid("test_user"), }); err != nil { t.Fatalf("failed to add binding: %v", err) } // Get policies for user - policies, err := s.GetPoliciesForPrincipal(ctx, "user", "test_user") + policies, err := s.GetPoliciesForPrincipal(ctx, "user", tid("test_user")) if err != nil { t.Fatalf("failed to get policies: %v", err) } diff --git a/pkg/hub/handlers_principals_test.go b/pkg/hub/handlers_principals_test.go index f05b77c64..1720952ff 100644 --- a/pkg/hub/handlers_principals_test.go +++ b/pkg/hub/handlers_principals_test.go @@ -67,14 +67,14 @@ func TestAgentGroups(t *testing.T) { ctx := context.Background() project := &store.Project{ - ID: "project-1", + ID: tid("project-1"), Name: "Test Project", Slug: "test-project", } require.NoError(t, s.CreateProject(ctx, project)) agent := &store.Agent{ - ID: "agent-1", + ID: tid("agent-1"), Slug: "agent-1-slug", Name: "Agent 1", ProjectID: project.ID, @@ -82,7 +82,7 @@ func TestAgentGroups(t *testing.T) { } require.NoError(t, s.CreateAgent(ctx, agent)) - rec := doRequest(t, srv, http.MethodGet, "/api/v1/agents/agent-1/groups", nil) + rec := doRequest(t, srv, http.MethodGet, "/api/v1/agents/"+tid("agent-1")+"/groups", nil) assert.Equal(t, http.StatusOK, rec.Code) @@ -104,7 +104,7 @@ func TestPrincipalResolve_User(t *testing.T) { ctx := context.Background() user := &store.User{ - ID: "user-1", + ID: tid("user-1"), Email: "alice@example.com", DisplayName: "Alice", Role: "member", @@ -112,14 +112,14 @@ func TestPrincipalResolve_User(t *testing.T) { } require.NoError(t, s.CreateUser(ctx, user)) - rec := doRequest(t, srv, http.MethodGet, "/api/v1/principals/user/user-1", nil) + rec := doRequest(t, srv, http.MethodGet, "/api/v1/principals/user/"+tid("user-1"), nil) assert.Equal(t, http.StatusOK, rec.Code) var resp PrincipalResolutionResponse require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp)) assert.Equal(t, "user", resp.Principal.Type) - assert.Equal(t, "user-1", resp.Principal.ID) + assert.Equal(t, tid("user-1"), resp.Principal.ID) assert.Equal(t, "Alice", resp.Principal.DisplayName) assert.NotNil(t, resp.DirectGroups) assert.NotNil(t, resp.EffectiveGroups) @@ -130,14 +130,14 @@ func TestPrincipalResolve_Agent(t *testing.T) { ctx := context.Background() project := &store.Project{ - ID: "project-1", + ID: tid("project-1"), Name: "Test Project", Slug: "test-project", } require.NoError(t, s.CreateProject(ctx, project)) agent := &store.Agent{ - ID: "agent-1", + ID: tid("agent-1"), Slug: "agent-1-slug", Name: "Agent 1", ProjectID: project.ID, @@ -145,16 +145,16 @@ func TestPrincipalResolve_Agent(t *testing.T) { } require.NoError(t, s.CreateAgent(ctx, agent)) - rec := doRequest(t, srv, http.MethodGet, "/api/v1/principals/agent/agent-1", nil) + rec := doRequest(t, srv, http.MethodGet, "/api/v1/principals/agent/"+tid("agent-1"), nil) assert.Equal(t, http.StatusOK, rec.Code) var resp PrincipalResolutionResponse require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp)) assert.Equal(t, "agent", resp.Principal.Type) - assert.Equal(t, "agent-1", resp.Principal.ID) + assert.Equal(t, tid("agent-1"), resp.Principal.ID) assert.Equal(t, "Agent 1", resp.Principal.DisplayName) - assert.Equal(t, "project-1", resp.Principal.ProjectID) + assert.Equal(t, tid("project-1"), resp.Principal.ProjectID) assert.NotNil(t, resp.DirectGroups) assert.NotNil(t, resp.EffectiveGroups) } @@ -164,21 +164,21 @@ func TestPrincipalResolve_Group(t *testing.T) { ctx := context.Background() group := &store.Group{ - ID: "group-1", + ID: tid("group-1"), Name: "Platform Team", Slug: "platform-team", Description: "The platform team", } require.NoError(t, s.CreateGroup(ctx, group)) - rec := doRequest(t, srv, http.MethodGet, "/api/v1/principals/group/group-1", nil) + rec := doRequest(t, srv, http.MethodGet, "/api/v1/principals/group/"+tid("group-1"), nil) assert.Equal(t, http.StatusOK, rec.Code) var resp PrincipalResolutionResponse require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp)) assert.Equal(t, "group", resp.Principal.Type) - assert.Equal(t, "group-1", resp.Principal.ID) + assert.Equal(t, tid("group-1"), resp.Principal.ID) assert.Equal(t, "Platform Team", resp.Principal.DisplayName) assert.Empty(t, resp.DirectGroups) assert.Empty(t, resp.EffectiveGroups) @@ -223,7 +223,7 @@ func TestPrincipalResolve_AgentWithCreator(t *testing.T) { // Create a user as the agent's creator user := &store.User{ - ID: "creator-1", + ID: tid("creator-1"), Email: "creator@example.com", DisplayName: "Creator User", Role: "member", @@ -232,33 +232,33 @@ func TestPrincipalResolve_AgentWithCreator(t *testing.T) { require.NoError(t, s.CreateUser(ctx, user)) project := &store.Project{ - ID: "project-1", + ID: tid("project-1"), Name: "Test Project", Slug: "test-project", } require.NoError(t, s.CreateProject(ctx, project)) agent := &store.Agent{ - ID: "agent-deleg", + ID: tid("agent-deleg"), Slug: "agent-deleg-slug", Name: "Delegated Agent", ProjectID: project.ID, Phase: string(state.PhaseRunning), - CreatedBy: "creator-1", + CreatedBy: tid("creator-1"), } require.NoError(t, s.CreateAgent(ctx, agent)) - rec := doRequest(t, srv, http.MethodGet, "/api/v1/principals/agent/agent-deleg", nil) + rec := doRequest(t, srv, http.MethodGet, "/api/v1/principals/agent/"+tid("agent-deleg"), nil) assert.Equal(t, http.StatusOK, rec.Code) var resp PrincipalResolutionResponse require.NoError(t, json.NewDecoder(rec.Body).Decode(&resp)) assert.Equal(t, "agent", resp.Principal.Type) - assert.Equal(t, "agent-deleg", resp.Principal.ID) + assert.Equal(t, tid("agent-deleg"), resp.Principal.ID) // Should include delegation info require.NotNil(t, resp.DelegatesFrom) assert.Equal(t, "user", resp.DelegatesFrom.Type) - assert.Equal(t, "creator-1", resp.DelegatesFrom.ID) + assert.Equal(t, tid("creator-1"), resp.DelegatesFrom.ID) assert.Equal(t, "Creator User", resp.DelegatesFrom.DisplayName) } diff --git a/pkg/hub/handlers_project_test.go b/pkg/hub/handlers_project_test.go index 2663f605f..44f6b3833 100644 --- a/pkg/hub/handlers_project_test.go +++ b/pkg/hub/handlers_project_test.go @@ -133,7 +133,7 @@ func TestPopulateAgentConfig_HubManagedProject_SetsWorkspace(t *testing.T) { srv, _ := testServer(t) project := &store.Project{ - ID: "project-hub-managed", + ID: tid("project-hub-managed"), Name: "Hub Managed", Slug: "hub-managed", // No GitRemote — hub-managed project @@ -183,7 +183,7 @@ func TestPopulateAgentConfig_GitProject_NoWorkspace(t *testing.T) { srv, _ := testServer(t) project := &store.Project{ - ID: "project-git", + ID: tid("project-git"), Name: "Git Project", Slug: "git-project", GitRemote: "github.com/test/repo", @@ -517,7 +517,7 @@ func TestCreateAgent_HubManagedProject_ExplicitBroker_AutoLinks(t *testing.T) { // Create a runtime broker broker := &store.RuntimeBroker{ - ID: "broker-hub-autolink", + ID: tid("broker-hub-autolink"), Slug: "hub-autolink-broker", Name: "Hub Autolink Broker", Status: store.BrokerStatusOnline, @@ -526,7 +526,7 @@ func TestCreateAgent_HubManagedProject_ExplicitBroker_AutoLinks(t *testing.T) { // Create a hub-managed project (no git remote, no default broker, no providers) project := &store.Project{ - ID: "project-hub-autolink", + ID: tid("project-hub-autolink"), Slug: "hub-autolink", Name: "Hub Autolink Project", // No GitRemote — hub-managed @@ -572,7 +572,7 @@ func TestCreateProject_HubManaged_AutoProvide(t *testing.T) { // Create a broker with auto_provide enabled broker := &store.RuntimeBroker{ - ID: "broker-autoprovide", + ID: tid("broker-autoprovide"), Slug: "autoprovide-broker", Name: "Auto Provide Broker", Status: store.BrokerStatusOnline, @@ -673,15 +673,15 @@ func TestDeleteProject_DeleteAgents_DispatchesToBroker(t *testing.T) { disp := &deleteDispatcher{} srv.SetDispatcher(disp) - project, _, _ := setupOnlineBrokerAgent(t, s, "project-del") + project, broker, agent1 := setupOnlineBrokerAgent(t, s, "project-del") // Create a second agent in the same project agent2 := &store.Agent{ - ID: "agent-online-project-del-2", + ID: tid("agent-online-project-del-2"), Slug: "agent-online-project-del-2-slug", Name: "Agent Online project-del 2", ProjectID: project.ID, - RuntimeBrokerID: "broker-online-project-del", + RuntimeBrokerID: broker.ID, Phase: string(state.PhaseRunning), } require.NoError(t, s.CreateAgent(ctx, agent2)) @@ -700,7 +700,7 @@ func TestDeleteProject_DeleteAgents_DispatchesToBroker(t *testing.T) { assert.ErrorIs(t, err, store.ErrNotFound) // Verify agents cascade-deleted from database - _, err = s.GetAgent(ctx, "agent-online-project-del") + _, err = s.GetAgent(ctx, agent1.ID) assert.ErrorIs(t, err, store.ErrNotFound) _, err = s.GetAgent(ctx, agent2.ID) assert.ErrorIs(t, err, store.ErrNotFound) @@ -734,7 +734,7 @@ func TestCreateAgent_HubManagedProject_NoProviders_NoBroker(t *testing.T) { // Create a hub-managed project with no providers project := &store.Project{ - ID: "project-hub-noproviders", + ID: tid("project-hub-noproviders"), Slug: "hub-noproviders", Name: "No Providers Project", } @@ -761,7 +761,7 @@ func TestAutoLinkProviders_HubManagedProject_NoLocalPath(t *testing.T) { // Create a broker with auto_provide enabled broker := &store.RuntimeBroker{ - ID: "broker-localpath-auto", + ID: tid("broker-localpath-auto"), Slug: "localpath-auto-broker", Name: "LocalPath Auto Broker", Status: store.BrokerStatusOnline, @@ -803,7 +803,7 @@ func TestAutoLinkProviders_GitProject_NoLocalPath(t *testing.T) { // Create a broker with auto_provide enabled broker := &store.RuntimeBroker{ - ID: "broker-localpath-git", + ID: tid("broker-localpath-git"), Slug: "localpath-git-broker", Name: "LocalPath Git Broker", Status: store.BrokerStatusOnline, @@ -839,7 +839,7 @@ func TestDeleteProject_HubManaged_DispatchesCleanupToBrokers(t *testing.T) { // Create a hub-managed project project := &store.Project{ - ID: "project-cleanup-dispatch", + ID: tid("project-cleanup-dispatch"), Slug: "cleanup-dispatch", Name: "Cleanup Dispatch Project", // No GitRemote — hub-managed @@ -848,14 +848,14 @@ func TestDeleteProject_HubManaged_DispatchesCleanupToBrokers(t *testing.T) { // Create two brokers broker1 := &store.RuntimeBroker{ - ID: "broker-cleanup-1", + ID: tid("broker-cleanup-1"), Slug: "cleanup-broker-1", Name: "Cleanup Broker 1", Status: store.BrokerStatusOnline, Endpoint: "http://broker1:9800", } broker2 := &store.RuntimeBroker{ - ID: "broker-cleanup-2", + ID: tid("broker-cleanup-2"), Slug: "cleanup-broker-2", Name: "Cleanup Broker 2", Status: store.BrokerStatusOnline, @@ -866,14 +866,16 @@ func TestDeleteProject_HubManaged_DispatchesCleanupToBrokers(t *testing.T) { // Link both as providers require.NoError(t, s.AddProjectProvider(ctx, &store.ProjectProvider{ - ProjectID: project.ID, - BrokerID: broker1.ID, - LinkedBy: "test", + ProjectID: project.ID, + BrokerID: broker1.ID, + BrokerName: broker1.Name, + LinkedBy: "test", })) require.NoError(t, s.AddProjectProvider(ctx, &store.ProjectProvider{ - ProjectID: project.ID, - BrokerID: broker2.ID, - LinkedBy: "test", + ProjectID: project.ID, + BrokerID: broker2.ID, + BrokerName: broker2.Name, + LinkedBy: "test", })) // Set up a mock client and dispatcher @@ -902,7 +904,7 @@ func TestDeleteProject_HubManaged_SkipsEmbeddedBroker(t *testing.T) { // Create a hub-managed project project := &store.Project{ - ID: "project-cleanup-embedded", + ID: tid("project-cleanup-embedded"), Slug: "cleanup-embedded", Name: "Cleanup Embedded Project", } @@ -910,14 +912,14 @@ func TestDeleteProject_HubManaged_SkipsEmbeddedBroker(t *testing.T) { // Create embedded and remote brokers embeddedBroker := &store.RuntimeBroker{ - ID: "broker-embedded", + ID: tid("broker-embedded"), Slug: "embedded-broker", Name: "Embedded Broker", Status: store.BrokerStatusOnline, Endpoint: "http://localhost:9800", } remoteBroker := &store.RuntimeBroker{ - ID: "broker-remote", + ID: tid("broker-remote"), Slug: "remote-broker", Name: "Remote Broker", Status: store.BrokerStatusOnline, @@ -928,14 +930,16 @@ func TestDeleteProject_HubManaged_SkipsEmbeddedBroker(t *testing.T) { // Link both as providers require.NoError(t, s.AddProjectProvider(ctx, &store.ProjectProvider{ - ProjectID: project.ID, - BrokerID: embeddedBroker.ID, - LinkedBy: "test", + ProjectID: project.ID, + BrokerID: embeddedBroker.ID, + BrokerName: embeddedBroker.Name, + LinkedBy: "test", })) require.NoError(t, s.AddProjectProvider(ctx, &store.ProjectProvider{ - ProjectID: project.ID, - BrokerID: remoteBroker.ID, - LinkedBy: "test", + ProjectID: project.ID, + BrokerID: remoteBroker.ID, + BrokerName: remoteBroker.Name, + LinkedBy: "test", })) // Mark embedded broker @@ -963,7 +967,7 @@ func TestDeleteProject_GitBacked_NoCleanupDispatched(t *testing.T) { // Create a git-backed project project := &store.Project{ - ID: "project-git-nocleanup", + ID: tid("project-git-nocleanup"), Slug: "git-nocleanup", Name: "Git No Cleanup Project", GitRemote: "github.com/test/nocleanup", @@ -972,7 +976,7 @@ func TestDeleteProject_GitBacked_NoCleanupDispatched(t *testing.T) { // Create a broker and link as provider broker := &store.RuntimeBroker{ - ID: "broker-git-nocleanup", + ID: tid("broker-git-nocleanup"), Slug: "git-nocleanup-broker", Name: "Git NoCleanup Broker", Status: store.BrokerStatusOnline, @@ -980,9 +984,10 @@ func TestDeleteProject_GitBacked_NoCleanupDispatched(t *testing.T) { } require.NoError(t, s.CreateRuntimeBroker(ctx, broker)) require.NoError(t, s.AddProjectProvider(ctx, &store.ProjectProvider{ - ProjectID: project.ID, - BrokerID: broker.ID, - LinkedBy: "test", + ProjectID: project.ID, + BrokerID: broker.ID, + BrokerName: broker.Name, + LinkedBy: "test", })) // Set up mock client and dispatcher @@ -1007,7 +1012,7 @@ func TestResolveRuntimeBroker_HubManagedProject_NoLocalPath(t *testing.T) { // Create a runtime broker (not auto-provide — will be explicitly selected) broker := &store.RuntimeBroker{ - ID: "broker-resolve-localpath", + ID: tid("broker-resolve-localpath"), Slug: "resolve-localpath-broker", Name: "Resolve LocalPath Broker", Status: store.BrokerStatusOnline, @@ -1016,7 +1021,7 @@ func TestResolveRuntimeBroker_HubManagedProject_NoLocalPath(t *testing.T) { // Create a hub-managed project with no providers project := &store.Project{ - ID: "project-resolve-localpath", + ID: tid("project-resolve-localpath"), Slug: "resolve-localpath", Name: "Resolve LocalPath Project", } @@ -1050,7 +1055,7 @@ func TestProjectRegisterPreservesProviderLocalPath(t *testing.T) { // Create a broker broker := &store.RuntimeBroker{ - ID: "broker-preserve-path", + ID: tid("broker-preserve-path"), Name: "Preserve Path Broker", Slug: "preserve-path-broker", Status: store.BrokerStatusOnline, @@ -1355,13 +1360,16 @@ func TestProjectRegister_ExistingProject_CreatesMembershipGroup(t *testing.T) { ctx := context.Background() // Create a project directly in the store (simulating one created before - // membership group support was added — no group exists yet). + // membership group support was added — no group exists yet). The creator is + // backfilled as a group owner, so it must reference an existing user. + creatorID := tid("original-creator-id") + permSeedUser(t, ctx, s, creatorID) project := &store.Project{ ID: api.NewUUID(), Name: "Pre-Existing Project", Slug: "pre-existing-project", GitRemote: "github.com/test/pre-existing", - CreatedBy: "original-creator-id", + CreatedBy: creatorID, } require.NoError(t, s.CreateProject(ctx, project)) @@ -1396,7 +1404,7 @@ func TestProjectRegister_ExistingProject_CreatesMembershipGroup(t *testing.T) { ownerIDs[m.MemberID] = true } } - assert.True(t, ownerIDs["original-creator-id"], "original creator should be an owner") + assert.True(t, ownerIDs[creatorID], "original creator should be an owner") assert.True(t, ownerIDs[DevUserID], "linking user should be an owner") } @@ -1477,7 +1485,7 @@ func TestPopulateAgentConfig_SharedWorkspace_SetsWorkspaceNotClone(t *testing.T) srv, _ := testServer(t) project := &store.Project{ - ID: "project-shared-ws", + ID: tid("project-shared-ws"), Name: "Shared WS", Slug: "shared-ws", GitRemote: "github.com/test/shared", @@ -1672,7 +1680,7 @@ func TestResolveCloneToken_FallsBackToCreatorUserToken(t *testing.T) { ctx := context.Background() require.NoError(t, st.CreateSecret(ctx, &store.Secret{ - ID: "sec-user-gh", + ID: tid("sec-user-gh"), Key: "GITHUB_TOKEN", EncryptedValue: "ghp_user_token_123", SecretType: store.SecretTypeEnvironment, @@ -1699,7 +1707,7 @@ func TestResolveCloneToken_PrefersProjectTokenOverUserToken(t *testing.T) { ctx := context.Background() require.NoError(t, st.CreateSecret(ctx, &store.Secret{ - ID: "sec-project-gh", + ID: tid("sec-project-gh"), Key: "GITHUB_TOKEN", EncryptedValue: "ghp_project_token", SecretType: store.SecretTypeEnvironment, @@ -1708,7 +1716,7 @@ func TestResolveCloneToken_PrefersProjectTokenOverUserToken(t *testing.T) { ScopeID: "project-with-both", })) require.NoError(t, st.CreateSecret(ctx, &store.Secret{ - ID: "sec-user-gh-2", + ID: tid("sec-user-gh-2"), Key: "GITHUB_TOKEN", EncryptedValue: "ghp_user_token", SecretType: store.SecretTypeEnvironment, @@ -1804,7 +1812,7 @@ func TestAutoAssociateGitHubInstallation_NoMatch(t *testing.T) { require.NoError(t, st.CreateGitHubInstallation(ctx, inst)) project := &store.Project{ - ID: "project-no-match", + ID: tid("project-no-match"), Name: "No Match", Slug: "no-match", GitRemote: "github.com/myorg/myrepo", @@ -1833,7 +1841,7 @@ func TestAutoAssociateGitHubInstallation_SkipsSuspended(t *testing.T) { require.NoError(t, st.CreateGitHubInstallation(ctx, inst)) project := &store.Project{ - ID: "project-suspended", + ID: tid("project-suspended"), Name: "Suspended", Slug: "suspended", GitRemote: "github.com/myorg/myrepo", @@ -1924,8 +1932,8 @@ func TestCreateProject_ListByGitRemote_ReturnsMultiple(t *testing.T) { // Pre-create two projects for the same git remote. for _, g := range []*store.Project{ - {ID: "g1", Name: "widgets", Slug: "widgets", GitRemote: "github.com/acme/widgets"}, - {ID: "g2", Name: "widgets (1)", Slug: "widgets-1", GitRemote: "github.com/acme/widgets"}, + {ID: tid("g1"), Name: "widgets", Slug: "widgets", GitRemote: "github.com/acme/widgets"}, + {ID: tid("g2"), Name: "widgets (1)", Slug: "widgets-1", GitRemote: "github.com/acme/widgets"}, } { require.NoError(t, s.CreateProject(ctx, g)) } diff --git a/pkg/hub/handlers_scheduled_events_test.go b/pkg/hub/handlers_scheduled_events_test.go index e97955945..71b85b6a7 100644 --- a/pkg/hub/handlers_scheduled_events_test.go +++ b/pkg/hub/handlers_scheduled_events_test.go @@ -39,7 +39,7 @@ func setupScheduledEventTest(t *testing.T) (*Server, store.Store, string) { srv.scheduler.RegisterEventHandler("message", srv.messageEventHandler()) project := &store.Project{ - ID: "project-sched-test", + ID: tid("project-sched-test"), Name: "Scheduler Test Project", Slug: "sched-test-project", } @@ -200,7 +200,7 @@ func TestScheduledEvent_List(t *testing.T) { // Create a couple of events directly in the store for i, status := range []string{store.ScheduledEventPending, store.ScheduledEventFired} { evt := &store.ScheduledEvent{ - ID: "list-evt-" + string(rune('a'+i)), + ID: tid("list-evt-" + string(rune('a'+i))), ProjectID: projectID, EventType: "message", FireAt: time.Now().Add(time.Duration(i+1) * time.Hour), @@ -234,7 +234,7 @@ func TestScheduledEvent_Get(t *testing.T) { ctx := context.Background() evt := &store.ScheduledEvent{ - ID: "get-evt-1", + ID: tid("get-evt-1"), ProjectID: projectID, EventType: "message", FireAt: time.Now().Add(1 * time.Hour), @@ -244,12 +244,12 @@ func TestScheduledEvent_Get(t *testing.T) { } require.NoError(t, s.CreateScheduledEvent(ctx, evt)) - rec := doRequest(t, srv, http.MethodGet, "/api/v1/projects/"+projectID+"/scheduled-events/get-evt-1", nil) + rec := doRequest(t, srv, http.MethodGet, "/api/v1/projects/"+projectID+"/scheduled-events/"+tid("get-evt-1")+"", nil) assert.Equal(t, http.StatusOK, rec.Code) var got store.ScheduledEvent require.NoError(t, json.NewDecoder(rec.Body).Decode(&got)) - assert.Equal(t, "get-evt-1", got.ID) + assert.Equal(t, tid("get-evt-1"), got.ID) assert.Equal(t, "message", got.EventType) } @@ -266,7 +266,7 @@ func TestScheduledEvent_GetWrongProject(t *testing.T) { // Create a second project project2 := &store.Project{ - ID: "project-sched-other", + ID: tid("project-sched-other"), Name: "Other Project", Slug: "other-project", } @@ -274,7 +274,7 @@ func TestScheduledEvent_GetWrongProject(t *testing.T) { // Create event in first project evt := &store.ScheduledEvent{ - ID: "wrong-project-evt", + ID: tid("wrong-project-evt"), ProjectID: projectID, EventType: "message", FireAt: time.Now().Add(1 * time.Hour), @@ -294,7 +294,7 @@ func TestScheduledEvent_Cancel(t *testing.T) { ctx := context.Background() evt := &store.ScheduledEvent{ - ID: "cancel-evt-1", + ID: tid("cancel-evt-1"), ProjectID: projectID, EventType: "message", FireAt: time.Now().Add(1 * time.Hour), @@ -304,11 +304,11 @@ func TestScheduledEvent_Cancel(t *testing.T) { } require.NoError(t, s.CreateScheduledEvent(ctx, evt)) - rec := doRequest(t, srv, http.MethodDelete, "/api/v1/projects/"+projectID+"/scheduled-events/cancel-evt-1", nil) + rec := doRequest(t, srv, http.MethodDelete, "/api/v1/projects/"+projectID+"/scheduled-events/"+tid("cancel-evt-1")+"", nil) assert.Equal(t, http.StatusNoContent, rec.Code) // Verify it was cancelled in the store - got, err := s.GetScheduledEvent(ctx, "cancel-evt-1") + got, err := s.GetScheduledEvent(ctx, tid("cancel-evt-1")) require.NoError(t, err) assert.Equal(t, store.ScheduledEventCancelled, got.Status) } diff --git a/pkg/hub/handlers_schedules_test.go b/pkg/hub/handlers_schedules_test.go index 0ab9c88a7..4d63c8172 100644 --- a/pkg/hub/handlers_schedules_test.go +++ b/pkg/hub/handlers_schedules_test.go @@ -37,7 +37,7 @@ func setupScheduleTest(t *testing.T) (*Server, store.Store, string) { srv.scheduler.RegisterEventHandler("message", srv.messageEventHandler()) project := &store.Project{ - ID: "project-sched-recurring", + ID: tid("project-sched-recurring"), Name: "Schedule Test Project", Slug: "schedule-test-project", } @@ -251,7 +251,7 @@ func TestSchedule_History(t *testing.T) { // Create some events linked to this schedule for i := 0; i < 3; i++ { evt := &store.ScheduledEvent{ - ID: "hist-evt-" + string(rune('a'+i)), + ID: tid("hist-evt-" + string(rune('a'+i))), ProjectID: projectID, EventType: "message", FireAt: created.CreatedAt, @@ -276,7 +276,7 @@ func TestSchedule_ProjectIsolation(t *testing.T) { // Create another project otherProject := &store.Project{ - ID: "project-other-sched", + ID: tid("project-other-sched"), Name: "Other Project", Slug: "other-project-sched", } diff --git a/pkg/hub/handlers_stopall_test.go b/pkg/hub/handlers_stopall_test.go index e46757af9..882d201c2 100644 --- a/pkg/hub/handlers_stopall_test.go +++ b/pkg/hub/handlers_stopall_test.go @@ -35,14 +35,14 @@ func TestStopAllAgents_Global(t *testing.T) { // Create a project project := &store.Project{ - ID: "project-1", + ID: tid("project-1"), Name: "Test Project", Slug: "test-project", } require.NoError(t, s.CreateProject(ctx, project)) // Create running agents - for i, name := range []string{"agent-1", "agent-2", "agent-3"} { + for i, name := range []string{tid("agent-1"), tid("agent-2"), tid("agent-3")} { agent := &store.Agent{ ID: name, Slug: name, @@ -69,7 +69,7 @@ func TestStopAllAgents_Global(t *testing.T) { assert.Equal(t, 2, resp.Total) // Verify agents are stopped in store - for _, name := range []string{"agent-1", "agent-2"} { + for _, name := range []string{tid("agent-1"), tid("agent-2")} { agent, err := s.GetAgent(ctx, name) require.NoError(t, err) assert.Equal(t, string(state.PhaseStopped), agent.Phase) @@ -102,27 +102,27 @@ func TestStopAllAgents_ProjectScoped(t *testing.T) { ctx := context.Background() // Create two projects - project1 := &store.Project{ID: "project-1", Name: "Project 1", Slug: "project-1"} - project2 := &store.Project{ID: "project-2", Name: "Project 2", Slug: "project-2"} + project1 := &store.Project{ID: tid("project-1"), Name: "Project 1", Slug: tid("project-1")} + project2 := &store.Project{ID: tid("project-2"), Name: "Project 2", Slug: tid("project-2")} require.NoError(t, s.CreateProject(ctx, project1)) require.NoError(t, s.CreateProject(ctx, project2)) // Create running agents in both projects require.NoError(t, s.CreateAgent(ctx, &store.Agent{ - ID: "g1-agent-1", Slug: "g1-agent-1", Name: "G1 Agent 1", + ID: tid("g1-agent-1"), Slug: tid("g1-agent-1"), Name: "G1 Agent 1", ProjectID: project1.ID, Phase: string(state.PhaseRunning), })) require.NoError(t, s.CreateAgent(ctx, &store.Agent{ - ID: "g1-agent-2", Slug: "g1-agent-2", Name: "G1 Agent 2", + ID: tid("g1-agent-2"), Slug: tid("g1-agent-2"), Name: "G1 Agent 2", ProjectID: project1.ID, Phase: string(state.PhaseRunning), })) require.NoError(t, s.CreateAgent(ctx, &store.Agent{ - ID: "g2-agent-1", Slug: "g2-agent-1", Name: "G2 Agent 1", + ID: tid("g2-agent-1"), Slug: tid("g2-agent-1"), Name: "G2 Agent 1", ProjectID: project2.ID, Phase: string(state.PhaseRunning), })) t.Run("stops only agents in scoped project", func(t *testing.T) { - rec := doRequest(t, srv, http.MethodPost, "/api/v1/projects/project-1/agents/stop-all", nil) + rec := doRequest(t, srv, http.MethodPost, "/api/v1/projects/"+project1.ID+"/agents/stop-all", nil) assert.Equal(t, http.StatusOK, rec.Code) var resp StopAllAgentsResponse @@ -133,11 +133,11 @@ func TestStopAllAgents_ProjectScoped(t *testing.T) { assert.Equal(t, 2, resp.Total) // Verify project-1 agents are stopped - a1, _ := s.GetAgent(ctx, "g1-agent-1") + a1, _ := s.GetAgent(ctx, tid("g1-agent-1")) assert.Equal(t, string(state.PhaseStopped), a1.Phase) // Verify project-2 agent is still running - a2, _ := s.GetAgent(ctx, "g2-agent-1") + a2, _ := s.GetAgent(ctx, tid("g2-agent-1")) assert.Equal(t, string(state.PhaseRunning), a2.Phase) }) } @@ -166,13 +166,14 @@ func TestStopAllAgents_ProjectOwner_StopsAllAgents(t *testing.T) { ctx := context.Background() // Create running agents owned by different users + permSeedUser(t, ctx, s, tid("user-other")) require.NoError(t, s.CreateAgent(ctx, &store.Agent{ - ID: "alice-agent", Slug: "alice-agent", Name: "Alice Agent", + ID: tid("alice-agent"), Slug: tid("alice-agent"), Name: "Alice Agent", ProjectID: project.ID, OwnerID: alice.ID, Phase: string(state.PhaseRunning), })) require.NoError(t, s.CreateAgent(ctx, &store.Agent{ - ID: "other-agent", Slug: "other-agent", Name: "Other Agent", - ProjectID: project.ID, OwnerID: "user-other", Phase: string(state.PhaseRunning), + ID: tid("other-agent"), Slug: tid("other-agent"), Name: "Other Agent", + ProjectID: project.ID, OwnerID: tid("user-other"), Phase: string(state.PhaseRunning), })) // Alice is project owner — should stop ALL agents, scope = "all" @@ -188,9 +189,9 @@ func TestStopAllAgents_ProjectOwner_StopsAllAgents(t *testing.T) { assert.Equal(t, "all", resp.Scope) // Verify both agents are stopped - a1, _ := s.GetAgent(ctx, "alice-agent") + a1, _ := s.GetAgent(ctx, tid("alice-agent")) assert.Equal(t, string(state.PhaseStopped), a1.Phase) - a2, _ := s.GetAgent(ctx, "other-agent") + a2, _ := s.GetAgent(ctx, tid("other-agent")) assert.Equal(t, string(state.PhaseStopped), a2.Phase) } @@ -200,7 +201,7 @@ func TestStopAllAgents_ProjectMember_StopsOnlyOwnAgents(t *testing.T) { // Create a third user "carol" as a regular project member carol := &store.User{ - ID: "user-carol", + ID: tid("user-carol"), Email: "carol@test.com", DisplayName: "Carol", Role: store.UserRoleMember, @@ -222,16 +223,16 @@ func TestStopAllAgents_ProjectMember_StopsOnlyOwnAgents(t *testing.T) { // Create agents owned by carol and by alice require.NoError(t, s.CreateAgent(ctx, &store.Agent{ - ID: "carol-agent-1", Slug: "carol-agent-1", Name: "Carol Agent 1", + ID: tid("carol-agent-1"), Slug: tid("carol-agent-1"), Name: "Carol Agent 1", ProjectID: project.ID, OwnerID: carol.ID, Phase: string(state.PhaseRunning), })) require.NoError(t, s.CreateAgent(ctx, &store.Agent{ - ID: "carol-agent-2", Slug: "carol-agent-2", Name: "Carol Agent 2", + ID: tid("carol-agent-2"), Slug: tid("carol-agent-2"), Name: "Carol Agent 2", ProjectID: project.ID, OwnerID: carol.ID, Phase: string(state.PhaseRunning), })) require.NoError(t, s.CreateAgent(ctx, &store.Agent{ - ID: "alice-agent", Slug: "alice-agent", Name: "Alice Agent", - ProjectID: project.ID, OwnerID: "user-alice", Phase: string(state.PhaseRunning), + ID: tid("alice-agent"), Slug: tid("alice-agent"), Name: "Alice Agent", + ProjectID: project.ID, OwnerID: tid("user-alice"), Phase: string(state.PhaseRunning), })) // Carol (regular member) should only stop her own agents, scope = "own" @@ -247,13 +248,13 @@ func TestStopAllAgents_ProjectMember_StopsOnlyOwnAgents(t *testing.T) { assert.Equal(t, "own", resp.Scope) // Verify carol's agents are stopped - c1, _ := s.GetAgent(ctx, "carol-agent-1") + c1, _ := s.GetAgent(ctx, tid("carol-agent-1")) assert.Equal(t, string(state.PhaseStopped), c1.Phase) - c2, _ := s.GetAgent(ctx, "carol-agent-2") + c2, _ := s.GetAgent(ctx, tid("carol-agent-2")) assert.Equal(t, string(state.PhaseStopped), c2.Phase) // Verify alice's agent is still running - a1, _ := s.GetAgent(ctx, "alice-agent") + a1, _ := s.GetAgent(ctx, tid("alice-agent")) assert.Equal(t, string(state.PhaseRunning), a1.Phase) } @@ -263,7 +264,7 @@ func TestStopAllAgents_NonMember_Forbidden(t *testing.T) { // Create a running agent in the project require.NoError(t, s.CreateAgent(ctx, &store.Agent{ - ID: "agent-1", Slug: "agent-1", Name: "Agent 1", + ID: tid("agent-1"), Slug: tid("agent-1"), Name: "Agent 1", ProjectID: project.ID, Phase: string(state.PhaseRunning), })) @@ -273,7 +274,7 @@ func TestStopAllAgents_NonMember_Forbidden(t *testing.T) { assert.Equal(t, http.StatusForbidden, rec.Code) // Agent should still be running - a, _ := s.GetAgent(ctx, "agent-1") + a, _ := s.GetAgent(ctx, tid("agent-1")) assert.Equal(t, string(state.PhaseRunning), a.Phase) } diff --git a/pkg/hub/handlers_test.go b/pkg/hub/handlers_test.go index 4b813a566..8fb4d6715 100644 --- a/pkg/hub/handlers_test.go +++ b/pkg/hub/handlers_test.go @@ -20,6 +20,7 @@ import ( "bytes" "context" "encoding/json" + "fmt" "net/http" "net/http/httptest" "strings" @@ -29,7 +30,6 @@ import ( "github.com/GoogleCloudPlatform/scion/pkg/agent/state" "github.com/GoogleCloudPlatform/scion/pkg/api" "github.com/GoogleCloudPlatform/scion/pkg/store" - "github.com/GoogleCloudPlatform/scion/pkg/store/sqlite" "github.com/go-jose/go-jose/v4/jwt" ) @@ -40,7 +40,7 @@ const testDevToken = "scion_dev_test_token_for_unit_tests_1234567890" // The server is configured with dev auth enabled using testDevToken. func testServer(t *testing.T) (*Server, store.Store) { t.Helper() - s, err := sqlite.New(":memory:") + s, err := newTestStore(":memory:") if err != nil { if strings.Contains(err.Error(), "sqlite driver not registered") { t.Skip("Skipping test because sqlite driver is not registered (build with -tags sqlite to enable)") @@ -183,7 +183,7 @@ func TestAgentList(t *testing.T) { // Create a project first (agents reference projects) project := &store.Project{ - ID: "project_test123", + ID: tid("project_test123"), Slug: "test-project", Name: "Test Project", GitRemote: "https://github.com/test/repo", @@ -197,8 +197,8 @@ func TestAgentList(t *testing.T) { // Create some test agents for i := 0; i < 3; i++ { agent := &store.Agent{ - ID: "agent_" + string(rune('a'+i)), - Slug: "test-agent-" + string(rune('a'+i)), + ID: tid("agent_" + string(rune('a'+i))), + Slug: tid("test-agent-" + string(rune('a'+i))), Name: "Test Agent " + string(rune('A'+i)), ProjectID: project.ID, Phase: string(state.PhaseStopped), @@ -237,7 +237,7 @@ func TestAgentCreate(t *testing.T) { // Create a runtime broker first broker := &store.RuntimeBroker{ - ID: "host_test123", + ID: tid("host_test123"), Slug: "test-host", Name: "Test Host", Status: store.BrokerStatusOnline, @@ -248,8 +248,8 @@ func TestAgentCreate(t *testing.T) { // Create a project with default runtime broker project := &store.Project{ - ID: "project_abc123", - Slug: "my-project", + ID: tid("project_abc123"), + Slug: tid("my-project"), Name: "My Project", GitRemote: "github.com/test/repo", DefaultRuntimeBrokerID: broker.ID, @@ -316,7 +316,7 @@ func TestAgentCreate_NoTask(t *testing.T) { // Create a runtime broker broker := &store.RuntimeBroker{ - ID: "host_notask", + ID: tid("host_notask"), Slug: "notask-host", Name: "No Task Host", Status: store.BrokerStatusOnline, @@ -327,7 +327,7 @@ func TestAgentCreate_NoTask(t *testing.T) { // Create a project with default runtime broker project := &store.Project{ - ID: "project_notask", + ID: tid("project_notask"), Slug: "notask-project", Name: "No Task Project", GitRemote: "github.com/test/notask", @@ -387,7 +387,7 @@ func TestAgentCreate_NoTaskViaProject(t *testing.T) { // Create a runtime broker broker := &store.RuntimeBroker{ - ID: "host_notask_project", + ID: tid("host_notask_project"), Slug: "notask-project-host", Name: "No Task Project Host", Status: store.BrokerStatusOnline, @@ -398,7 +398,7 @@ func TestAgentCreate_NoTaskViaProject(t *testing.T) { // Create a project with default runtime broker project := &store.Project{ - ID: "project_notask_project", + ID: tid("project_notask_project"), Slug: "notask-project-ep", Name: "No Task Project EP", GitRemote: "github.com/test/notask-project", @@ -454,7 +454,7 @@ func TestAgentCreate_AttachNoTask(t *testing.T) { // Create a runtime broker broker := &store.RuntimeBroker{ - ID: "host_attach", + ID: tid("host_attach"), Slug: "attach-host", Name: "Attach Host", Status: store.BrokerStatusOnline, @@ -465,7 +465,7 @@ func TestAgentCreate_AttachNoTask(t *testing.T) { // Create a project with default runtime broker project := &store.Project{ - ID: "project_attach", + ID: tid("project_attach"), Slug: "attach-project", Name: "Attach Project", GitRemote: "github.com/test/attach", @@ -525,7 +525,7 @@ func TestAgentCreate_SingleProvider(t *testing.T) { // Create a runtime broker broker := &store.RuntimeBroker{ - ID: "host_single", + ID: tid("host_single"), Slug: "single-host", Name: "Single Host", Status: store.BrokerStatusOnline, @@ -536,7 +536,7 @@ func TestAgentCreate_SingleProvider(t *testing.T) { // Create a project WITHOUT a default runtime broker project := &store.Project{ - ID: "project_single", + ID: tid("project_single"), Slug: "single-project", Name: "Single Project", GitRemote: "github.com/test/single", @@ -589,7 +589,7 @@ func TestAgentCreate_SingleOfflineProvider(t *testing.T) { ctx := context.Background() broker := &store.RuntimeBroker{ - ID: "host_single_offline", + ID: tid("host_single_offline"), Slug: "single-host-offline", Name: "Single Host Offline", Status: store.BrokerStatusOffline, @@ -599,7 +599,7 @@ func TestAgentCreate_SingleOfflineProvider(t *testing.T) { } project := &store.Project{ - ID: "project_single_offline", + ID: tid("project_single_offline"), Slug: "single-project-offline", Name: "Single Project Offline", GitRemote: "github.com/test/single-offline", @@ -647,7 +647,7 @@ func TestAgentCreate_MultipleProviders(t *testing.T) { // Create two runtime brokers broker1 := &store.RuntimeBroker{ - ID: "host_multi1", + ID: tid("host_multi1"), Slug: "multi-host-1", Name: "Multi Host 1", Status: store.BrokerStatusOnline, @@ -657,7 +657,7 @@ func TestAgentCreate_MultipleProviders(t *testing.T) { } broker2 := &store.RuntimeBroker{ - ID: "host_multi2", + ID: tid("host_multi2"), Slug: "multi-host-2", Name: "Multi Host 2", Status: store.BrokerStatusOnline, @@ -668,7 +668,7 @@ func TestAgentCreate_MultipleProviders(t *testing.T) { // Create a project WITHOUT a default runtime broker project := &store.Project{ - ID: "project_multi", + ID: tid("project_multi"), Slug: "multi-project", Name: "Multi Project", GitRemote: "github.com/test/multi", @@ -739,7 +739,7 @@ func TestAgentGetByID(t *testing.T) { // Create project and agent project := &store.Project{ - ID: "project_xyz", + ID: tid("project_xyz"), Slug: "project-xyz", Name: "Project XYZ", GitRemote: "https://github.com/test/repo", @@ -751,7 +751,7 @@ func TestAgentGetByID(t *testing.T) { } agent := &store.Agent{ - ID: "agent_test1", + ID: tid("agent_test1"), Slug: "test-agent", Name: "Test Agent", ProjectID: project.ID, @@ -764,7 +764,7 @@ func TestAgentGetByID(t *testing.T) { t.Fatalf("failed to create agent: %v", err) } - rec := doRequest(t, srv, http.MethodGet, "/api/v1/agents/agent_test1", nil) + rec := doRequest(t, srv, http.MethodGet, fmt.Sprintf("/api/v1/agents/%s", tid("agent_test1")), nil) if rec.Code != http.StatusOK { t.Errorf("expected status 200, got %d: %s", rec.Code, rec.Body.String()) @@ -775,7 +775,7 @@ func TestAgentGetByID(t *testing.T) { t.Fatalf("failed to decode response: %v", err) } - if resp.ID != "agent_test1" { + if resp.ID != tid("agent_test1") { t.Errorf("expected ID 'agent_test1', got %q", resp.ID) } } @@ -805,7 +805,7 @@ func TestAgentDelete(t *testing.T) { // Create project and agent project := &store.Project{ - ID: "project_del", + ID: tid("project_del"), Slug: "project-del", Name: "Project Del", GitRemote: "https://github.com/test/repo", @@ -817,7 +817,7 @@ func TestAgentDelete(t *testing.T) { } agent := &store.Agent{ - ID: "agent_delete", + ID: tid("agent_delete"), Slug: "delete-me", Name: "Delete Me", ProjectID: project.ID, @@ -830,14 +830,14 @@ func TestAgentDelete(t *testing.T) { t.Fatalf("failed to create agent: %v", err) } - rec := doRequest(t, srv, http.MethodDelete, "/api/v1/agents/agent_delete", nil) + rec := doRequest(t, srv, http.MethodDelete, fmt.Sprintf("/api/v1/agents/%s", tid("agent_delete")), nil) if rec.Code != http.StatusNoContent { t.Errorf("expected status 204, got %d: %s", rec.Code, rec.Body.String()) } // Verify agent is deleted - _, err := s.GetAgent(ctx, "agent_delete") + _, err := s.GetAgent(ctx, tid("agent_delete")) if err == nil { t.Error("expected agent to be deleted") } @@ -853,8 +853,8 @@ func TestProjectList(t *testing.T) { for i := 0; i < 2; i++ { project := &store.Project{ - ID: "project_" + string(rune('a'+i)), - Slug: "project-" + string(rune('a'+i)), + ID: tid("project_" + string(rune('a'+i))), + Slug: tid("project-" + string(rune('a'+i))), Name: "Project " + string(rune('A'+i)), GitRemote: "https://github.com/test/repo" + string(rune('a'+i)), Created: time.Now(), @@ -1014,13 +1014,13 @@ func TestProjectRegisterMultipleGitRemoteMatches(t *testing.T) { // Pre-create two projects for the same git remote. project1 := &store.Project{ - ID: "project-1", + ID: tid("project-1"), Name: "widgets", Slug: "widgets", GitRemote: "github.com/acme/widgets", } project2 := &store.Project{ - ID: "project-2", + ID: tid("project-2"), Name: "widgets (2)", Slug: "widgets-2", GitRemote: "github.com/acme/widgets", @@ -1062,7 +1062,7 @@ func TestProjectRegisterMultipleGitRemoteMatches(t *testing.T) { for _, m := range resp.Matches { matchIDs[m.ID] = true } - if !matchIDs["project-1"] || !matchIDs["project-2"] { + if !matchIDs[tid("project-1")] || !matchIDs[tid("project-2")] { t.Errorf("expected matches to include project-1 and project-2, got %v", resp.Matches) } @@ -1136,7 +1136,7 @@ func TestProjectRegisterWithBrokerID(t *testing.T) { // First, create a broker directly (simulating Phase 1 + 2 of two-phase flow) broker := &store.RuntimeBroker{ - ID: "host_twophase_test", + ID: tid("host_twophase_test"), Name: "Two Phase Test Host", Slug: "two-phase-test-host", Status: store.BrokerStatusOnline, @@ -1230,7 +1230,7 @@ func TestAddProvider(t *testing.T) { // Create a project project := &store.Project{ - ID: "project_contrib_test", + ID: tid("project_contrib_test"), Slug: "contrib-test", Name: "Provider Test Project", GitRemote: "https://github.com/test/contrib-test", @@ -1243,7 +1243,7 @@ func TestAddProvider(t *testing.T) { // Create a broker broker := &store.RuntimeBroker{ - ID: "host_contrib_test", + ID: tid("host_contrib_test"), Name: "Provider Test Host", Slug: "contrib-test-host", Status: store.BrokerStatusOnline, @@ -1295,7 +1295,7 @@ func TestListProviders(t *testing.T) { // Create a project project := &store.Project{ - ID: "project_list_contrib", + ID: tid("project_list_contrib"), Slug: "list-contrib", Name: "List Providers Project", Created: time.Now(), @@ -1307,7 +1307,7 @@ func TestListProviders(t *testing.T) { // Create and add a broker as provider broker := &store.RuntimeBroker{ - ID: "host_list_contrib", + ID: tid("host_list_contrib"), Name: "List Providers Host", Slug: "list-contrib-host", Status: store.BrokerStatusOnline, @@ -1352,7 +1352,7 @@ func TestProjectGetByID(t *testing.T) { ctx := context.Background() project := &store.Project{ - ID: "project_gettest", + ID: tid("project_gettest"), Slug: "get-test", Name: "Get Test", GitRemote: "https://github.com/test/get-test", @@ -1363,7 +1363,7 @@ func TestProjectGetByID(t *testing.T) { t.Fatalf("failed to create project: %v", err) } - rec := doRequest(t, srv, http.MethodGet, "/api/v1/projects/project_gettest", nil) + rec := doRequest(t, srv, http.MethodGet, fmt.Sprintf("/api/v1/projects/%s", tid("project_gettest")), nil) if rec.Code != http.StatusOK { t.Errorf("expected status 200, got %d: %s", rec.Code, rec.Body.String()) @@ -1374,7 +1374,7 @@ func TestProjectGetByID(t *testing.T) { t.Fatalf("failed to decode response: %v", err) } - if resp.ID != "project_gettest" { + if resp.ID != tid("project_gettest") { t.Errorf("expected ID 'project_gettest', got %q", resp.ID) } } @@ -1388,7 +1388,7 @@ func TestRuntimeBrokerList(t *testing.T) { ctx := context.Background() broker := &store.RuntimeBroker{ - ID: "host_test1", + ID: tid("host_test1"), Name: "Test Host", Slug: "test-host", Status: store.BrokerStatusOnline, @@ -1422,7 +1422,7 @@ func TestRuntimeBrokerListByName(t *testing.T) { // Create two brokers with different names broker1 := &store.RuntimeBroker{ - ID: "host_name_test1", + ID: tid("host_name_test1"), Name: "Alpha Host", Slug: "alpha-host", Status: store.BrokerStatusOnline, @@ -1431,7 +1431,7 @@ func TestRuntimeBrokerListByName(t *testing.T) { Updated: time.Now(), } broker2 := &store.RuntimeBroker{ - ID: "host_name_test2", + ID: tid("host_name_test2"), Name: "Beta Host", Slug: "beta-host", Status: store.BrokerStatusOnline, @@ -1499,7 +1499,7 @@ func TestRuntimeBrokerDeleteCascadesProviders(t *testing.T) { // Create a broker broker := &store.RuntimeBroker{ - ID: "broker_cascade_test", + ID: tid("broker_cascade_test"), Name: "Cascade Test Broker", Slug: "cascade-test-broker", Status: store.BrokerStatusOnline, @@ -1512,7 +1512,7 @@ func TestRuntimeBrokerDeleteCascadesProviders(t *testing.T) { // Create two projects, one with default_runtime_broker_id pointing to this broker project1 := &store.Project{ - ID: "project_cascade_1", + ID: tid("project_cascade_1"), Name: "Cascade Project 1", Slug: "cascade-project-1", DefaultRuntimeBrokerID: broker.ID, @@ -1520,7 +1520,7 @@ func TestRuntimeBrokerDeleteCascadesProviders(t *testing.T) { Updated: time.Now(), } project2 := &store.Project{ - ID: "project_cascade_2", + ID: tid("project_cascade_2"), Name: "Cascade Project 2", Slug: "cascade-project-2", Created: time.Now(), @@ -1599,7 +1599,7 @@ func TestRuntimeBrokerGetByID(t *testing.T) { ctx := context.Background() broker := &store.RuntimeBroker{ - ID: "host_gettest", + ID: tid("host_gettest"), Name: "Get Test Host", Slug: "get-test-host", Status: store.BrokerStatusOnline, @@ -1611,7 +1611,7 @@ func TestRuntimeBrokerGetByID(t *testing.T) { t.Fatalf("failed to create runtime broker: %v", err) } - rec := doRequest(t, srv, http.MethodGet, "/api/v1/runtime-brokers/host_gettest", nil) + rec := doRequest(t, srv, http.MethodGet, fmt.Sprintf("/api/v1/runtime-brokers/%s", tid("host_gettest")), nil) if rec.Code != http.StatusOK { t.Errorf("expected status 200, got %d: %s", rec.Code, rec.Body.String()) @@ -1622,7 +1622,7 @@ func TestRuntimeBrokerGetByID(t *testing.T) { t.Fatalf("failed to decode response: %v", err) } - if resp.ID != "host_gettest" { + if resp.ID != tid("host_gettest") { t.Errorf("expected ID 'host_gettest', got %q", resp.ID) } } @@ -1633,7 +1633,7 @@ func TestRuntimeBrokerGetByID_CreatedByName(t *testing.T) { // Create a user to be the broker creator if err := s.CreateUser(ctx, &store.User{ - ID: "user_broker_creator", + ID: tid("user_broker_creator"), Email: "creator@test.com", DisplayName: "Broker Creator", Role: "member", @@ -1643,11 +1643,11 @@ func TestRuntimeBrokerGetByID_CreatedByName(t *testing.T) { } broker := &store.RuntimeBroker{ - ID: "broker_createdby_test", + ID: tid("broker_createdby_test"), Name: "CreatedBy Test Broker", Slug: "createdby-test-broker", Status: store.BrokerStatusOnline, - CreatedBy: "user_broker_creator", + CreatedBy: tid("user_broker_creator"), LastHeartbeat: time.Now(), Created: time.Now(), Updated: time.Now(), @@ -1656,7 +1656,7 @@ func TestRuntimeBrokerGetByID_CreatedByName(t *testing.T) { t.Fatalf("failed to create runtime broker: %v", err) } - rec := doRequest(t, srv, http.MethodGet, "/api/v1/runtime-brokers/broker_createdby_test", nil) + rec := doRequest(t, srv, http.MethodGet, fmt.Sprintf("/api/v1/runtime-brokers/%s", tid("broker_createdby_test")), nil) if rec.Code != http.StatusOK { t.Fatalf("expected status 200, got %d: %s", rec.Code, rec.Body.String()) } @@ -1692,7 +1692,7 @@ func TestRuntimeBrokerGetByID_CreatedByNameFallsBackToEmail(t *testing.T) { // Create a user with no display name if err := s.CreateUser(ctx, &store.User{ - ID: "user_no_display", + ID: tid("user_no_display"), Email: "nodisplay@test.com", Role: "member", Status: "active", @@ -1701,11 +1701,11 @@ func TestRuntimeBrokerGetByID_CreatedByNameFallsBackToEmail(t *testing.T) { } broker := &store.RuntimeBroker{ - ID: "broker_email_fallback", + ID: tid("broker_email_fallback"), Name: "Email Fallback Broker", Slug: "email-fallback-broker", Status: store.BrokerStatusOnline, - CreatedBy: "user_no_display", + CreatedBy: tid("user_no_display"), LastHeartbeat: time.Now(), Created: time.Now(), Updated: time.Now(), @@ -1714,7 +1714,7 @@ func TestRuntimeBrokerGetByID_CreatedByNameFallsBackToEmail(t *testing.T) { t.Fatalf("failed to create runtime broker: %v", err) } - rec := doRequest(t, srv, http.MethodGet, "/api/v1/runtime-brokers/broker_email_fallback", nil) + rec := doRequest(t, srv, http.MethodGet, fmt.Sprintf("/api/v1/runtime-brokers/%s", tid("broker_email_fallback")), nil) if rec.Code != http.StatusOK { t.Fatalf("expected status 200, got %d: %s", rec.Code, rec.Body.String()) } @@ -1734,7 +1734,7 @@ func TestRuntimeBrokerList_Capabilities(t *testing.T) { ctx := context.Background() broker := &store.RuntimeBroker{ - ID: "broker_caps_list", + ID: tid("broker_caps_list"), Name: "Caps List Broker", Slug: "caps-list-broker", Status: store.BrokerStatusOnline, @@ -1771,7 +1771,7 @@ func TestRuntimeBrokerList_CreatedByName(t *testing.T) { // Create a user to be the broker creator if err := s.CreateUser(ctx, &store.User{ - ID: "user_list_creator", + ID: tid("user_list_creator"), Email: "listcreator@test.com", DisplayName: "List Creator", Role: "member", @@ -1781,11 +1781,11 @@ func TestRuntimeBrokerList_CreatedByName(t *testing.T) { } broker := &store.RuntimeBroker{ - ID: "broker_list_createdby", + ID: tid("broker_list_createdby"), Name: "List CreatedBy Broker", Slug: "list-createdby-broker", Status: store.BrokerStatusOnline, - CreatedBy: "user_list_creator", + CreatedBy: tid("user_list_creator"), LastHeartbeat: time.Now(), Created: time.Now(), Updated: time.Now(), @@ -1819,7 +1819,7 @@ func TestRuntimeBrokerListWithProjectLocalPath(t *testing.T) { // Create a project project := &store.Project{ - ID: "project_localpath_test", + ID: tid("project_localpath_test"), Name: "Local Path Test Project", Slug: "local-path-test", Visibility: store.VisibilityPrivate, @@ -1832,7 +1832,7 @@ func TestRuntimeBrokerListWithProjectLocalPath(t *testing.T) { // Create a runtime broker broker := &store.RuntimeBroker{ - ID: "host_localpath_test", + ID: tid("host_localpath_test"), Name: "Local Path Test Host", Slug: "local-path-test-host", Status: store.BrokerStatusOnline, @@ -1857,7 +1857,7 @@ func TestRuntimeBrokerListWithProjectLocalPath(t *testing.T) { } // List runtime brokers filtered by project - should include localPath - rec := doRequest(t, srv, http.MethodGet, "/api/v1/runtime-brokers?projectId=project_localpath_test", nil) + rec := doRequest(t, srv, http.MethodGet, fmt.Sprintf("/api/v1/runtime-brokers?projectId=%s", tid("project_localpath_test")), nil) if rec.Code != http.StatusOK { t.Errorf("expected status 200, got %d: %s", rec.Code, rec.Body.String()) @@ -1872,7 +1872,7 @@ func TestRuntimeBrokerListWithProjectLocalPath(t *testing.T) { t.Errorf("expected 1 broker, got %d", len(resp.Brokers)) } - if resp.Brokers[0].ID != "host_localpath_test" { + if resp.Brokers[0].ID != tid("host_localpath_test") { t.Errorf("expected broker ID 'host_localpath_test', got %q", resp.Brokers[0].ID) } @@ -1904,7 +1904,7 @@ func TestRuntimeBrokerListWithProjectLocalPath(t *testing.T) { // testServerWithBrokerAuth creates a test server with broker auth enabled. func testServerWithBrokerAuth(t *testing.T) (*Server, store.Store) { t.Helper() - s, err := sqlite.New(":memory:") + s, err := newTestStore(":memory:") if err != nil { t.Fatalf("failed to create test store: %v", err) } @@ -2052,7 +2052,7 @@ func TestTemplateList(t *testing.T) { ctx := context.Background() template := &store.Template{ - ID: "tmpl_test1", + ID: tid("tmpl_test1"), Slug: "test-template", Name: "Test Template", Harness: "claude", @@ -2088,7 +2088,7 @@ func TestTemplateListByProjectID(t *testing.T) { // Create a global template if err := s.CreateTemplate(ctx, &store.Template{ - ID: "tmpl_global1", Slug: "global-tmpl", Name: "Global Template", + ID: tid("tmpl_global1"), Slug: "global-tmpl", Name: "Global Template", Harness: "claude", Scope: "global", Visibility: store.VisibilityPublic, Status: "active", Created: now, Updated: now, @@ -2098,8 +2098,8 @@ func TestTemplateListByProjectID(t *testing.T) { // Create a project-scoped template for project "project_abc" if err := s.CreateTemplate(ctx, &store.Template{ - ID: "tmpl_project1", Slug: "project-tmpl", Name: "Project Template", - Harness: "gemini", Scope: "project", ScopeID: "project_abc", + ID: tid("tmpl_project1"), Slug: "project-tmpl", Name: "Project Template", + Harness: "gemini", Scope: "project", ScopeID: tid("project_abc"), Visibility: store.VisibilityPublic, Status: "active", Created: now, Updated: now, }); err != nil { @@ -2108,8 +2108,8 @@ func TestTemplateListByProjectID(t *testing.T) { // Create a project-scoped template for a different project if err := s.CreateTemplate(ctx, &store.Template{ - ID: "tmpl_project2", Slug: "other-project-tmpl", Name: "Other Project Template", - Harness: "claude", Scope: "project", ScopeID: "project_xyz", + ID: tid("tmpl_project2"), Slug: "other-project-tmpl", Name: "Other Project Template", + Harness: "claude", Scope: "project", ScopeID: tid("project_xyz"), Visibility: store.VisibilityPublic, Status: "active", Created: now, Updated: now, }); err != nil { @@ -2117,7 +2117,7 @@ func TestTemplateListByProjectID(t *testing.T) { } // Query with projectId=project_abc should return global + project_abc templates only - rec := doRequest(t, srv, http.MethodGet, "/api/v1/templates?projectId=project_abc", nil) + rec := doRequest(t, srv, http.MethodGet, fmt.Sprintf("/api/v1/templates?projectId=%s", tid("project_abc")), nil) if rec.Code != http.StatusOK { t.Fatalf("expected status 200, got %d: %s", rec.Code, rec.Body.String()) } @@ -2136,13 +2136,13 @@ func TestTemplateListByProjectID(t *testing.T) { for _, tmpl := range resp.Templates { ids[tmpl.ID] = true } - if !ids["tmpl_global1"] { + if !ids[tid("tmpl_global1")] { t.Error("expected global template in results") } - if !ids["tmpl_project1"] { + if !ids[tid("tmpl_project1")] { t.Error("expected project_abc template in results") } - if ids["tmpl_project2"] { + if ids[tid("tmpl_project2")] { t.Error("did not expect project_xyz template in results") } } @@ -2191,7 +2191,7 @@ func TestUserList(t *testing.T) { ctx := context.Background() user := &store.User{ - ID: "user_test1", + ID: tid("user_test1"), Email: "test@example.com", DisplayName: "Test User", Role: store.UserRoleMember, @@ -2256,7 +2256,7 @@ func TestInvalidJSON(t *testing.T) { // Create a project first project := &store.Project{ - ID: "project_invalid", + ID: tid("project_invalid"), Slug: "invalid-project", Name: "Invalid Project", GitRemote: "https://github.com/test/invalid", @@ -2325,8 +2325,9 @@ func TestCORSPreflight(t *testing.T) { func TestProjectCreateIdempotent(t *testing.T) { srv, _ := testServer(t) + deterministicID := tid("deterministic-id-1234") body := CreateProjectRequest{ - ID: "deterministic-id-1234", + ID: deterministicID, Name: "My Project", Slug: "my-project", GitRemote: "github.com/acme/widgets", @@ -2342,8 +2343,8 @@ func TestProjectCreateIdempotent(t *testing.T) { if err := json.NewDecoder(rec.Body).Decode(&project1); err != nil { t.Fatalf("failed to decode first response: %v", err) } - if project1.ID != "deterministic-id-1234" { - t.Errorf("expected ID %q, got %q", "deterministic-id-1234", project1.ID) + if project1.ID != deterministicID { + t.Errorf("expected ID %q, got %q", deterministicID, project1.ID) } // Second create with same ID — should return 200 with same project @@ -2419,7 +2420,7 @@ func TestAgentCreate_StoresTemplateSlug(t *testing.T) { // Create a runtime broker broker := &store.RuntimeBroker{ - ID: "host_tmpl_slug", + ID: tid("host_tmpl_slug"), Slug: "tmpl-host", Name: "Template Host", Status: store.BrokerStatusOnline, @@ -2430,7 +2431,7 @@ func TestAgentCreate_StoresTemplateSlug(t *testing.T) { // Create a project project := &store.Project{ - ID: "project_tmpl_slug", + ID: tid("project_tmpl_slug"), Slug: "tmpl-project", Name: "Template Project", GitRemote: "github.com/test/tmpl-repo", @@ -2455,7 +2456,7 @@ func TestAgentCreate_StoresTemplateSlug(t *testing.T) { // Create a template with a known slug tmpl := &store.Template{ - ID: "tmpl_uuid_123", + ID: tid("tmpl_uuid_123"), Slug: "my-claude-template", Name: "My Claude Template", Harness: "claude", @@ -2513,7 +2514,7 @@ func TestEnrichAgents_ResolvesTemplateSlug(t *testing.T) { // Create a template tmpl := &store.Template{ - ID: "tmpl_enrich_123", + ID: tid("tmpl_enrich_123"), Slug: "enriched-template", Name: "Enriched Template", Harness: "gemini", @@ -2554,7 +2555,7 @@ func TestEnrichAgent_ResolvesTemplateSlug(t *testing.T) { // Create a template tmpl := &store.Template{ - ID: "tmpl_enrich_single", + ID: tid("tmpl_enrich_single"), Slug: "single-enriched", Name: "Single Enriched", Harness: "claude", @@ -2599,7 +2600,7 @@ func TestOutboundMessage_UnknownRecipient(t *testing.T) { } rb := &store.RuntimeBroker{ - ID: "broker-msg", + ID: tid("broker-msg"), Name: "test-broker", Slug: "test-broker", Endpoint: "http://localhost:9800", @@ -2615,7 +2616,7 @@ func TestOutboundMessage_UnknownRecipient(t *testing.T) { Slug: "sender", ProjectID: project.ID, Phase: "running", - RuntimeBrokerID: "broker-msg", + RuntimeBrokerID: tid("broker-msg"), Visibility: store.VisibilityPrivate, } if err := s.CreateAgent(ctx, agent); err != nil { diff --git a/pkg/hub/harness_capabilities_test.go b/pkg/hub/harness_capabilities_test.go index d1e7cc3c0..1c3dc29d7 100644 --- a/pkg/hub/harness_capabilities_test.go +++ b/pkg/hub/harness_capabilities_test.go @@ -33,11 +33,11 @@ func seedCreatedAgentForHarnessTest(t *testing.T, s store.Store, id, harnessConf t.Helper() ctx := context.Background() - project := &store.Project{ID: "project-" + id, Name: "Project " + id, Slug: "project-" + id} + project := &store.Project{ID: tid("project-" + id), Name: "Project " + id, Slug: "project-" + id} require.NoError(t, s.CreateProject(ctx, project)) agent := &store.Agent{ - ID: "agent-" + id, + ID: tid("agent-" + id), Slug: "agent-" + id, Name: "Agent " + id, ProjectID: project.ID, @@ -127,7 +127,7 @@ func TestGetAgent_CustomHarnessTypeFromHarnessConfig(t *testing.T) { ctx := context.Background() hc := &store.HarnessConfig{ - ID: "hc-custom", + ID: tid("hc-custom"), Name: "custom-harness", Slug: "custom-harness", Harness: "custom-harness", diff --git a/pkg/hub/harness_config_handlers_test.go b/pkg/hub/harness_config_handlers_test.go index c3cdcc0fd..fd6390c1c 100644 --- a/pkg/hub/harness_config_handlers_test.go +++ b/pkg/hub/harness_config_handlers_test.go @@ -19,6 +19,7 @@ package hub import ( "context" "encoding/json" + "fmt" "net/http" "testing" "time" @@ -31,7 +32,7 @@ func TestHarnessConfigList(t *testing.T) { ctx := context.Background() hc := &store.HarnessConfig{ - ID: "hc_test1", + ID: tid("hc_test1"), Slug: "test-hc", Name: "Test HC", Harness: "claude", @@ -68,7 +69,7 @@ func TestHarnessConfigListByProjectID(t *testing.T) { // Create a global harness config if err := s.CreateHarnessConfig(ctx, &store.HarnessConfig{ - ID: "hc_global1", Slug: "global-hc", Name: "Global HC", + ID: tid("hc_global1"), Slug: "global-hc", Name: "Global HC", Harness: "claude", Scope: "global", Visibility: store.VisibilityPublic, Status: store.HarnessConfigStatusActive, Created: now, Updated: now, @@ -78,8 +79,8 @@ func TestHarnessConfigListByProjectID(t *testing.T) { // Create a project-scoped harness config for project "project_abc" if err := s.CreateHarnessConfig(ctx, &store.HarnessConfig{ - ID: "hc_project1", Slug: "project-hc", Name: "Project HC", - Harness: "gemini", Scope: "project", ScopeID: "project_abc", + ID: tid("hc_project1"), Slug: "project-hc", Name: "Project HC", + Harness: "gemini", Scope: "project", ScopeID: tid("project_abc"), Visibility: store.VisibilityPublic, Status: store.HarnessConfigStatusActive, Created: now, Updated: now, }); err != nil { @@ -88,8 +89,8 @@ func TestHarnessConfigListByProjectID(t *testing.T) { // Create a project-scoped harness config for a different project if err := s.CreateHarnessConfig(ctx, &store.HarnessConfig{ - ID: "hc_project2", Slug: "other-project-hc", Name: "Other Project HC", - Harness: "claude", Scope: "project", ScopeID: "project_xyz", + ID: tid("hc_project2"), Slug: "other-project-hc", Name: "Other Project HC", + Harness: "claude", Scope: "project", ScopeID: tid("project_xyz"), Visibility: store.VisibilityPublic, Status: store.HarnessConfigStatusActive, Created: now, Updated: now, }); err != nil { @@ -98,8 +99,8 @@ func TestHarnessConfigListByProjectID(t *testing.T) { // Create a user-scoped harness config if err := s.CreateHarnessConfig(ctx, &store.HarnessConfig{ - ID: "hc_user1", Slug: "user-hc", Name: "User HC", - Harness: "claude", Scope: "user", ScopeID: "user_123", + ID: tid("hc_user1"), Slug: "user-hc", Name: "User HC", + Harness: "claude", Scope: "user", ScopeID: tid("user_123"), Visibility: store.VisibilityPrivate, Status: store.HarnessConfigStatusActive, Created: now, Updated: now, }); err != nil { @@ -107,7 +108,7 @@ func TestHarnessConfigListByProjectID(t *testing.T) { } // Query with projectId=project_abc should return global + project_abc configs only - rec := doRequest(t, srv, http.MethodGet, "/api/v1/harness-configs?projectId=project_abc", nil) + rec := doRequest(t, srv, http.MethodGet, fmt.Sprintf("/api/v1/harness-configs?projectId=%s", tid("project_abc")), nil) if rec.Code != http.StatusOK { t.Fatalf("expected status 200, got %d: %s", rec.Code, rec.Body.String()) } @@ -126,16 +127,16 @@ func TestHarnessConfigListByProjectID(t *testing.T) { for _, hc := range resp.HarnessConfigs { ids[hc.ID] = true } - if !ids["hc_global1"] { + if !ids[tid("hc_global1")] { t.Error("expected global harness config in results") } - if !ids["hc_project1"] { + if !ids[tid("hc_project1")] { t.Error("expected project_abc harness config in results") } - if ids["hc_project2"] { + if ids[tid("hc_project2")] { t.Error("did not expect project_xyz harness config in results") } - if ids["hc_user1"] { + if ids[tid("hc_user1")] { t.Error("did not expect user harness config in results") } } @@ -224,7 +225,7 @@ func TestHarnessConfigGet(t *testing.T) { ctx := context.Background() hc := &store.HarnessConfig{ - ID: "hc_get1", + ID: tid("hc_get1"), Slug: "get-test", Name: "Get Test", Harness: "gemini", @@ -238,7 +239,7 @@ func TestHarnessConfigGet(t *testing.T) { t.Fatalf("failed to create harness config: %v", err) } - rec := doRequest(t, srv, http.MethodGet, "/api/v1/harness-configs/hc_get1", nil) + rec := doRequest(t, srv, http.MethodGet, fmt.Sprintf("/api/v1/harness-configs/%s", tid("hc_get1")), nil) if rec.Code != http.StatusOK { t.Errorf("expected status 200, got %d: %s", rec.Code, rec.Body.String()) @@ -262,7 +263,7 @@ func TestHarnessConfigDelete(t *testing.T) { ctx := context.Background() hc := &store.HarnessConfig{ - ID: "hc_del1", + ID: tid("hc_del1"), Slug: "del-test", Name: "Del Test", Harness: "claude", @@ -276,13 +277,13 @@ func TestHarnessConfigDelete(t *testing.T) { t.Fatalf("failed to create harness config: %v", err) } - rec := doRequest(t, srv, http.MethodDelete, "/api/v1/harness-configs/hc_del1", nil) + rec := doRequest(t, srv, http.MethodDelete, fmt.Sprintf("/api/v1/harness-configs/%s", tid("hc_del1")), nil) if rec.Code != http.StatusNoContent { t.Errorf("expected status 204, got %d: %s", rec.Code, rec.Body.String()) } // Verify deleted - rec = doRequest(t, srv, http.MethodGet, "/api/v1/harness-configs/hc_del1", nil) + rec = doRequest(t, srv, http.MethodGet, fmt.Sprintf("/api/v1/harness-configs/%s", tid("hc_del1")), nil) if rec.Code != http.StatusNotFound { t.Errorf("expected status 404 after delete, got %d", rec.Code) } @@ -293,7 +294,7 @@ func TestHarnessConfigPatch(t *testing.T) { ctx := context.Background() hc := &store.HarnessConfig{ - ID: "hc_patch1", + ID: tid("hc_patch1"), Slug: "patch-test", Name: "Patch Test", Harness: "claude", @@ -312,7 +313,7 @@ func TestHarnessConfigPatch(t *testing.T) { "description": "Updated description", } - rec := doRequest(t, srv, http.MethodPatch, "/api/v1/harness-configs/hc_patch1", body) + rec := doRequest(t, srv, http.MethodPatch, fmt.Sprintf("/api/v1/harness-configs/%s", tid("hc_patch1")), body) if rec.Code != http.StatusOK { t.Errorf("expected status 200, got %d: %s", rec.Code, rec.Body.String()) diff --git a/pkg/hub/heartbeat_timeout_test.go b/pkg/hub/heartbeat_timeout_test.go index 789ebc2bd..f5c6d6acf 100644 --- a/pkg/hub/heartbeat_timeout_test.go +++ b/pkg/hub/heartbeat_timeout_test.go @@ -26,7 +26,6 @@ import ( "github.com/GoogleCloudPlatform/scion/pkg/agent/state" "github.com/GoogleCloudPlatform/scion/pkg/api" "github.com/GoogleCloudPlatform/scion/pkg/store" - "github.com/GoogleCloudPlatform/scion/pkg/store/sqlite" ) // trackingEventPublisher records PublishAgentStatus calls for test assertions. @@ -59,7 +58,7 @@ func (t *trackingEventPublisher) reset() { func setupHeartbeatTestServer(t *testing.T) (*Server, store.Store, *trackingEventPublisher) { t.Helper() - s, err := sqlite.New(":memory:") + s, err := newTestStore(":memory:") if err != nil { t.Fatalf("failed to create test store: %v", err) } diff --git a/pkg/hub/httpdispatcher_test.go b/pkg/hub/httpdispatcher_test.go index 5cfa7b46d..9e774d4c6 100644 --- a/pkg/hub/httpdispatcher_test.go +++ b/pkg/hub/httpdispatcher_test.go @@ -30,13 +30,12 @@ import ( "github.com/GoogleCloudPlatform/scion/pkg/api" "github.com/GoogleCloudPlatform/scion/pkg/messages" "github.com/GoogleCloudPlatform/scion/pkg/store" - "github.com/GoogleCloudPlatform/scion/pkg/store/sqlite" ) // createTestStore creates an in-memory SQLite store for testing. func createTestStore(t *testing.T) store.Store { t.Helper() - s, err := sqlite.New(":memory:") + s, err := newTestStore(":memory:") if err != nil { t.Fatalf("failed to create test store: %v", err) } @@ -215,7 +214,7 @@ func TestHTTPAgentDispatcher_DispatchAgentCreate(t *testing.T) { // Create a runtime broker with an endpoint broker := &store.RuntimeBroker{ - ID: "host-1", + ID: tid("host-1"), Name: "test-host", Slug: "test-host", Endpoint: "http://localhost:9800", @@ -229,11 +228,11 @@ func TestHTTPAgentDispatcher_DispatchAgentCreate(t *testing.T) { dispatcher := NewHTTPAgentDispatcherWithClient(memStore, mockClient, false, slog.Default()) agent := &store.Agent{ - ID: "agent-1", + ID: tid("agent-1"), Name: "test-agent", Slug: "test-agent", - ProjectID: "project-1", - RuntimeBrokerID: "host-1", + ProjectID: tid("project-1"), + RuntimeBrokerID: tid("host-1"), AppliedConfig: &store.AgentAppliedConfig{ HarnessConfig: "claude", Task: "Fix a bug", @@ -258,8 +257,9 @@ func TestHTTPAgentDispatcher_DispatchAgentStop(t *testing.T) { memStore := createTestStore(t) broker := &store.RuntimeBroker{ - ID: "host-1", + ID: tid("host-1"), Name: "test-host", + Slug: "test-host", Endpoint: "http://localhost:9800", Status: store.BrokerStatusOnline, } @@ -271,10 +271,10 @@ func TestHTTPAgentDispatcher_DispatchAgentStop(t *testing.T) { dispatcher := NewHTTPAgentDispatcherWithClient(memStore, mockClient, false, slog.Default()) agent := &store.Agent{ - ID: "agent-1", + ID: tid("agent-1"), Name: "test-agent", Slug: "test-agent", - RuntimeBrokerID: "host-1", + RuntimeBrokerID: tid("host-1"), } err := dispatcher.DispatchAgentStop(ctx, agent) @@ -295,8 +295,9 @@ func TestHTTPAgentDispatcher_DispatchAgentDelete(t *testing.T) { memStore := createTestStore(t) broker := &store.RuntimeBroker{ - ID: "host-1", + ID: tid("host-1"), Name: "test-host", + Slug: "test-host", Endpoint: "http://localhost:9800", Status: store.BrokerStatusOnline, } @@ -308,10 +309,10 @@ func TestHTTPAgentDispatcher_DispatchAgentDelete(t *testing.T) { dispatcher := NewHTTPAgentDispatcherWithClient(memStore, mockClient, false, slog.Default()) agent := &store.Agent{ - ID: "agent-1", + ID: tid("agent-1"), Name: "test-agent", Slug: "test-agent", - RuntimeBrokerID: "host-1", + RuntimeBrokerID: tid("host-1"), } err := dispatcher.DispatchAgentDelete(ctx, agent, true, false, false, time.Time{}) @@ -335,8 +336,9 @@ func TestHTTPAgentDispatcher_DispatchAgentMessage(t *testing.T) { memStore := createTestStore(t) broker := &store.RuntimeBroker{ - ID: "host-1", + ID: tid("host-1"), Name: "test-host", + Slug: "test-host", Endpoint: "http://localhost:9800", Status: store.BrokerStatusOnline, } @@ -348,10 +350,10 @@ func TestHTTPAgentDispatcher_DispatchAgentMessage(t *testing.T) { dispatcher := NewHTTPAgentDispatcherWithClient(memStore, mockClient, false, slog.Default()) agent := &store.Agent{ - ID: "agent-1", + ID: tid("agent-1"), Name: "test-agent", Slug: "test-agent", - RuntimeBrokerID: "host-1", + RuntimeBrokerID: tid("host-1"), } err := dispatcher.DispatchAgentMessage(ctx, agent, "Hello, agent!", true, nil) @@ -407,12 +409,12 @@ func TestHTTPRuntimeBrokerClient_CreateAgent(t *testing.T) { req := &RemoteCreateAgentRequest{ ID: "hub-uuid-1", - Slug: "agent-1", + Slug: tid("agent-1"), Name: "test-agent", - ProjectID: "project-1", + ProjectID: tid("project-1"), } - resp, err := client.CreateAgent(context.Background(), "host-1", server.URL, req) + resp, err := client.CreateAgent(context.Background(), tid("host-1"), server.URL, req) if err != nil { t.Fatalf("CreateAgent failed: %v", err) } @@ -440,7 +442,7 @@ func TestHTTPRuntimeBrokerClient_StartAgent_InvalidJSONFails(t *testing.T) { defer server.Close() client := NewHTTPRuntimeBrokerClient() - _, err := client.StartAgent(context.Background(), "host-1", server.URL, "test-agent", "", "", "", "", "", nil, nil, nil, nil, false) + _, err := client.StartAgent(context.Background(), tid("host-1"), server.URL, "test-agent", "", "", "", "", "", nil, nil, nil, nil, false) if err == nil { t.Fatal("expected StartAgent to fail on invalid JSON response") } @@ -464,7 +466,7 @@ func TestHTTPRuntimeBrokerClient_StopAgent(t *testing.T) { client := NewHTTPRuntimeBrokerClient() - err := client.StopAgent(context.Background(), "host-1", server.URL, "test-agent", "") + err := client.StopAgent(context.Background(), tid("host-1"), server.URL, "test-agent", "") if err != nil { t.Fatalf("StopAgent failed: %v", err) } @@ -493,7 +495,7 @@ func TestHTTPRuntimeBrokerClient_DeleteAgent(t *testing.T) { client := NewHTTPRuntimeBrokerClient() - err := client.DeleteAgent(context.Background(), "host-1", server.URL, "test-agent", "", true, false, false, time.Time{}) + err := client.DeleteAgent(context.Background(), tid("host-1"), server.URL, "test-agent", "", true, false, false, time.Time{}) if err != nil { t.Fatalf("DeleteAgent failed: %v", err) } @@ -526,7 +528,7 @@ func TestHTTPRuntimeBrokerClient_MessageAgent(t *testing.T) { client := NewHTTPRuntimeBrokerClient() - err := client.MessageAgent(context.Background(), "host-1", server.URL, "test-agent", "", "Hello!", true, nil) + err := client.MessageAgent(context.Background(), tid("host-1"), server.URL, "test-agent", "", "Hello!", true, nil) if err != nil { t.Fatalf("MessageAgent failed: %v", err) } @@ -540,7 +542,7 @@ func TestHTTPAgentDispatcher_DispatchAgentCreate_WithProjectProviderPath(t *test // (not hub-managed). This ensures buildCreateRequest looks up the // provider's LocalPath instead of sending a projectSlug. project := &store.Project{ - ID: "project-1", + ID: tid("project-1"), Name: "test-project", Slug: "test-project", GitRemote: "https://github.com/example/repo.git", @@ -551,7 +553,7 @@ func TestHTTPAgentDispatcher_DispatchAgentCreate_WithProjectProviderPath(t *test // Create a runtime broker broker := &store.RuntimeBroker{ - ID: "broker-1", + ID: tid("broker-1"), Name: "test-broker", Slug: "test-broker", Endpoint: "http://localhost:9800", @@ -563,8 +565,8 @@ func TestHTTPAgentDispatcher_DispatchAgentCreate_WithProjectProviderPath(t *test // Add a project provider record WITH a local path provider := &store.ProjectProvider{ - ProjectID: "project-1", - BrokerID: "broker-1", + ProjectID: tid("project-1"), + BrokerID: tid("broker-1"), BrokerName: "test-broker", LocalPath: "/home/user/projects/myproject/.scion", Status: store.BrokerStatusOnline, @@ -577,11 +579,11 @@ func TestHTTPAgentDispatcher_DispatchAgentCreate_WithProjectProviderPath(t *test dispatcher := NewHTTPAgentDispatcherWithClient(memStore, mockClient, false, slog.Default()) agent := &store.Agent{ - ID: "agent-1", + ID: tid("agent-1"), Name: "test-agent", Slug: "test-agent", - ProjectID: "project-1", - RuntimeBrokerID: "broker-1", + ProjectID: tid("project-1"), + RuntimeBrokerID: tid("broker-1"), } err := dispatcher.DispatchAgentCreate(ctx, agent) @@ -606,7 +608,7 @@ func TestHTTPAgentDispatcher_DispatchAgentCreate_MissingBrokerEndpoint(t *testin memStore := createTestStore(t) broker := &store.RuntimeBroker{ - ID: "host-1", + ID: tid("host-1"), Name: "test-host", Slug: "test-host", Status: store.BrokerStatusOnline, @@ -619,10 +621,10 @@ func TestHTTPAgentDispatcher_DispatchAgentCreate_MissingBrokerEndpoint(t *testin dispatcher := NewHTTPAgentDispatcherWithClient(memStore, mockClient, false, slog.Default()) agent := &store.Agent{ - ID: "agent-1", + ID: tid("agent-1"), Name: "test-agent", Slug: "test-agent", - RuntimeBrokerID: "host-1", + RuntimeBrokerID: tid("host-1"), } err := dispatcher.DispatchAgentCreate(ctx, agent) @@ -639,7 +641,7 @@ func TestHTTPAgentDispatcher_DispatchAgentCreate_MissingBrokerEndpoint(t *testin func TestBrokerHTTPTransport_RejectsEmptyEndpoint(t *testing.T) { transport := newBrokerHTTPTransport(false, nil) - _, err := transport.CreateAgent(context.Background(), "broker-1", "", &RemoteCreateAgentRequest{}) + _, err := transport.CreateAgent(context.Background(), tid("broker-1"), "", &RemoteCreateAgentRequest{}) if err == nil { t.Fatal("expected error when endpoint is empty") } @@ -654,7 +656,7 @@ func TestHTTPAgentDispatcher_DispatchAgentCreate_WithoutProjectProviderPath(t *t // Create the project (required by FK constraint) project := &store.Project{ - ID: "project-1", + ID: tid("project-1"), Name: "test-project", Slug: "test-project", } @@ -664,7 +666,7 @@ func TestHTTPAgentDispatcher_DispatchAgentCreate_WithoutProjectProviderPath(t *t // Create a runtime broker broker := &store.RuntimeBroker{ - ID: "broker-1", + ID: tid("broker-1"), Name: "test-broker", Slug: "test-broker", Endpoint: "http://localhost:9800", @@ -676,8 +678,8 @@ func TestHTTPAgentDispatcher_DispatchAgentCreate_WithoutProjectProviderPath(t *t // Add a project provider record WITHOUT a local path (simulating auto-provide) provider := &store.ProjectProvider{ - ProjectID: "project-1", - BrokerID: "broker-1", + ProjectID: tid("project-1"), + BrokerID: tid("broker-1"), BrokerName: "test-broker", LocalPath: "", Status: store.BrokerStatusOnline, @@ -691,11 +693,11 @@ func TestHTTPAgentDispatcher_DispatchAgentCreate_WithoutProjectProviderPath(t *t dispatcher := NewHTTPAgentDispatcherWithClient(memStore, mockClient, false, slog.Default()) agent := &store.Agent{ - ID: "agent-1", + ID: tid("agent-1"), Name: "test-agent", Slug: "test-agent", - ProjectID: "project-1", - RuntimeBrokerID: "broker-1", + ProjectID: tid("project-1"), + RuntimeBrokerID: tid("broker-1"), } err := dispatcher.DispatchAgentCreate(ctx, agent) @@ -718,7 +720,7 @@ func TestHTTPAgentDispatcher_DispatchAgentProvision(t *testing.T) { // Create a runtime broker with an endpoint broker := &store.RuntimeBroker{ - ID: "host-1", + ID: tid("host-1"), Name: "test-host", Slug: "test-host", Endpoint: "http://localhost:9800", @@ -732,11 +734,11 @@ func TestHTTPAgentDispatcher_DispatchAgentProvision(t *testing.T) { dispatcher := NewHTTPAgentDispatcherWithClient(memStore, mockClient, false, slog.Default()) agent := &store.Agent{ - ID: "agent-1", + ID: tid("agent-1"), Name: "test-agent", Slug: "test-agent", - ProjectID: "project-1", - RuntimeBrokerID: "host-1", + ProjectID: tid("project-1"), + RuntimeBrokerID: tid("host-1"), AppliedConfig: &store.AgentAppliedConfig{ HarnessConfig: "claude", }, @@ -762,7 +764,7 @@ func TestHTTPAgentDispatcher_DispatchAgentProvision(t *testing.T) { } // Verify broker ID was passed - if mockClient.lastBrokerID != "host-1" { + if mockClient.lastBrokerID != tid("host-1") { t.Errorf("expected brokerID 'host-1', got '%s'", mockClient.lastBrokerID) } } @@ -775,7 +777,7 @@ func TestHTTPAgentDispatcher_DispatchAgentProvision_NoBroker(t *testing.T) { dispatcher := NewHTTPAgentDispatcherWithClient(memStore, mockClient, false, slog.Default()) agent := &store.Agent{ - ID: "agent-1", + ID: tid("agent-1"), Name: "test-agent", Slug: "test-agent", RuntimeBrokerID: "", // No broker assigned @@ -796,7 +798,7 @@ func TestHTTPAgentDispatcher_DispatchAgentProvision_PassesTaskThrough(t *testing memStore := createTestStore(t) broker := &store.RuntimeBroker{ - ID: "host-1", + ID: tid("host-1"), Name: "test-host", Slug: "test-host", Endpoint: "http://localhost:9800", @@ -810,11 +812,11 @@ func TestHTTPAgentDispatcher_DispatchAgentProvision_PassesTaskThrough(t *testing dispatcher := NewHTTPAgentDispatcherWithClient(memStore, mockClient, false, slog.Default()) agent := &store.Agent{ - ID: "agent-1", + ID: tid("agent-1"), Name: "test-agent", Slug: "test-agent", - ProjectID: "project-1", - RuntimeBrokerID: "host-1", + ProjectID: tid("project-1"), + RuntimeBrokerID: tid("host-1"), AppliedConfig: &store.AgentAppliedConfig{ Task: "implement feature X", }, @@ -845,7 +847,7 @@ func TestHTTPAgentDispatcher_DispatchAgentCreate_WithWorkspace(t *testing.T) { // Create a runtime broker broker := &store.RuntimeBroker{ - ID: "host-1", + ID: tid("host-1"), Name: "test-host", Slug: "test-host", Endpoint: "http://localhost:9800", @@ -859,11 +861,11 @@ func TestHTTPAgentDispatcher_DispatchAgentCreate_WithWorkspace(t *testing.T) { dispatcher := NewHTTPAgentDispatcherWithClient(memStore, mockClient, false, slog.Default()) agent := &store.Agent{ - ID: "agent-1", + ID: tid("agent-1"), Name: "test-agent", Slug: "test-agent", - ProjectID: "project-1", - RuntimeBrokerID: "host-1", + ProjectID: tid("project-1"), + RuntimeBrokerID: tid("host-1"), AppliedConfig: &store.AgentAppliedConfig{ HarnessConfig: "claude", Task: "do something", @@ -893,7 +895,7 @@ func TestHTTPAgentDispatcher_DispatchAgentCreate_WithCreatorName(t *testing.T) { // Create a runtime broker broker := &store.RuntimeBroker{ - ID: "host-1", + ID: tid("host-1"), Name: "test-host", Slug: "test-host", Endpoint: "http://localhost:9800", @@ -907,11 +909,11 @@ func TestHTTPAgentDispatcher_DispatchAgentCreate_WithCreatorName(t *testing.T) { dispatcher := NewHTTPAgentDispatcherWithClient(memStore, mockClient, false, slog.Default()) agent := &store.Agent{ - ID: "agent-1", + ID: tid("agent-1"), Name: "test-agent", Slug: "test-agent", - ProjectID: "project-1", - RuntimeBrokerID: "host-1", + ProjectID: tid("project-1"), + RuntimeBrokerID: tid("host-1"), AppliedConfig: &store.AgentAppliedConfig{ HarnessConfig: "claude", Task: "do something", @@ -939,7 +941,7 @@ func TestHTTPAgentDispatcher_DispatchAgentCreate_WithoutCreatorName(t *testing.T memStore := createTestStore(t) broker := &store.RuntimeBroker{ - ID: "host-1", + ID: tid("host-1"), Name: "test-host", Slug: "test-host", Endpoint: "http://localhost:9800", @@ -953,11 +955,11 @@ func TestHTTPAgentDispatcher_DispatchAgentCreate_WithoutCreatorName(t *testing.T dispatcher := NewHTTPAgentDispatcherWithClient(memStore, mockClient, false, slog.Default()) agent := &store.Agent{ - ID: "agent-1", + ID: tid("agent-1"), Name: "test-agent", Slug: "test-agent", - ProjectID: "project-1", - RuntimeBrokerID: "host-1", + ProjectID: tid("project-1"), + RuntimeBrokerID: tid("host-1"), AppliedConfig: &store.AgentAppliedConfig{ HarnessConfig: "claude", }, @@ -980,7 +982,7 @@ func TestHTTPAgentDispatcher_DispatchAgentCreate_DoesNotSetProvisionOnly(t *test // Create a runtime broker broker := &store.RuntimeBroker{ - ID: "host-1", + ID: tid("host-1"), Name: "test-host", Slug: "test-host", Endpoint: "http://localhost:9800", @@ -994,11 +996,11 @@ func TestHTTPAgentDispatcher_DispatchAgentCreate_DoesNotSetProvisionOnly(t *test dispatcher := NewHTTPAgentDispatcherWithClient(memStore, mockClient, false, slog.Default()) agent := &store.Agent{ - ID: "agent-1", + ID: tid("agent-1"), Name: "test-agent", Slug: "test-agent", - ProjectID: "project-1", - RuntimeBrokerID: "host-1", + ProjectID: tid("project-1"), + RuntimeBrokerID: tid("host-1"), AppliedConfig: &store.AgentAppliedConfig{ Task: "do something", }, @@ -1021,7 +1023,7 @@ func TestHTTPAgentDispatcher_DispatchAgentStart_WithProjectProviderPath(t *testi // Create the project with a GitRemote so it is treated as a linked project project := &store.Project{ - ID: "project-1", + ID: tid("project-1"), Name: "test-project", Slug: "test-project", GitRemote: "https://github.com/example/repo.git", @@ -1032,7 +1034,7 @@ func TestHTTPAgentDispatcher_DispatchAgentStart_WithProjectProviderPath(t *testi // Create a runtime broker broker := &store.RuntimeBroker{ - ID: "broker-1", + ID: tid("broker-1"), Name: "test-broker", Slug: "test-broker", Endpoint: "http://localhost:9800", @@ -1044,8 +1046,8 @@ func TestHTTPAgentDispatcher_DispatchAgentStart_WithProjectProviderPath(t *testi // Add a project provider record with a local path provider := &store.ProjectProvider{ - ProjectID: "project-1", - BrokerID: "broker-1", + ProjectID: tid("project-1"), + BrokerID: tid("broker-1"), BrokerName: "test-broker", LocalPath: "/home/user/projects/myproject/.scion", Status: store.BrokerStatusOnline, @@ -1058,11 +1060,11 @@ func TestHTTPAgentDispatcher_DispatchAgentStart_WithProjectProviderPath(t *testi dispatcher := NewHTTPAgentDispatcherWithClient(memStore, mockClient, false, slog.Default()) agent := &store.Agent{ - ID: "agent-1", + ID: tid("agent-1"), Name: "test-agent", Slug: "test-agent", - ProjectID: "project-1", - RuntimeBrokerID: "broker-1", + ProjectID: tid("project-1"), + RuntimeBrokerID: tid("broker-1"), } err := dispatcher.DispatchAgentStart(ctx, agent, "do task") @@ -1098,7 +1100,7 @@ func TestHTTPAgentDispatcher_DispatchAgentStart_IncludesAgentIdentity(t *testing memStore := createTestStore(t) project := &store.Project{ - ID: "project-1", + ID: tid("project-1"), Name: "test-project", Slug: "test-project", GitRemote: "https://github.com/example/repo.git", @@ -1108,7 +1110,7 @@ func TestHTTPAgentDispatcher_DispatchAgentStart_IncludesAgentIdentity(t *testing } broker := &store.RuntimeBroker{ - ID: "broker-1", + ID: tid("broker-1"), Name: "test-broker", Slug: "test-broker", Endpoint: "http://localhost:9800", @@ -1119,8 +1121,8 @@ func TestHTTPAgentDispatcher_DispatchAgentStart_IncludesAgentIdentity(t *testing } provider := &store.ProjectProvider{ - ProjectID: "project-1", - BrokerID: "broker-1", + ProjectID: tid("project-1"), + BrokerID: tid("broker-1"), BrokerName: "test-broker", LocalPath: "/home/user/projects/myproject/.scion", Status: store.BrokerStatusOnline, @@ -1136,8 +1138,8 @@ func TestHTTPAgentDispatcher_DispatchAgentStart_IncludesAgentIdentity(t *testing ID: "agent-uuid-123", Name: "test-agent", Slug: "test-agent-slug", - ProjectID: "project-1", - RuntimeBrokerID: "broker-1", + ProjectID: tid("project-1"), + RuntimeBrokerID: tid("broker-1"), } err := dispatcher.DispatchAgentStart(ctx, agent, "") @@ -1166,7 +1168,7 @@ func TestHTTPAgentDispatcher_DispatchAgentStart_IncludesAgentIdentity(t *testing // Verify SCION_GROVE_ID is included in resolvedEnv if v, ok := mockClient.lastResolvedEnv["SCION_GROVE_ID"]; !ok { t.Error("expected SCION_GROVE_ID in resolvedEnv, but not found") - } else if v != "project-1" { + } else if v != tid("project-1") { t.Errorf("expected SCION_GROVE_ID='project-1', got %q", v) } } @@ -1177,7 +1179,7 @@ func TestHTTPAgentDispatcher_DispatchAgentStart_HubManagedProject(t *testing.T) // Create a hub-managed project (no git remote) project := &store.Project{ - ID: "project-hub", + ID: tid("project-hub"), Name: "My Hub Project", Slug: "my-hub-project", // No GitRemote — this is a hub-managed project @@ -1188,7 +1190,7 @@ func TestHTTPAgentDispatcher_DispatchAgentStart_HubManagedProject(t *testing.T) // Create a runtime broker with no local provider path for this project broker := &store.RuntimeBroker{ - ID: "broker-1", + ID: tid("broker-1"), Name: "test-broker", Slug: "test-broker", Endpoint: "http://localhost:9800", @@ -1205,8 +1207,8 @@ func TestHTTPAgentDispatcher_DispatchAgentStart_HubManagedProject(t *testing.T) ID: "agent-hub-1", Name: "hub-agent", Slug: "hub-agent", - ProjectID: "project-hub", - RuntimeBrokerID: "broker-1", + ProjectID: tid("project-hub"), + RuntimeBrokerID: tid("broker-1"), } err := dispatcher.DispatchAgentStart(ctx, agent, "") @@ -1235,7 +1237,7 @@ func TestHTTPAgentDispatcher_DispatchAgentStart_ProjectSlugSetForGitRemoteWithou // The broker needs the projectSlug to resolve agent directories under // ~/.scion/projects// instead of falling back to the global project. project := &store.Project{ - ID: "project-git", + ID: tid("project-git"), Name: "Git Project", Slug: "git-project", GitRemote: "https://github.com/user/repo.git", @@ -1245,7 +1247,7 @@ func TestHTTPAgentDispatcher_DispatchAgentStart_ProjectSlugSetForGitRemoteWithou } broker := &store.RuntimeBroker{ - ID: "broker-1", + ID: tid("broker-1"), Name: "test-broker", Slug: "test-broker", Endpoint: "http://localhost:9800", @@ -1262,8 +1264,8 @@ func TestHTTPAgentDispatcher_DispatchAgentStart_ProjectSlugSetForGitRemoteWithou ID: "agent-git-1", Name: "git-agent", Slug: "git-agent", - ProjectID: "project-git", - RuntimeBrokerID: "broker-1", + ProjectID: tid("project-git"), + RuntimeBrokerID: tid("broker-1"), } err := dispatcher.DispatchAgentStart(ctx, agent, "") @@ -1287,7 +1289,7 @@ func TestHTTPAgentDispatcher_DispatchAgentStart_ResolvesEnvFromStorage(t *testin // Create a project project := &store.Project{ - ID: "project-env", + ID: tid("project-env"), Name: "env-test-project", Slug: "env-test-project", } @@ -1297,7 +1299,7 @@ func TestHTTPAgentDispatcher_DispatchAgentStart_ResolvesEnvFromStorage(t *testin // Create a runtime broker broker := &store.RuntimeBroker{ - ID: "broker-env", + ID: tid("broker-env"), Name: "test-broker", Slug: "test-broker", Endpoint: "http://localhost:9800", @@ -1309,8 +1311,8 @@ func TestHTTPAgentDispatcher_DispatchAgentStart_ResolvesEnvFromStorage(t *testin // Add a project provider with a local path provider := &store.ProjectProvider{ - ProjectID: "project-env", - BrokerID: "broker-env", + ProjectID: tid("project-env"), + BrokerID: tid("broker-env"), BrokerName: "test-broker", LocalPath: "/home/user/project/.scion", Status: store.BrokerStatusOnline, @@ -1321,18 +1323,18 @@ func TestHTTPAgentDispatcher_DispatchAgentStart_ResolvesEnvFromStorage(t *testin // Store an env var in project scope (simulating API key stored in hub) if err := memStore.CreateEnvVar(ctx, &store.EnvVar{ - ID: "ev-project-1", + ID: tid("ev-project-1"), Key: "GEMINI_API_KEY", Value: "test-api-key-123", Scope: "project", - ScopeID: "project-env", + ScopeID: tid("project-env"), }); err != nil { t.Fatalf("failed to set env var: %v", err) } // Store a user-scoped env var if err := memStore.CreateEnvVar(ctx, &store.EnvVar{ - ID: "ev-user-1", + ID: tid("ev-user-1"), Key: "CUSTOM_VAR", Value: "user-value", Scope: "user", @@ -1348,9 +1350,9 @@ func TestHTTPAgentDispatcher_DispatchAgentStart_ResolvesEnvFromStorage(t *testin ID: "agent-env", Name: "test-agent", Slug: "test-agent", - ProjectID: "project-env", + ProjectID: tid("project-env"), OwnerID: "owner-1", - RuntimeBrokerID: "broker-env", + RuntimeBrokerID: tid("broker-env"), AppliedConfig: &store.AgentAppliedConfig{ HarnessConfig: "gemini", Env: map[string]string{"EXISTING_VAR": "from-config"}, @@ -1393,7 +1395,7 @@ func TestHTTPAgentDispatcher_DispatchAgentStart_ConfigEnvTakesPrecedence(t *test // Create project and broker project := &store.Project{ - ID: "project-prec", + ID: tid("project-prec"), Name: "precedence-test", Slug: "precedence-test", } @@ -1402,7 +1404,7 @@ func TestHTTPAgentDispatcher_DispatchAgentStart_ConfigEnvTakesPrecedence(t *test } broker := &store.RuntimeBroker{ - ID: "broker-prec", + ID: tid("broker-prec"), Name: "test-broker", Slug: "test-broker", Endpoint: "http://localhost:9800", @@ -1414,11 +1416,11 @@ func TestHTTPAgentDispatcher_DispatchAgentStart_ConfigEnvTakesPrecedence(t *test // Store an env var that conflicts with config env if err := memStore.CreateEnvVar(ctx, &store.EnvVar{ - ID: "ev-prec-1", + ID: tid("ev-prec-1"), Key: "API_KEY", Value: "storage-value", Scope: "project", - ScopeID: "project-prec", + ScopeID: tid("project-prec"), }); err != nil { t.Fatalf("failed to set env var: %v", err) } @@ -1430,8 +1432,8 @@ func TestHTTPAgentDispatcher_DispatchAgentStart_ConfigEnvTakesPrecedence(t *test ID: "agent-prec", Name: "test-agent", Slug: "test-agent", - ProjectID: "project-prec", - RuntimeBrokerID: "broker-prec", + ProjectID: tid("project-prec"), + RuntimeBrokerID: tid("broker-prec"), AppliedConfig: &store.AgentAppliedConfig{ HarnessConfig: "gemini", Env: map[string]string{"API_KEY": "config-value"}, @@ -1456,7 +1458,7 @@ func TestHTTPAgentDispatcher_DispatchAgentStart_StorageOverridesEmptyConfigEnv(t memStore := createTestStore(t) project := &store.Project{ - ID: "project-empty-env", + ID: tid("project-empty-env"), Name: "empty-env-test", Slug: "empty-env-test", } @@ -1465,7 +1467,7 @@ func TestHTTPAgentDispatcher_DispatchAgentStart_StorageOverridesEmptyConfigEnv(t } broker := &store.RuntimeBroker{ - ID: "broker-empty-env", + ID: tid("broker-empty-env"), Name: "test-broker", Slug: "test-broker", Endpoint: "http://localhost:9800", @@ -1477,11 +1479,11 @@ func TestHTTPAgentDispatcher_DispatchAgentStart_StorageOverridesEmptyConfigEnv(t // Store an env var that should override the empty config value if err := memStore.CreateEnvVar(ctx, &store.EnvVar{ - ID: "ev-empty-1", + ID: tid("ev-empty-1"), Key: "GEMINI_API_KEY", Value: "stored-api-key", Scope: "project", - ScopeID: "project-empty-env", + ScopeID: tid("project-empty-env"), }); err != nil { t.Fatalf("failed to set env var: %v", err) } @@ -1493,8 +1495,8 @@ func TestHTTPAgentDispatcher_DispatchAgentStart_StorageOverridesEmptyConfigEnv(t ID: "agent-empty-env", Name: "test-agent", Slug: "test-agent", - ProjectID: "project-empty-env", - RuntimeBrokerID: "broker-empty-env", + ProjectID: tid("project-empty-env"), + RuntimeBrokerID: tid("broker-empty-env"), AppliedConfig: &store.AgentAppliedConfig{ HarnessConfig: "gemini", // Empty value = passthrough marker; storage should fill it in @@ -1526,7 +1528,7 @@ func TestHTTPAgentDispatcher_DispatchAgentCreate_InjectsDevToken(t *testing.T) { memStore := createTestStore(t) broker := &store.RuntimeBroker{ - ID: "host-1", + ID: tid("host-1"), Name: "test-host", Slug: "test-host", Endpoint: "http://localhost:9800", @@ -1541,11 +1543,11 @@ func TestHTTPAgentDispatcher_DispatchAgentCreate_InjectsDevToken(t *testing.T) { dispatcher.SetDevAuthToken("my-dev-token") agent := &store.Agent{ - ID: "agent-1", + ID: tid("agent-1"), Name: "test-agent", Slug: "test-agent", - ProjectID: "project-1", - RuntimeBrokerID: "host-1", + ProjectID: tid("project-1"), + RuntimeBrokerID: tid("host-1"), AppliedConfig: &store.AgentAppliedConfig{ HarnessConfig: "claude", }, @@ -1575,7 +1577,7 @@ func TestHTTPAgentDispatcher_DispatchAgentCreate_NoDevToken(t *testing.T) { memStore := createTestStore(t) broker := &store.RuntimeBroker{ - ID: "host-1", + ID: tid("host-1"), Name: "test-host", Slug: "test-host", Endpoint: "http://localhost:9800", @@ -1590,11 +1592,11 @@ func TestHTTPAgentDispatcher_DispatchAgentCreate_NoDevToken(t *testing.T) { // Do NOT set dev auth token agent := &store.Agent{ - ID: "agent-1", + ID: tid("agent-1"), Name: "test-agent", Slug: "test-agent", - ProjectID: "project-1", - RuntimeBrokerID: "host-1", + ProjectID: tid("project-1"), + RuntimeBrokerID: tid("host-1"), } err := dispatcher.DispatchAgentCreate(ctx, agent) @@ -1615,7 +1617,7 @@ func TestHTTPAgentDispatcher_DispatchAgentCreate_DevTokenMergesWithExistingEnv(t memStore := createTestStore(t) broker := &store.RuntimeBroker{ - ID: "host-1", + ID: tid("host-1"), Name: "test-host", Slug: "test-host", Endpoint: "http://localhost:9800", @@ -1630,11 +1632,11 @@ func TestHTTPAgentDispatcher_DispatchAgentCreate_DevTokenMergesWithExistingEnv(t dispatcher.SetDevAuthToken("my-dev-token") agent := &store.Agent{ - ID: "agent-1", + ID: tid("agent-1"), Name: "test-agent", Slug: "test-agent", - ProjectID: "project-1", - RuntimeBrokerID: "host-1", + ProjectID: tid("project-1"), + RuntimeBrokerID: tid("host-1"), AppliedConfig: &store.AgentAppliedConfig{ HarnessConfig: "claude", Env: map[string]string{ @@ -1664,7 +1666,7 @@ func TestHTTPAgentDispatcher_DispatchAgentStart_AppliesBrokerResponse(t *testing memStore := createTestStore(t) broker := &store.RuntimeBroker{ - ID: "broker-1", + ID: tid("broker-1"), Name: "test-broker", Slug: "test-broker", Endpoint: "http://localhost:9800", @@ -1689,11 +1691,11 @@ func TestHTTPAgentDispatcher_DispatchAgentStart_AppliesBrokerResponse(t *testing dispatcher := NewHTTPAgentDispatcherWithClient(memStore, mockClient, false, slog.Default()) agent := &store.Agent{ - ID: "agent-1", + ID: tid("agent-1"), Name: "test-agent", Slug: "test-agent", - ProjectID: "project-1", - RuntimeBrokerID: "broker-1", + ProjectID: tid("project-1"), + RuntimeBrokerID: tid("broker-1"), Phase: string(state.PhaseCreated), } @@ -1725,7 +1727,7 @@ func TestHTTPAgentDispatcher_DispatchAgentCreate_PropagatesGitClone(t *testing.T memStore := createTestStore(t) broker := &store.RuntimeBroker{ - ID: "host-1", + ID: tid("host-1"), Name: "test-host", Slug: "test-host", Endpoint: "http://localhost:9800", @@ -1742,8 +1744,8 @@ func TestHTTPAgentDispatcher_DispatchAgentCreate_PropagatesGitClone(t *testing.T ID: "agent-gc-1", Name: "git-clone-agent", Slug: "git-clone-agent", - ProjectID: "project-1", - RuntimeBrokerID: "host-1", + ProjectID: tid("project-1"), + RuntimeBrokerID: tid("host-1"), AppliedConfig: &store.AgentAppliedConfig{ HarnessConfig: "claude", Task: "implement feature", @@ -1788,7 +1790,7 @@ func TestHTTPAgentDispatcher_DispatchAgentCreate_PropagatesProfile(t *testing.T) memStore := createTestStore(t) broker := &store.RuntimeBroker{ - ID: "host-1", + ID: tid("host-1"), Name: "test-host", Slug: "test-host", Endpoint: "http://localhost:9800", @@ -1805,8 +1807,8 @@ func TestHTTPAgentDispatcher_DispatchAgentCreate_PropagatesProfile(t *testing.T) ID: "agent-profile-1", Name: "profile-agent", Slug: "profile-agent", - ProjectID: "project-1", - RuntimeBrokerID: "host-1", + ProjectID: tid("project-1"), + RuntimeBrokerID: tid("host-1"), AppliedConfig: &store.AgentAppliedConfig{ HarnessConfig: "claude", Task: "do something", @@ -1836,7 +1838,7 @@ func TestHTTPAgentDispatcher_DispatchAgentCreate_PropagatesProjectSlug_HubManage // Create a hub-managed project (no GitRemote) project := &store.Project{ - ID: "project-hub-managed", + ID: tid("project-hub-managed"), Name: "Hub Managed Project", Slug: "hub-managed-project", // No GitRemote = hub-managed @@ -1846,7 +1848,7 @@ func TestHTTPAgentDispatcher_DispatchAgentCreate_PropagatesProjectSlug_HubManage } broker := &store.RuntimeBroker{ - ID: "host-1", + ID: tid("host-1"), Name: "test-host", Slug: "test-host", Endpoint: "http://localhost:9800", @@ -1860,11 +1862,11 @@ func TestHTTPAgentDispatcher_DispatchAgentCreate_PropagatesProjectSlug_HubManage dispatcher := NewHTTPAgentDispatcherWithClient(memStore, mockClient, false, slog.Default()) agent := &store.Agent{ - ID: "agent-1", + ID: tid("agent-1"), Name: "test-agent", Slug: "test-agent", - ProjectID: "project-hub-managed", - RuntimeBrokerID: "host-1", + ProjectID: tid("project-hub-managed"), + RuntimeBrokerID: tid("host-1"), AppliedConfig: &store.AgentAppliedConfig{ HarnessConfig: "claude", }, @@ -1889,7 +1891,7 @@ func TestHTTPAgentDispatcher_DispatchAgentCreate_ProjectSlugSet_GitProject(t *te // Create a git-backed project (has GitRemote) without a local provider path. project := &store.Project{ - ID: "project-git", + ID: tid("project-git"), Name: "Git Project", Slug: "git-project", GitRemote: "github.com/test/repo", @@ -1899,7 +1901,7 @@ func TestHTTPAgentDispatcher_DispatchAgentCreate_ProjectSlugSet_GitProject(t *te } broker := &store.RuntimeBroker{ - ID: "host-1", + ID: tid("host-1"), Name: "test-host", Slug: "test-host", Endpoint: "http://localhost:9800", @@ -1913,11 +1915,11 @@ func TestHTTPAgentDispatcher_DispatchAgentCreate_ProjectSlugSet_GitProject(t *te dispatcher := NewHTTPAgentDispatcherWithClient(memStore, mockClient, false, slog.Default()) agent := &store.Agent{ - ID: "agent-1", + ID: tid("agent-1"), Name: "test-agent", Slug: "test-agent", - ProjectID: "project-git", - RuntimeBrokerID: "host-1", + ProjectID: tid("project-git"), + RuntimeBrokerID: tid("host-1"), AppliedConfig: &store.AgentAppliedConfig{ HarnessConfig: "claude", }, @@ -1943,7 +1945,7 @@ func TestHTTPAgentDispatcher_DispatchAgentCreate_EmptyProfile(t *testing.T) { memStore := createTestStore(t) broker := &store.RuntimeBroker{ - ID: "host-1", + ID: tid("host-1"), Name: "test-host", Slug: "test-host", Endpoint: "http://localhost:9800", @@ -1960,8 +1962,8 @@ func TestHTTPAgentDispatcher_DispatchAgentCreate_EmptyProfile(t *testing.T) { ID: "agent-no-profile-1", Name: "no-profile-agent", Slug: "no-profile-agent", - ProjectID: "project-1", - RuntimeBrokerID: "host-1", + ProjectID: tid("project-1"), + RuntimeBrokerID: tid("host-1"), AppliedConfig: &store.AgentAppliedConfig{ HarnessConfig: "claude", Task: "do something", @@ -1993,7 +1995,7 @@ func TestHTTPAgentDispatcher_DispatchAgentCreate_NoProjectSlug_LocalPathProject( // Even though the broker has the repo locally, all hub-linked projects with a // git remote use clone-based provisioning (HTTPS + GitHub token). project := &store.Project{ - ID: "project-local", + ID: tid("project-local"), Name: "Local Project", Slug: "local-project", GitRemote: "https://github.com/example/local-project.git", @@ -2003,7 +2005,7 @@ func TestHTTPAgentDispatcher_DispatchAgentCreate_NoProjectSlug_LocalPathProject( } broker := &store.RuntimeBroker{ - ID: "broker-1", + ID: tid("broker-1"), Name: "test-broker", Slug: "test-broker", Endpoint: "http://localhost:9800", @@ -2015,8 +2017,8 @@ func TestHTTPAgentDispatcher_DispatchAgentCreate_NoProjectSlug_LocalPathProject( // Add a project provider record WITH a local path provider := &store.ProjectProvider{ - ProjectID: "project-local", - BrokerID: "broker-1", + ProjectID: tid("project-local"), + BrokerID: tid("broker-1"), BrokerName: "test-broker", LocalPath: "/home/user/projects/myproject/.scion", Status: store.BrokerStatusOnline, @@ -2029,11 +2031,11 @@ func TestHTTPAgentDispatcher_DispatchAgentCreate_NoProjectSlug_LocalPathProject( dispatcher := NewHTTPAgentDispatcherWithClient(memStore, mockClient, false, slog.Default()) agent := &store.Agent{ - ID: "agent-1", + ID: tid("agent-1"), Name: "test-agent", Slug: "test-agent", - ProjectID: "project-local", - RuntimeBrokerID: "broker-1", + ProjectID: tid("project-local"), + RuntimeBrokerID: tid("broker-1"), AppliedConfig: &store.AgentAppliedConfig{ HarnessConfig: "claude", Workspace: "/should/be/cleared", @@ -2099,7 +2101,7 @@ func TestHTTPAgentDispatcher_DispatchAgentCreate_LinkedProjectNoGitRemote(t *tes // Create a linked project WITHOUT a GitRemote — this is what happens when // a user links a local project via `scion hub projects link`. project := &store.Project{ - ID: "project-linked-no-git", + ID: tid("project-linked-no-git"), Name: "Linked No Git Project", Slug: "linked-no-git", // No GitRemote — looks like hub-managed, but has a provider path @@ -2109,7 +2111,7 @@ func TestHTTPAgentDispatcher_DispatchAgentCreate_LinkedProjectNoGitRemote(t *tes } broker := &store.RuntimeBroker{ - ID: "broker-1", + ID: tid("broker-1"), Name: "test-broker", Slug: "test-broker", Endpoint: "http://localhost:9800", @@ -2121,8 +2123,8 @@ func TestHTTPAgentDispatcher_DispatchAgentCreate_LinkedProjectNoGitRemote(t *tes // Add a project provider record WITH a local path provider := &store.ProjectProvider{ - ProjectID: "project-linked-no-git", - BrokerID: "broker-1", + ProjectID: tid("project-linked-no-git"), + BrokerID: tid("broker-1"), BrokerName: "test-broker", LocalPath: "/Users/user/dev/projects/my-project/.scion", Status: store.BrokerStatusOnline, @@ -2135,11 +2137,11 @@ func TestHTTPAgentDispatcher_DispatchAgentCreate_LinkedProjectNoGitRemote(t *tes dispatcher := NewHTTPAgentDispatcherWithClient(memStore, mockClient, false, slog.Default()) agent := &store.Agent{ - ID: "agent-1", + ID: tid("agent-1"), Name: "test-agent", Slug: "test-agent", - ProjectID: "project-linked-no-git", - RuntimeBrokerID: "broker-1", + ProjectID: tid("project-linked-no-git"), + RuntimeBrokerID: tid("broker-1"), AppliedConfig: &store.AgentAppliedConfig{ HarnessConfig: "claude", Workspace: "/should/be/cleared", @@ -2180,7 +2182,7 @@ func TestBuildCreateRequest_ResolvesStorageEnvVars(t *testing.T) { // Create a runtime broker broker := &store.RuntimeBroker{ - ID: "host-1", + ID: tid("host-1"), Name: "test-host", Slug: "test-host", Endpoint: "http://localhost:9800", @@ -2192,11 +2194,11 @@ func TestBuildCreateRequest_ResolvesStorageEnvVars(t *testing.T) { // Store a user-scoped env var envVar := &store.EnvVar{ - ID: "ev-1", + ID: tid("ev-1"), Key: "GEMINI_API_KEY", Value: "stored-key-value", Scope: "user", - ScopeID: "user-1", + ScopeID: tid("user-1"), } if err := memStore.CreateEnvVar(ctx, envVar); err != nil { t.Fatalf("failed to create env var: %v", err) @@ -2206,11 +2208,11 @@ func TestBuildCreateRequest_ResolvesStorageEnvVars(t *testing.T) { dispatcher := NewHTTPAgentDispatcherWithClient(memStore, mockClient, false, slog.Default()) agent := &store.Agent{ - ID: "agent-1", + ID: tid("agent-1"), Name: "test-agent", Slug: "test-agent", - OwnerID: "user-1", - RuntimeBrokerID: "host-1", + OwnerID: tid("user-1"), + RuntimeBrokerID: tid("host-1"), AppliedConfig: &store.AgentAppliedConfig{}, } @@ -2233,7 +2235,7 @@ func TestBuildCreateRequest_ConfigEnvOverridesStorage(t *testing.T) { // Create a runtime broker broker := &store.RuntimeBroker{ - ID: "host-1", + ID: tid("host-1"), Name: "test-host", Slug: "test-host", Endpoint: "http://localhost:9800", @@ -2245,11 +2247,11 @@ func TestBuildCreateRequest_ConfigEnvOverridesStorage(t *testing.T) { // Store a user-scoped env var with the same key as config env envVar := &store.EnvVar{ - ID: "ev-1", + ID: tid("ev-1"), Key: "MY_KEY", Value: "storage-value", Scope: "user", - ScopeID: "user-1", + ScopeID: tid("user-1"), } if err := memStore.CreateEnvVar(ctx, envVar); err != nil { t.Fatalf("failed to create env var: %v", err) @@ -2259,11 +2261,11 @@ func TestBuildCreateRequest_ConfigEnvOverridesStorage(t *testing.T) { dispatcher := NewHTTPAgentDispatcherWithClient(memStore, mockClient, false, slog.Default()) agent := &store.Agent{ - ID: "agent-1", + ID: tid("agent-1"), Name: "test-agent", Slug: "test-agent", - OwnerID: "user-1", - RuntimeBrokerID: "host-1", + OwnerID: tid("user-1"), + RuntimeBrokerID: tid("host-1"), AppliedConfig: &store.AgentAppliedConfig{ Env: map[string]string{ "MY_KEY": "config-value", @@ -2288,7 +2290,7 @@ func TestBuildCreateRequest_ResolvesProjectAndUserScopes(t *testing.T) { // Create project and broker project := &store.Project{ - ID: "project-1", + ID: tid("project-1"), Name: "test-project", Slug: "test-project", } @@ -2297,7 +2299,7 @@ func TestBuildCreateRequest_ResolvesProjectAndUserScopes(t *testing.T) { } broker := &store.RuntimeBroker{ - ID: "host-1", + ID: tid("host-1"), Name: "test-host", Slug: "test-host", Endpoint: "http://localhost:9800", @@ -2309,11 +2311,11 @@ func TestBuildCreateRequest_ResolvesProjectAndUserScopes(t *testing.T) { // Store a project-scoped env var projectEnv := &store.EnvVar{ - ID: "ev-project", + ID: tid("ev-project"), Key: "SHARED_KEY", Value: "project-value", Scope: "project", - ScopeID: "project-1", + ScopeID: tid("project-1"), } if err := memStore.CreateEnvVar(ctx, projectEnv); err != nil { t.Fatalf("failed to create project env var: %v", err) @@ -2321,11 +2323,11 @@ func TestBuildCreateRequest_ResolvesProjectAndUserScopes(t *testing.T) { // Store a user-scoped env var with the same key (higher precedence) userEnv := &store.EnvVar{ - ID: "ev-user", + ID: tid("ev-user"), Key: "SHARED_KEY", Value: "user-value", Scope: "user", - ScopeID: "user-1", + ScopeID: tid("user-1"), } if err := memStore.CreateEnvVar(ctx, userEnv); err != nil { t.Fatalf("failed to create user env var: %v", err) @@ -2333,11 +2335,11 @@ func TestBuildCreateRequest_ResolvesProjectAndUserScopes(t *testing.T) { // Store a project-only env var projectOnly := &store.EnvVar{ - ID: "ev-project-only", + ID: tid("ev-project-only"), Key: "GROVE_ONLY_KEY", Value: "project-only-value", Scope: "project", - ScopeID: "project-1", + ScopeID: tid("project-1"), } if err := memStore.CreateEnvVar(ctx, projectOnly); err != nil { t.Fatalf("failed to create project-only env var: %v", err) @@ -2347,12 +2349,12 @@ func TestBuildCreateRequest_ResolvesProjectAndUserScopes(t *testing.T) { dispatcher := NewHTTPAgentDispatcherWithClient(memStore, mockClient, false, slog.Default()) agent := &store.Agent{ - ID: "agent-1", + ID: tid("agent-1"), Name: "test-agent", Slug: "test-agent", - OwnerID: "user-1", - ProjectID: "project-1", - RuntimeBrokerID: "host-1", + OwnerID: tid("user-1"), + ProjectID: tid("project-1"), + RuntimeBrokerID: tid("host-1"), AppliedConfig: &store.AgentAppliedConfig{}, } @@ -2378,7 +2380,7 @@ func TestDispatchAgentCreate_IncludesStorageEnvVars(t *testing.T) { // Create a runtime broker broker := &store.RuntimeBroker{ - ID: "host-1", + ID: tid("host-1"), Name: "test-host", Slug: "test-host", Endpoint: "http://localhost:9800", @@ -2390,11 +2392,11 @@ func TestDispatchAgentCreate_IncludesStorageEnvVars(t *testing.T) { // Store user-scoped env vars envVar := &store.EnvVar{ - ID: "ev-1", + ID: tid("ev-1"), Key: "API_TOKEN", Value: "secret-token-123", Scope: "user", - ScopeID: "user-1", + ScopeID: tid("user-1"), } if err := memStore.CreateEnvVar(ctx, envVar); err != nil { t.Fatalf("failed to create env var: %v", err) @@ -2404,11 +2406,11 @@ func TestDispatchAgentCreate_IncludesStorageEnvVars(t *testing.T) { dispatcher := NewHTTPAgentDispatcherWithClient(memStore, mockClient, false, slog.Default()) agent := &store.Agent{ - ID: "agent-1", + ID: tid("agent-1"), Name: "test-agent", Slug: "test-agent", - OwnerID: "user-1", - RuntimeBrokerID: "host-1", + OwnerID: tid("user-1"), + RuntimeBrokerID: tid("host-1"), AppliedConfig: &store.AgentAppliedConfig{ HarnessConfig: "claude", }, @@ -2438,7 +2440,7 @@ func TestBuildCreateRequest_PropagatesHarnessName(t *testing.T) { memStore := createTestStore(t) broker := &store.RuntimeBroker{ - ID: "host-1", + ID: tid("host-1"), Name: "test-host", Slug: "test-host", Endpoint: "http://localhost:9800", @@ -2455,8 +2457,8 @@ func TestBuildCreateRequest_PropagatesHarnessName(t *testing.T) { ID: "agent-harness-1", Name: "harness-agent", Slug: "harness-agent", - ProjectID: "project-1", - RuntimeBrokerID: "host-1", + ProjectID: tid("project-1"), + RuntimeBrokerID: tid("host-1"), AppliedConfig: &store.AgentAppliedConfig{ HarnessConfig: "gemini", Task: "do something", @@ -2483,7 +2485,7 @@ func TestHTTPAgentDispatcher_DispatchAgentStop_UsesSlugNotName(t *testing.T) { memStore := createTestStore(t) broker := &store.RuntimeBroker{ - ID: "host-1", + ID: tid("host-1"), Name: "test-host", Slug: "test-host", Endpoint: "http://localhost:9800", @@ -2497,10 +2499,10 @@ func TestHTTPAgentDispatcher_DispatchAgentStop_UsesSlugNotName(t *testing.T) { dispatcher := NewHTTPAgentDispatcherWithClient(memStore, mockClient, false, slog.Default()) agent := &store.Agent{ - ID: "agent-1", + ID: tid("agent-1"), Name: "My Special Agent!", Slug: "my-special-agent", - RuntimeBrokerID: "host-1", + RuntimeBrokerID: tid("host-1"), } err := dispatcher.DispatchAgentStop(ctx, agent) @@ -2518,7 +2520,7 @@ func TestHTTPAgentDispatcher_DispatchAgentDelete_UsesSlugNotName(t *testing.T) { memStore := createTestStore(t) broker := &store.RuntimeBroker{ - ID: "host-1", + ID: tid("host-1"), Name: "test-host", Slug: "test-host", Endpoint: "http://localhost:9800", @@ -2532,10 +2534,10 @@ func TestHTTPAgentDispatcher_DispatchAgentDelete_UsesSlugNotName(t *testing.T) { dispatcher := NewHTTPAgentDispatcherWithClient(memStore, mockClient, false, slog.Default()) agent := &store.Agent{ - ID: "agent-1", + ID: tid("agent-1"), Name: "slug Stres$@ . / test", Slug: "slug-stres-test", - RuntimeBrokerID: "host-1", + RuntimeBrokerID: tid("host-1"), } err := dispatcher.DispatchAgentDelete(ctx, agent, true, true, false, time.Time{}) @@ -2553,7 +2555,7 @@ func TestHTTPAgentDispatcher_DispatchAgentRestart_UsesSlugNotName(t *testing.T) memStore := createTestStore(t) broker := &store.RuntimeBroker{ - ID: "host-1", + ID: tid("host-1"), Name: "test-host", Slug: "test-host", Endpoint: "http://localhost:9800", @@ -2567,10 +2569,10 @@ func TestHTTPAgentDispatcher_DispatchAgentRestart_UsesSlugNotName(t *testing.T) dispatcher := NewHTTPAgentDispatcherWithClient(memStore, mockClient, false, slog.Default()) agent := &store.Agent{ - ID: "agent-1", + ID: tid("agent-1"), Name: "My Special Agent!", Slug: "my-special-agent", - RuntimeBrokerID: "host-1", + RuntimeBrokerID: tid("host-1"), } err := dispatcher.DispatchAgentRestart(ctx, agent) @@ -2588,7 +2590,7 @@ func TestHTTPAgentDispatcher_DispatchAgentMessage_UsesSlugNotName(t *testing.T) memStore := createTestStore(t) broker := &store.RuntimeBroker{ - ID: "host-1", + ID: tid("host-1"), Name: "test-host", Slug: "test-host", Endpoint: "http://localhost:9800", @@ -2602,10 +2604,10 @@ func TestHTTPAgentDispatcher_DispatchAgentMessage_UsesSlugNotName(t *testing.T) dispatcher := NewHTTPAgentDispatcherWithClient(memStore, mockClient, false, slog.Default()) agent := &store.Agent{ - ID: "agent-1", + ID: tid("agent-1"), Name: "My Special Agent!", Slug: "my-special-agent", - RuntimeBrokerID: "host-1", + RuntimeBrokerID: tid("host-1"), } err := dispatcher.DispatchAgentMessage(ctx, agent, "hello", false, nil) @@ -2623,7 +2625,7 @@ func TestHTTPAgentDispatcher_DispatchAgentStart_IncludesAgentIDAndSlug(t *testin memStore := createTestStore(t) broker := &store.RuntimeBroker{ - ID: "broker-id-test", + ID: tid("broker-id-test"), Name: "test-broker", Slug: "test-broker", Endpoint: "http://localhost:9800", @@ -2634,7 +2636,7 @@ func TestHTTPAgentDispatcher_DispatchAgentStart_IncludesAgentIDAndSlug(t *testin } project := &store.Project{ - ID: "project-id-test", + ID: tid("project-id-test"), Name: "test-project", Slug: "test-project", } @@ -2643,8 +2645,8 @@ func TestHTTPAgentDispatcher_DispatchAgentStart_IncludesAgentIDAndSlug(t *testin } provider := &store.ProjectProvider{ - ProjectID: "project-id-test", - BrokerID: "broker-id-test", + ProjectID: tid("project-id-test"), + BrokerID: tid("broker-id-test"), BrokerName: "test-broker", LocalPath: "/home/user/project/.scion", Status: store.BrokerStatusOnline, @@ -2660,8 +2662,8 @@ func TestHTTPAgentDispatcher_DispatchAgentStart_IncludesAgentIDAndSlug(t *testin ID: "agent-uuid-123", Name: "my-agent", Slug: "my-agent", - ProjectID: "project-id-test", - RuntimeBrokerID: "broker-id-test", + ProjectID: tid("project-id-test"), + RuntimeBrokerID: tid("broker-id-test"), AppliedConfig: &store.AgentAppliedConfig{ HarnessConfig: "claude", }, @@ -2695,7 +2697,7 @@ func TestHTTPAgentDispatcher_DispatchAgentStart_IncludesInlineConfig(t *testing. memStore := createTestStore(t) broker := &store.RuntimeBroker{ - ID: "broker-inline", + ID: tid("broker-inline"), Name: "test-broker", Slug: "test-broker", Endpoint: "http://localhost:9800", @@ -2706,7 +2708,7 @@ func TestHTTPAgentDispatcher_DispatchAgentStart_IncludesInlineConfig(t *testing. } project := &store.Project{ - ID: "project-inline", + ID: tid("project-inline"), Name: "test-project", Slug: "test-project", } @@ -2715,8 +2717,8 @@ func TestHTTPAgentDispatcher_DispatchAgentStart_IncludesInlineConfig(t *testing. } provider := &store.ProjectProvider{ - ProjectID: "project-inline", - BrokerID: "broker-inline", + ProjectID: tid("project-inline"), + BrokerID: tid("broker-inline"), BrokerName: "test-broker", LocalPath: "/home/user/project/.scion", Status: store.BrokerStatusOnline, @@ -2737,8 +2739,8 @@ func TestHTTPAgentDispatcher_DispatchAgentStart_IncludesInlineConfig(t *testing. ID: "agent-inline-cfg", Name: "inline-agent", Slug: "inline-agent", - ProjectID: "project-inline", - RuntimeBrokerID: "broker-inline", + ProjectID: tid("project-inline"), + RuntimeBrokerID: tid("broker-inline"), AppliedConfig: &store.AgentAppliedConfig{ HarnessConfig: "claude", InlineConfig: inlineCfg, @@ -2770,7 +2772,7 @@ func TestDispatchAgentStart_IncludesHubEndpoint(t *testing.T) { memStore := createTestStore(t) broker := &store.RuntimeBroker{ - ID: "host-1", + ID: tid("host-1"), Name: "test-host", Slug: "test-host", Endpoint: "http://localhost:9800", @@ -2785,12 +2787,12 @@ func TestDispatchAgentStart_IncludesHubEndpoint(t *testing.T) { dispatcher.SetHubEndpoint("http://hub.example.com:8080") agent := &store.Agent{ - ID: "agent-1", + ID: tid("agent-1"), Name: "test-agent", Slug: "test-agent", - ProjectID: "project-1", - OwnerID: "user-1", - RuntimeBrokerID: "host-1", + ProjectID: tid("project-1"), + OwnerID: tid("user-1"), + RuntimeBrokerID: tid("host-1"), AppliedConfig: &store.AgentAppliedConfig{ HarnessConfig: "claude", }, @@ -2817,11 +2819,11 @@ func TestDispatchAgentStart_IncludesHubEndpoint(t *testing.T) { } // Verify agent identity vars are also present - if mockClient.lastResolvedEnv["SCION_AGENT_ID"] != "agent-1" { - t.Errorf("SCION_AGENT_ID = %q, want %q", mockClient.lastResolvedEnv["SCION_AGENT_ID"], "agent-1") + if mockClient.lastResolvedEnv["SCION_AGENT_ID"] != tid("agent-1") { + t.Errorf("SCION_AGENT_ID = %q, want %q", mockClient.lastResolvedEnv["SCION_AGENT_ID"], tid("agent-1")) } - if mockClient.lastResolvedEnv["SCION_GROVE_ID"] != "project-1" { - t.Errorf("SCION_GROVE_ID = %q, want %q", mockClient.lastResolvedEnv["SCION_GROVE_ID"], "project-1") + if mockClient.lastResolvedEnv["SCION_GROVE_ID"] != tid("project-1") { + t.Errorf("SCION_GROVE_ID = %q, want %q", mockClient.lastResolvedEnv["SCION_GROVE_ID"], tid("project-1")) } } @@ -2831,7 +2833,7 @@ func TestHTTPAgentDispatcher_DispatchAgentCreate_PropagatesSharedWorkspace(t *te // Create a shared-workspace git project project := &store.Project{ - ID: "project-shared-ws", + ID: tid("project-shared-ws"), Name: "Shared WS", Slug: "shared-ws", GitRemote: "github.com/test/shared", @@ -2844,7 +2846,7 @@ func TestHTTPAgentDispatcher_DispatchAgentCreate_PropagatesSharedWorkspace(t *te } broker := &store.RuntimeBroker{ - ID: "host-1", + ID: tid("host-1"), Name: "test-host", Slug: "test-host", Endpoint: "http://localhost:9800", @@ -2861,8 +2863,8 @@ func TestHTTPAgentDispatcher_DispatchAgentCreate_PropagatesSharedWorkspace(t *te ID: "agent-shared-1", Name: "shared-agent", Slug: "shared-agent", - ProjectID: "project-shared-ws", - RuntimeBrokerID: "host-1", + ProjectID: tid("project-shared-ws"), + RuntimeBrokerID: tid("host-1"), AppliedConfig: &store.AgentAppliedConfig{ HarnessConfig: "claude", Workspace: "/home/user/.scion/projects/shared-ws", @@ -2897,7 +2899,7 @@ func TestHTTPAgentDispatcher_DispatchAgentStart_InjectsGCPIdentityEnv(t *testing memStore := createTestStore(t) project := &store.Project{ - ID: "project-gcp", + ID: tid("project-gcp"), Name: "gcp-project", Slug: "gcp-project", GitRemote: "https://github.com/example/repo.git", @@ -2907,7 +2909,7 @@ func TestHTTPAgentDispatcher_DispatchAgentStart_InjectsGCPIdentityEnv(t *testing } broker := &store.RuntimeBroker{ - ID: "broker-gcp", + ID: tid("broker-gcp"), Name: "test-broker", Slug: "test-broker", Endpoint: "http://localhost:9800", @@ -2918,8 +2920,8 @@ func TestHTTPAgentDispatcher_DispatchAgentStart_InjectsGCPIdentityEnv(t *testing } provider := &store.ProjectProvider{ - ProjectID: "project-gcp", - BrokerID: "broker-gcp", + ProjectID: tid("project-gcp"), + BrokerID: tid("broker-gcp"), BrokerName: "test-broker", LocalPath: "/home/user/projects/myproject/.scion", Status: store.BrokerStatusOnline, @@ -2935,14 +2937,14 @@ func TestHTTPAgentDispatcher_DispatchAgentStart_InjectsGCPIdentityEnv(t *testing ID: "agent-gcp-1", Name: "gcp-agent", Slug: "gcp-agent", - ProjectID: "project-gcp", - RuntimeBrokerID: "broker-gcp", + ProjectID: tid("project-gcp"), + RuntimeBrokerID: tid("broker-gcp"), AppliedConfig: &store.AgentAppliedConfig{ GCPIdentity: &store.GCPIdentityConfig{ MetadataMode: store.GCPMetadataModeAssign, ServiceAccountID: "sa-123", ServiceAccountEmail: "sa@proj.iam.gserviceaccount.com", - ProjectID: "my-project", + ProjectID: tid("my-project"), }, }, } @@ -2963,7 +2965,7 @@ func TestHTTPAgentDispatcher_DispatchAgentStart_InjectsGCPIdentityEnv(t *testing if v := mockClient.lastResolvedEnv["SCION_METADATA_SA_EMAIL"]; v != "sa@proj.iam.gserviceaccount.com" { t.Errorf("expected SCION_METADATA_SA_EMAIL='sa@proj.iam.gserviceaccount.com', got %q", v) } - if v := mockClient.lastResolvedEnv["SCION_METADATA_PROJECT_ID"]; v != "my-project" { + if v := mockClient.lastResolvedEnv["SCION_METADATA_PROJECT_ID"]; v != tid("my-project") { t.Errorf("expected SCION_METADATA_PROJECT_ID='my-project', got %q", v) } } @@ -2973,7 +2975,7 @@ func TestHTTPAgentDispatcher_DispatchAgentStart_GCPBlockMode(t *testing.T) { memStore := createTestStore(t) project := &store.Project{ - ID: "project-gcp-block", + ID: tid("project-gcp-block"), Name: "gcp-project", Slug: "gcp-project", GitRemote: "https://github.com/example/repo.git", @@ -2983,7 +2985,7 @@ func TestHTTPAgentDispatcher_DispatchAgentStart_GCPBlockMode(t *testing.T) { } broker := &store.RuntimeBroker{ - ID: "broker-gcp-block", + ID: tid("broker-gcp-block"), Name: "test-broker", Slug: "test-broker", Endpoint: "http://localhost:9800", @@ -2994,8 +2996,8 @@ func TestHTTPAgentDispatcher_DispatchAgentStart_GCPBlockMode(t *testing.T) { } provider := &store.ProjectProvider{ - ProjectID: "project-gcp-block", - BrokerID: "broker-gcp-block", + ProjectID: tid("project-gcp-block"), + BrokerID: tid("broker-gcp-block"), BrokerName: "test-broker", LocalPath: "/home/user/projects/myproject/.scion", Status: store.BrokerStatusOnline, @@ -3011,8 +3013,8 @@ func TestHTTPAgentDispatcher_DispatchAgentStart_GCPBlockMode(t *testing.T) { ID: "agent-gcp-block", Name: "gcp-agent", Slug: "gcp-agent", - ProjectID: "project-gcp-block", - RuntimeBrokerID: "broker-gcp-block", + ProjectID: tid("project-gcp-block"), + RuntimeBrokerID: tid("broker-gcp-block"), AppliedConfig: &store.AgentAppliedConfig{ GCPIdentity: &store.GCPIdentityConfig{ MetadataMode: store.GCPMetadataModeBlock, @@ -3063,7 +3065,7 @@ func TestBuildCreateRequest_UserGitHubTokenPrecedesApp(t *testing.T) { } project := &store.Project{ - ID: "project-1", + ID: tid("project-1"), Name: "test-project", Slug: "test-project", GitHubInstallationID: &installID, @@ -3073,7 +3075,7 @@ func TestBuildCreateRequest_UserGitHubTokenPrecedesApp(t *testing.T) { } broker := &store.RuntimeBroker{ - ID: "host-1", + ID: tid("host-1"), Name: "test-host", Slug: "test-host", Endpoint: "http://localhost:9800", @@ -3093,12 +3095,12 @@ func TestBuildCreateRequest_UserGitHubTokenPrecedesApp(t *testing.T) { dispatcher.SetGitHubAppMinter(minter) agent := &store.Agent{ - ID: "agent-1", + ID: tid("agent-1"), Name: "test-agent", Slug: "test-agent", - OwnerID: "user-1", - ProjectID: "project-1", - RuntimeBrokerID: "host-1", + OwnerID: tid("user-1"), + ProjectID: tid("project-1"), + RuntimeBrokerID: tid("host-1"), AppliedConfig: &store.AgentAppliedConfig{ Env: map[string]string{ "GITHUB_TOKEN": "ghp_user_pat_xyz", @@ -3148,7 +3150,7 @@ func TestBuildCreateRequest_GitHubAppTokenWhenNoUserToken(t *testing.T) { } project := &store.Project{ - ID: "project-1", + ID: tid("project-1"), Name: "test-project", Slug: "test-project", GitHubInstallationID: &installID, @@ -3158,7 +3160,7 @@ func TestBuildCreateRequest_GitHubAppTokenWhenNoUserToken(t *testing.T) { } broker := &store.RuntimeBroker{ - ID: "host-1", + ID: tid("host-1"), Name: "test-host", Slug: "test-host", Endpoint: "http://localhost:9800", @@ -3178,12 +3180,12 @@ func TestBuildCreateRequest_GitHubAppTokenWhenNoUserToken(t *testing.T) { dispatcher.SetGitHubAppMinter(minter) agent := &store.Agent{ - ID: "agent-1", + ID: tid("agent-1"), Name: "test-agent", Slug: "test-agent", - OwnerID: "user-1", - ProjectID: "project-1", - RuntimeBrokerID: "host-1", + OwnerID: tid("user-1"), + ProjectID: tid("project-1"), + RuntimeBrokerID: tid("host-1"), AppliedConfig: &store.AgentAppliedConfig{}, } diff --git a/pkg/hub/logquery_test.go b/pkg/hub/logquery_test.go index c128d8ff0..dc6ce2343 100644 --- a/pkg/hub/logquery_test.go +++ b/pkg/hub/logquery_test.go @@ -180,7 +180,7 @@ func TestConvertLogEntry(t *testing.T) { Payload: "Agent started processing task", Labels: map[string]string{ "agent_id": "abc123", - "project_id": "my-project", + "project_id": tid("my-project"), }, InsertID: "insert-1", } diff --git a/pkg/hub/maintenance_executors_test.go b/pkg/hub/maintenance_executors_test.go index 08f194855..df4069f3d 100644 --- a/pkg/hub/maintenance_executors_test.go +++ b/pkg/hub/maintenance_executors_test.go @@ -28,11 +28,10 @@ import ( "time" "github.com/GoogleCloudPlatform/scion/pkg/secret" - "github.com/GoogleCloudPlatform/scion/pkg/store/sqlite" ) func TestSecretMigrationExecutor_NoGCPBackend(t *testing.T) { - s, err := sqlite.New(":memory:") + s, err := newTestStore(":memory:") if err != nil { t.Fatalf("failed to create sqlite store: %v", err) } diff --git a/pkg/hub/messagebroker.go b/pkg/hub/messagebroker.go index bf6fd062a..ac863a8e6 100644 --- a/pkg/hub/messagebroker.go +++ b/pkg/hub/messagebroker.go @@ -45,7 +45,7 @@ const brokerCallbackTimeout = 30 * time.Second type MessageBrokerProxy struct { bus eventbus.EventBus store store.Store - events *ChannelEventPublisher + events EventPublisher getDispatcher func() AgentDispatcher log *slog.Logger messageLog *slog.Logger @@ -63,7 +63,7 @@ type MessageBrokerProxy struct { func NewMessageBrokerProxy( b eventbus.EventBus, s store.Store, - events *ChannelEventPublisher, + events EventPublisher, getDispatcher func() AgentDispatcher, log *slog.Logger, ) *MessageBrokerProxy { diff --git a/pkg/hub/messagebroker_test.go b/pkg/hub/messagebroker_test.go index 66b5bd18f..9ee4aa605 100644 --- a/pkg/hub/messagebroker_test.go +++ b/pkg/hub/messagebroker_test.go @@ -27,7 +27,6 @@ import ( "github.com/GoogleCloudPlatform/scion/pkg/eventbus" "github.com/GoogleCloudPlatform/scion/pkg/messages" "github.com/GoogleCloudPlatform/scion/pkg/store" - "github.com/GoogleCloudPlatform/scion/pkg/store/sqlite" ) // brokerMockDispatcher records dispatched messages for test assertions. @@ -98,7 +97,7 @@ func (d *brokerMockDispatcher) getMessages() []brokerDispatchedMsg { func newBrokerTestStore(t *testing.T) store.Store { t.Helper() - s, err := sqlite.New(":memory:") + s, err := newTestStore(":memory:") if err != nil { t.Fatalf("failed to create test store: %v", err) } @@ -115,7 +114,7 @@ func setupBrokerTestProject(t *testing.T, s store.Store) string { // Create a runtime broker for agent FK constraints rb := &store.RuntimeBroker{ - ID: "broker-1", + ID: tid("broker-1"), Name: "test-broker", Slug: "test-broker", Endpoint: "http://localhost:9800", @@ -146,7 +145,7 @@ func setupBrokerTestAgent(t *testing.T, s store.Store, projectID, slug, phase st Slug: slug, ProjectID: projectID, Phase: phase, - RuntimeBrokerID: "broker-1", + RuntimeBrokerID: tid("broker-1"), Visibility: store.VisibilityPrivate, } if err := s.CreateAgent(context.Background(), agent); err != nil { @@ -239,7 +238,7 @@ func TestMessageBrokerProxy_BroadcastSkipsSender(t *testing.T) { s := newBrokerTestStore(t) projectID := setupBrokerTestProject(t, s) setupBrokerTestAgent(t, s, projectID, "sender-agent", "running") - setupBrokerTestAgent(t, s, projectID, "other-agent", "running") + setupBrokerTestAgent(t, s, projectID, tid("other-agent"), "running") events := NewChannelEventPublisher() defer events.Close() @@ -265,7 +264,7 @@ func TestMessageBrokerProxy_BroadcastSkipsSender(t *testing.T) { if len(dispatched) != 1 { t.Fatalf("expected 1 message (sender excluded), got %d", len(dispatched)) } - if dispatched[0].agentSlug != "other-agent" { + if dispatched[0].agentSlug != tid("other-agent") { t.Errorf("expected message delivered to 'other-agent', got %q", dispatched[0].agentSlug) } } diff --git a/pkg/hub/notifications.go b/pkg/hub/notifications.go index 377393632..a79fcd3ba 100644 --- a/pkg/hub/notifications.go +++ b/pkg/hub/notifications.go @@ -33,7 +33,7 @@ import ( // messages to subscriber agents. type NotificationDispatcher struct { store store.Store - events *ChannelEventPublisher + events EventPublisher getDispatcher func() AgentDispatcher // lazy getter; dispatcher may be set after startup log *slog.Logger messageLog *slog.Logger // dedicated message audit logger (nil = disabled) @@ -48,7 +48,7 @@ type NotificationDispatcher struct { // The getDispatcher function is called at dispatch time to resolve the current // AgentDispatcher, allowing the dispatcher to be set up after the notification // system starts (e.g. in combined hub+web mode). -func NewNotificationDispatcher(s store.Store, events *ChannelEventPublisher, getDispatcher func() AgentDispatcher, log *slog.Logger) *NotificationDispatcher { +func NewNotificationDispatcher(s store.Store, events EventPublisher, getDispatcher func() AgentDispatcher, log *slog.Logger) *NotificationDispatcher { return &NotificationDispatcher{ store: s, events: events, diff --git a/pkg/hub/notifications_integration_test.go b/pkg/hub/notifications_integration_test.go index d458d509b..dbec588b2 100644 --- a/pkg/hub/notifications_integration_test.go +++ b/pkg/hub/notifications_integration_test.go @@ -67,7 +67,7 @@ func setupIntegrationTest(t *testing.T) *integrationTestEnv { srv.SetDispatcher(recorder) project := &store.Project{ - ID: "project-integ", + ID: tid("project-integ"), Name: "Integration Project", Slug: "integration-project", Visibility: store.VisibilityPrivate, @@ -75,7 +75,7 @@ func setupIntegrationTest(t *testing.T) *integrationTestEnv { require.NoError(t, s.CreateProject(ctx, project)) broker := &store.RuntimeBroker{ - ID: "broker-integ", + ID: tid("broker-integ"), Name: "Integration Broker", Slug: "integration-broker", Status: store.BrokerStatusOnline, @@ -115,6 +115,10 @@ func setupIntegrationTest(t *testing.T) *integrationTestEnv { func (env *integrationTestEnv) createAgentWithNotify(t *testing.T, callingAgent *store.Agent, subAgentName string) *store.Agent { t.Helper() + // The created sub-agent's created_by/owner_id FK references the users table, + // so seed a user sharing the calling agent's ID. + permSeedUser(t, context.Background(), env.store, callingAgent.ID) + token, err := env.tokenSvc.GenerateAgentToken(callingAgent.ID, env.project.ID, []AgentTokenScope{ ScopeAgentStatusUpdate, ScopeAgentCreate, @@ -186,7 +190,7 @@ func TestIntegration_AgentCreatesAgentWithNotify_FullFlow(t *testing.T) { // Create the parent agent (subscriber) parent := &store.Agent{ - ID: "agent-parent", + ID: tid("agent-parent"), Slug: "parent-agent", Name: "Parent Agent", ProjectID: env.project.ID, @@ -242,7 +246,7 @@ func TestIntegration_AgentCreatesAgentWithNotify_WaitingForInput(t *testing.T) { ctx := context.Background() parent := &store.Agent{ - ID: "agent-parent-wfi", + ID: tid("agent-parent-wfi"), Slug: "parent-agent-wfi", Name: "Parent Agent WFI", ProjectID: env.project.ID, @@ -290,7 +294,7 @@ func TestIntegration_AgentCreatesAgentWithNotify_MultipleStatusChanges(t *testin ctx := context.Background() parent := &store.Agent{ - ID: "agent-parent-multi", + ID: tid("agent-parent-multi"), Slug: "parent-multi", Name: "Parent Multi", ProjectID: env.project.ID, @@ -348,7 +352,7 @@ func TestIntegration_StatusNormalization_LowercaseEventMatchesUppercaseTrigger(t ctx := context.Background() parent := &store.Agent{ - ID: "agent-parent-case", + ID: tid("agent-parent-case"), Slug: "parent-case", Name: "Parent Case", ProjectID: env.project.ID, @@ -385,7 +389,7 @@ func TestIntegration_StatusNormalization_DedupAcrossCaseBoundaries(t *testing.T) ctx := context.Background() parent := &store.Agent{ - ID: "agent-parent-dedup", + ID: tid("agent-parent-dedup"), Slug: "parent-dedup", Name: "Parent Dedup", ProjectID: env.project.ID, @@ -426,7 +430,7 @@ func TestIntegration_StatusNormalization_NonTriggerStatusNoNotification(t *testi ctx := context.Background() parent := &store.Agent{ - ID: "agent-parent-nontrig", + ID: tid("agent-parent-nontrig"), Slug: "parent-nontrig", Name: "Parent NonTrig", ProjectID: env.project.ID, @@ -476,7 +480,7 @@ func TestIntegration_SubscriptionCleanup_HardDeleteCascades(t *testing.T) { ctx := context.Background() parent := &store.Agent{ - ID: "agent-parent-hdel", + ID: tid("agent-parent-hdel"), Slug: "parent-hdel", Name: "Parent Hard Delete", ProjectID: env.project.ID, @@ -528,7 +532,7 @@ func TestIntegration_SubscriptionCleanup_SoftDeleteRetainsSubscriptions(t *testi env.srv.config.SoftDeleteRetention = 24 * time.Hour parent := &store.Agent{ - ID: "agent-parent-sdel", + ID: tid("agent-parent-sdel"), Slug: "parent-sdel", Name: "Parent Soft Delete", ProjectID: env.project.ID, @@ -741,7 +745,7 @@ func TestIntegration_MultipleSubscribers_AgentAndUser(t *testing.T) { // Create parent agent parent := &store.Agent{ - ID: "agent-parent-multi-sub", + ID: tid("agent-parent-multi-sub"), Slug: "parent-multi-sub", Name: "Parent Multi Sub", ProjectID: env.project.ID, @@ -756,7 +760,7 @@ func TestIntegration_MultipleSubscribers_AgentAndUser(t *testing.T) { // User also subscribes to the same child (manually, since the API doesn't support this yet) userSub := &store.NotificationSubscription{ - ID: "user-sub-multi", + ID: tid("user-sub-multi"), AgentID: child.ID, SubscriberType: store.SubscriberTypeUser, SubscriberID: DevUserID, @@ -802,7 +806,7 @@ func TestIntegration_NoNotifyFlag_NoSubscription(t *testing.T) { ctx := context.Background() parent := &store.Agent{ - ID: "agent-parent-no-notify", + ID: tid("agent-parent-no-notify"), Slug: "parent-no-notify", Name: "Parent No Notify", ProjectID: env.project.ID, @@ -812,6 +816,9 @@ func TestIntegration_NoNotifyFlag_NoSubscription(t *testing.T) { } require.NoError(t, env.store.CreateAgent(ctx, parent)) + // The created sub-agent's created_by/owner_id FK references the users table. + permSeedUser(t, ctx, env.store, parent.ID) + // Create sub-agent WITHOUT notify token, err := env.tokenSvc.GenerateAgentToken(parent.ID, env.project.ID, []AgentTokenScope{ ScopeAgentStatusUpdate, @@ -860,7 +867,7 @@ func TestIntegration_PATCHSubscriptionTriggers(t *testing.T) { // Create a subscription via store (SubscriberID must match DevUserID) sub := &store.NotificationSubscription{ - ID: "sub-patch-test", + ID: tid("sub-patch-test"), Scope: store.SubscriptionScopeProject, SubscriberType: store.SubscriberTypeUser, SubscriberID: DevUserID, diff --git a/pkg/hub/notifications_test.go b/pkg/hub/notifications_test.go index c744934a0..dd678daff 100644 --- a/pkg/hub/notifications_test.go +++ b/pkg/hub/notifications_test.go @@ -29,7 +29,6 @@ import ( "github.com/GoogleCloudPlatform/scion/pkg/eventbus" "github.com/GoogleCloudPlatform/scion/pkg/messages" "github.com/GoogleCloudPlatform/scion/pkg/store" - "github.com/GoogleCloudPlatform/scion/pkg/store/sqlite" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -149,7 +148,7 @@ type notificationTestEnv struct { func setupNotificationTest(t *testing.T) *notificationTestEnv { t.Helper() - s, err := sqlite.New(":memory:") + s, err := newTestStore(":memory:") if err != nil { t.Fatalf("failed to create test store: %v", err) } @@ -172,7 +171,7 @@ func setupNotificationTest(t *testing.T) *notificationTestEnv { require.NoError(t, s.CreateProject(ctx, project)) broker := &store.RuntimeBroker{ - ID: "broker-1", + ID: tid("broker-1"), Name: "Test Broker", Slug: "test-broker", Status: store.BrokerStatusOnline, @@ -186,7 +185,7 @@ func setupNotificationTest(t *testing.T) *notificationTestEnv { Template: "claude", ProjectID: project.ID, Phase: string(state.PhaseRunning), - RuntimeBrokerID: "broker-1", + RuntimeBrokerID: tid("broker-1"), Visibility: store.VisibilityPrivate, } require.NoError(t, s.CreateAgent(ctx, watched)) @@ -198,7 +197,7 @@ func setupNotificationTest(t *testing.T) *notificationTestEnv { Template: "claude", ProjectID: project.ID, Phase: string(state.PhaseRunning), - RuntimeBrokerID: "broker-1", + RuntimeBrokerID: tid("broker-1"), Visibility: store.VisibilityPrivate, } require.NoError(t, s.CreateAgent(ctx, subscriber)) @@ -1238,7 +1237,7 @@ func TestUpdateNotificationSubscriptionTriggers_NotFound(t *testing.T) { env := setupNotificationTest(t) ctx := context.Background() - err := env.store.UpdateNotificationSubscriptionTriggers(ctx, "nonexistent-id", []string{"COMPLETED"}) + err := env.store.UpdateNotificationSubscriptionTriggers(ctx, tid("nonexistent-id"), []string{"COMPLETED"}) assert.ErrorIs(t, err, store.ErrNotFound) } diff --git a/pkg/hub/project_cache_test.go b/pkg/hub/project_cache_test.go index 0b5c1b1fe..cc30fd3aa 100644 --- a/pkg/hub/project_cache_test.go +++ b/pkg/hub/project_cache_test.go @@ -49,13 +49,15 @@ func createTestLinkedProject(t *testing.T, srv *Server, s store.Store, name, rem // Create a provider broker record with a local path brokerLocalPath := t.TempDir() broker := &store.RuntimeBroker{ - ID: "test-broker-remote", + ID: tid("test-broker-remote"), Name: "remote-broker", + Slug: "remote-broker", } require.NoError(t, s.CreateRuntimeBroker(context.Background(), broker)) require.NoError(t, s.AddProjectProvider(context.Background(), &store.ProjectProvider{ - ProjectID: project.ID, - BrokerID: broker.ID, + ProjectID: project.ID, + BrokerID: broker.ID, + BrokerName: broker.Name, // LocalPath is set to simulate a linked project with workspace on broker LocalPath: brokerLocalPath, })) @@ -101,18 +103,20 @@ func TestResolveProjectWebDAVPath_LinkedProject_EmbeddedBroker(t *testing.T) { project := createTestGitProject(t, srv, "WebDAV Embedded", "https://github.com/org/embedded-repo.git") embeddedPath := t.TempDir() - embeddedBrokerID := "test-embedded-broker" + embeddedBrokerID := tid("test-embedded-broker") srv.SetEmbeddedBrokerID(embeddedBrokerID) broker := &store.RuntimeBroker{ ID: embeddedBrokerID, Name: "embedded-broker", + Slug: "embedded-broker", } require.NoError(t, s.CreateRuntimeBroker(context.Background(), broker)) require.NoError(t, s.AddProjectProvider(context.Background(), &store.ProjectProvider{ - ProjectID: project.ID, - BrokerID: embeddedBrokerID, - LocalPath: embeddedPath, + ProjectID: project.ID, + BrokerID: embeddedBrokerID, + BrokerName: broker.Name, + LocalPath: embeddedPath, })) // For embedded broker, should serve directly from local path @@ -143,15 +147,16 @@ func TestIsLinkedProject_EmbeddedBrokerOnly(t *testing.T) { srv, s := testServer(t) project := createTestGitProject(t, srv, "IsLinked Embedded", "https://github.com/org/emb.git") - embeddedBrokerID := "embedded-only" + embeddedBrokerID := tid("embedded-only") srv.SetEmbeddedBrokerID(embeddedBrokerID) - broker := &store.RuntimeBroker{ID: embeddedBrokerID, Name: "emb"} + broker := &store.RuntimeBroker{ID: embeddedBrokerID, Name: "emb", Slug: "emb"} require.NoError(t, s.CreateRuntimeBroker(context.Background(), broker)) require.NoError(t, s.AddProjectProvider(context.Background(), &store.ProjectProvider{ - ProjectID: project.ID, - BrokerID: embeddedBrokerID, - LocalPath: "/some/path", + ProjectID: project.ID, + BrokerID: embeddedBrokerID, + BrokerName: broker.Name, + LocalPath: "/some/path", })) // Embedded broker with local path should NOT be considered "linked" (it's co-located) diff --git a/pkg/hub/project_settings_handlers_test.go b/pkg/hub/project_settings_handlers_test.go index cf34abc6e..db262a638 100644 --- a/pkg/hub/project_settings_handlers_test.go +++ b/pkg/hub/project_settings_handlers_test.go @@ -283,7 +283,7 @@ func TestProjectSettings_NotFound(t *testing.T) { func createTestProjectForSettings(t *testing.T, s store.Store) *store.Project { t.Helper() project := &store.Project{ - ID: "test-project-settings-" + t.Name(), + ID: tid("test-project-settings-" + t.Name()), Name: "Test Project", Slug: "test-project-settings", Visibility: "private", diff --git a/pkg/hub/project_workspace_handlers_test.go b/pkg/hub/project_workspace_handlers_test.go index 50068be08..fd6420a11 100644 --- a/pkg/hub/project_workspace_handlers_test.go +++ b/pkg/hub/project_workspace_handlers_test.go @@ -884,7 +884,7 @@ func TestSharedDirFiles_GitProjectWithEmbeddedBroker(t *testing.T) { // Create a broker and set it as the embedded broker broker := &store.RuntimeBroker{ - ID: "embedded-broker-001", + ID: tid("embedded-broker-001"), Name: "local-broker", Slug: "local-broker", Endpoint: "http://localhost:9090", @@ -937,7 +937,7 @@ func TestSharedDirFiles_GitProjectMultipleProviders(t *testing.T) { // Create embedded broker embeddedBroker := &store.RuntimeBroker{ - ID: "embedded-broker-002", + ID: tid("embedded-broker-002"), Name: "local-broker", Slug: "local-broker-2", Endpoint: "http://localhost:9090", @@ -948,7 +948,7 @@ func TestSharedDirFiles_GitProjectMultipleProviders(t *testing.T) { // Create a second (remote) broker remoteBroker := &store.RuntimeBroker{ - ID: "remote-broker-001", + ID: tid("remote-broker-001"), Name: "remote-broker", Slug: "remote-broker", Endpoint: "http://remote:9090", @@ -1091,7 +1091,7 @@ func TestProjectWorkspacePull_MethodNotAllowed(t *testing.T) { // Create shared-workspace project directly in the store to avoid clone attempt project := store.Project{ - ID: "pull-method-test-id", + ID: tid("pull-method-test-id"), Name: "Pull Method Test", Slug: "pull-method-test", GitRemote: "github.com/test/pull-method", diff --git a/pkg/hub/resolve_secrets_test.go b/pkg/hub/resolve_secrets_test.go index 1d934fcd7..a4c6eb54f 100644 --- a/pkg/hub/resolve_secrets_test.go +++ b/pkg/hub/resolve_secrets_test.go @@ -31,50 +31,50 @@ func TestResolveSecrets(t *testing.T) { // Create test secrets across multiple scopes userSecret := &store.Secret{ - ID: "s1", + ID: tid("s1"), Key: "API_KEY", EncryptedValue: "user-api-key", SecretType: store.SecretTypeEnvironment, Target: "API_KEY", Scope: store.ScopeUser, - ScopeID: "user-1", + ScopeID: tid("user-1"), } projectSecret := &store.Secret{ - ID: "s2", + ID: tid("s2"), Key: "DB_PASS", EncryptedValue: "project-db-pass", SecretType: store.SecretTypeEnvironment, Target: "DATABASE_PASSWORD", Scope: store.ScopeProject, - ScopeID: "project-1", + ScopeID: tid("project-1"), } // Project-level override of user API_KEY projectOverride := &store.Secret{ - ID: "s3", + ID: tid("s3"), Key: "API_KEY", EncryptedValue: "project-api-key", SecretType: store.SecretTypeEnvironment, Target: "API_KEY", Scope: store.ScopeProject, - ScopeID: "project-1", + ScopeID: tid("project-1"), } fileSecret := &store.Secret{ - ID: "s4", + ID: tid("s4"), Key: "TLS_CERT", EncryptedValue: "cert-data", SecretType: store.SecretTypeFile, Target: "/etc/ssl/cert.pem", Scope: store.ScopeUser, - ScopeID: "user-1", + ScopeID: tid("user-1"), } varSecret := &store.Secret{ - ID: "s5", + ID: tid("s5"), Key: "CONFIG", EncryptedValue: `{"key":"val"}`, SecretType: store.SecretTypeVariable, Target: "config", Scope: store.ScopeUser, - ScopeID: "user-1", + ScopeID: tid("user-1"), } for _, s := range []*store.Secret{userSecret, projectSecret, projectOverride, fileSecret, varSecret} { @@ -90,10 +90,10 @@ func TestResolveSecrets(t *testing.T) { dispatcher.SetSecretBackend(backend) agent := &store.Agent{ - ID: "agent-1", + ID: tid("agent-1"), Name: "test-agent", - OwnerID: "user-1", - ProjectID: "project-1", + OwnerID: tid("user-1"), + ProjectID: tid("project-1"), } resolved, err := dispatcher.resolveSecrets(ctx, agent) @@ -165,31 +165,31 @@ func TestResolveSecrets_WithBackend(t *testing.T) { // Seed secrets directly through the store for _, s := range []*store.Secret{ { - ID: "s1", + ID: tid("s1"), Key: "API_KEY", EncryptedValue: "user-api-key", SecretType: store.SecretTypeEnvironment, Target: "API_KEY", Scope: store.ScopeUser, - ScopeID: "user-1", + ScopeID: tid("user-1"), }, { - ID: "s2", + ID: tid("s2"), Key: "API_KEY", EncryptedValue: "project-api-key", SecretType: store.SecretTypeEnvironment, Target: "API_KEY", Scope: store.ScopeProject, - ScopeID: "project-1", + ScopeID: tid("project-1"), }, { - ID: "s3", + ID: tid("s3"), Key: "DB_PASS", EncryptedValue: "db-password", SecretType: store.SecretTypeEnvironment, Target: "DATABASE_PASSWORD", Scope: store.ScopeProject, - ScopeID: "project-1", + ScopeID: tid("project-1"), }, } { if err := memStore.CreateSecret(ctx, s); err != nil { @@ -204,10 +204,10 @@ func TestResolveSecrets_WithBackend(t *testing.T) { dispatcher.SetSecretBackend(backend) agent := &store.Agent{ - ID: "agent-1", + ID: tid("agent-1"), Name: "test-agent", - OwnerID: "user-1", - ProjectID: "project-1", + OwnerID: tid("user-1"), + ProjectID: tid("project-1"), } resolved, err := dispatcher.resolveSecrets(ctx, agent) @@ -256,7 +256,7 @@ func TestResolveSecrets_NoOwner(t *testing.T) { dispatcher.SetSecretBackend(backend) agent := &store.Agent{ - ID: "agent-1", + ID: tid("agent-1"), Name: "test-agent", } @@ -276,7 +276,7 @@ func TestResolveSecrets_HubScope(t *testing.T) { // Create hub-scoped secrets hubSecret := &store.Secret{ - ID: "sh1", + ID: tid("sh1"), Key: "ORG_API_KEY", EncryptedValue: "hub-org-api-key", SecretType: store.SecretTypeEnvironment, @@ -286,7 +286,7 @@ func TestResolveSecrets_HubScope(t *testing.T) { } // Create a hub secret that will be overridden by user scope hubOverridden := &store.Secret{ - ID: "sh2", + ID: tid("sh2"), Key: "API_KEY", EncryptedValue: "hub-default-api-key", SecretType: store.SecretTypeEnvironment, @@ -296,17 +296,17 @@ func TestResolveSecrets_HubScope(t *testing.T) { } // Create user secret that overrides hub userSecret := &store.Secret{ - ID: "su1", + ID: tid("su1"), Key: "API_KEY", EncryptedValue: "user-personal-api-key", SecretType: store.SecretTypeEnvironment, Target: "API_KEY", Scope: store.ScopeUser, - ScopeID: "user-1", + ScopeID: tid("user-1"), } // Create a hub secret overridden by project scope hubProjectOverridden := &store.Secret{ - ID: "sh3", + ID: tid("sh3"), Key: "DB_PASS", EncryptedValue: "hub-default-db-pass", SecretType: store.SecretTypeEnvironment, @@ -315,13 +315,13 @@ func TestResolveSecrets_HubScope(t *testing.T) { ScopeID: "test-hub-id", } projectSecret := &store.Secret{ - ID: "sg1", + ID: tid("sg1"), Key: "DB_PASS", EncryptedValue: "project-db-pass", SecretType: store.SecretTypeEnvironment, Target: "DB_PASS", Scope: store.ScopeProject, - ScopeID: "project-1", + ScopeID: tid("project-1"), } for _, s := range []*store.Secret{hubSecret, hubOverridden, userSecret, hubProjectOverridden, projectSecret} { @@ -338,8 +338,8 @@ func TestResolveSecrets_HubScope(t *testing.T) { agent := &store.Agent{ ID: "agent-hub-1", Name: "hub-test-agent", - OwnerID: "user-1", - ProjectID: "project-1", + OwnerID: tid("user-1"), + ProjectID: tid("project-1"), } resolved, err := dispatcher.resolveSecrets(ctx, agent) @@ -403,10 +403,10 @@ func TestResolveSecrets_NoBackend(t *testing.T) { dispatcher := NewHTTPAgentDispatcherWithClient(memStore, mockClient, false, slog.Default()) agent := &store.Agent{ - ID: "agent-1", + ID: tid("agent-1"), Name: "test-agent", - OwnerID: "user-1", - ProjectID: "project-1", + OwnerID: tid("user-1"), + ProjectID: tid("project-1"), } resolved, err := dispatcher.resolveSecrets(ctx, agent) diff --git a/pkg/hub/scheduler.go b/pkg/hub/scheduler.go index 28a02abc8..825ad68c4 100644 --- a/pkg/hub/scheduler.go +++ b/pkg/hub/scheduler.go @@ -122,6 +122,61 @@ func (s *Scheduler) RegisterRecurring(name string, intervalMinutes int, fn func( }) } +// RegisterRecurringSingleton registers a recurring handler that runs on AT MOST +// ONE replica per tick, guarded by a cluster-wide advisory lock keyed by key. +// +// This is the singleton/leader primitive (P3-5) for cluster-wide-once work such +// as the stale-agent sweep, stalled detection, purge, schedule evaluation, and +// the GitHub App health check. In a single-replica or SQLite deployment the +// lock is a no-op that always succeeds, so the handler runs exactly as before. +// +// If the store does not implement store.AdvisoryLocker, the handler runs +// unguarded (correct for a single replica). +func (s *Scheduler) RegisterRecurringSingleton(name string, intervalMinutes int, key store.AdvisoryLockKey, fn func(ctx context.Context)) { + s.RegisterRecurring(name, intervalMinutes, s.singletonGuard(name, key, fn)) +} + +// singletonGuard wraps fn so it only runs while this replica holds the named +// advisory lock. The lock is released as soon as fn returns, so the next tick on +// any replica is free to win it. +// +// A store that does not implement AdvisoryLocker falls open to running fn +// unguarded — correct for a single-replica / SQLite deployment where there is no +// other replica to collide with. +// +// A lock-acquisition error (e.g. a connection timeout to Postgres) does NOT fall +// open: in a multi-replica deployment running unguarded would let two replicas +// execute the same singleton work concurrently. Since we cannot prove we are +// alone when the lock query itself failed, we SKIP this tick and let the next one +// retry. Missing one tick of idempotent maintenance work is safer than running it +// in duplicate. +func (s *Scheduler) singletonGuard(name string, key store.AdvisoryLockKey, fn func(ctx context.Context)) func(ctx context.Context) { + return func(ctx context.Context) { + locker, ok := s.store.(store.AdvisoryLocker) + if !ok { + fn(ctx) + return + } + acquired, release, err := locker.TryAdvisoryLock(ctx, key) + if err != nil { + s.log.Warn("Scheduler: advisory lock acquisition failed; skipping tick to avoid running unguarded across replicas", + "name", name, "error", err) + return + } + if !acquired { + s.log.Debug("Scheduler: singleton handler held by another replica, skipping", + "name", name) + return + } + defer func() { + if rerr := release(); rerr != nil { + s.log.Warn("Scheduler: advisory unlock failed", "name", name, "error", rerr) + } + }() + fn(ctx) + } +} + // Start begins the root ticker loop and runs eligible handlers immediately // on startup (tick 0). The provided context is used as the parent for handler // invocations. Before starting the ticker, persisted one-shot timers are @@ -298,6 +353,27 @@ func (s *Scheduler) fireEvent(ctx context.Context, evt store.ScheduledEvent, was status = store.ScheduledEventExpired } + // Multi-replica dedup (P3-5): several replicas may each recover the same + // pending event from the database on startup and arm a timer for it. Claim + // the event atomically (pending -> status) before running its side effect so + // exactly one replica delivers it. If the store supports claiming and we + // lose the race (already claimed/cancelled), skip silently. Backends without + // the capability fall through to the legacy run-then-mark behavior, which is + // correct for a single replica. + if claimer, ok := s.store.(store.ScheduledEventClaimer); ok { + claimed, err := claimer.ClaimScheduledEvent(ctx, evt.ID, status) + if err != nil { + s.log.Warn("Scheduler: failed to claim scheduled event; skipping to avoid duplicate", + "eventID", evt.ID, "type", evt.EventType, "error", err) + return + } + if !claimed { + s.log.Debug("Scheduler: scheduled event already claimed by another replica, skipping", + "eventID", evt.ID, "type", evt.EventType) + return + } + } + var errMsg string func() { defer func() { diff --git a/pkg/hub/scheduler_test.go b/pkg/hub/scheduler_test.go index 0c3d8caef..29fb504d1 100644 --- a/pkg/hub/scheduler_test.go +++ b/pkg/hub/scheduler_test.go @@ -1265,3 +1265,83 @@ func TestDispatchAgentEventHandler_CreatesAgentNoDispatcher(t *testing.T) { t.Error("agent was not created in the store") } } + +// ============================================================================ +// Singleton Guard Tests (advisory-lock leader election) +// ============================================================================ + +// lockerStore is a minimal store that also implements store.AdvisoryLocker so +// the singleton guard's lock-acquisition branches can be exercised in isolation. +type lockerStore struct { + store.Store // embedded; unused methods panic if called + + acquired bool + err error + released *atomic.Int32 +} + +func (l *lockerStore) TryAdvisoryLock(_ context.Context, _ store.AdvisoryLockKey) (bool, func() error, error) { + if l.err != nil { + return false, func() error { return nil }, l.err + } + return l.acquired, func() error { + if l.released != nil { + l.released.Add(1) + } + return nil + }, nil +} + +// TestSingletonGuard_SkipsTickOnLockError verifies that a lock-acquisition error +// (e.g. a connection timeout) causes the tick to be SKIPPED rather than running +// the handler unguarded — running unguarded would let multiple replicas execute +// the same singleton work concurrently. +func TestSingletonGuard_SkipsTickOnLockError(t *testing.T) { + s := NewScheduler(&lockerStore{err: fmt.Errorf("connection timeout")}, slog.Default()) + + var ran atomic.Int32 + guarded := s.singletonGuard("test", store.LockSoftDeletePurge, func(_ context.Context) { + ran.Add(1) + }) + guarded(context.Background()) + + if got := ran.Load(); got != 0 { + t.Fatalf("handler ran %d times on lock error; expected 0 (tick must be skipped, not run unguarded)", got) + } +} + +// TestSingletonGuard_RunsWhenAcquired verifies the handler runs and the lock is +// released when acquisition succeeds. +func TestSingletonGuard_RunsWhenAcquired(t *testing.T) { + var released atomic.Int32 + s := NewScheduler(&lockerStore{acquired: true, released: &released}, slog.Default()) + + var ran atomic.Int32 + guarded := s.singletonGuard("test", store.LockSoftDeletePurge, func(_ context.Context) { + ran.Add(1) + }) + guarded(context.Background()) + + if got := ran.Load(); got != 1 { + t.Fatalf("handler ran %d times; expected 1", got) + } + if got := released.Load(); got != 1 { + t.Fatalf("lock released %d times; expected 1", got) + } +} + +// TestSingletonGuard_SkipsWhenHeldByAnother verifies the handler does NOT run +// when another replica holds the lock (acquired=false, no error). +func TestSingletonGuard_SkipsWhenHeldByAnother(t *testing.T) { + s := NewScheduler(&lockerStore{acquired: false}, slog.Default()) + + var ran atomic.Int32 + guarded := s.singletonGuard("test", store.LockSoftDeletePurge, func(_ context.Context) { + ran.Add(1) + }) + guarded(context.Background()) + + if got := ran.Load(); got != 0 { + t.Fatalf("handler ran %d times while lock held by another replica; expected 0", got) + } +} diff --git a/pkg/hub/server.go b/pkg/hub/server.go index aef19366d..e1184323f 100644 --- a/pkg/hub/server.go +++ b/pkg/hub/server.go @@ -19,6 +19,7 @@ import ( "context" "crypto/rand" "crypto/sha256" + "database/sql" "encoding/base64" "encoding/hex" "encoding/json" @@ -35,10 +36,12 @@ import ( "github.com/GoogleCloudPlatform/scion/pkg/eventbus" "github.com/GoogleCloudPlatform/scion/pkg/hub/githubapp" "github.com/GoogleCloudPlatform/scion/pkg/messages" + "github.com/GoogleCloudPlatform/scion/pkg/observability/dbmetrics" "github.com/GoogleCloudPlatform/scion/pkg/secret" "github.com/GoogleCloudPlatform/scion/pkg/storage" "github.com/GoogleCloudPlatform/scion/pkg/store" "github.com/GoogleCloudPlatform/scion/pkg/util/logging" + "github.com/google/uuid" "github.com/robfig/cron/v3" ) @@ -533,6 +536,14 @@ type Server struct { // GCP token metrics tracker (nil = disabled) gcpTokenMetrics *GCPTokenMetrics + // Database connection-pool / notify metrics recorder (P0-5). Defaults to a + // disabled no-op recorder; SetDBMetrics wires a real exporter. Drives the + // connection-pool sampler started in StartBackgroundServices. + dbMetrics dbmetrics.Recorder + + // stopPoolSampler stops the DB pool-stats sampling goroutine on shutdown. + stopPoolSampler func() + // Message broker proxy for pub/sub message routing (nil = disabled) messageBrokerProxy *MessageBrokerProxy @@ -1044,11 +1055,13 @@ func (s *Server) backupSigningKeyToStore(ctx context.Context, keyName, encodedVa // signingKeySecretID returns a deterministic primary key for a signing key record, // scoped to the hub instance to avoid PK collisions during migration. +// signingKeySecretID derives a stable surrogate primary key for the signing-key +// backup secret. The store keys secrets by the (key, scope, scope_id) triple, so +// the ID is only a surrogate; it is generated deterministically as a UUIDv5 so +// the value is valid for the UUID-typed primary key while remaining stable +// across restarts. func signingKeySecretID(keyName, hubID string) string { - if hubID == "" { - return fmt.Sprintf("hub-%s", keyName) - } - return fmt.Sprintf("hub-%s-%s", hubID, keyName) + return uuid.NewSHA1(uuid.NameSpaceOID, []byte("hub-signing-key:"+hubID+":"+keyName)).String() } // SetDispatcher sets the agent dispatcher for co-located runtime broker operations. @@ -1245,6 +1258,16 @@ func (s *Server) SetMetrics(m MetricsRecorder) { s.metrics = m } +// SetDBMetrics wires the database connection-pool / notify metrics recorder +// (P0-5). When set to an enabled recorder before StartBackgroundServices, the +// hub starts sampling the DB connection pool into the pool gauges. Passing a +// disabled recorder (or never calling this) leaves pool sampling off. +func (s *Server) SetDBMetrics(rec dbmetrics.Recorder) { + s.mu.Lock() + defer s.mu.Unlock() + s.dbMetrics = rec +} + // GetMaintenanceState returns the runtime maintenance state. func (s *Server) GetMaintenanceState() *MaintenanceState { return s.maintenance @@ -1279,7 +1302,7 @@ func (s *Server) SetEventPublisher(ep EventPublisher) { } // StartNotificationDispatcher creates and starts the notification dispatcher -// if a ChannelEventPublisher is available. It uses a lazy getter for the +// if a subscription-capable EventPublisher is available. It uses a lazy getter for the // AgentDispatcher so it works even if SetDispatcher is called later. // Safe to call multiple times; subsequent calls are no-ops. func (s *Server) StartNotificationDispatcher() { @@ -1290,13 +1313,12 @@ func (s *Server) StartNotificationDispatcher() { return // already started } - ep, ok := s.events.(*ChannelEventPublisher) - if !ok { + if _, isNoop := s.events.(noopEventPublisher); isNoop || s.events == nil { slog.Warn("Event publisher does not support subscriptions, notification dispatcher not started") return } - nd := NewNotificationDispatcher(s.store, ep, s.GetDispatcher, logging.Subsystem("hub.notifications")) + nd := NewNotificationDispatcher(s.store, s.events, s.GetDispatcher, logging.Subsystem("hub.notifications")) nd.messageLog = s.dedicatedMessageLog nd.channelRegistry = s.channelRegistry s.notificationDispatcher = nd @@ -1304,7 +1326,7 @@ func (s *Server) StartNotificationDispatcher() { } // StartMessageBroker creates and starts the message broker proxy if a -// ChannelEventPublisher is available. The broker enables pub/sub message +// subscription-capable EventPublisher is available. The broker enables pub/sub message // routing with topic-based subscriptions and broadcast fan-out. // Safe to call multiple times; subsequent calls are no-ops. func (s *Server) StartMessageBroker(b eventbus.EventBus) { @@ -1315,13 +1337,12 @@ func (s *Server) StartMessageBroker(b eventbus.EventBus) { return // already started } - ep, ok := s.events.(*ChannelEventPublisher) - if !ok { + if _, isNoop := s.events.(noopEventPublisher); isNoop || s.events == nil { slog.Warn("Event publisher does not support subscriptions, message broker proxy not started") return } - proxy := NewMessageBrokerProxy(b, s.store, ep, s.GetDispatcher, logging.Subsystem("hub.broker")) + proxy := NewMessageBrokerProxy(b, s.store, s.events, s.GetDispatcher, logging.Subsystem("hub.broker")) proxy.messageLog = s.dedicatedMessageLog s.messageBrokerProxy = proxy proxy.Start() @@ -1838,14 +1859,19 @@ func (s *Server) StartBackgroundServices(ctx context.Context) { // Initialize and start the scheduler s.scheduler = NewScheduler(s.store, logging.Subsystem("hub.scheduler")) - s.scheduler.RegisterRecurring("agent-heartbeat-timeout", 1, s.agentHeartbeatTimeoutHandler()) - s.scheduler.RegisterRecurring("agent-stalled-detection", 1, s.agentStalledDetectionHandler()) + // Recurring sweeps are cluster-wide-once work: under multi-replica Postgres + // they must run on a single replica per tick (gated by an advisory lock), + // otherwise every replica would publish duplicate offline/stalled events and + // race on the schedule claim. On SQLite the lock is a no-op. See + // CONCURRENCY-AUDIT.md §"Singleton / leader". + s.scheduler.RegisterRecurringSingleton("agent-heartbeat-timeout", 1, store.LockAgentHeartbeatTimeout, s.agentHeartbeatTimeoutHandler()) + s.scheduler.RegisterRecurringSingleton("agent-stalled-detection", 1, store.LockAgentStalledDetection, s.agentStalledDetectionHandler()) if s.config.SoftDeleteRetention > 0 { - s.scheduler.RegisterRecurring("soft-delete-purge", 60, s.purgeHandler()) + s.scheduler.RegisterRecurringSingleton("soft-delete-purge", 60, store.LockSoftDeletePurge, s.purgeHandler()) } s.scheduler.RegisterEventHandler("message", s.messageEventHandler()) s.scheduler.RegisterEventHandler("dispatch_agent", s.dispatchAgentEventHandler()) - s.scheduler.RegisterRecurring("schedule-evaluator", 1, s.evaluateSchedulesHandler()) + s.scheduler.RegisterRecurringSingleton("schedule-evaluator", 1, store.LockScheduleEvaluator, s.evaluateSchedulesHandler()) // Register GitHub App health check if the app is configured s.mu.RLock() @@ -1857,11 +1883,21 @@ func (s *Server) StartBackgroundServices(ctx context.Context) { if ghWebhooksEnabled { interval = 1440 // 24 hours when webhooks are enabled } - s.scheduler.RegisterRecurring("github-app-health-check", interval, s.githubAppHealthCheckHandler()) + s.scheduler.RegisterRecurringSingleton("github-app-health-check", interval, store.LockGitHubAppHealthCheck, s.githubAppHealthCheckHandler()) } s.scheduler.Start(ctx) + // Start the DB connection-pool stats sampler (P3-6 -> P0-5 gauges). It is a + // no-op unless an enabled recorder was wired via SetDBMetrics and the store + // exposes its *sql.DB; this keeps connection-budget saturation observable + // under multi-replica Postgres (see CONNECTION-BUDGET.md). + if rec := s.dbMetrics; rec != nil { + if dbp, ok := s.store.(interface{ DB() *sql.DB }); ok { + s.stopPoolSampler = dbmetrics.StartPoolSampler(ctx, rec, dbp.DB(), 0) + } + } + // Start rate limiter cleanup goroutine (exits when ctx is cancelled). if s.gcpTokenRateLimiter != nil { s.gcpTokenRateLimiter.StartCleanup(ctx) @@ -1935,6 +1971,11 @@ func (s *Server) Shutdown(ctx context.Context) error { s.scheduler.Stop() } + // Stop the DB pool-stats sampler. + if s.stopPoolSampler != nil { + s.stopPoolSampler() + } + // Stop notification dispatcher before closing event publisher if s.notificationDispatcher != nil { s.notificationDispatcher.Stop() diff --git a/pkg/hub/server_test.go b/pkg/hub/server_test.go index 2fc49dad0..63589080e 100644 --- a/pkg/hub/server_test.go +++ b/pkg/hub/server_test.go @@ -28,14 +28,13 @@ import ( smpb "cloud.google.com/go/secretmanager/apiv1/secretmanagerpb" "github.com/GoogleCloudPlatform/scion/pkg/secret" "github.com/GoogleCloudPlatform/scion/pkg/store" - "github.com/GoogleCloudPlatform/scion/pkg/store/sqlite" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) func TestServer_PersistentSigningKeys(t *testing.T) { // Create an in-memory SQLite store - s, err := sqlite.New(":memory:") + s, err := newTestStore(":memory:") if err != nil { t.Fatalf("failed to create test store: %v", err) } @@ -88,7 +87,7 @@ func TestServer_PersistentSigningKeys(t *testing.T) { } func TestServer_PersistentSigningKeys_WithHubID(t *testing.T) { - s, err := sqlite.New(":memory:") + s, err := newTestStore(":memory:") if err != nil { t.Fatalf("failed to create test store: %v", err) } @@ -154,7 +153,7 @@ func TestServer_PersistentSigningKeys_WithHubID(t *testing.T) { } func TestServer_SigningKeysExcludedFromResolve(t *testing.T) { - s, err := sqlite.New(":memory:") + s, err := newTestStore(":memory:") if err != nil { t.Fatalf("failed to create test store: %v", err) } @@ -188,7 +187,7 @@ func TestServer_UserTokenSurvivesRestart(t *testing.T) { // Simulate the exact production scenario: sign in, restart server, validate token. // Uses a file-based SQLite DB to match production behavior. dbPath := filepath.Join(t.TempDir(), "test-hub.db") - s, err := sqlite.New(dbPath) + s, err := newTestStore(dbPath) if err != nil { t.Fatalf("failed to create test store: %v", err) } @@ -210,7 +209,7 @@ func TestServer_UserTokenSurvivesRestart(t *testing.T) { } accessToken, _, _, err := srv1.userTokenService.GenerateTokenPair( - "user-1", "test@example.com", "Test User", store.UserRoleAdmin, ClientTypeWeb, + tid("user-1"), "test@example.com", "Test User", store.UserRoleAdmin, ClientTypeWeb, ) if err != nil { t.Fatalf("GenerateTokenPair failed: %v", err) @@ -225,7 +224,7 @@ func TestServer_UserTokenSurvivesRestart(t *testing.T) { // Close the store and reopen from the same file (simulates process restart) s.Close() - s2, err := sqlite.New(dbPath) + s2, err := newTestStore(dbPath) if err != nil { t.Fatalf("failed to reopen test store: %v", err) } @@ -264,7 +263,7 @@ func TestServer_SigningKeyMigration_LegacyHubScopeID(t *testing.T) { // Simulate the pre-hubID-namespacing scenario where keys were stored // with ScopeID="hub". A new server with a real hubID should find them // via the migration fallback. - s, err := sqlite.New(":memory:") + s, err := newTestStore(":memory:") if err != nil { t.Fatalf("failed to create test store: %v", err) } @@ -283,14 +282,14 @@ func TestServer_SigningKeyMigration_LegacyHubScopeID(t *testing.T) { userEncoded := base64.StdEncoding.EncodeToString(legacyUserKey) if err := s.CreateSecret(ctx, &store.Secret{ - ID: "hub-agent_signing_key", Key: SecretKeyAgentSigningKey, + ID: tid("hub-agent_signing_key"), Key: SecretKeyAgentSigningKey, EncryptedValue: agentEncoded, Scope: store.ScopeHub, ScopeID: "hub", Description: "Hub signing key for agent_signing_key", }); err != nil { t.Fatalf("failed to create legacy agent key: %v", err) } if err := s.CreateSecret(ctx, &store.Secret{ - ID: "hub-user_signing_key", Key: SecretKeyUserSigningKey, + ID: tid("hub-user_signing_key"), Key: SecretKeyUserSigningKey, EncryptedValue: userEncoded, Scope: store.ScopeHub, ScopeID: "hub", Description: "Hub signing key for user_signing_key", }); err != nil { @@ -346,7 +345,7 @@ func TestServer_SigningKeyMigration_DeletesLegacyFromBackend(t *testing.T) { // When migrating signing keys from legacy scope IDs, the old secret // should also be deleted from the secret backend to prevent stale secrets // from confusing label-based auto-discovery by external consumers. - s, err := sqlite.New(":memory:") + s, err := newTestStore(":memory:") if err != nil { t.Fatalf("failed to create test store: %v", err) } @@ -367,7 +366,7 @@ func TestServer_SigningKeyMigration_DeletesLegacyFromBackend(t *testing.T) { encoded := base64.StdEncoding.EncodeToString(legacyKey) if err := s.CreateSecret(ctx, &store.Secret{ - ID: "hub-user_signing_key", Key: SecretKeyUserSigningKey, + ID: tid("hub-user_signing_key"), Key: SecretKeyUserSigningKey, EncryptedValue: encoded, Scope: store.ScopeHub, ScopeID: legacyScopeID, Description: "legacy user signing key", }); err != nil { @@ -425,7 +424,7 @@ func TestServer_SigningKeyMigration_DeletesLegacyFromBackend(t *testing.T) { func TestServer_SigningKeyBootstrapWithSecretBackend(t *testing.T) { // Verify that when SecretBackend is set in ServerConfig, signing keys // are loaded through it and synced from SQLite to the backend. - s, err := sqlite.New(":memory:") + s, err := newTestStore(":memory:") if err != nil { t.Fatalf("failed to create test store: %v", err) } @@ -454,7 +453,7 @@ func TestServer_SigningKeyBootstrapWithSecretBackend(t *testing.T) { // Generate a token with this key accessToken, _, _, err := srv1.userTokenService.GenerateTokenPair( - "user-1", "test@example.com", "Test", store.UserRoleAdmin, ClientTypeWeb, + tid("user-1"), "test@example.com", "Test", store.UserRoleAdmin, ClientTypeWeb, ) if err != nil { t.Fatalf("GenerateTokenPair failed: %v", err) @@ -495,7 +494,7 @@ func TestServer_SigningKeyBootstrapWithSecretBackend(t *testing.T) { func TestServer_SigningKeySyncFromStoreToBackend(t *testing.T) { // Verify that keys pre-existing in SQLite are synced to the secret backend // when the backend is newly configured (migration from no-backend to backend). - s, err := sqlite.New(":memory:") + s, err := newTestStore(":memory:") if err != nil { t.Fatalf("failed to create test store: %v", err) } @@ -549,7 +548,7 @@ func TestServer_SigningKeyEmptyValueFromStore(t *testing.T) { // EncryptedValue="" in SQLite (using SecretRef instead). If GCP SM // later becomes unavailable, ensureSigningKey must not silently return // a nil key — it should generate a new one. - s, err := sqlite.New(":memory:") + s, err := newTestStore(":memory:") if err != nil { t.Fatalf("failed to create test store: %v", err) } @@ -562,7 +561,7 @@ func TestServer_SigningKeyEmptyValueFromStore(t *testing.T) { // Insert a signing key row with empty EncryptedValue (as the GCP backend would) ctx := context.Background() emptySecret := &store.Secret{ - ID: "hub-" + hubID + "-" + SecretKeyUserSigningKey, + ID: tid("hub-" + hubID + "-" + SecretKeyUserSigningKey), Key: SecretKeyUserSigningKey, EncryptedValue: "", SecretRef: "gcpsm:projects/test/secrets/test-key", @@ -597,7 +596,7 @@ func TestServer_SigningKeyEmptyValueFromStore(t *testing.T) { // Verify the new key actually works for token operations accessToken, _, _, err := srv.userTokenService.GenerateTokenPair( - "user-1", "test@example.com", "Test", store.UserRoleAdmin, ClientTypeWeb, + tid("user-1"), "test@example.com", "Test", store.UserRoleAdmin, ClientTypeWeb, ) if err != nil { t.Fatalf("GenerateTokenPair failed: %v", err) @@ -614,7 +613,7 @@ func TestServer_SigningKeyEmptyValueFromStore(t *testing.T) { func TestServer_SigningKeyBackupAfterBackendSet(t *testing.T) { // Verify that after persisting a key through the secret backend, // the actual key value remains in SQLite as a backup. - s, err := sqlite.New(":memory:") + s, err := newTestStore(":memory:") if err != nil { t.Fatalf("failed to create test store: %v", err) } @@ -658,7 +657,7 @@ func TestServer_SigningKeyBackupAfterBackendSet(t *testing.T) { } func TestServer_GenerateAgentToken_DevAuthAutoGrantsScopes(t *testing.T) { - s, err := sqlite.New(":memory:") + s, err := newTestStore(":memory:") if err != nil { t.Fatalf("failed to create test store: %v", err) } @@ -680,7 +679,7 @@ func TestServer_GenerateAgentToken_DevAuthAutoGrantsScopes(t *testing.T) { t.Cleanup(func() { srv.Shutdown(context.Background()) }) // Generate token without any additional scopes - token, err := srv.GenerateAgentToken("agent-1", "project-1", nil) + token, err := srv.GenerateAgentToken(tid("agent-1"), tid("project-1"), nil) if err != nil { t.Fatalf("GenerateAgentToken failed: %v", err) } @@ -706,7 +705,7 @@ func TestServer_GenerateAgentToken_DevAuthAutoGrantsScopes(t *testing.T) { } func TestServer_GenerateAgentToken_DevAuthDeduplicatesScopes(t *testing.T) { - s, err := sqlite.New(":memory:") + s, err := newTestStore(":memory:") if err != nil { t.Fatalf("failed to create test store: %v", err) } @@ -728,7 +727,7 @@ func TestServer_GenerateAgentToken_DevAuthDeduplicatesScopes(t *testing.T) { t.Cleanup(func() { srv.Shutdown(context.Background()) }) // Generate token with explicit scopes that overlap with auto-granted ones - token, err := srv.GenerateAgentToken("agent-1", "project-1", nil, + token, err := srv.GenerateAgentToken(tid("agent-1"), tid("project-1"), nil, ScopeAgentCreate, ScopeAgentLifecycle, ScopeProjectSecretRead) if err != nil { t.Fatalf("GenerateAgentToken failed: %v", err) @@ -757,7 +756,7 @@ func TestServer_GenerateAgentToken_DevAuthDeduplicatesScopes(t *testing.T) { } func TestServer_GenerateAgentToken_NoDevAuthDoesNotAutoGrant(t *testing.T) { - s, err := sqlite.New(":memory:") + s, err := newTestStore(":memory:") if err != nil { t.Fatalf("failed to create test store: %v", err) } @@ -778,7 +777,7 @@ func TestServer_GenerateAgentToken_NoDevAuthDoesNotAutoGrant(t *testing.T) { } t.Cleanup(func() { srv.Shutdown(context.Background()) }) - token, err := srv.GenerateAgentToken("agent-1", "project-1", nil) + token, err := srv.GenerateAgentToken(tid("agent-1"), tid("project-1"), nil) if err != nil { t.Fatalf("GenerateAgentToken failed: %v", err) } @@ -830,7 +829,7 @@ func (f *failingSMClient) Close() error { return nil } func TestServer_GCPBackendFailureIsFatal(t *testing.T) { // When GCPBackend is configured but GCP SM is unavailable, hub.New() should // return an error rather than silently generating an ephemeral key. - s, err := sqlite.New(":memory:") + s, err := newTestStore(":memory:") if err != nil { t.Fatalf("failed to create test store: %v", err) } @@ -858,7 +857,7 @@ func TestServer_SigningKeyBackupPreservesSecretRef(t *testing.T) { // Verify that after loading a signing key from the secret backend and // backing it up to SQLite, the SecretRef is preserved so the UI shows // the secret as SM-backed. - s, err := sqlite.New(":memory:") + s, err := newTestStore(":memory:") if err != nil { t.Fatalf("failed to create test store: %v", err) } diff --git a/pkg/hub/stalled_detection_test.go b/pkg/hub/stalled_detection_test.go index 27c3ef51b..fc51453a4 100644 --- a/pkg/hub/stalled_detection_test.go +++ b/pkg/hub/stalled_detection_test.go @@ -25,13 +25,13 @@ import ( "github.com/GoogleCloudPlatform/scion/pkg/agent/state" "github.com/GoogleCloudPlatform/scion/pkg/api" "github.com/GoogleCloudPlatform/scion/pkg/store" - "github.com/GoogleCloudPlatform/scion/pkg/store/sqlite" + "github.com/GoogleCloudPlatform/scion/pkg/store/entadapter" ) func setupStalledTestServer(t *testing.T) (*Server, store.Store, *trackingEventPublisher) { t.Helper() - s, err := sqlite.New(":memory:") + s, err := newTestStore(":memory:") if err != nil { t.Fatalf("failed to create test store: %v", err) } @@ -92,7 +92,7 @@ func TestAgentStalledDetectionHandler_MarksStalledAgents(t *testing.T) { // Make activity stale but keep heartbeat recent staleActivity := time.Now().Add(-10 * time.Minute) recentHB := time.Now().Add(-30 * time.Second) - db := s.(*sqlite.SQLiteStore).DB() + db := s.(*entadapter.CompositeStore).DB() if _, err := db.ExecContext(ctx, "UPDATE agents SET last_activity_event = ?, last_seen = ? WHERE id = ?", staleActivity, recentHB, agent.ID); err != nil { @@ -238,7 +238,7 @@ func TestAgentStalledDetectionHandler_StalledFromActivityIsPreserved(t *testing. // Make activity stale but keep heartbeat recent staleActivity := time.Now().Add(-10 * time.Minute) recentHB := time.Now().Add(-30 * time.Second) - db := s.(*sqlite.SQLiteStore).DB() + db := s.(*entadapter.CompositeStore).DB() if _, err := db.ExecContext(ctx, "UPDATE agents SET last_activity_event = ?, last_seen = ? WHERE id = ?", staleActivity, recentHB, agent.ID); err != nil { @@ -319,7 +319,7 @@ func TestAgentStalledDetectionHandler_BlockedAgentNotStalled(t *testing.T) { // Make activity stale but keep heartbeat recent (simulates long wait for child agent) staleActivity := time.Now().Add(-10 * time.Minute) recentHB := time.Now().Add(-30 * time.Second) - db := s.(*sqlite.SQLiteStore).DB() + db := s.(*entadapter.CompositeStore).DB() if _, err := db.ExecContext(ctx, "UPDATE agents SET last_activity_event = ?, last_seen = ? WHERE id = ?", staleActivity, recentHB, agent.ID); err != nil { @@ -383,7 +383,7 @@ func TestAgentStalledDetectionHandler_IdleAgentMarkedStalled(t *testing.T) { // Make activity stale but keep heartbeat recent (process alive but stuck at working) staleActivity := time.Now().Add(-10 * time.Minute) recentHB := time.Now().Add(-30 * time.Second) - db := s.(*sqlite.SQLiteStore).DB() + db := s.(*entadapter.CompositeStore).DB() if _, err := db.ExecContext(ctx, "UPDATE agents SET last_activity_event = ?, last_seen = ? WHERE id = ?", staleActivity, recentHB, agent.ID); err != nil { @@ -416,7 +416,7 @@ func TestAgentStalledDetectionHandler_IdleAgentMarkedStalled(t *testing.T) { } func TestNew_DefaultsStalledThresholdWhenZero(t *testing.T) { - s, err := sqlite.New(":memory:") + s, err := newTestStore(":memory:") if err != nil { t.Fatalf("failed to create test store: %v", err) } diff --git a/pkg/hub/template_bootstrap_test.go b/pkg/hub/template_bootstrap_test.go index 241275135..822f58aa4 100644 --- a/pkg/hub/template_bootstrap_test.go +++ b/pkg/hub/template_bootstrap_test.go @@ -32,14 +32,13 @@ import ( "github.com/GoogleCloudPlatform/scion/pkg/secret" "github.com/GoogleCloudPlatform/scion/pkg/store" - "github.com/GoogleCloudPlatform/scion/pkg/store/sqlite" ) // testTemplateBootstrapServer creates a hub Server backed by an in-memory // SQLite store and a mock storage, suitable for template bootstrap tests. func testTemplateBootstrapServer(t *testing.T) (*Server, store.Store, *mockStorage) { t.Helper() - s, err := sqlite.New(":memory:") + s, err := newTestStore(":memory:") if err != nil { if strings.Contains(err.Error(), "sqlite driver not registered") { t.Skip("Skipping: sqlite driver not registered") @@ -133,7 +132,7 @@ func TestBootstrapTemplatesFromDir_ImportsNewAlongsideExisting(t *testing.T) { // Pre-create a template in the store existing := &store.Template{ - ID: "existing-id", + ID: tid("existing-id"), Name: "existing", Slug: "existing", Scope: store.TemplateScopeGlobal, @@ -246,7 +245,7 @@ func TestBootstrapTemplatesFromDir_SkipsUnchangedTemplate(t *testing.T) { func TestBootstrapTemplatesFromDir_NoopWhenNoStorage(t *testing.T) { // Create server without storage - s, err := sqlite.New(":memory:") + s, err := newTestStore(":memory:") if err != nil { if strings.Contains(err.Error(), "sqlite driver not registered") { t.Skip("Skipping: sqlite driver not registered") @@ -767,7 +766,7 @@ func setupWorkspaceProject(t *testing.T, projectName string) (*Server, store.Sto workspaceRoot := t.TempDir() project := &store.Project{ - ID: "project-ws-" + projectName, + ID: tid("project-ws-" + projectName), Name: projectName, Slug: projectName, GitRemote: "https://github.com/test/" + projectName, @@ -776,10 +775,11 @@ func setupWorkspaceProject(t *testing.T, projectName string) (*Server, store.Sto t.Fatalf("failed to create project: %v", err) } - brokerID := "broker-ws-" + projectName + brokerID := tid("broker-ws-" + projectName) broker := &store.RuntimeBroker{ ID: brokerID, Name: "ws-broker", + Slug: "ws-broker", Endpoint: "http://localhost:9090", Status: store.BrokerStatusOnline, } diff --git a/pkg/hub/template_file_handlers_test.go b/pkg/hub/template_file_handlers_test.go index 81bb77ab5..8c249541f 100644 --- a/pkg/hub/template_file_handlers_test.go +++ b/pkg/hub/template_file_handlers_test.go @@ -20,6 +20,7 @@ import ( "bytes" "context" "encoding/json" + "fmt" "io" "mime/multipart" "net/http" @@ -30,7 +31,6 @@ import ( "github.com/GoogleCloudPlatform/scion/pkg/storage" "github.com/GoogleCloudPlatform/scion/pkg/store" - "github.com/GoogleCloudPlatform/scion/pkg/store/sqlite" ) // contentMockStorage extends mockStorage to also store file content for @@ -88,7 +88,7 @@ func (m *contentMockStorage) Exists(_ context.Context, objectPath string) (bool, // testTemplateFileServer creates a Server with content-aware mock storage. func testTemplateFileServer(t *testing.T) (*Server, store.Store, *contentMockStorage) { t.Helper() - s, err := sqlite.New(":memory:") + s, err := newTestStore(":memory:") if err != nil { if strings.Contains(err.Error(), "sqlite driver not registered") { t.Skip("Skipping: sqlite driver not registered") @@ -120,7 +120,7 @@ func createTestTemplate(t *testing.T, s store.Store, stor *contentMockStorage, f ctx := context.Background() tmpl := &store.Template{ - ID: "tmpl-test-1", + ID: tid("tmpl-test-1"), Name: "test-template", Slug: "test-template", Harness: "claude", @@ -354,7 +354,7 @@ func TestHandleTemplateFileWrite_ConflictHash(t *testing.T) { }) body := `{"content": "new", "expectedHash": "sha256:wronghash"}` - req := httptest.NewRequest(http.MethodPut, "/api/v1/templates/tmpl-test-1/files/CLAUDE.md", + req := httptest.NewRequest(http.MethodPut, fmt.Sprintf("/api/v1/templates/%s/files/CLAUDE.md", tid("tmpl-test-1")), strings.NewReader(body)) req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", "Bearer "+testDevToken) diff --git a/pkg/hub/teststore_test.go b/pkg/hub/teststore_test.go new file mode 100644 index 000000000..7e68c5298 --- /dev/null +++ b/pkg/hub/teststore_test.go @@ -0,0 +1,71 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build !no_sqlite + +package hub + +import ( + "context" + "fmt" + "sync/atomic" + + "github.com/GoogleCloudPlatform/scion/pkg/ent/entc" + "github.com/GoogleCloudPlatform/scion/pkg/store" + "github.com/GoogleCloudPlatform/scion/pkg/store/entadapter" + "github.com/google/uuid" +) + +// tid deterministically maps a human-readable test identifier (e.g. "user-1") +// to a stable UUID string. The Ent-backed store uses UUID primary keys, so test +// fixtures cannot use arbitrary strings as IDs; wrapping a readable name in tid +// preserves test legibility and cross-reference consistency (tid("user-1") +// always returns the same UUID) while satisfying the UUID requirement. +func tid(name string) string { + return uuid.NewSHA1(uuid.NameSpaceOID, []byte(name)).String() +} + +// testStoreSeq generates unique in-memory database names so each call to +// newTestStore(":memory:") gets an isolated database. +var testStoreSeq atomic.Int64 + +// newTestStore opens a fresh Ent-backed store for tests, mirroring the +// production single-database layout (see cmd/server_foreground.go:initStore). +// It is a drop-in replacement for the former sqlite.New: pass ":memory:" for an +// isolated in-memory database or a file path for a persistent one. The returned +// store is already migrated; callers may still invoke Migrate (it is +// idempotent). +func newTestStore(url string) (store.Store, error) { + var dsn string + if url == ":memory:" { + dsn = fmt.Sprintf("file:hubtest%d?mode=memory&cache=shared", testStoreSeq.Add(1)) + } else { + dsn = "file:" + url + "?cache=shared" + } + + // MaxOpenConns must be 1 for SQLite to serialize writes and avoid + // "database is locked" errors under concurrent access (e.g. the parallel + // per-agent writes in stop-all). This mirrors the production pool config in + // cmd/server_foreground.go / pkg/config. + client, err := entc.OpenSQLite(dsn, entc.PoolConfig{MaxOpenConns: 1}) + if err != nil { + return nil, err + } + s := entadapter.NewCompositeStore(client) + if err := s.Migrate(context.Background()); err != nil { + _ = s.Close() + return nil, err + } + return s, nil +} diff --git a/pkg/hub/user_activity_test.go b/pkg/hub/user_activity_test.go index 1e6de0b55..bfc6ede5e 100644 --- a/pkg/hub/user_activity_test.go +++ b/pkg/hub/user_activity_test.go @@ -58,7 +58,7 @@ func TestUserActivityTracker_ThrottlesWrites(t *testing.T) { tracker := NewUserActivityTracker(rec, time.Hour) // First touch should write - tracker.Touch("user-1") + tracker.Touch(tid("user-1")) // Wait for async goroutine time.Sleep(50 * time.Millisecond) @@ -67,12 +67,12 @@ func TestUserActivityTracker_ThrottlesWrites(t *testing.T) { if len(calls) != 1 { t.Fatalf("expected 1 call after first touch, got %d", len(calls)) } - if calls[0].id != "user-1" { + if calls[0].id != tid("user-1") { t.Errorf("expected user-1, got %s", calls[0].id) } // Second touch within the interval should be throttled - tracker.Touch("user-1") + tracker.Touch(tid("user-1")) time.Sleep(50 * time.Millisecond) calls = rec.getCalls() @@ -85,7 +85,7 @@ func TestUserActivityTracker_DifferentUsers(t *testing.T) { rec := &lastSeenRecorder{} tracker := NewUserActivityTracker(rec, time.Hour) - tracker.Touch("user-1") + tracker.Touch(tid("user-1")) tracker.Touch("user-2") time.Sleep(50 * time.Millisecond) @@ -101,11 +101,11 @@ func TestUserActivityTracker_WritesAfterInterval(t *testing.T) { // Use a very short interval for testing tracker := NewUserActivityTracker(rec, 10*time.Millisecond) - tracker.Touch("user-1") + tracker.Touch(tid("user-1")) time.Sleep(50 * time.Millisecond) // After interval has passed, a second touch should write again - tracker.Touch("user-1") + tracker.Touch(tid("user-1")) time.Sleep(50 * time.Millisecond) calls := rec.getCalls() diff --git a/pkg/hub/useraccesstoken_test.go b/pkg/hub/useraccesstoken_test.go index f842003e7..2d2b30a41 100644 --- a/pkg/hub/useraccesstoken_test.go +++ b/pkg/hub/useraccesstoken_test.go @@ -189,12 +189,12 @@ func newTestUATService() (*UserAccessTokenService, *mockUATStore, *mockUserStore tokenStore := newMockUATStore() userStore := &mockUserStore{ users: map[string]*store.User{ - "user-1": {ID: "user-1", Email: "test@example.com", DisplayName: "Test User", Role: "member"}, + tid("user-1"): {ID: tid("user-1"), Email: "test@example.com", DisplayName: "Test User", Role: "member"}, }, } projectStore := &mockProjectStore{ projects: map[string]*store.Project{ - "project-1": {ID: "project-1", Name: "test-project"}, + tid("project-1"): {ID: tid("project-1"), Name: "test-project"}, }, } svc := NewUserAccessTokenService(tokenStore, userStore, projectStore) @@ -206,7 +206,7 @@ func TestCreateToken(t *testing.T) { ctx := context.Background() t.Run("basic creation", func(t *testing.T) { - key, token, err := svc.CreateToken(ctx, "user-1", "ci-token", "project-1", + key, token, err := svc.CreateToken(ctx, tid("user-1"), "ci-token", tid("project-1"), []string{"agent:dispatch", "agent:read"}, nil) if err != nil { t.Fatalf("unexpected error: %v", err) @@ -217,7 +217,7 @@ func TestCreateToken(t *testing.T) { if token.Name != "ci-token" { t.Errorf("expected name 'ci-token', got %q", token.Name) } - if token.ProjectID != "project-1" { + if token.ProjectID != tid("project-1") { t.Errorf("expected projectID 'project-1', got %q", token.ProjectID) } if len(token.Scopes) != 2 { @@ -229,7 +229,7 @@ func TestCreateToken(t *testing.T) { }) t.Run("expands agent:manage", func(t *testing.T) { - _, token, err := svc.CreateToken(ctx, "user-1", "manage-token", "project-1", + _, token, err := svc.CreateToken(ctx, tid("user-1"), "manage-token", tid("project-1"), []string{"agent:manage"}, nil) if err != nil { t.Fatalf("unexpected error: %v", err) @@ -240,7 +240,7 @@ func TestCreateToken(t *testing.T) { }) t.Run("rejects invalid scope", func(t *testing.T) { - _, _, err := svc.CreateToken(ctx, "user-1", "bad-token", "project-1", + _, _, err := svc.CreateToken(ctx, tid("user-1"), "bad-token", tid("project-1"), []string{"invalid:scope"}, nil) if !errors.Is(err, ErrInvalidUATScope) { t.Errorf("expected ErrInvalidUATScope, got %v", err) @@ -248,7 +248,7 @@ func TestCreateToken(t *testing.T) { }) t.Run("rejects missing project", func(t *testing.T) { - _, _, err := svc.CreateToken(ctx, "user-1", "bad-token", "nonexistent", + _, _, err := svc.CreateToken(ctx, tid("user-1"), "bad-token", "nonexistent", []string{"agent:read"}, nil) if err == nil { t.Error("expected error for nonexistent project") @@ -257,7 +257,7 @@ func TestCreateToken(t *testing.T) { t.Run("rejects expiry too long", func(t *testing.T) { tooFar := time.Now().Add(400 * 24 * time.Hour) - _, _, err := svc.CreateToken(ctx, "user-1", "bad-token", "project-1", + _, _, err := svc.CreateToken(ctx, tid("user-1"), "bad-token", tid("project-1"), []string{"agent:read"}, &tooFar) if !errors.Is(err, ErrUATExpiryTooLong) { t.Errorf("expected ErrUATExpiryTooLong, got %v", err) @@ -265,7 +265,7 @@ func TestCreateToken(t *testing.T) { }) t.Run("rejects empty scopes", func(t *testing.T) { - _, _, err := svc.CreateToken(ctx, "user-1", "bad-token", "project-1", + _, _, err := svc.CreateToken(ctx, tid("user-1"), "bad-token", tid("project-1"), []string{}, nil) if err == nil { t.Error("expected error for empty scopes") @@ -277,7 +277,7 @@ func TestValidateToken(t *testing.T) { svc, _, _ := newTestUATService() ctx := context.Background() - key, _, err := svc.CreateToken(ctx, "user-1", "test-token", "project-1", + key, _, err := svc.CreateToken(ctx, tid("user-1"), "test-token", tid("project-1"), []string{"agent:dispatch", "agent:read"}, nil) if err != nil { t.Fatalf("failed to create token: %v", err) @@ -288,10 +288,10 @@ func TestValidateToken(t *testing.T) { if err != nil { t.Fatalf("unexpected error: %v", err) } - if identity.ID() != "user-1" { + if identity.ID() != tid("user-1") { t.Errorf("expected user ID 'user-1', got %q", identity.ID()) } - if identity.ScopedProjectID() != "project-1" { + if identity.ScopedProjectID() != tid("project-1") { t.Errorf("expected project 'project-1', got %q", identity.ScopedProjectID()) } if !identity.HasScope("agent:dispatch") { @@ -321,14 +321,14 @@ func TestRevokeToken(t *testing.T) { svc, _, _ := newTestUATService() ctx := context.Background() - key, token, err := svc.CreateToken(ctx, "user-1", "test-token", "project-1", + key, token, err := svc.CreateToken(ctx, tid("user-1"), "test-token", tid("project-1"), []string{"agent:read"}, nil) if err != nil { t.Fatalf("failed to create token: %v", err) } // Revoke it - if err := svc.RevokeToken(ctx, "user-1", token.ID); err != nil { + if err := svc.RevokeToken(ctx, tid("user-1"), token.ID); err != nil { t.Fatalf("failed to revoke token: %v", err) } @@ -339,7 +339,7 @@ func TestRevokeToken(t *testing.T) { } // Wrong user can't revoke - if err := svc.RevokeToken(ctx, "other-user", token.ID); !errors.Is(err, store.ErrNotFound) { + if err := svc.RevokeToken(ctx, tid("other-user"), token.ID); !errors.Is(err, store.ErrNotFound) { t.Errorf("expected ErrNotFound for wrong user, got %v", err) } } @@ -348,13 +348,13 @@ func TestDeleteToken(t *testing.T) { svc, _, _ := newTestUATService() ctx := context.Background() - key, token, err := svc.CreateToken(ctx, "user-1", "test-token", "project-1", + key, token, err := svc.CreateToken(ctx, tid("user-1"), "test-token", tid("project-1"), []string{"agent:read"}, nil) if err != nil { t.Fatalf("failed to create token: %v", err) } - if err := svc.DeleteToken(ctx, "user-1", token.ID); err != nil { + if err := svc.DeleteToken(ctx, tid("user-1"), token.ID); err != nil { t.Fatalf("failed to delete token: %v", err) } @@ -371,7 +371,7 @@ func TestTokenLimit(t *testing.T) { // Create max tokens for i := 0; i < store.UATMaxPerUser; i++ { - _, _, err := svc.CreateToken(ctx, "user-1", "token-"+string(rune('a'+i%26))+string(rune('0'+i/26)), "project-1", + _, _, err := svc.CreateToken(ctx, tid("user-1"), "token-"+string(rune('a'+i%26))+string(rune('0'+i/26)), tid("project-1"), []string{"agent:read"}, nil) if err != nil { t.Fatalf("failed to create token %d: %v", i, err) @@ -379,7 +379,7 @@ func TestTokenLimit(t *testing.T) { } // Next one should fail - _, _, err := svc.CreateToken(ctx, "user-1", "one-too-many", "project-1", + _, _, err := svc.CreateToken(ctx, tid("user-1"), "one-too-many", tid("project-1"), []string{"agent:read"}, nil) if !errors.Is(err, ErrUATLimitExceeded) { t.Errorf("expected ErrUATLimitExceeded, got %v", err) @@ -392,14 +392,14 @@ func TestListTokens(t *testing.T) { // Create 3 tokens for i := 0; i < 3; i++ { - _, _, err := svc.CreateToken(ctx, "user-1", "token-"+string(rune('a'+i)), "project-1", + _, _, err := svc.CreateToken(ctx, tid("user-1"), "token-"+string(rune('a'+i)), tid("project-1"), []string{"agent:read"}, nil) if err != nil { t.Fatalf("failed to create token: %v", err) } } - tokens, err := svc.ListTokens(ctx, "user-1") + tokens, err := svc.ListTokens(ctx, tid("user-1")) if err != nil { t.Fatalf("failed to list tokens: %v", err) } @@ -441,16 +441,16 @@ func TestExpandScopes(t *testing.T) { } func TestScopedUserIdentity(t *testing.T) { - base := NewAuthenticatedUser("user-1", "test@example.com", "Test", "member", "api") - scoped := NewScopedUserIdentity(base, "project-1", []string{"agent:dispatch", "agent:read"}) + base := NewAuthenticatedUser(tid("user-1"), "test@example.com", "Test", "member", "api") + scoped := NewScopedUserIdentity(base, tid("project-1"), []string{"agent:dispatch", "agent:read"}) - if scoped.ID() != "user-1" { + if scoped.ID() != tid("user-1") { t.Errorf("expected ID 'user-1', got %q", scoped.ID()) } if scoped.Email() != "test@example.com" { t.Errorf("expected email 'test@example.com', got %q", scoped.Email()) } - if scoped.ScopedProjectID() != "project-1" { + if scoped.ScopedProjectID() != tid("project-1") { t.Errorf("expected project 'project-1', got %q", scoped.ScopedProjectID()) } if !scoped.HasScope("agent:dispatch") { diff --git a/pkg/hub/wake_test.go b/pkg/hub/wake_test.go index 6e958afcc..8c223023c 100644 --- a/pkg/hub/wake_test.go +++ b/pkg/hub/wake_test.go @@ -37,14 +37,14 @@ func createWakeTestFixtures(t *testing.T, agentPhase string) (*Server, store.Sto ctx := context.Background() project := &store.Project{ - ID: "project-wake-" + agentPhase, + ID: tid("project-wake-" + agentPhase), Name: "Wake Test Project", Slug: "wake-test-project-" + agentPhase, } require.NoError(t, s.CreateProject(ctx, project)) broker := &store.RuntimeBroker{ - ID: "broker-wake-" + agentPhase, + ID: tid("broker-wake-" + agentPhase), Name: "Wake Test Broker", Slug: "wake-test-broker-" + agentPhase, Status: store.BrokerStatusOnline, @@ -59,7 +59,7 @@ func createWakeTestFixtures(t *testing.T, agentPhase string) (*Server, store.Sto })) agent := &store.Agent{ - ID: "agent-wake-" + agentPhase, + ID: tid("agent-wake-" + agentPhase), Slug: "agent-wake-" + agentPhase, Name: "Wake Agent", ProjectID: project.ID, diff --git a/pkg/hub/web.go b/pkg/hub/web.go index 46b1766e9..e867486e3 100644 --- a/pkg/hub/web.go +++ b/pkg/hub/web.go @@ -147,7 +147,7 @@ type WebServer struct { oauthService *OAuthService store store.Store userTokenSvc *UserTokenService - events *ChannelEventPublisher // nil when no publisher configured + events EventPublisher // nil when no publisher configured hubHandler http.Handler // mounted Hub API handler, or nil hubShutdown func(context.Context) error // Hub resource cleanup, or nil maintenance *MaintenanceState // runtime maintenance mode state (shared with Hub) @@ -492,7 +492,7 @@ func (ws *WebServer) SetUserTokenService(svc *UserTokenService) { } // SetEventPublisher sets the event publisher for real-time SSE streaming. -func (ws *WebServer) SetEventPublisher(pub *ChannelEventPublisher) { +func (ws *WebServer) SetEventPublisher(pub EventPublisher) { ws.events = pub } @@ -952,7 +952,7 @@ func (ws *WebServer) tryServeStaticFile(w http.ResponseWriter, r *http.Request) } // handleSSE serves the Server-Sent Events endpoint. It subscribes to the -// in-process ChannelEventPublisher and streams matching events to the browser. +// configured EventPublisher and streams matching events to the browser. // Route: GET /events?sub=&sub=... func (ws *WebServer) handleSSE(w http.ResponseWriter, r *http.Request) { if ws.events == nil { diff --git a/pkg/hub/web_test.go b/pkg/hub/web_test.go index 1f9f17692..2853400da 100644 --- a/pkg/hub/web_test.go +++ b/pkg/hub/web_test.go @@ -1426,7 +1426,7 @@ func TestSSEHandler_EventDelivery(t *testing.T) { select { case <-ticker.C: pub.publish("project.test123.agent.status", AgentStatusEvent{ - AgentID: "agent-1", + AgentID: tid("agent-1"), ProjectID: "test123", Phase: "running", }) @@ -1461,7 +1461,7 @@ func TestSSEHandler_EventDelivery(t *testing.T) { assert.Contains(t, frame, "event: update\n") assert.Contains(t, frame, "data: ") assert.Contains(t, frame, `"subject":"project.test123.agent.status"`) - assert.Contains(t, frame, `"agentId":"agent-1"`) + assert.Contains(t, frame, `"agentId":"`+tid("agent-1")+`"`) assert.Contains(t, frame, `"phase":"running"`) } @@ -1708,7 +1708,7 @@ func TestSPAShellHandler_ContainsInitialData(t *testing.T) { json.NewEncoder(w).Encode(map[string]interface{}{ "agents": []map[string]interface{}{ { - "id": "agent-1", + "id": tid("agent-1"), "name": "test-agent", "status": "running", "_capabilities": map[string]interface{}{ @@ -1738,7 +1738,7 @@ func TestSPAShellHandler_ContainsInitialData(t *testing.T) { assert.Equal(t, http.StatusOK, resp.StatusCode) // The __SCION_DATA__ should contain agent data - assert.Contains(t, html, `"agent-1"`) + assert.Contains(t, html, tid("agent-1")) assert.Contains(t, html, `"test-agent"`) assert.Contains(t, html, `"_capabilities"`) assert.Contains(t, html, `"actions"`) diff --git a/pkg/hub/workspace_handlers_test.go b/pkg/hub/workspace_handlers_test.go index 03b60d804..8c7b92407 100644 --- a/pkg/hub/workspace_handlers_test.go +++ b/pkg/hub/workspace_handlers_test.go @@ -19,6 +19,7 @@ package hub import ( "context" "encoding/json" + "fmt" "net/http" "net/http/httptest" "strings" @@ -27,7 +28,6 @@ import ( "github.com/GoogleCloudPlatform/scion/pkg/agent/state" "github.com/GoogleCloudPlatform/scion/pkg/store" - "github.com/GoogleCloudPlatform/scion/pkg/store/sqlite" "github.com/GoogleCloudPlatform/scion/pkg/transfer" ) @@ -37,7 +37,7 @@ const testWorkspaceDevToken = "scion_dev_workspace_test_token_1234567890" // testWorkspaceServer creates a test server for workspace handler tests. func testWorkspaceServer(t *testing.T) (*Server, store.Store) { t.Helper() - s, err := sqlite.New(":memory:") + s, err := newTestStore(":memory:") if err != nil { t.Fatalf("failed to create test store: %v", err) } @@ -82,25 +82,25 @@ func TestWorkspaceRoutesParsing(t *testing.T) { }{ { name: "workspace status", - url: "/api/v1/agents/agent-123/workspace", + url: fmt.Sprintf("/api/v1/agents/%s/workspace", "agent-123"), expectedID: "agent-123", expectedAction: "workspace", }, { name: "workspace sync-from", - url: "/api/v1/agents/agent-123/workspace/sync-from", + url: fmt.Sprintf("/api/v1/agents/%s/workspace/sync-from", "agent-123"), expectedID: "agent-123", expectedAction: "workspace/sync-from", }, { name: "workspace sync-to", - url: "/api/v1/agents/agent-123/workspace/sync-to", + url: fmt.Sprintf("/api/v1/agents/%s/workspace/sync-to", "agent-123"), expectedID: "agent-123", expectedAction: "workspace/sync-to", }, { name: "workspace sync-to finalize", - url: "/api/v1/agents/agent-123/workspace/sync-to/finalize", + url: fmt.Sprintf("/api/v1/agents/%s/workspace/sync-to/finalize", "agent-123"), expectedID: "agent-123", expectedAction: "workspace/sync-to/finalize", }, @@ -128,14 +128,14 @@ func TestWorkspaceStatusHandler(t *testing.T) { now := time.Now() // Create the project first (foreign key dependency) - createTestProject(t, s, "project_test_1") + createTestProject(t, s, tid("project_test_1")) // Create a test agent agent := &store.Agent{ - ID: "agent_workspace_test_1", + ID: tid("agent_workspace_test_1"), Slug: "workspace-test-agent", Name: "test-agent", - ProjectID: "project_test_1", + ProjectID: tid("project_test_1"), Phase: string(state.PhaseRunning), StateVersion: 1, Created: now, @@ -146,7 +146,7 @@ func TestWorkspaceStatusHandler(t *testing.T) { } // Test workspace status endpoint - req := httptest.NewRequest("GET", "/api/v1/agents/agent_workspace_test_1/workspace", nil) + req := httptest.NewRequest("GET", fmt.Sprintf("/api/v1/agents/%s/workspace", tid("agent_workspace_test_1")), nil) req.Header.Set("Authorization", "Bearer "+testWorkspaceDevToken) rec := httptest.NewRecorder() @@ -161,11 +161,11 @@ func TestWorkspaceStatusHandler(t *testing.T) { t.Fatalf("failed to decode response: %v", err) } - if resp.Slug != "agent_workspace_test_1" { - t.Errorf("response AgentID = %q, want %q", resp.Slug, "agent_workspace_test_1") + if resp.Slug != tid("agent_workspace_test_1") { + t.Errorf("response AgentID = %q, want %q", resp.Slug, tid("agent_workspace_test_1")) } - if resp.ProjectID != "project_test_1" { - t.Errorf("response ProjectID = %q, want %q", resp.ProjectID, "project_test_1") + if resp.ProjectID != tid("project_test_1") { + t.Errorf("response ProjectID = %q, want %q", resp.ProjectID, tid("project_test_1")) } } @@ -189,14 +189,14 @@ func TestWorkspaceSyncFromHandler_AgentNotRunning(t *testing.T) { now := time.Now() // Create the project first - createTestProject(t, s, "project_test") + createTestProject(t, s, tid("project_test")) // Create a stopped agent agent := &store.Agent{ - ID: "agent_stopped_1", + ID: tid("agent_stopped_1"), Slug: "stopped-agent", Name: "stopped-agent", - ProjectID: "project_test", + ProjectID: tid("project_test"), Phase: string(state.PhaseStopped), StateVersion: 1, Created: now, @@ -206,7 +206,7 @@ func TestWorkspaceSyncFromHandler_AgentNotRunning(t *testing.T) { t.Fatalf("failed to create agent: %v", err) } - req := httptest.NewRequest("POST", "/api/v1/agents/agent_stopped_1/workspace/sync-from", nil) + req := httptest.NewRequest("POST", fmt.Sprintf("/api/v1/agents/%s/workspace/sync-from", tid("agent_stopped_1")), nil) req.Header.Set("Authorization", "Bearer "+testWorkspaceDevToken) rec := httptest.NewRecorder() @@ -224,13 +224,13 @@ func TestWorkspaceSyncToHandler_EmptyFiles(t *testing.T) { now := time.Now() // Create the project first - createTestProject(t, s, "project_syncto") + createTestProject(t, s, tid("project_syncto")) agent := &store.Agent{ - ID: "agent_syncto_test", + ID: tid("agent_syncto_test"), Slug: "sync-to-test-agent", Name: "test-agent", - ProjectID: "project_syncto", + ProjectID: tid("project_syncto"), Phase: string(state.PhaseRunning), StateVersion: 1, Created: now, @@ -242,7 +242,7 @@ func TestWorkspaceSyncToHandler_EmptyFiles(t *testing.T) { // Send request with empty files list body := `{"files": []}` - req := httptest.NewRequest("POST", "/api/v1/agents/agent_syncto_test/workspace/sync-to", strings.NewReader(body)) + req := httptest.NewRequest("POST", fmt.Sprintf("/api/v1/agents/%s/workspace/sync-to", tid("agent_syncto_test")), strings.NewReader(body)) req.Header.Set("Authorization", "Bearer "+testWorkspaceDevToken) req.Header.Set("Content-Type", "application/json") rec := httptest.NewRecorder() @@ -261,13 +261,13 @@ func TestWorkspaceSyncToFinalizeHandler_MissingManifest(t *testing.T) { now := time.Now() // Create the project first - createTestProject(t, s, "project_finalize") + createTestProject(t, s, tid("project_finalize")) agent := &store.Agent{ - ID: "agent_finalize_test", + ID: tid("agent_finalize_test"), Slug: "finalize-test-agent", Name: "test-agent", - ProjectID: "project_finalize", + ProjectID: tid("project_finalize"), Phase: string(state.PhaseRunning), StateVersion: 1, Created: now, @@ -279,7 +279,7 @@ func TestWorkspaceSyncToFinalizeHandler_MissingManifest(t *testing.T) { // Send request without manifest body := `{}` - req := httptest.NewRequest("POST", "/api/v1/agents/agent_finalize_test/workspace/sync-to/finalize", strings.NewReader(body)) + req := httptest.NewRequest("POST", fmt.Sprintf("/api/v1/agents/%s/workspace/sync-to/finalize", tid("agent_finalize_test")), strings.NewReader(body)) req.Header.Set("Authorization", "Bearer "+testWorkspaceDevToken) req.Header.Set("Content-Type", "application/json") rec := httptest.NewRecorder() @@ -298,13 +298,13 @@ func TestWorkspaceRoutesRequireAuth(t *testing.T) { now := time.Now() // Create the project first - createTestProject(t, s, "project_auth") + createTestProject(t, s, tid("project_auth")) agent := &store.Agent{ - ID: "agent_auth_test", + ID: tid("agent_auth_test"), Slug: "auth-test-agent", Name: "test-agent", - ProjectID: "project_auth", + ProjectID: tid("project_auth"), Phase: string(state.PhaseRunning), StateVersion: 1, Created: now, @@ -319,10 +319,10 @@ func TestWorkspaceRoutesRequireAuth(t *testing.T) { method string url string }{ - {"workspace status", "GET", "/api/v1/agents/agent_auth_test/workspace"}, - {"sync-from", "POST", "/api/v1/agents/agent_auth_test/workspace/sync-from"}, - {"sync-to", "POST", "/api/v1/agents/agent_auth_test/workspace/sync-to"}, - {"sync-to finalize", "POST", "/api/v1/agents/agent_auth_test/workspace/sync-to/finalize"}, + {"workspace status", "GET", fmt.Sprintf("/api/v1/agents/%s/workspace", tid("agent_auth_test"))}, + {"sync-from", "POST", fmt.Sprintf("/api/v1/agents/%s/workspace/sync-from", tid("agent_auth_test"))}, + {"sync-to", "POST", fmt.Sprintf("/api/v1/agents/%s/workspace/sync-to", tid("agent_auth_test"))}, + {"sync-to finalize", "POST", fmt.Sprintf("/api/v1/agents/%s/workspace/sync-to/finalize", tid("agent_auth_test"))}, } for _, tt := range tests { @@ -414,8 +414,8 @@ func TestWorkspaceSyncFromHandler_StorageNotConfigured(t *testing.T) { now := time.Now() // Use unique IDs for this test - projectID := "project_nostor_syncfrom" - agentID := "agent_nostor_syncfrom" + projectID := tid("project_nostor_syncfrom") + agentID := tid("agent_nostor_syncfrom") // Create the project first createTestProject(t, s, projectID) @@ -460,13 +460,13 @@ func TestWorkspaceSyncToHandler_StorageNotConfigured(t *testing.T) { now := time.Now() // Create the project first - createTestProject(t, s, "project_syncto_no_storage") + createTestProject(t, s, tid("project_syncto_no_storage")) agent := &store.Agent{ - ID: "agent_syncto_no_storage", + ID: tid("agent_syncto_no_storage"), Slug: "sync-to-no-storage-agent", Name: "test-agent", - ProjectID: "project_syncto_no_storage", + ProjectID: tid("project_syncto_no_storage"), Phase: string(state.PhaseRunning), StateVersion: 1, Created: now, @@ -478,7 +478,7 @@ func TestWorkspaceSyncToHandler_StorageNotConfigured(t *testing.T) { // Send request with files but no storage configured body := `{"files": [{"path": "test.txt", "size": 100, "hash": "sha256:abc123"}]}` - req := httptest.NewRequest("POST", "/api/v1/agents/agent_syncto_no_storage/workspace/sync-to", strings.NewReader(body)) + req := httptest.NewRequest("POST", fmt.Sprintf("/api/v1/agents/%s/workspace/sync-to", tid("agent_syncto_no_storage")), strings.NewReader(body)) req.Header.Set("Authorization", "Bearer "+testWorkspaceDevToken) req.Header.Set("Content-Type", "application/json") rec := httptest.NewRecorder() @@ -497,13 +497,13 @@ func TestWorkspaceSyncToFinalizeHandler_StorageNotConfigured(t *testing.T) { now := time.Now() // Create the project first - createTestProject(t, s, "project_finalize_no_storage") + createTestProject(t, s, tid("project_finalize_no_storage")) agent := &store.Agent{ - ID: "agent_finalize_no_storage", + ID: tid("agent_finalize_no_storage"), Slug: "finalize-no-storage-agent", Name: "test-agent", - ProjectID: "project_finalize_no_storage", + ProjectID: tid("project_finalize_no_storage"), Phase: string(state.PhaseRunning), StateVersion: 1, Created: now, @@ -515,7 +515,7 @@ func TestWorkspaceSyncToFinalizeHandler_StorageNotConfigured(t *testing.T) { // Send request with manifest but no storage configured body := `{"manifest": {"version": "1.0", "files": [{"path": "test.txt", "size": 100, "hash": "sha256:abc123"}]}}` - req := httptest.NewRequest("POST", "/api/v1/agents/agent_finalize_no_storage/workspace/sync-to/finalize", strings.NewReader(body)) + req := httptest.NewRequest("POST", fmt.Sprintf("/api/v1/agents/%s/workspace/sync-to/finalize", tid("agent_finalize_no_storage")), strings.NewReader(body)) req.Header.Set("Authorization", "Bearer "+testWorkspaceDevToken) req.Header.Set("Content-Type", "application/json") rec := httptest.NewRecorder() @@ -534,14 +534,14 @@ func TestWorkspaceSyncToFinalizeHandler_AgentNotRunning(t *testing.T) { now := time.Now() // Create the project first - createTestProject(t, s, "project_finalize_stopped") + createTestProject(t, s, tid("project_finalize_stopped")) // Create a stopped agent agent := &store.Agent{ - ID: "agent_finalize_stopped", + ID: tid("agent_finalize_stopped"), Slug: "finalize-stopped-agent", Name: "stopped-agent", - ProjectID: "project_finalize_stopped", + ProjectID: tid("project_finalize_stopped"), Phase: string(state.PhaseStopped), StateVersion: 1, Created: now, @@ -552,7 +552,7 @@ func TestWorkspaceSyncToFinalizeHandler_AgentNotRunning(t *testing.T) { } body := `{"manifest": {"version": "1.0", "files": [{"path": "test.txt", "size": 100, "hash": "sha256:abc123"}]}}` - req := httptest.NewRequest("POST", "/api/v1/agents/agent_finalize_stopped/workspace/sync-to/finalize", strings.NewReader(body)) + req := httptest.NewRequest("POST", fmt.Sprintf("/api/v1/agents/%s/workspace/sync-to/finalize", tid("agent_finalize_stopped")), strings.NewReader(body)) req.Header.Set("Authorization", "Bearer "+testWorkspaceDevToken) req.Header.Set("Content-Type", "application/json") rec := httptest.NewRecorder() @@ -571,13 +571,13 @@ func TestWorkspaceMethodNotAllowed(t *testing.T) { now := time.Now() // Create the project first - createTestProject(t, s, "project_method") + createTestProject(t, s, tid("project_method")) agent := &store.Agent{ - ID: "agent_method_test", + ID: tid("agent_method_test"), Slug: "method-test-agent", Name: "test-agent", - ProjectID: "project_method", + ProjectID: tid("project_method"), Phase: string(state.PhaseRunning), StateVersion: 1, Created: now, @@ -594,24 +594,24 @@ func TestWorkspaceMethodNotAllowed(t *testing.T) { expectedStatus int }{ // workspace status - GET only - {"workspace status with POST", "POST", "/api/v1/agents/agent_method_test/workspace", http.StatusMethodNotAllowed}, - {"workspace status with PUT", "PUT", "/api/v1/agents/agent_method_test/workspace", http.StatusMethodNotAllowed}, - {"workspace status with DELETE", "DELETE", "/api/v1/agents/agent_method_test/workspace", http.StatusMethodNotAllowed}, + {"workspace status with POST", "POST", fmt.Sprintf("/api/v1/agents/%s/workspace", tid("agent_method_test")), http.StatusMethodNotAllowed}, + {"workspace status with PUT", "PUT", fmt.Sprintf("/api/v1/agents/%s/workspace", tid("agent_method_test")), http.StatusMethodNotAllowed}, + {"workspace status with DELETE", "DELETE", fmt.Sprintf("/api/v1/agents/%s/workspace", tid("agent_method_test")), http.StatusMethodNotAllowed}, // sync-from - POST only - {"sync-from with GET", "GET", "/api/v1/agents/agent_method_test/workspace/sync-from", http.StatusMethodNotAllowed}, - {"sync-from with PUT", "PUT", "/api/v1/agents/agent_method_test/workspace/sync-from", http.StatusMethodNotAllowed}, - {"sync-from with DELETE", "DELETE", "/api/v1/agents/agent_method_test/workspace/sync-from", http.StatusMethodNotAllowed}, + {"sync-from with GET", "GET", fmt.Sprintf("/api/v1/agents/%s/workspace/sync-from", tid("agent_method_test")), http.StatusMethodNotAllowed}, + {"sync-from with PUT", "PUT", fmt.Sprintf("/api/v1/agents/%s/workspace/sync-from", tid("agent_method_test")), http.StatusMethodNotAllowed}, + {"sync-from with DELETE", "DELETE", fmt.Sprintf("/api/v1/agents/%s/workspace/sync-from", tid("agent_method_test")), http.StatusMethodNotAllowed}, // sync-to - POST only - {"sync-to with GET", "GET", "/api/v1/agents/agent_method_test/workspace/sync-to", http.StatusMethodNotAllowed}, - {"sync-to with PUT", "PUT", "/api/v1/agents/agent_method_test/workspace/sync-to", http.StatusMethodNotAllowed}, - {"sync-to with DELETE", "DELETE", "/api/v1/agents/agent_method_test/workspace/sync-to", http.StatusMethodNotAllowed}, + {"sync-to with GET", "GET", fmt.Sprintf("/api/v1/agents/%s/workspace/sync-to", tid("agent_method_test")), http.StatusMethodNotAllowed}, + {"sync-to with PUT", "PUT", fmt.Sprintf("/api/v1/agents/%s/workspace/sync-to", tid("agent_method_test")), http.StatusMethodNotAllowed}, + {"sync-to with DELETE", "DELETE", fmt.Sprintf("/api/v1/agents/%s/workspace/sync-to", tid("agent_method_test")), http.StatusMethodNotAllowed}, // sync-to/finalize - POST only - {"finalize with GET", "GET", "/api/v1/agents/agent_method_test/workspace/sync-to/finalize", http.StatusMethodNotAllowed}, - {"finalize with PUT", "PUT", "/api/v1/agents/agent_method_test/workspace/sync-to/finalize", http.StatusMethodNotAllowed}, - {"finalize with DELETE", "DELETE", "/api/v1/agents/agent_method_test/workspace/sync-to/finalize", http.StatusMethodNotAllowed}, + {"finalize with GET", "GET", fmt.Sprintf("/api/v1/agents/%s/workspace/sync-to/finalize", tid("agent_method_test")), http.StatusMethodNotAllowed}, + {"finalize with PUT", "PUT", fmt.Sprintf("/api/v1/agents/%s/workspace/sync-to/finalize", tid("agent_method_test")), http.StatusMethodNotAllowed}, + {"finalize with DELETE", "DELETE", fmt.Sprintf("/api/v1/agents/%s/workspace/sync-to/finalize", tid("agent_method_test")), http.StatusMethodNotAllowed}, } for _, tt := range tests { @@ -635,13 +635,13 @@ func TestWorkspaceSyncToHandler_InvalidJSON(t *testing.T) { now := time.Now() // Create the project first - createTestProject(t, s, "project_invalid_json") + createTestProject(t, s, tid("project_invalid_json")) agent := &store.Agent{ - ID: "agent_invalid_json", + ID: tid("agent_invalid_json"), Slug: "invalid-json-agent", Name: "test-agent", - ProjectID: "project_invalid_json", + ProjectID: tid("project_invalid_json"), Phase: string(state.PhaseRunning), StateVersion: 1, Created: now, @@ -653,7 +653,7 @@ func TestWorkspaceSyncToHandler_InvalidJSON(t *testing.T) { // Send invalid JSON body := `{invalid json` - req := httptest.NewRequest("POST", "/api/v1/agents/agent_invalid_json/workspace/sync-to", strings.NewReader(body)) + req := httptest.NewRequest("POST", fmt.Sprintf("/api/v1/agents/%s/workspace/sync-to", tid("agent_invalid_json")), strings.NewReader(body)) req.Header.Set("Authorization", "Bearer "+testWorkspaceDevToken) req.Header.Set("Content-Type", "application/json") rec := httptest.NewRecorder() @@ -672,13 +672,13 @@ func TestWorkspaceSyncToFinalizeHandler_InvalidJSON(t *testing.T) { now := time.Now() // Create the project first - createTestProject(t, s, "project_finalize_invalid") + createTestProject(t, s, tid("project_finalize_invalid")) agent := &store.Agent{ - ID: "agent_finalize_invalid", + ID: tid("agent_finalize_invalid"), Slug: "finalize-invalid-agent", Name: "test-agent", - ProjectID: "project_finalize_invalid", + ProjectID: tid("project_finalize_invalid"), Phase: string(state.PhaseRunning), StateVersion: 1, Created: now, @@ -690,7 +690,7 @@ func TestWorkspaceSyncToFinalizeHandler_InvalidJSON(t *testing.T) { // Send invalid JSON body := `{not valid` - req := httptest.NewRequest("POST", "/api/v1/agents/agent_finalize_invalid/workspace/sync-to/finalize", strings.NewReader(body)) + req := httptest.NewRequest("POST", fmt.Sprintf("/api/v1/agents/%s/workspace/sync-to/finalize", tid("agent_finalize_invalid")), strings.NewReader(body)) req.Header.Set("Authorization", "Bearer "+testWorkspaceDevToken) req.Header.Set("Content-Type", "application/json") rec := httptest.NewRecorder() @@ -786,13 +786,13 @@ func TestWorkspaceUnknownAction(t *testing.T) { now := time.Now() // Create the project first - createTestProject(t, s, "project_unknown") + createTestProject(t, s, tid("project_unknown")) agent := &store.Agent{ - ID: "agent_unknown_action", + ID: tid("agent_unknown_action"), Slug: "unknown-action-agent", Name: "test-agent", - ProjectID: "project_unknown", + ProjectID: tid("project_unknown"), Phase: string(state.PhaseRunning), StateVersion: 1, Created: now, @@ -803,7 +803,7 @@ func TestWorkspaceUnknownAction(t *testing.T) { } // Request with unknown workspace action - req := httptest.NewRequest("POST", "/api/v1/agents/agent_unknown_action/workspace/unknown-action", nil) + req := httptest.NewRequest("POST", fmt.Sprintf("/api/v1/agents/%s/workspace/unknown-action", tid("agent_unknown_action")), nil) req.Header.Set("Authorization", "Bearer "+testWorkspaceDevToken) rec := httptest.NewRecorder() @@ -886,8 +886,8 @@ func TestSyncHubManagedWorkspaceBack_SkipsGitProject(t *testing.T) { // Create a git-backed project (has GitRemote) project := &store.Project{ - ID: "project-git-sync", - Slug: "project-git-sync", + ID: tid("project-git-sync"), + Slug: tid("project-git-sync"), Name: "Git Project", GitRemote: "github.com/test/repo", } @@ -897,7 +897,7 @@ func TestSyncHubManagedWorkspaceBack_SkipsGitProject(t *testing.T) { agent := &store.Agent{ ID: "agent-sync-1", - ProjectID: "project-git-sync", + ProjectID: tid("project-git-sync"), } // This should return without doing anything for git-backed projects @@ -911,8 +911,8 @@ func TestSyncHubManagedWorkspaceBack_SkipsColocatedBroker(t *testing.T) { // Create a hub-managed project project := &store.Project{ - ID: "project-colo-sync", - Slug: "project-colo-sync", + ID: tid("project-colo-sync"), + Slug: tid("project-colo-sync"), Name: "Hub Native Colo", // No GitRemote = hub-managed } @@ -922,7 +922,7 @@ func TestSyncHubManagedWorkspaceBack_SkipsColocatedBroker(t *testing.T) { // Create a broker with local path (colocated) broker := &store.RuntimeBroker{ - ID: "broker-colo", + ID: tid("broker-colo"), Name: "colo-broker", Slug: "colo-broker", Endpoint: "http://localhost:9800", @@ -932,8 +932,8 @@ func TestSyncHubManagedWorkspaceBack_SkipsColocatedBroker(t *testing.T) { t.Fatalf("failed to create broker: %v", err) } provider := &store.ProjectProvider{ - ProjectID: "project-colo-sync", - BrokerID: "broker-colo", + ProjectID: tid("project-colo-sync"), + BrokerID: tid("broker-colo"), BrokerName: "colo-broker", LocalPath: "/home/user/.scion", Status: store.BrokerStatusOnline, @@ -944,8 +944,8 @@ func TestSyncHubManagedWorkspaceBack_SkipsColocatedBroker(t *testing.T) { agent := &store.Agent{ ID: "agent-colo-sync", - ProjectID: "project-colo-sync", - RuntimeBrokerID: "broker-colo", + ProjectID: tid("project-colo-sync"), + RuntimeBrokerID: tid("broker-colo"), } // Should skip sync because broker has local path diff --git a/pkg/observability/dbmetrics/dbmetrics.go b/pkg/observability/dbmetrics/dbmetrics.go new file mode 100644 index 000000000..cfed73759 --- /dev/null +++ b/pkg/observability/dbmetrics/dbmetrics.go @@ -0,0 +1,272 @@ +/* +Copyright 2025 The Scion Authors. +*/ + +// Package dbmetrics provides Cloud Monitoring scaffolding for the Postgres +// LISTEN/NOTIFY observability requirement. +// +// It defines the OpenTelemetry metric instruments used to observe the +// notification pipeline (publish-to-deliver latency, notification counts, +// subscriber lag, listener reconnects, payload sizes) and the database +// connection pool (active/idle/waiting/max). +// +// The package is intentionally lightweight: it registers instruments against an +// OpenTelemetry MeterProvider and exposes a small Recorder interface so callers +// just invoke Record/Observe/Inc methods without touching the OTel SDK. When no +// MeterProvider is supplied (the safe default, e.g. when no GCP project or +// exporter is configured), a no-op MeterProvider is used so every call becomes a +// cheap no-op and nothing is exported. A Cloud Monitoring exporter can be wired +// into the MeterProvider later without any change to callers. +package dbmetrics + +import ( + "context" + "fmt" + + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/metric" + "go.opentelemetry.io/otel/metric/noop" +) + +// instrumentationName is the OTel instrumentation scope for this package. +const instrumentationName = "github.com/GoogleCloudPlatform/scion/pkg/observability/dbmetrics" + +// Metric names. Kept as constants so dashboards, alerts, and tests can reference +// the canonical strings. +const ( + MetricPublishToDeliverLatency = "scion.db.notify.publish_to_deliver.duration" + MetricNotificationsPublished = "scion.db.notify.published" + MetricNotificationsDelivered = "scion.db.notify.delivered" + MetricNotificationsDropped = "scion.db.notify.dropped" + MetricSubscriberLag = "scion.db.notify.subscriber.lag" + MetricListenerReconnects = "scion.db.notify.listener.reconnects" + MetricPayloadSize = "scion.db.notify.payload.size" + MetricPoolConnectionsActive = "scion.db.pool.connections.active" + MetricPoolConnectionsIdle = "scion.db.pool.connections.idle" + MetricPoolConnectionsWaiting = "scion.db.pool.connections.waiting" + MetricPoolConnectionsMax = "scion.db.pool.connections.max" +) + +// Recorder is the interface callers use to record Postgres LISTEN/NOTIFY and +// connection-pool metrics. All methods are safe to call concurrently and are +// cheap no-ops when metrics are disabled. +// +// The LISTEN/NOTIFY event-agent (P3-8) is the primary intended caller. +type Recorder interface { + // RecordPublishToDeliverLatency records the end-to-end latency, in + // milliseconds, between a notification being published to Postgres and it + // being delivered to a subscriber. + RecordPublishToDeliverLatency(ctx context.Context, ms float64, attrs ...attribute.KeyValue) + + // IncPublished increments the count of notifications published to Postgres. + IncPublished(ctx context.Context, n int64, attrs ...attribute.KeyValue) + // IncDelivered increments the count of notifications delivered to subscribers. + IncDelivered(ctx context.Context, n int64, attrs ...attribute.KeyValue) + // IncDropped increments the count of notifications dropped (e.g. full buffer, + // decode failure, no subscriber). + IncDropped(ctx context.Context, n int64, attrs ...attribute.KeyValue) + + // ObserveSubscriberLag records the current subscriber lag (number of + // notifications a subscriber is behind, or another caller-defined lag unit). + ObserveSubscriberLag(ctx context.Context, lag int64, attrs ...attribute.KeyValue) + + // IncListenerReconnects increments the count of LISTEN connection reconnects. + IncListenerReconnects(ctx context.Context, n int64, attrs ...attribute.KeyValue) + + // RecordPayloadSize records the size, in bytes, of a notification payload. + RecordPayloadSize(ctx context.Context, bytes int64, attrs ...attribute.KeyValue) + + // ObservePoolStats records a snapshot of the DB connection pool gauges. + ObservePoolStats(ctx context.Context, stats PoolStats, attrs ...attribute.KeyValue) + + // Enabled reports whether metrics are backed by a real (non-no-op) + // MeterProvider. Callers may use this to skip building attribute sets when + // nothing will be recorded. + Enabled() bool +} + +// PoolStats is a snapshot of database connection pool gauge values. +type PoolStats struct { + Active int64 // connections currently in use + Idle int64 // connections open but unused + Waiting int64 // goroutines/requests waiting for a connection + Max int64 // configured maximum pool size +} + +// recorder is the OpenTelemetry-backed implementation of Recorder. +type recorder struct { + enabled bool + + publishToDeliver metric.Float64Histogram + published metric.Int64Counter + delivered metric.Int64Counter + dropped metric.Int64Counter + subscriberLag metric.Int64Gauge + listenerReconn metric.Int64Counter + payloadSize metric.Int64Histogram + + poolActive metric.Int64Gauge + poolIdle metric.Int64Gauge + poolWaiting metric.Int64Gauge + poolMax metric.Int64Gauge +} + +// compile-time check that recorder satisfies Recorder. +var _ Recorder = (*recorder)(nil) + +// New creates a Recorder backed by the supplied MeterProvider. +// +// If mp is nil, a no-op MeterProvider is used: instruments still register +// successfully and every Record/Observe/Inc call becomes a cheap no-op. This is +// the safe default when no exporter (e.g. Cloud Monitoring) is configured, such +// as when no GCP project is set. +// +// New returns an error only if instrument registration fails, which should not +// happen with a well-behaved MeterProvider. +func New(mp metric.MeterProvider) (Recorder, error) { + enabled := mp != nil + if mp == nil { + mp = noop.NewMeterProvider() + } + + meter := mp.Meter(instrumentationName) + r := &recorder{enabled: enabled} + + var err error + + if r.publishToDeliver, err = meter.Float64Histogram( + MetricPublishToDeliverLatency, + metric.WithUnit("ms"), + metric.WithDescription("Latency from publishing a notification to delivering it to a subscriber"), + ); err != nil { + return nil, fmt.Errorf("registering %s: %w", MetricPublishToDeliverLatency, err) + } + + if r.published, err = meter.Int64Counter( + MetricNotificationsPublished, + metric.WithUnit("{notification}"), + metric.WithDescription("Number of notifications published to Postgres"), + ); err != nil { + return nil, fmt.Errorf("registering %s: %w", MetricNotificationsPublished, err) + } + + if r.delivered, err = meter.Int64Counter( + MetricNotificationsDelivered, + metric.WithUnit("{notification}"), + metric.WithDescription("Number of notifications delivered to subscribers"), + ); err != nil { + return nil, fmt.Errorf("registering %s: %w", MetricNotificationsDelivered, err) + } + + if r.dropped, err = meter.Int64Counter( + MetricNotificationsDropped, + metric.WithUnit("{notification}"), + metric.WithDescription("Number of notifications dropped before delivery"), + ); err != nil { + return nil, fmt.Errorf("registering %s: %w", MetricNotificationsDropped, err) + } + + if r.subscriberLag, err = meter.Int64Gauge( + MetricSubscriberLag, + metric.WithUnit("{notification}"), + metric.WithDescription("Current subscriber lag (notifications behind)"), + ); err != nil { + return nil, fmt.Errorf("registering %s: %w", MetricSubscriberLag, err) + } + + if r.listenerReconn, err = meter.Int64Counter( + MetricListenerReconnects, + metric.WithUnit("{reconnect}"), + metric.WithDescription("Number of LISTEN connection reconnects"), + ); err != nil { + return nil, fmt.Errorf("registering %s: %w", MetricListenerReconnects, err) + } + + if r.payloadSize, err = meter.Int64Histogram( + MetricPayloadSize, + metric.WithUnit("By"), + metric.WithDescription("Size of notification payloads in bytes"), + ); err != nil { + return nil, fmt.Errorf("registering %s: %w", MetricPayloadSize, err) + } + + if r.poolActive, err = meter.Int64Gauge( + MetricPoolConnectionsActive, + metric.WithUnit("{connection}"), + metric.WithDescription("Database connections currently in use"), + ); err != nil { + return nil, fmt.Errorf("registering %s: %w", MetricPoolConnectionsActive, err) + } + + if r.poolIdle, err = meter.Int64Gauge( + MetricPoolConnectionsIdle, + metric.WithUnit("{connection}"), + metric.WithDescription("Database connections open but idle"), + ); err != nil { + return nil, fmt.Errorf("registering %s: %w", MetricPoolConnectionsIdle, err) + } + + if r.poolWaiting, err = meter.Int64Gauge( + MetricPoolConnectionsWaiting, + metric.WithUnit("{request}"), + metric.WithDescription("Requests waiting for a database connection"), + ); err != nil { + return nil, fmt.Errorf("registering %s: %w", MetricPoolConnectionsWaiting, err) + } + + if r.poolMax, err = meter.Int64Gauge( + MetricPoolConnectionsMax, + metric.WithUnit("{connection}"), + metric.WithDescription("Configured maximum database pool size"), + ); err != nil { + return nil, fmt.Errorf("registering %s: %w", MetricPoolConnectionsMax, err) + } + + return r, nil +} + +// NewDisabled returns a Recorder whose calls are all no-ops. It is equivalent to +// New(nil) but never returns an error, which is convenient for tests and for +// call sites that want an explicit disabled recorder. +func NewDisabled() Recorder { + r, _ := New(nil) + return r +} + +func (r *recorder) Enabled() bool { return r.enabled } + +func (r *recorder) RecordPublishToDeliverLatency(ctx context.Context, ms float64, attrs ...attribute.KeyValue) { + r.publishToDeliver.Record(ctx, ms, metric.WithAttributes(attrs...)) +} + +func (r *recorder) IncPublished(ctx context.Context, n int64, attrs ...attribute.KeyValue) { + r.published.Add(ctx, n, metric.WithAttributes(attrs...)) +} + +func (r *recorder) IncDelivered(ctx context.Context, n int64, attrs ...attribute.KeyValue) { + r.delivered.Add(ctx, n, metric.WithAttributes(attrs...)) +} + +func (r *recorder) IncDropped(ctx context.Context, n int64, attrs ...attribute.KeyValue) { + r.dropped.Add(ctx, n, metric.WithAttributes(attrs...)) +} + +func (r *recorder) ObserveSubscriberLag(ctx context.Context, lag int64, attrs ...attribute.KeyValue) { + r.subscriberLag.Record(ctx, lag, metric.WithAttributes(attrs...)) +} + +func (r *recorder) IncListenerReconnects(ctx context.Context, n int64, attrs ...attribute.KeyValue) { + r.listenerReconn.Add(ctx, n, metric.WithAttributes(attrs...)) +} + +func (r *recorder) RecordPayloadSize(ctx context.Context, bytes int64, attrs ...attribute.KeyValue) { + r.payloadSize.Record(ctx, bytes, metric.WithAttributes(attrs...)) +} + +func (r *recorder) ObservePoolStats(ctx context.Context, stats PoolStats, attrs ...attribute.KeyValue) { + opt := metric.WithAttributes(attrs...) + r.poolActive.Record(ctx, stats.Active, opt) + r.poolIdle.Record(ctx, stats.Idle, opt) + r.poolWaiting.Record(ctx, stats.Waiting, opt) + r.poolMax.Record(ctx, stats.Max, opt) +} diff --git a/pkg/observability/dbmetrics/dbmetrics_test.go b/pkg/observability/dbmetrics/dbmetrics_test.go new file mode 100644 index 000000000..382ada63d --- /dev/null +++ b/pkg/observability/dbmetrics/dbmetrics_test.go @@ -0,0 +1,128 @@ +/* +Copyright 2025 The Scion Authors. +*/ + +package dbmetrics + +import ( + "context" + "testing" + + "go.opentelemetry.io/otel/attribute" + sdkmetric "go.opentelemetry.io/otel/sdk/metric" + "go.opentelemetry.io/otel/sdk/metric/metricdata" +) + +// TestNewDisabledRegisters verifies that the safe default (no MeterProvider, +// e.g. when no GCP project/exporter is configured) registers all instruments +// without error and reports itself disabled. +func TestNewDisabledRegisters(t *testing.T) { + r, err := New(nil) + if err != nil { + t.Fatalf("New(nil) returned error: %v", err) + } + if r == nil { + t.Fatal("New(nil) returned nil Recorder") + } + if r.Enabled() { + t.Error("expected Recorder backed by no-op provider to report Enabled()==false") + } +} + +// TestNewDisabledRecordsAreNoops ensures every method is safe to call when +// metrics are disabled (no panics, no errors). +func TestNewDisabledRecordsAreNoops(t *testing.T) { + r := NewDisabled() + ctx := context.Background() + attrs := []attribute.KeyValue{attribute.String("channel", "events")} + + // None of these should panic. + r.RecordPublishToDeliverLatency(ctx, 12.5, attrs...) + r.IncPublished(ctx, 1, attrs...) + r.IncDelivered(ctx, 1, attrs...) + r.IncDropped(ctx, 1, attrs...) + r.ObserveSubscriberLag(ctx, 3, attrs...) + r.IncListenerReconnects(ctx, 1, attrs...) + r.RecordPayloadSize(ctx, 256, attrs...) + r.ObservePoolStats(ctx, PoolStats{Active: 2, Idle: 8, Waiting: 0, Max: 10}, attrs...) +} + +// TestNewWithRealProviderRegisters verifies registration succeeds against a real +// SDK MeterProvider and that the resulting Recorder reports itself enabled. +func TestNewWithRealProviderRegisters(t *testing.T) { + reader := sdkmetric.NewManualReader() + mp := sdkmetric.NewMeterProvider(sdkmetric.WithReader(reader)) + t.Cleanup(func() { _ = mp.Shutdown(context.Background()) }) + + r, err := New(mp) + if err != nil { + t.Fatalf("New(mp) returned error: %v", err) + } + if !r.Enabled() { + t.Error("expected Recorder backed by real provider to report Enabled()==true") + } +} + +// TestRecordedMetricsAreExported drives every instrument and asserts the +// expected metric names show up in a collected snapshot. This proves the +// registration paths are wired correctly end-to-end. +func TestRecordedMetricsAreExported(t *testing.T) { + reader := sdkmetric.NewManualReader() + mp := sdkmetric.NewMeterProvider(sdkmetric.WithReader(reader)) + t.Cleanup(func() { _ = mp.Shutdown(context.Background()) }) + + r, err := New(mp) + if err != nil { + t.Fatalf("New(mp) returned error: %v", err) + } + + ctx := context.Background() + attrs := []attribute.KeyValue{attribute.String("channel", "events")} + + r.RecordPublishToDeliverLatency(ctx, 12.5, attrs...) + r.IncPublished(ctx, 2, attrs...) + r.IncDelivered(ctx, 1, attrs...) + r.IncDropped(ctx, 1, attrs...) + r.ObserveSubscriberLag(ctx, 5, attrs...) + r.IncListenerReconnects(ctx, 1, attrs...) + r.RecordPayloadSize(ctx, 256, attrs...) + r.ObservePoolStats(ctx, PoolStats{Active: 2, Idle: 8, Waiting: 1, Max: 10}, attrs...) + + var rm metricdata.ResourceMetrics + if err := reader.Collect(ctx, &rm); err != nil { + t.Fatalf("collecting metrics: %v", err) + } + + got := collectedNames(&rm) + + want := []string{ + MetricPublishToDeliverLatency, + MetricNotificationsPublished, + MetricNotificationsDelivered, + MetricNotificationsDropped, + MetricSubscriberLag, + MetricListenerReconnects, + MetricPayloadSize, + MetricPoolConnectionsActive, + MetricPoolConnectionsIdle, + MetricPoolConnectionsWaiting, + MetricPoolConnectionsMax, + } + + for _, name := range want { + if !got[name] { + t.Errorf("expected metric %q to be exported, but it was not present", name) + } + } +} + +// collectedNames flattens the collected metric names into a set for assertion. +func collectedNames(rm *metricdata.ResourceMetrics) map[string]bool { + names := make(map[string]bool) + for _, sm := range rm.ScopeMetrics { + for _, m := range sm.Metrics { + names[m.Name] = true + } + } + return names +} diff --git a/pkg/observability/dbmetrics/pool_sampler.go b/pkg/observability/dbmetrics/pool_sampler.go new file mode 100644 index 000000000..0990abb6f --- /dev/null +++ b/pkg/observability/dbmetrics/pool_sampler.go @@ -0,0 +1,79 @@ +/* +Copyright 2025 The Scion Authors. +*/ + +package dbmetrics + +import ( + "context" + "database/sql" + "time" + + "go.opentelemetry.io/otel/attribute" +) + +// DefaultPoolSampleInterval is the cadence used by StartPoolSampler when the +// caller passes a non-positive interval. +const DefaultPoolSampleInterval = 15 * time.Second + +// StatsProvider is the subset of *sql.DB needed to sample connection-pool +// gauges. *sql.DB satisfies it directly; tests can supply a fake. +type StatsProvider interface { + Stats() sql.DBStats +} + +// poolStatsFrom maps a database/sql DBStats snapshot onto the PoolStats gauge +// set understood by the Recorder. +// +// Note on Waiting: database/sql does not expose an instantaneous "callers +// currently blocked on a connection" gauge. WaitCount is the cumulative number +// of times a caller had to wait for a connection, which is the canonical +// pool-saturation signal: a flat WaitCount means the pool is never exhausted, a +// rising one means callers are queuing (the trigger for the pooler decision in +// CONNECTION-BUDGET.md). It is reported as-is so dashboards can rate() it. +func poolStatsFrom(s sql.DBStats) PoolStats { + return PoolStats{ + Active: int64(s.InUse), + Idle: int64(s.Idle), + Waiting: s.WaitCount, + Max: int64(s.MaxOpenConnections), + } +} + +// StartPoolSampler launches a goroutine that periodically snapshots db's +// connection-pool stats and records them via rec, until ctx is cancelled or the +// returned stop func is called (whichever happens first). stop is idempotent. +// +// It is the P3-6 wiring that feeds the P0-5 monitoring scaffold's pool gauges +// (scion.db.pool.connections.{active,idle,waiting,max}). When rec is disabled +// (no MeterProvider configured) or db is nil, sampling is skipped entirely so +// there is no idle goroutine in the common no-exporter case. +func StartPoolSampler(ctx context.Context, rec Recorder, db StatsProvider, interval time.Duration, attrs ...attribute.KeyValue) (stop func()) { + if rec == nil || !rec.Enabled() || db == nil { + return func() {} + } + if interval <= 0 { + interval = DefaultPoolSampleInterval + } + + sampleCtx, cancel := context.WithCancel(ctx) + go func() { + ticker := time.NewTicker(interval) + defer ticker.Stop() + + // Emit one snapshot immediately so the gauges are populated without + // waiting a full interval. + rec.ObservePoolStats(sampleCtx, poolStatsFrom(db.Stats()), attrs...) + + for { + select { + case <-sampleCtx.Done(): + return + case <-ticker.C: + rec.ObservePoolStats(sampleCtx, poolStatsFrom(db.Stats()), attrs...) + } + } + }() + + return cancel +} diff --git a/pkg/observability/dbmetrics/pool_sampler_test.go b/pkg/observability/dbmetrics/pool_sampler_test.go new file mode 100644 index 000000000..10f1beb68 --- /dev/null +++ b/pkg/observability/dbmetrics/pool_sampler_test.go @@ -0,0 +1,123 @@ +/* +Copyright 2025 The Scion Authors. +*/ + +package dbmetrics + +import ( + "context" + "database/sql" + "sync" + "testing" + "time" + + "go.opentelemetry.io/otel/attribute" +) + +// fakeStatsProvider returns a fixed DBStats snapshot. +type fakeStatsProvider struct{ s sql.DBStats } + +func (f fakeStatsProvider) Stats() sql.DBStats { return f.s } + +// capturingRecorder records ObservePoolStats calls and reports itself enabled. +// All other Recorder methods delegate to a disabled no-op recorder. +type capturingRecorder struct { + Recorder + mu sync.Mutex + got []PoolStats + seen chan struct{} +} + +func newCapturingRecorder() *capturingRecorder { + return &capturingRecorder{Recorder: NewDisabled(), seen: make(chan struct{}, 16)} +} + +func (c *capturingRecorder) Enabled() bool { return true } + +func (c *capturingRecorder) ObservePoolStats(_ context.Context, stats PoolStats, _ ...attribute.KeyValue) { + c.mu.Lock() + c.got = append(c.got, stats) + c.mu.Unlock() + select { + case c.seen <- struct{}{}: + default: + } +} + +func (c *capturingRecorder) snapshot() []PoolStats { + c.mu.Lock() + defer c.mu.Unlock() + out := make([]PoolStats, len(c.got)) + copy(out, c.got) + return out +} + +func TestPoolStatsFrom(t *testing.T) { + in := sql.DBStats{ + MaxOpenConnections: 20, + InUse: 7, + Idle: 3, + WaitCount: 42, + } + got := poolStatsFrom(in) + want := PoolStats{Active: 7, Idle: 3, Waiting: 42, Max: 20} + if got != want { + t.Fatalf("poolStatsFrom(%+v) = %+v, want %+v", in, got, want) + } +} + +// A disabled recorder must not start a sampling goroutine; stop is a safe no-op. +func TestStartPoolSampler_DisabledIsNoop(t *testing.T) { + stop := StartPoolSampler(context.Background(), NewDisabled(), fakeStatsProvider{}, time.Millisecond) + if stop == nil { + t.Fatal("stop must never be nil") + } + stop() // must not panic +} + +// A nil db must not start a goroutine. +func TestStartPoolSampler_NilDBIsNoop(t *testing.T) { + stop := StartPoolSampler(context.Background(), newCapturingRecorder(), nil, time.Millisecond) + stop() +} + +// An enabled recorder samples immediately (without waiting a full interval) and +// stop halts further sampling. +func TestStartPoolSampler_EmitsImmediatelyAndStops(t *testing.T) { + rec := newCapturingRecorder() + db := fakeStatsProvider{s: sql.DBStats{MaxOpenConnections: 10, InUse: 2, Idle: 1, WaitCount: 5}} + + stop := StartPoolSampler(context.Background(), rec, db, time.Hour) // long interval: rely on immediate emit + select { + case <-rec.seen: + case <-time.After(2 * time.Second): + t.Fatal("expected an immediate pool-stats sample") + } + stop() + + got := rec.snapshot() + if len(got) == 0 { + t.Fatal("expected at least one sample") + } + want := PoolStats{Active: 2, Idle: 1, Waiting: 5, Max: 10} + if got[0] != want { + t.Fatalf("first sample = %+v, want %+v", got[0], want) + } +} + +// Cancelling the parent context stops sampling. +func TestStartPoolSampler_ContextCancelStops(t *testing.T) { + rec := newCapturingRecorder() + db := fakeStatsProvider{s: sql.DBStats{MaxOpenConnections: 4}} + + ctx, cancel := context.WithCancel(context.Background()) + stop := StartPoolSampler(ctx, rec, db, time.Hour) + defer stop() + + select { + case <-rec.seen: + case <-time.After(2 * time.Second): + t.Fatal("expected an immediate sample") + } + cancel() // goroutine should observe ctx.Done and exit; no assertion beyond no-panic/no-leak +} diff --git a/pkg/secret/gcpbackend_test.go b/pkg/secret/gcpbackend_test.go index fb0159b93..d50fbbdbd 100644 --- a/pkg/secret/gcpbackend_test.go +++ b/pkg/secret/gcpbackend_test.go @@ -27,7 +27,6 @@ import ( smpb "cloud.google.com/go/secretmanager/apiv1/secretmanagerpb" "github.com/GoogleCloudPlatform/scion/pkg/store" - "github.com/GoogleCloudPlatform/scion/pkg/store/sqlite" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) @@ -138,7 +137,7 @@ func (m *mockSMClient) Close() error { func createTestGCPBackend(t *testing.T) (*GCPBackend, *mockSMClient) { t.Helper() - s, err := sqlite.New(":memory:") + s, err := newTestStore(":memory:") if err != nil { t.Fatalf("failed to create test store: %v", err) } @@ -156,7 +155,7 @@ func TestGCPBackend_GetRecoverFromGCPSM_NoDBRecord(t *testing.T) { ctx := context.Background() // Create first backend, store a secret via Set (populates both GCP SM and DB) - s1, err := sqlite.New(":memory:") + s1, err := newTestStore(":memory:") if err != nil { t.Fatalf("failed to create test store: %v", err) } @@ -180,7 +179,7 @@ func TestGCPBackend_GetRecoverFromGCPSM_NoDBRecord(t *testing.T) { // Create a second backend with a FRESH database (simulating DB reset) // but sharing the same mock GCP SM client (secrets still exist there) - s2, err := sqlite.New(":memory:") + s2, err := newTestStore(":memory:") if err != nil { t.Fatalf("failed to create test store 2: %v", err) } diff --git a/pkg/secret/localbackend_test.go b/pkg/secret/localbackend_test.go index a1a967ecb..ef87b26d1 100644 --- a/pkg/secret/localbackend_test.go +++ b/pkg/secret/localbackend_test.go @@ -21,12 +21,11 @@ import ( "testing" "github.com/GoogleCloudPlatform/scion/pkg/store" - "github.com/GoogleCloudPlatform/scion/pkg/store/sqlite" ) func createTestStore(t *testing.T) store.SecretStore { t.Helper() - s, err := sqlite.New(":memory:") + s, err := newTestStore(":memory:") if err != nil { t.Fatalf("failed to create test store: %v", err) } @@ -173,7 +172,7 @@ func TestLocalBackend_Get(t *testing.T) { ctx := context.Background() seedSecret(t, s, &store.Secret{ - ID: "s1", + ID: tid("s1"), Key: "API_KEY", EncryptedValue: "sk-test-123", SecretType: store.SecretTypeEnvironment, @@ -200,7 +199,7 @@ func TestLocalBackend_Delete(t *testing.T) { ctx := context.Background() seedSecret(t, s, &store.Secret{ - ID: "s1", + ID: tid("s1"), Key: "TO_DELETE", EncryptedValue: "value", SecretType: store.SecretTypeEnvironment, @@ -235,7 +234,7 @@ func TestLocalBackend_List(t *testing.T) { for i, name := range []string{"A_KEY", "B_KEY", "C_KEY"} { seedSecret(t, s, &store.Secret{ - ID: "s" + string(rune('1'+i)), + ID: tid("s" + string(rune('1'+i))), Key: name, EncryptedValue: "val-" + name, SecretType: store.SecretTypeEnvironment, @@ -259,7 +258,7 @@ func TestLocalBackend_ListFilterByType(t *testing.T) { ctx := context.Background() seedSecret(t, s, &store.Secret{ - ID: "s1", + ID: tid("s1"), Key: "ENV_KEY", EncryptedValue: "val", SecretType: store.SecretTypeEnvironment, @@ -268,7 +267,7 @@ func TestLocalBackend_ListFilterByType(t *testing.T) { ScopeID: "user-1", }) seedSecret(t, s, &store.Secret{ - ID: "s2", + ID: tid("s2"), Key: "FILE_KEY", EncryptedValue: "data", SecretType: store.SecretTypeFile, @@ -294,7 +293,7 @@ func TestLocalBackend_GetMeta(t *testing.T) { ctx := context.Background() seedSecret(t, s, &store.Secret{ - ID: "s1", + ID: tid("s1"), Key: "META_KEY", EncryptedValue: "secret-value", SecretType: store.SecretTypeVariable, @@ -321,7 +320,7 @@ func TestLocalBackend_Resolve(t *testing.T) { // User-level secrets seedSecret(t, s, &store.Secret{ - ID: "s1", + ID: tid("s1"), Key: "API_KEY", EncryptedValue: "user-api-key", SecretType: store.SecretTypeEnvironment, @@ -330,7 +329,7 @@ func TestLocalBackend_Resolve(t *testing.T) { ScopeID: "user-1", }) seedSecret(t, s, &store.Secret{ - ID: "s2", + ID: tid("s2"), Key: "TLS_CERT", EncryptedValue: "cert-data", SecretType: store.SecretTypeFile, @@ -341,7 +340,7 @@ func TestLocalBackend_Resolve(t *testing.T) { // Project-level override seedSecret(t, s, &store.Secret{ - ID: "s3", + ID: tid("s3"), Key: "API_KEY", EncryptedValue: "grove-api-key", SecretType: store.SecretTypeEnvironment, @@ -350,7 +349,7 @@ func TestLocalBackend_Resolve(t *testing.T) { ScopeID: "grove-1", }) seedSecret(t, s, &store.Secret{ - ID: "s4", + ID: tid("s4"), Key: "DB_PASS", EncryptedValue: "grove-db-pass", SecretType: store.SecretTypeEnvironment, @@ -425,7 +424,7 @@ func TestLocalBackend_ResolveBrokerOverride(t *testing.T) { ctx := context.Background() seedSecret(t, s, &store.Secret{ - ID: "s1", + ID: tid("s1"), Key: "API_KEY", EncryptedValue: "user-key", SecretType: store.SecretTypeEnvironment, @@ -434,7 +433,7 @@ func TestLocalBackend_ResolveBrokerOverride(t *testing.T) { ScopeID: "user-1", }) seedSecret(t, s, &store.Secret{ - ID: "s2", + ID: tid("s2"), Key: "API_KEY", EncryptedValue: "broker-key", SecretType: store.SecretTypeEnvironment, @@ -465,7 +464,7 @@ func TestLocalBackend_ResolveExcludesInternalSecrets(t *testing.T) { // Seed an internal signing key at hub scope (simulates hub signing keys) seedSecret(t, s, &store.Secret{ - ID: "signing-1", + ID: tid("signing-1"), Key: "agent_signing_key", EncryptedValue: "super-secret-key-material", SecretType: store.SecretTypeInternal, @@ -476,7 +475,7 @@ func TestLocalBackend_ResolveExcludesInternalSecrets(t *testing.T) { // Seed a normal hub-scoped environment secret seedSecret(t, s, &store.Secret{ - ID: "hub-env-1", + ID: tid("hub-env-1"), Key: "HUB_API_TOKEN", EncryptedValue: "hub-token-value", SecretType: store.SecretTypeEnvironment, @@ -487,7 +486,7 @@ func TestLocalBackend_ResolveExcludesInternalSecrets(t *testing.T) { // Seed a user-scoped secret seedSecret(t, s, &store.Secret{ - ID: "user-env-1", + ID: tid("user-env-1"), Key: "USER_KEY", EncryptedValue: "user-key-value", SecretType: store.SecretTypeEnvironment, @@ -532,7 +531,7 @@ func TestLocalBackend_ResolveDuplicateTargetAcrossScopes(t *testing.T) { // User-level file secret targeting /tmp/my-secret.json seedSecret(t, s, &store.Secret{ - ID: "u1", + ID: tid("u1"), Key: "my-svc-account", EncryptedValue: "user-cert-data", SecretType: store.SecretTypeFile, @@ -544,7 +543,7 @@ func TestLocalBackend_ResolveDuplicateTargetAcrossScopes(t *testing.T) { // Project-level file secret targeting the SAME path seedSecret(t, s, &store.Secret{ - ID: "g1", + ID: tid("g1"), Key: "my-key", EncryptedValue: "grove-cert-data", SecretType: store.SecretTypeFile, @@ -585,7 +584,7 @@ func TestLocalBackend_ResolveDuplicateEnvTargetAcrossScopes(t *testing.T) { // User-level env secret targeting FOO_VAR seedSecret(t, s, &store.Secret{ - ID: "u1", + ID: tid("u1"), Key: "user-foo", EncryptedValue: "user-val", SecretType: store.SecretTypeEnvironment, @@ -596,7 +595,7 @@ func TestLocalBackend_ResolveDuplicateEnvTargetAcrossScopes(t *testing.T) { // Project-level env secret targeting the SAME env var seedSecret(t, s, &store.Secret{ - ID: "g1", + ID: tid("g1"), Key: "grove-foo", EncryptedValue: "grove-val", SecretType: store.SecretTypeEnvironment, @@ -675,7 +674,7 @@ func TestLocalBackend_ResolveProgeny_AllowProgenyGrantsAccess(t *testing.T) { // User "alice" creates a secret with allowProgeny seedSecret(t, s, &store.Secret{ - ID: "sec-prog-1", + ID: tid("sec-prog-1"), Key: "ANTHROPIC_API_KEY", EncryptedValue: "sk-ant-progeny", SecretType: store.SecretTypeEnvironment, @@ -716,7 +715,7 @@ func TestLocalBackend_ResolveProgeny_DeepAncestry(t *testing.T) { ctx := context.Background() seedSecret(t, s, &store.Secret{ - ID: "sec-prog-deep", + ID: tid("sec-prog-deep"), Key: "DEEP_KEY", EncryptedValue: "deep-value", SecretType: store.SecretTypeEnvironment, @@ -754,7 +753,7 @@ func TestLocalBackend_ResolveProgeny_ProjectOverridesProgeny(t *testing.T) { // User-scoped progeny secret seedSecret(t, s, &store.Secret{ - ID: "sec-prog-override-user", + ID: tid("sec-prog-override-user"), Key: "API_KEY", EncryptedValue: "user-progeny-value", SecretType: store.SecretTypeEnvironment, @@ -767,7 +766,7 @@ func TestLocalBackend_ResolveProgeny_ProjectOverridesProgeny(t *testing.T) { // Project-scoped secret with same key (higher precedence) seedSecret(t, s, &store.Secret{ - ID: "sec-prog-override-grove", + ID: tid("sec-prog-override-grove"), Key: "API_KEY", EncryptedValue: "grove-value", SecretType: store.SecretTypeEnvironment, @@ -812,7 +811,7 @@ func TestLocalBackend_ResolveProgeny_DeniedWhenFlagFalse(t *testing.T) { // User-scoped secret WITHOUT allowProgeny seedSecret(t, s, &store.Secret{ - ID: "sec-no-prog", + ID: tid("sec-no-prog"), Key: "PRIVATE_KEY", EncryptedValue: "private-value", SecretType: store.SecretTypeEnvironment, @@ -848,7 +847,7 @@ func TestLocalBackend_ResolveProgeny_DeniedWhenAncestryMismatch(t *testing.T) { // Alice's secret with allowProgeny seedSecret(t, s, &store.Secret{ - ID: "sec-ancestry-miss", + ID: tid("sec-ancestry-miss"), Key: "ALICE_SECRET", EncryptedValue: "alice-value", SecretType: store.SecretTypeEnvironment, @@ -885,7 +884,7 @@ func TestLocalBackend_ResolveProgeny_DeniedByPolicyCheck(t *testing.T) { ctx := context.Background() seedSecret(t, s, &store.Secret{ - ID: "sec-policy-deny", + ID: tid("sec-policy-deny"), Key: "POLICY_KEY", EncryptedValue: "policy-value", SecretType: store.SecretTypeEnvironment, @@ -921,7 +920,7 @@ func TestLocalBackend_ResolveProgeny_NilAuthzCheckIncludesAll(t *testing.T) { ctx := context.Background() seedSecret(t, s, &store.Secret{ - ID: "sec-no-authz", + ID: tid("sec-no-authz"), Key: "NO_AUTHZ_KEY", EncryptedValue: "no-authz-value", SecretType: store.SecretTypeEnvironment, @@ -960,7 +959,7 @@ func TestLocalBackend_ResolveProgeny_NilOptsNoProgeny(t *testing.T) { ctx := context.Background() seedSecret(t, s, &store.Secret{ - ID: "sec-nil-opts", + ID: tid("sec-nil-opts"), Key: "NIL_OPTS_KEY", EncryptedValue: "nil-opts-value", SecretType: store.SecretTypeEnvironment, diff --git a/pkg/secret/teststore_test.go b/pkg/secret/teststore_test.go new file mode 100644 index 000000000..7ac72ffe7 --- /dev/null +++ b/pkg/secret/teststore_test.go @@ -0,0 +1,64 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build !no_sqlite + +package secret + +import ( + "context" + "fmt" + "sync/atomic" + + "github.com/GoogleCloudPlatform/scion/pkg/ent/entc" + "github.com/GoogleCloudPlatform/scion/pkg/store" + "github.com/GoogleCloudPlatform/scion/pkg/store/entadapter" + "github.com/google/uuid" +) + +// tid deterministically maps a human-readable test identifier (e.g. "user-1") +// to a stable UUID string. The Ent-backed store uses UUID primary keys, so test +// fixtures cannot use arbitrary strings as IDs; wrapping a readable name in tid +// preserves test legibility and cross-reference consistency while satisfying the +// UUID requirement. +func tid(name string) string { + return uuid.NewSHA1(uuid.NameSpaceOID, []byte(name)).String() +} + +// testStoreSeq generates unique in-memory database names so each call to +// newTestStore(":memory:") gets an isolated database. +var testStoreSeq atomic.Int64 + +// newTestStore opens a fresh Ent-backed store for tests, mirroring the +// production single-database layout. It is a drop-in replacement for the former +// raw-SQL constructor: pass ":memory:" for an isolated in-memory database or a +// file path for a persistent one. The returned store is already migrated. +func newTestStore(url string) (store.Store, error) { + var dsn string + if url == ":memory:" { + dsn = fmt.Sprintf("file:secrettest%d?mode=memory&cache=shared", testStoreSeq.Add(1)) + } else { + dsn = "file:" + url + "?cache=shared" + } + + client, err := entc.OpenSQLite(dsn, entc.PoolConfig{}) + if err != nil { + return nil, err + } + if err := entc.AutoMigrate(context.Background(), client); err != nil { + _ = client.Close() + return nil, err + } + return entadapter.NewCompositeStore(client), nil +} diff --git a/pkg/store/concurrency.go b/pkg/store/concurrency.go new file mode 100644 index 000000000..454afe632 --- /dev/null +++ b/pkg/store/concurrency.go @@ -0,0 +1,97 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package store + +import "context" + +// The interfaces in this file are OPTIONAL capabilities that a store backend may +// implement to support running N stateless hub processes against one shared +// database (the multi-replica Postgres deployment, D3). +// +// They are deliberately kept out of the core store.Store interface so that: +// - backends that do not need cluster coordination (e.g. the single-writer +// SQLite store, or test fakes that embed store.Store) are unaffected; +// - callers degrade gracefully via a type assertion: when the capability is +// absent the caller falls back to the historical single-process behavior, +// which is correct for a single replica. +// +// See /scion-volumes/scratchpad/postgres-integration/CONCURRENCY-AUDIT.md for +// the per-site mapping of which primitive guards which read-modify-write path. + +// AdvisoryLockKey identifies a piece of cluster-wide-once work. Keys must be +// stable across releases and unique per logical job, because they are passed to +// pg_try_advisory_lock as the lock identifier. The chosen values are arbitrary +// but fixed; the 0x5C10 ("SCIO") prefix namespaces them away from any advisory +// keys a future feature might pick. +type AdvisoryLockKey int64 + +const ( + // LockScheduleEvaluator guards the recurring schedule-evaluator tick so a + // single replica claims and fires due schedules per tick. + LockScheduleEvaluator AdvisoryLockKey = 0x5C100001 + // LockAgentHeartbeatTimeout guards the stale-agent → offline sweep. + LockAgentHeartbeatTimeout AdvisoryLockKey = 0x5C100002 + // LockAgentStalledDetection guards the stalled-agent sweep. + LockAgentStalledDetection AdvisoryLockKey = 0x5C100003 + // LockSoftDeletePurge guards the soft-deleted-agent / old-event purge. + LockSoftDeletePurge AdvisoryLockKey = 0x5C100004 + // LockGitHubAppHealthCheck guards the periodic GitHub App installation + // health check. + LockGitHubAppHealthCheck AdvisoryLockKey = 0x5C100005 +) + +// AdvisoryLocker is implemented by backends that can take a cluster-wide +// advisory lock. It is the singleton/leader primitive for "run this work on +// exactly one replica per tick" jobs (schedule tick, maintenance, cleanup). +// +// On Postgres this is backed by session-level pg_try_advisory_lock held on a +// dedicated connection for the lifetime of the returned release func. On +// single-writer backends (SQLite) the lock is a no-op that always succeeds: +// there is only ever one writer, so the work is already effectively singleton. +type AdvisoryLocker interface { + // TryAdvisoryLock attempts to acquire the named advisory lock without + // blocking. If acquired is true the caller owns the lock and MUST call the + // returned release func exactly once when the critical section ends + // (release is always non-nil and safe to call even when acquired is false). + // If acquired is false another replica currently holds the lock and the + // caller should skip the work this round. + TryAdvisoryLock(ctx context.Context, key AdvisoryLockKey) (acquired bool, release func() error, err error) +} + +// NOTE: the SERIALIZABLE + retry-on-serialization-failure primitive (P3-4) is +// provided as a concrete, dialect-aware helper on the Ent-backed store +// (entadapter.CompositeStore.RunSerializable) rather than as a store-level +// interface here, because its callback operates on a *sql.Tx and is intended +// for backend-internal multi-row-invariant paths. No core store path requires +// it today (the hot RMW paths use single-row state_version CAS or SELECT ... +// FOR UPDATE, and cross-row uniqueness is enforced by DB constraints); it is +// kept available and tested for future multi-row invariants. See +// CONCURRENCY-AUDIT.md §"Serializable retry". + +// ScheduledEventClaimer is implemented by backends that can atomically claim a +// one-shot scheduled event for execution. It is the multi-replica dedup +// primitive for the scheduler's in-memory timers: several replicas may each +// recover the same pending event from the database on startup, but only the +// replica whose atomic UPDATE ... WHERE status = 'pending' affects a row may +// execute the event's side effect (deliver a message, dispatch an agent). +type ScheduledEventClaimer interface { + // ClaimScheduledEvent atomically transitions a scheduled event from + // "pending" to claimedStatus. It returns claimed=true if this caller won + // the claim (the conditional UPDATE affected exactly one row), and + // claimed=false if the event was already claimed by another replica, was + // cancelled, or no longer exists. claimedStatus is normally + // ScheduledEventFired or ScheduledEventExpired. + ClaimScheduledEvent(ctx context.Context, id string, claimedStatus string) (claimed bool, err error) +} diff --git a/pkg/store/entadapter/agent_store.go b/pkg/store/entadapter/agent_store.go new file mode 100644 index 000000000..401b8d867 --- /dev/null +++ b/pkg/store/entadapter/agent_store.go @@ -0,0 +1,745 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package entadapter + +import ( + "context" + "encoding/json" + "sync" + "time" + + "entgo.io/ent/dialect" + entsql "entgo.io/ent/dialect/sql" + "github.com/google/uuid" + + "github.com/GoogleCloudPlatform/scion/pkg/ent" + "github.com/GoogleCloudPlatform/scion/pkg/ent/agent" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/GoogleCloudPlatform/scion/pkg/store" +) + +// defaultAgentListLimit and maxAgentListLimit mirror the pagination bounds of +// the legacy SQLite agent store so listing behavior is identical across +// backends. +const ( + defaultAgentListLimit = 50 + maxAgentListLimit = 200 +) + +// AgentStore implements the store.AgentStore sub-interface using the Ent ORM. +// +// It supersedes the former raw-SQL store implementation and is designed for +// multi-replica Postgres deployments: +// - UpdateAgent guards writes with a state_version compare-and-swap so +// concurrent updates surface store.ErrVersionConflict rather than silently +// clobbering each other. +// - The read-modify-write hot paths (UpdateAgentStatus, MarkStaleAgentsOffline, +// MarkStalledAgents) run inside a transaction and take row locks via +// SELECT ... FOR UPDATE (a no-op on SQLite, enforced on Postgres). +// - Soft-deleted agents (deleted_at IS NOT NULL) are excluded from default +// listings via an Ent predicate. +type AgentStore struct { + client *ent.Client + + // dialect is detected lazily on first use of a lock-taking path and + // memoized. SELECT ... FOR UPDATE is only emitted on Postgres; the SQLite + // driver rejects the clause outright, so it must be elided there. + dialectOnce sync.Once + dialectName string +} + +// NewAgentStore creates a new Ent-backed AgentStore. +func NewAgentStore(client *ent.Client) *AgentStore { + return &AgentStore{client: client} +} + +// usesRowLocks reports whether the backend supports SELECT ... FOR UPDATE. +// The dialect is captured from a no-op selector the first time it is needed. +func (s *AgentStore) usesRowLocks(ctx context.Context) bool { + s.dialectOnce.Do(func() { + _, _ = s.client.Agent.Query(). + Where(func(sel *entsql.Selector) { s.dialectName = sel.Dialect() }). + Exist(ctx) + }) + return s.dialectName == dialect.Postgres +} + +// Compile-time assertion that AgentStore satisfies the store.AgentStore +// sub-interface. +var _ store.AgentStore = (*AgentStore)(nil) + +// entAgentToStore converts an Ent Agent entity into a store.Agent model. +func entAgentToStore(a *ent.Agent) *store.Agent { + sa := &store.Agent{ + ID: a.ID.String(), + Slug: a.Slug, + Name: a.Name, + Template: a.Template, + ProjectID: a.ProjectID.String(), + Labels: a.Labels, + Annotations: a.Annotations, + Phase: a.Phase, + Activity: a.Activity, + ToolName: a.ToolName, + ConnectionState: a.ConnectionState, + ContainerStatus: a.ContainerStatus, + RuntimeState: a.RuntimeState, + StalledFromActivity: a.StalledFromActivity, + CurrentTurns: a.CurrentTurns, + CurrentModelCalls: a.CurrentModelCalls, + Image: a.Image, + Detached: a.Detached, + Runtime: a.Runtime, + RuntimeBrokerID: a.RuntimeBrokerID, + WebPTYEnabled: a.WebPtyEnabled, + TaskSummary: a.TaskSummary, + Message: a.Message, + Created: a.Created, + Updated: a.Updated, + Visibility: a.Visibility, + Ancestry: a.Ancestry, + StateVersion: a.StateVersion, + } + if a.CreatedBy != nil { + sa.CreatedBy = a.CreatedBy.String() + } + if a.OwnerID != nil { + sa.OwnerID = a.OwnerID.String() + } + if a.LastSeen != nil { + sa.LastSeen = *a.LastSeen + } + if a.LastActivityEvent != nil { + sa.LastActivityEvent = *a.LastActivityEvent + } + if a.StartedAt != nil { + sa.StartedAt = *a.StartedAt + } + if a.DeletedAt != nil { + sa.DeletedAt = *a.DeletedAt + } + if a.AppliedConfig != "" { + var cfg store.AgentAppliedConfig + if err := json.Unmarshal([]byte(a.AppliedConfig), &cfg); err == nil { + sa.AppliedConfig = &cfg + } + } + return sa +} + +// CreateAgent creates a new agent record. +func (s *AgentStore) CreateAgent(ctx context.Context, a *store.Agent) error { + uid, err := parseUUID(a.ID) + if err != nil { + return err + } + projectUID, err := parseUUID(a.ProjectID) + if err != nil { + return err + } + + now := time.Now() + a.Created = now + a.Updated = now + a.StateVersion = 1 + + create := s.client.Agent.Create(). + SetID(uid). + SetSlug(a.Slug). + SetName(a.Name). + SetTemplate(a.Template). + SetProjectID(projectUID). + SetPhase(a.Phase). + SetActivity(a.Activity). + SetToolName(a.ToolName). + SetConnectionState(a.ConnectionState). + SetContainerStatus(a.ContainerStatus). + SetRuntimeState(a.RuntimeState). + SetStalledFromActivity(a.StalledFromActivity). + SetCurrentTurns(a.CurrentTurns). + SetCurrentModelCalls(a.CurrentModelCalls). + SetImage(a.Image). + SetDetached(a.Detached). + SetRuntime(a.Runtime). + SetRuntimeBrokerID(a.RuntimeBrokerID). + SetWebPtyEnabled(a.WebPTYEnabled). + SetTaskSummary(a.TaskSummary). + SetMessage(a.Message). + SetCreated(now). + SetUpdated(now). + SetStateVersion(a.StateVersion) + + if a.Visibility != "" { + create.SetVisibility(a.Visibility) + } + if a.Labels != nil { + create.SetLabels(a.Labels) + } + if a.Annotations != nil { + create.SetAnnotations(a.Annotations) + } + if len(a.Ancestry) > 0 { + create.SetAncestry(a.Ancestry) + } + if cfg := marshalAppliedConfig(a.AppliedConfig); cfg != "" { + create.SetAppliedConfig(cfg) + } + if !a.LastSeen.IsZero() { + create.SetLastSeen(a.LastSeen) + } + if !a.LastActivityEvent.IsZero() { + create.SetLastActivityEvent(a.LastActivityEvent) + } + if !a.StartedAt.IsZero() { + create.SetStartedAt(a.StartedAt) + } + if !a.DeletedAt.IsZero() { + create.SetDeletedAt(a.DeletedAt) + } + if a.CreatedBy != "" { + createdByUID, err := parseUUID(a.CreatedBy) + if err != nil { + return err + } + create.SetCreatedBy(createdByUID) + } + if a.OwnerID != "" { + ownerUID, err := parseUUID(a.OwnerID) + if err != nil { + return err + } + create.SetOwnerID(ownerUID) + } + + created, err := create.Save(ctx) + if err != nil { + return mapError(err) + } + + a.Created = created.Created + a.Updated = created.Updated + return nil +} + +// GetAgent retrieves an agent by ID. +func (s *AgentStore) GetAgent(ctx context.Context, id string) (*store.Agent, error) { + uid, err := parseGetID(id) + if err != nil { + return nil, err + } + a, err := s.client.Agent.Get(ctx, uid) + if err != nil { + return nil, mapError(err) + } + return entAgentToStore(a), nil +} + +// GetAgentBySlug retrieves an agent by its slug within a project. +func (s *AgentStore) GetAgentBySlug(ctx context.Context, projectID, slug string) (*store.Agent, error) { + projectUID, err := parseUUID(projectID) + if err != nil { + return nil, err + } + a, err := s.client.Agent.Query(). + Where(agent.ProjectIDEQ(projectUID), agent.SlugEQ(slug)). + Only(ctx) + if err != nil { + return nil, mapError(err) + } + return entAgentToStore(a), nil +} + +// UpdateAgent updates an existing agent using optimistic locking on +// state_version. The mutable field set mirrors the legacy SQLite store: +// identity-adjacent operational fields are updated, while immutable lineage +// fields (created_at, created_by, project_id, ancestry) and the sciontool-owned +// counters (current_turns, current_model_calls, started_at) are left untouched. +func (s *AgentStore) UpdateAgent(ctx context.Context, a *store.Agent) error { + uid, err := parseUUID(a.ID) + if err != nil { + return err + } + + now := time.Now() + expectedVersion := a.StateVersion + newVersion := expectedVersion + 1 + + update := s.client.Agent.Update(). + Where(agent.IDEQ(uid), agent.StateVersionEQ(expectedVersion)). + SetSlug(a.Slug). + SetName(a.Name). + SetTemplate(a.Template). + SetPhase(a.Phase). + SetActivity(a.Activity). + SetToolName(a.ToolName). + SetConnectionState(a.ConnectionState). + SetContainerStatus(a.ContainerStatus). + SetRuntimeState(a.RuntimeState). + SetStalledFromActivity(a.StalledFromActivity). + SetImage(a.Image). + SetDetached(a.Detached). + SetRuntime(a.Runtime). + SetRuntimeBrokerID(a.RuntimeBrokerID). + SetWebPtyEnabled(a.WebPTYEnabled). + SetTaskSummary(a.TaskSummary). + SetMessage(a.Message). + SetVisibility(a.Visibility). + SetUpdated(now). + SetStateVersion(newVersion) + + if a.Labels != nil { + update.SetLabels(a.Labels) + } else { + update.ClearLabels() + } + if a.Annotations != nil { + update.SetAnnotations(a.Annotations) + } else { + update.ClearAnnotations() + } + if cfg := marshalAppliedConfig(a.AppliedConfig); cfg != "" { + update.SetAppliedConfig(cfg) + } else { + update.ClearAppliedConfig() + } + if a.LastSeen.IsZero() { + update.ClearLastSeen() + } else { + update.SetLastSeen(a.LastSeen) + } + if a.LastActivityEvent.IsZero() { + update.ClearLastActivityEvent() + } else { + update.SetLastActivityEvent(a.LastActivityEvent) + } + if a.DeletedAt.IsZero() { + update.ClearDeletedAt() + } else { + update.SetDeletedAt(a.DeletedAt) + } + if a.OwnerID == "" { + update.ClearOwnerID() + } else { + ownerUID, err := parseUUID(a.OwnerID) + if err != nil { + return err + } + update.SetOwnerID(ownerUID) + } + + affected, err := update.Save(ctx) + if err != nil { + return mapError(err) + } + if affected == 0 { + // No row matched the (id, state_version) pair. Distinguish a missing + // agent from a stale write so callers can retry conflicts. + exists, existErr := s.client.Agent.Query().Where(agent.IDEQ(uid)).Exist(ctx) + if existErr != nil { + return existErr + } + if !exists { + return store.ErrNotFound + } + return store.ErrVersionConflict + } + + a.Updated = now + a.StateVersion = newVersion + return nil +} + +// DeleteAgent permanently removes an agent by ID (hard delete). +func (s *AgentStore) DeleteAgent(ctx context.Context, id string) error { + uid, err := parseUUID(id) + if err != nil { + return err + } + err = s.client.Agent.DeleteOneID(uid).Exec(ctx) + if err != nil { + return mapError(err) + } + return nil +} + +// ListAgents returns agents matching the filter criteria. +func (s *AgentStore) ListAgents(ctx context.Context, filter store.AgentFilter, opts store.ListOptions) (*store.ListResult[store.Agent], error) { + preds, err := agentFilterPredicates(filter) + if err != nil { + return nil, err + } + + query := s.client.Agent.Query() + if len(preds) > 0 { + query.Where(preds...) + } + + totalCount, err := query.Clone().Count(ctx) + if err != nil { + return nil, err + } + + limit := opts.Limit + if limit <= 0 { + limit = defaultAgentListLimit + } + if limit > maxAgentListLimit { + limit = maxAgentListLimit + } + + // Fetch one extra row to detect whether a further page exists. + rows, err := query. + Order(agent.ByCreated(entsql.OrderDesc())). + Limit(limit + 1). + All(ctx) + if err != nil { + return nil, err + } + + items := make([]store.Agent, 0, len(rows)) + for _, a := range rows { + items = append(items, *entAgentToStore(a)) + } + + result := &store.ListResult[store.Agent]{ + Items: items, + TotalCount: totalCount, + } + if len(items) > limit { + result.Items = items[:limit] + result.NextCursor = items[limit-1].ID + } + return result, nil +} + +// agentFilterPredicates translates a store.AgentFilter into Ent predicates, +// preserving the exact OR/AND composition of the legacy SQLite query. +func agentFilterPredicates(filter store.AgentFilter) ([]predicate.Agent, error) { + var preds []predicate.Agent + + switch { + case len(filter.MemberOrOwnerProjectIDs) > 0: + // (project_id IN (...) OR owner_id = OwnerID) + projectUIDs := parseUUIDList(filter.MemberOrOwnerProjectIDs) + var orParts []predicate.Agent + if len(projectUIDs) > 0 { + orParts = append(orParts, agent.ProjectIDIn(projectUIDs...)) + } + if filter.OwnerID != "" { + ownerUID, err := parseUUID(filter.OwnerID) + if err != nil { + return nil, err + } + orParts = append(orParts, agent.OwnerIDEQ(ownerUID)) + } + if len(orParts) > 0 { + preds = append(preds, agent.Or(orParts...)) + } + case len(filter.MemberProjectIDs) > 0: + projectUIDs := parseUUIDList(filter.MemberProjectIDs) + preds = append(preds, agent.ProjectIDIn(projectUIDs...)) + case filter.OwnerID != "": + ownerUID, err := parseUUID(filter.OwnerID) + if err != nil { + return nil, err + } + preds = append(preds, agent.OwnerIDEQ(ownerUID)) + } + + if filter.ExcludeOwnerID != "" { + excludeUID, err := parseUUID(filter.ExcludeOwnerID) + if err != nil { + return nil, err + } + preds = append(preds, agent.OwnerIDNEQ(excludeUID)) + } + if filter.ProjectID != "" { + projectUID, err := parseUUID(filter.ProjectID) + if err != nil { + return nil, err + } + preds = append(preds, agent.ProjectIDEQ(projectUID)) + } + if filter.RuntimeBrokerID != "" { + preds = append(preds, agent.RuntimeBrokerIDEQ(filter.RuntimeBrokerID)) + } + if filter.Phase != "" { + preds = append(preds, agent.PhaseEQ(filter.Phase)) + } + if filter.AncestorID != "" { + preds = append(preds, ancestryContains(filter.AncestorID)) + } + + // Exclude soft-deleted agents unless explicitly requested. + if !filter.IncludeDeleted { + preds = append(preds, agent.DeletedAtIsNil()) + } + + return preds, nil +} + +// UpdateAgentStatus applies a partial, status-only update. It is the hottest +// agent write path, so it runs as a locked read-modify-write: the row is loaded +// with SELECT ... FOR UPDATE, the legacy sticky/transition rules are applied in +// Go, and the result is written back inside the same transaction. Unlike +// UpdateAgent it does not touch state_version (status churn is not a +// conflict-worthy mutation). +func (s *AgentStore) UpdateAgentStatus(ctx context.Context, id string, su store.AgentStatusUpdate) error { + uid, err := parseUUID(id) + if err != nil { + return err + } + + // Prime dialect detection before opening the transaction: the detection + // probe runs on s.client, which would contend with the open transaction on + // single-connection SQLite. + useLock := s.usesRowLocks(ctx) + + tx, err := s.client.Tx(ctx) + if err != nil { + return err + } + defer func() { _ = tx.Rollback() }() + + q := tx.Agent.Query().Where(agent.IDEQ(uid)) + if useLock { + q = q.ForUpdate() + } + current, err := q.Only(ctx) + if err != nil { + return mapError(err) + } + + now := time.Now() + upd := tx.Agent.UpdateOneID(uid). + SetUpdated(now). + SetLastSeen(now) + + if su.Phase != "" { + upd.SetPhase(su.Phase) + } + + activityProvided := su.Activity != "" + if activityProvided { + // Preserve a terminal/sticky activity: once an agent is stopped with a + // crashed/limits_exceeded activity, a non-terminal status report must + // not overwrite it. + sticky := current.Phase == "stopped" && + isTerminalActivity(current.Activity) && + !isTerminalActivity(su.Activity) + if !sticky { + upd.SetActivity(su.Activity) + } + // A fresh activity report clears any stalled marker and refreshes the + // activity timestamp; tool_name tracks the new activity verbatim. + upd.SetStalledFromActivity("") + upd.SetLastActivityEvent(now) + upd.SetToolName(su.ToolName) + } + + if su.Message != "" { + upd.SetMessage(su.Message) + } + if su.ConnectionState != "" { + upd.SetConnectionState(su.ConnectionState) + } + if su.ContainerStatus != "" { + upd.SetContainerStatus(su.ContainerStatus) + } + if su.RuntimeState != "" { + upd.SetRuntimeState(su.RuntimeState) + } + if su.TaskSummary != "" { + upd.SetTaskSummary(su.TaskSummary) + } + if su.CurrentTurns != nil { + upd.SetCurrentTurns(*su.CurrentTurns) + } + if su.CurrentModelCalls != nil { + upd.SetCurrentModelCalls(*su.CurrentModelCalls) + } + if su.StartedAt != "" { + if t, ok := parseTimeString(su.StartedAt); ok { + upd.SetStartedAt(t) + } + } + + if err := upd.Exec(ctx); err != nil { + return mapError(err) + } + return tx.Commit() +} + +// PurgeDeletedAgents permanently removes soft-deleted agents older than cutoff. +func (s *AgentStore) PurgeDeletedAgents(ctx context.Context, cutoff time.Time) (int, error) { + deleted, err := s.client.Agent.Delete(). + Where(agent.DeletedAtNotNil(), agent.DeletedAtLT(cutoff)). + Exec(ctx) + if err != nil { + return 0, err + } + return deleted, nil +} + +// staleOfflineExcluded lists the terminal/sticky activities that must not be +// overwritten when sweeping stale agents to "offline". +var staleOfflineExcluded = []string{"completed", "limits_exceeded", "blocked", "offline"} + +// MarkStaleAgentsOffline marks running agents whose last heartbeat predates +// threshold as offline, returning the updated records for event publishing. +func (s *AgentStore) MarkStaleAgentsOffline(ctx context.Context, threshold time.Time) ([]store.Agent, error) { + useLock := s.usesRowLocks(ctx) + + tx, err := s.client.Tx(ctx) + if err != nil { + return nil, err + } + defer func() { _ = tx.Rollback() }() + + now := time.Now() + + q := tx.Agent.Query().Where( + agent.LastSeenNotNil(), + agent.LastSeenLT(threshold), + agent.PhaseEQ("running"), + agent.ActivityNotIn(staleOfflineExcluded...), + ) + if useLock { + q = q.ForUpdate() + } + candidates, err := q.All(ctx) + if err != nil { + return nil, err + } + + updated := make([]store.Agent, 0, len(candidates)) + for _, a := range candidates { + if err := tx.Agent.UpdateOneID(a.ID). + SetActivity("offline"). + SetUpdated(now). + Exec(ctx); err != nil { + return nil, err + } + a.Activity = "offline" + a.Updated = now + updated = append(updated, *entAgentToStore(a)) + } + + if err := tx.Commit(); err != nil { + return nil, err + } + return updated, nil +} + +// stalledExcluded lists the activities that disqualify a running agent from +// being marked "stalled" (terminal, already-stalled, or intentionally waiting). +var stalledExcluded = []string{"completed", "limits_exceeded", "blocked", "stalled", "offline", "waiting_for_input"} + +// MarkStalledAgents marks running agents whose last activity event predates +// activityThreshold but whose heartbeat is still recent (>= heartbeatRecency) +// as stalled, preserving the prior activity in stalled_from_activity. +func (s *AgentStore) MarkStalledAgents(ctx context.Context, activityThreshold, heartbeatRecency time.Time) ([]store.Agent, error) { + useLock := s.usesRowLocks(ctx) + + tx, err := s.client.Tx(ctx) + if err != nil { + return nil, err + } + defer func() { _ = tx.Rollback() }() + + now := time.Now() + + q := tx.Agent.Query().Where( + agent.LastActivityEventNotNil(), + agent.LastActivityEventLT(activityThreshold), + agent.LastSeenNotNil(), + agent.LastSeenGTE(heartbeatRecency), + agent.PhaseEQ("running"), + agent.ActivityNotIn(stalledExcluded...), + ) + if useLock { + q = q.ForUpdate() + } + candidates, err := q.All(ctx) + if err != nil { + return nil, err + } + + updated := make([]store.Agent, 0, len(candidates)) + for _, a := range candidates { + prevActivity := a.Activity + if err := tx.Agent.UpdateOneID(a.ID). + SetStalledFromActivity(prevActivity). + SetActivity("stalled"). + SetUpdated(now). + Exec(ctx); err != nil { + return nil, err + } + a.StalledFromActivity = prevActivity + a.Activity = "stalled" + a.Updated = now + updated = append(updated, *entAgentToStore(a)) + } + + if err := tx.Commit(); err != nil { + return nil, err + } + return updated, nil +} + +// --- helpers --- + +// isTerminalActivity reports whether the activity is a terminal/sticky state +// that a non-terminal status report must not overwrite on a stopped agent. +func isTerminalActivity(activity string) bool { + return activity == "crashed" || activity == "limits_exceeded" +} + +// marshalAppliedConfig serializes the applied-config document to JSON text, +// returning "" for a nil config so the column is left empty. +func marshalAppliedConfig(cfg *store.AgentAppliedConfig) string { + if cfg == nil { + return "" + } + data, err := json.Marshal(cfg) + if err != nil { + return "" + } + return string(data) +} + +// parseTimeString parses a status update's started_at string, accepting the +// RFC3339 forms the legacy store persisted. It reports false when the value is +// unparseable so the caller leaves the field unchanged. +func parseTimeString(s string) (time.Time, bool) { + for _, layout := range []string{time.RFC3339Nano, time.RFC3339} { + if t, err := time.Parse(layout, s); err == nil { + return t, true + } + } + return time.Time{}, false +} + +// parseUUIDList parses a list of string UUIDs, silently skipping any that are +// malformed (mirroring the lenient handling of the legacy IN (...) filters). +func parseUUIDList(ids []string) []uuid.UUID { + out := make([]uuid.UUID, 0, len(ids)) + for _, id := range ids { + if uid, err := uuid.Parse(id); err == nil { + out = append(out, uid) + } + } + return out +} diff --git a/pkg/store/entadapter/agent_store_test.go b/pkg/store/entadapter/agent_store_test.go new file mode 100644 index 000000000..f866b5baf --- /dev/null +++ b/pkg/store/entadapter/agent_store_test.go @@ -0,0 +1,378 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build !no_sqlite + +package entadapter + +import ( + "context" + "testing" + "time" + + "github.com/GoogleCloudPlatform/scion/pkg/store" + "github.com/GoogleCloudPlatform/scion/pkg/store/enttest" + "github.com/google/uuid" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +var agentTestProjectUID = uuid.MustParse("30000000-0000-0000-0000-0000000000a1") + +// newTestAgentStore returns a fresh Ent-backed AgentStore with a single project +// seeded to satisfy the required project FK. MaxOpenConns is pinned to 1 so the +// in-memory SQLite backend serializes the transactional RMW paths. +func newTestAgentStore(t *testing.T) (*AgentStore, string) { + t.Helper() + client := enttest.NewClient(t) + + _, err := client.Project.Create(). + SetID(agentTestProjectUID). + SetName("test-project"). + SetSlug("test-project"). + Save(context.Background()) + require.NoError(t, err) + + return NewAgentStore(client), agentTestProjectUID.String() +} + +// makeAgent builds a minimal valid agent for the seeded project. +func makeAgent(projectID, slug string) *store.Agent { + return &store.Agent{ + ID: uuid.NewString(), + Slug: slug, + Name: "Agent " + slug, + Template: "default", + ProjectID: projectID, + Phase: "running", + Activity: "thinking", + Labels: map[string]string{"k": "v"}, + } +} + +func TestAgentStore_CRUD(t *testing.T) { + ctx := context.Background() + s, projectID := newTestAgentStore(t) + + a := makeAgent(projectID, "crud-1") + a.AppliedConfig = &store.AgentAppliedConfig{Image: "img:1", Model: "opus"} + require.NoError(t, s.CreateAgent(ctx, a)) + assert.Equal(t, int64(1), a.StateVersion, "CreateAgent should initialize state_version to 1") + assert.False(t, a.Created.IsZero()) + + // Get by ID round-trips all the fields we set. + got, err := s.GetAgent(ctx, a.ID) + require.NoError(t, err) + assert.Equal(t, a.Slug, got.Slug) + assert.Equal(t, a.Name, got.Name) + assert.Equal(t, a.ProjectID, got.ProjectID) + assert.Equal(t, "running", got.Phase) + assert.Equal(t, map[string]string{"k": "v"}, got.Labels) + require.NotNil(t, got.AppliedConfig) + assert.Equal(t, "img:1", got.AppliedConfig.Image) + assert.Equal(t, "opus", got.AppliedConfig.Model) + + // Get by slug. + bySlug, err := s.GetAgentBySlug(ctx, projectID, "crud-1") + require.NoError(t, err) + assert.Equal(t, a.ID, bySlug.ID) + + // Missing IDs surface as ErrNotFound. + _, err = s.GetAgent(ctx, uuid.NewString()) + assert.ErrorIs(t, err, store.ErrNotFound) + _, err = s.GetAgentBySlug(ctx, projectID, "does-not-exist") + assert.ErrorIs(t, err, store.ErrNotFound) + + // Update bumps state_version and persists changes. + got.Name = "Renamed" + got.Phase = "stopped" + require.NoError(t, s.UpdateAgent(ctx, got)) + assert.Equal(t, int64(2), got.StateVersion) + + reread, err := s.GetAgent(ctx, a.ID) + require.NoError(t, err) + assert.Equal(t, "Renamed", reread.Name) + assert.Equal(t, "stopped", reread.Phase) + assert.Equal(t, int64(2), reread.StateVersion) + + // Delete is a hard delete. + require.NoError(t, s.DeleteAgent(ctx, a.ID)) + _, err = s.GetAgent(ctx, a.ID) + assert.ErrorIs(t, err, store.ErrNotFound) + assert.ErrorIs(t, s.DeleteAgent(ctx, a.ID), store.ErrNotFound) +} + +// TestAgentStore_AncestryFilter exercises the dialect-switched json_each / +// json_array_elements_text membership filter. +func TestAgentStore_AncestryFilter(t *testing.T) { + ctx := context.Background() + s, projectID := newTestAgentStore(t) + + root := "user-root" + mid := "agent-mid" + + // child is a descendant of both root and mid. + child := makeAgent(projectID, "child") + child.Ancestry = []string{root, mid} + require.NoError(t, s.CreateAgent(ctx, child)) + + // sibling descends only from root. + sibling := makeAgent(projectID, "sibling") + sibling.Ancestry = []string{root} + require.NoError(t, s.CreateAgent(ctx, sibling)) + + // orphan has no ancestry at all. + orphan := makeAgent(projectID, "orphan") + require.NoError(t, s.CreateAgent(ctx, orphan)) + + // Filtering by root returns both descendants but not the orphan. + byRoot, err := s.ListAgents(ctx, store.AgentFilter{AncestorID: root}, store.ListOptions{}) + require.NoError(t, err) + assert.Equal(t, 2, byRoot.TotalCount) + assert.ElementsMatch(t, []string{child.ID, sibling.ID}, ids(byRoot.Items)) + + // Filtering by mid returns only the child. + byMid, err := s.ListAgents(ctx, store.AgentFilter{AncestorID: mid}, store.ListOptions{}) + require.NoError(t, err) + assert.Equal(t, 1, byMid.TotalCount) + require.Len(t, byMid.Items, 1) + assert.Equal(t, child.ID, byMid.Items[0].ID) + + // An ancestor that matches nobody returns no rows. + none, err := s.ListAgents(ctx, store.AgentFilter{AncestorID: "nobody"}, store.ListOptions{}) + require.NoError(t, err) + assert.Equal(t, 0, none.TotalCount) + assert.Empty(t, none.Items) +} + +// TestAgentStore_SoftDeleteExclusion verifies soft-deleted agents are hidden +// from default listings but returned when explicitly included. +func TestAgentStore_SoftDeleteExclusion(t *testing.T) { + ctx := context.Background() + s, projectID := newTestAgentStore(t) + + live := makeAgent(projectID, "live") + require.NoError(t, s.CreateAgent(ctx, live)) + + gone := makeAgent(projectID, "gone") + require.NoError(t, s.CreateAgent(ctx, gone)) + + // Soft-delete via UpdateAgent setting DeletedAt. + gone.DeletedAt = time.Now() + require.NoError(t, s.UpdateAgent(ctx, gone)) + + // Default listing excludes the soft-deleted agent. + def, err := s.ListAgents(ctx, store.AgentFilter{}, store.ListOptions{}) + require.NoError(t, err) + assert.Equal(t, 1, def.TotalCount) + require.Len(t, def.Items, 1) + assert.Equal(t, live.ID, def.Items[0].ID) + + // IncludeDeleted brings it back. + incl, err := s.ListAgents(ctx, store.AgentFilter{IncludeDeleted: true}, store.ListOptions{}) + require.NoError(t, err) + assert.Equal(t, 2, incl.TotalCount) + assert.ElementsMatch(t, []string{live.ID, gone.ID}, ids(incl.Items)) +} + +// TestAgentStore_OptimisticLockConflict verifies the state_version CAS guard: +// a second update issued against a stale version is rejected with +// ErrVersionConflict rather than silently overwriting the first. +func TestAgentStore_OptimisticLockConflict(t *testing.T) { + ctx := context.Background() + s, projectID := newTestAgentStore(t) + + a := makeAgent(projectID, "locked") + require.NoError(t, s.CreateAgent(ctx, a)) + + // Two readers load the same version (1). + readerA, err := s.GetAgent(ctx, a.ID) + require.NoError(t, err) + readerB, err := s.GetAgent(ctx, a.ID) + require.NoError(t, err) + require.Equal(t, int64(1), readerA.StateVersion) + require.Equal(t, int64(1), readerB.StateVersion) + + // First writer wins and advances the version to 2. + readerA.Name = "WriterA" + require.NoError(t, s.UpdateAgent(ctx, readerA)) + assert.Equal(t, int64(2), readerA.StateVersion) + + // Second writer holds the now-stale version 1 and must conflict. + readerB.Name = "WriterB" + err = s.UpdateAgent(ctx, readerB) + assert.ErrorIs(t, err, store.ErrVersionConflict) + + // The losing write left no trace. + final, err := s.GetAgent(ctx, a.ID) + require.NoError(t, err) + assert.Equal(t, "WriterA", final.Name) + assert.Equal(t, int64(2), final.StateVersion) + + // Updating a non-existent agent reports ErrNotFound, not a conflict. + ghost := makeAgent(projectID, "ghost") + ghost.StateVersion = 1 + assert.ErrorIs(t, s.UpdateAgent(ctx, ghost), store.ErrNotFound) +} + +func TestAgentStore_UpdateAgentStatus(t *testing.T) { + ctx := context.Background() + s, projectID := newTestAgentStore(t) + + a := makeAgent(projectID, "status") + a.Activity = "thinking" + require.NoError(t, s.CreateAgent(ctx, a)) + + // A normal status report updates activity, tool, and refreshes last_seen. + require.NoError(t, s.UpdateAgentStatus(ctx, a.ID, store.AgentStatusUpdate{ + Activity: "executing", + ToolName: "Bash", + })) + got, err := s.GetAgent(ctx, a.ID) + require.NoError(t, err) + assert.Equal(t, "executing", got.Activity) + assert.Equal(t, "Bash", got.ToolName) + assert.False(t, got.LastSeen.IsZero(), "last_seen should be refreshed") + assert.False(t, got.LastActivityEvent.IsZero(), "last_activity_event should be set") + + // Drive the agent to a terminal sticky state. + require.NoError(t, s.UpdateAgentStatus(ctx, a.ID, store.AgentStatusUpdate{ + Phase: "stopped", + Activity: "crashed", + })) + // A subsequent non-terminal report must NOT overwrite the sticky activity. + require.NoError(t, s.UpdateAgentStatus(ctx, a.ID, store.AgentStatusUpdate{ + Activity: "thinking", + })) + got, err = s.GetAgent(ctx, a.ID) + require.NoError(t, err) + assert.Equal(t, "crashed", got.Activity, "terminal activity must stick") + + // Unknown agent reports ErrNotFound. + assert.ErrorIs(t, s.UpdateAgentStatus(ctx, uuid.NewString(), store.AgentStatusUpdate{Phase: "running"}), store.ErrNotFound) +} + +func TestAgentStore_MarkStaleAgentsOffline(t *testing.T) { + ctx := context.Background() + s, projectID := newTestAgentStore(t) + + old := time.Now().Add(-1 * time.Hour) + threshold := time.Now().Add(-30 * time.Minute) + + // Stale running agent with an old heartbeat -> should be marked offline. + stale := makeAgent(projectID, "stale") + stale.Phase = "running" + stale.Activity = "thinking" + stale.LastSeen = old + require.NoError(t, s.CreateAgent(ctx, stale)) + + // Recent agent -> untouched. + fresh := makeAgent(projectID, "fresh") + fresh.Phase = "running" + fresh.Activity = "thinking" + fresh.LastSeen = time.Now() + require.NoError(t, s.CreateAgent(ctx, fresh)) + + // Already-completed agent -> sticky, untouched. + done := makeAgent(projectID, "done") + done.Phase = "running" + done.Activity = "completed" + done.LastSeen = old + require.NoError(t, s.CreateAgent(ctx, done)) + + updated, err := s.MarkStaleAgentsOffline(ctx, threshold) + require.NoError(t, err) + require.Len(t, updated, 1) + assert.Equal(t, stale.ID, updated[0].ID) + assert.Equal(t, "offline", updated[0].Activity) + + gotFresh, _ := s.GetAgent(ctx, fresh.ID) + assert.Equal(t, "thinking", gotFresh.Activity) + gotDone, _ := s.GetAgent(ctx, done.ID) + assert.Equal(t, "completed", gotDone.Activity) +} + +func TestAgentStore_MarkStalledAgents(t *testing.T) { + ctx := context.Background() + s, projectID := newTestAgentStore(t) + + now := time.Now() + activityThreshold := now.Add(-15 * time.Minute) + heartbeatRecency := now.Add(-2 * time.Minute) + + // Recent heartbeat but stale activity -> stalled. + stalled := makeAgent(projectID, "stalled") + stalled.Phase = "running" + stalled.Activity = "executing" + stalled.LastActivityEvent = now.Add(-30 * time.Minute) + stalled.LastSeen = now + require.NoError(t, s.CreateAgent(ctx, stalled)) + + // Active recently -> untouched. + active := makeAgent(projectID, "active") + active.Phase = "running" + active.Activity = "executing" + active.LastActivityEvent = now + active.LastSeen = now + require.NoError(t, s.CreateAgent(ctx, active)) + + updated, err := s.MarkStalledAgents(ctx, activityThreshold, heartbeatRecency) + require.NoError(t, err) + require.Len(t, updated, 1) + assert.Equal(t, stalled.ID, updated[0].ID) + assert.Equal(t, "stalled", updated[0].Activity) + assert.Equal(t, "executing", updated[0].StalledFromActivity, "prior activity should be preserved") + + gotActive, _ := s.GetAgent(ctx, active.ID) + assert.Equal(t, "executing", gotActive.Activity) +} + +func TestAgentStore_PurgeDeletedAgents(t *testing.T) { + ctx := context.Background() + s, projectID := newTestAgentStore(t) + + // Old soft-deleted agent -> purged. + oldDeleted := makeAgent(projectID, "old-deleted") + require.NoError(t, s.CreateAgent(ctx, oldDeleted)) + oldDeleted.DeletedAt = time.Now().Add(-48 * time.Hour) + require.NoError(t, s.UpdateAgent(ctx, oldDeleted)) + + // Recently soft-deleted agent -> retained. + recentDeleted := makeAgent(projectID, "recent-deleted") + require.NoError(t, s.CreateAgent(ctx, recentDeleted)) + recentDeleted.DeletedAt = time.Now().Add(-1 * time.Hour) + require.NoError(t, s.UpdateAgent(ctx, recentDeleted)) + + // Live agent -> retained. + live := makeAgent(projectID, "live") + require.NoError(t, s.CreateAgent(ctx, live)) + + purged, err := s.PurgeDeletedAgents(ctx, time.Now().Add(-24*time.Hour)) + require.NoError(t, err) + assert.Equal(t, 1, purged) + + _, err = s.GetAgent(ctx, oldDeleted.ID) + assert.ErrorIs(t, err, store.ErrNotFound) + _, err = s.GetAgent(ctx, recentDeleted.ID) + assert.NoError(t, err) +} + +// ids extracts the agent IDs from a slice for order-independent comparison. +func ids(agents []store.Agent) []string { + out := make([]string, len(agents)) + for i := range agents { + out[i] = agents[i].ID + } + return out +} diff --git a/pkg/store/entadapter/allowlist_store.go b/pkg/store/entadapter/allowlist_store.go new file mode 100644 index 000000000..ea61cb281 --- /dev/null +++ b/pkg/store/entadapter/allowlist_store.go @@ -0,0 +1,588 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package entadapter + +import ( + "context" + "fmt" + "sort" + "strings" + "time" + + "entgo.io/ent/dialect/sql" + "github.com/GoogleCloudPlatform/scion/pkg/ent" + "github.com/GoogleCloudPlatform/scion/pkg/ent/allowlistentry" + "github.com/GoogleCloudPlatform/scion/pkg/ent/invitecode" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/GoogleCloudPlatform/scion/pkg/ent/user" + "github.com/GoogleCloudPlatform/scion/pkg/store" + "github.com/google/uuid" +) + +// AllowListStore implements store.AllowListStore and store.InviteCodeStore using +// Ent ORM. Both interfaces are co-located because the invite-only access control +// flow (allow list + invite codes) is a single logical domain. +type AllowListStore struct { + client *ent.Client +} + +// NewAllowListStore creates a new Ent-backed AllowListStore. +func NewAllowListStore(client *ent.Client) *AllowListStore { + return &AllowListStore{client: client} +} + +// entAllowListToStore converts an Ent AllowListEntry to a store model. +func entAllowListToStore(e *ent.AllowListEntry) *store.AllowListEntry { + return &store.AllowListEntry{ + ID: e.ID.String(), + Email: e.Email, + Note: e.Note, + AddedBy: e.AddedBy, + InviteID: e.InviteID, + Created: e.Created, + } +} + +// entInviteToStore converts an Ent InviteCode to a store model. +func entInviteToStore(i *ent.InviteCode) *store.InviteCode { + return &store.InviteCode{ + ID: i.ID.String(), + CodeHash: i.CodeHash, + CodePrefix: i.CodePrefix, + MaxUses: i.MaxUses, + UseCount: i.UseCount, + ExpiresAt: i.ExpiresAt, + Revoked: i.Revoked, + CreatedBy: i.CreatedBy, + Note: i.Note, + Created: i.Created, + } +} + +// ============================================================================ +// Allow List Operations +// ============================================================================ + +// AddAllowListEntry adds a single email to the allow list. +func (s *AllowListStore) AddAllowListEntry(ctx context.Context, entry *store.AllowListEntry) error { + uid, err := parseUUID(entry.ID) + if err != nil { + return err + } + if entry.Created.IsZero() { + entry.Created = time.Now() + } + entry.Email = normalizeEmail(entry.Email) + + create := s.client.AllowListEntry.Create(). + SetID(uid). + SetEmail(entry.Email). + SetNote(entry.Note). + SetAddedBy(entry.AddedBy). + SetCreated(entry.Created) + if entry.InviteID != "" { + create.SetInviteID(entry.InviteID) + } + + if err := create.Exec(ctx); err != nil { + return mapError(err) + } + return nil +} + +// RemoveAllowListEntry removes an email from the allow list. +func (s *AllowListStore) RemoveAllowListEntry(ctx context.Context, email string) error { + n, err := s.client.AllowListEntry.Delete(). + Where(allowlistentry.EmailEQ(normalizeEmail(email))). + Exec(ctx) + if err != nil { + return err + } + if n == 0 { + return store.ErrNotFound + } + return nil +} + +// GetAllowListEntry retrieves an allow list entry by email. +func (s *AllowListStore) GetAllowListEntry(ctx context.Context, email string) (*store.AllowListEntry, error) { + e, err := s.client.AllowListEntry.Query(). + Where(allowlistentry.EmailEQ(normalizeEmail(email))). + Only(ctx) + if err != nil { + return nil, mapError(err) + } + return entAllowListToStore(e), nil +} + +// ListAllowListEntries returns a keyset-paginated page of allow list entries +// ordered by (created DESC, id DESC), matching the legacy SQLite store. +func (s *AllowListStore) ListAllowListEntries(ctx context.Context, opts store.ListOptions) (*store.ListResult[store.AllowListEntry], error) { + totalCount, err := s.client.AllowListEntry.Query().Count(ctx) + if err != nil { + return nil, err + } + + limit := opts.Limit + if limit <= 0 { + limit = 50 + } + + query := s.client.AllowListEntry.Query() + if opts.Cursor != "" { + pred, err := s.allowListCursorPredicate(ctx, opts.Cursor) + if err != nil { + return nil, err + } + query.Where(pred) + } + + entries, err := query. + Order(allowlistentry.ByCreated(sql.OrderDesc()), allowlistentry.ByID(sql.OrderDesc())). + Limit(limit + 1). + All(ctx) + if err != nil { + return nil, err + } + + items := make([]store.AllowListEntry, 0, len(entries)) + for _, e := range entries { + items = append(items, *entAllowListToStore(e)) + } + + result := &store.ListResult[store.AllowListEntry]{ + Items: items, + TotalCount: totalCount, + } + if len(items) > limit { + result.NextCursor = items[limit-1].ID + result.Items = items[:limit] + } + return result, nil +} + +// allowListCursorPredicate builds the keyset predicate for paginating after the +// entry identified by cursor (an entry ID). +func (s *AllowListStore) allowListCursorPredicate(ctx context.Context, cursor string) (predicate.AllowListEntry, error) { + cursorUID, err := parseUUID(cursor) + if err != nil { + return nil, fmt.Errorf("invalid cursor: %w", err) + } + c, err := s.client.AllowListEntry.Get(ctx, cursorUID) + if err != nil { + return nil, fmt.Errorf("invalid cursor: %w", mapError(err)) + } + return allowlistentry.Or( + allowlistentry.CreatedLT(c.Created), + allowlistentry.And(allowlistentry.CreatedEQ(c.Created), allowlistentry.IDLT(cursorUID)), + ), nil +} + +// IsEmailAllowListed reports whether an email is present on the allow list. +func (s *AllowListStore) IsEmailAllowListed(ctx context.Context, email string) (bool, error) { + return s.client.AllowListEntry.Query(). + Where(allowlistentry.EmailEQ(normalizeEmail(email))). + Exist(ctx) +} + +// BulkAddAllowListEntries inserts many entries idempotently, skipping any whose +// email already exists. It mirrors the legacy `INSERT OR IGNORE` behavior: +// duplicate emails (already present or repeated within the batch) are counted as +// skipped rather than erroring. OnConflict().Ignore() additionally makes the +// bulk insert safe against rows inserted concurrently between the existence +// check and the write. +func (s *AllowListStore) BulkAddAllowListEntries(ctx context.Context, entries []*store.AllowListEntry) (int, int, error) { + now := time.Now() + for _, e := range entries { + e.Email = normalizeEmail(e.Email) + if e.Created.IsZero() { + e.Created = now + } + } + + // Determine which emails already exist so we can report accurate counts. + emails := make([]string, 0, len(entries)) + for _, e := range entries { + emails = append(emails, e.Email) + } + existingRows, err := s.client.AllowListEntry.Query(). + Where(allowlistentry.EmailIn(emails...)). + Select(allowlistentry.FieldEmail). + Strings(ctx) + if err != nil { + return 0, 0, err + } + seen := make(map[string]bool, len(existingRows)) + for _, e := range existingRows { + seen[e] = true + } + + bulk := make([]*ent.AllowListEntryCreate, 0, len(entries)) + added, skipped := 0, 0 + for _, e := range entries { + if seen[e.Email] { + skipped++ + continue + } + seen[e.Email] = true // dedupe repeats within the same batch + + uid, err := parseUUID(e.ID) + if err != nil { + return added, skipped, err + } + create := s.client.AllowListEntry.Create(). + SetID(uid). + SetEmail(e.Email). + SetNote(e.Note). + SetAddedBy(e.AddedBy). + SetCreated(e.Created) + if e.InviteID != "" { + create.SetInviteID(e.InviteID) + } + bulk = append(bulk, create) + added++ + } + + if len(bulk) > 0 { + if err := s.client.AllowListEntry.CreateBulk(bulk...). + OnConflictColumns(allowlistentry.FieldEmail). + Ignore(). + Exec(ctx); err != nil { + return 0, 0, err + } + } + return added, skipped, nil +} + +// ListEmailDomains returns the distinct, sorted set of email domains across all +// users. +func (s *AllowListStore) ListEmailDomains(ctx context.Context) ([]string, error) { + emails, err := s.client.User.Query(). + Select(user.FieldEmail). + Strings(ctx) + if err != nil { + return nil, err + } + + domainSet := make(map[string]struct{}) + for _, e := range emails { + at := strings.LastIndex(e, "@") + if at < 0 || at == len(e)-1 { + continue + } + domainSet[e[at+1:]] = struct{}{} + } + + domains := make([]string, 0, len(domainSet)) + for d := range domainSet { + domains = append(domains, d) + } + sort.Strings(domains) + return domains, nil +} + +// UpdateAllowListEntryInviteID associates an invite code with an allow list entry. +func (s *AllowListStore) UpdateAllowListEntryInviteID(ctx context.Context, email string, inviteID string) error { + n, err := s.client.AllowListEntry.Update(). + Where(allowlistentry.EmailEQ(normalizeEmail(email))). + SetInviteID(inviteID). + Save(ctx) + if err != nil { + return mapError(err) + } + if n == 0 { + return store.ErrNotFound + } + return nil +} + +// ListAllowListEntriesWithInvites returns allow list entries enriched with their +// associated invite code details. invite_id is a plain column (not an Ent edge), +// so the join is performed by batch-loading the referenced invite codes. +func (s *AllowListStore) ListAllowListEntriesWithInvites(ctx context.Context, opts store.ListOptions) (*store.ListResult[store.AllowListEntryWithInvite], error) { + base, err := s.ListAllowListEntries(ctx, opts) + if err != nil { + return nil, err + } + + // Collect referenced invite IDs. + inviteUIDs := make([]uuid.UUID, 0) + for i := range base.Items { + if base.Items[i].InviteID == "" { + continue + } + if uid, err := parseUUID(base.Items[i].InviteID); err == nil { + inviteUIDs = append(inviteUIDs, uid) + } + } + + invitesByID := make(map[string]*ent.InviteCode) + if len(inviteUIDs) > 0 { + invites, err := s.client.InviteCode.Query(). + Where(invitecode.IDIn(inviteUIDs...)). + All(ctx) + if err != nil { + return nil, err + } + for _, inv := range invites { + invitesByID[inv.ID.String()] = inv + } + } + + items := make([]store.AllowListEntryWithInvite, 0, len(base.Items)) + for i := range base.Items { + entry := store.AllowListEntryWithInvite{AllowListEntry: base.Items[i]} + if inv, ok := invitesByID[base.Items[i].InviteID]; ok { + entry.InviteCodePrefix = inv.CodePrefix + entry.InviteMaxUses = inv.MaxUses + entry.InviteUseCount = inv.UseCount + entry.InviteExpiresAt = inv.ExpiresAt + entry.InviteRevoked = inv.Revoked + } + items = append(items, entry) + } + + return &store.ListResult[store.AllowListEntryWithInvite]{ + Items: items, + TotalCount: base.TotalCount, + NextCursor: base.NextCursor, + }, nil +} + +// ============================================================================ +// Invite Code Operations +// ============================================================================ + +// CreateInviteCode creates a new invite code. +func (s *AllowListStore) CreateInviteCode(ctx context.Context, invite *store.InviteCode) error { + uid, err := parseUUID(invite.ID) + if err != nil { + return err + } + if invite.Created.IsZero() { + invite.Created = time.Now() + } + + create := s.client.InviteCode.Create(). + SetID(uid). + SetCodeHash(invite.CodeHash). + SetCodePrefix(invite.CodePrefix). + SetMaxUses(invite.MaxUses). + SetUseCount(invite.UseCount). + SetExpiresAt(invite.ExpiresAt). + SetRevoked(invite.Revoked). + SetCreatedBy(invite.CreatedBy). + SetNote(invite.Note). + SetCreated(invite.Created) + + if err := create.Exec(ctx); err != nil { + return mapError(err) + } + return nil +} + +// GetInviteCodeByHash retrieves an invite code by its hash. +func (s *AllowListStore) GetInviteCodeByHash(ctx context.Context, codeHash string) (*store.InviteCode, error) { + i, err := s.client.InviteCode.Query(). + Where(invitecode.CodeHashEQ(codeHash)). + Only(ctx) + if err != nil { + return nil, mapError(err) + } + return entInviteToStore(i), nil +} + +// GetInviteCode retrieves an invite code by ID. +func (s *AllowListStore) GetInviteCode(ctx context.Context, id string) (*store.InviteCode, error) { + uid, err := parseGetID(id) + if err != nil { + return nil, err + } + i, err := s.client.InviteCode.Get(ctx, uid) + if err != nil { + return nil, mapError(err) + } + return entInviteToStore(i), nil +} + +// ListInviteCodes returns a keyset-paginated page of invite codes ordered by +// (created DESC, id DESC). CodeHash is sensitive and not exposed in listings. +func (s *AllowListStore) ListInviteCodes(ctx context.Context, opts store.ListOptions) (*store.ListResult[store.InviteCode], error) { + totalCount, err := s.client.InviteCode.Query().Count(ctx) + if err != nil { + return nil, err + } + + limit := opts.Limit + if limit <= 0 { + limit = 50 + } + + query := s.client.InviteCode.Query() + if opts.Cursor != "" { + cursorUID, err := parseUUID(opts.Cursor) + if err != nil { + return nil, fmt.Errorf("invalid cursor: %w", err) + } + c, err := s.client.InviteCode.Get(ctx, cursorUID) + if err != nil { + return nil, fmt.Errorf("invalid cursor: %w", mapError(err)) + } + query.Where(invitecode.Or( + invitecode.CreatedLT(c.Created), + invitecode.And(invitecode.CreatedEQ(c.Created), invitecode.IDLT(cursorUID)), + )) + } + + invites, err := query. + Order(invitecode.ByCreated(sql.OrderDesc()), invitecode.ByID(sql.OrderDesc())). + Limit(limit + 1). + All(ctx) + if err != nil { + return nil, err + } + + items := make([]store.InviteCode, 0, len(invites)) + for _, i := range invites { + si := entInviteToStore(i) + si.CodeHash = "" // not exposed in listings, matching the legacy store + items = append(items, *si) + } + + result := &store.ListResult[store.InviteCode]{ + Items: items, + TotalCount: totalCount, + } + if len(items) > limit { + result.NextCursor = items[limit-1].ID + result.Items = items[:limit] + } + return result, nil +} + +// IncrementInviteUseCount atomically increments use_count for an invite code +// that is still redeemable (not revoked, not expired, and below max_uses). +// +// This is expressed as a single conditional UPDATE rather than a +// SELECT-then-UPDATE read-modify-write: the predicate and the increment execute +// in one statement, so the operation is race-free on both SQLite and Postgres +// without needing SELECT ... FOR UPDATE row locking. (The sql/lock feature is +// enabled and ForUpdate is available for genuine multi-statement RMW paths.) +func (s *AllowListStore) IncrementInviteUseCount(ctx context.Context, id string) error { + uid, err := parseUUID(id) + if err != nil { + return err + } + + n, err := s.client.InviteCode.Update(). + Where( + invitecode.IDEQ(uid), + invitecode.RevokedEQ(false), + invitecode.ExpiresAtGT(time.Now()), + // (max_uses = 0 OR use_count < max_uses) — a column-to-column + // comparison expressed via a raw selector predicate. + predicate.InviteCode(func(sel *sql.Selector) { + sel.Where(sql.Or( + sql.EQ(sel.C(invitecode.FieldMaxUses), 0), + sql.ColumnsLT(sel.C(invitecode.FieldUseCount), sel.C(invitecode.FieldMaxUses)), + )) + }), + ). + AddUseCount(1). + Save(ctx) + if err != nil { + return err + } + if n == 0 { + return store.ErrNotFound + } + return nil +} + +// RevokeInviteCode marks an invite code as revoked. +func (s *AllowListStore) RevokeInviteCode(ctx context.Context, id string) error { + uid, err := parseUUID(id) + if err != nil { + return err + } + n, err := s.client.InviteCode.Update(). + Where(invitecode.IDEQ(uid)). + SetRevoked(true). + Save(ctx) + if err != nil { + return err + } + if n == 0 { + return store.ErrNotFound + } + return nil +} + +// DeleteInviteCode removes an invite code by ID. +func (s *AllowListStore) DeleteInviteCode(ctx context.Context, id string) error { + uid, err := parseUUID(id) + if err != nil { + return err + } + if err := s.client.InviteCode.DeleteOneID(uid).Exec(ctx); err != nil { + return mapError(err) + } + return nil +} + +// GetInviteStats returns aggregate statistics about invite codes and the allow +// list. +func (s *AllowListStore) GetInviteStats(ctx context.Context) (*store.InviteStats, error) { + stats := &store.InviteStats{ + RecentRedemptions: []store.InviteCodeInfo{}, + } + + // The invite_codes table is small (admin-managed); load it once ordered by + // recency and derive pending count, total redemptions, and recent + // redemptions in a single pass. + codes, err := s.client.InviteCode.Query(). + Order(invitecode.ByCreated(sql.OrderDesc())). + All(ctx) + if err != nil { + return nil, err + } + + now := time.Now() + for _, c := range codes { + stats.TotalRedemptions += c.UseCount + if !c.Revoked && c.ExpiresAt.After(now) && (c.MaxUses == 0 || c.UseCount < c.MaxUses) { + stats.PendingInvites++ + } + if c.UseCount > 0 && len(stats.RecentRedemptions) < 10 { + stats.RecentRedemptions = append(stats.RecentRedemptions, store.InviteCodeInfo{ + ID: c.ID.String(), + CodePrefix: c.CodePrefix, + UseCount: c.UseCount, + MaxUses: c.MaxUses, + ExpiresAt: c.ExpiresAt, + Note: c.Note, + Created: c.Created, + }) + } + } + + allowCount, err := s.client.AllowListEntry.Query().Count(ctx) + if err != nil { + return nil, err + } + stats.AllowListCount = allowCount + + return stats, nil +} diff --git a/pkg/store/entadapter/brokersecret_store.go b/pkg/store/entadapter/brokersecret_store.go new file mode 100644 index 000000000..d93f815be --- /dev/null +++ b/pkg/store/entadapter/brokersecret_store.go @@ -0,0 +1,266 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package entadapter + +import ( + "context" + "time" + + "github.com/GoogleCloudPlatform/scion/pkg/ent" + "github.com/GoogleCloudPlatform/scion/pkg/ent/brokerjointoken" + "github.com/GoogleCloudPlatform/scion/pkg/ent/brokersecret" + "github.com/GoogleCloudPlatform/scion/pkg/store" +) + +// BrokerSecretStore implements store.BrokerSecretStore using Ent ORM. It backs +// runtime broker HMAC authentication: per-broker shared secrets (broker_secrets) +// and short-lived registration join tokens (broker_join_tokens). +// +// Both tables key their primary id on broker_id (one active secret / join token +// per broker), so the surrogate Ent id is stored directly in the broker_id +// column. +type BrokerSecretStore struct { + client *ent.Client +} + +// NewBrokerSecretStore creates a new Ent-backed BrokerSecretStore. +func NewBrokerSecretStore(client *ent.Client) *BrokerSecretStore { + return &BrokerSecretStore{client: client} +} + +// ============================================================================= +// Broker Secret operations +// ============================================================================= + +// entBrokerSecretToStore converts an Ent BrokerSecret to a store model. +func entBrokerSecretToStore(b *ent.BrokerSecret) *store.BrokerSecret { + secret := &store.BrokerSecret{ + BrokerID: b.ID.String(), + SecretKey: b.SecretKey, + Algorithm: b.Algorithm, + Status: b.Status, + CreatedAt: b.Created, + } + if b.RotatedAt != nil { + secret.RotatedAt = *b.RotatedAt + } + if b.ExpiresAt != nil { + secret.ExpiresAt = *b.ExpiresAt + } + return secret +} + +// CreateBrokerSecret creates a new broker secret record. +func (s *BrokerSecretStore) CreateBrokerSecret(ctx context.Context, secret *store.BrokerSecret) error { + if secret.BrokerID == "" { + return store.ErrInvalidInput + } + uid, err := parseUUID(secret.BrokerID) + if err != nil { + return err + } + + if secret.CreatedAt.IsZero() { + secret.CreatedAt = time.Now() + } + if secret.Algorithm == "" { + secret.Algorithm = store.BrokerSecretAlgorithmHMACSHA256 + } + if secret.Status == "" { + secret.Status = store.BrokerSecretStatusActive + } + + create := s.client.BrokerSecret.Create(). + SetID(uid). + SetSecretKey(secret.SecretKey). + SetAlgorithm(secret.Algorithm). + SetStatus(secret.Status). + SetCreated(secret.CreatedAt) + if !secret.RotatedAt.IsZero() { + create.SetRotatedAt(secret.RotatedAt) + } + if !secret.ExpiresAt.IsZero() { + create.SetExpiresAt(secret.ExpiresAt) + } + + if _, err := create.Save(ctx); err != nil { + return mapError(err) + } + return nil +} + +// GetBrokerSecret retrieves a broker secret by broker ID. +func (s *BrokerSecretStore) GetBrokerSecret(ctx context.Context, brokerID string) (*store.BrokerSecret, error) { + uid, err := parseUUID(brokerID) + if err != nil { + return nil, err + } + b, err := s.client.BrokerSecret.Get(ctx, uid) + if err != nil { + return nil, mapError(err) + } + return entBrokerSecretToStore(b), nil +} + +// GetActiveSecrets retrieves all active and deprecated secrets for a broker, +// newest first. This supports dual-secret validation during rotation grace +// periods. Because broker_id is the primary key, there is at most one row. +func (s *BrokerSecretStore) GetActiveSecrets(ctx context.Context, brokerID string) ([]*store.BrokerSecret, error) { + uid, err := parseUUID(brokerID) + if err != nil { + return nil, err + } + rows, err := s.client.BrokerSecret.Query(). + Where( + brokersecret.IDEQ(uid), + brokersecret.StatusIn(store.BrokerSecretStatusActive, store.BrokerSecretStatusDeprecated), + ). + Order(ent.Desc(brokersecret.FieldCreated)). + All(ctx) + if err != nil { + return nil, err + } + secrets := make([]*store.BrokerSecret, 0, len(rows)) + for _, b := range rows { + secrets = append(secrets, entBrokerSecretToStore(b)) + } + return secrets, nil +} + +// UpdateBrokerSecret updates an existing broker secret. +func (s *BrokerSecretStore) UpdateBrokerSecret(ctx context.Context, secret *store.BrokerSecret) error { + uid, err := parseUUID(secret.BrokerID) + if err != nil { + return err + } + + update := s.client.BrokerSecret.UpdateOneID(uid). + SetSecretKey(secret.SecretKey). + SetAlgorithm(secret.Algorithm). + SetStatus(secret.Status) + if secret.RotatedAt.IsZero() { + update.ClearRotatedAt() + } else { + update.SetRotatedAt(secret.RotatedAt) + } + if secret.ExpiresAt.IsZero() { + update.ClearExpiresAt() + } else { + update.SetExpiresAt(secret.ExpiresAt) + } + + if _, err := update.Save(ctx); err != nil { + return mapError(err) + } + return nil +} + +// DeleteBrokerSecret removes a broker secret. +func (s *BrokerSecretStore) DeleteBrokerSecret(ctx context.Context, brokerID string) error { + uid, err := parseUUID(brokerID) + if err != nil { + return err + } + if err := s.client.BrokerSecret.DeleteOneID(uid).Exec(ctx); err != nil { + return mapError(err) + } + return nil +} + +// ============================================================================= +// Broker Join Token operations +// ============================================================================= + +// entJoinTokenToStore converts an Ent BrokerJoinToken to a store model. +func entJoinTokenToStore(t *ent.BrokerJoinToken) *store.BrokerJoinToken { + return &store.BrokerJoinToken{ + BrokerID: t.ID.String(), + TokenHash: t.TokenHash, + ExpiresAt: t.ExpiresAt, + CreatedAt: t.Created, + CreatedBy: t.CreatedBy, + } +} + +// CreateJoinToken creates a new join token for broker registration. +func (s *BrokerSecretStore) CreateJoinToken(ctx context.Context, token *store.BrokerJoinToken) error { + if token.BrokerID == "" || token.TokenHash == "" { + return store.ErrInvalidInput + } + uid, err := parseUUID(token.BrokerID) + if err != nil { + return err + } + + if token.CreatedAt.IsZero() { + token.CreatedAt = time.Now() + } + + create := s.client.BrokerJoinToken.Create(). + SetID(uid). + SetTokenHash(token.TokenHash). + SetExpiresAt(token.ExpiresAt). + SetCreatedBy(token.CreatedBy). + SetCreated(token.CreatedAt) + + if _, err := create.Save(ctx); err != nil { + return mapError(err) + } + return nil +} + +// GetJoinToken retrieves a join token by token hash. +func (s *BrokerSecretStore) GetJoinToken(ctx context.Context, tokenHash string) (*store.BrokerJoinToken, error) { + t, err := s.client.BrokerJoinToken.Query(). + Where(brokerjointoken.TokenHashEQ(tokenHash)). + Only(ctx) + if err != nil { + return nil, mapError(err) + } + return entJoinTokenToStore(t), nil +} + +// GetJoinTokenByBrokerID retrieves a join token by broker ID. +func (s *BrokerSecretStore) GetJoinTokenByBrokerID(ctx context.Context, brokerID string) (*store.BrokerJoinToken, error) { + uid, err := parseUUID(brokerID) + if err != nil { + return nil, err + } + t, err := s.client.BrokerJoinToken.Get(ctx, uid) + if err != nil { + return nil, mapError(err) + } + return entJoinTokenToStore(t), nil +} + +// DeleteJoinToken removes a join token by broker ID. +func (s *BrokerSecretStore) DeleteJoinToken(ctx context.Context, brokerID string) error { + uid, err := parseUUID(brokerID) + if err != nil { + return err + } + if err := s.client.BrokerJoinToken.DeleteOneID(uid).Exec(ctx); err != nil { + return mapError(err) + } + return nil +} + +// CleanExpiredJoinTokens removes all expired join tokens. +func (s *BrokerSecretStore) CleanExpiredJoinTokens(ctx context.Context) error { + _, err := s.client.BrokerJoinToken.Delete(). + Where(brokerjointoken.ExpiresAtLT(time.Now())). + Exec(ctx) + return err +} diff --git a/pkg/store/entadapter/brokersecret_store_test.go b/pkg/store/entadapter/brokersecret_store_test.go new file mode 100644 index 000000000..a2240d951 --- /dev/null +++ b/pkg/store/entadapter/brokersecret_store_test.go @@ -0,0 +1,223 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build !no_sqlite + +package entadapter + +import ( + "context" + "testing" + "time" + + "github.com/GoogleCloudPlatform/scion/pkg/store" + "github.com/GoogleCloudPlatform/scion/pkg/store/enttest" + "github.com/google/uuid" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func newTestBrokerSecretStore(t *testing.T) *BrokerSecretStore { + t.Helper() + client := enttest.NewClient(t) + return NewBrokerSecretStore(client) +} + +func TestBrokerSecret_CreateGet(t *testing.T) { + bs := newTestBrokerSecretStore(t) + ctx := context.Background() + + secret := &store.BrokerSecret{ + BrokerID: uuid.NewString(), + SecretKey: []byte("super-secret-hmac-key"), + } + require.NoError(t, bs.CreateBrokerSecret(ctx, secret)) + // Defaults applied. + assert.Equal(t, store.BrokerSecretAlgorithmHMACSHA256, secret.Algorithm) + assert.Equal(t, store.BrokerSecretStatusActive, secret.Status) + assert.False(t, secret.CreatedAt.IsZero()) + + got, err := bs.GetBrokerSecret(ctx, secret.BrokerID) + require.NoError(t, err) + assert.Equal(t, secret.BrokerID, got.BrokerID) + assert.Equal(t, []byte("super-secret-hmac-key"), got.SecretKey) + assert.Equal(t, store.BrokerSecretAlgorithmHMACSHA256, got.Algorithm) + assert.Equal(t, store.BrokerSecretStatusActive, got.Status) +} + +func TestBrokerSecret_CreateMissingID(t *testing.T) { + bs := newTestBrokerSecretStore(t) + err := bs.CreateBrokerSecret(context.Background(), &store.BrokerSecret{SecretKey: []byte("k")}) + assert.ErrorIs(t, err, store.ErrInvalidInput) +} + +func TestBrokerSecret_CreateDuplicate(t *testing.T) { + bs := newTestBrokerSecretStore(t) + ctx := context.Background() + + id := uuid.NewString() + require.NoError(t, bs.CreateBrokerSecret(ctx, &store.BrokerSecret{BrokerID: id, SecretKey: []byte("k1")})) + err := bs.CreateBrokerSecret(ctx, &store.BrokerSecret{BrokerID: id, SecretKey: []byte("k2")}) + assert.ErrorIs(t, err, store.ErrAlreadyExists) +} + +func TestBrokerSecret_GetNotFound(t *testing.T) { + bs := newTestBrokerSecretStore(t) + _, err := bs.GetBrokerSecret(context.Background(), uuid.NewString()) + assert.ErrorIs(t, err, store.ErrNotFound) +} + +func TestBrokerSecret_GetActiveSecrets(t *testing.T) { + bs := newTestBrokerSecretStore(t) + ctx := context.Background() + + id := uuid.NewString() + require.NoError(t, bs.CreateBrokerSecret(ctx, &store.BrokerSecret{ + BrokerID: id, SecretKey: []byte("k"), Status: store.BrokerSecretStatusActive, + })) + + active, err := bs.GetActiveSecrets(ctx, id) + require.NoError(t, err) + require.Len(t, active, 1) + assert.Equal(t, store.BrokerSecretStatusActive, active[0].Status) + + // A revoked secret is excluded from the active set. + require.NoError(t, bs.UpdateBrokerSecret(ctx, &store.BrokerSecret{ + BrokerID: id, SecretKey: []byte("k"), Algorithm: store.BrokerSecretAlgorithmHMACSHA256, Status: store.BrokerSecretStatusRevoked, + })) + active, err = bs.GetActiveSecrets(ctx, id) + require.NoError(t, err) + assert.Empty(t, active) +} + +func TestBrokerSecret_Update(t *testing.T) { + bs := newTestBrokerSecretStore(t) + ctx := context.Background() + + id := uuid.NewString() + require.NoError(t, bs.CreateBrokerSecret(ctx, &store.BrokerSecret{BrokerID: id, SecretKey: []byte("k1")})) + + rotated := time.Now().UTC().Truncate(time.Second) + require.NoError(t, bs.UpdateBrokerSecret(ctx, &store.BrokerSecret{ + BrokerID: id, + SecretKey: []byte("k2"), + Algorithm: store.BrokerSecretAlgorithmHMACSHA256, + Status: store.BrokerSecretStatusDeprecated, + RotatedAt: rotated, + })) + + got, err := bs.GetBrokerSecret(ctx, id) + require.NoError(t, err) + assert.Equal(t, []byte("k2"), got.SecretKey) + assert.Equal(t, store.BrokerSecretStatusDeprecated, got.Status) + assert.False(t, got.RotatedAt.IsZero()) +} + +func TestBrokerSecret_Delete(t *testing.T) { + bs := newTestBrokerSecretStore(t) + ctx := context.Background() + + id := uuid.NewString() + require.NoError(t, bs.CreateBrokerSecret(ctx, &store.BrokerSecret{BrokerID: id, SecretKey: []byte("k")})) + require.NoError(t, bs.DeleteBrokerSecret(ctx, id)) + _, err := bs.GetBrokerSecret(ctx, id) + assert.ErrorIs(t, err, store.ErrNotFound) + assert.ErrorIs(t, bs.DeleteBrokerSecret(ctx, id), store.ErrNotFound) +} + +// ============================================================================= +// Broker Join Tokens +// ============================================================================= + +func TestJoinToken_CreateGet(t *testing.T) { + bs := newTestBrokerSecretStore(t) + ctx := context.Background() + + token := &store.BrokerJoinToken{ + BrokerID: uuid.NewString(), + TokenHash: "hash-" + uuid.NewString(), + ExpiresAt: time.Now().Add(time.Hour), + CreatedBy: uuid.NewString(), + } + require.NoError(t, bs.CreateJoinToken(ctx, token)) + assert.False(t, token.CreatedAt.IsZero()) + + byHash, err := bs.GetJoinToken(ctx, token.TokenHash) + require.NoError(t, err) + assert.Equal(t, token.BrokerID, byHash.BrokerID) + + byBroker, err := bs.GetJoinTokenByBrokerID(ctx, token.BrokerID) + require.NoError(t, err) + assert.Equal(t, token.TokenHash, byBroker.TokenHash) +} + +func TestJoinToken_CreateInvalid(t *testing.T) { + bs := newTestBrokerSecretStore(t) + ctx := context.Background() + assert.ErrorIs(t, bs.CreateJoinToken(ctx, &store.BrokerJoinToken{TokenHash: "h"}), store.ErrInvalidInput) + assert.ErrorIs(t, bs.CreateJoinToken(ctx, &store.BrokerJoinToken{BrokerID: uuid.NewString()}), store.ErrInvalidInput) +} + +func TestJoinToken_GetNotFound(t *testing.T) { + bs := newTestBrokerSecretStore(t) + ctx := context.Background() + _, err := bs.GetJoinToken(ctx, "missing") + assert.ErrorIs(t, err, store.ErrNotFound) + _, err = bs.GetJoinTokenByBrokerID(ctx, uuid.NewString()) + assert.ErrorIs(t, err, store.ErrNotFound) +} + +func TestJoinToken_Delete(t *testing.T) { + bs := newTestBrokerSecretStore(t) + ctx := context.Background() + + token := &store.BrokerJoinToken{ + BrokerID: uuid.NewString(), + TokenHash: "hash-" + uuid.NewString(), + ExpiresAt: time.Now().Add(time.Hour), + CreatedBy: uuid.NewString(), + } + require.NoError(t, bs.CreateJoinToken(ctx, token)) + require.NoError(t, bs.DeleteJoinToken(ctx, token.BrokerID)) + _, err := bs.GetJoinTokenByBrokerID(ctx, token.BrokerID) + assert.ErrorIs(t, err, store.ErrNotFound) + assert.ErrorIs(t, bs.DeleteJoinToken(ctx, token.BrokerID), store.ErrNotFound) +} + +func TestJoinToken_CleanExpired(t *testing.T) { + bs := newTestBrokerSecretStore(t) + ctx := context.Background() + + expired := &store.BrokerJoinToken{ + BrokerID: uuid.NewString(), + TokenHash: "expired-" + uuid.NewString(), + ExpiresAt: time.Now().Add(-time.Hour), + CreatedBy: uuid.NewString(), + } + valid := &store.BrokerJoinToken{ + BrokerID: uuid.NewString(), + TokenHash: "valid-" + uuid.NewString(), + ExpiresAt: time.Now().Add(time.Hour), + CreatedBy: uuid.NewString(), + } + require.NoError(t, bs.CreateJoinToken(ctx, expired)) + require.NoError(t, bs.CreateJoinToken(ctx, valid)) + + require.NoError(t, bs.CleanExpiredJoinTokens(ctx)) + + _, err := bs.GetJoinTokenByBrokerID(ctx, expired.BrokerID) + assert.ErrorIs(t, err, store.ErrNotFound, "expired token should be cleaned") + _, err = bs.GetJoinTokenByBrokerID(ctx, valid.BrokerID) + assert.NoError(t, err, "valid token should remain") +} diff --git a/pkg/store/entadapter/composite.go b/pkg/store/entadapter/composite.go index 36a087e71..195e6825d 100644 --- a/pkg/store/entadapter/composite.go +++ b/pkg/store/entadapter/composite.go @@ -16,307 +16,162 @@ package entadapter import ( "context" + "database/sql" "fmt" + entsql "entgo.io/ent/dialect/sql" + "github.com/GoogleCloudPlatform/scion/pkg/ent" - entuser "github.com/GoogleCloudPlatform/scion/pkg/ent/user" + "github.com/GoogleCloudPlatform/scion/pkg/ent/agent" + "github.com/GoogleCloudPlatform/scion/pkg/ent/entc" + "github.com/GoogleCloudPlatform/scion/pkg/ent/notification" + "github.com/GoogleCloudPlatform/scion/pkg/ent/notificationsubscription" "github.com/GoogleCloudPlatform/scion/pkg/store" ) -// CompositeStore wraps an existing store.Store and overrides group and policy -// operations with Ent-backed implementations. +// CompositeStore is a fully Ent-backed implementation of store.Store. Every +// domain is served by a dedicated Ent sub-store; CompositeStore embeds them so +// their methods are promoted to satisfy the store.Store interface, while the +// store-level Close/Ping/Migrate operations act on the shared Ent client. +// +// There is no longer a separate raw-SQL store: all Hub state lives in a single +// Ent database. type CompositeStore struct { - store.Store - groups *GroupStore - policies *PolicyStore - client *ent.Client -} - -// NewCompositeStore creates a CompositeStore that delegates group and policy -// operations to Ent-backed stores while forwarding all other operations to the -// underlying store. -func NewCompositeStore(base store.Store, client *ent.Client) *CompositeStore { + *AgentStore + *ProjectStore + *UserStore + *SecretStore + *TemplateStore + *NotificationStore + *ScheduleStore + *MaintenanceStore + *MessageStore + *ExternalStore + *BrokerSecretStore + *AllowListStore + *GroupStore + *PolicyStore + + client *ent.Client +} + +// Compile-time assertion that CompositeStore satisfies the full store.Store +// interface purely through its embedded Ent-backed sub-stores. +var _ store.Store = (*CompositeStore)(nil) + +// NewCompositeStore creates a store.Store backed entirely by the given Ent +// client. Each domain sub-store shares the same client and therefore the same +// underlying database, so cross-domain foreign keys (e.g. group -> project, +// agent -> project) resolve natively without any shadow synchronization. +func NewCompositeStore(client *ent.Client) *CompositeStore { return &CompositeStore{ - Store: base, - groups: NewGroupStore(client), - policies: NewPolicyStore(client), - client: client, - } -} - -// Close closes both the Ent client and the underlying store. -func (c *CompositeStore) Close() error { - if err := c.client.Close(); err != nil { - _ = c.Store.Close() + AgentStore: NewAgentStore(client), + ProjectStore: NewProjectStore(client), + UserStore: NewUserStore(client), + SecretStore: NewSecretStore(client), + TemplateStore: NewTemplateStore(client), + NotificationStore: NewNotificationStore(client), + ScheduleStore: NewScheduleStore(client), + MaintenanceStore: NewMaintenanceStore(client), + MessageStore: NewMessageStore(client), + ExternalStore: NewExternalStore(client), + BrokerSecretStore: NewBrokerSecretStore(client), + AllowListStore: NewAllowListStore(client), + GroupStore: NewGroupStore(client), + PolicyStore: NewPolicyStore(client), + client: client, + } +} + +// DeleteAgent hard-deletes an agent and cascade-deletes its notification +// subscriptions and notifications. The former raw-SQL store enforced this via +// ON DELETE CASCADE foreign keys (notification_subscriptions.agent_id -> +// agents(id), notifications.subscription_id -> notification_subscriptions(id)). +// In the Ent schema agent_id is a plain field with no edge, so the cascade is +// performed explicitly here to preserve store parity. Soft delete goes through +// UpdateAgent and is unaffected, so subscriptions are retained for soft-deleted +// agents. +func (c *CompositeStore) DeleteAgent(ctx context.Context, id string) error { + if err := c.AgentStore.DeleteAgent(ctx, id); err != nil { return err } - return c.Store.Close() -} - -// GroupStore method overrides — delegate to Ent-backed GroupStore. - -func (c *CompositeStore) CreateGroup(ctx context.Context, group *store.Group) error { - // Ensure the project exists in the Ent database before creating the group, - // since projects are stored in the base (SQLite) store but groups are in Ent - // which has a foreign key constraint on project_id. - if group.ProjectID != "" { - if err := c.ensureEntProject(ctx, group.ProjectID); err != nil { - return fmt.Errorf("ensuring project in ent store: %w", err) - } - } - return c.groups.CreateGroup(ctx, group) -} - -func (c *CompositeStore) GetGroup(ctx context.Context, id string) (*store.Group, error) { - return c.groups.GetGroup(ctx, id) -} - -func (c *CompositeStore) GetGroupBySlug(ctx context.Context, slug string) (*store.Group, error) { - return c.groups.GetGroupBySlug(ctx, slug) -} - -func (c *CompositeStore) UpdateGroup(ctx context.Context, group *store.Group) error { - return c.groups.UpdateGroup(ctx, group) -} - -func (c *CompositeStore) DeleteGroup(ctx context.Context, id string) error { - return c.groups.DeleteGroup(ctx, id) -} - -func (c *CompositeStore) ListGroups(ctx context.Context, filter store.GroupFilter, opts store.ListOptions) (*store.ListResult[store.Group], error) { - return c.groups.ListGroups(ctx, filter, opts) -} - -func (c *CompositeStore) AddGroupMember(ctx context.Context, member *store.GroupMember) error { - switch member.MemberType { - case store.GroupMemberTypeUser: - if err := c.ensureEntUser(ctx, member.MemberID); err != nil { - return fmt.Errorf("ensuring user in ent store: %w", err) - } - case store.GroupMemberTypeAgent: - if err := c.ensureEntAgent(ctx, member.MemberID); err != nil { - return fmt.Errorf("ensuring agent in ent store: %w", err) - } - } - return c.groups.AddGroupMember(ctx, member) -} - -// ensureEntUser checks if a user exists in the Ent database and, if not, -// creates a minimal shadow record from the base store. This is needed because -// the Ent database has foreign key constraints on group memberships, but users -// may only exist in the base (main SQLite) database. -func (c *CompositeStore) ensureEntUser(ctx context.Context, userID string) error { - uid, err := parseUUID(userID) + uid, err := parseUUID(id) if err != nil { return err } - - // Check if user already exists in Ent - exists, err := c.client.User.Query().Where(entuser.IDEQ(uid)).Exist(ctx) - if err != nil { - return fmt.Errorf("checking ent user existence: %w", err) - } - if exists { - return nil - } - - // Fetch from the base store - u, err := c.Store.GetUser(ctx, userID) - if err != nil { - return fmt.Errorf("fetching user from base store: %w", err) - } - - // Create a minimal shadow record in Ent - _, err = c.client.User.Create(). - SetID(uid). - SetEmail(u.Email). - SetDisplayName(u.DisplayName). - SetRole(entuser.Role(u.Role)). - Save(ctx) - if err != nil { - // Another goroutine may have created it concurrently - if ent.IsConstraintError(err) { - return nil - } - return fmt.Errorf("creating shadow user in ent: %w", err) - } - - return nil -} - -// ensureEntAgent checks if an agent exists in the Ent database and, if not, -// creates a minimal shadow record from the base store. The agent.s project is -// also ensured to exist in Ent since it is a required FK. -func (c *CompositeStore) ensureEntAgent(ctx context.Context, agentID string) error { - uid, err := parseUUID(agentID) - if err != nil { + if _, err := c.client.Notification.Delete(). + Where(notification.AgentIDEQ(uid)).Exec(ctx); err != nil { return err } - - // Check if agent already exists in Ent - _, getErr := c.client.Agent.Get(ctx, uid) - if getErr == nil { - return nil // already exists - } - if !ent.IsNotFound(getErr) { - return fmt.Errorf("checking ent agent existence: %w", getErr) - } - - // Fetch from the base store - a, err := c.Store.GetAgent(ctx, agentID) - if err != nil { - return fmt.Errorf("fetching agent from base store: %w", err) - } - - // Ensure the project exists in Ent first (required FK) - if err := c.ensureEntProject(ctx, a.ProjectID); err != nil { - return fmt.Errorf("ensuring project in ent store: %w", err) - } - - projectUID, err := parseUUID(a.ProjectID) - if err != nil { + if _, err := c.client.NotificationSubscription.Delete(). + Where(notificationsubscription.AgentIDEQ(uid)).Exec(ctx); err != nil { return err } - - // Create a minimal shadow record in Ent - _, err = c.client.Agent.Create(). - SetID(uid). - SetName(a.Name). - SetSlug(a.Slug). - SetProjectID(projectUID). - Save(ctx) - if err != nil { - if ent.IsConstraintError(err) { - return nil - } - return fmt.Errorf("creating shadow agent in ent: %w", err) - } - return nil } -// ensureEntProject checks if a project exists in the Ent database and, if not, -// creates a minimal shadow record from the base store. -func (c *CompositeStore) ensureEntProject(ctx context.Context, projectID string) error { - uid, err := parseUUID(projectID) +// DeleteProject deletes a project and cascade-deletes its agents (and each +// agent's notification subscriptions/notifications). The former raw-SQL store +// enforced this via agents.grove_id -> groves(id) ON DELETE CASCADE; the Ent +// project->agents edge has no DB-level cascade, so deleting a project while +// agents still reference it would fail with a foreign-key violation. The bulk +// agent delete is a hard delete, so it also removes soft-deleted agents. +func (c *CompositeStore) DeleteProject(ctx context.Context, id string) error { + uid, err := parseUUID(id) if err != nil { return err } - - _, getErr := c.client.Project.Get(ctx, uid) - if getErr == nil { - return nil - } - if !ent.IsNotFound(getErr) { - return fmt.Errorf("checking ent project existence: %w", getErr) - } - - g, err := c.Store.GetProject(ctx, projectID) + agentIDs, err := c.client.Agent.Query().Where(agent.ProjectIDEQ(uid)).IDs(ctx) if err != nil { - return fmt.Errorf("fetching project from base store: %w", err) + return err } - - _, err = c.client.Project.Create(). - SetID(uid). - SetName(g.Name). - SetSlug(g.Slug). - Save(ctx) - if err != nil { - if ent.IsConstraintError(err) { - return nil + if len(agentIDs) > 0 { + if _, err := c.client.Notification.Delete(). + Where(notification.AgentIDIn(agentIDs...)).Exec(ctx); err != nil { + return err + } + if _, err := c.client.NotificationSubscription.Delete(). + Where(notificationsubscription.AgentIDIn(agentIDs...)).Exec(ctx); err != nil { + return err + } + if _, err := c.client.Agent.Delete(). + Where(agent.ProjectIDEQ(uid)).Exec(ctx); err != nil { + return err } - return fmt.Errorf("creating shadow project in ent: %w", err) } - - return nil + return c.ProjectStore.DeleteProject(ctx, id) } -func (c *CompositeStore) UpdateGroupMemberRole(ctx context.Context, groupID, memberType, memberID, newRole string) error { - return c.groups.UpdateGroupMemberRole(ctx, groupID, memberType, memberID, newRole) -} - -func (c *CompositeStore) RemoveGroupMember(ctx context.Context, groupID, memberType, memberID string) error { - return c.groups.RemoveGroupMember(ctx, groupID, memberType, memberID) -} - -func (c *CompositeStore) GetGroupMembers(ctx context.Context, groupID string) ([]store.GroupMember, error) { - return c.groups.GetGroupMembers(ctx, groupID) -} - -func (c *CompositeStore) GetUserGroups(ctx context.Context, userID string) ([]store.GroupMember, error) { - return c.groups.GetUserGroups(ctx, userID) -} - -func (c *CompositeStore) GetGroupMembership(ctx context.Context, groupID, memberType, memberID string) (*store.GroupMember, error) { - return c.groups.GetGroupMembership(ctx, groupID, memberType, memberID) -} - -func (c *CompositeStore) WouldCreateCycle(ctx context.Context, groupID, memberGroupID string) (bool, error) { - return c.groups.WouldCreateCycle(ctx, groupID, memberGroupID) -} - -func (c *CompositeStore) GetEffectiveGroups(ctx context.Context, userID string) ([]string, error) { - return c.groups.GetEffectiveGroups(ctx, userID) -} - -func (c *CompositeStore) GetGroupByProjectID(ctx context.Context, projectID string) (*store.Group, error) { - return c.groups.GetGroupByProjectID(ctx, projectID) -} - -func (c *CompositeStore) GetEffectiveGroupsForAgent(ctx context.Context, agentID string) ([]string, error) { - return c.groups.GetEffectiveGroupsForAgent(ctx, agentID) -} - -func (c *CompositeStore) CheckDelegatedAccess(ctx context.Context, agentID string, conditions *store.PolicyConditions) (bool, error) { - return c.groups.CheckDelegatedAccess(ctx, agentID, conditions) -} - -func (c *CompositeStore) GetGroupsByIDs(ctx context.Context, ids []string) ([]store.Group, error) { - return c.groups.GetGroupsByIDs(ctx, ids) -} - -func (c *CompositeStore) CountGroupMembersByRole(ctx context.Context, groupID, role string) (int, error) { - return c.groups.CountGroupMembersByRole(ctx, groupID, role) -} - -// PolicyStore method overrides — delegate to Ent-backed PolicyStore. - -func (c *CompositeStore) CreatePolicy(ctx context.Context, policy *store.Policy) error { - return c.policies.CreatePolicy(ctx, policy) -} - -func (c *CompositeStore) GetPolicy(ctx context.Context, id string) (*store.Policy, error) { - return c.policies.GetPolicy(ctx, id) -} - -func (c *CompositeStore) UpdatePolicy(ctx context.Context, policy *store.Policy) error { - return c.policies.UpdatePolicy(ctx, policy) -} - -func (c *CompositeStore) DeletePolicy(ctx context.Context, id string) error { - return c.policies.DeletePolicy(ctx, id) -} - -func (c *CompositeStore) ListPolicies(ctx context.Context, filter store.PolicyFilter, opts store.ListOptions) (*store.ListResult[store.Policy], error) { - return c.policies.ListPolicies(ctx, filter, opts) -} - -func (c *CompositeStore) AddPolicyBinding(ctx context.Context, binding *store.PolicyBinding) error { - return c.policies.AddPolicyBinding(ctx, binding) -} - -func (c *CompositeStore) RemovePolicyBinding(ctx context.Context, policyID, principalType, principalID string) error { - return c.policies.RemovePolicyBinding(ctx, policyID, principalType, principalID) +// Close closes the underlying Ent client. +func (c *CompositeStore) Close() error { + return c.client.Close() } -func (c *CompositeStore) GetPolicyBindings(ctx context.Context, policyID string) ([]store.PolicyBinding, error) { - return c.policies.GetPolicyBindings(ctx, policyID) +// Ping verifies connectivity to the underlying database. +func (c *CompositeStore) Ping(ctx context.Context) error { + drv, ok := c.client.Driver().(*entsql.Driver) + if !ok { + return fmt.Errorf("ent client driver does not expose a *sql.DB for ping") + } + return drv.DB().PingContext(ctx) } -func (c *CompositeStore) GetPoliciesForPrincipal(ctx context.Context, principalType, principalID string) ([]store.Policy, error) { - return c.policies.GetPoliciesForPrincipal(ctx, principalType, principalID) +// Migrate runs Ent's automatic schema migration against the shared client and +// seeds the built-in maintenance operations, matching the behavior of the +// former raw-SQL store (which seeded these as part of its migrations). +func (c *CompositeStore) Migrate(ctx context.Context) error { + if err := entc.AutoMigrate(ctx, c.client); err != nil { + return err + } + return c.MaintenanceStore.SeedMaintenanceOperations(ctx) } -func (c *CompositeStore) GetPoliciesForPrincipals(ctx context.Context, principals []store.PrincipalRef) ([]store.Policy, error) { - return c.policies.GetPoliciesForPrincipals(ctx, principals) +// DB returns the underlying *sql.DB, or nil if the client is not backed by a +// database/sql driver. It is an escape hatch for diagnostics and tests that +// need raw SQL access; production code should use the typed store methods. +func (c *CompositeStore) DB() *sql.DB { + if drv, ok := c.client.Driver().(*entsql.Driver); ok { + return drv.DB() + } + return nil } diff --git a/pkg/store/entadapter/composite_test.go b/pkg/store/entadapter/composite_test.go index 4cedfb51a..7543eac6d 100644 --- a/pkg/store/entadapter/composite_test.go +++ b/pkg/store/entadapter/composite_test.go @@ -22,42 +22,34 @@ import ( "time" "github.com/GoogleCloudPlatform/scion/pkg/agent/state" - "github.com/GoogleCloudPlatform/scion/pkg/ent/entc" "github.com/GoogleCloudPlatform/scion/pkg/store" - "github.com/GoogleCloudPlatform/scion/pkg/store/sqlite" + "github.com/GoogleCloudPlatform/scion/pkg/store/enttest" "github.com/google/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) -// newTestCompositeStore creates a CompositeStore with a real SQLite base store -// and a separate Ent client, simulating the production dual-database layout. +// newTestCompositeStore creates a CompositeStore backed by a single in-memory +// Ent database, matching the production single-database layout. func newTestCompositeStore(t *testing.T) *CompositeStore { t.Helper() - // Create the base SQLite store (main database) - base, err := sqlite.New(":memory:") - require.NoError(t, err) - require.NoError(t, base.Migrate(context.Background())) - - // Create a separate Ent-managed database (permissions database) - entClient, err := entc.OpenSQLite("file:" + t.Name() + "?mode=memory&cache=shared") - require.NoError(t, err) - require.NoError(t, entc.AutoMigrate(context.Background(), entClient)) + entClient := enttest.NewClient(t) - cs := NewCompositeStore(base, entClient) + cs := NewCompositeStore(entClient) t.Cleanup(func() { cs.Close() }) return cs } -func TestCompositeStore_AddGroupMember_UserShadowRecord(t *testing.T) { +func TestCompositeStore_AddGroupMember_User(t *testing.T) { cs := newTestCompositeStore(t) ctx := context.Background() - // Create a user in the base store only (simulating normal user creation) + // Create a user. With a single Ent-backed database the user, group, and + // membership all live in the same store and the FK resolves natively. userID := uuid.New().String() - err := cs.Store.CreateUser(ctx, &store.User{ + err := cs.CreateUser(ctx, &store.User{ ID: userID, Email: "test@example.com", DisplayName: "Test User", @@ -67,7 +59,7 @@ func TestCompositeStore_AddGroupMember_UserShadowRecord(t *testing.T) { }) require.NoError(t, err) - // Create a group in Ent + // Create a group. groupID := uuid.New().String() err = cs.CreateGroup(ctx, &store.Group{ ID: groupID, @@ -77,79 +69,33 @@ func TestCompositeStore_AddGroupMember_UserShadowRecord(t *testing.T) { }) require.NoError(t, err) - // Add the user as a member — this should succeed because the CompositeStore - // creates a shadow user record in the Ent database before adding the membership. + // Add the user as a member. err = cs.AddGroupMember(ctx, &store.GroupMember{ GroupID: groupID, MemberType: store.GroupMemberTypeUser, MemberID: userID, Role: store.GroupMemberRoleMember, }) - require.NoError(t, err, "AddGroupMember should succeed for user that exists only in base store") + require.NoError(t, err, "AddGroupMember should succeed for an existing user") - // Verify the membership was created + // Verify the membership was created. membership, err := cs.GetGroupMembership(ctx, groupID, store.GroupMemberTypeUser, userID) require.NoError(t, err) assert.Equal(t, userID, membership.MemberID) - // Verify the user appears in effective groups + // Verify the user appears in effective groups. groups, err := cs.GetEffectiveGroups(ctx, userID) require.NoError(t, err) assert.Contains(t, groups, groupID) } -func TestCompositeStore_AddGroupMember_UserAlreadyInEnt(t *testing.T) { - cs := newTestCompositeStore(t) - ctx := context.Background() - - userID := uuid.New().String() - userUID, _ := uuid.Parse(userID) - - // Create user in both base store and Ent - err := cs.Store.CreateUser(ctx, &store.User{ - ID: userID, - Email: "already@example.com", - DisplayName: "Already Here", - Role: store.UserRoleMember, - Status: "active", - Created: time.Now(), - }) - require.NoError(t, err) - - _, err = cs.client.User.Create(). - SetID(userUID). - SetEmail("already@example.com"). - SetDisplayName("Already Here"). - Save(ctx) - require.NoError(t, err) - - // Create a group - groupID := uuid.New().String() - err = cs.CreateGroup(ctx, &store.Group{ - ID: groupID, - Name: "Test Group 2", - Slug: "test-group-2", - GroupType: store.GroupTypeExplicit, - }) - require.NoError(t, err) - - // Should work without issues (no duplicate creation) - err = cs.AddGroupMember(ctx, &store.GroupMember{ - GroupID: groupID, - MemberType: store.GroupMemberTypeUser, - MemberID: userID, - Role: store.GroupMemberRoleMember, - }) - require.NoError(t, err) -} - -func TestCompositeStore_AddGroupMember_AgentShadowRecord(t *testing.T) { +func TestCompositeStore_AddGroupMember_Agent(t *testing.T) { cs := newTestCompositeStore(t) ctx := context.Background() - // Create a project in the base store + // Create a project. projectID := uuid.New().String() - err := cs.Store.CreateProject(ctx, &store.Project{ + err := cs.CreateProject(ctx, &store.Project{ ID: projectID, Name: "Test Project", Slug: "test-project", @@ -158,9 +104,9 @@ func TestCompositeStore_AddGroupMember_AgentShadowRecord(t *testing.T) { }) require.NoError(t, err) - // Create an agent in the base store only + // Create an agent referencing the project. agentID := uuid.New().String() - err = cs.Store.CreateAgent(ctx, &store.Agent{ + err = cs.CreateAgent(ctx, &store.Agent{ ID: agentID, Name: "Test Agent", Slug: "test-agent", @@ -172,7 +118,7 @@ func TestCompositeStore_AddGroupMember_AgentShadowRecord(t *testing.T) { }) require.NoError(t, err) - // Create a group + // Create a group. groupID := uuid.New().String() err = cs.CreateGroup(ctx, &store.Group{ ID: groupID, @@ -182,16 +128,16 @@ func TestCompositeStore_AddGroupMember_AgentShadowRecord(t *testing.T) { }) require.NoError(t, err) - // Add the agent as a member — should create shadow agent and project records + // Add the agent as a member. err = cs.AddGroupMember(ctx, &store.GroupMember{ GroupID: groupID, MemberType: store.GroupMemberTypeAgent, MemberID: agentID, Role: store.GroupMemberRoleMember, }) - require.NoError(t, err, "AddGroupMember should succeed for agent that exists only in base store") + require.NoError(t, err, "AddGroupMember should succeed for an existing agent") - // Verify membership + // Verify membership. membership, err := cs.GetGroupMembership(ctx, groupID, store.GroupMemberTypeAgent, agentID) require.NoError(t, err) assert.Equal(t, agentID, membership.MemberID) @@ -202,7 +148,7 @@ func TestCompositeStore_AddGroupMember_Idempotent(t *testing.T) { ctx := context.Background() userID := uuid.New().String() - err := cs.Store.CreateUser(ctx, &store.User{ + err := cs.CreateUser(ctx, &store.User{ ID: userID, Email: "idempotent@example.com", DisplayName: "Idempotent User", @@ -221,7 +167,7 @@ func TestCompositeStore_AddGroupMember_Idempotent(t *testing.T) { }) require.NoError(t, err) - // First add should succeed + // First add should succeed. member := &store.GroupMember{ GroupID: groupID, MemberType: store.GroupMemberTypeUser, @@ -231,48 +177,41 @@ func TestCompositeStore_AddGroupMember_Idempotent(t *testing.T) { err = cs.AddGroupMember(ctx, member) require.NoError(t, err) - // Second add of same membership should return ErrAlreadyExists + // Second add of same membership should return ErrAlreadyExists. err = cs.AddGroupMember(ctx, member) assert.ErrorIs(t, err, store.ErrAlreadyExists) } -// TestCompositeStore_CreateGroup_WithProjectID tests that creating a group with a -// project ID succeeds even though the project only exists in the base (SQLite) store. -// The CompositeStore should create a shadow project record in the Ent database to -// satisfy the foreign key constraint. +// TestCompositeStore_CreateGroup_WithProjectID verifies that creating a group +// referencing a project succeeds when the project lives in the same Ent store. func TestCompositeStore_CreateGroup_WithProjectID(t *testing.T) { cs := newTestCompositeStore(t) ctx := context.Background() - // Create a project in the base store only (not in Ent) projectID := uuid.New().String() - err := cs.Store.CreateProject(ctx, &store.Project{ + err := cs.CreateProject(ctx, &store.Project{ ID: projectID, - Name: "Shadow Project", - Slug: "shadow-project", + Name: "Project", + Slug: "project", Created: time.Now(), Updated: time.Now(), }) require.NoError(t, err) - // Create a group with project_id — this should succeed because the - // CompositeStore creates a shadow project record in Ent before creating - // the group. groupID := uuid.New().String() err = cs.CreateGroup(ctx, &store.Group{ ID: groupID, - Name: "Shadow Project Agents", - Slug: "project:shadow-project:agents", + Name: "Project Agents", + Slug: "project:project:agents", GroupType: store.GroupTypeProjectAgents, ProjectID: projectID, }) - require.NoError(t, err, "CreateGroup should succeed for project that exists only in base store") + require.NoError(t, err, "CreateGroup should succeed for an existing project") - // Verify the group was created with the correct project ID group, err := cs.GetGroup(ctx, groupID) require.NoError(t, err) assert.Equal(t, projectID, group.ProjectID) - assert.Equal(t, "project:shadow-project:agents", group.Slug) + assert.Equal(t, "project:project:agents", group.Slug) } // TestCompositeStore_CreateGroup_MultipleGroupsPerProject verifies that multiple @@ -283,7 +222,7 @@ func TestCompositeStore_CreateGroup_MultipleGroupsPerProject(t *testing.T) { ctx := context.Background() projectID := uuid.New().String() - err := cs.Store.CreateProject(ctx, &store.Project{ + err := cs.CreateProject(ctx, &store.Project{ ID: projectID, Name: "Multi-Group Project", Slug: "multi-group-project", @@ -292,7 +231,7 @@ func TestCompositeStore_CreateGroup_MultipleGroupsPerProject(t *testing.T) { }) require.NoError(t, err) - // Create agents group + // Create agents group. agentsGroupID := uuid.New().String() err = cs.CreateGroup(ctx, &store.Group{ ID: agentsGroupID, @@ -303,7 +242,7 @@ func TestCompositeStore_CreateGroup_MultipleGroupsPerProject(t *testing.T) { }) require.NoError(t, err, "agents group creation should succeed") - // Create members group for the same project — this must NOT fail + // Create members group for the same project — this must NOT fail. membersGroupID := uuid.New().String() err = cs.CreateGroup(ctx, &store.Group{ ID: membersGroupID, @@ -314,7 +253,7 @@ func TestCompositeStore_CreateGroup_MultipleGroupsPerProject(t *testing.T) { }) require.NoError(t, err, "members group creation should succeed for same project") - // Verify both groups exist with the correct project ID + // Verify both groups exist with the correct project ID. agents, err := cs.GetGroup(ctx, agentsGroupID) require.NoError(t, err) assert.Equal(t, projectID, agents.ProjectID) diff --git a/pkg/store/entadapter/dialect.go b/pkg/store/entadapter/dialect.go new file mode 100644 index 000000000..74be5a775 --- /dev/null +++ b/pkg/store/entadapter/dialect.go @@ -0,0 +1,80 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package entadapter + +import ( + "entgo.io/ent/dialect" + entsql "entgo.io/ent/dialect/sql" + + "github.com/GoogleCloudPlatform/scion/pkg/ent/agent" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" +) + +// ancestryContains returns an Ent predicate restricting results to agents whose +// `ancestry` JSON array contains principalID. +// +// JSON-array membership has no portable SQL spelling, so this is the one agent +// query that must dialect-switch its raw fragment. Both dialects expand the +// stored JSON array into a row set and test for membership inside a correlated +// EXISTS subquery, which composes cleanly with the surrounding typed Ent query +// (soft-delete predicate, ordering, pagination, COUNT): +// +// SQLite: EXISTS (SELECT 1 FROM json_each(ancestry) +// WHERE json_each.value = ?) +// Postgres: EXISTS (SELECT 1 FROM jsonb_array_elements_text(ancestry) AS elem +// WHERE elem = $n) +// +// Two dialect details are load-bearing: +// +// - Function name: Ent stores field.TypeJSON as `jsonb` on Postgres, so the +// set-returning function must be jsonb_array_elements_text (the json_* +// variant only accepts the `json` type). +// - Bind parameter: the fragment is emitted through Builder.Arg, not as a +// literal "?" via ExprP. ExprP writes raw text verbatim and does NOT rebind +// "?" to Postgres' "$n" syntax, which produced a syntax error against +// Postgres. Builder.Arg emits the dialect-correct placeholder ("?" on +// SQLite, "$n" on Postgres) and tracks the argument index. +// +// The dialect is read from the live selector via Builder.Dialect(), so the same +// store works against either backend with no external configuration. +// +// The ancestry IS NOT NULL guard short-circuits agents with no recorded +// lineage and keeps Postgres from invoking the set-returning function on a NULL +// input. +func ancestryContains(principalID string) predicate.Agent { + return func(s *entsql.Selector) { + col := s.C(agent.FieldAncestry) + switch s.Dialect() { + case dialect.Postgres: + s.Where(entsql.P(func(b *entsql.Builder) { + b.WriteString(col). + WriteString(" IS NOT NULL AND EXISTS (SELECT 1 FROM jsonb_array_elements_text("). + WriteString(col). + WriteString(") AS elem WHERE elem = "). + Arg(principalID). + WriteString(")") + })) + default: // SQLite and any other backend providing json_each(). + s.Where(entsql.P(func(b *entsql.Builder) { + b.WriteString(col). + WriteString(" IS NOT NULL AND EXISTS (SELECT 1 FROM json_each("). + WriteString(col). + WriteString(") WHERE json_each.value = "). + Arg(principalID). + WriteString(")") + })) + } + } +} diff --git a/pkg/store/entadapter/external_store.go b/pkg/store/entadapter/external_store.go new file mode 100644 index 000000000..d85846876 --- /dev/null +++ b/pkg/store/entadapter/external_store.go @@ -0,0 +1,548 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package entadapter + +import ( + "context" + "encoding/json" + "strings" + "time" + + "github.com/GoogleCloudPlatform/scion/pkg/ent" + "github.com/GoogleCloudPlatform/scion/pkg/ent/gcpserviceaccount" + "github.com/GoogleCloudPlatform/scion/pkg/ent/githubinstallation" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/GoogleCloudPlatform/scion/pkg/ent/useraccesstoken" + "github.com/GoogleCloudPlatform/scion/pkg/store" +) + +// ExternalStore implements the external-identity store sub-interfaces backed by +// Ent: GCP service accounts, GitHub App installations, and user access tokens. +// +// The legacy api_keys table (superseded by user_access_tokens in V34) is +// schematized in Ent for migration fidelity but has no store interface and is +// intentionally not surfaced here. +type ExternalStore struct { + client *ent.Client +} + +// NewExternalStore creates a new Ent-backed ExternalStore. +func NewExternalStore(client *ent.Client) *ExternalStore { + return &ExternalStore{client: client} +} + +// ============================================================================ +// GCP Service Accounts +// ============================================================================ + +// entGCPToStore converts an Ent GCPServiceAccount to the store model. +func entGCPToStore(e *ent.GCPServiceAccount) *store.GCPServiceAccount { + sa := &store.GCPServiceAccount{ + ID: e.ID.String(), + Scope: e.Scope, + ScopeID: e.ScopeID, + Email: e.Email, + ProjectID: e.ProjectID, + DisplayName: e.DisplayName, + Verified: e.Verified, + CreatedBy: e.CreatedBy, + CreatedAt: e.Created, + Managed: e.Managed, + ManagedBy: e.ManagedBy, + } + // default_scopes is stored as a CSV string for parity with the SQLite store. + if e.DefaultScopes != "" { + sa.DefaultScopes = strings.Split(e.DefaultScopes, ",") + } + if e.VerifiedAt != nil { + sa.VerifiedAt = *e.VerifiedAt + } + return sa +} + +// CreateGCPServiceAccount registers a new GCP service account. +func (s *ExternalStore) CreateGCPServiceAccount(ctx context.Context, sa *store.GCPServiceAccount) error { + id, err := parseUUID(sa.ID) + if err != nil { + return err + } + if sa.CreatedAt.IsZero() { + sa.CreatedAt = time.Now() + } + + create := s.client.GCPServiceAccount.Create(). + SetID(id). + SetScope(sa.Scope). + SetScopeID(sa.ScopeID). + SetEmail(sa.Email). + SetProjectID(sa.ProjectID). + SetDisplayName(sa.DisplayName). + SetDefaultScopes(strings.Join(sa.DefaultScopes, ",")). + SetVerified(sa.Verified). + SetCreatedBy(sa.CreatedBy). + SetManaged(sa.Managed). + SetManagedBy(sa.ManagedBy). + SetCreated(sa.CreatedAt) + + if !sa.VerifiedAt.IsZero() { + create.SetVerifiedAt(sa.VerifiedAt) + } + + if _, err := create.Save(ctx); err != nil { + return mapError(err) + } + return nil +} + +// GetGCPServiceAccount retrieves a GCP service account by ID. +func (s *ExternalStore) GetGCPServiceAccount(ctx context.Context, id string) (*store.GCPServiceAccount, error) { + uid, err := parseGetID(id) + if err != nil { + return nil, err + } + e, err := s.client.GCPServiceAccount.Get(ctx, uid) + if err != nil { + return nil, mapError(err) + } + return entGCPToStore(e), nil +} + +// UpdateGCPServiceAccount updates a GCP service account record. +func (s *ExternalStore) UpdateGCPServiceAccount(ctx context.Context, sa *store.GCPServiceAccount) error { + id, err := parseUUID(sa.ID) + if err != nil { + return err + } + update := s.client.GCPServiceAccount.UpdateOneID(id). + SetEmail(sa.Email). + SetProjectID(sa.ProjectID). + SetDisplayName(sa.DisplayName). + SetDefaultScopes(strings.Join(sa.DefaultScopes, ",")). + SetVerified(sa.Verified). + SetManaged(sa.Managed). + SetManagedBy(sa.ManagedBy) + + if sa.VerifiedAt.IsZero() { + update.ClearVerifiedAt() + } else { + update.SetVerifiedAt(sa.VerifiedAt) + } + + if _, err := update.Save(ctx); err != nil { + return mapError(err) + } + return nil +} + +// DeleteGCPServiceAccount removes a GCP service account by ID. +func (s *ExternalStore) DeleteGCPServiceAccount(ctx context.Context, id string) error { + uid, err := parseUUID(id) + if err != nil { + return err + } + if err := s.client.GCPServiceAccount.DeleteOneID(uid).Exec(ctx); err != nil { + return mapError(err) + } + return nil +} + +// gcpFilterPredicates builds the Ent predicates for a GCPServiceAccountFilter. +func gcpFilterPredicates(filter store.GCPServiceAccountFilter) []predicate.GCPServiceAccount { + var preds []predicate.GCPServiceAccount + if filter.Scope != "" { + preds = append(preds, gcpserviceaccount.ScopeEQ(filter.Scope)) + } + if filter.ScopeID != "" { + preds = append(preds, gcpserviceaccount.ScopeIDEQ(filter.ScopeID)) + } + if filter.Email != "" { + preds = append(preds, gcpserviceaccount.EmailEQ(filter.Email)) + } + if filter.Managed != nil { + preds = append(preds, gcpserviceaccount.ManagedEQ(*filter.Managed)) + } + return preds +} + +// ListGCPServiceAccounts returns GCP service accounts matching the filter. +func (s *ExternalStore) ListGCPServiceAccounts(ctx context.Context, filter store.GCPServiceAccountFilter) ([]store.GCPServiceAccount, error) { + rows, err := s.client.GCPServiceAccount.Query(). + Where(gcpFilterPredicates(filter)...). + Order(gcpserviceaccount.ByCreated(entDesc())). + All(ctx) + if err != nil { + return nil, err + } + out := make([]store.GCPServiceAccount, 0, len(rows)) + for _, e := range rows { + out = append(out, *entGCPToStore(e)) + } + return out, nil +} + +// CountGCPServiceAccounts returns the number of GCP service accounts matching the filter. +func (s *ExternalStore) CountGCPServiceAccounts(ctx context.Context, filter store.GCPServiceAccountFilter) (int, error) { + return s.client.GCPServiceAccount.Query(). + Where(gcpFilterPredicates(filter)...). + Count(ctx) +} + +// ============================================================================ +// GitHub App Installations +// ============================================================================ + +// marshalRepos serializes the repositories slice to the JSON string stored in +// the dialect-neutral repositories column. +func marshalRepos(repos []string) string { + if repos == nil { + repos = []string{} + } + b, _ := json.Marshal(repos) + return string(b) +} + +// entGitHubToStore converts an Ent GithubInstallation to the store model. +func entGitHubToStore(e *ent.GithubInstallation) *store.GitHubInstallation { + inst := &store.GitHubInstallation{ + InstallationID: e.ID, + AccountLogin: e.AccountLogin, + AccountType: e.AccountType, + AppID: e.AppID, + Status: e.Status, + CreatedAt: e.Created, + UpdatedAt: e.Updated, + } + if e.Repositories != "" { + _ = json.Unmarshal([]byte(e.Repositories), &inst.Repositories) + } + return inst +} + +// CreateGitHubInstallation creates a new GitHub App installation record. +// +// installation_id is the GitHub-provided natural key; mirroring the legacy +// "INSERT OR IGNORE" behavior, creating an installation that already exists is a +// no-op (idempotent) rather than an error. +func (s *ExternalStore) CreateGitHubInstallation(ctx context.Context, installation *store.GitHubInstallation) error { + // Idempotency guard: if the natural key already exists, do nothing. + exists, err := s.client.GithubInstallation.Query(). + Where(githubinstallation.IDEQ(installation.InstallationID)). + Exist(ctx) + if err != nil { + return err + } + if exists { + return nil + } + + if installation.CreatedAt.IsZero() { + installation.CreatedAt = time.Now() + } + if installation.UpdatedAt.IsZero() { + installation.UpdatedAt = installation.CreatedAt + } + if installation.Status == "" { + installation.Status = store.GitHubInstallationStatusActive + } + accountType := installation.AccountType + if accountType == "" { + accountType = "Organization" + } + + err = s.client.GithubInstallation.Create(). + SetID(installation.InstallationID). + SetAccountLogin(installation.AccountLogin). + SetAccountType(accountType). + SetAppID(installation.AppID). + SetRepositories(marshalRepos(installation.Repositories)). + SetStatus(installation.Status). + SetCreated(installation.CreatedAt). + SetUpdated(installation.UpdatedAt). + Exec(ctx) + if err != nil { + // Another writer may have created it concurrently — stay idempotent. + if ent.IsConstraintError(err) { + return nil + } + return mapError(err) + } + return nil +} + +// GetGitHubInstallation retrieves a GitHub App installation by installation ID. +func (s *ExternalStore) GetGitHubInstallation(ctx context.Context, installationID int64) (*store.GitHubInstallation, error) { + e, err := s.client.GithubInstallation.Get(ctx, installationID) + if err != nil { + return nil, mapError(err) + } + return entGitHubToStore(e), nil +} + +// UpdateGitHubInstallation updates an existing GitHub App installation. +func (s *ExternalStore) UpdateGitHubInstallation(ctx context.Context, installation *store.GitHubInstallation) error { + installation.UpdatedAt = time.Now() + + _, err := s.client.GithubInstallation.UpdateOneID(installation.InstallationID). + SetAccountLogin(installation.AccountLogin). + SetAccountType(installation.AccountType). + SetAppID(installation.AppID). + SetRepositories(marshalRepos(installation.Repositories)). + SetStatus(installation.Status). + SetUpdated(installation.UpdatedAt). + Save(ctx) + if err != nil { + return mapError(err) + } + return nil +} + +// DeleteGitHubInstallation removes a GitHub App installation by installation ID. +func (s *ExternalStore) DeleteGitHubInstallation(ctx context.Context, installationID int64) error { + if err := s.client.GithubInstallation.DeleteOneID(installationID).Exec(ctx); err != nil { + return mapError(err) + } + return nil +} + +// ListGitHubInstallations returns all GitHub App installations matching the filter. +func (s *ExternalStore) ListGitHubInstallations(ctx context.Context, filter store.GitHubInstallationFilter) ([]store.GitHubInstallation, error) { + query := s.client.GithubInstallation.Query() + if filter.AccountLogin != "" { + query = query.Where(githubinstallation.AccountLoginEQ(filter.AccountLogin)) + } + if filter.Status != "" { + query = query.Where(githubinstallation.StatusEQ(filter.Status)) + } + if filter.AppID != 0 { + query = query.Where(githubinstallation.AppIDEQ(filter.AppID)) + } + + rows, err := query.Order(githubinstallation.ByCreated()).All(ctx) + if err != nil { + return nil, err + } + + // Never return a nil slice (parity with the SQLite store). + results := make([]store.GitHubInstallation, 0, len(rows)) + for _, e := range rows { + results = append(results, *entGitHubToStore(e)) + } + return results, nil +} + +// GetInstallationForRepository returns an active GitHub App installation that +// covers the given repository (owner/repo format). +func (s *ExternalStore) GetInstallationForRepository(ctx context.Context, repoFullName string) (*store.GitHubInstallation, error) { + // Scan active installations whose repositories JSON array contains the repo. + installations, err := s.ListGitHubInstallations(ctx, store.GitHubInstallationFilter{ + Status: store.GitHubInstallationStatusActive, + }) + if err != nil { + return nil, err + } + + for i := range installations { + for _, repo := range installations[i].Repositories { + if repo == repoFullName { + return &installations[i], nil + } + } + } + return nil, store.ErrNotFound +} + +// ============================================================================ +// User Access Tokens (UATs) +// ============================================================================ + +// marshalScopes serializes token scopes to the JSON string stored in the scopes +// column. The column is NotEmpty, so a nil/empty slice serializes to "[]". +func marshalScopes(scopes []string) string { + if scopes == nil { + scopes = []string{} + } + b, _ := json.Marshal(scopes) + return string(b) +} + +// entUATToStore converts an Ent UserAccessToken to the store model. +func entUATToStore(e *ent.UserAccessToken) *store.UserAccessToken { + t := &store.UserAccessToken{ + ID: e.ID.String(), + UserID: e.UserID.String(), + Name: e.Name, + Prefix: e.Prefix, + KeyHash: e.KeyHash, + ProjectID: e.ProjectID.String(), + Revoked: e.Revoked, + Created: e.Created, + } + if e.Scopes != "" { + _ = json.Unmarshal([]byte(e.Scopes), &t.Scopes) + } + if e.ExpiresAt != nil { + t.ExpiresAt = e.ExpiresAt + } + if e.LastUsed != nil { + t.LastUsed = e.LastUsed + } + return t +} + +// CreateUserAccessToken creates a new user access token record. +func (s *ExternalStore) CreateUserAccessToken(ctx context.Context, token *store.UserAccessToken) error { + id, err := parseUUID(token.ID) + if err != nil { + return err + } + userUID, err := parseUUID(token.UserID) + if err != nil { + return err + } + projectUID, err := parseUUID(token.ProjectID) + if err != nil { + return err + } + + if token.Created.IsZero() { + token.Created = time.Now() + } + + create := s.client.UserAccessToken.Create(). + SetID(id). + SetUserID(userUID). + SetName(token.Name). + SetPrefix(token.Prefix). + SetKeyHash(token.KeyHash). + SetProjectID(projectUID). + SetScopes(marshalScopes(token.Scopes)). + SetRevoked(token.Revoked). + SetCreated(token.Created) + + if token.ExpiresAt != nil { + create.SetExpiresAt(*token.ExpiresAt) + } + if token.LastUsed != nil { + create.SetLastUsed(*token.LastUsed) + } + + if _, err := create.Save(ctx); err != nil { + return mapError(err) + } + return nil +} + +// GetUserAccessToken retrieves a user access token by ID. +func (s *ExternalStore) GetUserAccessToken(ctx context.Context, id string) (*store.UserAccessToken, error) { + uid, err := parseGetID(id) + if err != nil { + return nil, err + } + e, err := s.client.UserAccessToken.Get(ctx, uid) + if err != nil { + return nil, mapError(err) + } + return entUATToStore(e), nil +} + +// GetUserAccessTokenByHash retrieves a user access token by its key hash. +func (s *ExternalStore) GetUserAccessTokenByHash(ctx context.Context, hash string) (*store.UserAccessToken, error) { + e, err := s.client.UserAccessToken.Query(). + Where(useraccesstoken.KeyHashEQ(hash)). + Only(ctx) + if err != nil { + return nil, mapError(err) + } + return entUATToStore(e), nil +} + +// UpdateUserAccessTokenLastUsed updates the last used timestamp. +// Mirrors the SQLite store: a missing token is not treated as an error. +func (s *ExternalStore) UpdateUserAccessTokenLastUsed(ctx context.Context, id string) error { + uid, err := parseUUID(id) + if err != nil { + return err + } + _, err = s.client.UserAccessToken.Update(). + Where(useraccesstoken.IDEQ(uid)). + SetLastUsed(time.Now()). + Save(ctx) + return err +} + +// RevokeUserAccessToken marks a token as revoked. +func (s *ExternalStore) RevokeUserAccessToken(ctx context.Context, id string) error { + uid, err := parseUUID(id) + if err != nil { + return err + } + if _, err := s.client.UserAccessToken.UpdateOneID(uid).SetRevoked(true).Save(ctx); err != nil { + return mapError(err) + } + return nil +} + +// DeleteUserAccessToken permanently removes a token by ID. +func (s *ExternalStore) DeleteUserAccessToken(ctx context.Context, id string) error { + uid, err := parseUUID(id) + if err != nil { + return err + } + if err := s.client.UserAccessToken.DeleteOneID(uid).Exec(ctx); err != nil { + return mapError(err) + } + return nil +} + +// ListUserAccessTokens returns all tokens for a user, newest first. +func (s *ExternalStore) ListUserAccessTokens(ctx context.Context, userID string) ([]store.UserAccessToken, error) { + uid, err := parseUUID(userID) + if err != nil { + return nil, err + } + rows, err := s.client.UserAccessToken.Query(). + Where(useraccesstoken.UserIDEQ(uid)). + Order(useraccesstoken.ByCreated(entDesc())). + All(ctx) + if err != nil { + return nil, err + } + out := make([]store.UserAccessToken, 0, len(rows)) + for _, e := range rows { + out = append(out, *entUATToStore(e)) + } + return out, nil +} + +// CountUserAccessTokens returns the number of active (non-revoked) tokens for a user. +func (s *ExternalStore) CountUserAccessTokens(ctx context.Context, userID string) (int, error) { + uid, err := parseUUID(userID) + if err != nil { + return 0, err + } + return s.client.UserAccessToken.Query(). + Where( + useraccesstoken.UserIDEQ(uid), + useraccesstoken.RevokedEQ(false), + ). + Count(ctx) +} + +// Ensure ExternalStore satisfies the external-identity store sub-interfaces. +var ( + _ store.GCPServiceAccountStore = (*ExternalStore)(nil) + _ store.GitHubInstallationStore = (*ExternalStore)(nil) + _ store.UserAccessTokenStore = (*ExternalStore)(nil) +) diff --git a/pkg/store/entadapter/external_store_test.go b/pkg/store/entadapter/external_store_test.go new file mode 100644 index 000000000..5a715de44 --- /dev/null +++ b/pkg/store/entadapter/external_store_test.go @@ -0,0 +1,225 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build !no_sqlite + +package entadapter + +import ( + "context" + "testing" + "time" + + "github.com/GoogleCloudPlatform/scion/pkg/store" + "github.com/GoogleCloudPlatform/scion/pkg/store/enttest" + "github.com/google/uuid" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func newTestExternalStore(t *testing.T) *ExternalStore { + t.Helper() + client := enttest.NewClient(t) + return NewExternalStore(client) +} + +func TestExternalStore_GCPServiceAccountCRUD(t *testing.T) { + ctx := context.Background() + s := newTestExternalStore(t) + + projectID := uuid.NewString() + sa := &store.GCPServiceAccount{ + ID: uuid.NewString(), + Scope: "project", + ScopeID: projectID, + Email: "agent@project.iam.gserviceaccount.com", + ProjectID: projectID, + DisplayName: "Worker SA", + DefaultScopes: []string{"https://www.googleapis.com/auth/cloud-platform"}, + Verified: true, + VerifiedAt: time.Now().UTC().Truncate(time.Second), + CreatedBy: "tester", + Managed: true, + ManagedBy: "hub-1", + } + require.NoError(t, s.CreateGCPServiceAccount(ctx, sa)) + + got, err := s.GetGCPServiceAccount(ctx, sa.ID) + require.NoError(t, err) + assert.Equal(t, sa.Email, got.Email) + assert.Equal(t, []string{"https://www.googleapis.com/auth/cloud-platform"}, got.DefaultScopes) + assert.True(t, got.Verified) + assert.False(t, got.VerifiedAt.IsZero()) + assert.True(t, got.Managed) + + // Duplicate (email, scope, scope_id) -> ErrAlreadyExists. + dup := *sa + dup.ID = uuid.NewString() + assert.ErrorIs(t, s.CreateGCPServiceAccount(ctx, &dup), store.ErrAlreadyExists) + + // Update. + got.DisplayName = "Renamed SA" + got.Verified = false + got.VerifiedAt = time.Time{} + require.NoError(t, s.UpdateGCPServiceAccount(ctx, got)) + got, err = s.GetGCPServiceAccount(ctx, sa.ID) + require.NoError(t, err) + assert.Equal(t, "Renamed SA", got.DisplayName) + assert.False(t, got.Verified) + assert.True(t, got.VerifiedAt.IsZero()) + + // Filter + count. + managed := true + list, err := s.ListGCPServiceAccounts(ctx, store.GCPServiceAccountFilter{Scope: "project", Managed: &managed}) + require.NoError(t, err) + assert.Len(t, list, 1) + + count, err := s.CountGCPServiceAccounts(ctx, store.GCPServiceAccountFilter{Scope: "project"}) + require.NoError(t, err) + assert.Equal(t, 1, count) + + // Delete. + require.NoError(t, s.DeleteGCPServiceAccount(ctx, sa.ID)) + _, err = s.GetGCPServiceAccount(ctx, sa.ID) + assert.ErrorIs(t, err, store.ErrNotFound) + assert.ErrorIs(t, s.DeleteGCPServiceAccount(ctx, sa.ID), store.ErrNotFound) +} + +func TestExternalStore_GitHubInstallation(t *testing.T) { + ctx := context.Background() + s := newTestExternalStore(t) + + inst := &store.GitHubInstallation{ + InstallationID: 12345, + AccountLogin: "acme", + AccountType: "Organization", + AppID: 999, + Repositories: []string{"acme/repo1", "acme/repo2"}, + } + require.NoError(t, s.CreateGitHubInstallation(ctx, inst)) + + got, err := s.GetGitHubInstallation(ctx, 12345) + require.NoError(t, err) + assert.Equal(t, "acme", got.AccountLogin) + assert.Equal(t, store.GitHubInstallationStatusActive, got.Status) + assert.Equal(t, []string{"acme/repo1", "acme/repo2"}, got.Repositories) + + // Create with the same installation_id is an idempotent no-op (INSERT OR IGNORE). + dup := &store.GitHubInstallation{ + InstallationID: 12345, + AccountLogin: "changed", + AppID: 1, + } + require.NoError(t, s.CreateGitHubInstallation(ctx, dup)) + got, err = s.GetGitHubInstallation(ctx, 12345) + require.NoError(t, err) + assert.Equal(t, "acme", got.AccountLogin, "duplicate create must not overwrite") + + // Repository lookup. + found, err := s.GetInstallationForRepository(ctx, "acme/repo2") + require.NoError(t, err) + assert.Equal(t, int64(12345), found.InstallationID) + + _, err = s.GetInstallationForRepository(ctx, "other/repo") + assert.ErrorIs(t, err, store.ErrNotFound) + + // Update. + got.Repositories = []string{"acme/repo3"} + got.Status = store.GitHubInstallationStatusSuspended + require.NoError(t, s.UpdateGitHubInstallation(ctx, got)) + got, err = s.GetGitHubInstallation(ctx, 12345) + require.NoError(t, err) + assert.Equal(t, []string{"acme/repo3"}, got.Repositories) + assert.Equal(t, store.GitHubInstallationStatusSuspended, got.Status) + + // List filter by status. + active, err := s.ListGitHubInstallations(ctx, store.GitHubInstallationFilter{Status: store.GitHubInstallationStatusActive}) + require.NoError(t, err) + assert.Empty(t, active) + + // Delete. + require.NoError(t, s.DeleteGitHubInstallation(ctx, 12345)) + _, err = s.GetGitHubInstallation(ctx, 12345) + assert.ErrorIs(t, err, store.ErrNotFound) +} + +func TestExternalStore_UserAccessToken(t *testing.T) { + ctx := context.Background() + s := newTestExternalStore(t) + + userID := uuid.NewString() + projectID := uuid.NewString() + expires := time.Now().Add(24 * time.Hour).UTC().Truncate(time.Second) + + token := &store.UserAccessToken{ + ID: uuid.NewString(), + UserID: userID, + Name: "ci-token", + Prefix: "scion_pat_abc", + KeyHash: "hash-1", + ProjectID: projectID, + Scopes: []string{"project:read", "agent:list"}, + ExpiresAt: &expires, + } + require.NoError(t, s.CreateUserAccessToken(ctx, token)) + + got, err := s.GetUserAccessToken(ctx, token.ID) + require.NoError(t, err) + assert.Equal(t, "ci-token", got.Name) + assert.Equal(t, []string{"project:read", "agent:list"}, got.Scopes) + require.NotNil(t, got.ExpiresAt) + + // Lookup by hash. + byHash, err := s.GetUserAccessTokenByHash(ctx, "hash-1") + require.NoError(t, err) + assert.Equal(t, token.ID, byHash.ID) + + _, err = s.GetUserAccessTokenByHash(ctx, "missing") + assert.ErrorIs(t, err, store.ErrNotFound) + + // Duplicate hash -> ErrAlreadyExists. + dup := *token + dup.ID = uuid.NewString() + assert.ErrorIs(t, s.CreateUserAccessToken(ctx, &dup), store.ErrAlreadyExists) + + // LastUsed update. + require.NoError(t, s.UpdateUserAccessTokenLastUsed(ctx, token.ID)) + got, err = s.GetUserAccessToken(ctx, token.ID) + require.NoError(t, err) + require.NotNil(t, got.LastUsed) + + // Count active tokens. + count, err := s.CountUserAccessTokens(ctx, userID) + require.NoError(t, err) + assert.Equal(t, 1, count) + + // Revoke removes from active count but the row still exists. + require.NoError(t, s.RevokeUserAccessToken(ctx, token.ID)) + got, err = s.GetUserAccessToken(ctx, token.ID) + require.NoError(t, err) + assert.True(t, got.Revoked) + count, err = s.CountUserAccessTokens(ctx, userID) + require.NoError(t, err) + assert.Equal(t, 0, count) + + list, err := s.ListUserAccessTokens(ctx, userID) + require.NoError(t, err) + assert.Len(t, list, 1) + + // Delete. + require.NoError(t, s.DeleteUserAccessToken(ctx, token.ID)) + _, err = s.GetUserAccessToken(ctx, token.ID) + assert.ErrorIs(t, err, store.ErrNotFound) + assert.ErrorIs(t, s.RevokeUserAccessToken(ctx, token.ID), store.ErrNotFound) +} diff --git a/pkg/store/entadapter/group_store.go b/pkg/store/entadapter/group_store.go index 48e768ddd..3f75aac8e 100644 --- a/pkg/store/entadapter/group_store.go +++ b/pkg/store/entadapter/group_store.go @@ -18,6 +18,7 @@ package entadapter import ( "context" "fmt" + "strings" "github.com/GoogleCloudPlatform/scion/pkg/ent" "github.com/GoogleCloudPlatform/scion/pkg/ent/agent" @@ -47,6 +48,19 @@ func parseUUID(s string) (uuid.UUID, error) { return uid, nil } +// parseGetID parses a primary-key identifier for a get-by-id lookup. A malformed +// identifier cannot match any UUID primary key, so it is reported as +// store.ErrNotFound — matching the raw-SQL store, where such a lookup simply +// returned no row (callers like resolveTemplate rely on ErrNotFound to fall back +// to slug-based resolution). +func parseGetID(s string) (uuid.UUID, error) { + uid, err := uuid.Parse(s) + if err != nil { + return uuid.Nil, store.ErrNotFound + } + return uid, nil +} + // mapError converts Ent errors to store errors. func mapError(err error) error { if err == nil { @@ -56,6 +70,16 @@ func mapError(err error) error { return store.ErrNotFound } if ent.IsConstraintError(err) { + // Both unique-constraint and foreign-key violations surface as Ent + // constraint errors, but they mean very different things: a unique + // violation is a duplicate (ErrAlreadyExists), while a foreign-key + // violation is a reference to a row that does not exist (ErrInvalidInput). + // Mapping both to ErrAlreadyExists produced a misleading "already exists" + // (HTTP 409) for what is really a bad reference. + msg := strings.ToLower(err.Error()) + if strings.Contains(msg, "foreign key") || strings.Contains(msg, "sqlstate 23503") { + return fmt.Errorf("%w: %v", store.ErrInvalidInput, err) + } return store.ErrAlreadyExists } return err @@ -164,7 +188,7 @@ func (s *GroupStore) CreateGroup(ctx context.Context, g *store.Group) error { // GetGroup retrieves a group by ID. func (s *GroupStore) GetGroup(ctx context.Context, id string) (*store.Group, error) { - uid, err := parseUUID(id) + uid, err := parseGetID(id) if err != nil { return nil, err } diff --git a/pkg/store/entadapter/group_store_test.go b/pkg/store/entadapter/group_store_test.go index 7f8109b64..eca0dfd3f 100644 --- a/pkg/store/entadapter/group_store_test.go +++ b/pkg/store/entadapter/group_store_test.go @@ -20,8 +20,8 @@ import ( "context" "testing" - "github.com/GoogleCloudPlatform/scion/pkg/ent/entc" "github.com/GoogleCloudPlatform/scion/pkg/store" + "github.com/GoogleCloudPlatform/scion/pkg/store/enttest" "github.com/google/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -29,13 +29,10 @@ import ( func newTestGroupStore(t *testing.T) *GroupStore { t.Helper() - client, err := entc.OpenSQLite("file:" + t.Name() + "?mode=memory&cache=shared") - require.NoError(t, err) - t.Cleanup(func() { client.Close() }) - require.NoError(t, entc.AutoMigrate(context.Background(), client)) + client := enttest.NewClient(t) // Create a test user for membership tests - _, err = client.User.Create(). + _, err := client.User.Create(). SetID(testUserUID). SetEmail("test@example.com"). SetDisplayName("Test User"). @@ -816,14 +813,9 @@ func TestGetEffectiveGroupsNoMemberships(t *testing.T) { func TestCompositeStoreDelegation(t *testing.T) { // Verify the CompositeStore properly delegates group operations - client, err := entc.OpenSQLite("file:" + t.Name() + "?mode=memory&cache=shared") - require.NoError(t, err) - t.Cleanup(func() { client.Close() }) - require.NoError(t, entc.AutoMigrate(context.Background(), client)) + client := enttest.NewClient(t) - // We use nil as the base store since we're only testing group methods - // and they should all go to the Ent adapter. - composite := NewCompositeStore(nil, client) + composite := NewCompositeStore(client) ctx := context.Background() g := &store.Group{ @@ -832,7 +824,7 @@ func TestCompositeStoreDelegation(t *testing.T) { Slug: "composite-test", } - err = composite.CreateGroup(ctx, g) + err := composite.CreateGroup(ctx, g) require.NoError(t, err) got, err := composite.GetGroup(ctx, g.ID) diff --git a/pkg/store/entadapter/locking.go b/pkg/store/entadapter/locking.go new file mode 100644 index 000000000..1e76c3c69 --- /dev/null +++ b/pkg/store/entadapter/locking.go @@ -0,0 +1,254 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package entadapter + +import ( + "context" + "database/sql" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect" + + "github.com/GoogleCloudPlatform/scion/pkg/store" +) + +// advisoryLockTimeout bounds the two short, non-blocking database operations the +// advisory lock performs: checking a connection out of the pool + running +// pg_try_advisory_lock on acquire, and running pg_advisory_unlock on release. +// +// It is deliberately MUCH shorter than the scheduler's 55s per-handler timeout. +// Both operations are expected to complete in milliseconds: pg_try_advisory_lock +// never waits on the lock (it returns immediately), and checking out a +// connection only blocks when the pool has no usable connection to hand back. +// +// Binding acquire to a short deadline keeps a single bad tick cheap. If the pool +// cannot produce a healthy connection quickly we want to fail this tick fast and +// retry on the next one, NOT block a scheduler goroutine (and its pending pool +// connection request) for nearly the whole 55s window. Letting acquisition hang +// for ~55s lets slow ticks overlap across the 60s scheduler interval and across +// the several singleton handlers that fire each minute, which compounds pool +// pressure instead of shedding it. +// +// Binding release with its own fresh deadline (rather than context.Background) +// guarantees the unlock cannot hang forever on a connection that died while the +// lock was held, which would otherwise prevent conn.Close() from ever running +// and leak the connection out of the pool permanently. +const advisoryLockTimeout = 5 * time.Second + +// This file implements the dialect-aware cluster-coordination primitives that +// let N stateless hub processes share one database safely (multi-replica +// Postgres, D3). Every helper degrades to a correct single-process no-op on +// SQLite, where there is only ever one writer. +// +// Compile-time assertions that the Ent-backed store provides the optional +// cluster-coordination capabilities. AdvisoryLocker lives on CompositeStore; +// ScheduledEventClaimer is provided by the embedded ScheduleStore and is thus +// promoted onto CompositeStore as well. +var ( + _ store.AdvisoryLocker = (*CompositeStore)(nil) + _ store.ScheduledEventClaimer = (*ScheduleStore)(nil) + _ store.ScheduledEventClaimer = (*CompositeStore)(nil) +) + +// isPostgres reports whether the shared Ent client is talking to Postgres. +func (c *CompositeStore) isPostgres() bool { + return c.client.Driver().Dialect() == dialect.Postgres +} + +// noopRelease is returned whenever there is nothing to unlock (SQLite, or a lock +// that was not acquired). It is always safe to call. +func noopRelease() error { return nil } + +// TryAdvisoryLock acquires a cluster-wide advisory lock without blocking. +// +// On Postgres it grabs a dedicated *sql.Conn from the pool and runs +// pg_try_advisory_lock(key) on it. The lock is a SESSION-level lock, so it is +// held for exactly as long as that connection stays checked out: the returned +// release func runs pg_advisory_unlock(key) on the same connection and then +// returns it to the pool. Holding the connection for the duration of the +// critical section is what keeps the lock alive, so callers must keep the work +// short and always call release. +// +// On SQLite (and any non-Postgres backend) the lock is a no-op that always +// succeeds: the single-writer model already guarantees the work runs on one +// process at a time. +func (c *CompositeStore) TryAdvisoryLock(ctx context.Context, key store.AdvisoryLockKey) (bool, func() error, error) { + if !c.isPostgres() { + return true, noopRelease, nil + } + + db := c.DB() + if db == nil { + // No *sql.DB to lock against; fail open to single-process behavior + // rather than blocking cluster work. + return true, noopRelease, nil + } + + // Bound connection checkout + the try-lock query to a short deadline derived + // from ctx (but never longer than advisoryLockTimeout). A healthy pool serves + // these in milliseconds; if it cannot, we fail this tick fast and let the next + // one retry rather than parking a scheduler goroutine for the full 55s. + acquireCtx, cancelAcquire := context.WithTimeout(ctx, advisoryLockTimeout) + defer cancelAcquire() + + conn, err := db.Conn(acquireCtx) + if err != nil { + return false, noopRelease, fmt.Errorf("advisory lock: acquiring connection: %w", err) + } + + var acquired bool + // pg_try_advisory_lock returns immediately: true if the lock was granted, + // false if it is already held (by this or another session). + if err := conn.QueryRowContext(acquireCtx, "SELECT pg_try_advisory_lock($1)", int64(key)).Scan(&acquired); err != nil { + _ = conn.Close() + return false, noopRelease, fmt.Errorf("advisory lock: pg_try_advisory_lock(%d): %w", int64(key), err) + } + + if !acquired { + // Another replica holds it. Return the connection to the pool now. + _ = conn.Close() + return false, noopRelease, nil + } + + // We own the lock. release unlocks on the same connection, then frees it. + // cancelAcquire above only tears down acquireCtx; it does NOT close conn, so + // the session (and therefore the lock) stays alive until release runs. + release := func() error { + // Use a fresh, bounded context detached from the critical section's ctx + // so the unlock still runs even if that ctx was cancelled, but cannot + // hang forever on a connection that silently died while we held the + // lock. Without the bound, a dead connection would block this Exec + // indefinitely, conn.Close() below would never run, and the connection + // would leak out of the pool permanently. Closing the connection would + // also drop the session lock, but unlocking explicitly is cleaner and + // lets the connection be reused. + unlockCtx, cancel := context.WithTimeout(context.Background(), advisoryLockTimeout) + defer cancel() + _, unlockErr := conn.ExecContext(unlockCtx, "SELECT pg_advisory_unlock($1)", int64(key)) + closeErr := conn.Close() + if unlockErr != nil { + return fmt.Errorf("advisory lock: pg_advisory_unlock(%d): %w", int64(key), unlockErr) + } + return closeErr + } + return true, release, nil +} + +// isSerializationFailure reports whether err is a Postgres serialization failure +// that warrants a retry: SQLSTATE 40001 (serialization_failure) or 40P01 +// (deadlock_detected). It matches on the SQLSTATE string carried in the error +// message so it does not need a hard dependency on the pgx error type. +func isSerializationFailure(err error) bool { + if err == nil { + return false + } + type sqlStater interface{ SQLState() string } + var ss sqlStater + if errors.As(err, &ss) { + switch ss.SQLState() { + case "40001", "40P01": + return true + } + } + msg := err.Error() + return contains(msg, "40001") || contains(msg, "40P01") || + contains(msg, "serialization") || contains(msg, "deadlock detected") +} + +// contains is a tiny substring check kept local to avoid importing strings for a +// single call. +func contains(haystack, needle string) bool { + if len(needle) == 0 { + return true + } + for i := 0; i+len(needle) <= len(haystack); i++ { + if haystack[i:i+len(needle)] == needle { + return true + } + } + return false +} + +// maxSerializableRetries bounds the retry loop so a pathologically contended +// transaction cannot spin forever. +const maxSerializableRetries = 5 + +// RunSerializable runs fn inside a transaction and, on Postgres, retries it when +// the transaction aborts with a serialization failure (SQLSTATE 40001/40P01). +// +// It is the multi-row-invariant primitive from P3-4: use it when correctness +// depends on a set of rows being read and written as one atomic snapshot and the +// invariant cannot be reduced to a single-row state_version CAS or a SELECT ... +// FOR UPDATE critical section. +// +// fn MUST be idempotent — it can be invoked more than once. It receives the +// transaction it must use for all its statements; using the ambient pooled +// client instead would escape the serializable snapshot. +// +// - Postgres: BEGIN ISOLATION LEVEL SERIALIZABLE; on commit failure with a +// serialization error, the whole closure is retried up to +// maxSerializableRetries times. +// - SQLite: a single plain transaction with no retry. SQLite executes writes +// serially, so 40001 cannot occur and the SERIALIZABLE escalation is +// unnecessary. +func (c *CompositeStore) RunSerializable(ctx context.Context, fn func(ctx context.Context, tx *sql.Tx) error) error { + db := c.DB() + if db == nil { + return fmt.Errorf("RunSerializable: store is not backed by a *sql.DB") + } + + opts := &sql.TxOptions{} + if c.isPostgres() { + opts.Isolation = sql.LevelSerializable + } + + var lastErr error + attempts := 1 + if c.isPostgres() { + attempts = maxSerializableRetries + } + + for attempt := 0; attempt < attempts; attempt++ { + tx, err := db.BeginTx(ctx, opts) + if err != nil { + if isSerializationFailure(err) { + lastErr = err + continue + } + return err + } + + if err := fn(ctx, tx); err != nil { + _ = tx.Rollback() + if isSerializationFailure(err) { + lastErr = err + continue + } + return err + } + + if err := tx.Commit(); err != nil { + if isSerializationFailure(err) { + lastErr = err + continue + } + return err + } + return nil + } + return fmt.Errorf("RunSerializable: exhausted %d attempts: %w", attempts, lastErr) +} diff --git a/pkg/store/entadapter/locking_test.go b/pkg/store/entadapter/locking_test.go new file mode 100644 index 000000000..6babe0776 --- /dev/null +++ b/pkg/store/entadapter/locking_test.go @@ -0,0 +1,158 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build !no_sqlite + +package entadapter + +import ( + "context" + "database/sql" + "errors" + "sync" + "testing" + + "github.com/GoogleCloudPlatform/scion/pkg/store" + "github.com/google/uuid" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// On SQLite the advisory lock is a no-op that always succeeds, because the +// single-writer model already makes the guarded work effectively singleton. +func TestTryAdvisoryLock_SQLiteAlwaysAcquires(t *testing.T) { + c := newTestCompositeStore(t) + ctx := context.Background() + + acquired, release, err := c.TryAdvisoryLock(ctx, store.LockScheduleEvaluator) + require.NoError(t, err) + assert.True(t, acquired, "SQLite advisory lock must always acquire") + require.NotNil(t, release) + require.NoError(t, release()) + + // A second concurrent acquisition also succeeds (no real lock on SQLite). + acquired2, release2, err := c.TryAdvisoryLock(ctx, store.LockScheduleEvaluator) + require.NoError(t, err) + assert.True(t, acquired2) + require.NoError(t, release2()) +} + +// The store satisfies the optional AdvisoryLocker capability used by the hub +// scheduler's singleton gating. +func TestCompositeStore_ImplementsAdvisoryLocker(t *testing.T) { + var _ store.AdvisoryLocker = newTestCompositeStore(t) +} + +// RunSerializable runs the closure inside a transaction and commits it on +// SQLite (no isolation escalation, no retry). +func TestRunSerializable_CommitsOnSQLite(t *testing.T) { + c := newTestCompositeStore(t) + ctx := context.Background() + + calls := 0 + err := c.RunSerializable(ctx, func(ctx context.Context, tx *sql.Tx) error { + calls++ + // A trivial read proves the tx is usable. + var one int + return tx.QueryRowContext(ctx, "SELECT 1").Scan(&one) + }) + require.NoError(t, err) + assert.Equal(t, 1, calls, "SQLite must run the closure exactly once (no retry)") +} + +// A non-serialization error from the closure is returned verbatim and the +// transaction is rolled back (no retry loop on SQLite). +func TestRunSerializable_PropagatesError(t *testing.T) { + c := newTestCompositeStore(t) + ctx := context.Background() + + sentinel := errors.New("boom") + calls := 0 + err := c.RunSerializable(ctx, func(ctx context.Context, tx *sql.Tx) error { + calls++ + return sentinel + }) + require.ErrorIs(t, err, sentinel) + assert.Equal(t, 1, calls) +} + +// isSerializationFailure recognizes the Postgres serialization/deadlock SQLSTATEs +// (used to drive the retry loop) without depending on the pgx error type. +func TestIsSerializationFailure(t *testing.T) { + assert.False(t, isSerializationFailure(nil)) + assert.False(t, isSerializationFailure(errors.New("syntax error"))) + assert.True(t, isSerializationFailure(errors.New("pq: could not serialize access due to concurrent update (SQLSTATE 40001)"))) + assert.True(t, isSerializationFailure(errors.New("ERROR: deadlock detected (SQLSTATE 40P01)"))) +} + +// ClaimScheduledEvent is the multi-replica dedup primitive: exactly one caller +// wins the pending->fired transition; a second attempt loses. +func TestClaimScheduledEvent_ExactlyOnce(t *testing.T) { + s := newTestScheduleStore(t) + ctx := context.Background() + + evt := newTestScheduledEvent(uuid.NewString()) + require.NoError(t, s.CreateScheduledEvent(ctx, evt)) + + won, err := s.ClaimScheduledEvent(ctx, evt.ID, store.ScheduledEventFired) + require.NoError(t, err) + assert.True(t, won, "first claim must win") + + // Second claim loses: the event is no longer pending. + won2, err := s.ClaimScheduledEvent(ctx, evt.ID, store.ScheduledEventFired) + require.NoError(t, err) + assert.False(t, won2, "second claim must lose") + + got, err := s.GetScheduledEvent(ctx, evt.ID) + require.NoError(t, err) + assert.Equal(t, store.ScheduledEventFired, got.Status) + assert.NotNil(t, got.FiredAt) +} + +// Claiming a non-existent event is a clean loss, not an error. +func TestClaimScheduledEvent_MissingLoses(t *testing.T) { + s := newTestScheduleStore(t) + won, err := s.ClaimScheduledEvent(context.Background(), uuid.NewString(), store.ScheduledEventFired) + require.NoError(t, err) + assert.False(t, won) +} + +// Under concurrent claims of the same event, exactly one wins. This mirrors two +// replicas recovering the same pending event on startup. +func TestClaimScheduledEvent_ConcurrentSingleWinner(t *testing.T) { + s := newTestScheduleStore(t) + ctx := context.Background() + + evt := newTestScheduledEvent(uuid.NewString()) + require.NoError(t, s.CreateScheduledEvent(ctx, evt)) + + const racers = 8 + var wg sync.WaitGroup + var mu sync.Mutex + wins := 0 + wg.Add(racers) + for i := 0; i < racers; i++ { + go func() { + defer wg.Done() + won, err := s.ClaimScheduledEvent(ctx, evt.ID, store.ScheduledEventFired) + if err == nil && won { + mu.Lock() + wins++ + mu.Unlock() + } + }() + } + wg.Wait() + assert.Equal(t, 1, wins, "exactly one concurrent claim must win") +} diff --git a/pkg/store/entadapter/main_test.go b/pkg/store/entadapter/main_test.go new file mode 100644 index 000000000..f35e148df --- /dev/null +++ b/pkg/store/entadapter/main_test.go @@ -0,0 +1,34 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build !no_sqlite + +package entadapter + +import ( + "os" + "testing" + + "github.com/GoogleCloudPlatform/scion/pkg/store/enttest" +) + +// TestMain wires the enttest backend lifecycle so the Postgres integration +// backend can create and drop its per-package ephemeral database. Both calls are +// no-ops in the default SQLite build. +func TestMain(m *testing.M) { + enttest.MainSetup() + code := m.Run() + enttest.MainTeardown() + os.Exit(code) +} diff --git a/pkg/store/entadapter/maintenance_store.go b/pkg/store/entadapter/maintenance_store.go new file mode 100644 index 000000000..7172e90d3 --- /dev/null +++ b/pkg/store/entadapter/maintenance_store.go @@ -0,0 +1,348 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package entadapter + +import ( + "context" + "time" + + entsql "entgo.io/ent/dialect/sql" + "github.com/GoogleCloudPlatform/scion/pkg/ent" + "github.com/GoogleCloudPlatform/scion/pkg/ent/maintenanceoperation" + "github.com/GoogleCloudPlatform/scion/pkg/ent/maintenanceoperationrun" + "github.com/GoogleCloudPlatform/scion/pkg/store" +) + +// MaintenanceStore implements store.MaintenanceStore using the Ent ORM. +type MaintenanceStore struct { + client *ent.Client +} + +// NewMaintenanceStore creates a new Ent-backed MaintenanceStore. +func NewMaintenanceStore(client *ent.Client) *MaintenanceStore { + return &MaintenanceStore{client: client} +} + +// abortResult is the result message stamped on operations/runs that were +// interrupted by a server restart. Kept identical to the SQLite backend. +const abortResult = `{"error":"aborted: server was restarted while operation was running"}` + +// defaultSeedOperations are the built-in maintenance operations and migrations. +// In the SQLite backend these were seeded by migration SQL that minted ids via +// hex(randomblob(...)); here they are seeded in Go with ids generated by the +// Ent schema default (uuid.New) — see SeedMaintenanceOperations. +var defaultSeedOperations = []store.MaintenanceOperation{ + { + Key: "secret-hub-id-migration", + Title: "Secret Hub ID Namespace Migration", + Description: `Migrates hub-scoped secrets from the legacy fixed "hub" scope ID to the per-instance hub ID. Required when upgrading a hub that was created before the hub ID namespacing feature. Only needed for GCP Secret Manager backend.`, + Category: store.MaintenanceCategoryMigration, + }, + { + Key: "pull-images", + Title: "Pull Container Images", + Description: "Pulls the latest container images for all configured harnesses from the image registry.", + Category: store.MaintenanceCategoryOperation, + }, + { + Key: "rebuild-server", + Title: "Rebuild Server from Git", + Description: "Pulls latest code from the repository, rebuilds the server binary and web assets, then restarts the hub service. Equivalent to the fast-deploy mode of gce-start-hub.sh.", + Category: store.MaintenanceCategoryOperation, + }, + { + Key: "rebuild-web", + Title: "Rebuild Web Frontend", + Description: "Rebuilds only the web frontend assets from source without restarting the server binary. Changes take effect on the next page load.", + Category: store.MaintenanceCategoryOperation, + }, + { + Key: "rebuild-container-binaries", + Title: "Rebuild Container Binaries", + Description: "Rebuilds scion and sciontool binaries for Linux containers (make container-binaries). Only available when SCION_DEV_BINARIES is set. Binaries are written to .build/container/ in the source checkout.", + Category: store.MaintenanceCategoryOperation, + }, +} + +// ============================================================================ +// Conversions +// ============================================================================ + +func entMaintenanceOpToStore(e *ent.MaintenanceOperation) *store.MaintenanceOperation { + return &store.MaintenanceOperation{ + ID: e.ID.String(), + Key: e.Key, + Title: e.Title, + Description: e.Description, + Category: e.Category, + Status: e.Status, + CreatedAt: e.Created, + StartedAt: e.StartedAt, + CompletedAt: e.CompletedAt, + StartedBy: e.StartedBy, + Result: e.Result, + Metadata: e.Metadata, + } +} + +func entMaintenanceRunToStore(e *ent.MaintenanceOperationRun) *store.MaintenanceOperationRun { + return &store.MaintenanceOperationRun{ + ID: e.ID.String(), + OperationKey: e.OperationKey, + Status: e.Status, + StartedAt: e.StartedAt, + CompletedAt: e.CompletedAt, + StartedBy: e.StartedBy, + Result: e.Result, + Log: e.Log, + } +} + +// ============================================================================ +// Seed +// ============================================================================ + +// SeedMaintenanceOperations inserts the built-in operations and migrations that +// are missing from the store. It is idempotent and replaces the SQLite +// randomblob() UUID seeds with Go-side ids generated by the Ent default. +func (s *MaintenanceStore) SeedMaintenanceOperations(ctx context.Context) error { + for _, op := range defaultSeedOperations { + exists, err := s.client.MaintenanceOperation.Query(). + Where(maintenanceoperation.KeyEQ(op.Key)). + Exist(ctx) + if err != nil { + return err + } + if exists { + continue + } + // ID is intentionally left unset so the Ent schema default (uuid.New) + // mints it — the Go replacement for the SQLite randomblob() seed. + err = s.client.MaintenanceOperation.Create(). + SetKey(op.Key). + SetTitle(op.Title). + SetDescription(op.Description). + SetCategory(op.Category). + SetStatus(store.MaintenanceStatusPending). + Exec(ctx) + if err != nil && !ent.IsConstraintError(err) { + return err + } + } + return nil +} + +// ============================================================================ +// Maintenance Operation Operations +// ============================================================================ + +// ListMaintenanceOperations returns all registered operations and migrations. +func (s *MaintenanceStore) ListMaintenanceOperations(ctx context.Context) ([]store.MaintenanceOperation, error) { + entities, err := s.client.MaintenanceOperation.Query(). + Order( + maintenanceoperation.ByCategory(), + maintenanceoperation.ByCreated(), + ). + All(ctx) + if err != nil { + return nil, err + } + ops := make([]store.MaintenanceOperation, 0, len(entities)) + for _, e := range entities { + ops = append(ops, *entMaintenanceOpToStore(e)) + } + return ops, nil +} + +// GetMaintenanceOperation returns a single operation by key. +func (s *MaintenanceStore) GetMaintenanceOperation(ctx context.Context, key string) (*store.MaintenanceOperation, error) { + e, err := s.client.MaintenanceOperation.Query(). + Where(maintenanceoperation.KeyEQ(key)). + Only(ctx) + if err != nil { + return nil, mapError(err) + } + return entMaintenanceOpToStore(e), nil +} + +// UpdateMaintenanceOperation updates an operation's status and result fields. +func (s *MaintenanceStore) UpdateMaintenanceOperation(ctx context.Context, op *store.MaintenanceOperation) error { + update := s.client.MaintenanceOperation.Update(). + Where(maintenanceoperation.KeyEQ(op.Key)). + SetStatus(op.Status). + SetStartedBy(op.StartedBy). + SetResult(op.Result). + SetMetadata(op.Metadata) + + if op.StartedAt != nil { + update.SetStartedAt(*op.StartedAt) + } else { + update.ClearStartedAt() + } + if op.CompletedAt != nil { + update.SetCompletedAt(*op.CompletedAt) + } else { + update.ClearCompletedAt() + } + + n, err := update.Save(ctx) + if err != nil { + return mapError(err) + } + if n == 0 { + return store.ErrNotFound + } + return nil +} + +// ============================================================================ +// Maintenance Operation Run Operations +// ============================================================================ + +// CreateMaintenanceRun inserts a new run record. +func (s *MaintenanceStore) CreateMaintenanceRun(ctx context.Context, run *store.MaintenanceOperationRun) error { + create := s.client.MaintenanceOperationRun.Create(). + SetOperationKey(run.OperationKey). + SetStatus(run.Status). + SetLog(run.Log) + + if run.ID != "" { + uid, err := parseUUID(run.ID) + if err != nil { + return err + } + create.SetID(uid) + } + if !run.StartedAt.IsZero() { + create.SetStartedAt(run.StartedAt) + } + if run.CompletedAt != nil { + create.SetCompletedAt(*run.CompletedAt) + } + if run.StartedBy != "" { + create.SetStartedBy(run.StartedBy) + } + if run.Result != "" { + create.SetResult(run.Result) + } + + created, err := create.Save(ctx) + if err != nil { + return mapError(err) + } + run.ID = created.ID.String() + run.StartedAt = created.StartedAt + if run.Status == "" { + run.Status = created.Status + } + return nil +} + +// UpdateMaintenanceRun updates a run's status, result, and log. +func (s *MaintenanceStore) UpdateMaintenanceRun(ctx context.Context, run *store.MaintenanceOperationRun) error { + uid, err := parseUUID(run.ID) + if err != nil { + return err + } + + update := s.client.MaintenanceOperationRun.Update(). + Where(maintenanceoperationrun.IDEQ(uid)). + SetStatus(run.Status). + SetResult(run.Result). + SetLog(run.Log) + + if run.CompletedAt != nil { + update.SetCompletedAt(*run.CompletedAt) + } else { + update.ClearCompletedAt() + } + + n, err := update.Save(ctx) + if err != nil { + return mapError(err) + } + if n == 0 { + return store.ErrNotFound + } + return nil +} + +// GetMaintenanceRun returns a single run by ID. +func (s *MaintenanceStore) GetMaintenanceRun(ctx context.Context, id string) (*store.MaintenanceOperationRun, error) { + uid, err := parseGetID(id) + if err != nil { + return nil, err + } + e, err := s.client.MaintenanceOperationRun.Get(ctx, uid) + if err != nil { + return nil, mapError(err) + } + return entMaintenanceRunToStore(e), nil +} + +// ListMaintenanceRuns returns runs for a given operation key, ordered by +// started_at DESC. +func (s *MaintenanceStore) ListMaintenanceRuns(ctx context.Context, operationKey string, limit int) ([]store.MaintenanceOperationRun, error) { + if limit <= 0 { + limit = 20 + } + entities, err := s.client.MaintenanceOperationRun.Query(). + Where(maintenanceoperationrun.OperationKeyEQ(operationKey)). + Order(maintenanceoperationrun.ByStartedAt(entsql.OrderDesc())). + Limit(limit). + All(ctx) + if err != nil { + return nil, err + } + runs := make([]store.MaintenanceOperationRun, 0, len(entities)) + for _, e := range entities { + runs = append(runs, *entMaintenanceRunToStore(e)) + } + return runs, nil +} + +// AbortRunningMaintenanceOps transitions any "running" operation runs to +// "failed" and resets stalled "running" migrations back to "pending" so they +// can be retried. Called at server startup to clean up operations interrupted +// by a restart. +func (s *MaintenanceStore) AbortRunningMaintenanceOps(ctx context.Context) (int64, int64, error) { + now := time.Now() + + runs, err := s.client.MaintenanceOperationRun.Update(). + Where(maintenanceoperationrun.StatusEQ(store.MaintenanceStatusRunning)). + SetStatus(store.MaintenanceStatusFailed). + SetCompletedAt(now). + SetResult(abortResult). + Save(ctx) + if err != nil { + return 0, 0, err + } + + migrations, err := s.client.MaintenanceOperation.Update(). + Where( + maintenanceoperation.StatusEQ(store.MaintenanceStatusRunning), + maintenanceoperation.CategoryEQ(store.MaintenanceCategoryMigration), + ). + SetStatus(store.MaintenanceStatusPending). + ClearStartedAt(). + ClearCompletedAt(). + SetResult(abortResult). + Save(ctx) + if err != nil { + return int64(runs), 0, err + } + + return int64(runs), int64(migrations), nil +} diff --git a/pkg/store/entadapter/maintenance_store_test.go b/pkg/store/entadapter/maintenance_store_test.go new file mode 100644 index 000000000..78d2571d4 --- /dev/null +++ b/pkg/store/entadapter/maintenance_store_test.go @@ -0,0 +1,183 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build !no_sqlite + +package entadapter + +import ( + "context" + "testing" + "time" + + "github.com/GoogleCloudPlatform/scion/pkg/store" + "github.com/GoogleCloudPlatform/scion/pkg/store/enttest" + "github.com/google/uuid" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func newTestMaintenanceStore(t *testing.T) *MaintenanceStore { + t.Helper() + client := enttest.NewClient(t) + return NewMaintenanceStore(client) +} + +func TestSeedMaintenanceOperations(t *testing.T) { + s := newTestMaintenanceStore(t) + ctx := context.Background() + + require.NoError(t, s.SeedMaintenanceOperations(ctx)) + + ops, err := s.ListMaintenanceOperations(ctx) + require.NoError(t, err) + assert.Len(t, ops, len(defaultSeedOperations)) + + // Every seeded op must have a valid Go-generated UUID id (not a randomblob). + for _, op := range ops { + _, err := uuid.Parse(op.ID) + assert.NoError(t, err, "seeded op %q should have a valid UUID id", op.Key) + assert.Equal(t, store.MaintenanceStatusPending, op.Status) + } + + // Seeding is idempotent. + require.NoError(t, s.SeedMaintenanceOperations(ctx)) + ops, err = s.ListMaintenanceOperations(ctx) + require.NoError(t, err) + assert.Len(t, ops, len(defaultSeedOperations)) +} + +func TestGetMaintenanceOperationNotFound(t *testing.T) { + s := newTestMaintenanceStore(t) + _, err := s.GetMaintenanceOperation(context.Background(), "does-not-exist") + assert.ErrorIs(t, err, store.ErrNotFound) +} + +func TestUpdateMaintenanceOperation(t *testing.T) { + s := newTestMaintenanceStore(t) + ctx := context.Background() + require.NoError(t, s.SeedMaintenanceOperations(ctx)) + + startedAt := time.Now().UTC().Truncate(time.Second) + op := &store.MaintenanceOperation{ + Key: "pull-images", + Status: store.MaintenanceStatusRunning, + StartedAt: &startedAt, + StartedBy: "admin", + Metadata: `{"foo":"bar"}`, + } + require.NoError(t, s.UpdateMaintenanceOperation(ctx, op)) + + got, err := s.GetMaintenanceOperation(ctx, "pull-images") + require.NoError(t, err) + assert.Equal(t, store.MaintenanceStatusRunning, got.Status) + assert.Equal(t, "admin", got.StartedBy) + assert.Equal(t, `{"foo":"bar"}`, got.Metadata) + require.NotNil(t, got.StartedAt) +} + +func TestUpdateMaintenanceOperationNotFound(t *testing.T) { + s := newTestMaintenanceStore(t) + err := s.UpdateMaintenanceOperation(context.Background(), &store.MaintenanceOperation{ + Key: "ghost", + Status: store.MaintenanceStatusRunning, + }) + assert.ErrorIs(t, err, store.ErrNotFound) +} + +func TestMaintenanceRunRMW(t *testing.T) { + s := newTestMaintenanceStore(t) + ctx := context.Background() + require.NoError(t, s.SeedMaintenanceOperations(ctx)) + + run := &store.MaintenanceOperationRun{ + ID: uuid.NewString(), + OperationKey: "pull-images", + Status: store.MaintenanceStatusRunning, + StartedAt: time.Now().UTC().Truncate(time.Second), + StartedBy: "admin", + Log: "starting", + } + require.NoError(t, s.CreateMaintenanceRun(ctx, run)) + + got, err := s.GetMaintenanceRun(ctx, run.ID) + require.NoError(t, err) + assert.Equal(t, store.MaintenanceStatusRunning, got.Status) + assert.Equal(t, "pull-images", got.OperationKey) + + // Read-modify-write to completed. + completedAt := time.Now().UTC().Truncate(time.Second) + run.Status = store.MaintenanceStatusCompleted + run.CompletedAt = &completedAt + run.Result = `{"ok":true}` + run.Log = "starting\ndone" + require.NoError(t, s.UpdateMaintenanceRun(ctx, run)) + + got, err = s.GetMaintenanceRun(ctx, run.ID) + require.NoError(t, err) + assert.Equal(t, store.MaintenanceStatusCompleted, got.Status) + require.NotNil(t, got.CompletedAt) + assert.Equal(t, `{"ok":true}`, got.Result) + assert.Equal(t, "starting\ndone", got.Log) + + // List runs for the operation. + runs, err := s.ListMaintenanceRuns(ctx, "pull-images", 10) + require.NoError(t, err) + require.Len(t, runs, 1) + assert.Equal(t, run.ID, runs[0].ID) +} + +func TestGetMaintenanceRunNotFound(t *testing.T) { + s := newTestMaintenanceStore(t) + _, err := s.GetMaintenanceRun(context.Background(), uuid.NewString()) + assert.ErrorIs(t, err, store.ErrNotFound) +} + +func TestAbortRunningMaintenanceOps(t *testing.T) { + s := newTestMaintenanceStore(t) + ctx := context.Background() + require.NoError(t, s.SeedMaintenanceOperations(ctx)) + + // A running migration should be reset to pending. + startedAt := time.Now().UTC().Truncate(time.Second) + require.NoError(t, s.UpdateMaintenanceOperation(ctx, &store.MaintenanceOperation{ + Key: "secret-hub-id-migration", + Status: store.MaintenanceStatusRunning, + StartedAt: &startedAt, + })) + + // A running run should be marked failed. + run := &store.MaintenanceOperationRun{ + ID: uuid.NewString(), + OperationKey: "pull-images", + Status: store.MaintenanceStatusRunning, + StartedAt: startedAt, + } + require.NoError(t, s.CreateMaintenanceRun(ctx, run)) + + runs, migrations, err := s.AbortRunningMaintenanceOps(ctx) + require.NoError(t, err) + assert.Equal(t, int64(1), runs) + assert.Equal(t, int64(1), migrations) + + gotRun, err := s.GetMaintenanceRun(ctx, run.ID) + require.NoError(t, err) + assert.Equal(t, store.MaintenanceStatusFailed, gotRun.Status) + require.NotNil(t, gotRun.CompletedAt) + + gotMig, err := s.GetMaintenanceOperation(ctx, "secret-hub-id-migration") + require.NoError(t, err) + assert.Equal(t, store.MaintenanceStatusPending, gotMig.Status) + assert.Nil(t, gotMig.StartedAt) +} diff --git a/pkg/store/entadapter/message_store.go b/pkg/store/entadapter/message_store.go new file mode 100644 index 000000000..1743e1d74 --- /dev/null +++ b/pkg/store/entadapter/message_store.go @@ -0,0 +1,251 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package entadapter + +import ( + "context" + "time" + + entsql "entgo.io/ent/dialect/sql" + "github.com/GoogleCloudPlatform/scion/pkg/ent" + "github.com/GoogleCloudPlatform/scion/pkg/ent/message" + "github.com/GoogleCloudPlatform/scion/pkg/store" +) + +// MessagePublisher is the hook through which newly created messages are +// announced to other hub replicas. On Postgres this is implemented with +// LISTEN/NOTIFY (a pg_notify on a "user_message" channel) so that subscribers +// receive new messages without polling; the SQLite backend leaves it nil. +// +// It is intentionally an interface rather than a hard dependency so the message +// store stays decoupled from the notification transport, and so the publish +// call can be a no-op until the Postgres LISTEN/NOTIFY listener (Wave B) is +// wired in. +type MessagePublisher interface { + // PublishUserMessage announces that msg was persisted. Implementations must + // be best-effort: a publish failure must not fail the originating write. + PublishUserMessage(ctx context.Context, msg *store.Message) error +} + +// MessageStore implements store.MessageStore using the Ent ORM. +type MessageStore struct { + client *ent.Client + // publisher, when non-nil, is notified after each successful CreateMessage. + // See MessagePublisher. + publisher MessagePublisher +} + +// NewMessageStore creates a new Ent-backed MessageStore. +func NewMessageStore(client *ent.Client) *MessageStore { + return &MessageStore{client: client} +} + +// WithPublisher returns a copy of the store that announces newly created +// messages via the given publisher. Used to wire in the Postgres LISTEN/NOTIFY +// transport without changing the store's construction site. +func (s *MessageStore) WithPublisher(p MessagePublisher) *MessageStore { + clone := *s + clone.publisher = p + return &clone +} + +func entMessageToStore(e *ent.Message) *store.Message { + return &store.Message{ + ID: e.ID.String(), + ProjectID: e.ProjectID.String(), + Sender: e.Sender, + SenderID: e.SenderID, + Recipient: e.Recipient, + RecipientID: e.RecipientID, + Msg: e.Msg, + Type: e.Type, + Urgent: e.Urgent, + Broadcasted: e.Broadcasted, + Read: e.Read, + AgentID: e.AgentID, + GroupID: e.GroupID, + CreatedAt: e.Created, + } +} + +// CreateMessage persists a new message and announces it via the publisher. +func (s *MessageStore) CreateMessage(ctx context.Context, msg *store.Message) error { + if msg.ID == "" || msg.ProjectID == "" || msg.Msg == "" { + return store.ErrInvalidInput + } + uid, err := parseUUID(msg.ID) + if err != nil { + return err + } + pid, err := parseUUID(msg.ProjectID) + if err != nil { + return err + } + + create := s.client.Message.Create(). + SetID(uid). + SetProjectID(pid). + SetSender(msg.Sender). + SetSenderID(msg.SenderID). + SetRecipient(msg.Recipient). + SetRecipientID(msg.RecipientID). + SetMsg(msg.Msg). + SetType(msg.Type). + SetUrgent(msg.Urgent). + SetBroadcasted(msg.Broadcasted). + SetRead(msg.Read). + SetAgentID(msg.AgentID). + SetGroupID(msg.GroupID) + + if msg.Type == "" { + create.SetType("instruction") + } + if !msg.CreatedAt.IsZero() { + create.SetCreated(msg.CreatedAt) + } + + created, err := create.Save(ctx) + if err != nil { + return mapError(err) + } + msg.CreatedAt = created.Created + msg.Type = created.Type + + // Design-in: announce the new message for LISTEN/NOTIFY subscribers. + // Best-effort — a publish failure must not fail the write that succeeded. + if s.publisher != nil { + _ = s.publisher.PublishUserMessage(ctx, msg) + } + return nil +} + +// GetMessage returns a single message by ID. +func (s *MessageStore) GetMessage(ctx context.Context, id string) (*store.Message, error) { + uid, err := parseGetID(id) + if err != nil { + return nil, err + } + e, err := s.client.Message.Get(ctx, uid) + if err != nil { + return nil, mapError(err) + } + return entMessageToStore(e), nil +} + +// ListMessages returns messages matching the given filter, ordered by +// created_at DESC. +func (s *MessageStore) ListMessages(ctx context.Context, filter store.MessageFilter, opts store.ListOptions) (*store.ListResult[store.Message], error) { + query := s.client.Message.Query() + + if filter.ProjectID != "" { + pid, err := parseUUID(filter.ProjectID) + if err != nil { + return nil, err + } + query.Where(message.ProjectIDEQ(pid)) + } + if filter.AgentID != "" { + query.Where(message.AgentIDEQ(filter.AgentID)) + } + if filter.RecipientID != "" { + query.Where(message.RecipientIDEQ(filter.RecipientID)) + } + if filter.SenderID != "" { + query.Where(message.SenderIDEQ(filter.SenderID)) + } + if filter.ParticipantID != "" { + query.Where(message.Or( + message.RecipientIDEQ(filter.ParticipantID), + message.SenderIDEQ(filter.ParticipantID), + )) + } + if filter.OnlyUnread { + query.Where(message.ReadEQ(false)) + } + if filter.Type != "" { + query.Where(message.TypeEQ(filter.Type)) + } + + totalCount, err := query.Clone().Count(ctx) + if err != nil { + return nil, err + } + + limit := clampLimit(opts.Limit) + entities, err := query. + Order(message.ByCreated(entsql.OrderDesc())). + Limit(limit + 1). + All(ctx) + if err != nil { + return nil, err + } + + msgs := make([]store.Message, 0, len(entities)) + for _, e := range entities { + msgs = append(msgs, *entMessageToStore(e)) + } + + result := &store.ListResult[store.Message]{TotalCount: totalCount} + if len(msgs) > limit { + result.Items = msgs[:limit] + result.NextCursor = msgs[limit-1].ID + } else { + result.Items = msgs + } + return result, nil +} + +// MarkMessageRead marks a message as read. +func (s *MessageStore) MarkMessageRead(ctx context.Context, id string) error { + uid, err := parseUUID(id) + if err != nil { + return err + } + n, err := s.client.Message.Update(). + Where(message.IDEQ(uid)). + SetRead(true). + Save(ctx) + if err != nil { + return err + } + if n == 0 { + return store.ErrNotFound + } + return nil +} + +// MarkAllMessagesRead marks all messages for a recipient as read. +func (s *MessageStore) MarkAllMessagesRead(ctx context.Context, recipientID string) error { + _, err := s.client.Message.Update(). + Where(message.RecipientIDEQ(recipientID)). + SetRead(true). + Save(ctx) + return err +} + +// PurgeOldMessages removes read messages older than readCutoff and unread +// messages older than unreadCutoff. Returns the number of messages removed. +func (s *MessageStore) PurgeOldMessages(ctx context.Context, readCutoff time.Time, unreadCutoff time.Time) (int, error) { + n, err := s.client.Message.Delete(). + Where(message.Or( + message.And(message.ReadEQ(true), message.CreatedLT(readCutoff)), + message.And(message.ReadEQ(false), message.CreatedLT(unreadCutoff)), + )). + Exec(ctx) + if err != nil { + return 0, err + } + return n, nil +} diff --git a/pkg/store/entadapter/message_store_test.go b/pkg/store/entadapter/message_store_test.go new file mode 100644 index 000000000..05fb8d333 --- /dev/null +++ b/pkg/store/entadapter/message_store_test.go @@ -0,0 +1,202 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build !no_sqlite + +package entadapter + +import ( + "context" + "testing" + "time" + + "github.com/GoogleCloudPlatform/scion/pkg/store" + "github.com/GoogleCloudPlatform/scion/pkg/store/enttest" + "github.com/google/uuid" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func newTestMessageStore(t *testing.T) *MessageStore { + t.Helper() + client := enttest.NewClient(t) + return NewMessageStore(client) +} + +func newTestMessage(projectID, recipientID string) *store.Message { + return &store.Message{ + ID: uuid.NewString(), + ProjectID: projectID, + Sender: "user:alice", + SenderID: "sender-1", + Recipient: "agent:coder", + RecipientID: recipientID, + Msg: "Please fix the auth module.", + Type: "instruction", + AgentID: recipientID, + } +} + +func TestMessageCRUD(t *testing.T) { + s := newTestMessageStore(t) + ctx := context.Background() + projectID := uuid.NewString() + + msg := newTestMessage(projectID, "agent-1") + require.NoError(t, s.CreateMessage(ctx, msg)) + assert.False(t, msg.CreatedAt.IsZero()) + + got, err := s.GetMessage(ctx, msg.ID) + require.NoError(t, err) + assert.Equal(t, msg.ID, got.ID) + assert.Equal(t, projectID, got.ProjectID) + assert.Equal(t, "user:alice", got.Sender) + assert.Equal(t, "Please fix the auth module.", got.Msg) + assert.Equal(t, "instruction", got.Type) + assert.False(t, got.Read) +} + +func TestMessageGetNotFound(t *testing.T) { + s := newTestMessageStore(t) + _, err := s.GetMessage(context.Background(), uuid.NewString()) + assert.ErrorIs(t, err, store.ErrNotFound) +} + +func TestMessageInvalidInput(t *testing.T) { + s := newTestMessageStore(t) + err := s.CreateMessage(context.Background(), &store.Message{ID: uuid.NewString()}) + assert.ErrorIs(t, err, store.ErrInvalidInput) +} + +func TestMarkMessageRead(t *testing.T) { + s := newTestMessageStore(t) + ctx := context.Background() + msg := newTestMessage(uuid.NewString(), "agent-1") + require.NoError(t, s.CreateMessage(ctx, msg)) + + require.NoError(t, s.MarkMessageRead(ctx, msg.ID)) + got, err := s.GetMessage(ctx, msg.ID) + require.NoError(t, err) + assert.True(t, got.Read) + + assert.ErrorIs(t, s.MarkMessageRead(ctx, uuid.NewString()), store.ErrNotFound) +} + +func TestMarkAllMessagesRead(t *testing.T) { + s := newTestMessageStore(t) + ctx := context.Background() + projectID := uuid.NewString() + recipient := "agent-1" + + for i := 0; i < 3; i++ { + require.NoError(t, s.CreateMessage(ctx, newTestMessage(projectID, recipient))) + } + // A message for a different recipient must stay unread. + other := newTestMessage(projectID, "agent-2") + require.NoError(t, s.CreateMessage(ctx, other)) + + require.NoError(t, s.MarkAllMessagesRead(ctx, recipient)) + + res, err := s.ListMessages(ctx, store.MessageFilter{RecipientID: recipient, OnlyUnread: true}, store.ListOptions{}) + require.NoError(t, err) + assert.Equal(t, 0, res.TotalCount) + + got, err := s.GetMessage(ctx, other.ID) + require.NoError(t, err) + assert.False(t, got.Read) +} + +func TestListMessagesFilters(t *testing.T) { + s := newTestMessageStore(t) + ctx := context.Background() + projectID := uuid.NewString() + + m1 := newTestMessage(projectID, "agent-1") + m1.SenderID = "user-x" + require.NoError(t, s.CreateMessage(ctx, m1)) + + m2 := newTestMessage(projectID, "agent-2") + m2.SenderID = "user-x" + require.NoError(t, s.CreateMessage(ctx, m2)) + + // Filter by recipient. + res, err := s.ListMessages(ctx, store.MessageFilter{RecipientID: "agent-1"}, store.ListOptions{}) + require.NoError(t, err) + assert.Equal(t, 1, res.TotalCount) + + // ParticipantID matches sender or recipient. + res, err = s.ListMessages(ctx, store.MessageFilter{ParticipantID: "user-x"}, store.ListOptions{}) + require.NoError(t, err) + assert.Equal(t, 2, res.TotalCount) + + res, err = s.ListMessages(ctx, store.MessageFilter{ParticipantID: "agent-2"}, store.ListOptions{}) + require.NoError(t, err) + assert.Equal(t, 1, res.TotalCount) +} + +func TestPurgeOldMessages(t *testing.T) { + s := newTestMessageStore(t) + ctx := context.Background() + projectID := uuid.NewString() + + oldRead := newTestMessage(projectID, "agent-1") + oldRead.Read = true + oldRead.CreatedAt = time.Now().Add(-72 * time.Hour).UTC().Truncate(time.Second) + require.NoError(t, s.CreateMessage(ctx, oldRead)) + + oldUnread := newTestMessage(projectID, "agent-1") + oldUnread.CreatedAt = time.Now().Add(-72 * time.Hour).UTC().Truncate(time.Second) + require.NoError(t, s.CreateMessage(ctx, oldUnread)) + + recent := newTestMessage(projectID, "agent-1") + require.NoError(t, s.CreateMessage(ctx, recent)) + + // readCutoff 24h ago purges oldRead; unreadCutoff 96h ago keeps oldUnread. + readCutoff := time.Now().Add(-24 * time.Hour) + unreadCutoff := time.Now().Add(-96 * time.Hour) + n, err := s.PurgeOldMessages(ctx, readCutoff, unreadCutoff) + require.NoError(t, err) + assert.Equal(t, 1, n) + + _, err = s.GetMessage(ctx, oldRead.ID) + assert.ErrorIs(t, err, store.ErrNotFound) + _, err = s.GetMessage(ctx, oldUnread.ID) + require.NoError(t, err) + _, err = s.GetMessage(ctx, recent.ID) + require.NoError(t, err) +} + +// fakePublisher records PublishUserMessage calls to verify the LISTEN/NOTIFY +// design-in hook fires on create. +type fakePublisher struct { + published []*store.Message +} + +func (f *fakePublisher) PublishUserMessage(_ context.Context, msg *store.Message) error { + f.published = append(f.published, msg) + return nil +} + +func TestCreateMessagePublishesEvent(t *testing.T) { + base := newTestMessageStore(t) + pub := &fakePublisher{} + s := base.WithPublisher(pub) + ctx := context.Background() + + msg := newTestMessage(uuid.NewString(), "agent-1") + require.NoError(t, s.CreateMessage(ctx, msg)) + + require.Len(t, pub.published, 1) + assert.Equal(t, msg.ID, pub.published[0].ID) +} diff --git a/pkg/store/entadapter/notification_store.go b/pkg/store/entadapter/notification_store.go new file mode 100644 index 000000000..864c5c876 --- /dev/null +++ b/pkg/store/entadapter/notification_store.go @@ -0,0 +1,671 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package entadapter + +import ( + "context" + "encoding/json" + "fmt" + "time" + + entsql "entgo.io/ent/dialect/sql" + "github.com/GoogleCloudPlatform/scion/pkg/ent" + "github.com/GoogleCloudPlatform/scion/pkg/ent/notification" + "github.com/GoogleCloudPlatform/scion/pkg/ent/notificationsubscription" + "github.com/GoogleCloudPlatform/scion/pkg/ent/subscriptiontemplate" + "github.com/GoogleCloudPlatform/scion/pkg/store" +) + +// entDesc returns the Ent ordering option for descending order. +func entDesc() entsql.OrderTermOption { return entsql.OrderDesc() } + +// NotificationEventType enumerates the kinds of notification changes that are +// published to a real-time channel (e.g. Postgres LISTEN/NOTIFY). +type NotificationEventType string + +const ( + // NotificationEventCreated is published when a notification record is created. + NotificationEventCreated NotificationEventType = "created" + // NotificationEventDispatched is published when a notification is claimed for + // dispatch by exactly one replica (see MarkNotificationDispatched). + NotificationEventDispatched NotificationEventType = "dispatched" +) + +// NotificationEvent describes a change worth broadcasting to other hub replicas +// so they can react in real time (deliver to a connected agent, wake a poller, +// etc.) instead of busy-polling the database. +type NotificationEvent struct { + Type NotificationEventType `json:"type"` + NotificationID string `json:"notificationId"` + SubscriberType string `json:"subscriberType"` + SubscriberID string `json:"subscriberId"` + ProjectID string `json:"projectId"` +} + +// NotificationPublisher publishes notification events to a real-time channel. +// +// In a multi-replica Postgres deployment this is implemented on top of +// LISTEN/NOTIFY so a notification created or dispatched on one replica is seen +// immediately by the others. The SQLite / single-process path can leave the +// publisher nil, in which case publishing is a no-op. Publishing is best-effort: +// a publish failure never fails the underlying write. +type NotificationPublisher interface { + PublishNotification(ctx context.Context, evt NotificationEvent) error +} + +// NotificationStore implements store.NotificationStore using Ent ORM. +type NotificationStore struct { + client *ent.Client + publisher NotificationPublisher +} + +// NewNotificationStore creates a new Ent-backed NotificationStore. No publisher +// is attached; use WithPublisher to wire LISTEN/NOTIFY for multi-replica +// deployments. +func NewNotificationStore(client *ent.Client) *NotificationStore { + return &NotificationStore{client: client} +} + +// WithPublisher returns a copy of the store that publishes notification events +// through p. Passing nil disables publishing. +func (s *NotificationStore) WithPublisher(p NotificationPublisher) *NotificationStore { + clone := *s + clone.publisher = p + return &clone +} + +// publish emits an event best-effort. Errors are intentionally swallowed so the +// real-time fan-out never breaks the durable write path. +func (s *NotificationStore) publish(ctx context.Context, evt NotificationEvent) { + if s.publisher == nil { + return + } + _ = s.publisher.PublishNotification(ctx, evt) +} + +// ---------------------------------------------------------------------------- +// Conversions +// ---------------------------------------------------------------------------- + +// entSubToStore converts an Ent NotificationSubscription to the store model. +func entSubToStore(e *ent.NotificationSubscription) *store.NotificationSubscription { + sub := &store.NotificationSubscription{ + ID: e.ID.String(), + Scope: e.Scope, + SubscriberType: e.SubscriberType, + SubscriberID: e.SubscriberID, + ProjectID: e.ProjectID.String(), + CreatedAt: e.Created, + CreatedBy: e.CreatedBy, + } + if e.AgentID != nil { + sub.AgentID = e.AgentID.String() + } + if e.TriggerActivities != "" { + _ = json.Unmarshal([]byte(e.TriggerActivities), &sub.TriggerActivities) + } + return sub +} + +// entNotifToStore converts an Ent Notification to the store model. +func entNotifToStore(e *ent.Notification) *store.Notification { + return &store.Notification{ + ID: e.ID.String(), + SubscriptionID: e.SubscriptionID.String(), + AgentID: e.AgentID.String(), + ProjectID: e.ProjectID.String(), + SubscriberType: e.SubscriberType, + SubscriberID: e.SubscriberID, + Status: e.Status, + Message: e.Message, + Dispatched: e.Dispatched, + Acknowledged: e.Acknowledged, + CreatedAt: e.Created, + } +} + +// entTemplateToStore converts an Ent SubscriptionTemplate to the store model. +func entTemplateToStore(e *ent.SubscriptionTemplate) *store.SubscriptionTemplate { + tmpl := &store.SubscriptionTemplate{ + ID: e.ID.String(), + Name: e.Name, + Scope: e.Scope, + CreatedBy: e.CreatedBy, + } + if e.ProjectID != nil { + tmpl.ProjectID = e.ProjectID.String() + } + if e.TriggerActivities != "" { + _ = json.Unmarshal([]byte(e.TriggerActivities), &tmpl.TriggerActivities) + } + return tmpl +} + +// marshalTriggers serializes trigger activities to the JSON string stored in the +// dialect-neutral trigger_activities column. +func marshalTriggers(triggers []string) string { + if triggers == nil { + triggers = []string{} + } + b, _ := json.Marshal(triggers) + return string(b) +} + +// ---------------------------------------------------------------------------- +// Notification Subscription Operations +// ---------------------------------------------------------------------------- + +// CreateNotificationSubscription creates a new notification subscription. +func (s *NotificationStore) CreateNotificationSubscription(ctx context.Context, sub *store.NotificationSubscription) error { + if sub.ID == "" || sub.SubscriberID == "" || sub.ProjectID == "" { + return store.ErrInvalidInput + } + + // Default scope to agent for backward compatibility. + if sub.Scope == "" { + sub.Scope = store.SubscriptionScopeAgent + } + + // Validate scope-specific constraints. + switch sub.Scope { + case store.SubscriptionScopeAgent: + if sub.AgentID == "" { + return store.ErrInvalidInput + } + case store.SubscriptionScopeProject: + sub.AgentID = "" // Ensure no agent_id for project-scoped subscriptions. + default: + return fmt.Errorf("invalid scope %q: %w", sub.Scope, store.ErrInvalidInput) + } + + subscriberType := sub.SubscriberType + if subscriberType == "" { + subscriberType = "agent" + } + + id, err := parseUUID(sub.ID) + if err != nil { + return err + } + projectUID, err := parseUUID(sub.ProjectID) + if err != nil { + return err + } + + if sub.CreatedAt.IsZero() { + sub.CreatedAt = time.Now() + } + + create := s.client.NotificationSubscription.Create(). + SetID(id). + SetScope(sub.Scope). + SetSubscriberType(subscriberType). + SetSubscriberID(sub.SubscriberID). + SetProjectID(projectUID). + SetTriggerActivities(marshalTriggers(sub.TriggerActivities)). + SetCreatedBy(sub.CreatedBy). + SetCreated(sub.CreatedAt) + + if sub.AgentID != "" { + agentUID, err := parseUUID(sub.AgentID) + if err != nil { + return err + } + create.SetAgentID(agentUID) + } + + if _, err := create.Save(ctx); err != nil { + return mapError(err) + } + return nil +} + +// GetNotificationSubscription returns a single subscription by ID. +func (s *NotificationStore) GetNotificationSubscription(ctx context.Context, id string) (*store.NotificationSubscription, error) { + uid, err := parseGetID(id) + if err != nil { + return nil, err + } + e, err := s.client.NotificationSubscription.Get(ctx, uid) + if err != nil { + return nil, mapError(err) + } + return entSubToStore(e), nil +} + +// GetNotificationSubscriptions returns all agent-scoped subscriptions for a watched agent. +func (s *NotificationStore) GetNotificationSubscriptions(ctx context.Context, agentID string) ([]store.NotificationSubscription, error) { + uid, err := parseUUID(agentID) + if err != nil { + return nil, err + } + rows, err := s.client.NotificationSubscription.Query(). + Where(notificationsubscription.AgentIDEQ(uid)). + Order(notificationsubscription.ByCreated()). + All(ctx) + if err != nil { + return nil, err + } + return subsToStore(rows), nil +} + +// GetNotificationSubscriptionsByProject returns all subscriptions within a project (any scope). +func (s *NotificationStore) GetNotificationSubscriptionsByProject(ctx context.Context, projectID string) ([]store.NotificationSubscription, error) { + uid, err := parseUUID(projectID) + if err != nil { + return nil, err + } + rows, err := s.client.NotificationSubscription.Query(). + Where(notificationsubscription.ProjectIDEQ(uid)). + Order(notificationsubscription.ByCreated()). + All(ctx) + if err != nil { + return nil, err + } + return subsToStore(rows), nil +} + +// GetNotificationSubscriptionsByProjectScope returns project-scoped subscriptions +// (scope='project') for a given project. +func (s *NotificationStore) GetNotificationSubscriptionsByProjectScope(ctx context.Context, projectID string) ([]store.NotificationSubscription, error) { + uid, err := parseUUID(projectID) + if err != nil { + return nil, err + } + rows, err := s.client.NotificationSubscription.Query(). + Where( + notificationsubscription.ProjectIDEQ(uid), + notificationsubscription.ScopeEQ(store.SubscriptionScopeProject), + ). + Order(notificationsubscription.ByCreated()). + All(ctx) + if err != nil { + return nil, err + } + return subsToStore(rows), nil +} + +// GetSubscriptionsForSubscriber returns all subscriptions owned by a subscriber. +func (s *NotificationStore) GetSubscriptionsForSubscriber(ctx context.Context, subscriberType, subscriberID string) ([]store.NotificationSubscription, error) { + rows, err := s.client.NotificationSubscription.Query(). + Where( + notificationsubscription.SubscriberTypeEQ(subscriberType), + notificationsubscription.SubscriberIDEQ(subscriberID), + ). + Order(notificationsubscription.ByCreated()). + All(ctx) + if err != nil { + return nil, err + } + return subsToStore(rows), nil +} + +// UpdateNotificationSubscriptionTriggers updates the trigger activities of a subscription. +func (s *NotificationStore) UpdateNotificationSubscriptionTriggers(ctx context.Context, id string, triggerActivities []string) error { + if id == "" || len(triggerActivities) == 0 { + return store.ErrInvalidInput + } + uid, err := parseUUID(id) + if err != nil { + return err + } + _, err = s.client.NotificationSubscription.UpdateOneID(uid). + SetTriggerActivities(marshalTriggers(triggerActivities)). + Save(ctx) + if err != nil { + return mapError(err) + } + return nil +} + +// DeleteNotificationSubscription deletes a subscription by ID. +func (s *NotificationStore) DeleteNotificationSubscription(ctx context.Context, id string) error { + uid, err := parseUUID(id) + if err != nil { + return err + } + if err := s.client.NotificationSubscription.DeleteOneID(uid).Exec(ctx); err != nil { + return mapError(err) + } + return nil +} + +// DeleteNotificationSubscriptionsForAgent deletes all subscriptions for a watched agent. +// No error on zero rows affected. +func (s *NotificationStore) DeleteNotificationSubscriptionsForAgent(ctx context.Context, agentID string) error { + uid, err := parseUUID(agentID) + if err != nil { + return err + } + _, err = s.client.NotificationSubscription.Delete(). + Where(notificationsubscription.AgentIDEQ(uid)). + Exec(ctx) + return err +} + +// subsToStore converts a slice of Ent subscriptions to store models. +func subsToStore(rows []*ent.NotificationSubscription) []store.NotificationSubscription { + out := make([]store.NotificationSubscription, 0, len(rows)) + for _, e := range rows { + out = append(out, *entSubToStore(e)) + } + return out +} + +// ---------------------------------------------------------------------------- +// Notification Operations +// ---------------------------------------------------------------------------- + +// CreateNotification creates a new notification record. +func (s *NotificationStore) CreateNotification(ctx context.Context, notif *store.Notification) error { + if notif.ID == "" || notif.SubscriptionID == "" || notif.AgentID == "" { + return store.ErrInvalidInput + } + + id, err := parseUUID(notif.ID) + if err != nil { + return err + } + subUID, err := parseUUID(notif.SubscriptionID) + if err != nil { + return err + } + agentUID, err := parseUUID(notif.AgentID) + if err != nil { + return err + } + projectUID, err := parseUUID(notif.ProjectID) + if err != nil { + return err + } + + if notif.CreatedAt.IsZero() { + notif.CreatedAt = time.Now() + } + + _, err = s.client.Notification.Create(). + SetID(id). + SetSubscriptionID(subUID). + SetAgentID(agentUID). + SetProjectID(projectUID). + SetSubscriberType(notif.SubscriberType). + SetSubscriberID(notif.SubscriberID). + SetStatus(notif.Status). + SetMessage(notif.Message). + SetDispatched(notif.Dispatched). + SetAcknowledged(notif.Acknowledged). + SetCreated(notif.CreatedAt). + Save(ctx) + if err != nil { + return mapError(err) + } + + // Broadcast so other replicas can pick up delivery in real time. + s.publish(ctx, NotificationEvent{ + Type: NotificationEventCreated, + NotificationID: notif.ID, + SubscriberType: notif.SubscriberType, + SubscriberID: notif.SubscriberID, + ProjectID: notif.ProjectID, + }) + return nil +} + +// GetNotifications returns notifications for a subscriber, newest first. +func (s *NotificationStore) GetNotifications(ctx context.Context, subscriberType, subscriberID string, onlyUnacknowledged bool) ([]store.Notification, error) { + query := s.client.Notification.Query(). + Where( + notification.SubscriberTypeEQ(subscriberType), + notification.SubscriberIDEQ(subscriberID), + ) + if onlyUnacknowledged { + query = query.Where(notification.AcknowledgedEQ(false)) + } + rows, err := query.Order(notification.ByCreated(entDesc())).All(ctx) + if err != nil { + return nil, err + } + return notifsToStore(rows), nil +} + +// GetNotificationsByAgent returns notifications for a subscriber filtered by agent ID, newest first. +func (s *NotificationStore) GetNotificationsByAgent(ctx context.Context, agentID, subscriberType, subscriberID string, onlyUnacknowledged bool) ([]store.Notification, error) { + uid, err := parseUUID(agentID) + if err != nil { + return nil, err + } + query := s.client.Notification.Query(). + Where( + notification.AgentIDEQ(uid), + notification.SubscriberTypeEQ(subscriberType), + notification.SubscriberIDEQ(subscriberID), + ) + if onlyUnacknowledged { + query = query.Where(notification.AcknowledgedEQ(false)) + } + rows, err := query.Order(notification.ByCreated(entDesc())).All(ctx) + if err != nil { + return nil, err + } + return notifsToStore(rows), nil +} + +// AcknowledgeNotification marks a notification as acknowledged. +func (s *NotificationStore) AcknowledgeNotification(ctx context.Context, id string) error { + uid, err := parseUUID(id) + if err != nil { + return err + } + _, err = s.client.Notification.UpdateOneID(uid).SetAcknowledged(true).Save(ctx) + if err != nil { + return mapError(err) + } + return nil +} + +// AcknowledgeAllNotifications marks all notifications for a subscriber as acknowledged. +// No error on zero rows affected. +func (s *NotificationStore) AcknowledgeAllNotifications(ctx context.Context, subscriberType, subscriberID string) error { + _, err := s.client.Notification.Update(). + Where( + notification.SubscriberTypeEQ(subscriberType), + notification.SubscriberIDEQ(subscriberID), + ). + SetAcknowledged(true). + Save(ctx) + return err +} + +// MarkNotificationDispatched atomically claims a notification for dispatch. +// +// The conditional update (dispatched = false guard) is the multi-replica +// concurrency primitive: in a Postgres deployment several hub replicas may race +// to dispatch the same notification, but the UPDATE ... WHERE dispatched = false +// is atomic, so exactly one replica observes affected == 1 and "wins" the claim. +// That winner is the one that publishes the dispatch event / drives the side +// effect; losers (affected == 0 on an existing row) treat it as an idempotent +// no-op. A missing row is still reported as ErrNotFound to preserve the +// interface contract. +func (s *NotificationStore) MarkNotificationDispatched(ctx context.Context, id string) error { + uid, err := parseUUID(id) + if err != nil { + return err + } + + affected, err := s.client.Notification.Update(). + Where( + notification.IDEQ(uid), + notification.DispatchedEQ(false), + ). + SetDispatched(true). + Save(ctx) + if err != nil { + return mapError(err) + } + + if affected == 0 { + // Either the notification doesn't exist or another replica already + // claimed the dispatch. Disambiguate to preserve ErrNotFound semantics. + e, err := s.client.Notification.Query().Where(notification.IDEQ(uid)).Only(ctx) + if err != nil { + return mapError(err) + } + // Already dispatched by some replica — idempotent success. + _ = e + return nil + } + + // We won the claim; only this replica broadcasts the dispatch. + if e, err := s.client.Notification.Get(ctx, uid); err == nil { + s.publish(ctx, NotificationEvent{ + Type: NotificationEventDispatched, + NotificationID: id, + SubscriberType: e.SubscriberType, + SubscriberID: e.SubscriberID, + ProjectID: e.ProjectID.String(), + }) + } + return nil +} + +// GetLastNotificationStatus returns the status of the most recent notification +// for a given subscription. Returns ("", nil) if no notifications exist. +func (s *NotificationStore) GetLastNotificationStatus(ctx context.Context, subscriptionID string) (string, error) { + uid, err := parseUUID(subscriptionID) + if err != nil { + return "", err + } + e, err := s.client.Notification.Query(). + Where(notification.SubscriptionIDEQ(uid)). + Order(notification.ByCreated(entDesc())). + First(ctx) + if err != nil { + if ent.IsNotFound(err) { + return "", nil + } + return "", err + } + return e.Status, nil +} + +// notifsToStore converts a slice of Ent notifications to store models. +func notifsToStore(rows []*ent.Notification) []store.Notification { + out := make([]store.Notification, 0, len(rows)) + for _, e := range rows { + out = append(out, *entNotifToStore(e)) + } + return out +} + +// ---------------------------------------------------------------------------- +// Subscription Template Operations +// ---------------------------------------------------------------------------- + +// CreateSubscriptionTemplate creates a new subscription template. +func (s *NotificationStore) CreateSubscriptionTemplate(ctx context.Context, tmpl *store.SubscriptionTemplate) error { + if tmpl.ID == "" || tmpl.Name == "" || len(tmpl.TriggerActivities) == 0 { + return store.ErrInvalidInput + } + + id, err := parseUUID(tmpl.ID) + if err != nil { + return err + } + + scope := tmpl.Scope + if scope == "" { + scope = "project" + } + + create := s.client.SubscriptionTemplate.Create(). + SetID(id). + SetName(tmpl.Name). + SetScope(scope). + SetTriggerActivities(marshalTriggers(tmpl.TriggerActivities)). + SetCreatedBy(tmpl.CreatedBy) + + // An empty ProjectID denotes a global template (NULL project_id). + if tmpl.ProjectID != "" { + projectUID, err := parseUUID(tmpl.ProjectID) + if err != nil { + return err + } + create.SetProjectID(projectUID) + } + + if _, err := create.Save(ctx); err != nil { + return mapError(err) + } + return nil +} + +// GetSubscriptionTemplate returns a template by ID. +func (s *NotificationStore) GetSubscriptionTemplate(ctx context.Context, id string) (*store.SubscriptionTemplate, error) { + uid, err := parseGetID(id) + if err != nil { + return nil, err + } + e, err := s.client.SubscriptionTemplate.Get(ctx, uid) + if err != nil { + return nil, mapError(err) + } + return entTemplateToStore(e), nil +} + +// ListSubscriptionTemplates returns all templates. If projectID is non-empty, +// returns both global templates and project-specific templates. +func (s *NotificationStore) ListSubscriptionTemplates(ctx context.Context, projectID string) ([]store.SubscriptionTemplate, error) { + query := s.client.SubscriptionTemplate.Query() + + if projectID != "" { + uid, err := parseUUID(projectID) + if err != nil { + return nil, err + } + // Global templates (NULL project_id) plus those owned by this project. + query = query.Where(subscriptiontemplate.Or( + subscriptiontemplate.ProjectIDIsNil(), + subscriptiontemplate.ProjectIDEQ(uid), + )) + } else { + query = query.Where(subscriptiontemplate.ProjectIDIsNil()) + } + + rows, err := query.Order(subscriptiontemplate.ByName()).All(ctx) + if err != nil { + return nil, err + } + + out := make([]store.SubscriptionTemplate, 0, len(rows)) + for _, e := range rows { + out = append(out, *entTemplateToStore(e)) + } + return out, nil +} + +// DeleteSubscriptionTemplate deletes a template by ID. +func (s *NotificationStore) DeleteSubscriptionTemplate(ctx context.Context, id string) error { + uid, err := parseUUID(id) + if err != nil { + return err + } + if err := s.client.SubscriptionTemplate.DeleteOneID(uid).Exec(ctx); err != nil { + return mapError(err) + } + return nil +} + +// Ensure NotificationStore satisfies the store interface. +var _ store.NotificationStore = (*NotificationStore)(nil) diff --git a/pkg/store/entadapter/notification_store_test.go b/pkg/store/entadapter/notification_store_test.go new file mode 100644 index 000000000..ffaeaacea --- /dev/null +++ b/pkg/store/entadapter/notification_store_test.go @@ -0,0 +1,304 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build !no_sqlite + +package entadapter + +import ( + "context" + "sync" + "testing" + + "github.com/GoogleCloudPlatform/scion/pkg/store" + "github.com/GoogleCloudPlatform/scion/pkg/store/enttest" + "github.com/google/uuid" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func newTestNotificationStore(t *testing.T) *NotificationStore { + t.Helper() + client := enttest.NewClient(t) + return NewNotificationStore(client) +} + +func TestNotificationStore_SubscriptionCRUD(t *testing.T) { + ctx := context.Background() + s := newTestNotificationStore(t) + + projectID := uuid.NewString() + agentID := uuid.NewString() + sub := &store.NotificationSubscription{ + ID: uuid.NewString(), + Scope: store.SubscriptionScopeAgent, + AgentID: agentID, + SubscriberType: "user", + SubscriberID: "user-1", + ProjectID: projectID, + TriggerActivities: []string{"COMPLETED", "WAITING_FOR_INPUT"}, + CreatedBy: "tester", + } + require.NoError(t, s.CreateNotificationSubscription(ctx, sub)) + + got, err := s.GetNotificationSubscription(ctx, sub.ID) + require.NoError(t, err) + assert.Equal(t, sub.ID, got.ID) + assert.Equal(t, agentID, got.AgentID) + assert.Equal(t, []string{"COMPLETED", "WAITING_FOR_INPUT"}, got.TriggerActivities) + assert.False(t, got.CreatedAt.IsZero()) + + // Update triggers. + require.NoError(t, s.UpdateNotificationSubscriptionTriggers(ctx, sub.ID, []string{"FAILED"})) + got, err = s.GetNotificationSubscription(ctx, sub.ID) + require.NoError(t, err) + assert.Equal(t, []string{"FAILED"}, got.TriggerActivities) + + // Query helpers. + byAgent, err := s.GetNotificationSubscriptions(ctx, agentID) + require.NoError(t, err) + assert.Len(t, byAgent, 1) + + byProject, err := s.GetNotificationSubscriptionsByProject(ctx, projectID) + require.NoError(t, err) + assert.Len(t, byProject, 1) + + bySubscriber, err := s.GetSubscriptionsForSubscriber(ctx, "user", "user-1") + require.NoError(t, err) + assert.Len(t, bySubscriber, 1) + + // Delete. + require.NoError(t, s.DeleteNotificationSubscription(ctx, sub.ID)) + _, err = s.GetNotificationSubscription(ctx, sub.ID) + assert.ErrorIs(t, err, store.ErrNotFound) +} + +func TestNotificationStore_ProjectScopedSubscription(t *testing.T) { + ctx := context.Background() + s := newTestNotificationStore(t) + + projectID := uuid.NewString() + sub := &store.NotificationSubscription{ + ID: uuid.NewString(), + Scope: store.SubscriptionScopeProject, + SubscriberType: "user", + SubscriberID: "user-1", + ProjectID: projectID, + TriggerActivities: []string{"COMPLETED"}, + CreatedBy: "tester", + } + require.NoError(t, s.CreateNotificationSubscription(ctx, sub)) + assert.Empty(t, sub.AgentID, "project scope must clear agent id") + + scoped, err := s.GetNotificationSubscriptionsByProjectScope(ctx, projectID) + require.NoError(t, err) + require.Len(t, scoped, 1) + assert.Empty(t, scoped[0].AgentID) +} + +func TestNotificationStore_SubscriptionValidation(t *testing.T) { + ctx := context.Background() + s := newTestNotificationStore(t) + + // Missing required fields. + assert.ErrorIs(t, s.CreateNotificationSubscription(ctx, &store.NotificationSubscription{}), store.ErrInvalidInput) + + // Agent scope without agent id. + assert.ErrorIs(t, s.CreateNotificationSubscription(ctx, &store.NotificationSubscription{ + ID: uuid.NewString(), Scope: store.SubscriptionScopeAgent, + SubscriberID: "u", ProjectID: uuid.NewString(), + }), store.ErrInvalidInput) +} + +func TestNotificationStore_NotificationLifecycle(t *testing.T) { + ctx := context.Background() + s := newTestNotificationStore(t) + + projectID := uuid.NewString() + agentID := uuid.NewString() + subID := uuid.NewString() + + notif := &store.Notification{ + ID: uuid.NewString(), + SubscriptionID: subID, + AgentID: agentID, + ProjectID: projectID, + SubscriberType: "user", + SubscriberID: "user-1", + Status: "COMPLETED", + Message: "agent done", + } + require.NoError(t, s.CreateNotification(ctx, notif)) + + list, err := s.GetNotifications(ctx, "user", "user-1", false) + require.NoError(t, err) + require.Len(t, list, 1) + assert.False(t, list[0].Acknowledged) + assert.False(t, list[0].Dispatched) + + byAgent, err := s.GetNotificationsByAgent(ctx, agentID, "user", "user-1", true) + require.NoError(t, err) + require.Len(t, byAgent, 1) + + // Dispatch claim. + require.NoError(t, s.MarkNotificationDispatched(ctx, notif.ID)) + + // Acknowledge. + require.NoError(t, s.AcknowledgeNotification(ctx, notif.ID)) + unack, err := s.GetNotifications(ctx, "user", "user-1", true) + require.NoError(t, err) + assert.Empty(t, unack) + + // Last status by subscription. + status, err := s.GetLastNotificationStatus(ctx, subID) + require.NoError(t, err) + assert.Equal(t, "COMPLETED", status) + + // No notifications for an unknown subscription -> ("", nil). + status, err = s.GetLastNotificationStatus(ctx, uuid.NewString()) + require.NoError(t, err) + assert.Equal(t, "", status) +} + +func TestNotificationStore_AcknowledgeAll(t *testing.T) { + ctx := context.Background() + s := newTestNotificationStore(t) + + for i := 0; i < 3; i++ { + require.NoError(t, s.CreateNotification(ctx, &store.Notification{ + ID: uuid.NewString(), + SubscriptionID: uuid.NewString(), + AgentID: uuid.NewString(), + ProjectID: uuid.NewString(), + SubscriberType: "user", + SubscriberID: "user-1", + Status: "COMPLETED", + Message: "m", + })) + } + require.NoError(t, s.AcknowledgeAllNotifications(ctx, "user", "user-1")) + unack, err := s.GetNotifications(ctx, "user", "user-1", true) + require.NoError(t, err) + assert.Empty(t, unack) +} + +// TestNotificationStore_DispatchClaimIsExclusive verifies the multi-replica +// concurrency primitive: many concurrent MarkNotificationDispatched calls for +// the same notification must result in exactly one publisher "win". +func TestNotificationStore_DispatchClaimIsExclusive(t *testing.T) { + ctx := context.Background() + + client := enttest.NewClient(t) + + pub := &countingPublisher{} + s := NewNotificationStore(client).WithPublisher(pub) + + notifID := uuid.NewString() + require.NoError(t, s.CreateNotification(ctx, &store.Notification{ + ID: notifID, + SubscriptionID: uuid.NewString(), + AgentID: uuid.NewString(), + ProjectID: uuid.NewString(), + SubscriberType: "user", + SubscriberID: "user-1", + Status: "COMPLETED", + Message: "m", + })) + + const racers = 8 + var wg sync.WaitGroup + wg.Add(racers) + for i := 0; i < racers; i++ { + go func() { + defer wg.Done() + _ = s.MarkNotificationDispatched(ctx, notifID) + }() + } + wg.Wait() + + // Exactly one dispatch event should have been published despite the race. + assert.Equal(t, 1, pub.count(NotificationEventDispatched), "dispatch must be claimed exactly once") + + // Marking an unknown notification returns ErrNotFound. + assert.ErrorIs(t, s.MarkNotificationDispatched(ctx, uuid.NewString()), store.ErrNotFound) +} + +func TestNotificationStore_TemplateCRUD(t *testing.T) { + ctx := context.Background() + s := newTestNotificationStore(t) + + projectID := uuid.NewString() + + global := &store.SubscriptionTemplate{ + ID: uuid.NewString(), + Name: "all-events", + Scope: "project", + TriggerActivities: []string{"COMPLETED", "FAILED"}, + CreatedBy: "tester", + } + require.NoError(t, s.CreateSubscriptionTemplate(ctx, global)) + + scoped := &store.SubscriptionTemplate{ + ID: uuid.NewString(), + Name: "critical", + Scope: "project", + TriggerActivities: []string{"FAILED"}, + ProjectID: projectID, + CreatedBy: "tester", + } + require.NoError(t, s.CreateSubscriptionTemplate(ctx, scoped)) + + got, err := s.GetSubscriptionTemplate(ctx, scoped.ID) + require.NoError(t, err) + assert.Equal(t, projectID, got.ProjectID) + assert.Equal(t, []string{"FAILED"}, got.TriggerActivities) + + // Global-only listing. + globalOnly, err := s.ListSubscriptionTemplates(ctx, "") + require.NoError(t, err) + require.Len(t, globalOnly, 1) + assert.Equal(t, "all-events", globalOnly[0].Name) + assert.Empty(t, globalOnly[0].ProjectID) + + // Project listing includes global + project-specific. + withProject, err := s.ListSubscriptionTemplates(ctx, projectID) + require.NoError(t, err) + assert.Len(t, withProject, 2) + + require.NoError(t, s.DeleteSubscriptionTemplate(ctx, scoped.ID)) + _, err = s.GetSubscriptionTemplate(ctx, scoped.ID) + assert.ErrorIs(t, err, store.ErrNotFound) +} + +// countingPublisher is a thread-safe NotificationPublisher test double. +type countingPublisher struct { + mu sync.Mutex + counts map[NotificationEventType]int +} + +func (p *countingPublisher) PublishNotification(_ context.Context, evt NotificationEvent) error { + p.mu.Lock() + defer p.mu.Unlock() + if p.counts == nil { + p.counts = make(map[NotificationEventType]int) + } + p.counts[evt.Type]++ + return nil +} + +func (p *countingPublisher) count(t NotificationEventType) int { + p.mu.Lock() + defer p.mu.Unlock() + return p.counts[t] +} diff --git a/pkg/store/entadapter/policy_store.go b/pkg/store/entadapter/policy_store.go index f8679381b..8efcd3d58 100644 --- a/pkg/store/entadapter/policy_store.go +++ b/pkg/store/entadapter/policy_store.go @@ -153,7 +153,7 @@ func (s *PolicyStore) CreatePolicy(ctx context.Context, p *store.Policy) error { // GetPolicy retrieves a policy by ID. func (s *PolicyStore) GetPolicy(ctx context.Context, id string) (*store.Policy, error) { - uid, err := parseUUID(id) + uid, err := parseGetID(id) if err != nil { return nil, err } diff --git a/pkg/store/entadapter/policy_store_test.go b/pkg/store/entadapter/policy_store_test.go index e9c480155..2f5659d2d 100644 --- a/pkg/store/entadapter/policy_store_test.go +++ b/pkg/store/entadapter/policy_store_test.go @@ -20,8 +20,8 @@ import ( "context" "testing" - "github.com/GoogleCloudPlatform/scion/pkg/ent/entc" "github.com/GoogleCloudPlatform/scion/pkg/store" + "github.com/GoogleCloudPlatform/scion/pkg/store/enttest" "github.com/google/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -37,15 +37,12 @@ var ( func newTestPolicyStore(t *testing.T) *PolicyStore { t.Helper() - client, err := entc.OpenSQLite("file:" + t.Name() + "?mode=memory&cache=shared") - require.NoError(t, err) - t.Cleanup(func() { client.Close() }) - require.NoError(t, entc.AutoMigrate(context.Background(), client)) + client := enttest.NewClient(t) ctx := context.Background() // Create test user - _, err = client.User.Create(). + _, err := client.User.Create(). SetID(policyTestUserUID). SetEmail("alice@example.com"). SetDisplayName("Alice"). diff --git a/pkg/store/entadapter/project_store.go b/pkg/store/entadapter/project_store.go new file mode 100644 index 000000000..7a7e1a79c --- /dev/null +++ b/pkg/store/entadapter/project_store.go @@ -0,0 +1,1060 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package entadapter + +import ( + "context" + "encoding/json" + "fmt" + "strings" + "time" + + "github.com/GoogleCloudPlatform/scion/pkg/api" + "github.com/GoogleCloudPlatform/scion/pkg/ent" + "github.com/GoogleCloudPlatform/scion/pkg/ent/agent" + "github.com/GoogleCloudPlatform/scion/pkg/ent/project" + "github.com/GoogleCloudPlatform/scion/pkg/ent/projectcontributor" + "github.com/GoogleCloudPlatform/scion/pkg/ent/projectsyncstate" + "github.com/GoogleCloudPlatform/scion/pkg/ent/runtimebroker" + "github.com/GoogleCloudPlatform/scion/pkg/store" + "github.com/google/uuid" +) + +// maxCASRetries bounds the optimistic-concurrency retry loop on runtime broker +// read-modify-write paths (heartbeat and full update). A handful of retries is +// ample: contention on a single broker row is low and each retry only re-reads +// the lock_version token. +const maxCASRetries = 5 + +// ProjectStore implements store.ProjectStore, store.RuntimeBrokerStore, +// store.ProjectProviderStore and store.ProjectSyncStateStore using Ent ORM. +// +// These four interfaces form the project/broker domain (tables: projects, +// runtime_brokers, project_contributors, project_sync_state). They are grouped +// in one adapter because they are tightly coupled — projects reference brokers +// through contributors, and several computed fields on a project are derived +// from broker/contributor state. +type ProjectStore struct { + client *ent.Client +} + +// NewProjectStore creates a new Ent-backed ProjectStore. +func NewProjectStore(client *ent.Client) *ProjectStore { + return &ProjectStore{client: client} +} + +// ============================================================================= +// JSON helpers +// +// Several columns are stored as raw JSON strings (matching the dual-write +// behavior of the legacy SQLite store) rather than typed Ent JSON fields, to +// keep the schema dialect-neutral and free of store/api type imports. +// ============================================================================= + +// marshalRawJSON marshals v to a JSON string. A nil pointer/slice/map marshals +// to "null", which unmarshalRawJSON treats as "leave the target untouched". +func marshalRawJSON(v any) string { + b, err := json.Marshal(v) + if err != nil { + return "" + } + return string(b) +} + +// unmarshalRawJSON unmarshals s into v, tolerating empty/"null" payloads. +func unmarshalRawJSON(s string, v any) { + if s == "" || s == "null" { + return + } + _ = json.Unmarshal([]byte(s), v) +} + +// ============================================================================= +// Project model mapping +// ============================================================================= + +// entProjectToStore converts an Ent Project entity to a store.Project model. +// Computed fields (AgentCount, ActiveBrokerCount, ProjectType, OwnerName) are +// not set here; callers that need them invoke populateProjectComputed. +func entProjectToStore(p *ent.Project) *store.Project { + sp := &store.Project{ + ID: p.ID.String(), + Name: p.Name, + Slug: p.Slug, + Labels: p.Labels, + Annotations: p.Annotations, + Created: p.Created, + Updated: p.Updated, + CreatedBy: p.CreatedBy, + OwnerID: p.OwnerID, + Visibility: p.Visibility, + } + if p.GitRemote != nil { + sp.GitRemote = *p.GitRemote + } + if p.DefaultRuntimeBrokerID != nil { + sp.DefaultRuntimeBrokerID = *p.DefaultRuntimeBrokerID + } + if p.GithubInstallationID != nil { + sp.GitHubInstallationID = p.GithubInstallationID + } + if p.SharedDirs != "" { + var dirs []api.SharedDir + unmarshalRawJSON(p.SharedDirs, &dirs) + sp.SharedDirs = dirs + } + if p.GithubPermissions != "" { + sp.GitHubPermissions = &store.GitHubTokenPermissions{} + unmarshalRawJSON(p.GithubPermissions, sp.GitHubPermissions) + } + if p.GithubAppStatus != "" { + sp.GitHubAppStatus = &store.GitHubAppProjectStatus{} + unmarshalRawJSON(p.GithubAppStatus, sp.GitHubAppStatus) + } + if p.GitIdentity != "" { + sp.GitIdentity = &store.GitIdentityConfig{} + unmarshalRawJSON(p.GitIdentity, sp.GitIdentity) + } + return sp +} + +// CreateProject creates a new project record. +func (s *ProjectStore) CreateProject(ctx context.Context, p *store.Project) error { + uid, err := parseUUID(p.ID) + if err != nil { + return err + } + + create := s.client.Project.Create(). + SetID(uid). + SetName(p.Name). + SetSlug(p.Slug). + SetCreatedBy(p.CreatedBy). + SetOwnerID(p.OwnerID) + + if p.Visibility != "" { + create.SetVisibility(p.Visibility) + } + if p.GitRemote != "" { + create.SetGitRemote(p.GitRemote) + } + if p.DefaultRuntimeBrokerID != "" { + create.SetDefaultRuntimeBrokerID(p.DefaultRuntimeBrokerID) + } + if p.Labels != nil { + create.SetLabels(p.Labels) + } + if p.Annotations != nil { + create.SetAnnotations(p.Annotations) + } + if len(p.SharedDirs) > 0 { + create.SetSharedDirs(marshalRawJSON(p.SharedDirs)) + } + if p.GitHubInstallationID != nil { + create.SetGithubInstallationID(*p.GitHubInstallationID) + } + if p.GitHubPermissions != nil { + create.SetGithubPermissions(marshalRawJSON(p.GitHubPermissions)) + } + if p.GitHubAppStatus != nil { + create.SetGithubAppStatus(marshalRawJSON(p.GitHubAppStatus)) + } + if p.GitIdentity != nil { + create.SetGitIdentity(marshalRawJSON(p.GitIdentity)) + } + + created, err := create.Save(ctx) + if err != nil { + return mapError(err) + } + + p.Created = created.Created + p.Updated = created.Updated + if p.Visibility == "" { + p.Visibility = created.Visibility + } + return nil +} + +// GetProject retrieves a project by ID, including computed fields. +func (s *ProjectStore) GetProject(ctx context.Context, id string) (*store.Project, error) { + uid, err := parseGetID(id) + if err != nil { + return nil, err + } + + p, err := s.client.Project.Get(ctx, uid) + if err != nil { + return nil, mapError(err) + } + + sp := entProjectToStore(p) + if err := s.populateProjectComputed(ctx, sp, uid); err != nil { + return nil, err + } + return sp, nil +} + +// GetProjectBySlug retrieves a project by its exact (case-sensitive) slug. +func (s *ProjectStore) GetProjectBySlug(ctx context.Context, slug string) (*store.Project, error) { + p, err := s.client.Project.Query().Where(project.SlugEQ(slug)).Only(ctx) + if err != nil { + return nil, mapError(err) + } + sp := entProjectToStore(p) + if err := s.populateProjectComputed(ctx, sp, p.ID); err != nil { + return nil, err + } + return sp, nil +} + +// GetProjectBySlugCaseInsensitive retrieves a project by slug, ignoring case. +func (s *ProjectStore) GetProjectBySlugCaseInsensitive(ctx context.Context, slug string) (*store.Project, error) { + p, err := s.client.Project.Query().Where(project.SlugEqualFold(slug)).First(ctx) + if err != nil { + return nil, mapError(err) + } + sp := entProjectToStore(p) + if err := s.populateProjectComputed(ctx, sp, p.ID); err != nil { + return nil, err + } + return sp, nil +} + +// GetProjectsByGitRemote returns all projects matching the git remote URL, +// ordered by creation time ascending. Returns an empty slice if none match. +func (s *ProjectStore) GetProjectsByGitRemote(ctx context.Context, gitRemote string) ([]*store.Project, error) { + rows, err := s.client.Project.Query(). + Where(project.GitRemoteEQ(gitRemote)). + Order(ent.Asc(project.FieldCreated)). + All(ctx) + if err != nil { + return nil, err + } + + projects := make([]*store.Project, 0, len(rows)) + for _, p := range rows { + sp := entProjectToStore(p) + if err := s.populateProjectComputed(ctx, sp, p.ID); err != nil { + return nil, err + } + projects = append(projects, sp) + } + return projects, nil +} + +// NextAvailableSlug returns baseSlug if free, else baseSlug-1, baseSlug-2, ... +func (s *ProjectStore) NextAvailableSlug(ctx context.Context, baseSlug string) (string, error) { + exists, err := s.client.Project.Query().Where(project.SlugEQ(baseSlug)).Exist(ctx) + if err != nil { + return "", err + } + if !exists { + return baseSlug, nil + } + for i := 1; ; i++ { + candidate := fmt.Sprintf("%s-%d", baseSlug, i) + exists, err := s.client.Project.Query().Where(project.SlugEQ(candidate)).Exist(ctx) + if err != nil { + return "", err + } + if !exists { + return candidate, nil + } + } +} + +// UpdateProject updates an existing project. +func (s *ProjectStore) UpdateProject(ctx context.Context, p *store.Project) error { + uid, err := parseUUID(p.ID) + if err != nil { + return err + } + + update := s.client.Project.UpdateOneID(uid). + SetName(p.Name). + SetSlug(p.Slug). + SetOwnerID(p.OwnerID). + SetVisibility(p.Visibility) + + if p.GitRemote != "" { + update.SetGitRemote(p.GitRemote) + } else { + update.ClearGitRemote() + } + if p.DefaultRuntimeBrokerID != "" { + update.SetDefaultRuntimeBrokerID(p.DefaultRuntimeBrokerID) + } else { + update.ClearDefaultRuntimeBrokerID() + } + if p.Labels != nil { + update.SetLabels(p.Labels) + } else { + update.ClearLabels() + } + if p.Annotations != nil { + update.SetAnnotations(p.Annotations) + } else { + update.ClearAnnotations() + } + if len(p.SharedDirs) > 0 { + update.SetSharedDirs(marshalRawJSON(p.SharedDirs)) + } else { + update.ClearSharedDirs() + } + if p.GitHubInstallationID != nil { + update.SetGithubInstallationID(*p.GitHubInstallationID) + } else { + update.ClearGithubInstallationID() + } + if p.GitHubPermissions != nil { + update.SetGithubPermissions(marshalRawJSON(p.GitHubPermissions)) + } else { + update.ClearGithubPermissions() + } + if p.GitHubAppStatus != nil { + update.SetGithubAppStatus(marshalRawJSON(p.GitHubAppStatus)) + } else { + update.ClearGithubAppStatus() + } + if p.GitIdentity != nil { + update.SetGitIdentity(marshalRawJSON(p.GitIdentity)) + } else { + update.ClearGitIdentity() + } + + updated, err := update.Save(ctx) + if err != nil { + return mapError(err) + } + p.Updated = updated.Updated + return nil +} + +// DeleteProject removes a project by ID. +func (s *ProjectStore) DeleteProject(ctx context.Context, id string) error { + uid, err := parseUUID(id) + if err != nil { + return err + } + if err := s.client.Project.DeleteOneID(uid).Exec(ctx); err != nil { + return mapError(err) + } + return nil +} + +// ListProjects returns projects matching the filter criteria. +func (s *ProjectStore) ListProjects(ctx context.Context, filter store.ProjectFilter, opts store.ListOptions) (*store.ListResult[store.Project], error) { + query := s.client.Project.Query() + + // Membership / ownership filtering mirrors the SQLite precedence: + // MemberOrOwnerIDs > MemberProjectIDs > OwnerID. + switch { + case len(filter.MemberOrOwnerIDs) > 0: + ids, err := parseUUIDs(filter.MemberOrOwnerIDs) + if err != nil { + return nil, err + } + if filter.OwnerID != "" { + query.Where(project.Or(project.IDIn(ids...), project.OwnerIDEQ(filter.OwnerID))) + } else { + query.Where(project.IDIn(ids...)) + } + case len(filter.MemberProjectIDs) > 0: + ids, err := parseUUIDs(filter.MemberProjectIDs) + if err != nil { + return nil, err + } + query.Where(project.IDIn(ids...)) + case filter.OwnerID != "": + query.Where(project.OwnerIDEQ(filter.OwnerID)) + } + + if filter.ExcludeOwnerID != "" { + query.Where(project.OwnerIDNEQ(filter.ExcludeOwnerID)) + } + if filter.Visibility != "" { + query.Where(project.VisibilityEQ(filter.Visibility)) + } + if filter.GitRemote != "" { + query.Where(project.GitRemoteEQ(filter.GitRemote)) + } else if filter.GitRemotePrefix != "" { + query.Where(project.GitRemoteHasPrefix(filter.GitRemotePrefix)) + } + if filter.BrokerID != "" { + brokerUID, err := parseUUID(filter.BrokerID) + if err != nil { + return nil, err + } + projectIDs, err := s.client.ProjectContributor.Query(). + Where(projectcontributor.BrokerIDEQ(brokerUID)). + Select(projectcontributor.FieldProjectID). + Strings(ctx) + if err != nil { + return nil, err + } + ids, err := parseUUIDs(projectIDs) + if err != nil { + return nil, err + } + query.Where(project.IDIn(ids...)) + } + if filter.Name != "" { + query.Where(project.NameEqualFold(filter.Name)) + } + if filter.Slug != "" { + query.Where(project.SlugEqualFold(filter.Slug)) + } + + totalCount, err := query.Clone().Count(ctx) + if err != nil { + return nil, err + } + + limit := opts.Limit + if limit <= 0 { + limit = 50 + } + + rows, err := query. + Order(ent.Desc(project.FieldCreated)). + Limit(limit). + All(ctx) + if err != nil { + return nil, err + } + + items := make([]store.Project, 0, len(rows)) + for _, p := range rows { + sp := entProjectToStore(p) + if err := s.populateProjectComputed(ctx, sp, p.ID); err != nil { + return nil, err + } + items = append(items, *sp) + } + + return &store.ListResult[store.Project]{ + Items: items, + TotalCount: totalCount, + }, nil +} + +// populateProjectComputed fills the computed (non-persisted) fields on a project: +// AgentCount, ActiveBrokerCount and ProjectType. This mirrors the derivations +// performed by the legacy SQLite store on read. +func (s *ProjectStore) populateProjectComputed(ctx context.Context, p *store.Project, uid uuid.UUID) error { + agentCount, err := s.client.Agent.Query().Where(agent.ProjectIDEQ(uid)).Count(ctx) + if err != nil { + return err + } + p.AgentCount = agentCount + + contribs, err := s.client.ProjectContributor.Query(). + Where(projectcontributor.ProjectIDEQ(uid)). + All(ctx) + if err != nil { + return err + } + + onlineContrib := 0 + contribBrokerIDs := make([]uuid.UUID, 0, len(contribs)) + linked := false + for _, c := range contribs { + contribBrokerIDs = append(contribBrokerIDs, c.BrokerID) + if c.Status == store.BrokerStatusOnline { + onlineContrib++ + } + // A contributor with a local path outside ~/.scion/projects/ indicates a + // pre-existing local project that was linked to the hub. + if c.LocalPath != "" && !strings.Contains(c.LocalPath, "/.scion/projects/") { + linked = true + } + } + + autoQuery := s.client.RuntimeBroker.Query().Where( + runtimebroker.AutoProvide(true), + runtimebroker.StatusEQ(store.BrokerStatusOnline), + ) + if len(contribBrokerIDs) > 0 { + autoQuery = autoQuery.Where(runtimebroker.IDNotIn(contribBrokerIDs...)) + } + autoOnline, err := autoQuery.Count(ctx) + if err != nil { + return err + } + p.ActiveBrokerCount = onlineContrib + autoOnline + + if linked { + p.ProjectType = store.ProjectTypeLinked + } else { + p.ProjectType = store.ProjectTypeHubManaged + } + return nil +} + +// parseUUIDs parses a slice of string UUIDs, skipping any that fail to parse. +func parseUUIDs(ids []string) ([]uuid.UUID, error) { + out := make([]uuid.UUID, 0, len(ids)) + for _, id := range ids { + uid, err := uuid.Parse(id) + if err != nil { + continue + } + out = append(out, uid) + } + return out, nil +} + +// ============================================================================= +// RuntimeBroker operations +// ============================================================================= + +// entBrokerToStore converts an Ent RuntimeBroker entity to a store model. +func entBrokerToStore(b *ent.RuntimeBroker) *store.RuntimeBroker { + sb := &store.RuntimeBroker{ + ID: b.ID.String(), + Name: b.Name, + Slug: b.Slug, + Version: b.Version, + Status: b.Status, + ConnectionState: b.ConnectionState, + Endpoint: b.Endpoint, + AutoProvide: b.AutoProvide, + Created: b.Created, + Updated: b.Updated, + CreatedBy: b.CreatedBy, + } + if b.LastHeartbeat != nil { + sb.LastHeartbeat = *b.LastHeartbeat + } + unmarshalRawJSON(b.Capabilities, &sb.Capabilities) + // Profiles are persisted in the "runtimes" column (legacy naming). + unmarshalRawJSON(b.Runtimes, &sb.Profiles) + unmarshalRawJSON(b.Labels, &sb.Labels) + unmarshalRawJSON(b.Annotations, &sb.Annotations) + return sb +} + +// CreateRuntimeBroker creates a new runtime broker record. +func (s *ProjectStore) CreateRuntimeBroker(ctx context.Context, b *store.RuntimeBroker) error { + uid, err := parseUUID(b.ID) + if err != nil { + return err + } + + create := s.client.RuntimeBroker.Create(). + SetID(uid). + SetName(b.Name). + SetSlug(b.Slug). + SetEndpoint(b.Endpoint). + SetAutoProvide(b.AutoProvide). + SetCapabilities(marshalRawJSON(b.Capabilities)). + SetRuntimes(marshalRawJSON(b.Profiles)). + SetLabels(marshalRawJSON(b.Labels)). + SetAnnotations(marshalRawJSON(b.Annotations)) + + if b.Version != "" { + create.SetVersion(b.Version) + } + if b.Status != "" { + create.SetStatus(b.Status) + } + if b.ConnectionState != "" { + create.SetConnectionState(b.ConnectionState) + } + if !b.LastHeartbeat.IsZero() { + create.SetLastHeartbeat(b.LastHeartbeat) + } + if b.CreatedBy != "" { + create.SetCreatedBy(b.CreatedBy) + } + + created, err := create.Save(ctx) + if err != nil { + return mapError(err) + } + b.Created = created.Created + b.Updated = created.Updated + if b.Status == "" { + b.Status = created.Status + } + if b.ConnectionState == "" { + b.ConnectionState = created.ConnectionState + } + return nil +} + +// GetRuntimeBroker retrieves a runtime broker by ID. +func (s *ProjectStore) GetRuntimeBroker(ctx context.Context, id string) (*store.RuntimeBroker, error) { + uid, err := parseGetID(id) + if err != nil { + return nil, err + } + b, err := s.client.RuntimeBroker.Get(ctx, uid) + if err != nil { + return nil, mapError(err) + } + return entBrokerToStore(b), nil +} + +// GetRuntimeBrokerByName retrieves a runtime broker by name (case-insensitive). +func (s *ProjectStore) GetRuntimeBrokerByName(ctx context.Context, name string) (*store.RuntimeBroker, error) { + b, err := s.client.RuntimeBroker.Query(). + Where(runtimebroker.NameEqualFold(name)). + First(ctx) + if err != nil { + return nil, mapError(err) + } + return entBrokerToStore(b), nil +} + +// UpdateRuntimeBroker updates an existing runtime broker using an optimistic +// concurrency (version-CAS) loop on the internal lock_version token so that +// concurrent writers cannot silently clobber one another. This is portable +// across SQLite (tests) and Postgres (production) without SELECT ... FOR UPDATE. +func (s *ProjectStore) UpdateRuntimeBroker(ctx context.Context, b *store.RuntimeBroker) error { + uid, err := parseUUID(b.ID) + if err != nil { + return err + } + + now := time.Now() + for attempt := 0; attempt < maxCASRetries; attempt++ { + cur, err := s.client.RuntimeBroker.Get(ctx, uid) + if err != nil { + return mapError(err) + } + + affected, err := s.client.RuntimeBroker.Update(). + Where(runtimebroker.IDEQ(uid), runtimebroker.LockVersionEQ(cur.LockVersion)). + SetName(b.Name). + SetSlug(b.Slug). + SetVersion(b.Version). + SetStatus(b.Status). + SetConnectionState(b.ConnectionState). + SetLastHeartbeat(b.LastHeartbeat). + SetCapabilities(marshalRawJSON(b.Capabilities)). + SetRuntimes(marshalRawJSON(b.Profiles)). + SetLabels(marshalRawJSON(b.Labels)). + SetAnnotations(marshalRawJSON(b.Annotations)). + SetEndpoint(b.Endpoint). + SetAutoProvide(b.AutoProvide). + SetUpdated(now). + AddLockVersion(1). + Save(ctx) + if err != nil { + return mapError(err) + } + if affected == 1 { + b.Updated = now + return nil + } + // affected == 0: another writer advanced lock_version between our read + // and write — retry with the fresh value. + } + return store.ErrVersionConflict +} + +// DeleteRuntimeBroker removes a runtime broker by ID. +func (s *ProjectStore) DeleteRuntimeBroker(ctx context.Context, id string) error { + uid, err := parseUUID(id) + if err != nil { + return err + } + if err := s.client.RuntimeBroker.DeleteOneID(uid).Exec(ctx); err != nil { + return mapError(err) + } + return nil +} + +// ListRuntimeBrokers returns runtime brokers matching the filter criteria. +func (s *ProjectStore) ListRuntimeBrokers(ctx context.Context, filter store.RuntimeBrokerFilter, opts store.ListOptions) (*store.ListResult[store.RuntimeBroker], error) { + query := s.client.RuntimeBroker.Query() + + if filter.Status != "" { + query.Where(runtimebroker.StatusEQ(filter.Status)) + } + if filter.ProjectID != "" { + projectUID, err := parseUUID(filter.ProjectID) + if err != nil { + return nil, err + } + brokerIDStrs, err := s.client.ProjectContributor.Query(). + Where(projectcontributor.ProjectIDEQ(projectUID)). + Select(projectcontributor.FieldBrokerID). + Strings(ctx) + if err != nil { + return nil, err + } + brokerIDs, err := parseUUIDs(brokerIDStrs) + if err != nil { + return nil, err + } + // A broker provides for a project if it is an explicit contributor OR it + // is configured to auto-provide for all projects. + if len(brokerIDs) > 0 { + query.Where(runtimebroker.Or( + runtimebroker.IDIn(brokerIDs...), + runtimebroker.AutoProvide(true), + )) + } else { + query.Where(runtimebroker.AutoProvide(true)) + } + } + if filter.Name != "" { + query.Where(runtimebroker.NameEqualFold(filter.Name)) + } + if filter.AutoProvide != nil { + query.Where(runtimebroker.AutoProvide(*filter.AutoProvide)) + } + + totalCount, err := query.Clone().Count(ctx) + if err != nil { + return nil, err + } + + limit := opts.Limit + if limit <= 0 { + limit = 50 + } + + rows, err := query. + Order(ent.Desc(runtimebroker.FieldCreated)). + Limit(limit). + All(ctx) + if err != nil { + return nil, err + } + + items := make([]store.RuntimeBroker, 0, len(rows)) + for _, b := range rows { + items = append(items, *entBrokerToStore(b)) + } + return &store.ListResult[store.RuntimeBroker]{ + Items: items, + TotalCount: totalCount, + }, nil +} + +// UpdateRuntimeBrokerHeartbeat updates the broker's status and last-heartbeat +// timestamp. It uses the same version-CAS loop as UpdateRuntimeBroker so that a +// high-frequency heartbeat cannot lose an interleaved write; the bump on +// lock_version serializes concurrent heartbeats on both SQLite and Postgres. +func (s *ProjectStore) UpdateRuntimeBrokerHeartbeat(ctx context.Context, id string, status string) error { + uid, err := parseUUID(id) + if err != nil { + return err + } + + now := time.Now() + for attempt := 0; attempt < maxCASRetries; attempt++ { + cur, err := s.client.RuntimeBroker.Get(ctx, uid) + if err != nil { + return mapError(err) + } + affected, err := s.client.RuntimeBroker.Update(). + Where(runtimebroker.IDEQ(uid), runtimebroker.LockVersionEQ(cur.LockVersion)). + SetStatus(status). + SetLastHeartbeat(now). + SetUpdated(now). + AddLockVersion(1). + Save(ctx) + if err != nil { + return mapError(err) + } + if affected == 1 { + return nil + } + } + return store.ErrVersionConflict +} + +// ============================================================================= +// ProjectProvider (project_contributors) operations +// ============================================================================= + +// entContributorToStore converts an Ent ProjectContributor to a store model. +func entContributorToStore(c *ent.ProjectContributor) store.ProjectProvider { + pp := store.ProjectProvider{ + ProjectID: c.ProjectID.String(), + BrokerID: c.BrokerID.String(), + BrokerName: c.BrokerName, + LocalPath: c.LocalPath, + Status: c.Status, + LinkedBy: c.LinkedBy, + } + if c.LastSeen != nil { + pp.LastSeen = *c.LastSeen + } + if c.LinkedAt != nil { + pp.LinkedAt = *c.LinkedAt + } + return pp +} + +// AddProjectProvider adds (or replaces) a broker as a provider to a project. +// Mirrors the legacy INSERT OR REPLACE via Ent's OnConflict upsert keyed on the +// (project_id, broker_id) unique index. +func (s *ProjectStore) AddProjectProvider(ctx context.Context, provider *store.ProjectProvider) error { + projectUID, err := parseUUID(provider.ProjectID) + if err != nil { + return err + } + brokerUID, err := parseUUID(provider.BrokerID) + if err != nil { + return err + } + + if provider.LinkedAt.IsZero() && provider.LinkedBy != "" { + provider.LinkedAt = time.Now() + } + + status := provider.Status + if status == "" { + status = store.BrokerStatusOffline + } + + create := s.client.ProjectContributor.Create(). + SetProjectID(projectUID). + SetBrokerID(brokerUID). + SetBrokerName(provider.BrokerName). + SetLocalPath(provider.LocalPath). + SetStatus(status) + if !provider.LastSeen.IsZero() { + create.SetLastSeen(provider.LastSeen) + } + if provider.LinkedBy != "" { + create.SetLinkedBy(provider.LinkedBy) + } + if !provider.LinkedAt.IsZero() { + create.SetLinkedAt(provider.LinkedAt) + } + + return create. + OnConflictColumns(projectcontributor.FieldProjectID, projectcontributor.FieldBrokerID). + UpdateNewValues(). + Exec(ctx) +} + +// RemoveProjectProvider removes a broker from a project's providers. +func (s *ProjectStore) RemoveProjectProvider(ctx context.Context, projectID, brokerID string) error { + projectUID, err := parseUUID(projectID) + if err != nil { + return err + } + brokerUID, err := parseUUID(brokerID) + if err != nil { + return err + } + n, err := s.client.ProjectContributor.Delete(). + Where( + projectcontributor.ProjectIDEQ(projectUID), + projectcontributor.BrokerIDEQ(brokerUID), + ).Exec(ctx) + if err != nil { + return err + } + if n == 0 { + return store.ErrNotFound + } + return nil +} + +// GetProjectProvider returns a specific provider by project and broker ID. +func (s *ProjectStore) GetProjectProvider(ctx context.Context, projectID, brokerID string) (*store.ProjectProvider, error) { + projectUID, err := parseUUID(projectID) + if err != nil { + return nil, err + } + brokerUID, err := parseUUID(brokerID) + if err != nil { + return nil, err + } + c, err := s.client.ProjectContributor.Query(). + Where( + projectcontributor.ProjectIDEQ(projectUID), + projectcontributor.BrokerIDEQ(brokerUID), + ).Only(ctx) + if err != nil { + return nil, mapError(err) + } + pp := entContributorToStore(c) + return &pp, nil +} + +// GetProjectProviders returns all providers for a project. +func (s *ProjectStore) GetProjectProviders(ctx context.Context, projectID string) ([]store.ProjectProvider, error) { + projectUID, err := parseUUID(projectID) + if err != nil { + return nil, err + } + rows, err := s.client.ProjectContributor.Query(). + Where(projectcontributor.ProjectIDEQ(projectUID)). + All(ctx) + if err != nil { + return nil, err + } + providers := make([]store.ProjectProvider, 0, len(rows)) + for _, c := range rows { + providers = append(providers, entContributorToStore(c)) + } + return providers, nil +} + +// GetBrokerProjects returns all projects a broker provides for. +func (s *ProjectStore) GetBrokerProjects(ctx context.Context, brokerID string) ([]store.ProjectProvider, error) { + brokerUID, err := parseUUID(brokerID) + if err != nil { + return nil, err + } + rows, err := s.client.ProjectContributor.Query(). + Where(projectcontributor.BrokerIDEQ(brokerUID)). + All(ctx) + if err != nil { + return nil, err + } + providers := make([]store.ProjectProvider, 0, len(rows)) + for _, c := range rows { + providers = append(providers, entContributorToStore(c)) + } + return providers, nil +} + +// UpdateProviderStatus updates a provider's status and last-seen timestamp. +func (s *ProjectStore) UpdateProviderStatus(ctx context.Context, projectID, brokerID, status string) error { + projectUID, err := parseUUID(projectID) + if err != nil { + return err + } + brokerUID, err := parseUUID(brokerID) + if err != nil { + return err + } + n, err := s.client.ProjectContributor.Update(). + Where( + projectcontributor.ProjectIDEQ(projectUID), + projectcontributor.BrokerIDEQ(brokerUID), + ). + SetStatus(status). + SetLastSeen(time.Now()). + Save(ctx) + if err != nil { + return err + } + if n == 0 { + return store.ErrNotFound + } + return nil +} + +// ============================================================================= +// ProjectSyncState (project_sync_state) operations +// ============================================================================= + +// entSyncStateToStore converts an Ent ProjectSyncState to a store model. +func entSyncStateToStore(s *ent.ProjectSyncState) *store.ProjectSyncState { + state := &store.ProjectSyncState{ + ProjectID: s.ProjectID.String(), + BrokerID: s.BrokerID, + LastCommitSHA: s.LastCommitSha, + FileCount: s.FileCount, + TotalBytes: s.TotalBytes, + } + if s.LastSyncTime != nil { + state.LastSyncTime = s.LastSyncTime + } + return state +} + +// UpsertProjectSyncState creates or updates sync state for a project (optionally +// per broker). Mirrors the legacy ON CONFLICT(project_id, broker_id) upsert. +func (s *ProjectStore) UpsertProjectSyncState(ctx context.Context, state *store.ProjectSyncState) error { + projectUID, err := parseUUID(state.ProjectID) + if err != nil { + return err + } + + create := s.client.ProjectSyncState.Create(). + SetProjectID(projectUID). + SetBrokerID(state.BrokerID). + SetLastCommitSha(state.LastCommitSHA). + SetFileCount(state.FileCount). + SetTotalBytes(state.TotalBytes) + if state.LastSyncTime != nil { + create.SetLastSyncTime(*state.LastSyncTime) + } + + return create. + OnConflictColumns(projectsyncstate.FieldProjectID, projectsyncstate.FieldBrokerID). + UpdateNewValues(). + Exec(ctx) +} + +// GetProjectSyncState retrieves sync state for a project and optional broker. +func (s *ProjectStore) GetProjectSyncState(ctx context.Context, projectID, brokerID string) (*store.ProjectSyncState, error) { + projectUID, err := parseUUID(projectID) + if err != nil { + return nil, err + } + row, err := s.client.ProjectSyncState.Query(). + Where( + projectsyncstate.ProjectIDEQ(projectUID), + projectsyncstate.BrokerIDEQ(brokerID), + ).Only(ctx) + if err != nil { + return nil, mapError(err) + } + return entSyncStateToStore(row), nil +} + +// ListProjectSyncStates returns all sync states for a project, ordered by broker. +func (s *ProjectStore) ListProjectSyncStates(ctx context.Context, projectID string) ([]store.ProjectSyncState, error) { + projectUID, err := parseUUID(projectID) + if err != nil { + return nil, err + } + rows, err := s.client.ProjectSyncState.Query(). + Where(projectsyncstate.ProjectIDEQ(projectUID)). + Order(ent.Asc(projectsyncstate.FieldBrokerID)). + All(ctx) + if err != nil { + return nil, err + } + states := make([]store.ProjectSyncState, 0, len(rows)) + for _, row := range rows { + states = append(states, *entSyncStateToStore(row)) + } + return states, nil +} + +// DeleteProjectSyncState removes sync state for a project and optional broker. +func (s *ProjectStore) DeleteProjectSyncState(ctx context.Context, projectID, brokerID string) error { + projectUID, err := parseUUID(projectID) + if err != nil { + return err + } + n, err := s.client.ProjectSyncState.Delete(). + Where( + projectsyncstate.ProjectIDEQ(projectUID), + projectsyncstate.BrokerIDEQ(brokerID), + ).Exec(ctx) + if err != nil { + return err + } + if n == 0 { + return store.ErrNotFound + } + return nil +} diff --git a/pkg/store/entadapter/project_store_test.go b/pkg/store/entadapter/project_store_test.go new file mode 100644 index 000000000..0a1e75a26 --- /dev/null +++ b/pkg/store/entadapter/project_store_test.go @@ -0,0 +1,622 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build !no_sqlite + +package entadapter + +import ( + "context" + "testing" + "time" + + "github.com/GoogleCloudPlatform/scion/pkg/api" + "github.com/GoogleCloudPlatform/scion/pkg/store" + "github.com/GoogleCloudPlatform/scion/pkg/store/enttest" + "github.com/google/uuid" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func newTestProjectStore(t *testing.T) *ProjectStore { + t.Helper() + client := enttest.NewClient(t) + return NewProjectStore(client) +} + +func newProject(seq int) *store.Project { + id := uuid.NewString() + return &store.Project{ + ID: id, + Name: "Project " + id[:8], + Slug: "project-" + id[:8], + Visibility: store.VisibilityPrivate, + Labels: map[string]string{"seq": id[:4]}, + } +} + +func TestProject_CreateGet(t *testing.T) { + ps := newTestProjectStore(t) + ctx := context.Background() + + p := newProject(1) + p.GitRemote = "https://github.com/acme/repo.git" + p.OwnerID = uuid.NewString() + require.NoError(t, ps.CreateProject(ctx, p)) + assert.False(t, p.Created.IsZero()) + assert.False(t, p.Updated.IsZero()) + + got, err := ps.GetProject(ctx, p.ID) + require.NoError(t, err) + assert.Equal(t, p.ID, got.ID) + assert.Equal(t, p.Name, got.Name) + assert.Equal(t, p.Slug, got.Slug) + assert.Equal(t, "https://github.com/acme/repo.git", got.GitRemote) + assert.Equal(t, store.VisibilityPrivate, got.Visibility) + assert.Equal(t, store.ProjectTypeHubManaged, got.ProjectType) // computed default +} + +func TestProject_CreateDuplicateSlug(t *testing.T) { + ps := newTestProjectStore(t) + ctx := context.Background() + + p1 := newProject(1) + p1.Slug = "dup-slug" + require.NoError(t, ps.CreateProject(ctx, p1)) + + p2 := newProject(2) + p2.Slug = "dup-slug" + err := ps.CreateProject(ctx, p2) + assert.ErrorIs(t, err, store.ErrAlreadyExists) +} + +func TestProject_GetNotFound(t *testing.T) { + ps := newTestProjectStore(t) + _, err := ps.GetProject(context.Background(), uuid.NewString()) + assert.ErrorIs(t, err, store.ErrNotFound) +} + +func TestProject_GetBySlugCaseInsensitive(t *testing.T) { + ps := newTestProjectStore(t) + ctx := context.Background() + + p := newProject(1) + p.Slug = "MixedCase-Slug" + require.NoError(t, ps.CreateProject(ctx, p)) + + got, err := ps.GetProjectBySlugCaseInsensitive(ctx, "mixedcase-slug") + require.NoError(t, err) + assert.Equal(t, p.ID, got.ID) + + // Exact (case-sensitive) lookup must not match a different case. + _, err = ps.GetProjectBySlug(ctx, "mixedcase-slug") + assert.ErrorIs(t, err, store.ErrNotFound) +} + +func TestProject_GetByGitRemote(t *testing.T) { + ps := newTestProjectStore(t) + ctx := context.Background() + + remote := "https://github.com/acme/shared.git" + for i := 0; i < 2; i++ { + p := newProject(i) + p.GitRemote = remote + require.NoError(t, ps.CreateProject(ctx, p)) + } + other := newProject(99) + other.GitRemote = "https://github.com/acme/other.git" + require.NoError(t, ps.CreateProject(ctx, other)) + + got, err := ps.GetProjectsByGitRemote(ctx, remote) + require.NoError(t, err) + assert.Len(t, got, 2) + + none, err := ps.GetProjectsByGitRemote(ctx, "https://github.com/none.git") + require.NoError(t, err) + assert.Empty(t, none) +} + +func TestProject_NextAvailableSlug(t *testing.T) { + ps := newTestProjectStore(t) + ctx := context.Background() + + slug, err := ps.NextAvailableSlug(ctx, "myproj") + require.NoError(t, err) + assert.Equal(t, "myproj", slug) + + p := newProject(1) + p.Slug = "myproj" + require.NoError(t, ps.CreateProject(ctx, p)) + + slug, err = ps.NextAvailableSlug(ctx, "myproj") + require.NoError(t, err) + assert.Equal(t, "myproj-1", slug) + + p2 := newProject(2) + p2.Slug = "myproj-1" + require.NoError(t, ps.CreateProject(ctx, p2)) + + slug, err = ps.NextAvailableSlug(ctx, "myproj") + require.NoError(t, err) + assert.Equal(t, "myproj-2", slug) +} + +func TestProject_Update(t *testing.T) { + ps := newTestProjectStore(t) + ctx := context.Background() + + p := newProject(1) + require.NoError(t, ps.CreateProject(ctx, p)) + + p.Name = "Renamed" + p.GitRemote = "https://github.com/acme/renamed.git" + installID := int64(424242) + p.GitHubInstallationID = &installID + require.NoError(t, ps.UpdateProject(ctx, p)) + + got, err := ps.GetProject(ctx, p.ID) + require.NoError(t, err) + assert.Equal(t, "Renamed", got.Name) + assert.Equal(t, "https://github.com/acme/renamed.git", got.GitRemote) + require.NotNil(t, got.GitHubInstallationID) + assert.Equal(t, int64(424242), *got.GitHubInstallationID) +} + +func TestProject_SharedDirsRoundTrip(t *testing.T) { + ps := newTestProjectStore(t) + ctx := context.Background() + + p := newProject(1) + p.SharedDirs = []api.SharedDir{{Name: "build-cache", ReadOnly: true, InWorkspace: true}} + require.NoError(t, ps.CreateProject(ctx, p)) + + got, err := ps.GetProject(ctx, p.ID) + require.NoError(t, err) + require.Len(t, got.SharedDirs, 1) + assert.Equal(t, "build-cache", got.SharedDirs[0].Name) + assert.True(t, got.SharedDirs[0].ReadOnly) + assert.True(t, got.SharedDirs[0].InWorkspace) +} + +func TestProject_Delete(t *testing.T) { + ps := newTestProjectStore(t) + ctx := context.Background() + + p := newProject(1) + require.NoError(t, ps.CreateProject(ctx, p)) + require.NoError(t, ps.DeleteProject(ctx, p.ID)) + + _, err := ps.GetProject(ctx, p.ID) + assert.ErrorIs(t, err, store.ErrNotFound) + + assert.ErrorIs(t, ps.DeleteProject(ctx, p.ID), store.ErrNotFound) +} + +func TestProject_ListFilters(t *testing.T) { + ps := newTestProjectStore(t) + ctx := context.Background() + + owner := uuid.NewString() + pub := newProject(1) + pub.Visibility = "public" + pub.OwnerID = owner + require.NoError(t, ps.CreateProject(ctx, pub)) + + priv := newProject(2) + priv.Visibility = store.VisibilityPrivate + require.NoError(t, ps.CreateProject(ctx, priv)) + + all, err := ps.ListProjects(ctx, store.ProjectFilter{}, store.ListOptions{}) + require.NoError(t, err) + assert.Equal(t, 2, all.TotalCount) + + byVis, err := ps.ListProjects(ctx, store.ProjectFilter{Visibility: "public"}, store.ListOptions{}) + require.NoError(t, err) + assert.Equal(t, 1, byVis.TotalCount) + require.Len(t, byVis.Items, 1) + assert.Equal(t, pub.ID, byVis.Items[0].ID) + + byOwner, err := ps.ListProjects(ctx, store.ProjectFilter{OwnerID: owner}, store.ListOptions{}) + require.NoError(t, err) + assert.Equal(t, 1, byOwner.TotalCount) + + byName, err := ps.ListProjects(ctx, store.ProjectFilter{Name: pub.Name}, store.ListOptions{}) + require.NoError(t, err) + assert.Equal(t, 1, byName.TotalCount) + + limited, err := ps.ListProjects(ctx, store.ProjectFilter{}, store.ListOptions{Limit: 1}) + require.NoError(t, err) + assert.Len(t, limited.Items, 1) + assert.Equal(t, 2, limited.TotalCount) +} + +func TestProject_ComputedAgentCount(t *testing.T) { + ps := newTestProjectStore(t) + ctx := context.Background() + + p := newProject(1) + require.NoError(t, ps.CreateProject(ctx, p)) + uid := uuid.MustParse(p.ID) + + for i := 0; i < 3; i++ { + _, err := ps.client.Agent.Create(). + SetID(uuid.New()). + SetName("agent"). + SetSlug("agent-" + uuid.NewString()[:8]). + SetProjectID(uid). + Save(ctx) + require.NoError(t, err) + } + + got, err := ps.GetProject(ctx, p.ID) + require.NoError(t, err) + assert.Equal(t, 3, got.AgentCount) +} + +func TestProject_ProjectTypeLinked(t *testing.T) { + ps := newTestProjectStore(t) + ctx := context.Background() + + p := newProject(1) + require.NoError(t, ps.CreateProject(ctx, p)) + + // A contributor with a local path outside ~/.scion/projects/ marks it linked. + require.NoError(t, ps.AddProjectProvider(ctx, &store.ProjectProvider{ + ProjectID: p.ID, + BrokerID: uuid.NewString(), + BrokerName: "broker-1", + LocalPath: "/home/user/code/myrepo/.scion", + Status: store.BrokerStatusOnline, + })) + + got, err := ps.GetProject(ctx, p.ID) + require.NoError(t, err) + assert.Equal(t, store.ProjectTypeLinked, got.ProjectType) +} + +// ============================================================================= +// RuntimeBroker +// ============================================================================= + +func newBroker() *store.RuntimeBroker { + id := uuid.NewString() + return &store.RuntimeBroker{ + ID: id, + Name: "broker-" + id[:8], + Slug: "broker-" + id[:8], + Version: "1.0.0", + Status: store.BrokerStatusOnline, + } +} + +func TestBroker_CreateGet(t *testing.T) { + ps := newTestProjectStore(t) + ctx := context.Background() + + b := newBroker() + b.Capabilities = &store.BrokerCapabilities{WebPTY: true, Sync: true} + b.Profiles = []store.BrokerProfile{{Name: "docker-default", Type: "docker", Available: true}} + b.AutoProvide = true + require.NoError(t, ps.CreateRuntimeBroker(ctx, b)) + assert.False(t, b.Created.IsZero()) + + got, err := ps.GetRuntimeBroker(ctx, b.ID) + require.NoError(t, err) + assert.Equal(t, b.Name, got.Name) + assert.Equal(t, "1.0.0", got.Version) + assert.Equal(t, store.BrokerStatusOnline, got.Status) + assert.True(t, got.AutoProvide) + require.NotNil(t, got.Capabilities) + assert.True(t, got.Capabilities.WebPTY) + require.Len(t, got.Profiles, 1) + assert.Equal(t, "docker-default", got.Profiles[0].Name) +} + +func TestBroker_GetByName(t *testing.T) { + ps := newTestProjectStore(t) + ctx := context.Background() + + b := newBroker() + b.Name = "MyBroker" + require.NoError(t, ps.CreateRuntimeBroker(ctx, b)) + + got, err := ps.GetRuntimeBrokerByName(ctx, "mybroker") + require.NoError(t, err) + assert.Equal(t, b.ID, got.ID) + + _, err = ps.GetRuntimeBrokerByName(ctx, "nonexistent") + assert.ErrorIs(t, err, store.ErrNotFound) +} + +func TestBroker_Update(t *testing.T) { + ps := newTestProjectStore(t) + ctx := context.Background() + + b := newBroker() + require.NoError(t, ps.CreateRuntimeBroker(ctx, b)) + + b.Name = "Renamed" + b.Version = "2.0.0" + b.Status = store.BrokerStatusDegraded + require.NoError(t, ps.UpdateRuntimeBroker(ctx, b)) + + got, err := ps.GetRuntimeBroker(ctx, b.ID) + require.NoError(t, err) + assert.Equal(t, "Renamed", got.Name) + assert.Equal(t, "2.0.0", got.Version) + assert.Equal(t, store.BrokerStatusDegraded, got.Status) +} + +func TestBroker_UpdateBumpsLockVersion(t *testing.T) { + ps := newTestProjectStore(t) + ctx := context.Background() + + b := newBroker() + require.NoError(t, ps.CreateRuntimeBroker(ctx, b)) + uid := uuid.MustParse(b.ID) + + before, err := ps.client.RuntimeBroker.Get(ctx, uid) + require.NoError(t, err) + + b.Status = store.BrokerStatusOffline + require.NoError(t, ps.UpdateRuntimeBroker(ctx, b)) + + after, err := ps.client.RuntimeBroker.Get(ctx, uid) + require.NoError(t, err) + assert.Equal(t, before.LockVersion+1, after.LockVersion, "update must advance the lock_version CAS token") +} + +func TestBroker_Heartbeat(t *testing.T) { + ps := newTestProjectStore(t) + ctx := context.Background() + + b := newBroker() + b.Status = store.BrokerStatusOffline + require.NoError(t, ps.CreateRuntimeBroker(ctx, b)) + uid := uuid.MustParse(b.ID) + before, err := ps.client.RuntimeBroker.Get(ctx, uid) + require.NoError(t, err) + + require.NoError(t, ps.UpdateRuntimeBrokerHeartbeat(ctx, b.ID, store.BrokerStatusOnline)) + + got, err := ps.GetRuntimeBroker(ctx, b.ID) + require.NoError(t, err) + assert.Equal(t, store.BrokerStatusOnline, got.Status) + assert.False(t, got.LastHeartbeat.IsZero()) + + after, err := ps.client.RuntimeBroker.Get(ctx, uid) + require.NoError(t, err) + assert.Equal(t, before.LockVersion+1, after.LockVersion) +} + +func TestBroker_HeartbeatNotFound(t *testing.T) { + ps := newTestProjectStore(t) + err := ps.UpdateRuntimeBrokerHeartbeat(context.Background(), uuid.NewString(), store.BrokerStatusOnline) + assert.ErrorIs(t, err, store.ErrNotFound) +} + +func TestBroker_Delete(t *testing.T) { + ps := newTestProjectStore(t) + ctx := context.Background() + + b := newBroker() + require.NoError(t, ps.CreateRuntimeBroker(ctx, b)) + require.NoError(t, ps.DeleteRuntimeBroker(ctx, b.ID)) + _, err := ps.GetRuntimeBroker(ctx, b.ID) + assert.ErrorIs(t, err, store.ErrNotFound) +} + +func TestBroker_ListFilters(t *testing.T) { + ps := newTestProjectStore(t) + ctx := context.Background() + + online := newBroker() + online.Status = store.BrokerStatusOnline + require.NoError(t, ps.CreateRuntimeBroker(ctx, online)) + + offline := newBroker() + offline.Status = store.BrokerStatusOffline + require.NoError(t, ps.CreateRuntimeBroker(ctx, offline)) + + all, err := ps.ListRuntimeBrokers(ctx, store.RuntimeBrokerFilter{}, store.ListOptions{}) + require.NoError(t, err) + assert.Equal(t, 2, all.TotalCount) + + byStatus, err := ps.ListRuntimeBrokers(ctx, store.RuntimeBrokerFilter{Status: store.BrokerStatusOnline}, store.ListOptions{}) + require.NoError(t, err) + assert.Equal(t, 1, byStatus.TotalCount) + + yes := true + autoProvide := newBroker() + autoProvide.AutoProvide = true + require.NoError(t, ps.CreateRuntimeBroker(ctx, autoProvide)) + byAuto, err := ps.ListRuntimeBrokers(ctx, store.RuntimeBrokerFilter{AutoProvide: &yes}, store.ListOptions{}) + require.NoError(t, err) + assert.Equal(t, 1, byAuto.TotalCount) +} + +// ============================================================================= +// ProjectProvider (contributors) +// ============================================================================= + +func TestProvider_UpsertAndGet(t *testing.T) { + ps := newTestProjectStore(t) + ctx := context.Background() + + projectID := uuid.NewString() + brokerID := uuid.NewString() + require.NoError(t, ps.CreateProject(ctx, &store.Project{ID: projectID, Name: "p", Slug: "p-" + projectID[:8], Visibility: store.VisibilityPrivate})) + + prov := &store.ProjectProvider{ + ProjectID: projectID, + BrokerID: brokerID, + BrokerName: "broker-a", + LocalPath: "/tmp/a", + Status: store.BrokerStatusOffline, + LinkedBy: uuid.NewString(), + } + require.NoError(t, ps.AddProjectProvider(ctx, prov)) + assert.False(t, prov.LinkedAt.IsZero(), "LinkedAt should be set when LinkedBy present") + + got, err := ps.GetProjectProvider(ctx, projectID, brokerID) + require.NoError(t, err) + assert.Equal(t, "broker-a", got.BrokerName) + assert.Equal(t, "/tmp/a", got.LocalPath) + assert.Equal(t, store.BrokerStatusOffline, got.Status) + + // Upsert (INSERT OR REPLACE): same (project, broker) updates in place. + prov2 := &store.ProjectProvider{ + ProjectID: projectID, + BrokerID: brokerID, + BrokerName: "broker-a-renamed", + LocalPath: "/tmp/b", + Status: store.BrokerStatusOnline, + } + require.NoError(t, ps.AddProjectProvider(ctx, prov2)) + + providers, err := ps.GetProjectProviders(ctx, projectID) + require.NoError(t, err) + require.Len(t, providers, 1, "upsert must not create a duplicate row") + assert.Equal(t, "broker-a-renamed", providers[0].BrokerName) + assert.Equal(t, store.BrokerStatusOnline, providers[0].Status) +} + +func TestProvider_RemoveAndStatus(t *testing.T) { + ps := newTestProjectStore(t) + ctx := context.Background() + + projectID := uuid.NewString() + brokerID := uuid.NewString() + require.NoError(t, ps.AddProjectProvider(ctx, &store.ProjectProvider{ + ProjectID: projectID, BrokerID: brokerID, BrokerName: "b", Status: store.BrokerStatusOffline, + })) + + require.NoError(t, ps.UpdateProviderStatus(ctx, projectID, brokerID, store.BrokerStatusOnline)) + got, err := ps.GetProjectProvider(ctx, projectID, brokerID) + require.NoError(t, err) + assert.Equal(t, store.BrokerStatusOnline, got.Status) + assert.False(t, got.LastSeen.IsZero()) + + require.NoError(t, ps.RemoveProjectProvider(ctx, projectID, brokerID)) + _, err = ps.GetProjectProvider(ctx, projectID, brokerID) + assert.ErrorIs(t, err, store.ErrNotFound) + + assert.ErrorIs(t, ps.RemoveProjectProvider(ctx, projectID, brokerID), store.ErrNotFound) + assert.ErrorIs(t, ps.UpdateProviderStatus(ctx, projectID, brokerID, store.BrokerStatusOnline), store.ErrNotFound) +} + +func TestProvider_GetBrokerProjects(t *testing.T) { + ps := newTestProjectStore(t) + ctx := context.Background() + + brokerID := uuid.NewString() + for i := 0; i < 2; i++ { + require.NoError(t, ps.AddProjectProvider(ctx, &store.ProjectProvider{ + ProjectID: uuid.NewString(), BrokerID: brokerID, BrokerName: "b", Status: store.BrokerStatusOnline, + })) + } + got, err := ps.GetBrokerProjects(ctx, brokerID) + require.NoError(t, err) + assert.Len(t, got, 2) +} + +func TestProject_ListByBrokerID(t *testing.T) { + ps := newTestProjectStore(t) + ctx := context.Background() + + p := newProject(1) + require.NoError(t, ps.CreateProject(ctx, p)) + brokerID := uuid.NewString() + require.NoError(t, ps.AddProjectProvider(ctx, &store.ProjectProvider{ + ProjectID: p.ID, BrokerID: brokerID, BrokerName: "b", Status: store.BrokerStatusOnline, + })) + // A second project with no contributor for this broker. + require.NoError(t, ps.CreateProject(ctx, newProject(2))) + + res, err := ps.ListProjects(ctx, store.ProjectFilter{BrokerID: brokerID}, store.ListOptions{}) + require.NoError(t, err) + assert.Equal(t, 1, res.TotalCount) + require.Len(t, res.Items, 1) + assert.Equal(t, p.ID, res.Items[0].ID) +} + +// ============================================================================= +// ProjectSyncState +// ============================================================================= + +func TestSyncState_Upsert(t *testing.T) { + ps := newTestProjectStore(t) + ctx := context.Background() + + projectID := uuid.NewString() + now := time.Now().UTC().Truncate(time.Second) + state := &store.ProjectSyncState{ + ProjectID: projectID, + BrokerID: "", // hub-native, project-wide + LastSyncTime: &now, + LastCommitSHA: "abc123", + FileCount: 10, + TotalBytes: 2048, + } + require.NoError(t, ps.UpsertProjectSyncState(ctx, state)) + + got, err := ps.GetProjectSyncState(ctx, projectID, "") + require.NoError(t, err) + assert.Equal(t, "abc123", got.LastCommitSHA) + assert.Equal(t, 10, got.FileCount) + assert.Equal(t, int64(2048), got.TotalBytes) + require.NotNil(t, got.LastSyncTime) + + // Upsert again on the same key updates in place. + state.FileCount = 20 + state.LastCommitSHA = "def456" + require.NoError(t, ps.UpsertProjectSyncState(ctx, state)) + + states, err := ps.ListProjectSyncStates(ctx, projectID) + require.NoError(t, err) + require.Len(t, states, 1) + assert.Equal(t, 20, states[0].FileCount) + assert.Equal(t, "def456", states[0].LastCommitSHA) +} + +func TestSyncState_PerBroker(t *testing.T) { + ps := newTestProjectStore(t) + ctx := context.Background() + + projectID := uuid.NewString() + brokerID := uuid.NewString() + require.NoError(t, ps.UpsertProjectSyncState(ctx, &store.ProjectSyncState{ProjectID: projectID, BrokerID: "", FileCount: 1})) + require.NoError(t, ps.UpsertProjectSyncState(ctx, &store.ProjectSyncState{ProjectID: projectID, BrokerID: brokerID, FileCount: 2})) + + states, err := ps.ListProjectSyncStates(ctx, projectID) + require.NoError(t, err) + assert.Len(t, states, 2) + + perBroker, err := ps.GetProjectSyncState(ctx, projectID, brokerID) + require.NoError(t, err) + assert.Equal(t, 2, perBroker.FileCount) +} + +func TestSyncState_DeleteAndNotFound(t *testing.T) { + ps := newTestProjectStore(t) + ctx := context.Background() + + projectID := uuid.NewString() + _, err := ps.GetProjectSyncState(ctx, projectID, "") + assert.ErrorIs(t, err, store.ErrNotFound) + + require.NoError(t, ps.UpsertProjectSyncState(ctx, &store.ProjectSyncState{ProjectID: projectID, BrokerID: "", FileCount: 1})) + require.NoError(t, ps.DeleteProjectSyncState(ctx, projectID, "")) + assert.ErrorIs(t, ps.DeleteProjectSyncState(ctx, projectID, ""), store.ErrNotFound) +} diff --git a/pkg/store/entadapter/schedule_store.go b/pkg/store/entadapter/schedule_store.go new file mode 100644 index 000000000..81287c063 --- /dev/null +++ b/pkg/store/entadapter/schedule_store.go @@ -0,0 +1,691 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package entadapter + +import ( + "context" + "time" + + "entgo.io/ent/dialect" + entsql "entgo.io/ent/dialect/sql" + "github.com/GoogleCloudPlatform/scion/pkg/ent" + "github.com/GoogleCloudPlatform/scion/pkg/ent/schedule" + "github.com/GoogleCloudPlatform/scion/pkg/ent/scheduledevent" + "github.com/GoogleCloudPlatform/scion/pkg/store" + "github.com/google/uuid" +) + +// ScheduleStore implements store.ScheduleStore and store.ScheduledEventStore +// using the Ent ORM. A single type serves both sub-interfaces, mirroring the +// SQLite backend where schedules and scheduled events share one store. +type ScheduleStore struct { + client *ent.Client +} + +// NewScheduleStore creates a new Ent-backed ScheduleStore. +func NewScheduleStore(client *ent.Client) *ScheduleStore { + return &ScheduleStore{client: client} +} + +// defaultListLimit / maxListLimit mirror the pagination bounds used by the +// SQLite backend so the two backends paginate identically. +const ( + defaultListLimit = 50 + maxListLimit = 200 +) + +func clampLimit(limit int) int { + if limit <= 0 { + return defaultListLimit + } + if limit > maxListLimit { + return maxListLimit + } + return limit +} + +// ============================================================================ +// Schedule <-> store conversion +// ============================================================================ + +func entScheduleToStore(e *ent.Schedule) *store.Schedule { + sc := &store.Schedule{ + ID: e.ID.String(), + ProjectID: e.ProjectID.String(), + Name: e.Name, + CronExpr: e.CronExpr, + EventType: e.EventType, + Payload: e.Payload, + Status: e.Status, + NextRunAt: e.NextRunAt, + LastRunAt: e.LastRunAt, + LastRunStatus: e.LastRunStatus, + LastRunError: e.LastRunError, + RunCount: e.RunCount, + ErrorCount: e.ErrorCount, + CreatedAt: e.Created, + CreatedBy: e.CreatedBy, + UpdatedAt: e.Updated, + } + return sc +} + +// ============================================================================ +// Schedule Operations (Recurring Schedules) +// ============================================================================ + +// CreateSchedule creates a new recurring schedule. +func (s *ScheduleStore) CreateSchedule(ctx context.Context, sc *store.Schedule) error { + if sc.ID == "" || sc.ProjectID == "" || sc.Name == "" || sc.CronExpr == "" { + return store.ErrInvalidInput + } + uid, err := parseUUID(sc.ID) + if err != nil { + return err + } + pid, err := parseUUID(sc.ProjectID) + if err != nil { + return err + } + if sc.Status == "" { + sc.Status = store.ScheduleStatusActive + } + + create := s.client.Schedule.Create(). + SetID(uid). + SetProjectID(pid). + SetName(sc.Name). + SetCronExpr(sc.CronExpr). + SetEventType(sc.EventType). + SetStatus(sc.Status). + SetRunCount(sc.RunCount). + SetErrorCount(sc.ErrorCount) + + if sc.Payload != "" { + create.SetPayload(sc.Payload) + } + if sc.NextRunAt != nil { + create.SetNextRunAt(*sc.NextRunAt) + } + if sc.LastRunAt != nil { + create.SetLastRunAt(*sc.LastRunAt) + } + if sc.LastRunStatus != "" { + create.SetLastRunStatus(sc.LastRunStatus) + } + if sc.LastRunError != "" { + create.SetLastRunError(sc.LastRunError) + } + if sc.CreatedBy != "" { + create.SetCreatedBy(sc.CreatedBy) + } + if !sc.CreatedAt.IsZero() { + create.SetCreated(sc.CreatedAt) + } + if !sc.UpdatedAt.IsZero() { + create.SetUpdated(sc.UpdatedAt) + } + + created, err := create.Save(ctx) + if err != nil { + return mapError(err) + } + sc.CreatedAt = created.Created + sc.UpdatedAt = created.Updated + sc.Status = created.Status + return nil +} + +// GetSchedule retrieves a schedule by ID. +func (s *ScheduleStore) GetSchedule(ctx context.Context, id string) (*store.Schedule, error) { + uid, err := parseGetID(id) + if err != nil { + return nil, err + } + e, err := s.client.Schedule.Get(ctx, uid) + if err != nil { + return nil, mapError(err) + } + return entScheduleToStore(e), nil +} + +// ListSchedules returns schedules matching the filter criteria. +func (s *ScheduleStore) ListSchedules(ctx context.Context, filter store.ScheduleFilter, opts store.ListOptions) (*store.ListResult[store.Schedule], error) { + query := s.client.Schedule.Query() + + if filter.ProjectID != "" { + pid, err := parseUUID(filter.ProjectID) + if err != nil { + return nil, err + } + query.Where(schedule.ProjectIDEQ(pid)) + } + if filter.Status != "" { + query.Where(schedule.StatusEQ(filter.Status)) + } else { + // By default, exclude deleted schedules. + query.Where(schedule.StatusNEQ(store.ScheduleStatusDeleted)) + } + if filter.Name != "" { + query.Where(schedule.NameEQ(filter.Name)) + } + + totalCount, err := query.Clone().Count(ctx) + if err != nil { + return nil, err + } + + limit := clampLimit(opts.Limit) + entities, err := query. + Order(schedule.ByCreated(entsql.OrderDesc())). + Limit(limit + 1). + All(ctx) + if err != nil { + return nil, err + } + + schedules := make([]store.Schedule, 0, len(entities)) + for _, e := range entities { + schedules = append(schedules, *entScheduleToStore(e)) + } + + result := &store.ListResult[store.Schedule]{TotalCount: totalCount} + if len(schedules) > limit { + result.Items = schedules[:limit] + result.NextCursor = schedules[limit-1].ID + } else { + result.Items = schedules + } + return result, nil +} + +// UpdateSchedule updates an existing schedule. +func (s *ScheduleStore) UpdateSchedule(ctx context.Context, sc *store.Schedule) error { + uid, err := parseUUID(sc.ID) + if err != nil { + return err + } + + update := s.client.Schedule.UpdateOneID(uid). + SetName(sc.Name). + SetCronExpr(sc.CronExpr). + SetEventType(sc.EventType). + SetPayload(sc.Payload). + SetStatus(sc.Status) + + if sc.NextRunAt != nil { + update.SetNextRunAt(*sc.NextRunAt) + } else { + update.ClearNextRunAt() + } + + updated, err := update.Save(ctx) + if err != nil { + return mapError(err) + } + sc.UpdatedAt = updated.Updated + return nil +} + +// UpdateScheduleStatus updates only the status of a schedule. +func (s *ScheduleStore) UpdateScheduleStatus(ctx context.Context, id string, status string) error { + uid, err := parseUUID(id) + if err != nil { + return err + } + err = s.client.Schedule.UpdateOneID(uid).SetStatus(status).Exec(ctx) + if err != nil { + return mapError(err) + } + return nil +} + +// UpdateScheduleAfterRun updates a schedule after a run completes. It performs a +// read-modify-write of the run/error counters atomically via Ent's Add* setters. +func (s *ScheduleStore) UpdateScheduleAfterRun(ctx context.Context, id string, ranAt time.Time, nextRunAt time.Time, errMsg string) error { + uid, err := parseUUID(id) + if err != nil { + return err + } + + update := s.client.Schedule.UpdateOneID(uid). + SetLastRunAt(ranAt). + SetNextRunAt(nextRunAt). + AddRunCount(1) + + if errMsg != "" { + update. + SetLastRunStatus(store.ScheduleRunError). + SetLastRunError(errMsg). + AddErrorCount(1) + } else { + update. + SetLastRunStatus(store.ScheduleRunSuccess). + ClearLastRunError() + } + + if err := update.Exec(ctx); err != nil { + return mapError(err) + } + return nil +} + +// DeleteSchedule removes a schedule by ID (hard delete). +func (s *ScheduleStore) DeleteSchedule(ctx context.Context, id string) error { + uid, err := parseUUID(id) + if err != nil { + return err + } + if err := s.client.Schedule.DeleteOneID(uid).Exec(ctx); err != nil { + return mapError(err) + } + return nil +} + +// ListDueSchedules returns active schedules whose next_run_at has passed. +// +// This is a JOB-CLAIM PATH (§2.A.3): multiple hub replicas poll it concurrently. +// The dialect-aware claim helper applies SELECT ... FOR UPDATE SKIP LOCKED on +// Postgres so two replicas never pick up the same schedule, and falls back to a +// plain SELECT on SQLite (single writer, no SKIP LOCKED support). +func (s *ScheduleStore) ListDueSchedules(ctx context.Context, now time.Time) ([]store.Schedule, error) { + ids, err := s.skipLockedIDs(ctx, schedule.Table, func(sel *entsql.Selector) { + sel.Where(entsql.And( + entsql.EQ(schedule.FieldStatus, store.ScheduleStatusActive), + entsql.NotNull(schedule.FieldNextRunAt), + entsql.LTE(schedule.FieldNextRunAt, now), + )).OrderBy(entsql.Asc(schedule.FieldNextRunAt)) + }) + if err != nil { + return nil, err + } + if len(ids) == 0 { + return nil, nil + } + + entities, err := s.client.Schedule.Query(). + Where(schedule.IDIn(ids...)). + Order(schedule.ByNextRunAt()). + All(ctx) + if err != nil { + return nil, err + } + out := make([]store.Schedule, 0, len(entities)) + for _, e := range entities { + out = append(out, *entScheduleToStore(e)) + } + return out, nil +} + +// ============================================================================ +// ScheduledEvent <-> store conversion +// ============================================================================ + +func entScheduledEventToStore(e *ent.ScheduledEvent) *store.ScheduledEvent { + return &store.ScheduledEvent{ + ID: e.ID.String(), + ProjectID: e.ProjectID.String(), + EventType: e.EventType, + FireAt: e.FireAt, + Payload: e.Payload, + Status: e.Status, + CreatedAt: e.Created, + CreatedBy: e.CreatedBy, + FiredAt: e.FiredAt, + Error: e.Error, + ScheduleID: e.ScheduleID, + } +} + +// ============================================================================ +// Scheduled Event Operations +// ============================================================================ + +// CreateScheduledEvent creates a new scheduled event. +func (s *ScheduleStore) CreateScheduledEvent(ctx context.Context, event *store.ScheduledEvent) error { + if event.ID == "" || event.ProjectID == "" || event.EventType == "" { + return store.ErrInvalidInput + } + uid, err := parseUUID(event.ID) + if err != nil { + return err + } + pid, err := parseUUID(event.ProjectID) + if err != nil { + return err + } + if event.Status == "" { + event.Status = store.ScheduledEventPending + } + + create := s.client.ScheduledEvent.Create(). + SetID(uid). + SetProjectID(pid). + SetEventType(event.EventType). + SetFireAt(event.FireAt). + SetPayload(event.Payload). + SetStatus(event.Status) + + if event.CreatedBy != "" { + create.SetCreatedBy(event.CreatedBy) + } + if event.FiredAt != nil { + create.SetFiredAt(*event.FiredAt) + } + if event.Error != "" { + create.SetError(event.Error) + } + if event.ScheduleID != "" { + create.SetScheduleID(event.ScheduleID) + } + if !event.CreatedAt.IsZero() { + create.SetCreated(event.CreatedAt) + } + + created, err := create.Save(ctx) + if err != nil { + return mapError(err) + } + event.CreatedAt = created.Created + event.Status = created.Status + return nil +} + +// GetScheduledEvent retrieves a scheduled event by ID. +func (s *ScheduleStore) GetScheduledEvent(ctx context.Context, id string) (*store.ScheduledEvent, error) { + uid, err := parseGetID(id) + if err != nil { + return nil, err + } + e, err := s.client.ScheduledEvent.Get(ctx, uid) + if err != nil { + return nil, mapError(err) + } + return entScheduledEventToStore(e), nil +} + +// ListPendingScheduledEvents returns all events with status "pending", +// ordered by fire_at ASC. +// +// Like ListDueSchedules this is a JOB-CLAIM PATH and uses the dialect-aware +// SKIP LOCKED helper for safe multi-replica polling. +func (s *ScheduleStore) ListPendingScheduledEvents(ctx context.Context) ([]store.ScheduledEvent, error) { + ids, err := s.skipLockedIDs(ctx, scheduledevent.Table, func(sel *entsql.Selector) { + sel.Where(entsql.EQ(scheduledevent.FieldStatus, store.ScheduledEventPending)). + OrderBy(entsql.Asc(scheduledevent.FieldFireAt)) + }) + if err != nil { + return nil, err + } + if len(ids) == 0 { + return nil, nil + } + + entities, err := s.client.ScheduledEvent.Query(). + Where(scheduledevent.IDIn(ids...)). + Order(scheduledevent.ByFireAt()). + All(ctx) + if err != nil { + return nil, err + } + out := make([]store.ScheduledEvent, 0, len(entities)) + for _, e := range entities { + out = append(out, *entScheduledEventToStore(e)) + } + return out, nil +} + +// UpdateScheduledEventStatus updates the status and optional error for an event. +// Mirroring the SQLite backend, a missing event is a no-op (not ErrNotFound). +func (s *ScheduleStore) UpdateScheduledEventStatus(ctx context.Context, id string, status string, firedAt *time.Time, errMsg string) error { + uid, err := parseUUID(id) + if err != nil { + return err + } + + update := s.client.ScheduledEvent.Update(). + Where(scheduledevent.IDEQ(uid)). + SetStatus(status) + + if firedAt != nil { + update.SetFiredAt(*firedAt) + } else { + update.ClearFiredAt() + } + if errMsg != "" { + update.SetError(errMsg) + } else { + update.ClearError() + } + + _, err = update.Save(ctx) + return err +} + +// ClaimScheduledEvent atomically transitions a scheduled event from "pending" to +// claimedStatus, returning whether this caller won the claim. It is the +// multi-replica dedup primitive (store.ScheduledEventClaimer): several hub +// replicas may each recover the same pending event from the database on startup +// and arm an in-memory timer for it, but the conditional +// UPDATE ... WHERE status = 'pending' is atomic, so exactly one replica observes +// affected == 1 and is allowed to execute the event's side effect. Losers +// observe affected == 0 and skip execution. +// +// The same atomicity holds on SQLite (a conditional UPDATE is atomic there too); +// it is simply never contended because there is a single writer. +func (s *ScheduleStore) ClaimScheduledEvent(ctx context.Context, id string, claimedStatus string) (bool, error) { + uid, err := parseUUID(id) + if err != nil { + return false, err + } + if claimedStatus == "" { + claimedStatus = store.ScheduledEventFired + } + affected, err := s.client.ScheduledEvent.Update(). + Where( + scheduledevent.IDEQ(uid), + scheduledevent.StatusEQ(store.ScheduledEventPending), + ). + SetStatus(claimedStatus). + SetFiredAt(time.Now()). + Save(ctx) + if err != nil { + return false, mapError(err) + } + return affected == 1, nil +} + +// CancelScheduledEvent marks a pending event as cancelled. Returns ErrNotFound +// if the event doesn't exist or is not pending. +func (s *ScheduleStore) CancelScheduledEvent(ctx context.Context, id string) error { + uid, err := parseUUID(id) + if err != nil { + return err + } + n, err := s.client.ScheduledEvent.Update(). + Where( + scheduledevent.IDEQ(uid), + scheduledevent.StatusEQ(store.ScheduledEventPending), + ). + SetStatus(store.ScheduledEventCancelled). + Save(ctx) + if err != nil { + return err + } + if n == 0 { + return store.ErrNotFound + } + return nil +} + +// ListScheduledEvents returns events matching the filter criteria. +func (s *ScheduleStore) ListScheduledEvents(ctx context.Context, filter store.ScheduledEventFilter, opts store.ListOptions) (*store.ListResult[store.ScheduledEvent], error) { + query := s.client.ScheduledEvent.Query() + + if filter.ProjectID != "" { + pid, err := parseUUID(filter.ProjectID) + if err != nil { + return nil, err + } + query.Where(scheduledevent.ProjectIDEQ(pid)) + } + if filter.EventType != "" { + query.Where(scheduledevent.EventTypeEQ(filter.EventType)) + } + if filter.Status != "" { + query.Where(scheduledevent.StatusEQ(filter.Status)) + } + if filter.ScheduleID != "" { + query.Where(scheduledevent.ScheduleIDEQ(filter.ScheduleID)) + } + + totalCount, err := query.Clone().Count(ctx) + if err != nil { + return nil, err + } + + if opts.Cursor != "" { + cursorUID, err := parseUUID(opts.Cursor) + if err != nil { + return nil, err + } + query.Where(scheduledevent.IDLT(cursorUID)) + } + + limit := clampLimit(opts.Limit) + entities, err := query. + Order(scheduledevent.ByCreated(entsql.OrderDesc())). + Limit(limit + 1). + All(ctx) + if err != nil { + return nil, err + } + + events := make([]store.ScheduledEvent, 0, len(entities)) + for _, e := range entities { + events = append(events, *entScheduledEventToStore(e)) + } + + result := &store.ListResult[store.ScheduledEvent]{TotalCount: totalCount} + if len(events) > limit { + result.Items = events[:limit] + result.NextCursor = events[limit-1].ID + } else { + result.Items = events + } + return result, nil +} + +// PurgeOldScheduledEvents removes non-pending events older than cutoff. +func (s *ScheduleStore) PurgeOldScheduledEvents(ctx context.Context, cutoff time.Time) (int, error) { + n, err := s.client.ScheduledEvent.Delete(). + Where( + scheduledevent.StatusNEQ(store.ScheduledEventPending), + scheduledevent.CreatedLT(cutoff), + ). + Exec(ctx) + if err != nil { + return 0, err + } + return n, nil +} + +// ============================================================================ +// Dialect-aware claim helper +// ============================================================================ + +// skipLockedIDs runs the claim SELECT described by apply and returns the ids of +// the matching rows. On Postgres it issues `SELECT id ... FOR UPDATE SKIP +// LOCKED` inside a short transaction so concurrent replicas receive disjoint +// row sets; the caller then transitions the claimed rows to their next state. +// On SQLite (and any non-Postgres dialect) it degrades to a plain `SELECT id`, +// which is correct for the single-writer backend that has no SKIP LOCKED +// support. +func (s *ScheduleStore) skipLockedIDs(ctx context.Context, table string, apply func(*entsql.Selector)) ([]uuid.UUID, error) { + drv := s.client.Driver() + d := drv.Dialect() + + builder := entsql.Dialect(d) + selector := builder.Select(genericIDColumn).From(builder.Table(table)) + apply(selector) + if d == dialect.Postgres { + selector.ForUpdate(entsql.WithLockAction(entsql.SkipLocked)) + } + + query, args := selector.Query() + + if d == dialect.Postgres { + return s.queryIDsTx(ctx, drv, query, args) + } + return s.queryIDs(ctx, drv, query, args) +} + +// genericIDColumn is the primary-key column shared by all ported tables. +const genericIDColumn = "id" + +// queryIDs runs the claim SELECT directly (no surrounding transaction). Used for +// dialects that do not support row-level locking. +func (s *ScheduleStore) queryIDs(ctx context.Context, drv interface { + Query(context.Context, string, any, any) error +}, query string, args []any) ([]uuid.UUID, error) { + rows := &entsql.Rows{} + if err := drv.Query(ctx, query, args, rows); err != nil { + return nil, err + } + defer rows.Close() + return scanUUIDRows(rows) +} + +// queryIDsTx runs the SKIP LOCKED claim SELECT within a transaction so the row +// locks are held while the disjoint id set is materialized. +func (s *ScheduleStore) queryIDsTx(ctx context.Context, drv interface { + Tx(context.Context) (dialect.Tx, error) +}, query string, args []any) ([]uuid.UUID, error) { + tx, err := drv.Tx(ctx) + if err != nil { + return nil, err + } + rows := &entsql.Rows{} + if err := tx.Query(ctx, query, args, rows); err != nil { + _ = tx.Rollback() + return nil, err + } + ids, scanErr := scanUUIDRows(rows) + _ = rows.Close() + if scanErr != nil { + _ = tx.Rollback() + return nil, scanErr + } + if err := tx.Commit(); err != nil { + return nil, err + } + return ids, nil +} + +// scanUUIDRows scans a single-column id result set into a slice of UUIDs. +func scanUUIDRows(rows *entsql.Rows) ([]uuid.UUID, error) { + var ids []uuid.UUID + for rows.Next() { + var id uuid.UUID + if err := rows.Scan(&id); err != nil { + return nil, err + } + ids = append(ids, id) + } + if err := rows.Err(); err != nil { + return nil, err + } + return ids, nil +} diff --git a/pkg/store/entadapter/schedule_store_test.go b/pkg/store/entadapter/schedule_store_test.go new file mode 100644 index 000000000..a197f6d86 --- /dev/null +++ b/pkg/store/entadapter/schedule_store_test.go @@ -0,0 +1,259 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build !no_sqlite + +package entadapter + +import ( + "context" + "sync" + "testing" + "time" + + "entgo.io/ent/dialect" + "github.com/GoogleCloudPlatform/scion/pkg/store" + "github.com/GoogleCloudPlatform/scion/pkg/store/enttest" + "github.com/google/uuid" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func newTestScheduleStore(t *testing.T) *ScheduleStore { + t.Helper() + client := enttest.NewClient(t) + return NewScheduleStore(client) +} + +func newTestSchedule(projectID string, name string) *store.Schedule { + next := time.Now().Add(time.Hour).UTC().Truncate(time.Second) + return &store.Schedule{ + ID: uuid.NewString(), + ProjectID: projectID, + Name: name, + CronExpr: "0 9 * * 1-5", + EventType: "message", + Payload: `{"message":"standup"}`, + NextRunAt: &next, + CreatedBy: "user-123", + } +} + +func TestScheduleCRUD(t *testing.T) { + s := newTestScheduleStore(t) + ctx := context.Background() + projectID := uuid.NewString() + + sc := newTestSchedule(projectID, "daily-standup") + require.NoError(t, s.CreateSchedule(ctx, sc)) + assert.False(t, sc.CreatedAt.IsZero()) + assert.Equal(t, store.ScheduleStatusActive, sc.Status) + + got, err := s.GetSchedule(ctx, sc.ID) + require.NoError(t, err) + assert.Equal(t, sc.ID, got.ID) + assert.Equal(t, projectID, got.ProjectID) + assert.Equal(t, "daily-standup", got.Name) + assert.Equal(t, "0 9 * * 1-5", got.CronExpr) + assert.Equal(t, "message", got.EventType) + assert.Equal(t, store.ScheduleStatusActive, got.Status) + assert.Equal(t, "user-123", got.CreatedBy) + require.NotNil(t, got.NextRunAt) + + // Update + got.Name = "weekly-standup" + got.CronExpr = "0 9 * * 1" + require.NoError(t, s.UpdateSchedule(ctx, got)) + updated, err := s.GetSchedule(ctx, sc.ID) + require.NoError(t, err) + assert.Equal(t, "weekly-standup", updated.Name) + assert.Equal(t, "0 9 * * 1", updated.CronExpr) + + // Status + require.NoError(t, s.UpdateScheduleStatus(ctx, sc.ID, store.ScheduleStatusPaused)) + paused, err := s.GetSchedule(ctx, sc.ID) + require.NoError(t, err) + assert.Equal(t, store.ScheduleStatusPaused, paused.Status) + + // Delete + require.NoError(t, s.DeleteSchedule(ctx, sc.ID)) + _, err = s.GetSchedule(ctx, sc.ID) + assert.ErrorIs(t, err, store.ErrNotFound) +} + +func TestScheduleGetNotFound(t *testing.T) { + s := newTestScheduleStore(t) + _, err := s.GetSchedule(context.Background(), uuid.NewString()) + assert.ErrorIs(t, err, store.ErrNotFound) +} + +func TestScheduleInvalidInput(t *testing.T) { + s := newTestScheduleStore(t) + err := s.CreateSchedule(context.Background(), &store.Schedule{ID: uuid.NewString()}) + assert.ErrorIs(t, err, store.ErrInvalidInput) +} + +func TestScheduleUpdateNotFound(t *testing.T) { + s := newTestScheduleStore(t) + sc := newTestSchedule(uuid.NewString(), "ghost") + err := s.UpdateSchedule(context.Background(), sc) + assert.ErrorIs(t, err, store.ErrNotFound) +} + +func TestUpdateScheduleAfterRun(t *testing.T) { + s := newTestScheduleStore(t) + ctx := context.Background() + sc := newTestSchedule(uuid.NewString(), "runner") + require.NoError(t, s.CreateSchedule(ctx, sc)) + + ranAt := time.Now().UTC().Truncate(time.Second) + nextRun := ranAt.Add(time.Hour) + + // Success run increments run_count, clears error. + require.NoError(t, s.UpdateScheduleAfterRun(ctx, sc.ID, ranAt, nextRun, "")) + got, err := s.GetSchedule(ctx, sc.ID) + require.NoError(t, err) + assert.Equal(t, 1, got.RunCount) + assert.Equal(t, 0, got.ErrorCount) + assert.Equal(t, store.ScheduleRunSuccess, got.LastRunStatus) + assert.Empty(t, got.LastRunError) + + // Error run increments both counters and records the error. + require.NoError(t, s.UpdateScheduleAfterRun(ctx, sc.ID, ranAt, nextRun, "boom")) + got, err = s.GetSchedule(ctx, sc.ID) + require.NoError(t, err) + assert.Equal(t, 2, got.RunCount) + assert.Equal(t, 1, got.ErrorCount) + assert.Equal(t, store.ScheduleRunError, got.LastRunStatus) + assert.Equal(t, "boom", got.LastRunError) +} + +func TestListSchedulesFilterAndPagination(t *testing.T) { + s := newTestScheduleStore(t) + ctx := context.Background() + projectID := uuid.NewString() + other := uuid.NewString() + + for i := 0; i < 3; i++ { + require.NoError(t, s.CreateSchedule(ctx, newTestSchedule(projectID, "sched-"+uuid.NewString()[:8]))) + } + require.NoError(t, s.CreateSchedule(ctx, newTestSchedule(other, "other"))) + + // Filter by project. + res, err := s.ListSchedules(ctx, store.ScheduleFilter{ProjectID: projectID}, store.ListOptions{}) + require.NoError(t, err) + assert.Equal(t, 3, res.TotalCount) + assert.Len(t, res.Items, 3) + + // Pagination: limit honored, total independent of limit. + page, err := s.ListSchedules(ctx, store.ScheduleFilter{ProjectID: projectID}, store.ListOptions{Limit: 2}) + require.NoError(t, err) + assert.Len(t, page.Items, 2) + assert.Equal(t, 3, page.TotalCount) + assert.NotEmpty(t, page.NextCursor) +} + +func TestListSchedulesExcludesDeletedByDefault(t *testing.T) { + s := newTestScheduleStore(t) + ctx := context.Background() + projectID := uuid.NewString() + + active := newTestSchedule(projectID, "active") + require.NoError(t, s.CreateSchedule(ctx, active)) + deleted := newTestSchedule(projectID, "deleted") + require.NoError(t, s.CreateSchedule(ctx, deleted)) + require.NoError(t, s.UpdateScheduleStatus(ctx, deleted.ID, store.ScheduleStatusDeleted)) + + res, err := s.ListSchedules(ctx, store.ScheduleFilter{ProjectID: projectID}, store.ListOptions{}) + require.NoError(t, err) + assert.Equal(t, 1, res.TotalCount) + assert.Equal(t, active.ID, res.Items[0].ID) +} + +// TestListDueSchedulesClaimPath exercises the dialect-aware SKIP LOCKED claim +// helper. On SQLite it degrades to a plain SELECT, so this verifies the +// functional contract of the claim query: only active, already-due schedules +// are returned, ordered by next_run_at ascending. The concurrency sub-test +// hammers the helper from multiple goroutines to flush out races in the +// claim/transaction handling. +func TestListDueSchedulesClaimPath(t *testing.T) { + s := newTestScheduleStore(t) + ctx := context.Background() + projectID := uuid.NewString() + now := time.Now().UTC().Truncate(time.Second) + + // Two due schedules (past next_run_at), at distinct times for ordering. + dueEarly := newTestSchedule(projectID, "due-early") + early := now.Add(-2 * time.Hour) + dueEarly.NextRunAt = &early + require.NoError(t, s.CreateSchedule(ctx, dueEarly)) + + dueLate := newTestSchedule(projectID, "due-late") + late := now.Add(-1 * time.Hour) + dueLate.NextRunAt = &late + require.NoError(t, s.CreateSchedule(ctx, dueLate)) + + // Not due (future next_run_at). + future := newTestSchedule(projectID, "future") + require.NoError(t, s.CreateSchedule(ctx, future)) + + // Due but paused — must be excluded (status != active). + paused := newTestSchedule(projectID, "paused") + paused.NextRunAt = &early + require.NoError(t, s.CreateSchedule(ctx, paused)) + require.NoError(t, s.UpdateScheduleStatus(ctx, paused.ID, store.ScheduleStatusPaused)) + + due, err := s.ListDueSchedules(ctx, now) + require.NoError(t, err) + require.Len(t, due, 2, "only the two active, due schedules should be claimed") + assert.Equal(t, dueEarly.ID, due[0].ID, "ordered by next_run_at ascending") + assert.Equal(t, dueLate.ID, due[1].ID) + + // Concurrent claims must not race or error. The expected per-call count is + // backend-dependent: + // - SQLite has no SELECT ... FOR UPDATE SKIP LOCKED, and the test store + // pins MaxOpenConns=1, so the claim path serializes and every caller + // observes both due schedules. + // - Postgres uses FOR UPDATE SKIP LOCKED inside a transaction that holds + // the row locks until commit, so a concurrent caller skips rows locked + // by a sibling and may observe a disjoint subset (0..2). The cross-call + // invariant is only that no caller errors or observes more than the two + // due rows. + isPostgres := s.client.Driver().Dialect() == dialect.Postgres + var wg sync.WaitGroup + errs := make(chan error, 8) + for i := 0; i < 8; i++ { + wg.Add(1) + go func() { + defer wg.Done() + res, err := s.ListDueSchedules(ctx, now) + if err != nil { + errs <- err + return + } + if isPostgres { + if len(res) > 2 { + errs <- assert.AnError + } + } else if len(res) != 2 { + errs <- assert.AnError + } + }() + } + wg.Wait() + close(errs) + for err := range errs { + require.NoError(t, err) + } +} diff --git a/pkg/store/entadapter/scheduled_event_store_test.go b/pkg/store/entadapter/scheduled_event_store_test.go new file mode 100644 index 000000000..681862fbf --- /dev/null +++ b/pkg/store/entadapter/scheduled_event_store_test.go @@ -0,0 +1,171 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build !no_sqlite + +package entadapter + +import ( + "context" + "testing" + "time" + + "github.com/GoogleCloudPlatform/scion/pkg/store" + "github.com/google/uuid" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func newTestScheduledEvent(projectID string) *store.ScheduledEvent { + fireAt := time.Now().Add(time.Hour).UTC().Truncate(time.Second) + return &store.ScheduledEvent{ + ID: uuid.NewString(), + ProjectID: projectID, + EventType: "message", + FireAt: fireAt, + Payload: `{"text":"hello"}`, + CreatedBy: "user-123", + } +} + +func TestScheduledEventCRUD(t *testing.T) { + s := newTestScheduleStore(t) + ctx := context.Background() + projectID := uuid.NewString() + + evt := newTestScheduledEvent(projectID) + require.NoError(t, s.CreateScheduledEvent(ctx, evt)) + assert.False(t, evt.CreatedAt.IsZero()) + assert.Equal(t, store.ScheduledEventPending, evt.Status) + + got, err := s.GetScheduledEvent(ctx, evt.ID) + require.NoError(t, err) + assert.Equal(t, evt.ID, got.ID) + assert.Equal(t, projectID, got.ProjectID) + assert.Equal(t, "message", got.EventType) + assert.Equal(t, store.ScheduledEventPending, got.Status) + assert.Equal(t, "user-123", got.CreatedBy) + + // Update status -> fired. + firedAt := time.Now().UTC().Truncate(time.Second) + require.NoError(t, s.UpdateScheduledEventStatus(ctx, evt.ID, store.ScheduledEventFired, &firedAt, "")) + got, err = s.GetScheduledEvent(ctx, evt.ID) + require.NoError(t, err) + assert.Equal(t, store.ScheduledEventFired, got.Status) + require.NotNil(t, got.FiredAt) +} + +func TestScheduledEventGetNotFound(t *testing.T) { + s := newTestScheduleStore(t) + _, err := s.GetScheduledEvent(context.Background(), uuid.NewString()) + assert.ErrorIs(t, err, store.ErrNotFound) +} + +func TestScheduledEventInvalidInput(t *testing.T) { + s := newTestScheduleStore(t) + err := s.CreateScheduledEvent(context.Background(), &store.ScheduledEvent{ID: uuid.NewString()}) + assert.ErrorIs(t, err, store.ErrInvalidInput) +} + +func TestCancelScheduledEvent(t *testing.T) { + s := newTestScheduleStore(t) + ctx := context.Background() + evt := newTestScheduledEvent(uuid.NewString()) + require.NoError(t, s.CreateScheduledEvent(ctx, evt)) + + require.NoError(t, s.CancelScheduledEvent(ctx, evt.ID)) + got, err := s.GetScheduledEvent(ctx, evt.ID) + require.NoError(t, err) + assert.Equal(t, store.ScheduledEventCancelled, got.Status) + + // Cancelling a non-pending event is ErrNotFound. + err = s.CancelScheduledEvent(ctx, evt.ID) + assert.ErrorIs(t, err, store.ErrNotFound) +} + +func TestListScheduledEventsFilter(t *testing.T) { + s := newTestScheduleStore(t) + ctx := context.Background() + projectID := uuid.NewString() + + for i := 0; i < 3; i++ { + require.NoError(t, s.CreateScheduledEvent(ctx, newTestScheduledEvent(projectID))) + } + require.NoError(t, s.CreateScheduledEvent(ctx, newTestScheduledEvent(uuid.NewString()))) + + res, err := s.ListScheduledEvents(ctx, store.ScheduledEventFilter{ProjectID: projectID}, store.ListOptions{}) + require.NoError(t, err) + assert.Equal(t, 3, res.TotalCount) + assert.Len(t, res.Items, 3) + + res, err = s.ListScheduledEvents(ctx, store.ScheduledEventFilter{Status: store.ScheduledEventPending}, store.ListOptions{}) + require.NoError(t, err) + assert.Equal(t, 4, res.TotalCount) +} + +func TestPurgeOldScheduledEvents(t *testing.T) { + s := newTestScheduleStore(t) + ctx := context.Background() + projectID := uuid.NewString() + + // Old fired event (should be purged). + old := newTestScheduledEvent(projectID) + old.CreatedAt = time.Now().Add(-48 * time.Hour).UTC().Truncate(time.Second) + require.NoError(t, s.CreateScheduledEvent(ctx, old)) + require.NoError(t, s.UpdateScheduledEventStatus(ctx, old.ID, store.ScheduledEventFired, nil, "")) + + // Old pending event (should NOT be purged — still pending). + oldPending := newTestScheduledEvent(projectID) + oldPending.CreatedAt = time.Now().Add(-48 * time.Hour).UTC().Truncate(time.Second) + require.NoError(t, s.CreateScheduledEvent(ctx, oldPending)) + + cutoff := time.Now().Add(-24 * time.Hour) + n, err := s.PurgeOldScheduledEvents(ctx, cutoff) + require.NoError(t, err) + assert.Equal(t, 1, n) + + _, err = s.GetScheduledEvent(ctx, old.ID) + assert.ErrorIs(t, err, store.ErrNotFound) + _, err = s.GetScheduledEvent(ctx, oldPending.ID) + require.NoError(t, err) +} + +// TestListPendingScheduledEventsClaimPath verifies the scheduled-event job-claim +// path (dialect-aware SKIP LOCKED helper): only pending events are returned, +// ordered by fire_at ascending. +func TestListPendingScheduledEventsClaimPath(t *testing.T) { + s := newTestScheduleStore(t) + ctx := context.Background() + projectID := uuid.NewString() + now := time.Now().UTC().Truncate(time.Second) + + early := newTestScheduledEvent(projectID) + early.FireAt = now.Add(1 * time.Minute) + require.NoError(t, s.CreateScheduledEvent(ctx, early)) + + late := newTestScheduledEvent(projectID) + late.FireAt = now.Add(10 * time.Minute) + require.NoError(t, s.CreateScheduledEvent(ctx, late)) + + // A fired event must be excluded from the pending claim. + fired := newTestScheduledEvent(projectID) + require.NoError(t, s.CreateScheduledEvent(ctx, fired)) + require.NoError(t, s.UpdateScheduledEventStatus(ctx, fired.ID, store.ScheduledEventFired, &now, "")) + + pending, err := s.ListPendingScheduledEvents(ctx) + require.NoError(t, err) + require.Len(t, pending, 2) + assert.Equal(t, early.ID, pending[0].ID, "ordered by fire_at ascending") + assert.Equal(t, late.ID, pending[1].ID) +} diff --git a/pkg/store/entadapter/secret_store.go b/pkg/store/entadapter/secret_store.go new file mode 100644 index 000000000..fff4aaf92 --- /dev/null +++ b/pkg/store/entadapter/secret_store.go @@ -0,0 +1,528 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package entadapter + +import ( + "context" + "errors" + "time" + + "github.com/GoogleCloudPlatform/scion/pkg/ent" + entenvvar "github.com/GoogleCloudPlatform/scion/pkg/ent/envvar" + entsecret "github.com/GoogleCloudPlatform/scion/pkg/ent/secret" + "github.com/GoogleCloudPlatform/scion/pkg/store" +) + +// SecretStore implements store.SecretStore and store.EnvVarStore using Ent ORM. +// +// Secrets and env vars are polymorphically scoped to different entity types +// (hub/user/project/runtime_broker) via the (scope, scope_id) pair, so there +// are no FK edges; lookups are keyed by the (key, scope, scope_id) triple, +// mirroring the legacy SQLite implementation. +type SecretStore struct { + client *ent.Client +} + +// NewSecretStore creates a new Ent-backed SecretStore. +func NewSecretStore(client *ent.Client) *SecretStore { + return &SecretStore{client: client} +} + +// ============================================================================= +// Secret operations +// ============================================================================= + +// entSecretRowToStore converts an Ent Secret entity to a store.Secret model. +// When includeValue is false the EncryptedValue is left empty, matching the +// SQLite listing queries that never select the encrypted payload. +func entSecretRowToStore(e *ent.Secret, includeValue bool) store.Secret { + s := store.Secret{ + ID: e.ID.String(), + Key: e.Key, + SecretRef: e.SecretRef, + SecretType: string(e.SecretType), + Target: e.Target, + Scope: e.Scope, + ScopeID: e.ScopeID, + Description: e.Description, + InjectionMode: string(e.InjectionMode), + AllowProgeny: e.AllowProgeny, + Version: e.Version, + Created: e.Created, + Updated: e.Updated, + CreatedBy: e.CreatedBy, + UpdatedBy: e.UpdatedBy, + } + // Mirror SQLite's COALESCE(target, key): an unset target projects to the key. + if s.Target == "" { + s.Target = s.Key + } + if includeValue { + s.EncryptedValue = e.EncryptedValue + } + return s +} + +// CreateSecret creates a new secret. +func (s *SecretStore) CreateSecret(ctx context.Context, secret *store.Secret) error { + uid, err := parseUUID(secret.ID) + if err != nil { + return err + } + + now := time.Now() + secret.Created = now + secret.Updated = now + secret.Version = 1 + + if secret.SecretType == "" { + secret.SecretType = store.SecretTypeEnvironment + } + if secret.Target == "" { + secret.Target = secret.Key + } + if secret.InjectionMode == "" { + secret.InjectionMode = store.InjectionModeAsNeeded + } + + create := s.client.Secret.Create(). + SetID(uid). + SetKey(secret.Key). + SetEncryptedValue(secret.EncryptedValue). + SetSecretType(entsecret.SecretType(secret.SecretType)). + SetTarget(secret.Target). + SetScope(secret.Scope). + SetScopeID(secret.ScopeID). + SetInjectionMode(entsecret.InjectionMode(secret.InjectionMode)). + SetAllowProgeny(secret.AllowProgeny). + SetVersion(secret.Version). + SetCreated(secret.Created). + SetUpdated(secret.Updated) + + if secret.SecretRef != "" { + create.SetSecretRef(secret.SecretRef) + } + if secret.Description != "" { + create.SetDescription(secret.Description) + } + if secret.CreatedBy != "" { + create.SetCreatedBy(secret.CreatedBy) + } + if secret.UpdatedBy != "" { + create.SetUpdatedBy(secret.UpdatedBy) + } + + if _, err := create.Save(ctx); err != nil { + return mapError(err) + } + return nil +} + +// GetSecret retrieves secret metadata (including the encrypted value) by key, +// scope, and scopeID. +func (s *SecretStore) GetSecret(ctx context.Context, key, scope, scopeID string) (*store.Secret, error) { + e, err := s.client.Secret.Query(). + Where( + entsecret.KeyEQ(key), + entsecret.ScopeEQ(scope), + entsecret.ScopeIDEQ(scopeID), + ). + Only(ctx) + if err != nil { + return nil, mapError(err) + } + sec := entSecretRowToStore(e, true) + return &sec, nil +} + +// UpdateSecret updates an existing secret, incrementing its version. +func (s *SecretStore) UpdateSecret(ctx context.Context, secret *store.Secret) error { + secret.Updated = time.Now() + secret.Version++ // Increment version on each update + + if secret.SecretType == "" { + secret.SecretType = store.SecretTypeEnvironment + } + if secret.Target == "" { + secret.Target = secret.Key + } + if secret.InjectionMode == "" { + secret.InjectionMode = store.InjectionModeAsNeeded + } + + update := s.client.Secret.Update(). + Where( + entsecret.KeyEQ(secret.Key), + entsecret.ScopeEQ(secret.Scope), + entsecret.ScopeIDEQ(secret.ScopeID), + ). + SetEncryptedValue(secret.EncryptedValue). + SetSecretType(entsecret.SecretType(secret.SecretType)). + SetTarget(secret.Target). + SetDescription(secret.Description). + SetInjectionMode(entsecret.InjectionMode(secret.InjectionMode)). + SetAllowProgeny(secret.AllowProgeny). + SetVersion(secret.Version). + SetUpdatedBy(secret.UpdatedBy). + SetUpdated(secret.Updated) + + if secret.SecretRef != "" { + update.SetSecretRef(secret.SecretRef) + } else { + update.ClearSecretRef() + } + + n, err := update.Save(ctx) + if err != nil { + return mapError(err) + } + if n == 0 { + return store.ErrNotFound + } + return nil +} + +// UpsertSecret creates or updates a secret, keyed by (key, scope, scopeID). +func (s *SecretStore) UpsertSecret(ctx context.Context, secret *store.Secret) (bool, error) { + now := time.Now() + secret.Updated = now + + existing, err := s.GetSecret(ctx, secret.Key, secret.Scope, secret.ScopeID) + if err != nil && !errors.Is(err, store.ErrNotFound) { + return false, err + } + + if existing != nil { + // Update existing: preserve identity/creation metadata. UpdateSecret + // increments the version from the existing baseline. + secret.ID = existing.ID + secret.Created = existing.Created + secret.CreatedBy = existing.CreatedBy + secret.Version = existing.Version + if err := s.UpdateSecret(ctx, secret); err != nil { + return false, err + } + return false, nil + } + + secret.Created = now + if err := s.CreateSecret(ctx, secret); err != nil { + return false, err + } + return true, nil +} + +// DeleteSecret removes a secret by key, scope, and scopeID. +func (s *SecretStore) DeleteSecret(ctx context.Context, key, scope, scopeID string) error { + n, err := s.client.Secret.Delete(). + Where( + entsecret.KeyEQ(key), + entsecret.ScopeEQ(scope), + entsecret.ScopeIDEQ(scopeID), + ). + Exec(ctx) + if err != nil { + return err + } + if n == 0 { + return store.ErrNotFound + } + return nil +} + +// DeleteSecretsByScope removes all secrets for a given scope, returning the +// number of deleted records. +func (s *SecretStore) DeleteSecretsByScope(ctx context.Context, scope, scopeID string) (int, error) { + n, err := s.client.Secret.Delete(). + Where( + entsecret.ScopeEQ(scope), + entsecret.ScopeIDEQ(scopeID), + ). + Exec(ctx) + if err != nil { + return 0, err + } + return n, nil +} + +// ListSecrets returns secret metadata matching the filter. The EncryptedValue +// is never populated. +func (s *SecretStore) ListSecrets(ctx context.Context, filter store.SecretFilter) ([]store.Secret, error) { + query := s.client.Secret.Query() + + if filter.Scope != "" { + query.Where(entsecret.ScopeEQ(filter.Scope)) + } + if filter.ScopeID != "" { + query.Where(entsecret.ScopeIDEQ(filter.ScopeID)) + } + if filter.Key != "" { + query.Where(entsecret.KeyEQ(filter.Key)) + } + if filter.Type != "" { + query.Where(entsecret.SecretTypeEQ(entsecret.SecretType(filter.Type))) + } + + rows, err := query.Order(entsecret.ByKey()).All(ctx) + if err != nil { + return nil, err + } + + secrets := make([]store.Secret, 0, len(rows)) + for _, e := range rows { + secrets = append(secrets, entSecretRowToStore(e, false)) + } + return secrets, nil +} + +// ListProgenySecrets returns user-scoped secrets with allowProgeny=true whose +// createdBy is in the given set of ancestor IDs. The EncryptedValue is never +// populated. This preserves the progeny-inheritance semantics of the legacy +// SQLite query via an IN-list over created_by. +func (s *SecretStore) ListProgenySecrets(ctx context.Context, ancestorIDs []string) ([]store.Secret, error) { + if len(ancestorIDs) == 0 { + return nil, nil + } + + rows, err := s.client.Secret.Query(). + Where( + entsecret.ScopeEQ(store.ScopeUser), + entsecret.AllowProgenyEQ(true), + entsecret.CreatedByIn(ancestorIDs...), + ). + Order(entsecret.ByKey()). + All(ctx) + if err != nil { + return nil, err + } + + secrets := make([]store.Secret, 0, len(rows)) + for _, e := range rows { + secrets = append(secrets, entSecretRowToStore(e, false)) + } + return secrets, nil +} + +// GetSecretValue retrieves the encrypted value of a secret. +func (s *SecretStore) GetSecretValue(ctx context.Context, key, scope, scopeID string) (string, error) { + e, err := s.client.Secret.Query(). + Where( + entsecret.KeyEQ(key), + entsecret.ScopeEQ(scope), + entsecret.ScopeIDEQ(scopeID), + ). + Only(ctx) + if err != nil { + return "", mapError(err) + } + return e.EncryptedValue, nil +} + +// ============================================================================= +// EnvVar operations +// ============================================================================= + +// entEnvVarToStore converts an Ent EnvVar entity to a store.EnvVar model. +func entEnvVarToStore(e *ent.EnvVar) store.EnvVar { + return store.EnvVar{ + ID: e.ID.String(), + Key: e.Key, + Value: e.Value, + Scope: e.Scope, + ScopeID: e.ScopeID, + Description: e.Description, + Sensitive: e.Sensitive, + InjectionMode: string(e.InjectionMode), + Secret: e.Secret, + Created: e.Created, + Updated: e.Updated, + CreatedBy: e.CreatedBy, + } +} + +// CreateEnvVar creates a new environment variable. +func (s *SecretStore) CreateEnvVar(ctx context.Context, envVar *store.EnvVar) error { + uid, err := parseUUID(envVar.ID) + if err != nil { + return err + } + + now := time.Now() + envVar.Created = now + envVar.Updated = now + + // The Ent enum column rejects empty values; normalize to the default the + // legacy schema applied (as_needed) and reflect it back on the model. + if envVar.InjectionMode == "" { + envVar.InjectionMode = store.InjectionModeAsNeeded + } + + create := s.client.EnvVar.Create(). + SetID(uid). + SetKey(envVar.Key). + SetValue(envVar.Value). + SetScope(envVar.Scope). + SetScopeID(envVar.ScopeID). + SetSensitive(envVar.Sensitive). + SetInjectionMode(entenvvar.InjectionMode(envVar.InjectionMode)). + SetSecret(envVar.Secret). + SetCreated(envVar.Created). + SetUpdated(envVar.Updated) + + if envVar.Description != "" { + create.SetDescription(envVar.Description) + } + if envVar.CreatedBy != "" { + create.SetCreatedBy(envVar.CreatedBy) + } + + if _, err := create.Save(ctx); err != nil { + return mapError(err) + } + return nil +} + +// GetEnvVar retrieves an environment variable by key, scope, and scopeID. +func (s *SecretStore) GetEnvVar(ctx context.Context, key, scope, scopeID string) (*store.EnvVar, error) { + e, err := s.client.EnvVar.Query(). + Where( + entenvvar.KeyEQ(key), + entenvvar.ScopeEQ(scope), + entenvvar.ScopeIDEQ(scopeID), + ). + Only(ctx) + if err != nil { + return nil, mapError(err) + } + ev := entEnvVarToStore(e) + return &ev, nil +} + +// UpdateEnvVar updates an existing environment variable. +func (s *SecretStore) UpdateEnvVar(ctx context.Context, envVar *store.EnvVar) error { + envVar.Updated = time.Now() + + if envVar.InjectionMode == "" { + envVar.InjectionMode = store.InjectionModeAsNeeded + } + + n, err := s.client.EnvVar.Update(). + Where( + entenvvar.KeyEQ(envVar.Key), + entenvvar.ScopeEQ(envVar.Scope), + entenvvar.ScopeIDEQ(envVar.ScopeID), + ). + SetValue(envVar.Value). + SetDescription(envVar.Description). + SetSensitive(envVar.Sensitive). + SetInjectionMode(entenvvar.InjectionMode(envVar.InjectionMode)). + SetSecret(envVar.Secret). + SetUpdated(envVar.Updated). + Save(ctx) + if err != nil { + return mapError(err) + } + if n == 0 { + return store.ErrNotFound + } + return nil +} + +// UpsertEnvVar creates or updates an environment variable, keyed by +// (key, scope, scopeID). +func (s *SecretStore) UpsertEnvVar(ctx context.Context, envVar *store.EnvVar) (bool, error) { + now := time.Now() + envVar.Updated = now + + existing, err := s.GetEnvVar(ctx, envVar.Key, envVar.Scope, envVar.ScopeID) + if err != nil && !errors.Is(err, store.ErrNotFound) { + return false, err + } + + if existing != nil { + envVar.ID = existing.ID + envVar.Created = existing.Created + envVar.CreatedBy = existing.CreatedBy + if err := s.UpdateEnvVar(ctx, envVar); err != nil { + return false, err + } + return false, nil + } + + envVar.Created = now + if err := s.CreateEnvVar(ctx, envVar); err != nil { + return false, err + } + return true, nil +} + +// DeleteEnvVar removes an environment variable by key, scope, and scopeID. +func (s *SecretStore) DeleteEnvVar(ctx context.Context, key, scope, scopeID string) error { + n, err := s.client.EnvVar.Delete(). + Where( + entenvvar.KeyEQ(key), + entenvvar.ScopeEQ(scope), + entenvvar.ScopeIDEQ(scopeID), + ). + Exec(ctx) + if err != nil { + return err + } + if n == 0 { + return store.ErrNotFound + } + return nil +} + +// DeleteEnvVarsByScope removes all environment variables for a given scope, +// returning the number of deleted records. +func (s *SecretStore) DeleteEnvVarsByScope(ctx context.Context, scope, scopeID string) (int, error) { + n, err := s.client.EnvVar.Delete(). + Where( + entenvvar.ScopeEQ(scope), + entenvvar.ScopeIDEQ(scopeID), + ). + Exec(ctx) + if err != nil { + return 0, err + } + return n, nil +} + +// ListEnvVars returns environment variables matching the filter, ordered by key. +func (s *SecretStore) ListEnvVars(ctx context.Context, filter store.EnvVarFilter) ([]store.EnvVar, error) { + query := s.client.EnvVar.Query() + + if filter.Scope != "" { + query.Where(entenvvar.ScopeEQ(filter.Scope)) + } + if filter.ScopeID != "" { + query.Where(entenvvar.ScopeIDEQ(filter.ScopeID)) + } + if filter.Key != "" { + query.Where(entenvvar.KeyEQ(filter.Key)) + } + + rows, err := query.Order(entenvvar.ByKey()).All(ctx) + if err != nil { + return nil, err + } + + envVars := make([]store.EnvVar, 0, len(rows)) + for _, e := range rows { + envVars = append(envVars, entEnvVarToStore(e)) + } + return envVars, nil +} diff --git a/pkg/store/entadapter/secret_store_test.go b/pkg/store/entadapter/secret_store_test.go new file mode 100644 index 000000000..8f3b9d26d --- /dev/null +++ b/pkg/store/entadapter/secret_store_test.go @@ -0,0 +1,443 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build !no_sqlite + +package entadapter + +import ( + "context" + "testing" + + "github.com/GoogleCloudPlatform/scion/pkg/store" + "github.com/GoogleCloudPlatform/scion/pkg/store/enttest" + "github.com/google/uuid" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func newTestSecretStore(t *testing.T) *SecretStore { + t.Helper() + client := enttest.NewClient(t) + return NewSecretStore(client) +} + +// ============================================================================= +// Secret tests +// ============================================================================= + +func TestCreateAndGetSecret(t *testing.T) { + ss := newTestSecretStore(t) + ctx := context.Background() + + sec := &store.Secret{ + ID: uuid.New().String(), + Key: "API_KEY", + EncryptedValue: "enc-value", + Scope: store.ScopeUser, + ScopeID: uuid.New().String(), + Description: "an api key", + AllowProgeny: true, + CreatedBy: uuid.New().String(), + } + require.NoError(t, ss.CreateSecret(ctx, sec)) + + // Defaults are applied on create. + assert.Equal(t, store.SecretTypeEnvironment, sec.SecretType) + assert.Equal(t, store.InjectionModeAsNeeded, sec.InjectionMode) + assert.Equal(t, "API_KEY", sec.Target, "empty target should default to key") + assert.Equal(t, 1, sec.Version) + assert.False(t, sec.Created.IsZero()) + + got, err := ss.GetSecret(ctx, "API_KEY", store.ScopeUser, sec.ScopeID) + require.NoError(t, err) + assert.Equal(t, sec.ID, got.ID) + assert.Equal(t, "enc-value", got.EncryptedValue) + assert.Equal(t, "API_KEY", got.Target) + assert.True(t, got.AllowProgeny) + assert.Equal(t, 1, got.Version) +} + +func TestGetSecretNotFound(t *testing.T) { + ss := newTestSecretStore(t) + ctx := context.Background() + + _, err := ss.GetSecret(ctx, "missing", store.ScopeUser, uuid.New().String()) + assert.ErrorIs(t, err, store.ErrNotFound) +} + +func TestCreateSecretDuplicate(t *testing.T) { + ss := newTestSecretStore(t) + ctx := context.Background() + + scopeID := uuid.New().String() + sec := &store.Secret{ID: uuid.New().String(), Key: "DUP", EncryptedValue: "v", Scope: store.ScopeUser, ScopeID: scopeID} + require.NoError(t, ss.CreateSecret(ctx, sec)) + + dup := &store.Secret{ID: uuid.New().String(), Key: "DUP", EncryptedValue: "v2", Scope: store.ScopeUser, ScopeID: scopeID} + err := ss.CreateSecret(ctx, dup) + assert.ErrorIs(t, err, store.ErrAlreadyExists) +} + +func TestUpdateSecretIncrementsVersion(t *testing.T) { + ss := newTestSecretStore(t) + ctx := context.Background() + + scopeID := uuid.New().String() + sec := &store.Secret{ID: uuid.New().String(), Key: "K", EncryptedValue: "v1", Scope: store.ScopeProject, ScopeID: scopeID} + require.NoError(t, ss.CreateSecret(ctx, sec)) + require.Equal(t, 1, sec.Version) + + sec.EncryptedValue = "v2" + require.NoError(t, ss.UpdateSecret(ctx, sec)) + assert.Equal(t, 2, sec.Version) + + got, err := ss.GetSecret(ctx, "K", store.ScopeProject, scopeID) + require.NoError(t, err) + assert.Equal(t, "v2", got.EncryptedValue) + assert.Equal(t, 2, got.Version) +} + +func TestUpdateSecretNotFound(t *testing.T) { + ss := newTestSecretStore(t) + ctx := context.Background() + + sec := &store.Secret{Key: "ghost", EncryptedValue: "v", Scope: store.ScopeUser, ScopeID: uuid.New().String()} + err := ss.UpdateSecret(ctx, sec) + assert.ErrorIs(t, err, store.ErrNotFound) +} + +func TestUpsertSecret(t *testing.T) { + ss := newTestSecretStore(t) + ctx := context.Background() + + scopeID := uuid.New().String() + sec := &store.Secret{ID: uuid.New().String(), Key: "UP", EncryptedValue: "v1", Scope: store.ScopeUser, ScopeID: scopeID, CreatedBy: "creator"} + + created, err := ss.UpsertSecret(ctx, sec) + require.NoError(t, err) + assert.True(t, created, "first upsert should create") + + // Second upsert updates, preserving identity and creation metadata. + upd := &store.Secret{Key: "UP", EncryptedValue: "v2", Scope: store.ScopeUser, ScopeID: scopeID} + created, err = ss.UpsertSecret(ctx, upd) + require.NoError(t, err) + assert.False(t, created, "second upsert should update") + assert.Equal(t, sec.ID, upd.ID, "ID preserved across upsert") + assert.Equal(t, "creator", upd.CreatedBy, "createdBy preserved across upsert") + + got, err := ss.GetSecret(ctx, "UP", store.ScopeUser, scopeID) + require.NoError(t, err) + assert.Equal(t, "v2", got.EncryptedValue) + assert.Equal(t, 2, got.Version) +} + +func TestDeleteSecret(t *testing.T) { + ss := newTestSecretStore(t) + ctx := context.Background() + + scopeID := uuid.New().String() + sec := &store.Secret{ID: uuid.New().String(), Key: "DEL", EncryptedValue: "v", Scope: store.ScopeUser, ScopeID: scopeID} + require.NoError(t, ss.CreateSecret(ctx, sec)) + + require.NoError(t, ss.DeleteSecret(ctx, "DEL", store.ScopeUser, scopeID)) + + _, err := ss.GetSecret(ctx, "DEL", store.ScopeUser, scopeID) + assert.ErrorIs(t, err, store.ErrNotFound) + + // Deleting again returns ErrNotFound. + assert.ErrorIs(t, ss.DeleteSecret(ctx, "DEL", store.ScopeUser, scopeID), store.ErrNotFound) +} + +func TestDeleteSecretsByScope(t *testing.T) { + ss := newTestSecretStore(t) + ctx := context.Background() + + scopeID := uuid.New().String() + for _, k := range []string{"A", "B", "C"} { + require.NoError(t, ss.CreateSecret(ctx, &store.Secret{ + ID: uuid.New().String(), Key: k, EncryptedValue: "v", Scope: store.ScopeProject, ScopeID: scopeID, + })) + } + // A secret in a different scope must survive. + require.NoError(t, ss.CreateSecret(ctx, &store.Secret{ + ID: uuid.New().String(), Key: "OTHER", EncryptedValue: "v", Scope: store.ScopeProject, ScopeID: uuid.New().String(), + })) + + n, err := ss.DeleteSecretsByScope(ctx, store.ScopeProject, scopeID) + require.NoError(t, err) + assert.Equal(t, 3, n) + + remaining, err := ss.ListSecrets(ctx, store.SecretFilter{Scope: store.ScopeProject, ScopeID: scopeID}) + require.NoError(t, err) + assert.Empty(t, remaining) +} + +func TestListSecretsExcludesEncryptedValue(t *testing.T) { + ss := newTestSecretStore(t) + ctx := context.Background() + + scopeID := uuid.New().String() + require.NoError(t, ss.CreateSecret(ctx, &store.Secret{ + ID: uuid.New().String(), Key: "LIST", EncryptedValue: "super-secret", Scope: store.ScopeUser, ScopeID: scopeID, + })) + + list, err := ss.ListSecrets(ctx, store.SecretFilter{Scope: store.ScopeUser, ScopeID: scopeID}) + require.NoError(t, err) + require.Len(t, list, 1) + assert.Empty(t, list[0].EncryptedValue, "ListSecrets must not expose encrypted value") + assert.Equal(t, "LIST", list[0].Key) +} + +func TestListSecretsFilterByType(t *testing.T) { + ss := newTestSecretStore(t) + ctx := context.Background() + + scopeID := uuid.New().String() + require.NoError(t, ss.CreateSecret(ctx, &store.Secret{ + ID: uuid.New().String(), Key: "ENV", EncryptedValue: "v", SecretType: store.SecretTypeEnvironment, Scope: store.ScopeUser, ScopeID: scopeID, + })) + require.NoError(t, ss.CreateSecret(ctx, &store.Secret{ + ID: uuid.New().String(), Key: "FILE", EncryptedValue: "v", SecretType: store.SecretTypeFile, Target: "/etc/x", Scope: store.ScopeUser, ScopeID: scopeID, + })) + + files, err := ss.ListSecrets(ctx, store.SecretFilter{Scope: store.ScopeUser, ScopeID: scopeID, Type: store.SecretTypeFile}) + require.NoError(t, err) + require.Len(t, files, 1) + assert.Equal(t, "FILE", files[0].Key) + assert.Equal(t, "/etc/x", files[0].Target) +} + +func TestGetSecretValue(t *testing.T) { + ss := newTestSecretStore(t) + ctx := context.Background() + + scopeID := uuid.New().String() + require.NoError(t, ss.CreateSecret(ctx, &store.Secret{ + ID: uuid.New().String(), Key: "VAL", EncryptedValue: "the-value", Scope: store.ScopeUser, ScopeID: scopeID, + })) + + v, err := ss.GetSecretValue(ctx, "VAL", store.ScopeUser, scopeID) + require.NoError(t, err) + assert.Equal(t, "the-value", v) + + _, err = ss.GetSecretValue(ctx, "nope", store.ScopeUser, scopeID) + assert.ErrorIs(t, err, store.ErrNotFound) +} + +// TestScopePolymorphism verifies that identical keys in different scopes are +// independent records (no cross-scope collisions). +func TestSecretScopePolymorphism(t *testing.T) { + ss := newTestSecretStore(t) + ctx := context.Background() + + userID := uuid.New().String() + projectID := uuid.New().String() + + require.NoError(t, ss.CreateSecret(ctx, &store.Secret{ID: uuid.New().String(), Key: "TOKEN", EncryptedValue: "user-val", Scope: store.ScopeUser, ScopeID: userID})) + require.NoError(t, ss.CreateSecret(ctx, &store.Secret{ID: uuid.New().String(), Key: "TOKEN", EncryptedValue: "proj-val", Scope: store.ScopeProject, ScopeID: projectID})) + + u, err := ss.GetSecret(ctx, "TOKEN", store.ScopeUser, userID) + require.NoError(t, err) + assert.Equal(t, "user-val", u.EncryptedValue) + + p, err := ss.GetSecret(ctx, "TOKEN", store.ScopeProject, projectID) + require.NoError(t, err) + assert.Equal(t, "proj-val", p.EncryptedValue) +} + +// TestListProgenySecretsInheritance verifies the transitive progeny-inheritance +// query: only user-scoped, allow_progeny=true secrets whose created_by is within +// the ancestor set are returned, and the encrypted value is never exposed. +func TestListProgenySecretsInheritance(t *testing.T) { + ss := newTestSecretStore(t) + ctx := context.Background() + + ancestor1 := uuid.New().String() + ancestor2 := uuid.New().String() + stranger := uuid.New().String() + + // Eligible: user-scoped, allow_progeny, created by an ancestor. + require.NoError(t, ss.CreateSecret(ctx, &store.Secret{ + ID: uuid.New().String(), Key: "INHERIT_1", EncryptedValue: "secret-1", + Scope: store.ScopeUser, ScopeID: ancestor1, AllowProgeny: true, CreatedBy: ancestor1, + })) + require.NoError(t, ss.CreateSecret(ctx, &store.Secret{ + ID: uuid.New().String(), Key: "INHERIT_2", EncryptedValue: "secret-2", + Scope: store.ScopeUser, ScopeID: ancestor2, AllowProgeny: true, CreatedBy: ancestor2, + })) + + // Ineligible: allow_progeny=false. + require.NoError(t, ss.CreateSecret(ctx, &store.Secret{ + ID: uuid.New().String(), Key: "NO_PROGENY", EncryptedValue: "x", + Scope: store.ScopeUser, ScopeID: ancestor1, AllowProgeny: false, CreatedBy: ancestor1, + })) + // Ineligible: created by a non-ancestor. + require.NoError(t, ss.CreateSecret(ctx, &store.Secret{ + ID: uuid.New().String(), Key: "STRANGER", EncryptedValue: "x", + Scope: store.ScopeUser, ScopeID: stranger, AllowProgeny: true, CreatedBy: stranger, + })) + // Ineligible: wrong scope, even though allow_progeny + ancestor creator. + require.NoError(t, ss.CreateSecret(ctx, &store.Secret{ + ID: uuid.New().String(), Key: "PROJ", EncryptedValue: "x", + Scope: store.ScopeProject, ScopeID: uuid.New().String(), AllowProgeny: true, CreatedBy: ancestor1, + })) + + got, err := ss.ListProgenySecrets(ctx, []string{ancestor1, ancestor2}) + require.NoError(t, err) + require.Len(t, got, 2) + + keys := map[string]bool{} + for _, s := range got { + keys[s.Key] = true + assert.Empty(t, s.EncryptedValue, "progeny secrets must not expose encrypted value") + } + assert.True(t, keys["INHERIT_1"]) + assert.True(t, keys["INHERIT_2"]) +} + +func TestListProgenySecretsEmptyAncestors(t *testing.T) { + ss := newTestSecretStore(t) + ctx := context.Background() + + got, err := ss.ListProgenySecrets(ctx, nil) + require.NoError(t, err) + assert.Nil(t, got) +} + +// ============================================================================= +// EnvVar tests +// ============================================================================= + +func TestCreateAndGetEnvVar(t *testing.T) { + ss := newTestSecretStore(t) + ctx := context.Background() + + scopeID := uuid.New().String() + ev := &store.EnvVar{ + ID: uuid.New().String(), + Key: "LOG_LEVEL", + Value: "debug", + Scope: store.ScopeProject, + ScopeID: scopeID, + Description: "logging", + Sensitive: true, + } + require.NoError(t, ss.CreateEnvVar(ctx, ev)) + assert.Equal(t, store.InjectionModeAsNeeded, ev.InjectionMode, "empty injection mode normalized") + assert.False(t, ev.Created.IsZero()) + + got, err := ss.GetEnvVar(ctx, "LOG_LEVEL", store.ScopeProject, scopeID) + require.NoError(t, err) + assert.Equal(t, "debug", got.Value) + assert.True(t, got.Sensitive) + assert.Equal(t, "logging", got.Description) +} + +func TestCreateEnvVarDuplicate(t *testing.T) { + ss := newTestSecretStore(t) + ctx := context.Background() + + scopeID := uuid.New().String() + require.NoError(t, ss.CreateEnvVar(ctx, &store.EnvVar{ID: uuid.New().String(), Key: "X", Value: "1", Scope: store.ScopeUser, ScopeID: scopeID})) + err := ss.CreateEnvVar(ctx, &store.EnvVar{ID: uuid.New().String(), Key: "X", Value: "2", Scope: store.ScopeUser, ScopeID: scopeID}) + assert.ErrorIs(t, err, store.ErrAlreadyExists) +} + +func TestUpdateEnvVar(t *testing.T) { + ss := newTestSecretStore(t) + ctx := context.Background() + + scopeID := uuid.New().String() + ev := &store.EnvVar{ID: uuid.New().String(), Key: "U", Value: "1", Scope: store.ScopeUser, ScopeID: scopeID} + require.NoError(t, ss.CreateEnvVar(ctx, ev)) + + ev.Value = "2" + require.NoError(t, ss.UpdateEnvVar(ctx, ev)) + + got, err := ss.GetEnvVar(ctx, "U", store.ScopeUser, scopeID) + require.NoError(t, err) + assert.Equal(t, "2", got.Value) +} + +func TestUpdateEnvVarNotFound(t *testing.T) { + ss := newTestSecretStore(t) + ctx := context.Background() + + err := ss.UpdateEnvVar(ctx, &store.EnvVar{Key: "ghost", Value: "v", Scope: store.ScopeUser, ScopeID: uuid.New().String()}) + assert.ErrorIs(t, err, store.ErrNotFound) +} + +func TestUpsertEnvVar(t *testing.T) { + ss := newTestSecretStore(t) + ctx := context.Background() + + scopeID := uuid.New().String() + ev := &store.EnvVar{ID: uuid.New().String(), Key: "UP", Value: "1", Scope: store.ScopeUser, ScopeID: scopeID, CreatedBy: "creator"} + + created, err := ss.UpsertEnvVar(ctx, ev) + require.NoError(t, err) + assert.True(t, created) + + upd := &store.EnvVar{Key: "UP", Value: "2", Scope: store.ScopeUser, ScopeID: scopeID} + created, err = ss.UpsertEnvVar(ctx, upd) + require.NoError(t, err) + assert.False(t, created) + assert.Equal(t, ev.ID, upd.ID) + assert.Equal(t, "creator", upd.CreatedBy) + + got, err := ss.GetEnvVar(ctx, "UP", store.ScopeUser, scopeID) + require.NoError(t, err) + assert.Equal(t, "2", got.Value) +} + +func TestDeleteEnvVar(t *testing.T) { + ss := newTestSecretStore(t) + ctx := context.Background() + + scopeID := uuid.New().String() + require.NoError(t, ss.CreateEnvVar(ctx, &store.EnvVar{ID: uuid.New().String(), Key: "D", Value: "1", Scope: store.ScopeUser, ScopeID: scopeID})) + require.NoError(t, ss.DeleteEnvVar(ctx, "D", store.ScopeUser, scopeID)) + _, err := ss.GetEnvVar(ctx, "D", store.ScopeUser, scopeID) + assert.ErrorIs(t, err, store.ErrNotFound) + assert.ErrorIs(t, ss.DeleteEnvVar(ctx, "D", store.ScopeUser, scopeID), store.ErrNotFound) +} + +func TestDeleteEnvVarsByScope(t *testing.T) { + ss := newTestSecretStore(t) + ctx := context.Background() + + scopeID := uuid.New().String() + for _, k := range []string{"A", "B"} { + require.NoError(t, ss.CreateEnvVar(ctx, &store.EnvVar{ID: uuid.New().String(), Key: k, Value: "v", Scope: store.ScopeProject, ScopeID: scopeID})) + } + n, err := ss.DeleteEnvVarsByScope(ctx, store.ScopeProject, scopeID) + require.NoError(t, err) + assert.Equal(t, 2, n) +} + +func TestListEnvVarsOrderedByKey(t *testing.T) { + ss := newTestSecretStore(t) + ctx := context.Background() + + scopeID := uuid.New().String() + for _, k := range []string{"ZEBRA", "ALPHA", "MIKE"} { + require.NoError(t, ss.CreateEnvVar(ctx, &store.EnvVar{ID: uuid.New().String(), Key: k, Value: "v", Scope: store.ScopeUser, ScopeID: scopeID})) + } + + list, err := ss.ListEnvVars(ctx, store.EnvVarFilter{Scope: store.ScopeUser, ScopeID: scopeID}) + require.NoError(t, err) + require.Len(t, list, 3) + assert.Equal(t, []string{"ALPHA", "MIKE", "ZEBRA"}, []string{list[0].Key, list[1].Key, list[2].Key}) +} diff --git a/pkg/store/entadapter/template_store.go b/pkg/store/entadapter/template_store.go new file mode 100644 index 000000000..ea8a5dc23 --- /dev/null +++ b/pkg/store/entadapter/template_store.go @@ -0,0 +1,585 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package entadapter + +import ( + "context" + "encoding/json" + "time" + + entsql "entgo.io/ent/dialect/sql" + "github.com/GoogleCloudPlatform/scion/pkg/ent" + entharnessconfig "github.com/GoogleCloudPlatform/scion/pkg/ent/harnessconfig" + enttemplate "github.com/GoogleCloudPlatform/scion/pkg/ent/template" + "github.com/GoogleCloudPlatform/scion/pkg/store" +) + +// TemplateStore implements store.TemplateStore and store.HarnessConfigStore +// using Ent ORM. +// +// Both entities use (scope, scope_id) polymorphic addressing rather than FK +// edges so that global/unscoped rows port cleanly. The structured config and +// file-manifest columns are stored as raw JSON strings, matching the legacy +// SQLite layout. Subscription templates, although a sibling notification-domain +// table, are intentionally NOT handled here — they are owned by NotificationStore. +type TemplateStore struct { + client *ent.Client +} + +// NewTemplateStore creates a new Ent-backed TemplateStore. +func NewTemplateStore(client *ent.Client) *TemplateStore { + return &TemplateStore{client: client} +} + +// marshalJSONString serializes a value to a JSON string for storage in a TEXT +// column. A nil value yields an empty string. This mirrors the SQLite adapter's +// marshalJSON helper so both backends round-trip identically. +func marshalJSONString(v interface{}) string { + if v == nil { + return "" + } + data, err := json.Marshal(v) + if err != nil { + return "" + } + return string(data) +} + +// unmarshalJSONString deserializes a JSON string into v. An empty string is a +// no-op, leaving v at its zero value. +func unmarshalJSONString[T any](data string, v *T) { + if data == "" { + return + } + _ = json.Unmarshal([]byte(data), v) +} + +// ============================================================================= +// Template operations +// ============================================================================= + +// entTemplateRowToStore converts an Ent Template entity to a store.Template model. +func entTemplateRowToStore(e *ent.Template) *store.Template { + t := &store.Template{ + ID: e.ID.String(), + Name: e.Name, + Slug: e.Slug, + DisplayName: e.DisplayName, + Description: e.Description, + Harness: e.Harness, + DefaultHarnessConfig: e.DefaultHarnessConfig, + Image: e.Image, + ContentHash: e.ContentHash, + Scope: e.Scope, + ScopeID: e.ScopeID, + ProjectID: e.ProjectID, + StorageURI: e.StorageURI, + StorageBucket: e.StorageBucket, + StoragePath: e.StoragePath, + BaseTemplate: e.BaseTemplate, + Locked: e.Locked, + Status: string(e.Status), + OwnerID: e.OwnerID, + CreatedBy: e.CreatedBy, + UpdatedBy: e.UpdatedBy, + Visibility: e.Visibility, + Created: e.Created, + Updated: e.Updated, + } + unmarshalJSONString(e.Config, &t.Config) + unmarshalJSONString(e.Files, &t.Files) + return t +} + +// CreateTemplate creates a new template record. +func (s *TemplateStore) CreateTemplate(ctx context.Context, template *store.Template) error { + uid, err := parseUUID(template.ID) + if err != nil { + return err + } + + now := time.Now() + template.Created = now + template.Updated = now + + if template.Status == "" { + template.Status = store.TemplateStatusActive + } + + create := s.client.Template.Create(). + SetID(uid). + SetName(template.Name). + SetSlug(template.Slug). + SetDisplayName(template.DisplayName). + SetDescription(template.Description). + SetHarness(template.Harness). + SetDefaultHarnessConfig(template.DefaultHarnessConfig). + SetImage(template.Image). + SetConfig(marshalJSONString(template.Config)). + SetContentHash(template.ContentHash). + SetScope(template.Scope). + SetScopeID(template.ScopeID). + SetProjectID(template.ProjectID). + SetStorageURI(template.StorageURI). + SetStorageBucket(template.StorageBucket). + SetStoragePath(template.StoragePath). + SetFiles(marshalJSONString(template.Files)). + SetBaseTemplate(template.BaseTemplate). + SetLocked(template.Locked). + SetStatus(enttemplate.Status(template.Status)). + SetOwnerID(template.OwnerID). + SetCreatedBy(template.CreatedBy). + SetUpdatedBy(template.UpdatedBy). + SetVisibility(template.Visibility). + SetCreated(template.Created). + SetUpdated(template.Updated) + + if _, err := create.Save(ctx); err != nil { + return mapError(err) + } + return nil +} + +// GetTemplate retrieves a template by ID. +func (s *TemplateStore) GetTemplate(ctx context.Context, id string) (*store.Template, error) { + uid, err := parseGetID(id) + if err != nil { + return nil, err + } + e, err := s.client.Template.Get(ctx, uid) + if err != nil { + return nil, mapError(err) + } + return entTemplateRowToStore(e), nil +} + +// GetTemplateBySlug retrieves a template by its slug and scope. For the project +// scope it also matches the legacy project_id column for backwards compatibility. +func (s *TemplateStore) GetTemplateBySlug(ctx context.Context, slug, scope, scopeID string) (*store.Template, error) { + query := s.client.Template.Query(). + Where( + enttemplate.SlugEQ(slug), + enttemplate.ScopeEQ(scope), + ) + + switch { + case scope == store.TemplateScopeProject && scopeID != "": + query.Where(enttemplate.Or( + enttemplate.ScopeIDEQ(scopeID), + enttemplate.ProjectIDEQ(scopeID), + )) + case scope == store.TemplateScopeUser && scopeID != "": + query.Where(enttemplate.ScopeIDEQ(scopeID)) + } + + e, err := query.First(ctx) + if err != nil { + return nil, mapError(err) + } + return entTemplateRowToStore(e), nil +} + +// UpdateTemplate updates an existing template. +func (s *TemplateStore) UpdateTemplate(ctx context.Context, template *store.Template) error { + uid, err := parseUUID(template.ID) + if err != nil { + return err + } + + template.Updated = time.Now() + + _, err = s.client.Template.UpdateOneID(uid). + SetName(template.Name). + SetSlug(template.Slug). + SetDisplayName(template.DisplayName). + SetDescription(template.Description). + SetHarness(template.Harness). + SetDefaultHarnessConfig(template.DefaultHarnessConfig). + SetImage(template.Image). + SetConfig(marshalJSONString(template.Config)). + SetContentHash(template.ContentHash). + SetScope(template.Scope). + SetScopeID(template.ScopeID). + SetProjectID(template.ProjectID). + SetStorageURI(template.StorageURI). + SetStorageBucket(template.StorageBucket). + SetStoragePath(template.StoragePath). + SetFiles(marshalJSONString(template.Files)). + SetBaseTemplate(template.BaseTemplate). + SetLocked(template.Locked). + SetStatus(enttemplate.Status(template.Status)). + SetOwnerID(template.OwnerID). + SetUpdatedBy(template.UpdatedBy). + SetVisibility(template.Visibility). + SetUpdated(template.Updated). + Save(ctx) + if err != nil { + return mapError(err) + } + return nil +} + +// DeleteTemplate removes a template by ID. +func (s *TemplateStore) DeleteTemplate(ctx context.Context, id string) error { + uid, err := parseUUID(id) + if err != nil { + return err + } + if err := s.client.Template.DeleteOneID(uid).Exec(ctx); err != nil { + return mapError(err) + } + return nil +} + +// DeleteTemplatesByScope removes all templates for a given scope, returning the +// number of deleted records. +func (s *TemplateStore) DeleteTemplatesByScope(ctx context.Context, scope, scopeID string) (int, error) { + n, err := s.client.Template.Delete(). + Where( + enttemplate.ScopeEQ(scope), + enttemplate.ScopeIDEQ(scopeID), + ). + Exec(ctx) + if err != nil { + return 0, err + } + return n, nil +} + +// ListTemplates returns templates matching the filter criteria. +func (s *TemplateStore) ListTemplates(ctx context.Context, filter store.TemplateFilter, opts store.ListOptions) (*store.ListResult[store.Template], error) { + query := s.client.Template.Query() + + if filter.Name != "" { + query.Where(enttemplate.Or( + enttemplate.NameEQ(filter.Name), + enttemplate.SlugEQ(filter.Name), + )) + } + if filter.Scope != "" { + query.Where(enttemplate.ScopeEQ(filter.Scope)) + } + switch { + case filter.ScopeID != "": + query.Where(enttemplate.Or( + enttemplate.ScopeIDEQ(filter.ScopeID), + enttemplate.ProjectIDEQ(filter.ScopeID), + )) + case filter.ProjectID != "" && filter.Scope == "": + // Project-without-scope: return global plus this project's templates. + query.Where(enttemplate.Or( + enttemplate.ScopeEQ(store.TemplateScopeGlobal), + enttemplate.And( + enttemplate.ScopeEQ(store.TemplateScopeProject), + enttemplate.Or( + enttemplate.ScopeIDEQ(filter.ProjectID), + enttemplate.ProjectIDEQ(filter.ProjectID), + ), + ), + )) + case filter.ProjectID != "": + query.Where(enttemplate.Or( + enttemplate.ScopeIDEQ(filter.ProjectID), + enttemplate.ProjectIDEQ(filter.ProjectID), + )) + } + if filter.Harness != "" { + query.Where(enttemplate.HarnessEQ(filter.Harness)) + } + if filter.OwnerID != "" { + query.Where(enttemplate.OwnerIDEQ(filter.OwnerID)) + } + if filter.Status != "" { + query.Where(enttemplate.StatusEQ(enttemplate.Status(filter.Status))) + } + if filter.Search != "" { + query.Where(enttemplate.Or( + enttemplate.NameContainsFold(filter.Search), + enttemplate.DescriptionContainsFold(filter.Search), + )) + } + + totalCount, err := query.Clone().Count(ctx) + if err != nil { + return nil, err + } + + limit := opts.Limit + if limit <= 0 { + limit = 50 + } + + rows, err := query. + Order(enttemplate.ByCreated(entsql.OrderDesc())). + Limit(limit). + All(ctx) + if err != nil { + return nil, err + } + + items := make([]store.Template, 0, len(rows)) + for _, e := range rows { + items = append(items, *entTemplateRowToStore(e)) + } + + return &store.ListResult[store.Template]{ + Items: items, + TotalCount: totalCount, + }, nil +} + +// ============================================================================= +// HarnessConfig operations +// ============================================================================= + +// entHarnessConfigToStore converts an Ent HarnessConfig entity to a +// store.HarnessConfig model. +func entHarnessConfigToStore(e *ent.HarnessConfig) *store.HarnessConfig { + hc := &store.HarnessConfig{ + ID: e.ID.String(), + Name: e.Name, + Slug: e.Slug, + DisplayName: e.DisplayName, + Description: e.Description, + Harness: e.Harness, + ContentHash: e.ContentHash, + Scope: e.Scope, + ScopeID: e.ScopeID, + StorageURI: e.StorageURI, + StorageBucket: e.StorageBucket, + StoragePath: e.StoragePath, + Locked: e.Locked, + Status: string(e.Status), + OwnerID: e.OwnerID, + CreatedBy: e.CreatedBy, + UpdatedBy: e.UpdatedBy, + Visibility: e.Visibility, + Created: e.Created, + Updated: e.Updated, + } + unmarshalJSONString(e.Config, &hc.Config) + unmarshalJSONString(e.Files, &hc.Files) + return hc +} + +// CreateHarnessConfig creates a new harness config record. +func (s *TemplateStore) CreateHarnessConfig(ctx context.Context, hc *store.HarnessConfig) error { + uid, err := parseUUID(hc.ID) + if err != nil { + return err + } + + now := time.Now() + hc.Created = now + hc.Updated = now + + if hc.Status == "" { + hc.Status = store.HarnessConfigStatusActive + } + + create := s.client.HarnessConfig.Create(). + SetID(uid). + SetName(hc.Name). + SetSlug(hc.Slug). + SetDisplayName(hc.DisplayName). + SetDescription(hc.Description). + SetHarness(hc.Harness). + SetConfig(marshalJSONString(hc.Config)). + SetContentHash(hc.ContentHash). + SetScope(hc.Scope). + SetScopeID(hc.ScopeID). + SetStorageURI(hc.StorageURI). + SetStorageBucket(hc.StorageBucket). + SetStoragePath(hc.StoragePath). + SetFiles(marshalJSONString(hc.Files)). + SetLocked(hc.Locked). + SetStatus(entharnessconfig.Status(hc.Status)). + SetOwnerID(hc.OwnerID). + SetCreatedBy(hc.CreatedBy). + SetUpdatedBy(hc.UpdatedBy). + SetVisibility(hc.Visibility). + SetCreated(hc.Created). + SetUpdated(hc.Updated) + + if _, err := create.Save(ctx); err != nil { + return mapError(err) + } + return nil +} + +// GetHarnessConfig retrieves a harness config by ID. +func (s *TemplateStore) GetHarnessConfig(ctx context.Context, id string) (*store.HarnessConfig, error) { + uid, err := parseGetID(id) + if err != nil { + return nil, err + } + e, err := s.client.HarnessConfig.Get(ctx, uid) + if err != nil { + return nil, mapError(err) + } + return entHarnessConfigToStore(e), nil +} + +// GetHarnessConfigBySlug retrieves a harness config by its slug and scope. +func (s *TemplateStore) GetHarnessConfigBySlug(ctx context.Context, slug, scope, scopeID string) (*store.HarnessConfig, error) { + query := s.client.HarnessConfig.Query(). + Where( + entharnessconfig.SlugEQ(slug), + entharnessconfig.ScopeEQ(scope), + ) + if scopeID != "" { + query.Where(entharnessconfig.ScopeIDEQ(scopeID)) + } + + e, err := query.First(ctx) + if err != nil { + return nil, mapError(err) + } + return entHarnessConfigToStore(e), nil +} + +// UpdateHarnessConfig updates an existing harness config. +func (s *TemplateStore) UpdateHarnessConfig(ctx context.Context, hc *store.HarnessConfig) error { + uid, err := parseUUID(hc.ID) + if err != nil { + return err + } + + hc.Updated = time.Now() + + _, err = s.client.HarnessConfig.UpdateOneID(uid). + SetName(hc.Name). + SetSlug(hc.Slug). + SetDisplayName(hc.DisplayName). + SetDescription(hc.Description). + SetHarness(hc.Harness). + SetConfig(marshalJSONString(hc.Config)). + SetContentHash(hc.ContentHash). + SetScope(hc.Scope). + SetScopeID(hc.ScopeID). + SetStorageURI(hc.StorageURI). + SetStorageBucket(hc.StorageBucket). + SetStoragePath(hc.StoragePath). + SetFiles(marshalJSONString(hc.Files)). + SetLocked(hc.Locked). + SetStatus(entharnessconfig.Status(hc.Status)). + SetOwnerID(hc.OwnerID). + SetUpdatedBy(hc.UpdatedBy). + SetVisibility(hc.Visibility). + SetUpdated(hc.Updated). + Save(ctx) + if err != nil { + return mapError(err) + } + return nil +} + +// DeleteHarnessConfig removes a harness config by ID. +func (s *TemplateStore) DeleteHarnessConfig(ctx context.Context, id string) error { + uid, err := parseUUID(id) + if err != nil { + return err + } + if err := s.client.HarnessConfig.DeleteOneID(uid).Exec(ctx); err != nil { + return mapError(err) + } + return nil +} + +// DeleteHarnessConfigsByScope removes all harness configs for a given scope, +// returning the number of deleted records. +func (s *TemplateStore) DeleteHarnessConfigsByScope(ctx context.Context, scope, scopeID string) (int, error) { + n, err := s.client.HarnessConfig.Delete(). + Where( + entharnessconfig.ScopeEQ(scope), + entharnessconfig.ScopeIDEQ(scopeID), + ). + Exec(ctx) + if err != nil { + return 0, err + } + return n, nil +} + +// ListHarnessConfigs returns harness configs matching the filter criteria. +func (s *TemplateStore) ListHarnessConfigs(ctx context.Context, filter store.HarnessConfigFilter, opts store.ListOptions) (*store.ListResult[store.HarnessConfig], error) { + query := s.client.HarnessConfig.Query() + + if filter.Name != "" { + query.Where(entharnessconfig.Or( + entharnessconfig.NameEQ(filter.Name), + entharnessconfig.SlugEQ(filter.Name), + )) + } + if filter.Scope != "" { + query.Where(entharnessconfig.ScopeEQ(filter.Scope)) + } + switch { + case filter.ScopeID != "": + query.Where(entharnessconfig.ScopeIDEQ(filter.ScopeID)) + case filter.ProjectID != "" && filter.Scope == "": + // Project-without-scope: return global plus this project's configs. + query.Where(entharnessconfig.Or( + entharnessconfig.ScopeEQ(store.HarnessConfigScopeGlobal), + entharnessconfig.And( + entharnessconfig.ScopeEQ(store.HarnessConfigScopeProject), + entharnessconfig.ScopeIDEQ(filter.ProjectID), + ), + )) + } + if filter.Harness != "" { + query.Where(entharnessconfig.HarnessEQ(filter.Harness)) + } + if filter.OwnerID != "" { + query.Where(entharnessconfig.OwnerIDEQ(filter.OwnerID)) + } + if filter.Status != "" { + query.Where(entharnessconfig.StatusEQ(entharnessconfig.Status(filter.Status))) + } + if filter.Search != "" { + query.Where(entharnessconfig.Or( + entharnessconfig.NameContainsFold(filter.Search), + entharnessconfig.DescriptionContainsFold(filter.Search), + )) + } + + totalCount, err := query.Clone().Count(ctx) + if err != nil { + return nil, err + } + + limit := opts.Limit + if limit <= 0 { + limit = 50 + } + + rows, err := query. + Order(entharnessconfig.ByCreated(entsql.OrderDesc())). + Limit(limit). + All(ctx) + if err != nil { + return nil, err + } + + items := make([]store.HarnessConfig, 0, len(rows)) + for _, e := range rows { + items = append(items, *entHarnessConfigToStore(e)) + } + + return &store.ListResult[store.HarnessConfig]{ + Items: items, + TotalCount: totalCount, + }, nil +} diff --git a/pkg/store/entadapter/template_store_test.go b/pkg/store/entadapter/template_store_test.go new file mode 100644 index 000000000..e1d6a2fdb --- /dev/null +++ b/pkg/store/entadapter/template_store_test.go @@ -0,0 +1,368 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build !no_sqlite + +package entadapter + +import ( + "context" + "testing" + + "github.com/GoogleCloudPlatform/scion/pkg/store" + "github.com/GoogleCloudPlatform/scion/pkg/store/enttest" + "github.com/google/uuid" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func newTestTemplateStore(t *testing.T) *TemplateStore { + t.Helper() + client := enttest.NewClient(t) + return NewTemplateStore(client) +} + +// ============================================================================= +// Template tests +// ============================================================================= + +func TestCreateAndGetTemplate(t *testing.T) { + ts := newTestTemplateStore(t) + ctx := context.Background() + + tmpl := &store.Template{ + ID: uuid.New().String(), + Name: "claude", + Slug: "claude", + Harness: "claude", + Image: "img:latest", + Scope: store.TemplateScopeGlobal, + Visibility: "public", + ContentHash: "abc123", + Config: &store.TemplateConfig{ + Harness: "claude", + Image: "img:latest", + Env: map[string]string{"FOO": "bar"}, + }, + Files: []store.TemplateFile{{Path: "home/.bashrc", Size: 10, Hash: "h"}}, + } + require.NoError(t, ts.CreateTemplate(ctx, tmpl)) + assert.Equal(t, store.TemplateStatusActive, tmpl.Status, "empty status defaults to active") + assert.False(t, tmpl.Created.IsZero()) + + got, err := ts.GetTemplate(ctx, tmpl.ID) + require.NoError(t, err) + assert.Equal(t, "claude", got.Name) + assert.Equal(t, "abc123", got.ContentHash) + require.NotNil(t, got.Config) + assert.Equal(t, "bar", got.Config.Env["FOO"]) + require.Len(t, got.Files, 1) + assert.Equal(t, "home/.bashrc", got.Files[0].Path) +} + +func TestGetTemplateNotFound(t *testing.T) { + ts := newTestTemplateStore(t) + _, err := ts.GetTemplate(context.Background(), uuid.New().String()) + assert.ErrorIs(t, err, store.ErrNotFound) +} + +func TestCreateTemplateDuplicateID(t *testing.T) { + ts := newTestTemplateStore(t) + ctx := context.Background() + + id := uuid.New().String() + require.NoError(t, ts.CreateTemplate(ctx, &store.Template{ID: id, Name: "a", Slug: "a", Harness: "claude", Scope: "global"})) + err := ts.CreateTemplate(ctx, &store.Template{ID: id, Name: "b", Slug: "b", Harness: "claude", Scope: "global"}) + assert.ErrorIs(t, err, store.ErrAlreadyExists) +} + +func TestGetTemplateBySlug(t *testing.T) { + ts := newTestTemplateStore(t) + ctx := context.Background() + + projectID := uuid.New().String() + tmpl := &store.Template{ + ID: uuid.New().String(), Name: "custom", Slug: "custom", Harness: "gemini", + Scope: store.TemplateScopeProject, ScopeID: projectID, + } + require.NoError(t, ts.CreateTemplate(ctx, tmpl)) + + got, err := ts.GetTemplateBySlug(ctx, "custom", store.TemplateScopeProject, projectID) + require.NoError(t, err) + assert.Equal(t, tmpl.ID, got.ID) + + _, err = ts.GetTemplateBySlug(ctx, "custom", store.TemplateScopeProject, uuid.New().String()) + assert.ErrorIs(t, err, store.ErrNotFound) +} + +// TestGetTemplateBySlugLegacyProjectID verifies the backwards-compat path where +// a project-scoped template was stored with project_id rather than scope_id. +func TestGetTemplateBySlugLegacyProjectID(t *testing.T) { + ts := newTestTemplateStore(t) + ctx := context.Background() + + projectID := uuid.New().String() + tmpl := &store.Template{ + ID: uuid.New().String(), Name: "legacy", Slug: "legacy", Harness: "claude", + Scope: store.TemplateScopeProject, ProjectID: projectID, // scope_id intentionally empty + } + require.NoError(t, ts.CreateTemplate(ctx, tmpl)) + + got, err := ts.GetTemplateBySlug(ctx, "legacy", store.TemplateScopeProject, projectID) + require.NoError(t, err) + assert.Equal(t, tmpl.ID, got.ID) +} + +func TestUpdateTemplate(t *testing.T) { + ts := newTestTemplateStore(t) + ctx := context.Background() + + tmpl := &store.Template{ID: uuid.New().String(), Name: "old", Slug: "old", Harness: "claude", Scope: "global", Status: store.TemplateStatusActive} + require.NoError(t, ts.CreateTemplate(ctx, tmpl)) + + tmpl.Name = "new" + tmpl.Status = store.TemplateStatusArchived + tmpl.Config = &store.TemplateConfig{Model: "opus"} + require.NoError(t, ts.UpdateTemplate(ctx, tmpl)) + + got, err := ts.GetTemplate(ctx, tmpl.ID) + require.NoError(t, err) + assert.Equal(t, "new", got.Name) + assert.Equal(t, store.TemplateStatusArchived, got.Status) + require.NotNil(t, got.Config) + assert.Equal(t, "opus", got.Config.Model) +} + +func TestUpdateTemplateNotFound(t *testing.T) { + ts := newTestTemplateStore(t) + tmpl := &store.Template{ID: uuid.New().String(), Name: "ghost", Slug: "ghost", Harness: "claude", Scope: "global", Status: "active"} + err := ts.UpdateTemplate(context.Background(), tmpl) + assert.ErrorIs(t, err, store.ErrNotFound) +} + +func TestDeleteTemplate(t *testing.T) { + ts := newTestTemplateStore(t) + ctx := context.Background() + + tmpl := &store.Template{ID: uuid.New().String(), Name: "del", Slug: "del", Harness: "claude", Scope: "global"} + require.NoError(t, ts.CreateTemplate(ctx, tmpl)) + require.NoError(t, ts.DeleteTemplate(ctx, tmpl.ID)) + _, err := ts.GetTemplate(ctx, tmpl.ID) + assert.ErrorIs(t, err, store.ErrNotFound) + assert.ErrorIs(t, ts.DeleteTemplate(ctx, tmpl.ID), store.ErrNotFound) +} + +func TestDeleteTemplatesByScope(t *testing.T) { + ts := newTestTemplateStore(t) + ctx := context.Background() + + scopeID := uuid.New().String() + for i, n := range []string{"a", "b", "c"} { + require.NoError(t, ts.CreateTemplate(ctx, &store.Template{ + ID: uuid.New().String(), Name: n, Slug: n, Harness: "claude", + Scope: store.TemplateScopeProject, ScopeID: scopeID, Status: "active", + })) + _ = i + } + // Different scope survives. + require.NoError(t, ts.CreateTemplate(ctx, &store.Template{ + ID: uuid.New().String(), Name: "other", Slug: "other", Harness: "claude", + Scope: store.TemplateScopeProject, ScopeID: uuid.New().String(), Status: "active", + })) + + n, err := ts.DeleteTemplatesByScope(ctx, store.TemplateScopeProject, scopeID) + require.NoError(t, err) + assert.Equal(t, 3, n) +} + +func TestListTemplatesPagination(t *testing.T) { + ts := newTestTemplateStore(t) + ctx := context.Background() + + for i := 0; i < 5; i++ { + require.NoError(t, ts.CreateTemplate(ctx, &store.Template{ + ID: uuid.New().String(), Name: uuid.NewString(), Slug: uuid.NewString(), Harness: "claude", Scope: "global", Status: "active", + })) + } + + all, err := ts.ListTemplates(ctx, store.TemplateFilter{}, store.ListOptions{}) + require.NoError(t, err) + assert.Len(t, all.Items, 5) + assert.Equal(t, 5, all.TotalCount) + + page, err := ts.ListTemplates(ctx, store.TemplateFilter{}, store.ListOptions{Limit: 2}) + require.NoError(t, err) + assert.Len(t, page.Items, 2) + assert.Equal(t, 5, page.TotalCount, "TotalCount independent of limit") +} + +func TestListTemplatesFilterByHarness(t *testing.T) { + ts := newTestTemplateStore(t) + ctx := context.Background() + + require.NoError(t, ts.CreateTemplate(ctx, &store.Template{ID: uuid.New().String(), Name: "c", Slug: "c", Harness: "claude", Scope: "global", Status: "active"})) + require.NoError(t, ts.CreateTemplate(ctx, &store.Template{ID: uuid.New().String(), Name: "g", Slug: "g", Harness: "gemini", Scope: "global", Status: "active"})) + + res, err := ts.ListTemplates(ctx, store.TemplateFilter{Harness: "gemini"}, store.ListOptions{}) + require.NoError(t, err) + require.Len(t, res.Items, 1) + assert.Equal(t, "gemini", res.Items[0].Harness) +} + +// TestListTemplatesProjectScopeIncludesGlobal verifies the projectId-without-scope +// filter returns global plus the project's own templates, but not other projects'. +func TestListTemplatesProjectScopeIncludesGlobal(t *testing.T) { + ts := newTestTemplateStore(t) + ctx := context.Background() + + projectID := uuid.New().String() + require.NoError(t, ts.CreateTemplate(ctx, &store.Template{ID: uuid.New().String(), Name: "global1", Slug: "global1", Harness: "claude", Scope: store.TemplateScopeGlobal, Status: "active"})) + require.NoError(t, ts.CreateTemplate(ctx, &store.Template{ID: uuid.New().String(), Name: "proj1", Slug: "proj1", Harness: "claude", Scope: store.TemplateScopeProject, ScopeID: projectID, Status: "active"})) + require.NoError(t, ts.CreateTemplate(ctx, &store.Template{ID: uuid.New().String(), Name: "otherproj", Slug: "otherproj", Harness: "claude", Scope: store.TemplateScopeProject, ScopeID: uuid.New().String(), Status: "active"})) + + res, err := ts.ListTemplates(ctx, store.TemplateFilter{ProjectID: projectID}, store.ListOptions{}) + require.NoError(t, err) + assert.Equal(t, 2, res.TotalCount, "should see global + own project, not other project") +} + +func TestListTemplatesSearch(t *testing.T) { + ts := newTestTemplateStore(t) + ctx := context.Background() + + require.NoError(t, ts.CreateTemplate(ctx, &store.Template{ID: uuid.New().String(), Name: "Production Web", Slug: "prod-web", Harness: "claude", Scope: "global", Status: "active"})) + require.NoError(t, ts.CreateTemplate(ctx, &store.Template{ID: uuid.New().String(), Name: "Staging", Slug: "staging", Harness: "claude", Scope: "global", Status: "active", Description: "production-like"})) + require.NoError(t, ts.CreateTemplate(ctx, &store.Template{ID: uuid.New().String(), Name: "Dev", Slug: "dev", Harness: "claude", Scope: "global", Status: "active"})) + + res, err := ts.ListTemplates(ctx, store.TemplateFilter{Search: "produc"}, store.ListOptions{}) + require.NoError(t, err) + assert.Equal(t, 2, res.TotalCount, "case-insensitive search matches name and description") +} + +// ============================================================================= +// HarnessConfig tests +// ============================================================================= + +func TestCreateAndGetHarnessConfig(t *testing.T) { + ts := newTestTemplateStore(t) + ctx := context.Background() + + hc := &store.HarnessConfig{ + ID: uuid.New().String(), + Name: "claude-web", + Slug: "claude-web", + Harness: "claude", + Scope: store.HarnessConfigScopeGlobal, + ContentHash: "h1", + Config: &store.HarnessConfigData{Harness: "claude", Model: "opus", Env: map[string]string{"A": "B"}}, + } + require.NoError(t, ts.CreateHarnessConfig(ctx, hc)) + assert.Equal(t, store.HarnessConfigStatusActive, hc.Status) + assert.False(t, hc.Created.IsZero()) + + got, err := ts.GetHarnessConfig(ctx, hc.ID) + require.NoError(t, err) + assert.Equal(t, "claude-web", got.Name) + require.NotNil(t, got.Config) + assert.Equal(t, "opus", got.Config.Model) + assert.Equal(t, "B", got.Config.Env["A"]) +} + +func TestGetHarnessConfigBySlug(t *testing.T) { + ts := newTestTemplateStore(t) + ctx := context.Background() + + scopeID := uuid.New().String() + hc := &store.HarnessConfig{ID: uuid.New().String(), Name: "x", Slug: "x", Harness: "claude", Scope: store.HarnessConfigScopeProject, ScopeID: scopeID} + require.NoError(t, ts.CreateHarnessConfig(ctx, hc)) + + got, err := ts.GetHarnessConfigBySlug(ctx, "x", store.HarnessConfigScopeProject, scopeID) + require.NoError(t, err) + assert.Equal(t, hc.ID, got.ID) + + _, err = ts.GetHarnessConfigBySlug(ctx, "x", store.HarnessConfigScopeProject, uuid.New().String()) + assert.ErrorIs(t, err, store.ErrNotFound) +} + +func TestUpdateHarnessConfig(t *testing.T) { + ts := newTestTemplateStore(t) + ctx := context.Background() + + hc := &store.HarnessConfig{ID: uuid.New().String(), Name: "old", Slug: "old", Harness: "claude", Scope: "global", Status: "active"} + require.NoError(t, ts.CreateHarnessConfig(ctx, hc)) + + hc.Name = "new" + hc.Config = &store.HarnessConfigData{Model: "haiku"} + require.NoError(t, ts.UpdateHarnessConfig(ctx, hc)) + + got, err := ts.GetHarnessConfig(ctx, hc.ID) + require.NoError(t, err) + assert.Equal(t, "new", got.Name) + require.NotNil(t, got.Config) + assert.Equal(t, "haiku", got.Config.Model) +} + +func TestUpdateHarnessConfigNotFound(t *testing.T) { + ts := newTestTemplateStore(t) + hc := &store.HarnessConfig{ID: uuid.New().String(), Name: "ghost", Slug: "ghost", Harness: "claude", Scope: "global", Status: "active"} + err := ts.UpdateHarnessConfig(context.Background(), hc) + assert.ErrorIs(t, err, store.ErrNotFound) +} + +func TestDeleteHarnessConfig(t *testing.T) { + ts := newTestTemplateStore(t) + ctx := context.Background() + + hc := &store.HarnessConfig{ID: uuid.New().String(), Name: "del", Slug: "del", Harness: "claude", Scope: "global"} + require.NoError(t, ts.CreateHarnessConfig(ctx, hc)) + require.NoError(t, ts.DeleteHarnessConfig(ctx, hc.ID)) + _, err := ts.GetHarnessConfig(ctx, hc.ID) + assert.ErrorIs(t, err, store.ErrNotFound) +} + +func TestDeleteHarnessConfigsByScope(t *testing.T) { + ts := newTestTemplateStore(t) + ctx := context.Background() + + scopeID := uuid.New().String() + for _, n := range []string{"a", "b"} { + require.NoError(t, ts.CreateHarnessConfig(ctx, &store.HarnessConfig{ID: uuid.New().String(), Name: n, Slug: n, Harness: "claude", Scope: store.HarnessConfigScopeProject, ScopeID: scopeID, Status: "active"})) + } + n, err := ts.DeleteHarnessConfigsByScope(ctx, store.HarnessConfigScopeProject, scopeID) + require.NoError(t, err) + assert.Equal(t, 2, n) +} + +func TestListHarnessConfigsPaginationAndFilter(t *testing.T) { + ts := newTestTemplateStore(t) + ctx := context.Background() + + for i := 0; i < 4; i++ { + require.NoError(t, ts.CreateHarnessConfig(ctx, &store.HarnessConfig{ID: uuid.New().String(), Name: uuid.NewString(), Slug: uuid.NewString(), Harness: "claude", Scope: "global", Status: "active"})) + } + require.NoError(t, ts.CreateHarnessConfig(ctx, &store.HarnessConfig{ID: uuid.New().String(), Name: "g", Slug: "g", Harness: "gemini", Scope: "global", Status: "active"})) + + all, err := ts.ListHarnessConfigs(ctx, store.HarnessConfigFilter{}, store.ListOptions{}) + require.NoError(t, err) + assert.Equal(t, 5, all.TotalCount) + + page, err := ts.ListHarnessConfigs(ctx, store.HarnessConfigFilter{}, store.ListOptions{Limit: 2}) + require.NoError(t, err) + assert.Len(t, page.Items, 2) + assert.Equal(t, 5, page.TotalCount) + + gemini, err := ts.ListHarnessConfigs(ctx, store.HarnessConfigFilter{Harness: "gemini"}, store.ListOptions{}) + require.NoError(t, err) + require.Len(t, gemini.Items, 1) + assert.Equal(t, "gemini", gemini.Items[0].Harness) +} diff --git a/pkg/store/entadapter/user_allowlist_behavior_test.go b/pkg/store/entadapter/user_allowlist_behavior_test.go new file mode 100644 index 000000000..27adf0133 --- /dev/null +++ b/pkg/store/entadapter/user_allowlist_behavior_test.go @@ -0,0 +1,218 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build !no_sqlite + +package entadapter + +import ( + "context" + "testing" + "time" + + "github.com/GoogleCloudPlatform/scion/pkg/ent" + "github.com/GoogleCloudPlatform/scion/pkg/store" + "github.com/GoogleCloudPlatform/scion/pkg/store/enttest" + "github.com/google/uuid" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func newTestEntClient(t *testing.T) *ent.Client { + t.Helper() + client := enttest.NewClient(t) + return client +} + +// TestUserStore_EmailCaseInsensitive verifies that email uniqueness and lookup +// are case-insensitive, preserving the legacy COLLATE NOCASE semantics. +func TestUserStore_EmailCaseInsensitive(t *testing.T) { + ctx := context.Background() + us := NewUserStore(newTestEntClient(t)) + + require.NoError(t, us.CreateUser(ctx, &store.User{ + ID: uuid.NewString(), + Email: "Mixed.Case@Example.com", + DisplayName: "Mixed", + Role: store.UserRoleMember, + Status: "active", + })) + + // Lookup with a different case must find the same user. + got, err := us.GetUserByEmail(ctx, "mixed.case@EXAMPLE.COM") + require.NoError(t, err) + assert.Equal(t, "mixed.case@example.com", got.Email, "email is normalized to lower case") + + // A case-variant insert must collide on the unique index. + err = us.CreateUser(ctx, &store.User{ + ID: uuid.NewString(), + Email: "MIXED.CASE@example.com", + DisplayName: "Dup", + Role: store.UserRoleMember, + Status: "active", + }) + assert.ErrorIs(t, err, store.ErrAlreadyExists) +} + +// TestUserStore_UpdateLastSeen verifies the dedicated last_seen mutator. +func TestUserStore_UpdateLastSeen(t *testing.T) { + ctx := context.Background() + us := NewUserStore(newTestEntClient(t)) + + id := uuid.NewString() + require.NoError(t, us.CreateUser(ctx, &store.User{ + ID: id, Email: "seen@example.com", DisplayName: "Seen", + Role: store.UserRoleMember, Status: "active", + })) + + ts := time.Now().Add(-time.Hour).UTC().Truncate(time.Second) + require.NoError(t, us.UpdateUserLastSeen(ctx, id, ts)) + + got, err := us.GetUser(ctx, id) + require.NoError(t, err) + assert.WithinDuration(t, ts, got.LastSeen, time.Second) + + // Missing user → ErrNotFound. + assert.ErrorIs(t, us.UpdateUserLastSeen(ctx, uuid.NewString(), ts), store.ErrNotFound) +} + +// TestAllowList_BulkAddIdempotent verifies INSERT-OR-IGNORE semantics: emails +// already present or repeated within the batch are skipped, not errored. +func TestAllowList_BulkAddIdempotent(t *testing.T) { + ctx := context.Background() + as := NewAllowListStore(newTestEntClient(t)) + + require.NoError(t, as.AddAllowListEntry(ctx, &store.AllowListEntry{ + ID: uuid.NewString(), Email: "existing@example.com", AddedBy: "admin", + })) + + entries := []*store.AllowListEntry{ + {ID: uuid.NewString(), Email: "EXISTING@example.com", AddedBy: "admin"}, // dup of existing (case-insensitive) + {ID: uuid.NewString(), Email: "new1@example.com", AddedBy: "admin"}, + {ID: uuid.NewString(), Email: "new2@example.com", AddedBy: "admin"}, + {ID: uuid.NewString(), Email: "New1@example.com", AddedBy: "admin"}, // dup within batch + } + added, skipped, err := as.BulkAddAllowListEntries(ctx, entries) + require.NoError(t, err) + assert.Equal(t, 2, added, "new1 and new2 are added") + assert.Equal(t, 2, skipped, "existing and the repeated new1 are skipped") + + ok, err := as.IsEmailAllowListed(ctx, "NEW2@EXAMPLE.COM") + require.NoError(t, err) + assert.True(t, ok) +} + +// TestInvite_IncrementUseCount verifies the conditional increment honors +// revoked, expired, and max-uses guards. +func TestInvite_IncrementUseCount(t *testing.T) { + ctx := context.Background() + as := NewAllowListStore(newTestEntClient(t)) + + mk := func(maxUses int, revoked bool, expires time.Time) string { + id := uuid.NewString() + require.NoError(t, as.CreateInviteCode(ctx, &store.InviteCode{ + ID: id, CodeHash: "h-" + id, CodePrefix: "scion_in", MaxUses: maxUses, + Revoked: revoked, ExpiresAt: expires, CreatedBy: "admin", + })) + return id + } + + future := time.Now().Add(time.Hour) + past := time.Now().Add(-time.Hour) + + // Redeemable: increments up to max_uses, then refuses. + limited := mk(2, false, future) + require.NoError(t, as.IncrementInviteUseCount(ctx, limited)) + require.NoError(t, as.IncrementInviteUseCount(ctx, limited)) + assert.ErrorIs(t, as.IncrementInviteUseCount(ctx, limited), store.ErrNotFound, "exhausted") + + got, err := as.GetInviteCode(ctx, limited) + require.NoError(t, err) + assert.Equal(t, 2, got.UseCount) + + // Unlimited (max_uses == 0) keeps incrementing. + unlimited := mk(0, false, future) + require.NoError(t, as.IncrementInviteUseCount(ctx, unlimited)) + require.NoError(t, as.IncrementInviteUseCount(ctx, unlimited)) + + // Revoked and expired codes are not redeemable. + assert.ErrorIs(t, as.IncrementInviteUseCount(ctx, mk(5, true, future)), store.ErrNotFound) + assert.ErrorIs(t, as.IncrementInviteUseCount(ctx, mk(5, false, past)), store.ErrNotFound) +} + +// TestInvite_GetStats verifies aggregate stats over invite codes and the allow +// list. +func TestInvite_GetStats(t *testing.T) { + ctx := context.Background() + as := NewAllowListStore(newTestEntClient(t)) + + future := time.Now().Add(time.Hour) + past := time.Now().Add(-time.Hour) + + // Pending (redeemed once), exhausted, and expired codes. + mk := func(maxUses, useCount int, revoked bool, expires time.Time) { + id := uuid.NewString() + require.NoError(t, as.CreateInviteCode(ctx, &store.InviteCode{ + ID: id, CodeHash: "h-" + id, CodePrefix: "scion_in", MaxUses: maxUses, + UseCount: useCount, Revoked: revoked, ExpiresAt: expires, CreatedBy: "admin", + })) + } + mk(5, 2, false, future) // pending, 2 redemptions + mk(1, 1, false, future) // exhausted, 1 redemption + mk(5, 3, false, past) // expired, 3 redemptions + + require.NoError(t, as.AddAllowListEntry(ctx, &store.AllowListEntry{ + ID: uuid.NewString(), Email: "a@example.com", AddedBy: "admin", + })) + + stats, err := as.GetInviteStats(ctx) + require.NoError(t, err) + assert.Equal(t, 1, stats.PendingInvites, "only the non-expired, non-exhausted code is pending") + assert.Equal(t, 6, stats.TotalRedemptions, "2+1+3") + assert.Equal(t, 1, stats.AllowListCount) + assert.Len(t, stats.RecentRedemptions, 3, "all three have use_count > 0") +} + +// TestAllowList_WithInvites verifies the manual join enriching entries with +// invite details. +func TestAllowList_WithInvites(t *testing.T) { + ctx := context.Background() + as := NewAllowListStore(newTestEntClient(t)) + + inviteID := uuid.NewString() + require.NoError(t, as.CreateInviteCode(ctx, &store.InviteCode{ + ID: inviteID, CodeHash: "h-" + inviteID, CodePrefix: "scion_inv_abc", + MaxUses: 10, UseCount: 4, ExpiresAt: time.Now().Add(time.Hour), CreatedBy: "admin", + })) + require.NoError(t, as.AddAllowListEntry(ctx, &store.AllowListEntry{ + ID: uuid.NewString(), Email: "linked@example.com", AddedBy: "admin", InviteID: inviteID, + })) + require.NoError(t, as.AddAllowListEntry(ctx, &store.AllowListEntry{ + ID: uuid.NewString(), Email: "unlinked@example.com", AddedBy: "admin", + })) + + res, err := as.ListAllowListEntriesWithInvites(ctx, store.ListOptions{}) + require.NoError(t, err) + require.Len(t, res.Items, 2) + + byEmail := map[string]store.AllowListEntryWithInvite{} + for _, e := range res.Items { + byEmail[e.Email] = e + } + linked := byEmail["linked@example.com"] + assert.Equal(t, "scion_inv_abc", linked.InviteCodePrefix) + assert.Equal(t, 10, linked.InviteMaxUses) + assert.Equal(t, 4, linked.InviteUseCount) + assert.Empty(t, byEmail["unlinked@example.com"].InviteCodePrefix) +} diff --git a/pkg/store/entadapter/user_allowlist_oracle_test.go b/pkg/store/entadapter/user_allowlist_oracle_test.go new file mode 100644 index 000000000..35cc5f896 --- /dev/null +++ b/pkg/store/entadapter/user_allowlist_oracle_test.go @@ -0,0 +1,144 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build !no_sqlite + +package entadapter + +import ( + "context" + "testing" + "time" + + "github.com/GoogleCloudPlatform/scion/pkg/store" + "github.com/GoogleCloudPlatform/scion/pkg/store/enttest" + "github.com/GoogleCloudPlatform/scion/pkg/store/storetest" +) + +// entUserAllowStore is a test-only store that routes the user and +// allowlist/invite domains to the Ent-backed adapters, while delegating +// everything else to the embedded CompositeStore. It previews the wiring that +// P2-collapse will fold into composite.go, letting the shared CRUD-parity +// oracle run directly against the new adapters. +type entUserAllowStore struct { + *CompositeStore + users *UserStore + allow *AllowListStore +} + +// UserStore overrides. + +func (s *entUserAllowStore) CreateUser(ctx context.Context, u *store.User) error { + return s.users.CreateUser(ctx, u) +} +func (s *entUserAllowStore) GetUser(ctx context.Context, id string) (*store.User, error) { + return s.users.GetUser(ctx, id) +} +func (s *entUserAllowStore) GetUserByEmail(ctx context.Context, email string) (*store.User, error) { + return s.users.GetUserByEmail(ctx, email) +} +func (s *entUserAllowStore) UpdateUser(ctx context.Context, u *store.User) error { + return s.users.UpdateUser(ctx, u) +} +func (s *entUserAllowStore) UpdateUserLastSeen(ctx context.Context, id string, t time.Time) error { + return s.users.UpdateUserLastSeen(ctx, id, t) +} +func (s *entUserAllowStore) DeleteUser(ctx context.Context, id string) error { + return s.users.DeleteUser(ctx, id) +} +func (s *entUserAllowStore) ListUsers(ctx context.Context, filter store.UserFilter, opts store.ListOptions) (*store.ListResult[store.User], error) { + return s.users.ListUsers(ctx, filter, opts) +} + +// AllowListStore overrides. + +func (s *entUserAllowStore) AddAllowListEntry(ctx context.Context, entry *store.AllowListEntry) error { + return s.allow.AddAllowListEntry(ctx, entry) +} +func (s *entUserAllowStore) RemoveAllowListEntry(ctx context.Context, email string) error { + return s.allow.RemoveAllowListEntry(ctx, email) +} +func (s *entUserAllowStore) GetAllowListEntry(ctx context.Context, email string) (*store.AllowListEntry, error) { + return s.allow.GetAllowListEntry(ctx, email) +} +func (s *entUserAllowStore) ListAllowListEntries(ctx context.Context, opts store.ListOptions) (*store.ListResult[store.AllowListEntry], error) { + return s.allow.ListAllowListEntries(ctx, opts) +} +func (s *entUserAllowStore) IsEmailAllowListed(ctx context.Context, email string) (bool, error) { + return s.allow.IsEmailAllowListed(ctx, email) +} +func (s *entUserAllowStore) BulkAddAllowListEntries(ctx context.Context, entries []*store.AllowListEntry) (int, int, error) { + return s.allow.BulkAddAllowListEntries(ctx, entries) +} +func (s *entUserAllowStore) ListEmailDomains(ctx context.Context) ([]string, error) { + return s.allow.ListEmailDomains(ctx) +} +func (s *entUserAllowStore) UpdateAllowListEntryInviteID(ctx context.Context, email string, inviteID string) error { + return s.allow.UpdateAllowListEntryInviteID(ctx, email, inviteID) +} +func (s *entUserAllowStore) ListAllowListEntriesWithInvites(ctx context.Context, opts store.ListOptions) (*store.ListResult[store.AllowListEntryWithInvite], error) { + return s.allow.ListAllowListEntriesWithInvites(ctx, opts) +} + +// InviteCodeStore overrides. + +func (s *entUserAllowStore) CreateInviteCode(ctx context.Context, invite *store.InviteCode) error { + return s.allow.CreateInviteCode(ctx, invite) +} +func (s *entUserAllowStore) GetInviteCodeByHash(ctx context.Context, codeHash string) (*store.InviteCode, error) { + return s.allow.GetInviteCodeByHash(ctx, codeHash) +} +func (s *entUserAllowStore) GetInviteCode(ctx context.Context, id string) (*store.InviteCode, error) { + return s.allow.GetInviteCode(ctx, id) +} +func (s *entUserAllowStore) ListInviteCodes(ctx context.Context, opts store.ListOptions) (*store.ListResult[store.InviteCode], error) { + return s.allow.ListInviteCodes(ctx, opts) +} +func (s *entUserAllowStore) IncrementInviteUseCount(ctx context.Context, id string) error { + return s.allow.IncrementInviteUseCount(ctx, id) +} +func (s *entUserAllowStore) RevokeInviteCode(ctx context.Context, id string) error { + return s.allow.RevokeInviteCode(ctx, id) +} +func (s *entUserAllowStore) DeleteInviteCode(ctx context.Context, id string) error { + return s.allow.DeleteInviteCode(ctx, id) +} +func (s *entUserAllowStore) GetInviteStats(ctx context.Context) (*store.InviteStats, error) { + return s.allow.GetInviteStats(ctx) +} + +// entUserAllowFactory builds a store backed by the Ent adapters for the user +// and allowlist/invite domains. +func entUserAllowFactory(t *testing.T) store.Store { + t.Helper() + + entClient := enttest.NewClient(t) + + cs := NewCompositeStore(entClient) + t.Cleanup(func() { _ = cs.Close() }) + + return &entUserAllowStore{ + CompositeStore: cs, + users: NewUserStore(entClient), + allow: NewAllowListStore(entClient), + } +} + +// TestEntAdapter_UserAllowlist_CRUDParity runs the shared CRUD-parity oracle +// against the Ent-backed user and allowlist/invite adapters. +func TestEntAdapter_UserAllowlist_CRUDParity(t *testing.T) { + storetest.RunDomain(t, entUserAllowFactory, storetest.UserDomain()) + storetest.RunDomain(t, entUserAllowFactory, storetest.AllowListDomain()) + storetest.RunDomain(t, entUserAllowFactory, storetest.InviteCodeDomain()) +} diff --git a/pkg/store/entadapter/user_store.go b/pkg/store/entadapter/user_store.go new file mode 100644 index 000000000..b48d1cbdb --- /dev/null +++ b/pkg/store/entadapter/user_store.go @@ -0,0 +1,326 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package entadapter + +import ( + "context" + "strconv" + "strings" + "time" + + "entgo.io/ent/dialect/sql" + "github.com/GoogleCloudPlatform/scion/pkg/ent" + entschema "github.com/GoogleCloudPlatform/scion/pkg/ent/schema" + "github.com/GoogleCloudPlatform/scion/pkg/ent/user" + "github.com/GoogleCloudPlatform/scion/pkg/store" +) + +// sortOpt returns the ent ordering option for the given sort direction, +// defaulting to descending (newest first) to match the legacy SQLite store. +func sortOpt(dir string) sql.OrderTermOption { + if dir == "asc" { + return sql.OrderAsc() + } + return sql.OrderDesc() +} + +// UserStore implements store.UserStore using Ent ORM. +type UserStore struct { + client *ent.Client +} + +// NewUserStore creates a new Ent-backed UserStore. +func NewUserStore(client *ent.Client) *UserStore { + return &UserStore{client: client} +} + +// normalizeEmail lower-cases an email so that the plain unique index on the +// email column enforces case-insensitive uniqueness. The legacy SQLite schema +// used UNIQUE COLLATE NOCASE; Postgres has no NOCASE collation, so we normalize +// at the port layer instead of relying on a functional lower(email) index that +// ent codegen + AutoMigrate cannot emit across both dialects. +func normalizeEmail(email string) string { + return strings.ToLower(strings.TrimSpace(email)) +} + +// storePrefsToEnt converts a store.UserPreferences to the ent schema type. +func storePrefsToEnt(p *store.UserPreferences) *entschema.UserPreferences { + if p == nil { + return nil + } + return &entschema.UserPreferences{ + DefaultTemplate: p.DefaultTemplate, + DefaultProfile: p.DefaultProfile, + Theme: p.Theme, + } +} + +// entPrefsToStore converts an ent schema UserPreferences to the store type. +func entPrefsToStore(p *entschema.UserPreferences) *store.UserPreferences { + if p == nil { + return nil + } + return &store.UserPreferences{ + DefaultTemplate: p.DefaultTemplate, + DefaultProfile: p.DefaultProfile, + Theme: p.Theme, + } +} + +// entUserToStore converts an Ent User entity to a store.User model. +func entUserToStore(u *ent.User) *store.User { + su := &store.User{ + ID: u.ID.String(), + Email: u.Email, + DisplayName: u.DisplayName, + AvatarURL: u.AvatarURL, + Role: string(u.Role), + Status: string(u.Status), + Preferences: entPrefsToStore(u.Preferences), + Created: u.Created, + } + if u.LastLogin != nil { + su.LastLogin = *u.LastLogin + } + if u.LastSeen != nil { + su.LastSeen = *u.LastSeen + } + return su +} + +// CreateUser creates a new user record. +func (s *UserStore) CreateUser(ctx context.Context, u *store.User) error { + uid, err := parseUUID(u.ID) + if err != nil { + return err + } + + if u.Created.IsZero() { + u.Created = time.Now() + } + u.Email = normalizeEmail(u.Email) + + create := s.client.User.Create(). + SetID(uid). + SetEmail(u.Email). + SetDisplayName(u.DisplayName). + SetCreated(u.Created) + + if u.AvatarURL != "" { + create.SetAvatarURL(u.AvatarURL) + } + // Role and Status fall back to the schema defaults (member/active) when the + // caller leaves them empty, matching how the enum validation expects a + // non-empty value. + if u.Role != "" { + create.SetRole(user.Role(u.Role)) + } + if u.Status != "" { + create.SetStatus(user.Status(u.Status)) + } + if u.Preferences != nil { + create.SetPreferences(storePrefsToEnt(u.Preferences)) + } + if !u.LastLogin.IsZero() { + create.SetLastLogin(u.LastLogin) + } + if !u.LastSeen.IsZero() { + create.SetLastSeen(u.LastSeen) + } + + created, err := create.Save(ctx) + if err != nil { + return mapError(err) + } + + u.Created = created.Created + return nil +} + +// GetUser retrieves a user by ID. +func (s *UserStore) GetUser(ctx context.Context, id string) (*store.User, error) { + uid, err := parseGetID(id) + if err != nil { + return nil, err + } + + u, err := s.client.User.Get(ctx, uid) + if err != nil { + return nil, mapError(err) + } + return entUserToStore(u), nil +} + +// GetUserByEmail retrieves a user by email using a case-insensitive match, +// preserving the COLLATE NOCASE semantics of the legacy SQLite schema. +func (s *UserStore) GetUserByEmail(ctx context.Context, email string) (*store.User, error) { + u, err := s.client.User.Query(). + Where(user.EmailEqualFold(normalizeEmail(email))). + Only(ctx) + if err != nil { + return nil, mapError(err) + } + return entUserToStore(u), nil +} + +// UpdateUser updates an existing user. +func (s *UserStore) UpdateUser(ctx context.Context, u *store.User) error { + uid, err := parseUUID(u.ID) + if err != nil { + return err + } + + u.Email = normalizeEmail(u.Email) + + update := s.client.User.UpdateOneID(uid). + SetEmail(u.Email). + SetDisplayName(u.DisplayName) + + if u.AvatarURL != "" { + update.SetAvatarURL(u.AvatarURL) + } else { + update.ClearAvatarURL() + } + if u.Role != "" { + update.SetRole(user.Role(u.Role)) + } + if u.Status != "" { + update.SetStatus(user.Status(u.Status)) + } + if u.Preferences != nil { + update.SetPreferences(storePrefsToEnt(u.Preferences)) + } else { + update.ClearPreferences() + } + if !u.LastLogin.IsZero() { + update.SetLastLogin(u.LastLogin) + } else { + update.ClearLastLogin() + } + if !u.LastSeen.IsZero() { + update.SetLastSeen(u.LastSeen) + } else { + update.ClearLastSeen() + } + + if err := update.Exec(ctx); err != nil { + return mapError(err) + } + return nil +} + +// UpdateUserLastSeen sets only the last_seen timestamp for a user. +func (s *UserStore) UpdateUserLastSeen(ctx context.Context, id string, t time.Time) error { + uid, err := parseUUID(id) + if err != nil { + return err + } + if err := s.client.User.UpdateOneID(uid).SetLastSeen(t).Exec(ctx); err != nil { + return mapError(err) + } + return nil +} + +// DeleteUser removes a user by ID. +func (s *UserStore) DeleteUser(ctx context.Context, id string) error { + uid, err := parseUUID(id) + if err != nil { + return err + } + if err := s.client.User.DeleteOneID(uid).Exec(ctx); err != nil { + return mapError(err) + } + return nil +} + +// ListUsers returns users matching the filter criteria. Pagination is +// offset-based to match the legacy SQLite store: the cursor is the integer +// offset of the next page. +func (s *UserStore) ListUsers(ctx context.Context, filter store.UserFilter, opts store.ListOptions) (*store.ListResult[store.User], error) { + query := s.client.User.Query() + + if filter.Role != "" { + query.Where(user.RoleEQ(user.Role(filter.Role))) + } + if filter.Status != "" { + query.Where(user.StatusEQ(user.Status(filter.Status))) + } + if filter.Search != "" { + query.Where(user.Or( + user.EmailContainsFold(filter.Search), + user.DisplayNameContainsFold(filter.Search), + )) + } + + totalCount, err := query.Clone().Count(ctx) + if err != nil { + return nil, err + } + + limit := opts.Limit + if limit <= 0 { + limit = 50 + } + if limit > 200 { + limit = 200 + } + + offset := 0 + if opts.Cursor != "" { + if parsed, err := strconv.Atoi(opts.Cursor); err == nil && parsed > 0 { + offset = parsed + } + } + + // Map the sort field to an ordering, whitelisting the supported columns. + var order user.OrderOption + switch opts.SortBy { + case "name": + // Name defaults to ascending unless an explicit direction is given. + if opts.SortDir == "desc" { + order = user.ByDisplayName(sql.OrderDesc()) + } else { + order = user.ByDisplayName(sql.OrderAsc()) + } + case "lastSeen": + order = user.ByLastSeen(sortOpt(opts.SortDir)) + default: // "created" and unspecified + order = user.ByCreated(sortOpt(opts.SortDir)) + } + + users, err := query. + Order(order). + Limit(limit + 1). + Offset(offset). + All(ctx) + if err != nil { + return nil, err + } + + items := make([]store.User, 0, len(users)) + for _, u := range users { + items = append(items, *entUserToStore(u)) + } + + result := &store.ListResult[store.User]{ + Items: items, + TotalCount: totalCount, + } + if len(items) > limit { + result.Items = items[:limit] + result.NextCursor = strconv.Itoa(offset + limit) + } + return result, nil +} diff --git a/pkg/store/enttest/enttest.go b/pkg/store/enttest/enttest.go new file mode 100644 index 000000000..867550ef5 --- /dev/null +++ b/pkg/store/enttest/enttest.go @@ -0,0 +1,73 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package enttest provides a single backend-selecting factory for Ent clients +// used by the store test suites. By default it returns an in-memory SQLite +// client; built with the `integration` tag and with SCION_TEST_POSTGRES_URL +// set, it returns a Postgres-backed client isolated in its own schema inside a +// per-package ephemeral database. +// +// This lets the same store tests (pkg/store/storetest, pkg/store/entadapter) +// run unchanged against both backends, proving CRUD parity between SQLite and +// Postgres. +// +// Lifecycle: +// - Each test package wires MainSetup/MainTeardown into its TestMain so the +// per-package ephemeral Postgres database is created once and dropped once. +// Both are no-ops in the default (SQLite) build. +// - NewClient(t) returns a fresh, migrated *ent.Client per call with cleanup +// registered via t.Cleanup. Under Postgres each call gets its own schema so +// tests never observe each other's rows. +package enttest + +import ( + "context" + "testing" + + "github.com/GoogleCloudPlatform/scion/pkg/ent" + "github.com/GoogleCloudPlatform/scion/pkg/ent/entc" +) + +// NewClient returns a fresh, migrated Ent client for the active backend with +// cleanup registered on t. See the package doc for backend selection. +func NewClient(t *testing.T) *ent.Client { + t.Helper() + return newClient(t) +} + +// MainSetup prepares package-level backend resources. Call from TestMain before +// m.Run(). No-op for the SQLite backend. +func MainSetup() { setup() } + +// MainTeardown releases package-level backend resources. Call from TestMain +// after m.Run(). No-op for the SQLite backend. +func MainTeardown() { teardown() } + +// newSQLiteClient opens an in-memory SQLite-backed Ent client, migrates it, and +// registers cleanup. It is the default backend and the fallback used by the +// integration build when SCION_TEST_POSTGRES_URL is unset. MaxOpenConns is +// pinned to 1 so the shared-cache in-memory database serializes writes, matching +// production SQLite behavior. +func newSQLiteClient(t *testing.T) *ent.Client { + t.Helper() + client, err := entc.OpenSQLite("file:"+t.Name()+"?mode=memory&cache=shared", entc.PoolConfig{MaxOpenConns: 1}) + if err != nil { + t.Fatalf("open sqlite ent client: %v", err) + } + t.Cleanup(func() { _ = client.Close() }) + if err := entc.AutoMigrate(context.Background(), client); err != nil { + t.Fatalf("migrate sqlite ent client: %v", err) + } + return client +} diff --git a/pkg/store/enttest/enttest_postgres.go b/pkg/store/enttest/enttest_postgres.go new file mode 100644 index 000000000..68a1fe5a5 --- /dev/null +++ b/pkg/store/enttest/enttest_postgres.go @@ -0,0 +1,316 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build integration + +// This file implements the Postgres test backend. It is compiled only with the +// `integration` build tag and is active only when SCION_TEST_POSTGRES_URL is +// set; otherwise NewClient transparently falls back to SQLite so the suite still +// runs under `go test -tags integration ./...`. +// +// go test -tags integration -run TestCompositeStore_CRUDParity \ +// ./pkg/store/... \ +// with SCION_TEST_POSTGRES_URL=postgres://user:pass@host:5432/db?sslmode=require +// +// Isolation model: +// - One ephemeral database is created per test package (MainSetup) and dropped +// when the package finishes (MainTeardown) so concurrent runs never collide. +// - Each NewClient call creates a uniquely-named schema inside that database +// and points the Ent client's search_path at it, so every test gets a fresh, +// empty set of tables and cannot observe rows created by other tests. The +// schema is dropped (CASCADE) on test cleanup. +package enttest + +import ( + "context" + "database/sql" + "log" + "net/url" + "os" + "sort" + "strings" + "testing" + + "github.com/google/uuid" + + "github.com/GoogleCloudPlatform/scion/pkg/ent" + "github.com/GoogleCloudPlatform/scion/pkg/ent/entc" + + // pgx stdlib driver registration ("pgx"). entc/driver_postgres.go also + // imports it, but we keep it explicit so this file is self-describing. + _ "github.com/jackc/pgx/v5/stdlib" +) + +// postgresURL is the operator-supplied connection string for the Postgres +// server. When empty, the backend is inactive and NewClient falls back to +// SQLite. +var postgresURL = os.Getenv("SCION_TEST_POSTGRES_URL") + +var ( + // active reports whether a per-package ephemeral database was provisioned. + active bool + // pkgDBName is the name of the per-package ephemeral database. + pkgDBName string + // adminDB is connected to the per-package database and is used to create + // and drop the per-test schemas. + adminDB *sql.DB +) + +// setup provisions the per-package ephemeral database. It is fatal on error so +// that a misconfigured integration run fails loudly rather than silently +// degrading to SQLite. +func setup() { + if postgresURL == "" { + return + } + + server, err := sql.Open("pgx", postgresURL) + if err != nil { + log.Fatalf("enttest: opening postgres server connection: %v", err) + } + defer server.Close() + if err := server.Ping(); err != nil { + log.Fatalf("enttest: pinging postgres server: %v", err) + } + + pkgDBName = "scion_test_" + hexID() + if _, err := server.Exec("CREATE DATABASE " + pkgDBName); err != nil { + log.Fatalf("enttest: creating ephemeral database %s: %v", pkgDBName, err) + } + + dbURL, err := rewriteDatabase(postgresURL, pkgDBName) + if err != nil { + log.Fatalf("enttest: building ephemeral database URL: %v", err) + } + adminDB, err = sql.Open("pgx", dbURL) + if err != nil { + log.Fatalf("enttest: opening ephemeral database connection: %v", err) + } + if err := adminDB.Ping(); err != nil { + log.Fatalf("enttest: pinging ephemeral database: %v", err) + } + + active = true + log.Printf("enttest: provisioned ephemeral postgres database %s", pkgDBName) +} + +// teardown drops the per-package ephemeral database. +func teardown() { + if !active { + return + } + if adminDB != nil { + _ = adminDB.Close() + adminDB = nil + } + server, err := sql.Open("pgx", postgresURL) + if err != nil { + log.Printf("enttest: warning: reopening server to drop %s: %v", pkgDBName, err) + return + } + defer server.Close() + // FORCE terminates any lingering connections so the drop cannot hang. + if _, err := server.Exec("DROP DATABASE IF EXISTS " + pkgDBName + " WITH (FORCE)"); err != nil { + log.Printf("enttest: warning: dropping ephemeral database %s: %v", pkgDBName, err) + } + active = false +} + +// newClient returns a Postgres-backed client isolated in its own schema, or a +// SQLite client when the Postgres backend is inactive. +func newClient(t *testing.T) *ent.Client { + t.Helper() + if !active { + return newSQLiteClient(t) + } + + schema := "t_" + hexID() + if _, err := adminDB.ExecContext(context.Background(), "CREATE SCHEMA "+schema); err != nil { + t.Fatalf("enttest: creating schema %s: %v", schema, err) + } + + clientURL, err := withSearchPath(postgresURL, pkgDBName, schema) + if err != nil { + t.Fatalf("enttest: building client URL: %v", err) + } + client, err := entc.OpenPostgres(clientURL, entc.PoolConfig{MaxOpenConns: 5, MaxIdleConns: 2}) + if err != nil { + t.Fatalf("enttest: opening postgres ent client: %v", err) + } + t.Cleanup(func() { + _ = client.Close() + if _, err := adminDB.ExecContext(context.Background(), "DROP SCHEMA IF EXISTS "+schema+" CASCADE"); err != nil { + t.Logf("enttest: warning: dropping schema %s: %v", schema, err) + } + }) + + if err := entc.AutoMigrate(context.Background(), client); err != nil { + t.Fatalf("enttest: migrating postgres ent client: %v", err) + } + return client +} + +// Active reports whether a per-package ephemeral Postgres database was +// provisioned (i.e. SCION_TEST_POSTGRES_URL was set and MainSetup succeeded). +// Integration tests that exercise Postgres-only behavior use it to skip cleanly +// when run without a live database. +func Active() bool { return active } + +// NewSchemaURL creates and migrates a fresh, isolated schema inside the +// per-package ephemeral database and returns a connection URL whose search_path +// points at it. Cleanup drops the schema (CASCADE) on test completion. +// +// Unlike NewClient (which hands back a ready *ent.Client with a fixed pool), this +// returns the raw DSN so callers can open their own clients/pools — needed by the +// connection-pool stress tests (custom MaxOpenConns) and the multi-process tests +// (a stable DSN shared with a forked child process). The schema is migrated once +// here so every client opened against the returned URL sees the full table set. +// +// It skips the calling test when the Postgres backend is inactive. +func NewSchemaURL(t *testing.T) string { + t.Helper() + if !active { + t.Skip("enttest: SCION_TEST_POSTGRES_URL not set; skipping Postgres-only integration test") + } + + schema := "t_" + hexID() + if _, err := adminDB.ExecContext(context.Background(), "CREATE SCHEMA "+schema); err != nil { + t.Fatalf("enttest: creating schema %s: %v", schema, err) + } + t.Cleanup(func() { + if _, err := adminDB.ExecContext(context.Background(), "DROP SCHEMA IF EXISTS "+schema+" CASCADE"); err != nil { + t.Logf("enttest: warning: dropping schema %s: %v", schema, err) + } + }) + + clientURL, err := withSearchPath(postgresURL, pkgDBName, schema) + if err != nil { + t.Fatalf("enttest: building schema URL: %v", err) + } + + // Migrate once so the schema is fully provisioned; callers open their own + // clients/pools against clientURL afterwards. + client, err := entc.OpenPostgres(clientURL, entc.PoolConfig{MaxOpenConns: 2, MaxIdleConns: 1}) + if err != nil { + t.Fatalf("enttest: opening migrate client for schema %s: %v", schema, err) + } + if err := entc.AutoMigrate(context.Background(), client); err != nil { + _ = client.Close() + t.Fatalf("enttest: migrating schema %s: %v", schema, err) + } + _ = client.Close() + return clientURL +} + +// hexID returns a 32-char lowercase hex identifier safe to embed in a Postgres +// database or schema name. +func hexID() string { + return strings.ReplaceAll(uuid.NewString(), "-", "") +} + +// rewriteDatabase returns rawURL with its database name replaced by dbName. +// It accepts both URL-style ("postgres://...") and libpq keyword/value +// ("host=... dbname=...") DSNs, mirroring what entc.OpenPostgres accepts. +func rewriteDatabase(rawURL, dbName string) (string, error) { + if isKeywordValueDSN(rawURL) { + m := parseKeywordValueDSN(rawURL) + m["dbname"] = dbName + return buildKeywordValueDSN(m), nil + } + u, err := url.Parse(rawURL) + if err != nil { + return "", err + } + u.Path = "/" + dbName + return u.String(), nil +} + +// withSearchPath returns rawURL pointing at dbName with the connection +// search_path set to schema, so unqualified table creation/queries resolve to +// that schema. It accepts both URL-style and libpq keyword/value DSNs. In both +// forms search_path is carried as a connection runtime parameter (pgx sends any +// unrecognized keyword/query param as a startup GUC). +func withSearchPath(rawURL, dbName, schema string) (string, error) { + if isKeywordValueDSN(rawURL) { + m := parseKeywordValueDSN(rawURL) + m["dbname"] = dbName + m["search_path"] = schema + return buildKeywordValueDSN(m), nil + } + u, err := url.Parse(rawURL) + if err != nil { + return "", err + } + u.Path = "/" + dbName + q := u.Query() + q.Set("search_path", schema) + u.RawQuery = q.Encode() + return u.String(), nil +} + +// WithConnParam returns dsn with the connection parameter key set to value, +// accepting both URL-style and libpq keyword/value DSNs. It is used by tests that +// need to attach an extra parameter (e.g. application_name) to the DSN returned by +// NewSchemaURL without assuming a particular DSN format. +func WithConnParam(dsn, key, value string) (string, error) { + if isKeywordValueDSN(dsn) { + m := parseKeywordValueDSN(dsn) + m[key] = value + return buildKeywordValueDSN(m), nil + } + u, err := url.Parse(dsn) + if err != nil { + return "", err + } + q := u.Query() + q.Set(key, value) + u.RawQuery = q.Encode() + return u.String(), nil +} + +// isKeywordValueDSN reports whether dsn is a libpq keyword/value connection +// string rather than a URL. URL DSNs contain a scheme separator ("://"); the +// keyword/value form ("host=... dbname=...") does not. +func isKeywordValueDSN(dsn string) bool { + return !strings.Contains(dsn, "://") +} + +// parseKeywordValueDSN parses a libpq keyword/value DSN into a map. It handles +// the unquoted form used by these tests (no spaces inside values); quoting of +// values is not required for the simple host/port/user/password/dbname tokens in +// the test connection string. +func parseKeywordValueDSN(dsn string) map[string]string { + m := make(map[string]string) + for _, field := range strings.Fields(dsn) { + if i := strings.IndexByte(field, '='); i >= 0 { + m[field[:i]] = field[i+1:] + } + } + return m +} + +// buildKeywordValueDSN serializes a keyword/value map back into a libpq DSN. +// Keys are emitted in a stable order so the result is deterministic. +func buildKeywordValueDSN(m map[string]string) string { + keys := make([]string, 0, len(m)) + for k := range m { + keys = append(keys, k) + } + sort.Strings(keys) + parts := make([]string, 0, len(keys)) + for _, k := range keys { + parts = append(parts, k+"="+m[k]) + } + return strings.Join(parts, " ") +} diff --git a/pkg/store/enttest/enttest_sqlite.go b/pkg/store/enttest/enttest_sqlite.go new file mode 100644 index 000000000..0346e3871 --- /dev/null +++ b/pkg/store/enttest/enttest_sqlite.go @@ -0,0 +1,45 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build !integration + +package enttest + +import ( + "testing" + + "github.com/GoogleCloudPlatform/scion/pkg/ent" +) + +// newClient returns an in-memory SQLite-backed client. This is the default +// build; the Postgres backend lives in enttest_postgres.go behind the +// `integration` tag. +func newClient(t *testing.T) *ent.Client { return newSQLiteClient(t) } + +// setup/teardown have nothing to do for the SQLite backend. +func setup() {} +func teardown() {} + +// Active always reports false in the SQLite build: there is no Postgres backend. +func Active() bool { return false } + +// NewSchemaURL has no meaning without the Postgres backend; it skips the calling +// test. The integration build provides the real implementation. The signature +// is kept identical so Postgres-only integration tests reference one symbol +// regardless of build tag. +func NewSchemaURL(t *testing.T) string { + t.Helper() + t.Skip("enttest: Postgres backend not built; rebuild with -tags integration and set SCION_TEST_POSTGRES_URL") + return "" +} diff --git a/pkg/store/integrationtest/README.md b/pkg/store/integrationtest/README.md new file mode 100644 index 000000000..6059a56df --- /dev/null +++ b/pkg/store/integrationtest/README.md @@ -0,0 +1,70 @@ +# Postgres stress / integration test suite + +This package exercises the Postgres-backed store under realistic, adversarial +conditions that **do not exist on the single-writer SQLite backend**: row-level +contention, transaction isolation, connection-pool saturation, LISTEN/NOTIFY +delivery, large-dataset migration, strict type/schema edge cases, and +multi-process coordination. + +It complements — rather than duplicates — the CRUD-parity suites in +`pkg/store/storetest` and `pkg/store/entadapter`, which run the *same* tests +against both backends to prove behavioral parity. Everything here is Postgres-only +and asserts behavior that only a real, concurrent, multi-writer database exhibits. + +## Running + +All tests are gated by the `integration` build tag **and** require a live +Postgres reachable via `SCION_TEST_POSTGRES_URL`. Without that variable every +test skips (and the default `go test ./...` build sees the package as empty). + +```sh +# Local Postgres +SCION_TEST_POSTGRES_URL='postgres://scion:scion@localhost:5432/scion?sslmode=disable' \ + go test -tags integration ./pkg/store/integrationtest/... + +# CloudSQL (e.g. via the auth proxy on localhost) +SCION_TEST_POSTGRES_URL='postgres://USER:PASS@127.0.0.1:5432/DB?sslmode=disable' \ + go test -tags integration -timeout 20m ./pkg/store/integrationtest/... +``` + +The suite provisions one ephemeral database per package run (created and dropped +automatically) and an isolated schema per test, so it never touches existing data +and parallel runs never collide. + +### Knobs + +| Variable | Default | Meaning | +| -------------------------- | ------- | -------------------------------------------------- | +| `SCION_TEST_POSTGRES_URL` | (unset) | Live Postgres DSN; unset ⇒ all tests skip. | +| `SCION_TEST_CONCURRENCY` | `10` | Worker count for contention/pool tests (≥ 2). | + +Target wall-clock: **< 5 min** against a local Postgres, **< 15 min** against +CloudSQL, at the default concurrency. + +## What's covered + +1. **Contention** (`contention_test.go`) — `state_version` CAS race (no lost + updates; ≥ N-1 retries; final version == 1+N), SKIP-LOCKED / conditional-UPDATE + scheduled-event claim (single winner; disjoint drain of a pool), and unique + constraint races on project slug / user email / agent (slug, project_id). +2. **Transaction isolation** (`isolation_test.go`) — SERIALIZABLE conflict with + `RunSerializable` retry recovery, REPEATABLE READ snapshot stability (no + phantom), READ COMMITTED dirty-read prevention. +3. **Connection pool** (`pool_test.go`) — exhaustion + queued recovery, saturated + pool honoring the context deadline, long transaction not starving short + queries, and pool healing after backends are killed with + `pg_terminate_backend`. +4. **LISTEN/NOTIFY** (`notify_test.go`) — ordered burst delivery with no drops, + the 8000-byte payload limit (which motivates the publisher's + reference-and-refetch offload), listener reconnect/resume, and cross-channel + isolation. (The higher-level `PostgresEventPublisher` refetch/resubscribe + behavior is covered in `pkg/hub/events_postgres_test.go`.) +5. **Migration** (`migration_test.go`) — 1000+ row dataset with correct counts + and bounded-memory listing, and idempotent re-migration that preserves data + (the property that makes a killed/restarted migration safe). +6. **Schema / type edge cases** (`schema_test.go`) — NULL semantics, Unicode/emoji + round-trip, nested JSON with special characters, large-text non-truncation, and + TIMESTAMPTZ microsecond precision. +7. **Multi-process** (`multiprocess_test.go`) — forks the test binary so two + separate OS processes contend for an advisory lock (exactly one wins) and a + child-published NOTIFY is delivered to a listener in the parent. diff --git a/pkg/store/integrationtest/contention_test.go b/pkg/store/integrationtest/contention_test.go new file mode 100644 index 000000000..588bd229d --- /dev/null +++ b/pkg/store/integrationtest/contention_test.go @@ -0,0 +1,301 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build integration + +// Category 1 — Contention. These tests put N writers in genuine, simultaneous +// conflict over a single row (or unique key) on a real multi-writer Postgres and +// assert the store's concurrency-control primitives hold: optimistic state_version +// compare-and-swap, the SKIP LOCKED / conditional-UPDATE event claim, and +// database unique constraints. None of this is observable on the single-writer +// SQLite fallback, so every test gates on requirePG via newStore. +package integrationtest + +import ( + "context" + "errors" + "strconv" + "sync" + "sync/atomic" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/GoogleCloudPlatform/scion/pkg/store" +) + +// TestContention_StateVersionCAS races N goroutines to increment a counter on a +// single agent through the optimistic state_version compare-and-swap in +// UpdateAgent. +// +// The workers proceed in two phases. A synchronization barrier forces all N to +// read the SAME starting version before any of them writes, so the first write +// round is guaranteed to produce exactly one winner and N-1 conflicts. Each loser +// then re-reads and retries until it too commits. This makes the lower bound on +// retries deterministic (>= N-1) rather than dependent on goroutine scheduling. +// +// Asserted invariants: +// - exactly N successful UpdateAgent calls (every worker commits once); +// - >= N-1 conflicts observed (real contention occurred); +// - final state_version == initial(1) + N (each commit bumped it exactly once); +// - final counter == N — NO LOST UPDATES (every increment landed on the row). +func TestContention_StateVersionCAS(t *testing.T) { + cs := newStore(t) + ctx := context.Background() + n := concurrency(t) + + project := seedProject(t, cs) + ag := makeAgent(project.ID, "cas-"+shortID()) + require.NoError(t, cs.CreateAgent(ctx, ag)) + require.Equal(t, int64(1), ag.StateVersion, "CreateAgent seeds state_version=1") + + var successes, retries int64 + errs := make(chan error, n) + + // readBarrier releases every worker's first write only after all N have done + // their initial stale read, guaranteeing the first round conflicts N-1 times. + var readBarrier sync.WaitGroup + readBarrier.Add(n) + + bump := func(a *store.Agent) { + cur := 0 + if a.Annotations != nil { + if v, ok := a.Annotations["counter"]; ok { + cur, _ = strconv.Atoi(v) + } + } + a.Annotations = map[string]string{"counter": strconv.Itoa(cur + 1)} + } + + runConcurrently(n, func(int) { + // Phase 1: stale read shared across all workers. + a, err := cs.GetAgent(ctx, ag.ID) + if err != nil { + readBarrier.Done() + errs <- err + return + } + readBarrier.Done() + readBarrier.Wait() // every worker now holds version 1 + + // Phase 2: first (lockstep) write attempt, then retry-until-success. + for { + bump(a) + err = cs.UpdateAgent(ctx, a) + if err == nil { + atomic.AddInt64(&successes, 1) + return + } + if errors.Is(err, store.ErrVersionConflict) { + atomic.AddInt64(&retries, 1) + a, err = cs.GetAgent(ctx, ag.ID) // re-read latest version + if err != nil { + errs <- err + return + } + continue + } + errs <- err + return + } + }) + close(errs) + for err := range errs { + require.NoError(t, err, "unexpected error during CAS contention") + } + + assert.Equal(t, int64(n), successes, "every worker must commit exactly once") + assert.GreaterOrEqualf(t, retries, int64(n-1), + "expected >= N-1 (%d) conflicts under true contention, got %d", n-1, retries) + + final, err := cs.GetAgent(ctx, ag.ID) + require.NoError(t, err) + assert.Equal(t, int64(1+n), final.StateVersion, + "final state_version must equal initial(1) + N commits") + require.NotNil(t, final.Annotations) + assert.Equal(t, strconv.Itoa(n), final.Annotations["counter"], + "counter must equal N — a smaller value means a lost update") + t.Logf("CAS contention: N=%d successes=%d retries=%d finalVersion=%d", + n, successes, retries, final.StateVersion) +} + +// TestContention_ClaimScheduledEventSingleWinner races N goroutines to claim the +// SAME pending scheduled event (ClaimScheduledEvent's conditional +// UPDATE ... WHERE status='pending'). This mirrors N hub replicas each recovering +// the same pending event on startup: exactly one must win the pending->fired +// transition and execute the side effect; the rest must lose cleanly. +func TestContention_ClaimScheduledEventSingleWinner(t *testing.T) { + cs := newStore(t) + ctx := context.Background() + n := concurrency(t) + + project := seedProject(t, cs) + evt := makeScheduledEvent(project.ID) + require.NoError(t, cs.CreateScheduledEvent(ctx, evt)) + + var wins int64 + errs := make(chan error, n) + runConcurrently(n, func(int) { + won, err := cs.ClaimScheduledEvent(ctx, evt.ID, store.ScheduledEventFired) + if err != nil { + errs <- err + return + } + if won { + atomic.AddInt64(&wins, 1) + } + }) + close(errs) + for err := range errs { + require.NoError(t, err, "unexpected error during claim race") + } + + assert.Equal(t, int64(1), wins, "exactly one concurrent claim must win") + + got, err := cs.GetScheduledEvent(ctx, evt.ID) + require.NoError(t, err) + assert.Equal(t, store.ScheduledEventFired, got.Status) + assert.NotNil(t, got.FiredAt) +} + +// TestContention_SkipLockedDisjointClaims drains a pool of M pending events with +// N concurrent pollers, each looping ListPendingScheduledEvents (SELECT ... FOR +// UPDATE SKIP LOCKED on Postgres) followed by ClaimScheduledEvent. The +// SKIP-LOCKED select hands disjoint row sets to overlapping pollers and the +// conditional claim is the final dedup, so the invariant is: every event is +// claimed EXACTLY ONCE in total — no event dropped, none double-fired. +func TestContention_SkipLockedDisjointClaims(t *testing.T) { + cs := newStore(t) + ctx := context.Background() + n := concurrency(t) + m := 5 * n // comfortably more events than pollers + + project := seedProject(t, cs) + for i := 0; i < m; i++ { + require.NoError(t, cs.CreateScheduledEvent(ctx, makeScheduledEvent(project.ID))) + } + + var mu sync.Mutex + claimedBy := make(map[string]int) // event id -> number of winning claims + errs := make(chan error, n) + + runConcurrently(n, func(int) { + for { + pending, err := cs.ListPendingScheduledEvents(ctx) + if err != nil { + errs <- err + return + } + if len(pending) == 0 { + return // nothing left for anyone + } + for _, e := range pending { + won, err := cs.ClaimScheduledEvent(ctx, e.ID, store.ScheduledEventFired) + if err != nil { + errs <- err + return + } + if won { + mu.Lock() + claimedBy[e.ID]++ + mu.Unlock() + } + } + } + }) + close(errs) + for err := range errs { + require.NoError(t, err, "unexpected error during SKIP LOCKED drain") + } + + assert.Len(t, claimedBy, m, "every event must be claimed exactly once (count of distinct winners)") + total := 0 + for id, c := range claimedBy { + assert.Equalf(t, 1, c, "event %s was claimed %d times (want exactly 1)", id, c) + total += c + } + assert.Equal(t, m, total, "total winning claims must equal the number of events") +} + +// TestContention_UniqueProjectSlug races N goroutines to create projects with the +// same slug. The slug unique index must admit exactly one and reject the rest +// with store.ErrAlreadyExists. +func TestContention_UniqueProjectSlug(t *testing.T) { + cs := newStore(t) + ctx := context.Background() + n := concurrency(t) + slug := "dup-project-" + shortID() + + ok, dup := raceUniqueCreate(t, n, func() error { + return cs.CreateProject(ctx, makeProject(slug)) + }) + assert.Equal(t, int64(1), ok, "exactly one project create must succeed") + assert.Equal(t, int64(n-1), dup, "all other creates must return ErrAlreadyExists") +} + +// TestContention_UniqueUserEmail races N goroutines to create users with the same +// (case-insensitively normalized) email. +func TestContention_UniqueUserEmail(t *testing.T) { + cs := newStore(t) + ctx := context.Background() + n := concurrency(t) + email := "dup-" + shortID() + "@example.com" + + ok, dup := raceUniqueCreate(t, n, func() error { + return cs.CreateUser(ctx, makeUser(email)) + }) + assert.Equal(t, int64(1), ok, "exactly one user create must succeed") + assert.Equal(t, int64(n-1), dup, "all other creates must return ErrAlreadyExists") +} + +// TestContention_UniqueAgentSlug races N goroutines to create agents with the same +// (slug, project_id) composite unique key inside one project. +func TestContention_UniqueAgentSlug(t *testing.T) { + cs := newStore(t) + ctx := context.Background() + n := concurrency(t) + project := seedProject(t, cs) + slug := "dup-agent-" + shortID() + + ok, dup := raceUniqueCreate(t, n, func() error { + return cs.CreateAgent(ctx, makeAgent(project.ID, slug)) + }) + assert.Equal(t, int64(1), ok, "exactly one agent create must succeed") + assert.Equal(t, int64(n-1), dup, "all other creates must return ErrAlreadyExists") +} + +// raceUniqueCreate runs create n times concurrently and returns (successes, +// already-exists). Any other error fails the test. Each create must target the +// same unique key but a distinct primary-key id (the factories use a fresh UUID +// per call), so the only possible conflict is the intended unique-key violation. +func raceUniqueCreate(t *testing.T, n int, create func() error) (successes, alreadyExists int64) { + t.Helper() + errs := make(chan error, n) + runConcurrently(n, func(int) { + switch err := create(); { + case err == nil: + atomic.AddInt64(&successes, 1) + case errors.Is(err, store.ErrAlreadyExists): + atomic.AddInt64(&alreadyExists, 1) + default: + errs <- err + } + }) + close(errs) + for err := range errs { + require.NoError(t, err, "unexpected (non-duplicate) error during unique-create race") + } + return successes, alreadyExists +} diff --git a/pkg/store/integrationtest/doc.go b/pkg/store/integrationtest/doc.go new file mode 100644 index 000000000..98dd4e570 --- /dev/null +++ b/pkg/store/integrationtest/doc.go @@ -0,0 +1,34 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package integrationtest holds the Postgres stress / integration suite. Unlike +// the CRUD-parity suites in pkg/store/storetest and pkg/store/entadapter (which +// run unchanged against both SQLite and Postgres to prove behavioral parity), +// every test here targets behavior that ONLY manifests under a real, multi-writer +// Postgres server: row-level contention, transaction isolation, connection-pool +// saturation, LISTEN/NOTIFY delivery, large-dataset migration, schema/type edge +// cases, and multi-process coordination. +// +// All test files are gated with the `integration` build tag and additionally skip +// at runtime unless SCION_TEST_POSTGRES_URL points at a live Postgres (local or +// CloudSQL); see requirePG. Under the default build only this file compiles, so +// `go test ./...` reports the package as having no tests rather than failing. +// +// go test -tags integration ./pkg/store/integrationtest/... \ +// with SCION_TEST_POSTGRES_URL=postgres://user:pass@host:5432/db?sslmode=disable +// +// Concurrency levels default to small values so the suite finishes well under the +// 5-minute local-Postgres target; set SCION_TEST_CONCURRENCY= to crank them up +// for a heavier stress run. +package integrationtest diff --git a/pkg/store/integrationtest/harness_test.go b/pkg/store/integrationtest/harness_test.go new file mode 100644 index 000000000..1123416d4 --- /dev/null +++ b/pkg/store/integrationtest/harness_test.go @@ -0,0 +1,163 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build integration + +package integrationtest + +import ( + "context" + "os" + "strconv" + "sync" + "testing" + "time" + + "github.com/google/uuid" + "github.com/stretchr/testify/require" + + "github.com/GoogleCloudPlatform/scion/pkg/ent/entc" + "github.com/GoogleCloudPlatform/scion/pkg/store" + "github.com/GoogleCloudPlatform/scion/pkg/store/entadapter" + "github.com/GoogleCloudPlatform/scion/pkg/store/enttest" +) + +// defaultConcurrency is the worker count used by the contention/pool tests when +// SCION_TEST_CONCURRENCY is unset. It is deliberately small so the suite stays +// well under the 5-minute local-Postgres target; bump the env var for stress. +const defaultConcurrency = 10 + +// requirePG skips a test unless a live Postgres backend was provisioned. Every +// test in this package is Postgres-only: the behaviors under test (real row +// locks, MVCC isolation, pool saturation, LISTEN/NOTIFY) do not exist on the +// single-writer in-memory SQLite fallback. +func requirePG(t *testing.T) { + t.Helper() + if !enttest.Active() { + t.Skip("integration: set SCION_TEST_POSTGRES_URL to a live Postgres to run the stress suite") + } +} + +// concurrency returns the worker count for contention tests: the value of +// SCION_TEST_CONCURRENCY if set (>= 2), otherwise defaultConcurrency. +func concurrency(t *testing.T) int { + t.Helper() + v := os.Getenv("SCION_TEST_CONCURRENCY") + if v == "" { + return defaultConcurrency + } + n, err := strconv.Atoi(v) + if err != nil || n < 2 { + t.Fatalf("integration: invalid SCION_TEST_CONCURRENCY=%q (want integer >= 2): %v", v, err) + } + return n +} + +// newStore returns a CompositeStore backed by a fresh, isolated Postgres schema +// with a pool large enough that the suite's concurrent writers genuinely overlap +// (rather than serializing behind a tiny pool). Each call gets its own schema, so +// tests never observe each other's rows; the schema and client are torn down on +// cleanup. +func newStore(t *testing.T) *entadapter.CompositeStore { + return newStoreWithPool(t, 16) +} + +// newStoreWithPool is newStore with an explicit MaxOpenConns, used by the +// connection-pool stress tests that need a known, small pool to saturate. +func newStoreWithPool(t *testing.T, maxOpen int) *entadapter.CompositeStore { + t.Helper() + dsn := enttest.NewSchemaURL(t) // skips when Postgres is inactive + client, err := entc.OpenPostgres(dsn, entc.PoolConfig{MaxOpenConns: maxOpen, MaxIdleConns: maxOpen}) + require.NoError(t, err, "open postgres ent client") + cs := entadapter.NewCompositeStore(client) + t.Cleanup(func() { _ = cs.Close() }) + return cs +} + +// --- entity factories (minimal valid rows for the schema under test) --- + +func makeProject(slug string) *store.Project { + return &store.Project{ + ID: uuid.NewString(), + Name: "project " + slug, + Slug: slug, + Created: time.Now(), + Updated: time.Now(), + } +} + +func makeAgent(projectID, slug string) *store.Agent { + return &store.Agent{ + ID: uuid.NewString(), + Slug: slug, + Name: "agent " + slug, + Template: "default", + ProjectID: projectID, + Phase: "running", + } +} + +func makeUser(email string) *store.User { + return &store.User{ + ID: uuid.NewString(), + Email: email, + DisplayName: "user " + email, + Role: store.UserRoleMember, + Status: "active", + Created: time.Now(), + } +} + +func makeScheduledEvent(projectID string) *store.ScheduledEvent { + return &store.ScheduledEvent{ + ID: uuid.NewString(), + ProjectID: projectID, + EventType: "message", + FireAt: time.Now().Add(time.Hour).UTC().Truncate(time.Second), + Payload: `{"text":"hi"}`, + CreatedBy: "tester", + } +} + +// seedProject creates and returns a fresh project, satisfying the agent / +// scheduled-event foreign keys those entities require. +func seedProject(t *testing.T, cs *entadapter.CompositeStore) *store.Project { + t.Helper() + p := makeProject("seed-" + shortID()) + require.NoError(t, cs.CreateProject(context.Background(), p), "seed project") + return p +} + +// shortID returns a short, collision-resistant suffix for unique slugs/emails. +func shortID() string { return uuid.NewString()[:8] } + +// runConcurrently starts n goroutines that all block on a shared barrier and are +// released simultaneously, maximizing real overlap (and thus real contention) +// rather than letting earlier goroutines finish before later ones start. fn +// receives the worker index 0..n-1. It returns once every worker has finished. +func runConcurrently(n int, fn func(i int)) { + var release sync.WaitGroup + var done sync.WaitGroup + release.Add(1) + done.Add(n) + for i := 0; i < n; i++ { + go func(i int) { + defer done.Done() + release.Wait() + fn(i) + }(i) + } + release.Done() // fire the starting gun + done.Wait() +} diff --git a/pkg/store/integrationtest/isolation_test.go b/pkg/store/integrationtest/isolation_test.go new file mode 100644 index 000000000..4863eb133 --- /dev/null +++ b/pkg/store/integrationtest/isolation_test.go @@ -0,0 +1,171 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build integration + +// Category 2 — Transaction isolation. These exercise Postgres MVCC behavior that +// the store relies on but that cannot be reproduced on SQLite: SERIALIZABLE +// conflict detection with the RunSerializable retry wrapper, REPEATABLE READ +// snapshot stability (no phantom rows), and READ COMMITTED dirty-read prevention. +package integrationtest + +import ( + "context" + "database/sql" + "sync" + "sync/atomic" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// TestIsolation_SerializableRetryRecovers drives two concurrent SERIALIZABLE +// read-modify-write transactions into a genuine serialization conflict and +// verifies CompositeStore.RunSerializable transparently retries the aborted one +// so both increments ultimately land. +// +// Determinism: a two-party barrier makes both transactions perform their initial +// SELECT before either issues its UPDATE, so they read the same value. One commit +// then necessarily aborts with SQLSTATE 40001 (or 40P01); RunSerializable +// re-runs that closure against a fresh snapshot, which reads the now-committed +// value and commits cleanly. The barrier only applies on each transaction's first +// attempt, so a retry can never deadlock against it. +func TestIsolation_SerializableRetryRecovers(t *testing.T) { + cs := newStore(t) + ctx := context.Background() + db := cs.DB() + require.NotNil(t, db, "store must expose *sql.DB") + + _, err := db.ExecContext(ctx, `CREATE TABLE iso_counter (id int PRIMARY KEY, val int NOT NULL)`) + require.NoError(t, err) + _, err = db.ExecContext(ctx, `INSERT INTO iso_counter (id, val) VALUES (1, 0)`) + require.NoError(t, err) + + var barrier sync.WaitGroup + barrier.Add(2) + var totalAttempts int64 + errs := make(chan error, 2) + + worker := func() { + firstAttempt := true + errs <- cs.RunSerializable(ctx, func(ctx context.Context, tx *sql.Tx) error { + atomic.AddInt64(&totalAttempts, 1) + var val int + if err := tx.QueryRowContext(ctx, `SELECT val FROM iso_counter WHERE id=1`).Scan(&val); err != nil { + return err + } + if firstAttempt { + firstAttempt = false + barrier.Done() + barrier.Wait() // both transactions have now read the same val + } + _, err := tx.ExecContext(ctx, `UPDATE iso_counter SET val=$1 WHERE id=1`, val+1) + return err + }) + } + + var wg sync.WaitGroup + wg.Add(2) + go func() { defer wg.Done(); worker() }() + go func() { defer wg.Done(); worker() }() + wg.Wait() + close(errs) + for e := range errs { + require.NoError(t, e, "RunSerializable must recover the conflict, not surface it") + } + + var val int + require.NoError(t, db.QueryRowContext(ctx, `SELECT val FROM iso_counter WHERE id=1`).Scan(&val)) + assert.Equal(t, 2, val, "both serializable increments must land — no lost update") + assert.Greaterf(t, totalAttempts, int64(2), + "expected at least one retry after a serialization failure, saw only %d attempts", totalAttempts) + t.Logf("serializable retry: total fn attempts=%d (2 commits + retries)", totalAttempts) +} + +// TestIsolation_RepeatableReadNoPhantom verifies a REPEATABLE READ transaction +// sees a stable snapshot: a row inserted by another connection AFTER the +// snapshot's first read is invisible to the transaction (no phantom), yet visible +// to a fresh read once the transaction ends. +func TestIsolation_RepeatableReadNoPhantom(t *testing.T) { + cs := newStore(t) + ctx := context.Background() + db := cs.DB() + require.NotNil(t, db) + + _, err := db.ExecContext(ctx, `CREATE TABLE phantom_rows (id int PRIMARY KEY)`) + require.NoError(t, err) + for i := 0; i < 10; i++ { + _, err := db.ExecContext(ctx, `INSERT INTO phantom_rows (id) VALUES ($1)`, i) + require.NoError(t, err) + } + + tx, err := db.BeginTx(ctx, &sql.TxOptions{Isolation: sql.LevelRepeatableRead}) + require.NoError(t, err) + defer func() { _ = tx.Rollback() }() + + var before int + require.NoError(t, tx.QueryRowContext(ctx, `SELECT count(*) FROM phantom_rows`).Scan(&before)) + require.Equal(t, 10, before, "snapshot established at 10 rows") + + // Concurrent committed insert on a different (pooled) connection. + _, err = db.ExecContext(ctx, `INSERT INTO phantom_rows (id) VALUES (1000)`) + require.NoError(t, err) + + var after int + require.NoError(t, tx.QueryRowContext(ctx, `SELECT count(*) FROM phantom_rows`).Scan(&after)) + assert.Equal(t, before, after, "REPEATABLE READ snapshot must not observe the concurrently-inserted phantom row") + require.NoError(t, tx.Commit()) + + var fresh int + require.NoError(t, db.QueryRowContext(ctx, `SELECT count(*) FROM phantom_rows`).Scan(&fresh)) + assert.Equal(t, 11, fresh, "a fresh read after the snapshot ends must see the new row") +} + +// TestIsolation_DirtyReadPrevention verifies the default isolation level prevents +// dirty reads: a row written but not yet committed in one transaction is +// invisible to other connections, and stays invisible if that transaction rolls +// back (and becomes visible only on commit). +func TestIsolation_DirtyReadPrevention(t *testing.T) { + cs := newStore(t) + ctx := context.Background() + db := cs.DB() + require.NotNil(t, db) + + _, err := db.ExecContext(ctx, `CREATE TABLE dirty_rows (id int PRIMARY KEY)`) + require.NoError(t, err) + + count := func() int { + var c int + require.NoError(t, db.QueryRowContext(ctx, `SELECT count(*) FROM dirty_rows`).Scan(&c)) + return c + } + + // Uncommitted write is invisible to other connections. + txRollback, err := db.BeginTx(ctx, nil) + require.NoError(t, err) + _, err = txRollback.ExecContext(ctx, `INSERT INTO dirty_rows (id) VALUES (1)`) + require.NoError(t, err) + assert.Equal(t, 0, count(), "uncommitted insert must not be visible to another connection (no dirty read)") + require.NoError(t, txRollback.Rollback()) + assert.Equal(t, 0, count(), "rolled-back insert must remain invisible") + + // Committed write becomes visible. + txCommit, err := db.BeginTx(ctx, nil) + require.NoError(t, err) + _, err = txCommit.ExecContext(ctx, `INSERT INTO dirty_rows (id) VALUES (2)`) + require.NoError(t, err) + require.NoError(t, txCommit.Commit()) + assert.Equal(t, 1, count(), "committed insert must be visible") +} diff --git a/pkg/store/integrationtest/main_test.go b/pkg/store/integrationtest/main_test.go new file mode 100644 index 000000000..1c8506611 --- /dev/null +++ b/pkg/store/integrationtest/main_test.go @@ -0,0 +1,35 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build integration + +package integrationtest + +import ( + "os" + "testing" + + "github.com/GoogleCloudPlatform/scion/pkg/store/enttest" +) + +// TestMain provisions the per-package ephemeral Postgres database once (creating +// it under a unique name so parallel `go test` invocations never collide) and +// drops it when the package finishes. Both calls are no-ops when +// SCION_TEST_POSTGRES_URL is unset, in which case every test skips. +func TestMain(m *testing.M) { + enttest.MainSetup() + code := m.Run() + enttest.MainTeardown() + os.Exit(code) +} diff --git a/pkg/store/integrationtest/migration_test.go b/pkg/store/integrationtest/migration_test.go new file mode 100644 index 000000000..cd473fe35 --- /dev/null +++ b/pkg/store/integrationtest/migration_test.go @@ -0,0 +1,104 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build integration + +// Category 5 — Migration stress. These verify the Ent AutoMigrate path behaves on +// a non-trivial dataset: a large table migrates and reports correct row counts, +// large result sets are accessed with bounded memory (the list path caps the +// page rather than loading every row), and re-running the migration is idempotent +// and non-destructive — the property that lets an interrupted/killed migration be +// safely restarted. +package integrationtest + +import ( + "context" + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/GoogleCloudPlatform/scion/pkg/store" +) + +// migrationRowCount is the dataset size for the large-migration tests: comfortably +// over the "1000+ rows" target while keeping the suite within its time budget. +const migrationRowCount = 1000 + +// TestMigration_LargeDatasetRowCounts seeds 1000+ rows, confirms the exact row +// count survives, and confirms the list path returns a BOUNDED page over the +// large table (proving the store does not materialize all rows into memory to +// answer a list request). +func TestMigration_LargeDatasetRowCounts(t *testing.T) { + cs := newStore(t) + ctx := context.Background() + db := cs.DB() + require.NotNil(t, db) + + project := seedProject(t, cs) + for i := 0; i < migrationRowCount; i++ { + require.NoErrorf(t, cs.CreateAgent(ctx, makeAgent(project.ID, fmt.Sprintf("bulk-%04d", i))), + "seeding agent %d", i) + } + + var count int + require.NoError(t, db.QueryRowContext(ctx, `SELECT count(*) FROM agents`).Scan(&count)) + assert.Equal(t, migrationRowCount, count, "all seeded rows must be present") + + // Bounded access: a capped page returns at most the limit, while TotalCount + // reflects the full table. + const page = 100 + res, err := cs.ListAgents(ctx, store.AgentFilter{ProjectID: project.ID}, store.ListOptions{Limit: page}) + require.NoError(t, err) + assert.Len(t, res.Items, page, "list must cap the page size, not return the whole table") + assert.Equal(t, migrationRowCount, res.TotalCount, "TotalCount must reflect the full dataset") +} + +// TestMigration_IdempotentReRunPreservesData seeds data and then re-runs Migrate +// several times, interleaving more writes. AutoMigrate is idempotent and +// converges to the same schema without touching data, which is exactly what makes +// a killed-and-restarted migration safe: re-running after a partial run finishes +// the job rather than corrupting or dropping rows. +func TestMigration_IdempotentReRunPreservesData(t *testing.T) { + cs := newStore(t) + ctx := context.Background() + db := cs.DB() + require.NotNil(t, db) + + project := seedProject(t, cs) + const firstBatch = 200 + for i := 0; i < firstBatch; i++ { + require.NoError(t, cs.CreateScheduledEvent(ctx, makeScheduledEvent(project.ID))) + } + + // Re-run the migration repeatedly (simulating restart after an interrupted + // run); each pass must succeed and leave existing rows untouched. + for pass := 0; pass < 3; pass++ { + require.NoErrorf(t, cs.Migrate(ctx), "re-migration pass %d must succeed", pass) + + // Migration must not change the row count. The expected count grows by one + // per pass because each iteration appends a row at the end of the loop. + var count int + require.NoError(t, db.QueryRowContext(ctx, `SELECT count(*) FROM scheduled_events`).Scan(&count)) + require.Equalf(t, firstBatch+pass, count, "re-migration pass %d changed the row count", pass) + + // Writes continue to work against the re-migrated schema. + require.NoError(t, cs.CreateScheduledEvent(ctx, makeScheduledEvent(project.ID))) + } + + var final int + require.NoError(t, db.QueryRowContext(ctx, `SELECT count(*) FROM scheduled_events`).Scan(&final)) + assert.Equal(t, firstBatch+3, final, "all rows (seed + one per pass) must be retained") +} diff --git a/pkg/store/integrationtest/multiprocess_test.go b/pkg/store/integrationtest/multiprocess_test.go new file mode 100644 index 000000000..96bab208e --- /dev/null +++ b/pkg/store/integrationtest/multiprocess_test.go @@ -0,0 +1,222 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build integration + +// Category 7 — Multi-process. These prove the coordination primitives hold across +// SEPARATE OS PROCESSES, not just goroutines in one process — the real +// multi-replica hub topology. The parent test forks the test binary (os.Args[0]) +// to run a dedicated worker entrypoint against a shared database: +// +// - advisory-lock exclusivity: two independent processes contend for the same +// pg_advisory_lock; exactly one wins; +// - cross-process LISTEN/NOTIFY: a notification published by a child process is +// delivered to a listener in the parent process. +// +// The TestWorker_* functions are those entrypoints. They no-op (skip) unless their +// SCION_TEST_WORKER_DSN env var is set, so they are inert in a normal suite run +// and only do work when launched by a parent test via workerCommand. +package integrationtest + +import ( + "context" + "fmt" + "os" + "os/exec" + "strconv" + "strings" + "sync" + "testing" + "time" + + "github.com/jackc/pgx/v5" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/GoogleCloudPlatform/scion/pkg/ent/entc" + "github.com/GoogleCloudPlatform/scion/pkg/store" + "github.com/GoogleCloudPlatform/scion/pkg/store/entadapter" + "github.com/GoogleCloudPlatform/scion/pkg/store/enttest" +) + +// crossProcessLockKey is a fixed advisory-lock key for the multi-process lock +// test. It is namespaced under the 0x5C10 ("SCIO") prefix like the production +// keys but uses a distinct value so it never collides with them. Each package run +// gets a fresh ephemeral database and runs this test once, so a constant is safe. +const crossProcessLockKey = int64(0x5C10FADE) + +// workerCommand builds an exec.Cmd that re-invokes THIS test binary to run a +// single TestWorker_* entrypoint as a child process. SCION_TEST_POSTGRES_URL is +// stripped from the child's environment so its TestMain does not provision a +// second ephemeral database; the child talks only to the DSN passed in extraEnv. +func workerCommand(testName string, extraEnv map[string]string) *exec.Cmd { + cmd := exec.Command(os.Args[0], "-test.run=^"+testName+"$", "-test.v=true") + env := make([]string, 0, len(os.Environ())+len(extraEnv)) + for _, e := range os.Environ() { + if strings.HasPrefix(e, "SCION_TEST_POSTGRES_URL=") { + continue + } + env = append(env, e) + } + for k, v := range extraEnv { + env = append(env, k+"="+v) + } + cmd.Env = env + return cmd +} + +// TestMultiProcess_AdvisoryLockExclusivity forks two worker processes that each +// try to take the SAME advisory lock and hold it. Exactly one must acquire it; +// the other must observe it held and report BLOCKED. This is the cross-process +// guarantee behind "run this maintenance job on exactly one replica". +func TestMultiProcess_AdvisoryLockExclusivity(t *testing.T) { + requirePG(t) + dsn := enttest.NewSchemaURL(t) + + env := map[string]string{ + "SCION_TEST_WORKER_DSN": dsn, + "SCION_TEST_WORKER_LOCKKEY": strconv.FormatInt(crossProcessLockKey, 10), + } + + const procs = 2 + outputs := make([]string, procs) + var wg sync.WaitGroup + for i := 0; i < procs; i++ { + wg.Add(1) + go func(i int) { + defer wg.Done() + out, _ := workerCommand("TestWorker_AdvisoryLock", env).CombinedOutput() + outputs[i] = string(out) + }(i) + } + wg.Wait() + + acquired, blocked := 0, 0 + for _, o := range outputs { + if strings.Contains(o, "WORKER_RESULT: ACQUIRED") { + acquired++ + } + if strings.Contains(o, "WORKER_RESULT: BLOCKED") { + blocked++ + } + } + assert.Equalf(t, 1, acquired, "exactly one process must acquire the advisory lock\n--- proc0 ---\n%s\n--- proc1 ---\n%s", outputs[0], outputs[1]) + assert.Equalf(t, 1, blocked, "exactly one process must be blocked\n--- proc0 ---\n%s\n--- proc1 ---\n%s", outputs[0], outputs[1]) +} + +// TestWorker_AdvisoryLock is the child entrypoint for the advisory-lock test. It +// takes the lock (if free) and holds it long enough to guarantee the sibling +// process's attempt overlaps, then reports the outcome on stdout. +func TestWorker_AdvisoryLock(t *testing.T) { + dsn := os.Getenv("SCION_TEST_WORKER_DSN") + if dsn == "" { + t.Skip("worker entrypoint; launched only by a parent multi-process test") + } + key, err := strconv.ParseInt(os.Getenv("SCION_TEST_WORKER_LOCKKEY"), 10, 64) + require.NoError(t, err) + ctx := context.Background() + + client, err := entc.OpenPostgres(dsn, entc.PoolConfig{MaxOpenConns: 2, MaxIdleConns: 1}) + if err != nil { + fmt.Println("WORKER_RESULT: ERROR open:", err) + t.Fatal(err) + } + defer client.Close() + cs := entadapter.NewCompositeStore(client) + + acquired, release, err := cs.TryAdvisoryLock(ctx, store.AdvisoryLockKey(key)) + if err != nil { + fmt.Println("WORKER_RESULT: ERROR lock:", err) + t.Fatal(err) + } + defer func() { _ = release() }() + + if acquired { + fmt.Println("WORKER_RESULT: ACQUIRED") + // Hold the lock well past sibling startup jitter so its attempt is + // guaranteed to land while we hold it. + time.Sleep(3 * time.Second) + return + } + fmt.Println("WORKER_RESULT: BLOCKED") +} + +// TestMultiProcess_CrossProcessNotify starts LISTENing in the parent, then forks a +// child process that publishes N notifications on the same channel via a separate +// connection. The parent must receive all N — proving NOTIFY crosses the +// process/connection boundary (the basis for cross-replica event delivery). +func TestMultiProcess_CrossProcessNotify(t *testing.T) { + requirePG(t) + dsn := enttest.NewSchemaURL(t) + ctx := context.Background() + + // Establish the listener BEFORE forking the publisher so no notification is + // missed (NOTIFY only reaches sessions already LISTENing at send time). + listener := pgConnect(t, dsn) + channel := uniqueChannel("xproc") + _, err := listener.Exec(ctx, "LISTEN "+channel) + require.NoError(t, err) + + const n = 50 + cmd := workerCommand("TestWorker_NotifyPublisher", map[string]string{ + "SCION_TEST_WORKER_DSN": dsn, + "SCION_TEST_WORKER_CHANNEL": channel, + "SCION_TEST_WORKER_COUNT": strconv.Itoa(n), + }) + var out strings.Builder + cmd.Stdout = &out + cmd.Stderr = &out + require.NoError(t, cmd.Start()) + + got := 0 + for got < n { + wctx, cancel := context.WithTimeout(ctx, 15*time.Second) + note, werr := listener.WaitForNotification(wctx) + cancel() + require.NoErrorf(t, werr, "received %d/%d cross-process notifications before timeout\nworker output:\n%s", got, n, out.String()) + require.Equal(t, channel, note.Channel) + got++ + } + + require.NoErrorf(t, cmd.Wait(), "publisher process failed\nworker output:\n%s", out.String()) + assert.Contains(t, out.String(), "WORKER_RESULT: PUBLISHED") +} + +// TestWorker_NotifyPublisher is the child entrypoint for the cross-process NOTIFY +// test. It connects independently and publishes N notifications on the channel. +func TestWorker_NotifyPublisher(t *testing.T) { + dsn := os.Getenv("SCION_TEST_WORKER_DSN") + if dsn == "" { + t.Skip("worker entrypoint; launched only by a parent multi-process test") + } + channel := os.Getenv("SCION_TEST_WORKER_CHANNEL") + n, err := strconv.Atoi(os.Getenv("SCION_TEST_WORKER_COUNT")) + require.NoError(t, err) + ctx := context.Background() + + conn, err := pgx.Connect(ctx, dsn) + if err != nil { + fmt.Println("WORKER_RESULT: ERROR connect:", err) + t.Fatal(err) + } + defer conn.Close(ctx) + + for i := 0; i < n; i++ { + if _, err := conn.Exec(ctx, "SELECT pg_notify($1, $2)", channel, strconv.Itoa(i)); err != nil { + fmt.Println("WORKER_RESULT: ERROR notify:", err) + t.Fatal(err) + } + } + fmt.Println("WORKER_RESULT: PUBLISHED") +} diff --git a/pkg/store/integrationtest/notify_test.go b/pkg/store/integrationtest/notify_test.go new file mode 100644 index 000000000..3ad4bbf81 --- /dev/null +++ b/pkg/store/integrationtest/notify_test.go @@ -0,0 +1,219 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build integration + +// Category 4 — LISTEN/NOTIFY under load. These exercise the raw Postgres +// asynchronous-notification primitive that the hub's PostgresEventPublisher is +// built on: ordered burst delivery without drops, the hard 8000-byte payload +// limit that motivates the publisher's reference-and-refetch offload, listener +// reconnect after a backend is terminated, and strict per-channel isolation. +// +// The higher-level publisher behaviors (reference-and-refetch round-trip, +// automatic resubscribe, NATS-style pattern fan-out) are covered against a live +// database in pkg/hub/events_postgres_test.go. Here we pin the underlying +// database guarantees those features depend on. +package integrationtest + +import ( + "context" + "strconv" + "strings" + "testing" + "time" + + "github.com/jackc/pgx/v5" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/GoogleCloudPlatform/scion/pkg/store/enttest" +) + +// pgConnect opens a raw pgx connection to the per-package database and registers +// cleanup. LISTEN/NOTIFY channels are database-global (not schema-scoped), so the +// schema in the DSN is irrelevant here; tests use unique channel names to stay +// isolated from one another on the shared database. +func pgConnect(t *testing.T, dsn string) *pgx.Conn { + t.Helper() + conn, err := pgx.Connect(context.Background(), dsn) + require.NoError(t, err, "pgx connect") + t.Cleanup(func() { _ = conn.Close(context.Background()) }) + return conn +} + +// uniqueChannel returns a Postgres channel name safe to use unquoted (hex + '_'). +func uniqueChannel(prefix string) string { return "itest_" + prefix + "_" + shortID() } + +// TestNotify_BurstDeliveredInOrderNoDrops fires a rapid burst of N notifications +// on one channel and asserts the listener receives all N, in publish order. +// Postgres guarantees ordered, lossless delivery of committed notifications to a +// session that was LISTENing before they were sent. +func TestNotify_BurstDeliveredInOrderNoDrops(t *testing.T) { + requirePG(t) + dsn := enttest.NewSchemaURL(t) + ctx := context.Background() + + listener := pgConnect(t, dsn) + channel := uniqueChannel("burst") + _, err := listener.Exec(ctx, "LISTEN "+channel) + require.NoError(t, err) + + notifier := pgConnect(t, dsn) + const n = 200 + for i := 0; i < n; i++ { + _, err := notifier.Exec(ctx, "SELECT pg_notify($1, $2)", channel, strconv.Itoa(i)) + require.NoErrorf(t, err, "publishing notification %d", i) + } + + got := make([]string, 0, n) + for len(got) < n { + wctx, cancel := context.WithTimeout(ctx, 5*time.Second) + note, err := listener.WaitForNotification(wctx) + cancel() + require.NoErrorf(t, err, "received only %d of %d notifications before timing out", len(got), n) + require.Equal(t, channel, note.Channel) + got = append(got, note.Payload) + } + + for i := 0; i < n; i++ { + require.Equalf(t, strconv.Itoa(i), got[i], "notification %d out of order or corrupted", i) + } +} + +// TestNotify_OversizedPayloadRejected pins the 8000-byte NOTIFY payload limit: +// a payload at/over the limit is rejected by the server (this is exactly why the +// PostgresEventPublisher offloads oversized events to the scion_event_payloads +// table and notifies a reference id instead), while a payload comfortably under +// the limit is delivered intact. +func TestNotify_OversizedPayloadRejected(t *testing.T) { + requirePG(t) + dsn := enttest.NewSchemaURL(t) + ctx := context.Background() + + listener := pgConnect(t, dsn) + channel := uniqueChannel("size") + _, err := listener.Exec(ctx, "LISTEN "+channel) + require.NoError(t, err) + + notifier := pgConnect(t, dsn) + + // At/over 8000 bytes Postgres rejects the NOTIFY. + oversized := strings.Repeat("x", 8000) + _, err = notifier.Exec(ctx, "SELECT pg_notify($1, $2)", channel, oversized) + require.Error(t, err, "Postgres must reject a NOTIFY payload of 8000 bytes") + + // Comfortably under the limit (matching the publisher's 7000-byte threshold) + // is accepted and delivered intact. + underLimit := strings.Repeat("y", 7000) + _, err = notifier.Exec(ctx, "SELECT pg_notify($1, $2)", channel, underLimit) + require.NoError(t, err) + + wctx, cancel := context.WithTimeout(ctx, 5*time.Second) + note, err := listener.WaitForNotification(wctx) + cancel() + require.NoError(t, err) + assert.Equal(t, underLimit, note.Payload, "under-limit payload must arrive intact") +} + +// TestNotify_ListenerReconnectResumes terminates a listener's backend mid-stream +// (simulating a dropped CloudSQL connection) and verifies that a freshly +// reconnected listener which re-issues LISTEN resumes receiving notifications. +// This is the database-level guarantee the publisher's automatic resubscribe +// loop relies on. +func TestNotify_ListenerReconnectResumes(t *testing.T) { + requirePG(t) + dsn := enttest.NewSchemaURL(t) + ctx := context.Background() + + channel := uniqueChannel("reconnect") + notifier := pgConnect(t, dsn) + + listener1, err := pgx.Connect(ctx, dsn) + require.NoError(t, err) + _, err = listener1.Exec(ctx, "LISTEN "+channel) + require.NoError(t, err) + + var pid uint32 + require.NoError(t, listener1.QueryRow(ctx, "SELECT pg_backend_pid()").Scan(&pid)) + + // Sanity: delivery works before the drop. + _, err = notifier.Exec(ctx, "SELECT pg_notify($1, $2)", channel, "before") + require.NoError(t, err) + wctx, cancel := context.WithTimeout(ctx, 5*time.Second) + note, err := listener1.WaitForNotification(wctx) + cancel() + require.NoError(t, err) + require.Equal(t, "before", note.Payload) + + // Drop the listener's backend from another session. + _, err = notifier.Exec(ctx, "SELECT pg_terminate_backend($1)", pid) + require.NoError(t, err) + + // The dead connection now errors instead of hanging. + wctx, cancel = context.WithTimeout(ctx, 3*time.Second) + _, err = listener1.WaitForNotification(wctx) + cancel() + assert.Error(t, err, "a terminated backend must surface an error to its listener") + _ = listener1.Close(ctx) + + // Reconnect, re-LISTEN, and confirm delivery resumes. + listener2 := pgConnect(t, dsn) + _, err = listener2.Exec(ctx, "LISTEN "+channel) + require.NoError(t, err) + _, err = notifier.Exec(ctx, "SELECT pg_notify($1, $2)", channel, "after") + require.NoError(t, err) + wctx, cancel = context.WithTimeout(ctx, 5*time.Second) + note, err = listener2.WaitForNotification(wctx) + cancel() + require.NoError(t, err, "reconnected listener must resume receiving notifications") + assert.Equal(t, "after", note.Payload) +} + +// TestNotify_CrossChannelIsolation verifies notifications are strictly scoped to +// their channel: a listener subscribed only to channel A never observes an event +// published on channel B. +func TestNotify_CrossChannelIsolation(t *testing.T) { + requirePG(t) + dsn := enttest.NewSchemaURL(t) + ctx := context.Background() + + channelA := uniqueChannel("a") + channelB := uniqueChannel("b") + + listener := pgConnect(t, dsn) + _, err := listener.Exec(ctx, "LISTEN "+channelA) // only A + require.NoError(t, err) + + notifier := pgConnect(t, dsn) + // Publish on B first (must be ignored), then on A. + _, err = notifier.Exec(ctx, "SELECT pg_notify($1, $2)", channelB, "leak-from-B") + require.NoError(t, err) + _, err = notifier.Exec(ctx, "SELECT pg_notify($1, $2)", channelA, "expected-from-A") + require.NoError(t, err) + + wctx, cancel := context.WithTimeout(ctx, 5*time.Second) + note, err := listener.WaitForNotification(wctx) + cancel() + require.NoError(t, err) + assert.Equal(t, channelA, note.Channel, "must only receive channel A") + assert.Equal(t, "expected-from-A", note.Payload) + + // Nothing else should arrive — the B notification must not leak through. + wctx, cancel = context.WithTimeout(ctx, 500*time.Millisecond) + leak, err := listener.WaitForNotification(wctx) + cancel() + if err == nil { + t.Fatalf("listener received an unexpected notification on channel %q: %q", leak.Channel, leak.Payload) + } +} diff --git a/pkg/store/integrationtest/pool_test.go b/pkg/store/integrationtest/pool_test.go new file mode 100644 index 000000000..c05e555bc --- /dev/null +++ b/pkg/store/integrationtest/pool_test.go @@ -0,0 +1,212 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build integration + +// Category 3 — Connection pool stress. These open stores with a deliberately +// small MaxOpenConns and then saturate, block, and forcibly drop connections to +// verify the database/sql pool behaves under pressure: queued requests are served +// once capacity frees up, a saturated pool honors the caller's context deadline +// instead of hanging forever, a long-running transaction does not starve short +// queries on the remaining connections, and the pool transparently heals after +// its backends are killed server-side. +package integrationtest + +import ( + "context" + "sync" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/GoogleCloudPlatform/scion/pkg/ent/entc" + "github.com/GoogleCloudPlatform/scion/pkg/store/entadapter" + "github.com/GoogleCloudPlatform/scion/pkg/store/enttest" +) + +// openPoolStore opens a CompositeStore against a fresh schema with an explicit +// pool size and optional application_name (used by the recovery test to target +// only its own backends for termination). +func openPoolStore(t *testing.T, maxOpen int, appName string) *entadapter.CompositeStore { + t.Helper() + dsn := enttest.NewSchemaURL(t) + if appName != "" { + var err error + dsn, err = enttest.WithConnParam(dsn, "application_name", appName) + require.NoError(t, err) + } + client, err := entc.OpenPostgres(dsn, entc.PoolConfig{MaxOpenConns: maxOpen, MaxIdleConns: maxOpen}) + require.NoError(t, err) + cs := entadapter.NewCompositeStore(client) + t.Cleanup(func() { _ = cs.Close() }) + return cs +} + +// waitFor polls cond until it returns true or the timeout elapses. +func waitFor(t *testing.T, timeout time.Duration, cond func() bool) bool { + t.Helper() + deadline := time.Now().Add(timeout) + for time.Now().Before(deadline) { + if cond() { + return true + } + time.Sleep(5 * time.Millisecond) + } + return cond() +} + +// TestPool_ExhaustedRequestsEventuallySucceed launches far more concurrent +// connection-holding queries than the pool has slots and asserts every one +// eventually completes: requests beyond MaxOpenConns queue and are served as +// connections free up, rather than erroring. +func TestPool_ExhaustedRequestsEventuallySucceed(t *testing.T) { + cs := newStoreWithPool(t, 4) + ctx := context.Background() + db := cs.DB() + require.NotNil(t, db) + require.Equal(t, 4, db.Stats().MaxOpenConnections, "pool sized to 4") + + const tasks = 32 // 8x the pool: most requests must queue + errs := make(chan error, tasks) + runConcurrently(tasks, func(int) { + // pg_sleep holds the connection checked out long enough to force queueing. + _, err := db.ExecContext(ctx, `SELECT pg_sleep(0.05)`) + errs <- err + }) + close(errs) + for err := range errs { + require.NoError(t, err, "a queued request failed instead of waiting for a free connection") + } +} + +// TestPool_SaturatedPoolRespectsContextDeadline checks out every connection in a +// 2-connection pool and holds them, then issues a query with a short deadline. +// With no connection available the acquire must fail with the context deadline +// (a clean, bounded failure) rather than blocking forever; once the held +// connections are released, queries succeed again. +func TestPool_SaturatedPoolRespectsContextDeadline(t *testing.T) { + cs := newStoreWithPool(t, 2) + ctx := context.Background() + db := cs.DB() + require.NotNil(t, db) + + release := make(chan struct{}) + var holding sync.WaitGroup + for i := 0; i < 2; i++ { + holding.Add(1) + go func() { + defer holding.Done() + conn, err := db.Conn(ctx) + if err != nil { + return + } + defer conn.Close() + // Touch the connection so it is genuinely checked out, then hold it. + _, _ = conn.ExecContext(ctx, `SELECT 1`) + <-release + }() + } + + require.True(t, waitFor(t, 5*time.Second, func() bool { return db.Stats().InUse == 2 }), + "both pool connections should be checked out") + + shortCtx, cancel := context.WithTimeout(ctx, 300*time.Millisecond) + _, err := db.ExecContext(shortCtx, `SELECT 1`) + cancel() + require.Error(t, err, "query on a saturated pool must not hang; it should fail on the deadline") + assert.ErrorIs(t, err, context.DeadlineExceeded) + + close(release) + holding.Wait() + + _, err = db.ExecContext(ctx, `SELECT 1`) + require.NoError(t, err, "pool must serve queries again once connections are released") +} + +// TestPool_LongTxnDoesNotStarveShortQueries holds one connection of a 4-connection +// pool inside a long (1s) transaction and asserts a batch of short queries on the +// other connections all complete quickly, well before the long transaction does. +func TestPool_LongTxnDoesNotStarveShortQueries(t *testing.T) { + cs := newStoreWithPool(t, 4) + ctx := context.Background() + db := cs.DB() + require.NotNil(t, db) + + longDone := make(chan error, 1) + go func() { + tx, err := db.BeginTx(ctx, nil) + if err != nil { + longDone <- err + return + } + if _, err := tx.ExecContext(ctx, `SELECT pg_sleep(1)`); err != nil { + _ = tx.Rollback() + longDone <- err + return + } + longDone <- tx.Commit() + }() + + // Let the long transaction grab its connection first. + require.True(t, waitFor(t, 5*time.Second, func() bool { return db.Stats().InUse >= 1 }), + "long transaction should have checked out a connection") + + start := time.Now() + for i := 0; i < 10; i++ { + shortCtx, cancel := context.WithTimeout(ctx, 500*time.Millisecond) + var one int + err := db.QueryRowContext(shortCtx, `SELECT 1`).Scan(&one) + cancel() + require.NoErrorf(t, err, "short query %d starved by the long transaction", i) + require.Equal(t, 1, one) + } + elapsed := time.Since(start) + assert.Lessf(t, elapsed, 900*time.Millisecond, + "10 short queries took %s — they appear to be waiting on the 1s transaction", elapsed) + + require.NoError(t, <-longDone, "long transaction itself must commit") +} + +// TestPool_RecoveryAfterConnectionDrop warms several pooled connections, then +// terminates them server-side with pg_terminate_backend (simulating a CloudSQL +// connection reset / failover). The pool must transparently discard the dead +// connections and open fresh ones so subsequent queries succeed. +func TestPool_RecoveryAfterConnectionDrop(t *testing.T) { + appName := "scion_pooltest_" + shortID() + cs := openPoolStore(t, 4, appName) + ctx := context.Background() + db := cs.DB() + require.NotNil(t, db) + + // Warm up multiple connections so there are siblings to kill. + runConcurrently(4, func(int) { _, _ = db.ExecContext(ctx, `SELECT pg_sleep(0.05)`) }) + + // Kill every backend with our application_name except the one running this + // statement, scoping the blast radius to this test's own pool. + _, err := db.ExecContext(ctx, + `SELECT pg_terminate_backend(pid) FROM pg_stat_activity + WHERE application_name = $1 AND pid <> pg_backend_pid()`, appName) + require.NoError(t, err) + + // The pool must heal: each query that lands on a dead connection is retried on + // a freshly opened one by database/sql. + for i := 0; i < 20; i++ { + var one int + err := db.QueryRowContext(ctx, `SELECT 1`).Scan(&one) + require.NoErrorf(t, err, "query %d failed; pool did not heal after backend termination", i) + require.Equal(t, 1, one) + } +} diff --git a/pkg/store/integrationtest/schema_test.go b/pkg/store/integrationtest/schema_test.go new file mode 100644 index 000000000..1561100d9 --- /dev/null +++ b/pkg/store/integrationtest/schema_test.go @@ -0,0 +1,176 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build integration + +// Category 6 — Schema / type edge cases. Postgres is strictly typed where SQLite +// is loose, so these pin behaviors that can silently differ between the two +// backends: NULL semantics for nullable columns, exact round-tripping of +// Unicode/emoji and JSON (including nested structures and special characters), +// large text values that must not be truncated, and TIMESTAMPTZ microsecond +// precision (vs SQLite's text timestamps). +package integrationtest + +import ( + "context" + "strings" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/GoogleCloudPlatform/scion/pkg/store" +) + +// TestSchema_NullableRoundTrip verifies nullable columns store and read back as +// genuine SQL NULL (not a zero-value sentinel) and transition correctly when set. +func TestSchema_NullableRoundTrip(t *testing.T) { + cs := newStore(t) + ctx := context.Background() + db := cs.DB() + require.NotNil(t, db) + project := seedProject(t, cs) + + // A pending scheduled event has a NULL fired_at. + evt := makeScheduledEvent(project.ID) + require.NoError(t, cs.CreateScheduledEvent(ctx, evt)) + got, err := cs.GetScheduledEvent(ctx, evt.ID) + require.NoError(t, err) + assert.Nil(t, got.FiredAt, "pending event must have nil FiredAt") + + var firedIsNull bool + require.NoError(t, db.QueryRowContext(ctx, + `SELECT fired_at IS NULL FROM scheduled_events WHERE id=$1`, evt.ID).Scan(&firedIsNull)) + assert.True(t, firedIsNull, "fired_at must be SQL NULL, not a zero timestamp") + + // Claiming sets fired_at to a real value. + won, err := cs.ClaimScheduledEvent(ctx, evt.ID, store.ScheduledEventFired) + require.NoError(t, err) + require.True(t, won) + got, err = cs.GetScheduledEvent(ctx, evt.ID) + require.NoError(t, err) + require.NotNil(t, got.FiredAt, "claimed event must have a non-nil FiredAt") + + require.NoError(t, db.QueryRowContext(ctx, + `SELECT fired_at IS NULL FROM scheduled_events WHERE id=$1`, evt.ID).Scan(&firedIsNull)) + assert.False(t, firedIsNull, "fired_at must no longer be NULL after a claim") + + // An agent created without an owner has a NULL owner_id (not an empty string). + ag := makeAgent(project.ID, "null-owner-"+shortID()) + require.NoError(t, cs.CreateAgent(ctx, ag)) + var ownerIsNull bool + require.NoError(t, db.QueryRowContext(ctx, + `SELECT owner_id IS NULL FROM agents WHERE id=$1`, ag.ID).Scan(&ownerIsNull)) + assert.True(t, ownerIsNull, "unset owner_id must be SQL NULL") + reread, err := cs.GetAgent(ctx, ag.ID) + require.NoError(t, err) + assert.Equal(t, "", reread.OwnerID, "NULL owner_id must read back as empty string") +} + +// TestSchema_UnicodeAndEmoji verifies multibyte Unicode and emoji round-trip +// byte-for-byte through text columns. +func TestSchema_UnicodeAndEmoji(t *testing.T) { + cs := newStore(t) + ctx := context.Background() + + const fancy = "项目 🚀 café ☃ Ω≈ç √∫ — “quotes” 𝔘𝔫𝔦𝔠𝔬𝔡𝔢" + p := makeProject("unicode-" + shortID()) + p.Name = fancy + require.NoError(t, cs.CreateProject(ctx, p)) + + got, err := cs.GetProject(ctx, p.ID) + require.NoError(t, err) + assert.Equal(t, fancy, got.Name, "Unicode/emoji must round-trip exactly") +} + +// TestSchema_NestedJSONAndSpecialChars verifies JSON-bearing columns preserve +// nested objects, arrays, and special characters exactly. ScheduledEvent.Payload +// is stored verbatim; Agent.Labels is marshaled to a JSON column. +func TestSchema_NestedJSONAndSpecialChars(t *testing.T) { + cs := newStore(t) + ctx := context.Background() + project := seedProject(t, cs) + + // Verbatim JSON text column. + const payload = `{"nested":{"arr":[1,2,3],"flag":true,"s":"emoji 🎉 \"quoted\" back\\slash tab\there","null":null},"unicode":"café"}` + evt := makeScheduledEvent(project.ID) + evt.Payload = payload + require.NoError(t, cs.CreateScheduledEvent(ctx, evt)) + gotEvt, err := cs.GetScheduledEvent(ctx, evt.ID) + require.NoError(t, err) + assert.Equal(t, payload, gotEvt.Payload, "nested JSON payload must round-trip verbatim") + + // JSON-marshaled map column with special-character values. + labels := map[string]string{ + "emoji": "🔥💧", + "quotes": `he said "hi"`, + "unicode": "naïve café", + "nl": "line1\nline2\ttabbed", + } + ag := makeAgent(project.ID, "json-labels-"+shortID()) + ag.Labels = labels + require.NoError(t, cs.CreateAgent(ctx, ag)) + gotAg, err := cs.GetAgent(ctx, ag.ID) + require.NoError(t, err) + assert.Equal(t, labels, gotAg.Labels, "JSON label map must round-trip exactly, special chars included") +} + +// TestSchema_LargeTextNoTruncation verifies a large string in a text column is +// stored and returned without truncation. Ent maps Go strings to unbounded +// Postgres TEXT, so there is no VARCHAR(n) boundary to silently clip at. +func TestSchema_LargeTextNoTruncation(t *testing.T) { + cs := newStore(t) + ctx := context.Background() + project := seedProject(t, cs) + + large := strings.Repeat("A", 100*1024) // 100 KiB + evt := makeScheduledEvent(project.ID) + evt.Payload = large + require.NoError(t, cs.CreateScheduledEvent(ctx, evt)) + + got, err := cs.GetScheduledEvent(ctx, evt.ID) + require.NoError(t, err) + assert.Equal(t, len(large), len(got.Payload), "large text must not be truncated") + assert.Equal(t, large, got.Payload) +} + +// TestSchema_TimestampPrecision verifies TIMESTAMPTZ preserves sub-second +// precision to the microsecond (Postgres' resolution) and a stable instant. +// Nanoseconds below the microsecond are truncated by Postgres — that truncation +// is the documented behavior, not data loss to the second as a naive text-based +// (SQLite) representation might do. +func TestSchema_TimestampPrecision(t *testing.T) { + cs := newStore(t) + ctx := context.Background() + project := seedProject(t, cs) + + // A time carrying both microsecond and stray nanosecond components. + in := time.Date(2026, 6, 2, 13, 14, 15, 123456789, time.UTC) + wantMicro := in.Truncate(time.Microsecond) // 123456000 ns + + evt := makeScheduledEvent(project.ID) + evt.FireAt = in + require.NoError(t, cs.CreateScheduledEvent(ctx, evt)) + + got, err := cs.GetScheduledEvent(ctx, evt.ID) + require.NoError(t, err) + + assert.True(t, got.FireAt.UTC().Equal(wantMicro), + "fire_at must preserve the microsecond instant: got %v, want %v", got.FireAt.UTC(), wantMicro) + assert.NotEqual(t, in.Truncate(time.Second), got.FireAt.UTC(), + "sub-second precision must be retained (not truncated to whole seconds)") + assert.Zero(t, got.FireAt.Nanosecond()%1000, + "Postgres resolution is microseconds; nanosecond remainder must be zero") +} diff --git a/pkg/store/sqlite/allow_list_invite_test.go b/pkg/store/sqlite/allow_list_invite_test.go deleted file mode 100644 index 37b3a44f1..000000000 --- a/pkg/store/sqlite/allow_list_invite_test.go +++ /dev/null @@ -1,236 +0,0 @@ -// Copyright 2026 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//go:build !no_sqlite - -package sqlite - -import ( - "context" - "testing" - "time" - - "github.com/GoogleCloudPlatform/scion/pkg/store" - "github.com/google/uuid" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestUpdateAllowListEntryInviteID(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - // Add an allow-list entry - entry := &store.AllowListEntry{ - ID: uuid.New().String(), - Email: "user@example.com", - Note: "test user", - AddedBy: "admin-1", - } - require.NoError(t, s.AddAllowListEntry(ctx, entry)) - - // Update its invite ID - inviteID := uuid.New().String() - err := s.UpdateAllowListEntryInviteID(ctx, "user@example.com", inviteID) - require.NoError(t, err) - - // Verify the update - got, err := s.GetAllowListEntry(ctx, "user@example.com") - require.NoError(t, err) - assert.Equal(t, inviteID, got.InviteID) -} - -func TestUpdateAllowListEntryInviteID_CaseInsensitive(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - entry := &store.AllowListEntry{ - ID: uuid.New().String(), - Email: "user@example.com", - Note: "test", - AddedBy: "admin-1", - } - require.NoError(t, s.AddAllowListEntry(ctx, entry)) - - inviteID := uuid.New().String() - err := s.UpdateAllowListEntryInviteID(ctx, "User@Example.COM", inviteID) - require.NoError(t, err) - - got, err := s.GetAllowListEntry(ctx, "user@example.com") - require.NoError(t, err) - assert.Equal(t, inviteID, got.InviteID) -} - -func TestUpdateAllowListEntryInviteID_NotFound(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - err := s.UpdateAllowListEntryInviteID(ctx, "nonexistent@example.com", "some-id") - assert.ErrorIs(t, err, store.ErrNotFound) -} - -func TestListAllowListEntriesWithInvites_NoInvite(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - entry := &store.AllowListEntry{ - ID: uuid.New().String(), - Email: "user@example.com", - Note: "test", - AddedBy: "admin-1", - } - require.NoError(t, s.AddAllowListEntry(ctx, entry)) - - result, err := s.ListAllowListEntriesWithInvites(ctx, store.ListOptions{Limit: 50}) - require.NoError(t, err) - require.Len(t, result.Items, 1) - - item := result.Items[0] - assert.Equal(t, "user@example.com", item.Email) - assert.Empty(t, item.InviteCodePrefix) - assert.Zero(t, item.InviteMaxUses) - assert.False(t, item.InviteRevoked) -} - -func TestListAllowListEntriesWithInvites_WithLinkedInvite(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - // Create an invite code - invite := &store.InviteCode{ - ID: uuid.New().String(), - CodeHash: "testhash123", - CodePrefix: "scion_inv_abcdefgh", - MaxUses: 1, - UseCount: 0, - ExpiresAt: time.Now().Add(24 * time.Hour), - CreatedBy: "admin-1", - Note: "test invite", - Created: time.Now(), - } - require.NoError(t, s.CreateInviteCode(ctx, invite)) - - // Create an allow-list entry linked to the invite - entry := &store.AllowListEntry{ - ID: uuid.New().String(), - Email: "user@example.com", - Note: "test", - AddedBy: "admin-1", - InviteID: invite.ID, - } - require.NoError(t, s.AddAllowListEntry(ctx, entry)) - - result, err := s.ListAllowListEntriesWithInvites(ctx, store.ListOptions{Limit: 50}) - require.NoError(t, err) - require.Len(t, result.Items, 1) - - item := result.Items[0] - assert.Equal(t, "user@example.com", item.Email) - assert.Equal(t, invite.ID, item.InviteID) - assert.Equal(t, "scion_inv_abcdefgh", item.InviteCodePrefix) - assert.Equal(t, 1, item.InviteMaxUses) - assert.Equal(t, 0, item.InviteUseCount) - assert.False(t, item.InviteRevoked) - assert.False(t, item.InviteExpiresAt.IsZero()) -} - -func TestListAllowListEntriesWithInvites_RevokedInvite(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - invite := &store.InviteCode{ - ID: uuid.New().String(), - CodeHash: "testhash456", - CodePrefix: "scion_inv_xyz12345", - MaxUses: 1, - ExpiresAt: time.Now().Add(24 * time.Hour), - Revoked: true, - CreatedBy: "admin-1", - Created: time.Now(), - } - require.NoError(t, s.CreateInviteCode(ctx, invite)) - - entry := &store.AllowListEntry{ - ID: uuid.New().String(), - Email: "revoked@example.com", - Note: "test", - AddedBy: "admin-1", - InviteID: invite.ID, - } - require.NoError(t, s.AddAllowListEntry(ctx, entry)) - - result, err := s.ListAllowListEntriesWithInvites(ctx, store.ListOptions{Limit: 50}) - require.NoError(t, err) - require.Len(t, result.Items, 1) - assert.True(t, result.Items[0].InviteRevoked) -} - -func TestListAllowListEntriesWithInvites_MixedEntries(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - // Create an invite - invite := &store.InviteCode{ - ID: uuid.New().String(), - CodeHash: "testhash789", - CodePrefix: "scion_inv_mixed123", - MaxUses: 5, - UseCount: 2, - ExpiresAt: time.Now().Add(48 * time.Hour), - CreatedBy: "admin-1", - Created: time.Now(), - } - require.NoError(t, s.CreateInviteCode(ctx, invite)) - - // Entry with invite - entry1 := &store.AllowListEntry{ - ID: uuid.New().String(), - Email: "with-invite@example.com", - Note: "has invite", - AddedBy: "admin-1", - InviteID: invite.ID, - } - require.NoError(t, s.AddAllowListEntry(ctx, entry1)) - - // Entry without invite - entry2 := &store.AllowListEntry{ - ID: uuid.New().String(), - Email: "no-invite@example.com", - Note: "no invite", - AddedBy: "admin-1", - } - require.NoError(t, s.AddAllowListEntry(ctx, entry2)) - - result, err := s.ListAllowListEntriesWithInvites(ctx, store.ListOptions{Limit: 50}) - require.NoError(t, err) - assert.Equal(t, 2, result.TotalCount) - require.Len(t, result.Items, 2) - - // Find the entry with invite - var withInvite, withoutInvite store.AllowListEntryWithInvite - for _, item := range result.Items { - if item.Email == "with-invite@example.com" { - withInvite = item - } else { - withoutInvite = item - } - } - - assert.Equal(t, "scion_inv_mixed123", withInvite.InviteCodePrefix) - assert.Equal(t, 5, withInvite.InviteMaxUses) - assert.Equal(t, 2, withInvite.InviteUseCount) - - assert.Empty(t, withoutInvite.InviteCodePrefix) - assert.Zero(t, withoutInvite.InviteMaxUses) -} diff --git a/pkg/store/sqlite/brokersecret.go b/pkg/store/sqlite/brokersecret.go deleted file mode 100644 index b215a4d5a..000000000 --- a/pkg/store/sqlite/brokersecret.go +++ /dev/null @@ -1,290 +0,0 @@ -// Copyright 2026 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package sqlite provides a SQLite implementation of the Store interface. -package sqlite - -import ( - "context" - "database/sql" - "errors" - "fmt" - "strings" - "time" - - "github.com/GoogleCloudPlatform/scion/pkg/store" -) - -// ============================================================================ -// Broker Secret Operations -// ============================================================================ - -// CreateBrokerSecret creates a new broker secret record. -func (s *SQLiteStore) CreateBrokerSecret(ctx context.Context, secret *store.BrokerSecret) error { - if secret.BrokerID == "" { - return store.ErrInvalidInput - } - - now := time.Now() - if secret.CreatedAt.IsZero() { - secret.CreatedAt = now - } - if secret.Algorithm == "" { - secret.Algorithm = store.BrokerSecretAlgorithmHMACSHA256 - } - if secret.Status == "" { - secret.Status = store.BrokerSecretStatusActive - } - - _, err := s.db.ExecContext(ctx, ` - INSERT INTO broker_secrets ( - broker_id, secret_key, algorithm, - created_at, rotated_at, expires_at, status - ) VALUES (?, ?, ?, ?, ?, ?, ?) - `, - secret.BrokerID, secret.SecretKey, secret.Algorithm, - secret.CreatedAt, nullableTime(secret.RotatedAt), nullableTime(secret.ExpiresAt), secret.Status, - ) - if err != nil { - if strings.Contains(err.Error(), "UNIQUE constraint failed") { - return store.ErrAlreadyExists - } - if strings.Contains(err.Error(), "FOREIGN KEY constraint failed") { - return fmt.Errorf("broker %s does not exist: %w", secret.BrokerID, store.ErrNotFound) - } - return err - } - return nil -} - -// GetBrokerSecret retrieves a broker secret by broker ID. -func (s *SQLiteStore) GetBrokerSecret(ctx context.Context, brokerID string) (*store.BrokerSecret, error) { - secret := &store.BrokerSecret{} - var rotatedAt, expiresAt sql.NullTime - - err := s.db.QueryRowContext(ctx, ` - SELECT broker_id, secret_key, algorithm, - created_at, rotated_at, expires_at, status - FROM broker_secrets WHERE broker_id = ? - `, brokerID).Scan( - &secret.BrokerID, &secret.SecretKey, &secret.Algorithm, - &secret.CreatedAt, &rotatedAt, &expiresAt, &secret.Status, - ) - if err != nil { - if errors.Is(err, sql.ErrNoRows) { - return nil, store.ErrNotFound - } - return nil, err - } - - if rotatedAt.Valid { - secret.RotatedAt = rotatedAt.Time - } - if expiresAt.Valid { - secret.ExpiresAt = expiresAt.Time - } - - return secret, nil -} - -// GetActiveSecrets retrieves all active and deprecated secrets for a broker. -// This supports dual-secret validation during rotation grace periods. -func (s *SQLiteStore) GetActiveSecrets(ctx context.Context, brokerID string) ([]*store.BrokerSecret, error) { - rows, err := s.db.QueryContext(ctx, ` - SELECT broker_id, secret_key, algorithm, - created_at, rotated_at, expires_at, status - FROM broker_secrets - WHERE broker_id = ? AND status IN (?, ?) - ORDER BY created_at DESC - `, brokerID, store.BrokerSecretStatusActive, store.BrokerSecretStatusDeprecated) - if err != nil { - return nil, err - } - defer rows.Close() - - var secrets []*store.BrokerSecret - for rows.Next() { - secret := &store.BrokerSecret{} - var rotatedAt, expiresAt sql.NullTime - - if err := rows.Scan( - &secret.BrokerID, &secret.SecretKey, &secret.Algorithm, - &secret.CreatedAt, &rotatedAt, &expiresAt, &secret.Status, - ); err != nil { - return nil, err - } - - if rotatedAt.Valid { - secret.RotatedAt = rotatedAt.Time - } - if expiresAt.Valid { - secret.ExpiresAt = expiresAt.Time - } - - secrets = append(secrets, secret) - } - - if err := rows.Err(); err != nil { - return nil, err - } - - return secrets, nil -} - -// UpdateBrokerSecret updates an existing broker secret. -func (s *SQLiteStore) UpdateBrokerSecret(ctx context.Context, secret *store.BrokerSecret) error { - result, err := s.db.ExecContext(ctx, ` - UPDATE broker_secrets SET - secret_key = ?, - algorithm = ?, - rotated_at = ?, - expires_at = ?, - status = ? - WHERE broker_id = ? - `, - secret.SecretKey, secret.Algorithm, - nullableTime(secret.RotatedAt), nullableTime(secret.ExpiresAt), secret.Status, - secret.BrokerID, - ) - if err != nil { - return err - } - - rows, err := result.RowsAffected() - if err != nil { - return err - } - if rows == 0 { - return store.ErrNotFound - } - return nil -} - -// DeleteBrokerSecret removes a broker secret. -func (s *SQLiteStore) DeleteBrokerSecret(ctx context.Context, brokerID string) error { - result, err := s.db.ExecContext(ctx, ` - DELETE FROM broker_secrets WHERE broker_id = ? - `, brokerID) - if err != nil { - return err - } - - rows, err := result.RowsAffected() - if err != nil { - return err - } - if rows == 0 { - return store.ErrNotFound - } - return nil -} - -// ============================================================================ -// Broker Join Token Operations -// ============================================================================ - -// CreateJoinToken creates a new join token for broker registration. -func (s *SQLiteStore) CreateJoinToken(ctx context.Context, token *store.BrokerJoinToken) error { - if token.BrokerID == "" || token.TokenHash == "" { - return store.ErrInvalidInput - } - - now := time.Now() - if token.CreatedAt.IsZero() { - token.CreatedAt = now - } - - _, err := s.db.ExecContext(ctx, ` - INSERT INTO broker_join_tokens ( - broker_id, token_hash, expires_at, created_at, created_by - ) VALUES (?, ?, ?, ?, ?) - `, - token.BrokerID, token.TokenHash, token.ExpiresAt, token.CreatedAt, token.CreatedBy, - ) - if err != nil { - if strings.Contains(err.Error(), "UNIQUE constraint failed") { - return store.ErrAlreadyExists - } - if strings.Contains(err.Error(), "FOREIGN KEY constraint failed") { - return store.ErrNotFound - } - return err - } - return nil -} - -// GetJoinToken retrieves a join token by token hash. -func (s *SQLiteStore) GetJoinToken(ctx context.Context, tokenHash string) (*store.BrokerJoinToken, error) { - token := &store.BrokerJoinToken{} - - err := s.db.QueryRowContext(ctx, ` - SELECT broker_id, token_hash, expires_at, created_at, created_by - FROM broker_join_tokens WHERE token_hash = ? - `, tokenHash).Scan( - &token.BrokerID, &token.TokenHash, &token.ExpiresAt, &token.CreatedAt, &token.CreatedBy, - ) - if err != nil { - if errors.Is(err, sql.ErrNoRows) { - return nil, store.ErrNotFound - } - return nil, err - } - return token, nil -} - -// GetJoinTokenByBrokerID retrieves a join token by broker ID. -func (s *SQLiteStore) GetJoinTokenByBrokerID(ctx context.Context, brokerID string) (*store.BrokerJoinToken, error) { - token := &store.BrokerJoinToken{} - - err := s.db.QueryRowContext(ctx, ` - SELECT broker_id, token_hash, expires_at, created_at, created_by - FROM broker_join_tokens WHERE broker_id = ? - `, brokerID).Scan( - &token.BrokerID, &token.TokenHash, &token.ExpiresAt, &token.CreatedAt, &token.CreatedBy, - ) - if err != nil { - if errors.Is(err, sql.ErrNoRows) { - return nil, store.ErrNotFound - } - return nil, err - } - return token, nil -} - -// DeleteJoinToken removes a join token by broker ID. -func (s *SQLiteStore) DeleteJoinToken(ctx context.Context, brokerID string) error { - result, err := s.db.ExecContext(ctx, ` - DELETE FROM broker_join_tokens WHERE broker_id = ? - `, brokerID) - if err != nil { - return err - } - - rows, err := result.RowsAffected() - if err != nil { - return err - } - if rows == 0 { - return store.ErrNotFound - } - return nil -} - -// CleanExpiredJoinTokens removes all expired join tokens. -func (s *SQLiteStore) CleanExpiredJoinTokens(ctx context.Context) error { - _, err := s.db.ExecContext(ctx, ` - DELETE FROM broker_join_tokens WHERE expires_at < ? - `, time.Now()) - return err -} diff --git a/pkg/store/sqlite/brokersecret_test.go b/pkg/store/sqlite/brokersecret_test.go deleted file mode 100644 index de8fa2f16..000000000 --- a/pkg/store/sqlite/brokersecret_test.go +++ /dev/null @@ -1,334 +0,0 @@ -// Copyright 2026 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//go:build !no_sqlite - -package sqlite - -import ( - "context" - "errors" - "testing" - "time" - - "github.com/GoogleCloudPlatform/scion/pkg/store" - "github.com/google/uuid" -) - -func TestBrokerSecretCRUD(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - // First create a runtime broker to satisfy FK constraint - brokerID := uuid.New().String() - broker := &store.RuntimeBroker{ - ID: brokerID, - Name: "test-host", - Slug: "test-host", - Status: store.BrokerStatusOnline, - Created: time.Now(), - Updated: time.Now(), - } - if err := s.CreateRuntimeBroker(ctx, broker); err != nil { - t.Fatalf("failed to create runtime broker: %v", err) - } - - // Test CreateBrokerSecret - secret := &store.BrokerSecret{ - BrokerID: brokerID, - SecretKey: []byte("test-secret-key-32-bytes-long!!"), - Algorithm: store.BrokerSecretAlgorithmHMACSHA256, - Status: store.BrokerSecretStatusActive, - } - if err := s.CreateBrokerSecret(ctx, secret); err != nil { - t.Fatalf("CreateBrokerSecret failed: %v", err) - } - - // Verify timestamps were set - if secret.CreatedAt.IsZero() { - t.Error("CreatedAt should be set automatically") - } - - // Test GetBrokerSecret - retrieved, err := s.GetBrokerSecret(ctx, brokerID) - if err != nil { - t.Fatalf("GetBrokerSecret failed: %v", err) - } - if retrieved.BrokerID != brokerID { - t.Errorf("BrokerID mismatch: got %s, want %s", retrieved.BrokerID, brokerID) - } - if string(retrieved.SecretKey) != string(secret.SecretKey) { - t.Error("SecretKey mismatch") - } - if retrieved.Algorithm != store.BrokerSecretAlgorithmHMACSHA256 { - t.Errorf("Algorithm mismatch: got %s, want %s", retrieved.Algorithm, store.BrokerSecretAlgorithmHMACSHA256) - } - if retrieved.Status != store.BrokerSecretStatusActive { - t.Errorf("Status mismatch: got %s, want %s", retrieved.Status, store.BrokerSecretStatusActive) - } - - // Test duplicate create returns error - if err := s.CreateBrokerSecret(ctx, secret); err != store.ErrAlreadyExists { - t.Errorf("Expected ErrAlreadyExists, got: %v", err) - } - - // Test UpdateBrokerSecret - newKey := []byte("new-secret-key-32-bytes-long!!!") - retrieved.SecretKey = newKey - retrieved.RotatedAt = time.Now() - retrieved.Status = store.BrokerSecretStatusDeprecated - - if err := s.UpdateBrokerSecret(ctx, retrieved); err != nil { - t.Fatalf("UpdateBrokerSecret failed: %v", err) - } - - // Verify update - updated, err := s.GetBrokerSecret(ctx, brokerID) - if err != nil { - t.Fatalf("GetBrokerSecret after update failed: %v", err) - } - if string(updated.SecretKey) != string(newKey) { - t.Error("SecretKey not updated") - } - if updated.Status != store.BrokerSecretStatusDeprecated { - t.Errorf("Status not updated: got %s, want %s", updated.Status, store.BrokerSecretStatusDeprecated) - } - if updated.RotatedAt.IsZero() { - t.Error("RotatedAt should be set") - } - - // Test DeleteBrokerSecret - if err := s.DeleteBrokerSecret(ctx, brokerID); err != nil { - t.Fatalf("DeleteBrokerSecret failed: %v", err) - } - - // Verify deletion - _, err = s.GetBrokerSecret(ctx, brokerID) - if err != store.ErrNotFound { - t.Errorf("Expected ErrNotFound after delete, got: %v", err) - } - - // Test delete non-existent returns error - if err := s.DeleteBrokerSecret(ctx, "non-existent"); err != store.ErrNotFound { - t.Errorf("Expected ErrNotFound for non-existent delete, got: %v", err) - } -} - -func TestBrokerSecretForeignKey(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - // Try to create secret for non-existent broker - secret := &store.BrokerSecret{ - BrokerID: "non-existent-host", - SecretKey: []byte("test-secret"), - Algorithm: store.BrokerSecretAlgorithmHMACSHA256, - Status: store.BrokerSecretStatusActive, - } - - err := s.CreateBrokerSecret(ctx, secret) - if err == nil { - t.Error("Expected error when creating secret for non-existent broker") - } - if !errors.Is(err, store.ErrNotFound) { - t.Errorf("Expected ErrNotFound for FK violation, got: %v", err) - } -} - -func TestBrokerJoinTokenCRUD(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - // First create a runtime broker to satisfy FK constraint - brokerID := uuid.New().String() - broker := &store.RuntimeBroker{ - ID: brokerID, - Name: "test-host-for-token", - Slug: "test-host-for-token", - Status: store.BrokerStatusOffline, - Created: time.Now(), - Updated: time.Now(), - } - if err := s.CreateRuntimeBroker(ctx, broker); err != nil { - t.Fatalf("failed to create runtime broker: %v", err) - } - - // Test CreateJoinToken - token := &store.BrokerJoinToken{ - BrokerID: brokerID, - TokenHash: "test-token-hash-abc123", - ExpiresAt: time.Now().Add(1 * time.Hour), - CreatedBy: "admin-user-id", - } - if err := s.CreateJoinToken(ctx, token); err != nil { - t.Fatalf("CreateJoinToken failed: %v", err) - } - - // Verify timestamps were set - if token.CreatedAt.IsZero() { - t.Error("CreatedAt should be set automatically") - } - - // Test GetJoinToken by hash - retrieved, err := s.GetJoinToken(ctx, "test-token-hash-abc123") - if err != nil { - t.Fatalf("GetJoinToken failed: %v", err) - } - if retrieved.BrokerID != brokerID { - t.Errorf("BrokerID mismatch: got %s, want %s", retrieved.BrokerID, brokerID) - } - if retrieved.TokenHash != "test-token-hash-abc123" { - t.Errorf("TokenHash mismatch: got %s, want %s", retrieved.TokenHash, "test-token-hash-abc123") - } - if retrieved.CreatedBy != "admin-user-id" { - t.Errorf("CreatedBy mismatch: got %s, want %s", retrieved.CreatedBy, "admin-user-id") - } - - // Test GetJoinTokenByBrokerID - byHost, err := s.GetJoinTokenByBrokerID(ctx, brokerID) - if err != nil { - t.Fatalf("GetJoinTokenByBrokerID failed: %v", err) - } - if byHost.TokenHash != "test-token-hash-abc123" { - t.Errorf("TokenHash mismatch: got %s, want %s", byHost.TokenHash, "test-token-hash-abc123") - } - - // Test duplicate create returns error - if err := s.CreateJoinToken(ctx, token); err != store.ErrAlreadyExists { - t.Errorf("Expected ErrAlreadyExists, got: %v", err) - } - - // Test DeleteJoinToken - if err := s.DeleteJoinToken(ctx, brokerID); err != nil { - t.Fatalf("DeleteJoinToken failed: %v", err) - } - - // Verify deletion - _, err = s.GetJoinToken(ctx, "test-token-hash-abc123") - if err != store.ErrNotFound { - t.Errorf("Expected ErrNotFound after delete, got: %v", err) - } - - // Test delete non-existent returns error - if err := s.DeleteJoinToken(ctx, "non-existent"); err != store.ErrNotFound { - t.Errorf("Expected ErrNotFound for non-existent delete, got: %v", err) - } -} - -func TestCleanExpiredJoinTokens(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - // Create two brokers - host1ID := uuid.New().String() - host2ID := uuid.New().String() - for i, id := range []string{host1ID, host2ID} { - broker := &store.RuntimeBroker{ - ID: id, - Name: "test-host-" + string(rune('a'+i)), - Slug: "test-host-" + string(rune('a'+i)), - Status: store.BrokerStatusOffline, - Created: time.Now(), - Updated: time.Now(), - } - if err := s.CreateRuntimeBroker(ctx, broker); err != nil { - t.Fatalf("failed to create runtime broker: %v", err) - } - } - - // Create an expired token and a valid token - expiredToken := &store.BrokerJoinToken{ - BrokerID: host1ID, - TokenHash: "expired-token-hash", - ExpiresAt: time.Now().Add(-1 * time.Hour), // Already expired - CreatedBy: "admin", - } - validToken := &store.BrokerJoinToken{ - BrokerID: host2ID, - TokenHash: "valid-token-hash", - ExpiresAt: time.Now().Add(1 * time.Hour), // Still valid - CreatedBy: "admin", - } - - if err := s.CreateJoinToken(ctx, expiredToken); err != nil { - t.Fatalf("CreateJoinToken (expired) failed: %v", err) - } - if err := s.CreateJoinToken(ctx, validToken); err != nil { - t.Fatalf("CreateJoinToken (valid) failed: %v", err) - } - - // Clean expired tokens - if err := s.CleanExpiredJoinTokens(ctx); err != nil { - t.Fatalf("CleanExpiredJoinTokens failed: %v", err) - } - - // Verify expired token is gone - _, err := s.GetJoinToken(ctx, "expired-token-hash") - if err != store.ErrNotFound { - t.Errorf("Expected expired token to be deleted, got: %v", err) - } - - // Verify valid token still exists - _, err = s.GetJoinToken(ctx, "valid-token-hash") - if err != nil { - t.Errorf("Expected valid token to still exist, got: %v", err) - } -} - -func TestBrokerSecretCascadeDelete(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - // Create a runtime broker - brokerID := uuid.New().String() - broker := &store.RuntimeBroker{ - ID: brokerID, - Name: "cascade-test-host", - Slug: "cascade-test-host", - Status: store.BrokerStatusOnline, - Created: time.Now(), - Updated: time.Now(), - } - if err := s.CreateRuntimeBroker(ctx, broker); err != nil { - t.Fatalf("failed to create runtime broker: %v", err) - } - - // Create a secret for the broker - secret := &store.BrokerSecret{ - BrokerID: brokerID, - SecretKey: []byte("test-secret"), - Algorithm: store.BrokerSecretAlgorithmHMACSHA256, - Status: store.BrokerSecretStatusActive, - } - if err := s.CreateBrokerSecret(ctx, secret); err != nil { - t.Fatalf("CreateBrokerSecret failed: %v", err) - } - - // Verify secret exists - _, err := s.GetBrokerSecret(ctx, brokerID) - if err != nil { - t.Fatalf("GetBrokerSecret failed: %v", err) - } - - // Delete the runtime broker - if err := s.DeleteRuntimeBroker(ctx, brokerID); err != nil { - t.Fatalf("DeleteRuntimeBroker failed: %v", err) - } - - // Verify secret was cascade deleted - _, err = s.GetBrokerSecret(ctx, brokerID) - if err != store.ErrNotFound { - t.Errorf("Expected secret to be cascade deleted, got: %v", err) - } -} diff --git a/pkg/store/sqlite/gcp_service_account.go b/pkg/store/sqlite/gcp_service_account.go deleted file mode 100644 index 6b9d9f9ed..000000000 --- a/pkg/store/sqlite/gcp_service_account.go +++ /dev/null @@ -1,189 +0,0 @@ -// Copyright 2026 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package sqlite - -import ( - "context" - "database/sql" - "strings" - "time" - - "github.com/GoogleCloudPlatform/scion/pkg/store" -) - -func (s *SQLiteStore) CreateGCPServiceAccount(ctx context.Context, sa *store.GCPServiceAccount) error { - if sa.CreatedAt.IsZero() { - sa.CreatedAt = time.Now() - } - - scopesStr := strings.Join(sa.DefaultScopes, ",") - - _, err := s.db.ExecContext(ctx, ` - INSERT INTO gcp_service_accounts (id, scope, scope_id, email, project_id, display_name, default_scopes, verified, verified_at, created_by, created_at, managed, managed_by) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, - sa.ID, sa.Scope, sa.ScopeID, sa.Email, sa.ProjectID, sa.DisplayName, - scopesStr, sa.Verified, nullableTime(sa.VerifiedAt), sa.CreatedBy, sa.CreatedAt, - sa.Managed, sa.ManagedBy, - ) - if err != nil { - if strings.Contains(err.Error(), "UNIQUE constraint failed") { - return store.ErrAlreadyExists - } - return err - } - return nil -} - -func (s *SQLiteStore) GetGCPServiceAccount(ctx context.Context, id string) (*store.GCPServiceAccount, error) { - var sa store.GCPServiceAccount - var scopesStr string - var verifiedAt sql.NullTime - - err := s.db.QueryRowContext(ctx, ` - SELECT id, scope, scope_id, email, project_id, display_name, default_scopes, verified, verified_at, created_by, created_at, managed, managed_by - FROM gcp_service_accounts WHERE id = ?`, id, - ).Scan(&sa.ID, &sa.Scope, &sa.ScopeID, &sa.Email, &sa.ProjectID, &sa.DisplayName, - &scopesStr, &sa.Verified, &verifiedAt, &sa.CreatedBy, &sa.CreatedAt, - &sa.Managed, &sa.ManagedBy, - ) - if err == sql.ErrNoRows { - return nil, store.ErrNotFound - } - if err != nil { - return nil, err - } - - if scopesStr != "" { - sa.DefaultScopes = strings.Split(scopesStr, ",") - } - if verifiedAt.Valid { - sa.VerifiedAt = verifiedAt.Time - } - - return &sa, nil -} - -func (s *SQLiteStore) UpdateGCPServiceAccount(ctx context.Context, sa *store.GCPServiceAccount) error { - scopesStr := strings.Join(sa.DefaultScopes, ",") - - result, err := s.db.ExecContext(ctx, ` - UPDATE gcp_service_accounts - SET email = ?, project_id = ?, display_name = ?, default_scopes = ?, verified = ?, verified_at = ?, managed = ?, managed_by = ? - WHERE id = ?`, - sa.Email, sa.ProjectID, sa.DisplayName, scopesStr, sa.Verified, nullableTime(sa.VerifiedAt), - sa.Managed, sa.ManagedBy, sa.ID, - ) - if err != nil { - return err - } - rows, _ := result.RowsAffected() - if rows == 0 { - return store.ErrNotFound - } - return nil -} - -func (s *SQLiteStore) DeleteGCPServiceAccount(ctx context.Context, id string) error { - result, err := s.db.ExecContext(ctx, `DELETE FROM gcp_service_accounts WHERE id = ?`, id) - if err != nil { - return err - } - rows, _ := result.RowsAffected() - if rows == 0 { - return store.ErrNotFound - } - return nil -} - -func (s *SQLiteStore) ListGCPServiceAccounts(ctx context.Context, filter store.GCPServiceAccountFilter) ([]store.GCPServiceAccount, error) { - query := `SELECT id, scope, scope_id, email, project_id, display_name, default_scopes, verified, verified_at, created_by, created_at, managed, managed_by FROM gcp_service_accounts WHERE 1=1` - var args []interface{} - - if filter.Scope != "" { - query += ` AND scope = ?` - args = append(args, filter.Scope) - } - if filter.ScopeID != "" { - query += ` AND scope_id = ?` - args = append(args, filter.ScopeID) - } - if filter.Email != "" { - query += ` AND email = ?` - args = append(args, filter.Email) - } - if filter.Managed != nil { - query += ` AND managed = ?` - args = append(args, *filter.Managed) - } - - query += ` ORDER BY created_at DESC` - - rows, err := s.db.QueryContext(ctx, query, args...) - if err != nil { - return nil, err - } - defer rows.Close() - - var results []store.GCPServiceAccount - for rows.Next() { - var sa store.GCPServiceAccount - var scopesStr string - var verifiedAt sql.NullTime - - if err := rows.Scan(&sa.ID, &sa.Scope, &sa.ScopeID, &sa.Email, &sa.ProjectID, &sa.DisplayName, - &scopesStr, &sa.Verified, &verifiedAt, &sa.CreatedBy, &sa.CreatedAt, - &sa.Managed, &sa.ManagedBy, - ); err != nil { - return nil, err - } - - if scopesStr != "" { - sa.DefaultScopes = strings.Split(scopesStr, ",") - } - if verifiedAt.Valid { - sa.VerifiedAt = verifiedAt.Time - } - - results = append(results, sa) - } - - return results, rows.Err() -} - -func (s *SQLiteStore) CountGCPServiceAccounts(ctx context.Context, filter store.GCPServiceAccountFilter) (int, error) { - query := `SELECT COUNT(*) FROM gcp_service_accounts WHERE 1=1` - var args []interface{} - - if filter.Scope != "" { - query += ` AND scope = ?` - args = append(args, filter.Scope) - } - if filter.ScopeID != "" { - query += ` AND scope_id = ?` - args = append(args, filter.ScopeID) - } - if filter.Email != "" { - query += ` AND email = ?` - args = append(args, filter.Email) - } - if filter.Managed != nil { - query += ` AND managed = ?` - args = append(args, *filter.Managed) - } - - var count int - err := s.db.QueryRowContext(ctx, query, args...).Scan(&count) - return count, err -} diff --git a/pkg/store/sqlite/gcp_service_account_test.go b/pkg/store/sqlite/gcp_service_account_test.go deleted file mode 100644 index 3d42cd4e4..000000000 --- a/pkg/store/sqlite/gcp_service_account_test.go +++ /dev/null @@ -1,230 +0,0 @@ -//go:build !no_sqlite - -// Copyright 2026 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package sqlite - -import ( - "context" - "testing" - "time" - - "github.com/GoogleCloudPlatform/scion/pkg/store" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestGCPServiceAccount_CRUD(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - sa := &store.GCPServiceAccount{ - ID: "sa-1", - Scope: store.ScopeProject, - ScopeID: "project-1", - Email: "agent@project.iam.gserviceaccount.com", - ProjectID: "my-project", - DisplayName: "Agent Worker", - DefaultScopes: []string{"https://www.googleapis.com/auth/cloud-platform"}, - CreatedBy: "user-1", - } - - // Create - err := s.CreateGCPServiceAccount(ctx, sa) - require.NoError(t, err) - assert.False(t, sa.CreatedAt.IsZero()) - - // Get - got, err := s.GetGCPServiceAccount(ctx, "sa-1") - require.NoError(t, err) - assert.Equal(t, "agent@project.iam.gserviceaccount.com", got.Email) - assert.Equal(t, "my-project", got.ProjectID) - assert.Equal(t, "Agent Worker", got.DisplayName) - assert.Equal(t, []string{"https://www.googleapis.com/auth/cloud-platform"}, got.DefaultScopes) - assert.False(t, got.Verified) - assert.Equal(t, "user-1", got.CreatedBy) - - // Update (verify) - got.Verified = true - got.VerifiedAt = time.Now() - err = s.UpdateGCPServiceAccount(ctx, got) - require.NoError(t, err) - - got2, err := s.GetGCPServiceAccount(ctx, "sa-1") - require.NoError(t, err) - assert.True(t, got2.Verified) - assert.False(t, got2.VerifiedAt.IsZero()) - - // List - list, err := s.ListGCPServiceAccounts(ctx, store.GCPServiceAccountFilter{ - Scope: store.ScopeProject, - ScopeID: "project-1", - }) - require.NoError(t, err) - assert.Len(t, list, 1) - assert.Equal(t, "sa-1", list[0].ID) - - // List with email filter - list, err = s.ListGCPServiceAccounts(ctx, store.GCPServiceAccountFilter{ - Email: "agent@project.iam.gserviceaccount.com", - }) - require.NoError(t, err) - assert.Len(t, list, 1) - - // List with wrong filter - list, err = s.ListGCPServiceAccounts(ctx, store.GCPServiceAccountFilter{ - ScopeID: "project-999", - Scope: store.ScopeProject, - }) - require.NoError(t, err) - assert.Len(t, list, 0) - - // Delete - err = s.DeleteGCPServiceAccount(ctx, "sa-1") - require.NoError(t, err) - - _, err = s.GetGCPServiceAccount(ctx, "sa-1") - assert.ErrorIs(t, err, store.ErrNotFound) -} - -func TestGCPServiceAccount_DuplicateEmail(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - sa1 := &store.GCPServiceAccount{ - ID: "sa-1", - Scope: store.ScopeProject, - ScopeID: "project-1", - Email: "agent@project.iam.gserviceaccount.com", - ProjectID: "my-project", - CreatedBy: "user-1", - } - err := s.CreateGCPServiceAccount(ctx, sa1) - require.NoError(t, err) - - // Same email, same scope = should fail - sa2 := &store.GCPServiceAccount{ - ID: "sa-2", - Scope: store.ScopeProject, - ScopeID: "project-1", - Email: "agent@project.iam.gserviceaccount.com", - ProjectID: "my-project", - CreatedBy: "user-1", - } - err = s.CreateGCPServiceAccount(ctx, sa2) - assert.ErrorIs(t, err, store.ErrAlreadyExists) - - // Same email, different scope = should succeed - sa3 := &store.GCPServiceAccount{ - ID: "sa-3", - Scope: store.ScopeProject, - ScopeID: "project-2", - Email: "agent@project.iam.gserviceaccount.com", - ProjectID: "my-project", - CreatedBy: "user-1", - } - err = s.CreateGCPServiceAccount(ctx, sa3) - assert.NoError(t, err) -} - -func TestGCPServiceAccount_ManagedFields(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - sa := &store.GCPServiceAccount{ - ID: "sa-managed-1", - Scope: store.ScopeProject, - ScopeID: "project-1", - Email: "scion-abc123@hub-project.iam.gserviceaccount.com", - ProjectID: "hub-project", - Managed: true, - ManagedBy: "hub-instance-1", - CreatedBy: "user-1", - } - - err := s.CreateGCPServiceAccount(ctx, sa) - require.NoError(t, err) - - got, err := s.GetGCPServiceAccount(ctx, "sa-managed-1") - require.NoError(t, err) - assert.True(t, got.Managed) - assert.Equal(t, "hub-instance-1", got.ManagedBy) - - // List with managed filter - managed := true - list, err := s.ListGCPServiceAccounts(ctx, store.GCPServiceAccountFilter{ - Scope: store.ScopeProject, - ScopeID: "project-1", - Managed: &managed, - }) - require.NoError(t, err) - assert.Len(t, list, 1) - assert.True(t, list[0].Managed) - - // Create a non-managed SA - sa2 := &store.GCPServiceAccount{ - ID: "sa-byosa-1", - Scope: store.ScopeProject, - ScopeID: "project-1", - Email: "user-sa@other-project.iam.gserviceaccount.com", - ProjectID: "other-project", - Managed: false, - CreatedBy: "user-1", - } - require.NoError(t, s.CreateGCPServiceAccount(ctx, sa2)) - - // Filter managed=true should return only the managed one - list, err = s.ListGCPServiceAccounts(ctx, store.GCPServiceAccountFilter{ - Scope: store.ScopeProject, - ScopeID: "project-1", - Managed: &managed, - }) - require.NoError(t, err) - assert.Len(t, list, 1) - assert.Equal(t, "sa-managed-1", list[0].ID) - - // Filter managed=false should return only the BYOSA one - notManaged := false - list, err = s.ListGCPServiceAccounts(ctx, store.GCPServiceAccountFilter{ - Scope: store.ScopeProject, - ScopeID: "project-1", - Managed: ¬Managed, - }) - require.NoError(t, err) - assert.Len(t, list, 1) - assert.Equal(t, "sa-byosa-1", list[0].ID) - - // No managed filter should return both - list, err = s.ListGCPServiceAccounts(ctx, store.GCPServiceAccountFilter{ - Scope: store.ScopeProject, - ScopeID: "project-1", - }) - require.NoError(t, err) - assert.Len(t, list, 2) -} - -func TestGCPServiceAccount_NotFound(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - _, err := s.GetGCPServiceAccount(ctx, "nonexistent") - assert.ErrorIs(t, err, store.ErrNotFound) - - err = s.DeleteGCPServiceAccount(ctx, "nonexistent") - assert.ErrorIs(t, err, store.ErrNotFound) - - err = s.UpdateGCPServiceAccount(ctx, &store.GCPServiceAccount{ID: "nonexistent"}) - assert.ErrorIs(t, err, store.ErrNotFound) -} diff --git a/pkg/store/sqlite/github_installation.go b/pkg/store/sqlite/github_installation.go deleted file mode 100644 index 03322ba69..000000000 --- a/pkg/store/sqlite/github_installation.go +++ /dev/null @@ -1,180 +0,0 @@ -// Copyright 2026 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package sqlite - -import ( - "context" - "database/sql" - "time" - - "github.com/GoogleCloudPlatform/scion/pkg/store" -) - -func (s *SQLiteStore) CreateGitHubInstallation(ctx context.Context, installation *store.GitHubInstallation) error { - if installation.CreatedAt.IsZero() { - installation.CreatedAt = time.Now() - } - if installation.UpdatedAt.IsZero() { - installation.UpdatedAt = installation.CreatedAt - } - if installation.Status == "" { - installation.Status = store.GitHubInstallationStatusActive - } - - _, err := s.db.ExecContext(ctx, ` - INSERT OR IGNORE INTO github_installations (installation_id, account_login, account_type, app_id, repositories, status, created_at, updated_at) - VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, - installation.InstallationID, installation.AccountLogin, installation.AccountType, - installation.AppID, marshalJSON(installation.Repositories), - installation.Status, installation.CreatedAt, installation.UpdatedAt, - ) - if err != nil { - return err - } - return nil -} - -func (s *SQLiteStore) GetGitHubInstallation(ctx context.Context, installationID int64) (*store.GitHubInstallation, error) { - var inst store.GitHubInstallation - var repos string - - err := s.db.QueryRowContext(ctx, ` - SELECT installation_id, account_login, account_type, app_id, repositories, status, created_at, updated_at - FROM github_installations WHERE installation_id = ?`, installationID, - ).Scan(&inst.InstallationID, &inst.AccountLogin, &inst.AccountType, - &inst.AppID, &repos, &inst.Status, &inst.CreatedAt, &inst.UpdatedAt, - ) - if err == sql.ErrNoRows { - return nil, store.ErrNotFound - } - if err != nil { - return nil, err - } - - unmarshalJSON(repos, &inst.Repositories) - return &inst, nil -} - -func (s *SQLiteStore) UpdateGitHubInstallation(ctx context.Context, installation *store.GitHubInstallation) error { - installation.UpdatedAt = time.Now() - - result, err := s.db.ExecContext(ctx, ` - UPDATE github_installations SET - account_login = ?, account_type = ?, app_id = ?, - repositories = ?, status = ?, updated_at = ? - WHERE installation_id = ?`, - installation.AccountLogin, installation.AccountType, installation.AppID, - marshalJSON(installation.Repositories), installation.Status, installation.UpdatedAt, - installation.InstallationID, - ) - if err != nil { - return err - } - - rowsAffected, err := result.RowsAffected() - if err != nil { - return err - } - if rowsAffected == 0 { - return store.ErrNotFound - } - return nil -} - -func (s *SQLiteStore) DeleteGitHubInstallation(ctx context.Context, installationID int64) error { - result, err := s.db.ExecContext(ctx, `DELETE FROM github_installations WHERE installation_id = ?`, installationID) - if err != nil { - return err - } - - rowsAffected, err := result.RowsAffected() - if err != nil { - return err - } - if rowsAffected == 0 { - return store.ErrNotFound - } - return nil -} - -func (s *SQLiteStore) GetInstallationForRepository(ctx context.Context, repoFullName string) (*store.GitHubInstallation, error) { - // Search active installations whose repositories JSON array contains the repo. - installations, err := s.ListGitHubInstallations(ctx, store.GitHubInstallationFilter{ - Status: store.GitHubInstallationStatusActive, - }) - if err != nil { - return nil, err - } - - for i := range installations { - for _, repo := range installations[i].Repositories { - if repo == repoFullName { - return &installations[i], nil - } - } - } - return nil, store.ErrNotFound -} - -func (s *SQLiteStore) ListGitHubInstallations(ctx context.Context, filter store.GitHubInstallationFilter) ([]store.GitHubInstallation, error) { - query := "SELECT installation_id, account_login, account_type, app_id, repositories, status, created_at, updated_at FROM github_installations WHERE 1=1" - var args []interface{} - - if filter.AccountLogin != "" { - query += " AND account_login = ?" - args = append(args, filter.AccountLogin) - } - if filter.Status != "" { - query += " AND status = ?" - args = append(args, filter.Status) - } - if filter.AppID != 0 { - query += " AND app_id = ?" - args = append(args, filter.AppID) - } - - query += " ORDER BY created_at ASC" - - rows, err := s.db.QueryContext(ctx, query, args...) - if err != nil { - return nil, err - } - defer rows.Close() - - var results []store.GitHubInstallation - for rows.Next() { - var inst store.GitHubInstallation - var repos string - - if err := rows.Scan(&inst.InstallationID, &inst.AccountLogin, &inst.AccountType, - &inst.AppID, &repos, &inst.Status, &inst.CreatedAt, &inst.UpdatedAt); err != nil { - return nil, err - } - - unmarshalJSON(repos, &inst.Repositories) - results = append(results, inst) - } - - if err := rows.Err(); err != nil { - return nil, err - } - - // Ensure we never return nil slice (return empty slice instead) - if results == nil { - results = []store.GitHubInstallation{} - } - - return results, nil -} diff --git a/pkg/store/sqlite/github_installation_test.go b/pkg/store/sqlite/github_installation_test.go deleted file mode 100644 index 5be418331..000000000 --- a/pkg/store/sqlite/github_installation_test.go +++ /dev/null @@ -1,184 +0,0 @@ -// Copyright 2026 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//go:build !no_sqlite - -package sqlite - -import ( - "context" - "testing" - - "github.com/GoogleCloudPlatform/scion/pkg/store" -) - -func TestGitHubInstallation_CRUD(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - inst := &store.GitHubInstallation{ - InstallationID: 12345, - AccountLogin: "acme-org", - AccountType: "Organization", - AppID: 42, - Repositories: []string{"widgets", "api"}, - Status: store.GitHubInstallationStatusActive, - } - - // Create - if err := s.CreateGitHubInstallation(ctx, inst); err != nil { - t.Fatalf("CreateGitHubInstallation failed: %v", err) - } - - // Get - got, err := s.GetGitHubInstallation(ctx, 12345) - if err != nil { - t.Fatalf("GetGitHubInstallation failed: %v", err) - } - if got.AccountLogin != "acme-org" { - t.Errorf("expected account_login acme-org, got %s", got.AccountLogin) - } - if got.AccountType != "Organization" { - t.Errorf("expected account_type Organization, got %s", got.AccountType) - } - if got.AppID != 42 { - t.Errorf("expected app_id 42, got %d", got.AppID) - } - if len(got.Repositories) != 2 || got.Repositories[0] != "widgets" { - t.Errorf("expected repos [widgets, api], got %v", got.Repositories) - } - if got.Status != store.GitHubInstallationStatusActive { - t.Errorf("expected status active, got %s", got.Status) - } - - // Update - got.Status = store.GitHubInstallationStatusSuspended - got.Repositories = []string{"widgets"} - if err := s.UpdateGitHubInstallation(ctx, got); err != nil { - t.Fatalf("UpdateGitHubInstallation failed: %v", err) - } - - updated, err := s.GetGitHubInstallation(ctx, 12345) - if err != nil { - t.Fatalf("GetGitHubInstallation after update failed: %v", err) - } - if updated.Status != store.GitHubInstallationStatusSuspended { - t.Errorf("expected status suspended, got %s", updated.Status) - } - if len(updated.Repositories) != 1 { - t.Errorf("expected 1 repo, got %d", len(updated.Repositories)) - } - - // Delete - if err := s.DeleteGitHubInstallation(ctx, 12345); err != nil { - t.Fatalf("DeleteGitHubInstallation failed: %v", err) - } - - _, err = s.GetGitHubInstallation(ctx, 12345) - if err != store.ErrNotFound { - t.Errorf("expected ErrNotFound after delete, got %v", err) - } -} - -func TestGitHubInstallation_CreateIdempotent(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - inst := &store.GitHubInstallation{ - InstallationID: 11111, - AccountLogin: "alice", - AccountType: "User", - AppID: 42, - Status: store.GitHubInstallationStatusActive, - } - - if err := s.CreateGitHubInstallation(ctx, inst); err != nil { - t.Fatalf("first create failed: %v", err) - } - - // Second create should be a no-op (INSERT OR IGNORE) - if err := s.CreateGitHubInstallation(ctx, inst); err != nil { - t.Fatalf("second create should not fail: %v", err) - } -} - -func TestGitHubInstallation_List(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - // Create several installations - for i, login := range []string{"org-a", "org-b", "user-c"} { - inst := &store.GitHubInstallation{ - InstallationID: int64(100 + i), - AccountLogin: login, - AccountType: "Organization", - AppID: 42, - Status: store.GitHubInstallationStatusActive, - } - if login == "user-c" { - inst.AccountType = "User" - inst.Status = store.GitHubInstallationStatusSuspended - } - if err := s.CreateGitHubInstallation(ctx, inst); err != nil { - t.Fatalf("CreateGitHubInstallation failed for %s: %v", login, err) - } - } - - // List all - all, err := s.ListGitHubInstallations(ctx, store.GitHubInstallationFilter{}) - if err != nil { - t.Fatalf("ListGitHubInstallations failed: %v", err) - } - if len(all) != 3 { - t.Errorf("expected 3 installations, got %d", len(all)) - } - - // Filter by status - active, err := s.ListGitHubInstallations(ctx, store.GitHubInstallationFilter{Status: "active"}) - if err != nil { - t.Fatalf("ListGitHubInstallations with status filter failed: %v", err) - } - if len(active) != 2 { - t.Errorf("expected 2 active installations, got %d", len(active)) - } - - // Filter by account login - byAccount, err := s.ListGitHubInstallations(ctx, store.GitHubInstallationFilter{AccountLogin: "org-a"}) - if err != nil { - t.Fatalf("ListGitHubInstallations with account filter failed: %v", err) - } - if len(byAccount) != 1 { - t.Errorf("expected 1 installation for org-a, got %d", len(byAccount)) - } -} - -func TestGitHubInstallation_NotFound(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - _, err := s.GetGitHubInstallation(ctx, 99999) - if err != store.ErrNotFound { - t.Errorf("expected ErrNotFound, got %v", err) - } - - err = s.UpdateGitHubInstallation(ctx, &store.GitHubInstallation{InstallationID: 99999}) - if err != store.ErrNotFound { - t.Errorf("expected ErrNotFound on update, got %v", err) - } - - err = s.DeleteGitHubInstallation(ctx, 99999) - if err != store.ErrNotFound { - t.Errorf("expected ErrNotFound on delete, got %v", err) - } -} diff --git a/pkg/store/sqlite/maintenance.go b/pkg/store/sqlite/maintenance.go deleted file mode 100644 index 4d48fdbda..000000000 --- a/pkg/store/sqlite/maintenance.go +++ /dev/null @@ -1,269 +0,0 @@ -// Copyright 2026 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package sqlite - -import ( - "context" - "database/sql" - "time" - - "github.com/GoogleCloudPlatform/scion/pkg/store" -) - -// ============================================================================ -// Maintenance Operation Operations -// ============================================================================ - -// ListMaintenanceOperations returns all registered operations and migrations. -func (s *SQLiteStore) ListMaintenanceOperations(ctx context.Context) ([]store.MaintenanceOperation, error) { - rows, err := s.db.QueryContext(ctx, ` - SELECT id, key, title, description, category, status, - created_at, started_at, completed_at, started_by, result, metadata - FROM maintenance_operations - ORDER BY category, created_at - `) - if err != nil { - return nil, err - } - defer rows.Close() - - var ops []store.MaintenanceOperation - for rows.Next() { - var op store.MaintenanceOperation - var startedAt, completedAt sql.NullTime - var startedBy, result, metadata sql.NullString - - if err := rows.Scan( - &op.ID, &op.Key, &op.Title, &op.Description, &op.Category, &op.Status, - &op.CreatedAt, &startedAt, &completedAt, &startedBy, &result, &metadata, - ); err != nil { - return nil, err - } - - if startedAt.Valid { - op.StartedAt = &startedAt.Time - } - if completedAt.Valid { - op.CompletedAt = &completedAt.Time - } - op.StartedBy = startedBy.String - op.Result = result.String - op.Metadata = metadata.String - - ops = append(ops, op) - } - return ops, rows.Err() -} - -// GetMaintenanceOperation returns a single operation by key. -func (s *SQLiteStore) GetMaintenanceOperation(ctx context.Context, key string) (*store.MaintenanceOperation, error) { - op := &store.MaintenanceOperation{} - var startedAt, completedAt sql.NullTime - var startedBy, result, metadata sql.NullString - - err := s.db.QueryRowContext(ctx, ` - SELECT id, key, title, description, category, status, - created_at, started_at, completed_at, started_by, result, metadata - FROM maintenance_operations WHERE key = ? - `, key).Scan( - &op.ID, &op.Key, &op.Title, &op.Description, &op.Category, &op.Status, - &op.CreatedAt, &startedAt, &completedAt, &startedBy, &result, &metadata, - ) - if err == sql.ErrNoRows { - return nil, store.ErrNotFound - } - if err != nil { - return nil, err - } - - if startedAt.Valid { - op.StartedAt = &startedAt.Time - } - if completedAt.Valid { - op.CompletedAt = &completedAt.Time - } - op.StartedBy = startedBy.String - op.Result = result.String - op.Metadata = metadata.String - - return op, nil -} - -// UpdateMaintenanceOperation updates an operation's status and result fields. -func (s *SQLiteStore) UpdateMaintenanceOperation(ctx context.Context, op *store.MaintenanceOperation) error { - res, err := s.db.ExecContext(ctx, ` - UPDATE maintenance_operations - SET status = ?, started_at = ?, completed_at = ?, started_by = ?, result = ?, metadata = ? - WHERE key = ? - `, - op.Status, - nullableTime(timeFromPtr(op.StartedAt)), - nullableTime(timeFromPtr(op.CompletedAt)), - nullableString(op.StartedBy), - nullableString(op.Result), - nullableString(op.Metadata), - op.Key, - ) - if err != nil { - return err - } - n, _ := res.RowsAffected() - if n == 0 { - return store.ErrNotFound - } - return nil -} - -// CreateMaintenanceRun inserts a new run record. -func (s *SQLiteStore) CreateMaintenanceRun(ctx context.Context, run *store.MaintenanceOperationRun) error { - _, err := s.db.ExecContext(ctx, ` - INSERT INTO maintenance_operation_runs ( - id, operation_key, status, started_at, completed_at, started_by, result, log - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?) - `, - run.ID, run.OperationKey, run.Status, run.StartedAt, - nullableTime(timeFromPtr(run.CompletedAt)), - nullableString(run.StartedBy), - nullableString(run.Result), - run.Log, - ) - return err -} - -// UpdateMaintenanceRun updates a run's status, result, and log. -func (s *SQLiteStore) UpdateMaintenanceRun(ctx context.Context, run *store.MaintenanceOperationRun) error { - res, err := s.db.ExecContext(ctx, ` - UPDATE maintenance_operation_runs - SET status = ?, completed_at = ?, result = ?, log = ? - WHERE id = ? - `, - run.Status, - nullableTime(timeFromPtr(run.CompletedAt)), - nullableString(run.Result), - run.Log, - run.ID, - ) - if err != nil { - return err - } - n, _ := res.RowsAffected() - if n == 0 { - return store.ErrNotFound - } - return nil -} - -// GetMaintenanceRun returns a single run by ID. -func (s *SQLiteStore) GetMaintenanceRun(ctx context.Context, id string) (*store.MaintenanceOperationRun, error) { - run := &store.MaintenanceOperationRun{} - var completedAt sql.NullTime - var startedBy, result sql.NullString - - err := s.db.QueryRowContext(ctx, ` - SELECT id, operation_key, status, started_at, completed_at, started_by, result, log - FROM maintenance_operation_runs WHERE id = ? - `, id).Scan( - &run.ID, &run.OperationKey, &run.Status, &run.StartedAt, - &completedAt, &startedBy, &result, &run.Log, - ) - if err == sql.ErrNoRows { - return nil, store.ErrNotFound - } - if err != nil { - return nil, err - } - - if completedAt.Valid { - run.CompletedAt = &completedAt.Time - } - run.StartedBy = startedBy.String - run.Result = result.String - - return run, nil -} - -// AbortRunningMaintenanceOps transitions any "running" operation runs and -// migrations to "failed" with an appropriate result message. This is called at -// server startup to clean up operations interrupted by a restart. -func (s *SQLiteStore) AbortRunningMaintenanceOps(ctx context.Context) (int64, int64, error) { - now := sql.NullTime{Time: time.Now(), Valid: true} - result := `{"error":"aborted: server was restarted while operation was running"}` - - // Abort stalled runs. - res, err := s.db.ExecContext(ctx, ` - UPDATE maintenance_operation_runs - SET status = 'failed', completed_at = ?, result = ? - WHERE status = 'running' - `, now, result) - if err != nil { - return 0, 0, err - } - runs, _ := res.RowsAffected() - - // Reset stalled migrations back to pending (they can be retried). - res, err = s.db.ExecContext(ctx, ` - UPDATE maintenance_operations - SET status = 'pending', started_at = NULL, completed_at = NULL, result = ? - WHERE status = 'running' AND category = 'migration' - `, result) - if err != nil { - return runs, 0, err - } - migrations, _ := res.RowsAffected() - - return runs, migrations, nil -} - -// ListMaintenanceRuns returns runs for a given operation key, ordered by started_at DESC. -func (s *SQLiteStore) ListMaintenanceRuns(ctx context.Context, operationKey string, limit int) ([]store.MaintenanceOperationRun, error) { - if limit <= 0 { - limit = 20 - } - - rows, err := s.db.QueryContext(ctx, ` - SELECT id, operation_key, status, started_at, completed_at, started_by, result, log - FROM maintenance_operation_runs - WHERE operation_key = ? - ORDER BY started_at DESC - LIMIT ? - `, operationKey, limit) - if err != nil { - return nil, err - } - defer rows.Close() - - var runs []store.MaintenanceOperationRun - for rows.Next() { - var run store.MaintenanceOperationRun - var completedAt sql.NullTime - var startedBy, result sql.NullString - - if err := rows.Scan( - &run.ID, &run.OperationKey, &run.Status, &run.StartedAt, - &completedAt, &startedBy, &result, &run.Log, - ); err != nil { - return nil, err - } - - if completedAt.Valid { - run.CompletedAt = &completedAt.Time - } - run.StartedBy = startedBy.String - run.Result = result.String - - runs = append(runs, run) - } - return runs, rows.Err() -} diff --git a/pkg/store/sqlite/maintenance_test.go b/pkg/store/sqlite/maintenance_test.go deleted file mode 100644 index 87d2a25dd..000000000 --- a/pkg/store/sqlite/maintenance_test.go +++ /dev/null @@ -1,209 +0,0 @@ -// Copyright 2026 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//go:build !no_sqlite - -package sqlite - -import ( - "context" - "testing" - "time" - - "github.com/GoogleCloudPlatform/scion/pkg/api" - "github.com/GoogleCloudPlatform/scion/pkg/store" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestMaintenanceOperationsSeeded(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - ops, err := s.ListMaintenanceOperations(ctx) - require.NoError(t, err) - require.Len(t, ops, 5, "expected 5 seeded operations (1 migration + 4 operations)") - - // Verify categories - var migrations, operations int - for _, op := range ops { - switch op.Category { - case store.MaintenanceCategoryMigration: - migrations++ - case store.MaintenanceCategoryOperation: - operations++ - } - } - assert.Equal(t, 1, migrations, "expected 1 migration") - assert.Equal(t, 4, operations, "expected 4 operations") -} - -func TestMaintenanceGetOperationByKey(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - op, err := s.GetMaintenanceOperation(ctx, "secret-hub-id-migration") - require.NoError(t, err) - assert.Equal(t, "Secret Hub ID Namespace Migration", op.Title) - assert.Equal(t, store.MaintenanceCategoryMigration, op.Category) - assert.Equal(t, store.MaintenanceStatusPending, op.Status) - - // Not found - _, err = s.GetMaintenanceOperation(ctx, "nonexistent") - assert.ErrorIs(t, err, store.ErrNotFound) -} - -func TestMaintenanceUpdateOperation(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - op, err := s.GetMaintenanceOperation(ctx, "secret-hub-id-migration") - require.NoError(t, err) - - now := time.Now().UTC().Truncate(time.Second) - op.Status = store.MaintenanceStatusCompleted - op.StartedAt = &now - op.CompletedAt = &now - op.StartedBy = "admin-user" - op.Result = `{"migrated": 5}` - - err = s.UpdateMaintenanceOperation(ctx, op) - require.NoError(t, err) - - updated, err := s.GetMaintenanceOperation(ctx, "secret-hub-id-migration") - require.NoError(t, err) - assert.Equal(t, store.MaintenanceStatusCompleted, updated.Status) - assert.Equal(t, "admin-user", updated.StartedBy) - assert.Equal(t, `{"migrated": 5}`, updated.Result) -} - -func TestMaintenanceRunCRUD(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - runID := api.NewUUID() - now := time.Now().UTC().Truncate(time.Second) - - run := &store.MaintenanceOperationRun{ - ID: runID, - OperationKey: "pull-images", - Status: store.MaintenanceStatusRunning, - StartedAt: now, - StartedBy: "admin-user", - Log: "Pulling images...", - } - - err := s.CreateMaintenanceRun(ctx, run) - require.NoError(t, err) - - // Get - got, err := s.GetMaintenanceRun(ctx, runID) - require.NoError(t, err) - assert.Equal(t, "pull-images", got.OperationKey) - assert.Equal(t, store.MaintenanceStatusRunning, got.Status) - assert.Equal(t, "Pulling images...", got.Log) - - // Update - completedAt := now.Add(30 * time.Second) - got.Status = store.MaintenanceStatusCompleted - got.CompletedAt = &completedAt - got.Result = `{"pulled": 3}` - got.Log = "Pulling images...\nDone." - - err = s.UpdateMaintenanceRun(ctx, got) - require.NoError(t, err) - - updated, err := s.GetMaintenanceRun(ctx, runID) - require.NoError(t, err) - assert.Equal(t, store.MaintenanceStatusCompleted, updated.Status) - assert.Equal(t, `{"pulled": 3}`, updated.Result) - - // List - runs, err := s.ListMaintenanceRuns(ctx, "pull-images", 10) - require.NoError(t, err) - require.Len(t, runs, 1) - assert.Equal(t, runID, runs[0].ID) - - // Not found - _, err = s.GetMaintenanceRun(ctx, "nonexistent") - assert.ErrorIs(t, err, store.ErrNotFound) -} - -func TestAbortRunningMaintenanceOps(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - // Set a migration to "running" to simulate an interrupted migration. - op, err := s.GetMaintenanceOperation(ctx, "secret-hub-id-migration") - require.NoError(t, err) - now := time.Now() - op.Status = store.MaintenanceStatusRunning - op.StartedAt = &now - op.StartedBy = "admin@example.com" - require.NoError(t, s.UpdateMaintenanceOperation(ctx, op)) - - // Create two "running" operation runs to simulate interrupted operations. - for i, key := range []string{"pull-images", "rebuild-server"} { - run := &store.MaintenanceOperationRun{ - ID: api.NewUUID(), - OperationKey: key, - Status: store.MaintenanceStatusRunning, - StartedAt: now.Add(time.Duration(i) * time.Second), - StartedBy: "admin@example.com", - } - require.NoError(t, s.CreateMaintenanceRun(ctx, run)) - } - - // Create a completed run that should NOT be affected. - completed := now.Add(-time.Hour) - completedRun := &store.MaintenanceOperationRun{ - ID: api.NewUUID(), - OperationKey: "pull-images", - Status: store.MaintenanceStatusCompleted, - StartedAt: completed, - CompletedAt: &completed, - StartedBy: "admin@example.com", - } - require.NoError(t, s.CreateMaintenanceRun(ctx, completedRun)) - - // Abort all running operations. - runs, migrations, err := s.AbortRunningMaintenanceOps(ctx) - require.NoError(t, err) - assert.Equal(t, int64(2), runs, "expected 2 stalled runs aborted") - assert.Equal(t, int64(1), migrations, "expected 1 stalled migration reset") - - // Verify the migration was reset to pending. - op, err = s.GetMaintenanceOperation(ctx, "secret-hub-id-migration") - require.NoError(t, err) - assert.Equal(t, store.MaintenanceStatusPending, op.Status) - assert.Nil(t, op.StartedAt) - - // Verify running runs were marked as failed. - allRuns, err := s.ListMaintenanceRuns(ctx, "pull-images", 10) - require.NoError(t, err) - for _, r := range allRuns { - if r.ID == completedRun.ID { - assert.Equal(t, store.MaintenanceStatusCompleted, r.Status, "completed run should be unchanged") - } else { - assert.Equal(t, store.MaintenanceStatusFailed, r.Status, "running run should be failed") - assert.NotNil(t, r.CompletedAt, "aborted run should have completedAt") - } - } - - // Running it again should be a no-op. - runs, migrations, err = s.AbortRunningMaintenanceOps(ctx) - require.NoError(t, err) - assert.Equal(t, int64(0), runs) - assert.Equal(t, int64(0), migrations) -} diff --git a/pkg/store/sqlite/messages.go b/pkg/store/sqlite/messages.go deleted file mode 100644 index 18dcf34ba..000000000 --- a/pkg/store/sqlite/messages.go +++ /dev/null @@ -1,226 +0,0 @@ -// Copyright 2026 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package sqlite provides a SQLite implementation of the Store interface. -package sqlite - -import ( - "context" - "database/sql" - "errors" - "fmt" - "strings" - "time" - - "github.com/GoogleCloudPlatform/scion/pkg/store" -) - -// ============================================================================ -// Message Operations -// ============================================================================ - -// CreateMessage persists a new message. -func (s *SQLiteStore) CreateMessage(ctx context.Context, msg *store.Message) error { - if msg.ID == "" || msg.ProjectID == "" || msg.Msg == "" { - return store.ErrInvalidInput - } - if msg.CreatedAt.IsZero() { - msg.CreatedAt = time.Now() - } - - _, err := s.db.ExecContext(ctx, ` - INSERT INTO messages ( - id, project_id, sender, sender_id, recipient, recipient_id, - msg, type, urgent, broadcasted, read, agent_id, group_id, created_at - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - `, - msg.ID, msg.ProjectID, msg.Sender, msg.SenderID, msg.Recipient, msg.RecipientID, - msg.Msg, msg.Type, - boolToInt(msg.Urgent), boolToInt(msg.Broadcasted), boolToInt(msg.Read), - msg.AgentID, msg.GroupID, msg.CreatedAt, - ) - if err != nil { - if strings.Contains(err.Error(), "UNIQUE constraint failed") { - return store.ErrAlreadyExists - } - return err - } - return nil -} - -// GetMessage returns a single message by ID. -func (s *SQLiteStore) GetMessage(ctx context.Context, id string) (*store.Message, error) { - row := s.db.QueryRowContext(ctx, ` - SELECT id, project_id, sender, sender_id, recipient, recipient_id, - msg, type, urgent, broadcasted, read, agent_id, group_id, created_at - FROM messages - WHERE id = ? - `, id) - - var msg store.Message - var urgent, broadcasted, read int - if err := row.Scan( - &msg.ID, &msg.ProjectID, &msg.Sender, &msg.SenderID, &msg.Recipient, &msg.RecipientID, - &msg.Msg, &msg.Type, &urgent, &broadcasted, &read, - &msg.AgentID, &msg.GroupID, &msg.CreatedAt, - ); err != nil { - if errors.Is(err, sql.ErrNoRows) { - return nil, store.ErrNotFound - } - return nil, err - } - msg.Urgent = urgent != 0 - msg.Broadcasted = broadcasted != 0 - msg.Read = read != 0 - return &msg, nil -} - -// ListMessages returns messages matching the given filter, ordered by created_at DESC. -func (s *SQLiteStore) ListMessages(ctx context.Context, filter store.MessageFilter, opts store.ListOptions) (*store.ListResult[store.Message], error) { - var conditions []string - var args []interface{} - - if filter.ProjectID != "" { - conditions = append(conditions, "project_id = ?") - args = append(args, filter.ProjectID) - } - if filter.AgentID != "" { - conditions = append(conditions, "agent_id = ?") - args = append(args, filter.AgentID) - } - if filter.RecipientID != "" { - conditions = append(conditions, "recipient_id = ?") - args = append(args, filter.RecipientID) - } - if filter.SenderID != "" { - conditions = append(conditions, "sender_id = ?") - args = append(args, filter.SenderID) - } - if filter.ParticipantID != "" { - conditions = append(conditions, "(recipient_id = ? OR sender_id = ?)") - args = append(args, filter.ParticipantID, filter.ParticipantID) - } - if filter.OnlyUnread { - conditions = append(conditions, "read = 0") - } - if filter.Type != "" { - conditions = append(conditions, "type = ?") - args = append(args, filter.Type) - } - - whereClause := "" - if len(conditions) > 0 { - whereClause = "WHERE " + strings.Join(conditions, " AND ") - } - - var totalCount int - countQuery := fmt.Sprintf("SELECT COUNT(*) FROM messages %s", whereClause) - if err := s.db.QueryRowContext(ctx, countQuery, args...).Scan(&totalCount); err != nil { - return nil, err - } - - limit := opts.Limit - if limit <= 0 { - limit = 50 - } - if limit > 200 { - limit = 200 - } - - query := fmt.Sprintf(` - SELECT id, project_id, sender, sender_id, recipient, recipient_id, - msg, type, urgent, broadcasted, read, agent_id, group_id, created_at - FROM messages %s ORDER BY created_at DESC LIMIT ? - `, whereClause) - args = append(args, limit+1) - - rows, err := s.db.QueryContext(ctx, query, args...) - if err != nil { - return nil, err - } - defer rows.Close() - - var msgs []store.Message - for rows.Next() { - var msg store.Message - var urgent, broadcasted, read int - if err := rows.Scan( - &msg.ID, &msg.ProjectID, &msg.Sender, &msg.SenderID, &msg.Recipient, &msg.RecipientID, - &msg.Msg, &msg.Type, &urgent, &broadcasted, &read, - &msg.AgentID, &msg.GroupID, &msg.CreatedAt, - ); err != nil { - return nil, err - } - msg.Urgent = urgent != 0 - msg.Broadcasted = broadcasted != 0 - msg.Read = read != 0 - msgs = append(msgs, msg) - } - if err := rows.Err(); err != nil { - return nil, err - } - - result := &store.ListResult[store.Message]{ - Items: msgs, - TotalCount: totalCount, - } - if len(msgs) > limit { - result.Items = msgs[:limit] - result.NextCursor = msgs[limit-1].ID - } - return result, nil -} - -// MarkMessageRead marks a message as read. -func (s *SQLiteStore) MarkMessageRead(ctx context.Context, id string) error { - result, err := s.db.ExecContext(ctx, ` - UPDATE messages SET read = 1 WHERE id = ? - `, id) - if err != nil { - return err - } - n, err := result.RowsAffected() - if err != nil { - return err - } - if n == 0 { - return store.ErrNotFound - } - return nil -} - -// MarkAllMessagesRead marks all messages for a recipient as read. -func (s *SQLiteStore) MarkAllMessagesRead(ctx context.Context, recipientID string) error { - _, err := s.db.ExecContext(ctx, ` - UPDATE messages SET read = 1 WHERE recipient_id = ? - `, recipientID) - return err -} - -// PurgeOldMessages removes read messages older than readCutoff and unread messages -// older than unreadCutoff. Returns the number of messages removed. -func (s *SQLiteStore) PurgeOldMessages(ctx context.Context, readCutoff time.Time, unreadCutoff time.Time) (int, error) { - result, err := s.db.ExecContext(ctx, ` - DELETE FROM messages - WHERE (read = 1 AND created_at < ?) OR (read = 0 AND created_at < ?) - `, readCutoff, unreadCutoff) - if err != nil { - return 0, err - } - n, err := result.RowsAffected() - if err != nil { - return 0, err - } - return int(n), nil -} diff --git a/pkg/store/sqlite/messages_test.go b/pkg/store/sqlite/messages_test.go deleted file mode 100644 index 57dbd73f1..000000000 --- a/pkg/store/sqlite/messages_test.go +++ /dev/null @@ -1,251 +0,0 @@ -// Copyright 2026 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//go:build !no_sqlite - -package sqlite - -import ( - "context" - "testing" - "time" - - "github.com/GoogleCloudPlatform/scion/pkg/api" - "github.com/GoogleCloudPlatform/scion/pkg/store" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func newTestMessage(projectID, agentID string) *store.Message { - return &store.Message{ - ID: api.NewUUID(), - ProjectID: projectID, - Sender: "user:alice", - SenderID: "user-uuid-alice", - Recipient: "agent:coder", - RecipientID: agentID, - Msg: "Please fix the auth module.", - Type: "instruction", - AgentID: agentID, - CreatedAt: time.Now().UTC().Truncate(time.Second), - } -} - -func TestMessageCRUD(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - projectID, agentID := createTestProjectAndAgent(t, s) - msg := newTestMessage(projectID, agentID) - - // Create - require.NoError(t, s.CreateMessage(ctx, msg)) - - // Get - got, err := s.GetMessage(ctx, msg.ID) - require.NoError(t, err) - assert.Equal(t, msg.ID, got.ID) - assert.Equal(t, msg.ProjectID, got.ProjectID) - assert.Equal(t, msg.Sender, got.Sender) - assert.Equal(t, msg.Recipient, got.Recipient) - assert.Equal(t, msg.Msg, got.Msg) - assert.Equal(t, msg.Type, got.Type) - assert.Equal(t, msg.AgentID, got.AgentID) - assert.False(t, got.Read) - - // Duplicate create returns ErrAlreadyExists - err = s.CreateMessage(ctx, msg) - assert.ErrorIs(t, err, store.ErrAlreadyExists) - - // Not found - _, err = s.GetMessage(ctx, "nonexistent") - assert.ErrorIs(t, err, store.ErrNotFound) -} - -func TestMessageMarkRead(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - projectID, agentID := createTestProjectAndAgent(t, s) - msg := newTestMessage(projectID, agentID) - require.NoError(t, s.CreateMessage(ctx, msg)) - - // Mark single message as read - require.NoError(t, s.MarkMessageRead(ctx, msg.ID)) - got, err := s.GetMessage(ctx, msg.ID) - require.NoError(t, err) - assert.True(t, got.Read) - - // Mark not-found returns ErrNotFound - assert.ErrorIs(t, s.MarkMessageRead(ctx, "nonexistent"), store.ErrNotFound) -} - -func TestMessageMarkAllRead(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - projectID, agentID := createTestProjectAndAgent(t, s) - - // Create two messages for the same recipient - recipientID := agentID - msg1 := newTestMessage(projectID, agentID) - msg1.RecipientID = recipientID - msg2 := newTestMessage(projectID, agentID) - msg2.ID = api.NewUUID() - msg2.RecipientID = recipientID - require.NoError(t, s.CreateMessage(ctx, msg1)) - require.NoError(t, s.CreateMessage(ctx, msg2)) - - require.NoError(t, s.MarkAllMessagesRead(ctx, recipientID)) - - got1, err := s.GetMessage(ctx, msg1.ID) - require.NoError(t, err) - assert.True(t, got1.Read) - - got2, err := s.GetMessage(ctx, msg2.ID) - require.NoError(t, err) - assert.True(t, got2.Read) -} - -func TestListMessages(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - projectID, agentID := createTestProjectAndAgent(t, s) - - // Create unread message - unread := newTestMessage(projectID, agentID) - require.NoError(t, s.CreateMessage(ctx, unread)) - - // Create read message - read := newTestMessage(projectID, agentID) - read.ID = api.NewUUID() - require.NoError(t, s.CreateMessage(ctx, read)) - require.NoError(t, s.MarkMessageRead(ctx, read.ID)) - - // List all - result, err := s.ListMessages(ctx, store.MessageFilter{ProjectID: projectID}, store.ListOptions{}) - require.NoError(t, err) - assert.Equal(t, 2, result.TotalCount) - assert.Len(t, result.Items, 2) - - // List unread only - result, err = s.ListMessages(ctx, store.MessageFilter{ProjectID: projectID, OnlyUnread: true}, store.ListOptions{}) - require.NoError(t, err) - assert.Equal(t, 1, result.TotalCount) - assert.Len(t, result.Items, 1) - assert.Equal(t, unread.ID, result.Items[0].ID) - - // Filter by agent - result, err = s.ListMessages(ctx, store.MessageFilter{AgentID: agentID}, store.ListOptions{}) - require.NoError(t, err) - assert.Equal(t, 2, result.TotalCount) - - // Filter by type - result, err = s.ListMessages(ctx, store.MessageFilter{Type: "instruction"}, store.ListOptions{}) - require.NoError(t, err) - assert.Equal(t, 2, result.TotalCount) - - result, err = s.ListMessages(ctx, store.MessageFilter{Type: "input-needed"}, store.ListOptions{}) - require.NoError(t, err) - assert.Equal(t, 0, result.TotalCount) -} - -func TestListMessages_ParticipantID(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - projectID, agentID := createTestProjectAndAgent(t, s) - userID := "user-uuid-alice" - - // Inbound: user → agent. Sender=user, recipient=agent. - inbound := newTestMessage(projectID, agentID) - inbound.SenderID = userID - inbound.RecipientID = agentID - require.NoError(t, s.CreateMessage(ctx, inbound)) - - // Outbound: agent → user. Sender=agent, recipient=user. - outbound := &store.Message{ - ID: api.NewUUID(), - ProjectID: projectID, - Sender: "agent:coder", - SenderID: agentID, - Recipient: "user:alice", - RecipientID: userID, - Msg: "Done — here's the patch.", - Type: "assistant-reply", - AgentID: agentID, - CreatedAt: time.Now().UTC().Truncate(time.Second), - } - require.NoError(t, s.CreateMessage(ctx, outbound)) - - // Unrelated message in the same project/agent with a different user. - other := &store.Message{ - ID: api.NewUUID(), - ProjectID: projectID, - Sender: "user:bob", - SenderID: "user-uuid-bob", - Recipient: "agent:coder", - RecipientID: agentID, - Msg: "Bob's message", - Type: "instruction", - AgentID: agentID, - CreatedAt: time.Now().UTC().Truncate(time.Second), - } - require.NoError(t, s.CreateMessage(ctx, other)) - - // ParticipantID + AgentID returns both sides of the alice↔agent chat - // but not bob's message. - result, err := s.ListMessages(ctx, store.MessageFilter{ - AgentID: agentID, - ParticipantID: userID, - }, store.ListOptions{}) - require.NoError(t, err) - assert.Equal(t, 2, result.TotalCount) - gotIDs := map[string]bool{} - for _, m := range result.Items { - gotIDs[m.ID] = true - } - assert.True(t, gotIDs[inbound.ID], "inbound (user→agent) should match") - assert.True(t, gotIDs[outbound.ID], "outbound (agent→user) should match") - assert.False(t, gotIDs[other.ID], "bob's message should not match alice's participant filter") -} - -func TestPurgeOldMessages(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - projectID, agentID := createTestProjectAndAgent(t, s) - - old := newTestMessage(projectID, agentID) - old.CreatedAt = time.Now().Add(-40 * 24 * time.Hour) - require.NoError(t, s.CreateMessage(ctx, old)) - require.NoError(t, s.MarkMessageRead(ctx, old.ID)) - - recent := newTestMessage(projectID, agentID) - recent.ID = api.NewUUID() - require.NoError(t, s.CreateMessage(ctx, recent)) - - readCutoff := time.Now().Add(-30 * 24 * time.Hour) - unreadCutoff := time.Now().Add(-90 * 24 * time.Hour) - n, err := s.PurgeOldMessages(ctx, readCutoff, unreadCutoff) - require.NoError(t, err) - assert.Equal(t, 1, n) - - _, err = s.GetMessage(ctx, old.ID) - assert.ErrorIs(t, err, store.ErrNotFound) - - _, err = s.GetMessage(ctx, recent.ID) - assert.NoError(t, err) -} diff --git a/pkg/store/sqlite/notification.go b/pkg/store/sqlite/notification.go deleted file mode 100644 index 995b1cef6..000000000 --- a/pkg/store/sqlite/notification.go +++ /dev/null @@ -1,553 +0,0 @@ -// Copyright 2026 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package sqlite provides a SQLite implementation of the Store interface. -package sqlite - -import ( - "context" - "database/sql" - "errors" - "fmt" - "strings" - "time" - - "github.com/GoogleCloudPlatform/scion/pkg/store" -) - -// ============================================================================ -// Notification Subscription Operations -// ============================================================================ - -// CreateNotificationSubscription creates a new notification subscription. -func (s *SQLiteStore) CreateNotificationSubscription(ctx context.Context, sub *store.NotificationSubscription) error { - if sub.ID == "" || sub.SubscriberID == "" || sub.ProjectID == "" { - return store.ErrInvalidInput - } - - // Default scope to agent for backward compatibility - if sub.Scope == "" { - sub.Scope = store.SubscriptionScopeAgent - } - - // Validate scope-specific constraints - switch sub.Scope { - case store.SubscriptionScopeAgent: - if sub.AgentID == "" { - return store.ErrInvalidInput - } - case store.SubscriptionScopeProject: - sub.AgentID = "" // Ensure no agent_id for project-scoped - default: - return fmt.Errorf("invalid scope %q: %w", sub.Scope, store.ErrInvalidInput) - } - - now := time.Now() - if sub.CreatedAt.IsZero() { - sub.CreatedAt = now - } - - _, err := s.db.ExecContext(ctx, ` - INSERT INTO notification_subscriptions ( - id, scope, agent_id, subscriber_type, subscriber_id, project_id, - trigger_activities, created_at, created_by - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) - `, - sub.ID, sub.Scope, nullableString(sub.AgentID), sub.SubscriberType, sub.SubscriberID, sub.ProjectID, - marshalJSON(sub.TriggerActivities), sub.CreatedAt, sub.CreatedBy, - ) - if err != nil { - if strings.Contains(err.Error(), "UNIQUE constraint failed") { - return store.ErrAlreadyExists - } - if strings.Contains(err.Error(), "FOREIGN KEY constraint failed") { - return fmt.Errorf("agent %s does not exist: %w", sub.AgentID, store.ErrInvalidInput) - } - return err - } - return nil -} - -// GetNotificationSubscription returns a single subscription by ID. -func (s *SQLiteStore) GetNotificationSubscription(ctx context.Context, id string) (*store.NotificationSubscription, error) { - row := s.db.QueryRowContext(ctx, ` - SELECT id, scope, agent_id, subscriber_type, subscriber_id, project_id, - trigger_activities, created_at, created_by - FROM notification_subscriptions - WHERE id = ? - `, id) - - var sub store.NotificationSubscription - var agentID sql.NullString - var triggerActivitiesJSON string - - if err := row.Scan( - &sub.ID, &sub.Scope, &agentID, &sub.SubscriberType, &sub.SubscriberID, &sub.ProjectID, - &triggerActivitiesJSON, &sub.CreatedAt, &sub.CreatedBy, - ); err != nil { - if errors.Is(err, sql.ErrNoRows) { - return nil, store.ErrNotFound - } - return nil, err - } - - if agentID.Valid { - sub.AgentID = agentID.String - } - unmarshalJSON(triggerActivitiesJSON, &sub.TriggerActivities) - return &sub, nil -} - -// GetNotificationSubscriptions returns all agent-scoped subscriptions for a watched agent. -func (s *SQLiteStore) GetNotificationSubscriptions(ctx context.Context, agentID string) ([]store.NotificationSubscription, error) { - rows, err := s.db.QueryContext(ctx, ` - SELECT id, scope, agent_id, subscriber_type, subscriber_id, project_id, - trigger_activities, created_at, created_by - FROM notification_subscriptions - WHERE agent_id = ? - ORDER BY created_at ASC - `, agentID) - if err != nil { - return nil, err - } - defer rows.Close() - - return scanSubscriptions(rows) -} - -// GetNotificationSubscriptionsByProject returns all subscriptions within a project (any scope). -func (s *SQLiteStore) GetNotificationSubscriptionsByProject(ctx context.Context, projectID string) ([]store.NotificationSubscription, error) { - rows, err := s.db.QueryContext(ctx, ` - SELECT id, scope, agent_id, subscriber_type, subscriber_id, project_id, - trigger_activities, created_at, created_by - FROM notification_subscriptions - WHERE project_id = ? - ORDER BY created_at ASC - `, projectID) - if err != nil { - return nil, err - } - defer rows.Close() - - return scanSubscriptions(rows) -} - -// GetNotificationSubscriptionsByProjectScope returns project-scoped subscriptions -// (scope='project') for a given project. -func (s *SQLiteStore) GetNotificationSubscriptionsByProjectScope(ctx context.Context, projectID string) ([]store.NotificationSubscription, error) { - rows, err := s.db.QueryContext(ctx, ` - SELECT id, scope, agent_id, subscriber_type, subscriber_id, project_id, - trigger_activities, created_at, created_by - FROM notification_subscriptions - WHERE project_id = ? AND scope = 'project' - ORDER BY created_at ASC - `, projectID) - if err != nil { - return nil, err - } - defer rows.Close() - - return scanSubscriptions(rows) -} - -// GetSubscriptionsForSubscriber returns all subscriptions owned by a subscriber. -func (s *SQLiteStore) GetSubscriptionsForSubscriber(ctx context.Context, subscriberType, subscriberID string) ([]store.NotificationSubscription, error) { - rows, err := s.db.QueryContext(ctx, ` - SELECT id, scope, agent_id, subscriber_type, subscriber_id, project_id, - trigger_activities, created_at, created_by - FROM notification_subscriptions - WHERE subscriber_type = ? AND subscriber_id = ? - ORDER BY created_at ASC - `, subscriberType, subscriberID) - if err != nil { - return nil, err - } - defer rows.Close() - - return scanSubscriptions(rows) -} - -// UpdateNotificationSubscriptionTriggers updates the trigger activities of a subscription. -func (s *SQLiteStore) UpdateNotificationSubscriptionTriggers(ctx context.Context, id string, triggerActivities []string) error { - if id == "" || len(triggerActivities) == 0 { - return store.ErrInvalidInput - } - - result, err := s.db.ExecContext(ctx, ` - UPDATE notification_subscriptions SET trigger_activities = ? WHERE id = ? - `, marshalJSON(triggerActivities), id) - if err != nil { - return err - } - - rows, err := result.RowsAffected() - if err != nil { - return err - } - if rows == 0 { - return store.ErrNotFound - } - return nil -} - -// DeleteNotificationSubscription deletes a subscription by ID. -func (s *SQLiteStore) DeleteNotificationSubscription(ctx context.Context, id string) error { - result, err := s.db.ExecContext(ctx, ` - DELETE FROM notification_subscriptions WHERE id = ? - `, id) - if err != nil { - return err - } - - rows, err := result.RowsAffected() - if err != nil { - return err - } - if rows == 0 { - return store.ErrNotFound - } - return nil -} - -// DeleteNotificationSubscriptionsForAgent deletes all subscriptions for a watched agent. -// No error on zero rows affected. -func (s *SQLiteStore) DeleteNotificationSubscriptionsForAgent(ctx context.Context, agentID string) error { - _, err := s.db.ExecContext(ctx, ` - DELETE FROM notification_subscriptions WHERE agent_id = ? - `, agentID) - return err -} - -// ============================================================================ -// Notification Operations -// ============================================================================ - -// CreateNotification creates a new notification record. -func (s *SQLiteStore) CreateNotification(ctx context.Context, notif *store.Notification) error { - if notif.ID == "" || notif.SubscriptionID == "" || notif.AgentID == "" { - return store.ErrInvalidInput - } - - now := time.Now() - if notif.CreatedAt.IsZero() { - notif.CreatedAt = now - } - - _, err := s.db.ExecContext(ctx, ` - INSERT INTO notifications ( - id, subscription_id, agent_id, project_id, - subscriber_type, subscriber_id, - status, message, dispatched, acknowledged, created_at - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - `, - notif.ID, notif.SubscriptionID, notif.AgentID, notif.ProjectID, - notif.SubscriberType, notif.SubscriberID, - notif.Status, notif.Message, - boolToInt(notif.Dispatched), boolToInt(notif.Acknowledged), - notif.CreatedAt, - ) - if err != nil { - if strings.Contains(err.Error(), "FOREIGN KEY constraint failed") { - return fmt.Errorf("subscription %s does not exist: %w", notif.SubscriptionID, store.ErrInvalidInput) - } - return err - } - return nil -} - -// GetNotifications returns notifications for a subscriber. -// If onlyUnacknowledged is true, only unacknowledged notifications are returned. -// Results are ordered by created_at DESC. -func (s *SQLiteStore) GetNotifications(ctx context.Context, subscriberType, subscriberID string, onlyUnacknowledged bool) ([]store.Notification, error) { - query := ` - SELECT id, subscription_id, agent_id, project_id, - subscriber_type, subscriber_id, - status, message, dispatched, acknowledged, created_at - FROM notifications - WHERE subscriber_type = ? AND subscriber_id = ? - ` - args := []interface{}{subscriberType, subscriberID} - - if onlyUnacknowledged { - query += ` AND acknowledged = 0` - } - - query += ` ORDER BY created_at DESC` - - rows, err := s.db.QueryContext(ctx, query, args...) - if err != nil { - return nil, err - } - defer rows.Close() - - return scanNotifications(rows) -} - -// GetNotificationsByAgent returns notifications for a subscriber filtered by agent ID. -// If onlyUnacknowledged is true, only unacknowledged notifications are returned. -// Results are ordered by created_at DESC. -func (s *SQLiteStore) GetNotificationsByAgent(ctx context.Context, agentID, subscriberType, subscriberID string, onlyUnacknowledged bool) ([]store.Notification, error) { - query := ` - SELECT id, subscription_id, agent_id, project_id, - subscriber_type, subscriber_id, - status, message, dispatched, acknowledged, created_at - FROM notifications - WHERE agent_id = ? AND subscriber_type = ? AND subscriber_id = ? - ` - args := []interface{}{agentID, subscriberType, subscriberID} - - if onlyUnacknowledged { - query += ` AND acknowledged = 0` - } - - query += ` ORDER BY created_at DESC` - - rows, err := s.db.QueryContext(ctx, query, args...) - if err != nil { - return nil, err - } - defer rows.Close() - - return scanNotifications(rows) -} - -// AcknowledgeNotification marks a notification as acknowledged. -func (s *SQLiteStore) AcknowledgeNotification(ctx context.Context, id string) error { - result, err := s.db.ExecContext(ctx, ` - UPDATE notifications SET acknowledged = 1 WHERE id = ? - `, id) - if err != nil { - return err - } - - rows, err := result.RowsAffected() - if err != nil { - return err - } - if rows == 0 { - return store.ErrNotFound - } - return nil -} - -// AcknowledgeAllNotifications marks all notifications for a subscriber as acknowledged. -// No error on zero rows affected. -func (s *SQLiteStore) AcknowledgeAllNotifications(ctx context.Context, subscriberType, subscriberID string) error { - _, err := s.db.ExecContext(ctx, ` - UPDATE notifications SET acknowledged = 1 - WHERE subscriber_type = ? AND subscriber_id = ? - `, subscriberType, subscriberID) - return err -} - -// MarkNotificationDispatched marks a notification as dispatched. -func (s *SQLiteStore) MarkNotificationDispatched(ctx context.Context, id string) error { - result, err := s.db.ExecContext(ctx, ` - UPDATE notifications SET dispatched = 1 WHERE id = ? - `, id) - if err != nil { - return err - } - - rows, err := result.RowsAffected() - if err != nil { - return err - } - if rows == 0 { - return store.ErrNotFound - } - return nil -} - -// GetLastNotificationStatus returns the status of the most recent notification -// for a given subscription. Returns ("", nil) if no notifications exist. -func (s *SQLiteStore) GetLastNotificationStatus(ctx context.Context, subscriptionID string) (string, error) { - var status string - err := s.db.QueryRowContext(ctx, ` - SELECT status FROM notifications - WHERE subscription_id = ? - ORDER BY created_at DESC - LIMIT 1 - `, subscriptionID).Scan(&status) - if err != nil { - if errors.Is(err, sql.ErrNoRows) { - return "", nil - } - return "", err - } - return status, nil -} - -// ============================================================================ -// Subscription Template Operations -// ============================================================================ - -// CreateSubscriptionTemplate creates a new subscription template. -func (s *SQLiteStore) CreateSubscriptionTemplate(ctx context.Context, tmpl *store.SubscriptionTemplate) error { - if tmpl.ID == "" || tmpl.Name == "" || len(tmpl.TriggerActivities) == 0 { - return store.ErrInvalidInput - } - - _, err := s.db.ExecContext(ctx, ` - INSERT INTO subscription_templates (id, name, scope, trigger_activities, project_id, created_by) - VALUES (?, ?, ?, ?, ?, ?) - `, tmpl.ID, tmpl.Name, tmpl.Scope, marshalJSON(tmpl.TriggerActivities), tmpl.ProjectID, tmpl.CreatedBy) - if err != nil { - if strings.Contains(err.Error(), "UNIQUE constraint failed") { - return store.ErrAlreadyExists - } - return err - } - return nil -} - -// GetSubscriptionTemplate returns a template by ID. -func (s *SQLiteStore) GetSubscriptionTemplate(ctx context.Context, id string) (*store.SubscriptionTemplate, error) { - row := s.db.QueryRowContext(ctx, ` - SELECT id, name, scope, trigger_activities, project_id, created_by - FROM subscription_templates WHERE id = ? - `, id) - - var tmpl store.SubscriptionTemplate - var triggersJSON string - if err := row.Scan(&tmpl.ID, &tmpl.Name, &tmpl.Scope, &triggersJSON, &tmpl.ProjectID, &tmpl.CreatedBy); err != nil { - if errors.Is(err, sql.ErrNoRows) { - return nil, store.ErrNotFound - } - return nil, err - } - unmarshalJSON(triggersJSON, &tmpl.TriggerActivities) - return &tmpl, nil -} - -// ListSubscriptionTemplates returns all templates. If projectID is non-empty, -// returns both global templates and project-specific templates. -func (s *SQLiteStore) ListSubscriptionTemplates(ctx context.Context, projectID string) ([]store.SubscriptionTemplate, error) { - var rows *sql.Rows - var err error - - if projectID != "" { - rows, err = s.db.QueryContext(ctx, ` - SELECT id, name, scope, trigger_activities, project_id, created_by - FROM subscription_templates - WHERE project_id = '' OR project_id = ? - ORDER BY project_id ASC, name ASC - `, projectID) - } else { - rows, err = s.db.QueryContext(ctx, ` - SELECT id, name, scope, trigger_activities, project_id, created_by - FROM subscription_templates - WHERE project_id = '' - ORDER BY name ASC - `) - } - if err != nil { - return nil, err - } - defer rows.Close() - - var templates []store.SubscriptionTemplate - for rows.Next() { - var tmpl store.SubscriptionTemplate - var triggersJSON string - if err := rows.Scan(&tmpl.ID, &tmpl.Name, &tmpl.Scope, &triggersJSON, &tmpl.ProjectID, &tmpl.CreatedBy); err != nil { - return nil, err - } - unmarshalJSON(triggersJSON, &tmpl.TriggerActivities) - templates = append(templates, tmpl) - } - return templates, rows.Err() -} - -// DeleteSubscriptionTemplate deletes a template by ID. -func (s *SQLiteStore) DeleteSubscriptionTemplate(ctx context.Context, id string) error { - result, err := s.db.ExecContext(ctx, ` - DELETE FROM subscription_templates WHERE id = ? - `, id) - if err != nil { - return err - } - rows, err := result.RowsAffected() - if err != nil { - return err - } - if rows == 0 { - return store.ErrNotFound - } - return nil -} - -// ============================================================================ -// Helpers -// ============================================================================ - -// boolToInt converts a bool to an int for SQLite storage. -func boolToInt(b bool) int { - if b { - return 1 - } - return 0 -} - -// scanSubscriptions scans rows into NotificationSubscription slices. -func scanSubscriptions(rows *sql.Rows) ([]store.NotificationSubscription, error) { - var subs []store.NotificationSubscription - for rows.Next() { - var sub store.NotificationSubscription - var agentID sql.NullString - var triggerActivitiesJSON string - - if err := rows.Scan( - &sub.ID, &sub.Scope, &agentID, &sub.SubscriberType, &sub.SubscriberID, &sub.ProjectID, - &triggerActivitiesJSON, &sub.CreatedAt, &sub.CreatedBy, - ); err != nil { - return nil, err - } - - if agentID.Valid { - sub.AgentID = agentID.String - } - unmarshalJSON(triggerActivitiesJSON, &sub.TriggerActivities) - subs = append(subs, sub) - } - if err := rows.Err(); err != nil { - return nil, err - } - return subs, nil -} - -// scanNotifications scans rows into Notification slices. -func scanNotifications(rows *sql.Rows) ([]store.Notification, error) { - var notifs []store.Notification - for rows.Next() { - var notif store.Notification - var dispatched, acknowledged int - - if err := rows.Scan( - ¬if.ID, ¬if.SubscriptionID, ¬if.AgentID, ¬if.ProjectID, - ¬if.SubscriberType, ¬if.SubscriberID, - ¬if.Status, ¬if.Message, &dispatched, &acknowledged, ¬if.CreatedAt, - ); err != nil { - return nil, err - } - - notif.Dispatched = dispatched != 0 - notif.Acknowledged = acknowledged != 0 - notifs = append(notifs, notif) - } - if err := rows.Err(); err != nil { - return nil, err - } - return notifs, nil -} diff --git a/pkg/store/sqlite/notification_test.go b/pkg/store/sqlite/notification_test.go deleted file mode 100644 index f5a1cdfa4..000000000 --- a/pkg/store/sqlite/notification_test.go +++ /dev/null @@ -1,878 +0,0 @@ -// Copyright 2026 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//go:build !no_sqlite - -package sqlite - -import ( - "context" - "testing" - "time" - - "github.com/GoogleCloudPlatform/scion/pkg/agent/state" - "github.com/GoogleCloudPlatform/scion/pkg/api" - "github.com/GoogleCloudPlatform/scion/pkg/store" - "github.com/google/uuid" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -// createTestProjectAndAgent is a helper that creates a project and agent for notification tests. -func createTestProjectAndAgent(t *testing.T, s *SQLiteStore) (projectID, agentID string) { - t.Helper() - ctx := context.Background() - - projectID = api.NewUUID() - project := &store.Project{ - ID: projectID, - Name: "Notification Test Project", - Slug: "notif-project-" + projectID[:8], - Visibility: store.VisibilityPrivate, - } - require.NoError(t, s.CreateProject(ctx, project)) - - agentID = api.NewUUID() - agent := &store.Agent{ - ID: agentID, - Slug: "notif-agent-" + agentID[:8], - Name: "Notification Test Agent", - ProjectID: projectID, - Phase: string(state.PhaseRunning), - Visibility: store.VisibilityPrivate, - } - require.NoError(t, s.CreateAgent(ctx, agent)) - - return projectID, agentID -} - -func TestNotificationSubscriptionCRUD(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - projectID, agentID := createTestProjectAndAgent(t, s) - - subID := uuid.New().String() - sub := &store.NotificationSubscription{ - ID: subID, - Scope: store.SubscriptionScopeAgent, - AgentID: agentID, - SubscriberType: store.SubscriberTypeAgent, - SubscriberID: "lead-agent", - ProjectID: projectID, - TriggerActivities: []string{"COMPLETED", "WAITING_FOR_INPUT", "LIMITS_EXCEEDED"}, - CreatedBy: "lead-agent", - } - - // Create - err := s.CreateNotificationSubscription(ctx, sub) - require.NoError(t, err) - assert.False(t, sub.CreatedAt.IsZero(), "CreatedAt should be set automatically") - - // Get by ID - got, err := s.GetNotificationSubscription(ctx, subID) - require.NoError(t, err) - assert.Equal(t, subID, got.ID) - assert.Equal(t, store.SubscriptionScopeAgent, got.Scope) - assert.Equal(t, agentID, got.AgentID) - assert.Equal(t, store.SubscriberTypeAgent, got.SubscriberType) - assert.Equal(t, "lead-agent", got.SubscriberID) - - // Get by ID not found - _, err = s.GetNotificationSubscription(ctx, "non-existent") - assert.ErrorIs(t, err, store.ErrNotFound) - - // Get by agent - subs, err := s.GetNotificationSubscriptions(ctx, agentID) - require.NoError(t, err) - require.Len(t, subs, 1) - assert.Equal(t, subID, subs[0].ID) - assert.Equal(t, store.SubscriptionScopeAgent, subs[0].Scope) - assert.Equal(t, agentID, subs[0].AgentID) - assert.Equal(t, store.SubscriberTypeAgent, subs[0].SubscriberType) - assert.Equal(t, "lead-agent", subs[0].SubscriberID) - assert.Equal(t, projectID, subs[0].ProjectID) - assert.Equal(t, []string{"COMPLETED", "WAITING_FOR_INPUT", "LIMITS_EXCEEDED"}, subs[0].TriggerActivities) - - // Get by project - subs, err = s.GetNotificationSubscriptionsByProject(ctx, projectID) - require.NoError(t, err) - require.Len(t, subs, 1) - assert.Equal(t, subID, subs[0].ID) - - // Delete - err = s.DeleteNotificationSubscription(ctx, subID) - require.NoError(t, err) - - // Verify deleted - subs, err = s.GetNotificationSubscriptions(ctx, agentID) - require.NoError(t, err) - assert.Empty(t, subs) - - // Delete not found - err = s.DeleteNotificationSubscription(ctx, "non-existent") - assert.ErrorIs(t, err, store.ErrNotFound) -} - -func TestNotificationSubscriptionScopeDefault(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - projectID, agentID := createTestProjectAndAgent(t, s) - - // Create subscription without explicit scope — should default to "agent" - sub := &store.NotificationSubscription{ - ID: uuid.New().String(), - AgentID: agentID, - SubscriberType: store.SubscriberTypeAgent, - SubscriberID: "default-scope-agent", - ProjectID: projectID, - TriggerActivities: []string{"COMPLETED"}, - CreatedBy: "test", - } - require.NoError(t, s.CreateNotificationSubscription(ctx, sub)) - assert.Equal(t, store.SubscriptionScopeAgent, sub.Scope) - - got, err := s.GetNotificationSubscription(ctx, sub.ID) - require.NoError(t, err) - assert.Equal(t, store.SubscriptionScopeAgent, got.Scope) -} - -func TestProjectScopedSubscription(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - projectID, agentID := createTestProjectAndAgent(t, s) - - // Create a project-scoped subscription - projectSub := &store.NotificationSubscription{ - ID: uuid.New().String(), - Scope: store.SubscriptionScopeProject, - SubscriberType: store.SubscriberTypeUser, - SubscriberID: "user-project-watcher", - ProjectID: projectID, - TriggerActivities: []string{"COMPLETED", "WAITING_FOR_INPUT"}, - CreatedBy: "user-project-watcher", - } - require.NoError(t, s.CreateNotificationSubscription(ctx, projectSub)) - - // Create an agent-scoped subscription in the same project - agentSub := &store.NotificationSubscription{ - ID: uuid.New().String(), - Scope: store.SubscriptionScopeAgent, - AgentID: agentID, - SubscriberType: store.SubscriberTypeUser, - SubscriberID: "user-agent-watcher", - ProjectID: projectID, - TriggerActivities: []string{"COMPLETED"}, - CreatedBy: "user-agent-watcher", - } - require.NoError(t, s.CreateNotificationSubscription(ctx, agentSub)) - - // GetNotificationSubscriptionsByProjectScope should return only project-scoped - projectSubs, err := s.GetNotificationSubscriptionsByProjectScope(ctx, projectID) - require.NoError(t, err) - require.Len(t, projectSubs, 1) - assert.Equal(t, projectSub.ID, projectSubs[0].ID) - assert.Equal(t, store.SubscriptionScopeProject, projectSubs[0].Scope) - assert.Empty(t, projectSubs[0].AgentID) - - // GetNotificationSubscriptionsByProject should return both - allSubs, err := s.GetNotificationSubscriptionsByProject(ctx, projectID) - require.NoError(t, err) - assert.Len(t, allSubs, 2) - - // GetNotificationSubscriptions (by agent) should return only agent-scoped - agentSubs, err := s.GetNotificationSubscriptions(ctx, agentID) - require.NoError(t, err) - require.Len(t, agentSubs, 1) - assert.Equal(t, agentSub.ID, agentSubs[0].ID) -} - -func TestGetSubscriptionsForSubscriber(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - projectID, agentID := createTestProjectAndAgent(t, s) - - // Create project-scoped subscription for user - sub1 := &store.NotificationSubscription{ - ID: uuid.New().String(), - Scope: store.SubscriptionScopeProject, - SubscriberType: store.SubscriberTypeUser, - SubscriberID: "sub-user", - ProjectID: projectID, - TriggerActivities: []string{"COMPLETED"}, - CreatedBy: "sub-user", - } - require.NoError(t, s.CreateNotificationSubscription(ctx, sub1)) - - // Create agent-scoped subscription for same user - sub2 := &store.NotificationSubscription{ - ID: uuid.New().String(), - Scope: store.SubscriptionScopeAgent, - AgentID: agentID, - SubscriberType: store.SubscriberTypeUser, - SubscriberID: "sub-user", - ProjectID: projectID, - TriggerActivities: []string{"COMPLETED"}, - CreatedBy: "sub-user", - } - require.NoError(t, s.CreateNotificationSubscription(ctx, sub2)) - - // Create subscription for different user - sub3 := &store.NotificationSubscription{ - ID: uuid.New().String(), - Scope: store.SubscriptionScopeAgent, - AgentID: agentID, - SubscriberType: store.SubscriberTypeUser, - SubscriberID: "other-user", - ProjectID: projectID, - TriggerActivities: []string{"COMPLETED"}, - CreatedBy: "other-user", - } - require.NoError(t, s.CreateNotificationSubscription(ctx, sub3)) - - // Get for sub-user - subs, err := s.GetSubscriptionsForSubscriber(ctx, store.SubscriberTypeUser, "sub-user") - require.NoError(t, err) - assert.Len(t, subs, 2) - - // Get for other-user - subs, err = s.GetSubscriptionsForSubscriber(ctx, store.SubscriberTypeUser, "other-user") - require.NoError(t, err) - assert.Len(t, subs, 1) - - // Get for non-existent - subs, err = s.GetSubscriptionsForSubscriber(ctx, store.SubscriberTypeUser, "nobody") - require.NoError(t, err) - assert.Empty(t, subs) -} - -func TestSubscriptionUniqueConstraint(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - projectID, agentID := createTestProjectAndAgent(t, s) - - // Create first subscription - sub1 := &store.NotificationSubscription{ - ID: uuid.New().String(), - Scope: store.SubscriptionScopeAgent, - AgentID: agentID, - SubscriberType: store.SubscriberTypeUser, - SubscriberID: "unique-user", - ProjectID: projectID, - TriggerActivities: []string{"COMPLETED"}, - CreatedBy: "unique-user", - } - require.NoError(t, s.CreateNotificationSubscription(ctx, sub1)) - - // Duplicate should fail with ErrAlreadyExists - sub2 := &store.NotificationSubscription{ - ID: uuid.New().String(), - Scope: store.SubscriptionScopeAgent, - AgentID: agentID, - SubscriberType: store.SubscriberTypeUser, - SubscriberID: "unique-user", - ProjectID: projectID, - TriggerActivities: []string{"COMPLETED", "WAITING_FOR_INPUT"}, - CreatedBy: "unique-user", - } - err := s.CreateNotificationSubscription(ctx, sub2) - assert.ErrorIs(t, err, store.ErrAlreadyExists) - - // Same subscriber with different scope should succeed - sub3 := &store.NotificationSubscription{ - ID: uuid.New().String(), - Scope: store.SubscriptionScopeProject, - SubscriberType: store.SubscriberTypeUser, - SubscriberID: "unique-user", - ProjectID: projectID, - TriggerActivities: []string{"COMPLETED"}, - CreatedBy: "unique-user", - } - require.NoError(t, s.CreateNotificationSubscription(ctx, sub3)) -} - -func TestProjectScopedValidation(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - projectID, _ := createTestProjectAndAgent(t, s) - - // project-scoped with agent_id should clear agent_id - sub := &store.NotificationSubscription{ - ID: uuid.New().String(), - Scope: store.SubscriptionScopeProject, - AgentID: "should-be-cleared", - SubscriberType: store.SubscriberTypeUser, - SubscriberID: "validation-user", - ProjectID: projectID, - TriggerActivities: []string{"COMPLETED"}, - CreatedBy: "validation-user", - } - require.NoError(t, s.CreateNotificationSubscription(ctx, sub)) - assert.Empty(t, sub.AgentID) // Should have been cleared - - // agent-scoped without agent_id should fail - sub2 := &store.NotificationSubscription{ - ID: uuid.New().String(), - Scope: store.SubscriptionScopeAgent, - AgentID: "", - SubscriberType: store.SubscriberTypeUser, - SubscriberID: "validation-user2", - ProjectID: projectID, - TriggerActivities: []string{"COMPLETED"}, - CreatedBy: "validation-user2", - } - err := s.CreateNotificationSubscription(ctx, sub2) - assert.ErrorIs(t, err, store.ErrInvalidInput) -} - -func TestNotificationSubscriptionFKConstraint(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - // Try to create subscription with non-existent agent - sub := &store.NotificationSubscription{ - ID: uuid.New().String(), - Scope: store.SubscriptionScopeAgent, - AgentID: "non-existent-agent", - SubscriberType: store.SubscriberTypeAgent, - SubscriberID: "lead-agent", - ProjectID: "some-project", - TriggerActivities: []string{"COMPLETED"}, - CreatedBy: "lead-agent", - } - - err := s.CreateNotificationSubscription(ctx, sub) - assert.Error(t, err) - assert.ErrorIs(t, err, store.ErrInvalidInput) -} - -func TestNotificationSubscriptionCascadeDelete(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - projectID, agentID := createTestProjectAndAgent(t, s) - - // Create subscription - subID := uuid.New().String() - sub := &store.NotificationSubscription{ - ID: subID, - Scope: store.SubscriptionScopeAgent, - AgentID: agentID, - SubscriberType: store.SubscriberTypeAgent, - SubscriberID: "lead-agent", - ProjectID: projectID, - TriggerActivities: []string{"COMPLETED"}, - CreatedBy: "lead-agent", - } - require.NoError(t, s.CreateNotificationSubscription(ctx, sub)) - - // Create notification for this subscription - notifID := uuid.New().String() - notif := &store.Notification{ - ID: notifID, - SubscriptionID: subID, - AgentID: agentID, - ProjectID: projectID, - SubscriberType: store.SubscriberTypeAgent, - SubscriberID: "lead-agent", - Status: "COMPLETED", - Message: "agent completed", - } - require.NoError(t, s.CreateNotification(ctx, notif)) - - // Verify notification exists - notifs, err := s.GetNotifications(ctx, store.SubscriberTypeAgent, "lead-agent", false) - require.NoError(t, err) - require.Len(t, notifs, 1) - - // Delete the agent — should cascade to subscriptions and their notifications - err = s.DeleteAgent(ctx, agentID) - require.NoError(t, err) - - // Verify subscription is gone - subs, err := s.GetNotificationSubscriptions(ctx, agentID) - require.NoError(t, err) - assert.Empty(t, subs) - - // Verify notification is gone (cascaded from subscription) - notifs, err = s.GetNotifications(ctx, store.SubscriberTypeAgent, "lead-agent", false) - require.NoError(t, err) - assert.Empty(t, notifs) -} - -func TestBulkDeleteSubscriptions(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - projectID, agentID := createTestProjectAndAgent(t, s) - - // Create multiple subscriptions - for i := 0; i < 3; i++ { - sub := &store.NotificationSubscription{ - ID: uuid.New().String(), - Scope: store.SubscriptionScopeAgent, - AgentID: agentID, - SubscriberType: store.SubscriberTypeAgent, - SubscriberID: "subscriber-" + uuid.New().String()[:8], - ProjectID: projectID, - TriggerActivities: []string{"COMPLETED"}, - CreatedBy: "test", - } - require.NoError(t, s.CreateNotificationSubscription(ctx, sub)) - } - - // Verify they exist - subs, err := s.GetNotificationSubscriptions(ctx, agentID) - require.NoError(t, err) - assert.Len(t, subs, 3) - - // Bulk delete - err = s.DeleteNotificationSubscriptionsForAgent(ctx, agentID) - require.NoError(t, err) - - // Verify all gone - subs, err = s.GetNotificationSubscriptions(ctx, agentID) - require.NoError(t, err) - assert.Empty(t, subs) - - // Repeat — no error on zero rows - err = s.DeleteNotificationSubscriptionsForAgent(ctx, agentID) - assert.NoError(t, err) -} - -func TestNotificationCRUD(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - projectID, agentID := createTestProjectAndAgent(t, s) - - // Create subscription first - subID := uuid.New().String() - sub := &store.NotificationSubscription{ - ID: subID, - Scope: store.SubscriptionScopeAgent, - AgentID: agentID, - SubscriberType: store.SubscriberTypeUser, - SubscriberID: "user-123", - ProjectID: projectID, - TriggerActivities: []string{"COMPLETED", "WAITING_FOR_INPUT"}, - CreatedBy: "user-123", - } - require.NoError(t, s.CreateNotificationSubscription(ctx, sub)) - - // Create notification - notifID := uuid.New().String() - notif := &store.Notification{ - ID: notifID, - SubscriptionID: subID, - AgentID: agentID, - ProjectID: projectID, - SubscriberType: store.SubscriberTypeUser, - SubscriberID: "user-123", - Status: "COMPLETED", - Message: "agent has reached a state of COMPLETED", - } - err := s.CreateNotification(ctx, notif) - require.NoError(t, err) - assert.False(t, notif.CreatedAt.IsZero(), "CreatedAt should be set automatically") - - // Get notifications for subscriber - notifs, err := s.GetNotifications(ctx, store.SubscriberTypeUser, "user-123", false) - require.NoError(t, err) - require.Len(t, notifs, 1) - assert.Equal(t, notifID, notifs[0].ID) - assert.Equal(t, subID, notifs[0].SubscriptionID) - assert.Equal(t, agentID, notifs[0].AgentID) - assert.Equal(t, "COMPLETED", notifs[0].Status) - assert.Equal(t, "agent has reached a state of COMPLETED", notifs[0].Message) - assert.False(t, notifs[0].Dispatched) - assert.False(t, notifs[0].Acknowledged) - - // Acknowledge - err = s.AcknowledgeNotification(ctx, notifID) - require.NoError(t, err) - - // Verify acknowledged - notifs, err = s.GetNotifications(ctx, store.SubscriberTypeUser, "user-123", false) - require.NoError(t, err) - require.Len(t, notifs, 1) - assert.True(t, notifs[0].Acknowledged) - - // Acknowledge not found - err = s.AcknowledgeNotification(ctx, "non-existent") - assert.ErrorIs(t, err, store.ErrNotFound) -} - -func TestNotificationFiltering(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - projectID, agentID := createTestProjectAndAgent(t, s) - - subID := uuid.New().String() - sub := &store.NotificationSubscription{ - ID: subID, - Scope: store.SubscriptionScopeAgent, - AgentID: agentID, - SubscriberType: store.SubscriberTypeUser, - SubscriberID: "filter-user", - ProjectID: projectID, - TriggerActivities: []string{"COMPLETED"}, - CreatedBy: "filter-user", - } - require.NoError(t, s.CreateNotificationSubscription(ctx, sub)) - - // Create two notifications — one acknowledged, one not - notif1 := &store.Notification{ - ID: uuid.New().String(), - SubscriptionID: subID, - AgentID: agentID, - ProjectID: projectID, - SubscriberType: store.SubscriberTypeUser, - SubscriberID: "filter-user", - Status: "COMPLETED", - Message: "first notification", - CreatedAt: time.Now().Add(-2 * time.Second), - } - notif2 := &store.Notification{ - ID: uuid.New().String(), - SubscriptionID: subID, - AgentID: agentID, - ProjectID: projectID, - SubscriberType: store.SubscriberTypeUser, - SubscriberID: "filter-user", - Status: "COMPLETED", - Message: "second notification", - CreatedAt: time.Now(), - } - require.NoError(t, s.CreateNotification(ctx, notif1)) - require.NoError(t, s.CreateNotification(ctx, notif2)) - - // Acknowledge the first one - require.NoError(t, s.AcknowledgeNotification(ctx, notif1.ID)) - - // Get all — should return both, ordered by created_at DESC - all, err := s.GetNotifications(ctx, store.SubscriberTypeUser, "filter-user", false) - require.NoError(t, err) - require.Len(t, all, 2) - assert.Equal(t, notif2.ID, all[0].ID, "most recent should be first") - assert.Equal(t, notif1.ID, all[1].ID) - - // Get only unacknowledged — should return only the second - unacked, err := s.GetNotifications(ctx, store.SubscriberTypeUser, "filter-user", true) - require.NoError(t, err) - require.Len(t, unacked, 1) - assert.Equal(t, notif2.ID, unacked[0].ID) -} - -func TestMarkNotificationDispatched(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - projectID, agentID := createTestProjectAndAgent(t, s) - - subID := uuid.New().String() - sub := &store.NotificationSubscription{ - ID: subID, - Scope: store.SubscriptionScopeAgent, - AgentID: agentID, - SubscriberType: store.SubscriberTypeAgent, - SubscriberID: "dispatch-target", - ProjectID: projectID, - TriggerActivities: []string{"COMPLETED"}, - CreatedBy: "test", - } - require.NoError(t, s.CreateNotificationSubscription(ctx, sub)) - - notifID := uuid.New().String() - notif := &store.Notification{ - ID: notifID, - SubscriptionID: subID, - AgentID: agentID, - ProjectID: projectID, - SubscriberType: store.SubscriberTypeAgent, - SubscriberID: "dispatch-target", - Status: "COMPLETED", - Message: "dispatched test", - } - require.NoError(t, s.CreateNotification(ctx, notif)) - - // Initially not dispatched - notifs, err := s.GetNotifications(ctx, store.SubscriberTypeAgent, "dispatch-target", false) - require.NoError(t, err) - require.Len(t, notifs, 1) - assert.False(t, notifs[0].Dispatched) - - // Mark dispatched - err = s.MarkNotificationDispatched(ctx, notifID) - require.NoError(t, err) - - // Verify dispatched - notifs, err = s.GetNotifications(ctx, store.SubscriberTypeAgent, "dispatch-target", false) - require.NoError(t, err) - require.Len(t, notifs, 1) - assert.True(t, notifs[0].Dispatched) - - // Not found - err = s.MarkNotificationDispatched(ctx, "non-existent") - assert.ErrorIs(t, err, store.ErrNotFound) -} - -func TestAcknowledgeAllNotifications(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - projectID, agentID := createTestProjectAndAgent(t, s) - - subID := uuid.New().String() - sub := &store.NotificationSubscription{ - ID: subID, - Scope: store.SubscriptionScopeAgent, - AgentID: agentID, - SubscriberType: store.SubscriberTypeUser, - SubscriberID: "ack-all-user", - ProjectID: projectID, - TriggerActivities: []string{"COMPLETED"}, - CreatedBy: "ack-all-user", - } - require.NoError(t, s.CreateNotificationSubscription(ctx, sub)) - - // Create multiple notifications - for i := 0; i < 3; i++ { - notif := &store.Notification{ - ID: uuid.New().String(), - SubscriptionID: subID, - AgentID: agentID, - ProjectID: projectID, - SubscriberType: store.SubscriberTypeUser, - SubscriberID: "ack-all-user", - Status: "COMPLETED", - Message: "notification", - } - require.NoError(t, s.CreateNotification(ctx, notif)) - } - - // All unacknowledged - unacked, err := s.GetNotifications(ctx, store.SubscriberTypeUser, "ack-all-user", true) - require.NoError(t, err) - assert.Len(t, unacked, 3) - - // Acknowledge all - err = s.AcknowledgeAllNotifications(ctx, store.SubscriberTypeUser, "ack-all-user") - require.NoError(t, err) - - // Verify all acknowledged - unacked, err = s.GetNotifications(ctx, store.SubscriberTypeUser, "ack-all-user", true) - require.NoError(t, err) - assert.Empty(t, unacked) - - // All should still be retrievable - all, err := s.GetNotifications(ctx, store.SubscriberTypeUser, "ack-all-user", false) - require.NoError(t, err) - assert.Len(t, all, 3) - - // Repeat — no error on zero rows - err = s.AcknowledgeAllNotifications(ctx, store.SubscriberTypeUser, "ack-all-user") - assert.NoError(t, err) -} - -func TestGetLastNotificationStatus(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - projectID, agentID := createTestProjectAndAgent(t, s) - - subID := uuid.New().String() - sub := &store.NotificationSubscription{ - ID: subID, - Scope: store.SubscriptionScopeAgent, - AgentID: agentID, - SubscriberType: store.SubscriberTypeAgent, - SubscriberID: "last-status-agent", - ProjectID: projectID, - TriggerActivities: []string{"COMPLETED", "WAITING_FOR_INPUT"}, - CreatedBy: "test", - } - require.NoError(t, s.CreateNotificationSubscription(ctx, sub)) - - // No notifications yet — should return empty string, no error - status, err := s.GetLastNotificationStatus(ctx, subID) - require.NoError(t, err) - assert.Equal(t, "", status) - - // Create first notification - notif1 := &store.Notification{ - ID: uuid.New().String(), - SubscriptionID: subID, - AgentID: agentID, - ProjectID: projectID, - SubscriberType: store.SubscriberTypeAgent, - SubscriberID: "last-status-agent", - Status: "WAITING_FOR_INPUT", - Message: "waiting", - CreatedAt: time.Now().Add(-1 * time.Second), - } - require.NoError(t, s.CreateNotification(ctx, notif1)) - - status, err = s.GetLastNotificationStatus(ctx, subID) - require.NoError(t, err) - assert.Equal(t, "WAITING_FOR_INPUT", status) - - // Create second notification (more recent) - notif2 := &store.Notification{ - ID: uuid.New().String(), - SubscriptionID: subID, - AgentID: agentID, - ProjectID: projectID, - SubscriberType: store.SubscriberTypeAgent, - SubscriberID: "last-status-agent", - Status: "COMPLETED", - Message: "done", - CreatedAt: time.Now(), - } - require.NoError(t, s.CreateNotification(ctx, notif2)) - - status, err = s.GetLastNotificationStatus(ctx, subID) - require.NoError(t, err) - assert.Equal(t, "COMPLETED", status) -} - -func TestMatchesActivity(t *testing.T) { - sub := &store.NotificationSubscription{ - TriggerActivities: []string{"COMPLETED", "WAITING_FOR_INPUT"}, - } - - // Case-insensitive matching - assert.True(t, sub.MatchesActivity("COMPLETED")) - assert.True(t, sub.MatchesActivity("completed")) - assert.True(t, sub.MatchesActivity("Completed")) - assert.True(t, sub.MatchesActivity("waiting_for_input")) - assert.True(t, sub.MatchesActivity("WAITING_FOR_INPUT")) - - // Non-matching - assert.False(t, sub.MatchesActivity("RUNNING")) - assert.False(t, sub.MatchesActivity("error")) - assert.False(t, sub.MatchesActivity("")) - - // Empty trigger list - emptySub := &store.NotificationSubscription{ - TriggerActivities: []string{}, - } - assert.False(t, emptySub.MatchesActivity("COMPLETED")) - - // Nil trigger list - nilSub := &store.NotificationSubscription{} - assert.False(t, nilSub.MatchesActivity("COMPLETED")) -} - -func TestGetNotificationsByAgent(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - // Create project and two agents - projectID, agent1ID := createTestProjectAndAgent(t, s) - agent2ID := api.NewUUID() - agent2 := &store.Agent{ - ID: agent2ID, - Slug: "notif-agent2-" + agent2ID[:8], - Name: "Second Agent", - ProjectID: projectID, - Phase: string(state.PhaseRunning), - Visibility: store.VisibilityPrivate, - } - require.NoError(t, s.CreateAgent(ctx, agent2)) - - // Create subscriptions for both agents - sub1ID := uuid.New().String() - sub1 := &store.NotificationSubscription{ - ID: sub1ID, - Scope: store.SubscriptionScopeAgent, - AgentID: agent1ID, - SubscriberType: store.SubscriberTypeUser, - SubscriberID: "user-by-agent", - ProjectID: projectID, - TriggerActivities: []string{"COMPLETED"}, - CreatedBy: "user-by-agent", - } - require.NoError(t, s.CreateNotificationSubscription(ctx, sub1)) - - sub2ID := uuid.New().String() - sub2 := &store.NotificationSubscription{ - ID: sub2ID, - Scope: store.SubscriptionScopeAgent, - AgentID: agent2ID, - SubscriberType: store.SubscriberTypeUser, - SubscriberID: "user-by-agent", - ProjectID: projectID, - TriggerActivities: []string{"COMPLETED"}, - CreatedBy: "user-by-agent", - } - require.NoError(t, s.CreateNotificationSubscription(ctx, sub2)) - - // Create notifications for agent1 (2 notifications, one acked) - n1 := &store.Notification{ - ID: uuid.New().String(), - SubscriptionID: sub1ID, - AgentID: agent1ID, - ProjectID: projectID, - SubscriberType: store.SubscriberTypeUser, - SubscriberID: "user-by-agent", - Status: "COMPLETED", - Message: "agent1 completed first", - CreatedAt: time.Now().Add(-2 * time.Second), - } - n2 := &store.Notification{ - ID: uuid.New().String(), - SubscriptionID: sub1ID, - AgentID: agent1ID, - ProjectID: projectID, - SubscriberType: store.SubscriberTypeUser, - SubscriberID: "user-by-agent", - Status: "COMPLETED", - Message: "agent1 completed second", - CreatedAt: time.Now(), - } - require.NoError(t, s.CreateNotification(ctx, n1)) - require.NoError(t, s.CreateNotification(ctx, n2)) - require.NoError(t, s.AcknowledgeNotification(ctx, n1.ID)) - - // Create notification for agent2 - n3 := &store.Notification{ - ID: uuid.New().String(), - SubscriptionID: sub2ID, - AgentID: agent2ID, - ProjectID: projectID, - SubscriberType: store.SubscriberTypeUser, - SubscriberID: "user-by-agent", - Status: "COMPLETED", - Message: "agent2 completed", - } - require.NoError(t, s.CreateNotification(ctx, n3)) - - // GetNotificationsByAgent for agent1 — all - notifs, err := s.GetNotificationsByAgent(ctx, agent1ID, store.SubscriberTypeUser, "user-by-agent", false) - require.NoError(t, err) - assert.Len(t, notifs, 2) - assert.Equal(t, n2.ID, notifs[0].ID, "most recent first") - assert.Equal(t, n1.ID, notifs[1].ID) - - // GetNotificationsByAgent for agent1 — only unacknowledged - notifs, err = s.GetNotificationsByAgent(ctx, agent1ID, store.SubscriberTypeUser, "user-by-agent", true) - require.NoError(t, err) - assert.Len(t, notifs, 1) - assert.Equal(t, n2.ID, notifs[0].ID) - - // GetNotificationsByAgent for agent2 - notifs, err = s.GetNotificationsByAgent(ctx, agent2ID, store.SubscriberTypeUser, "user-by-agent", false) - require.NoError(t, err) - assert.Len(t, notifs, 1) - assert.Equal(t, n3.ID, notifs[0].ID) - - // GetNotificationsByAgent for non-existent agent - notifs, err = s.GetNotificationsByAgent(ctx, "no-such-agent", store.SubscriberTypeUser, "user-by-agent", false) - require.NoError(t, err) - assert.Empty(t, notifs) -} diff --git a/pkg/store/sqlite/project_sync_state.go b/pkg/store/sqlite/project_sync_state.go deleted file mode 100644 index 8a6b9ce2d..000000000 --- a/pkg/store/sqlite/project_sync_state.go +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright 2026 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package sqlite - -import ( - "context" - "database/sql" - - "github.com/GoogleCloudPlatform/scion/pkg/store" -) - -// ============================================================================ -// Project Sync State Operations -// ============================================================================ - -// UpsertProjectSyncState creates or updates sync state for a project. -func (s *SQLiteStore) UpsertProjectSyncState(ctx context.Context, state *store.ProjectSyncState) error { - if state.ProjectID == "" { - return store.ErrInvalidInput - } - - _, err := s.db.ExecContext(ctx, ` - INSERT INTO project_sync_state (project_id, broker_id, last_sync_time, last_commit_sha, file_count, total_bytes) - VALUES (?, ?, ?, ?, ?, ?) - ON CONFLICT(project_id, broker_id) DO UPDATE SET - last_sync_time = excluded.last_sync_time, - last_commit_sha = excluded.last_commit_sha, - file_count = excluded.file_count, - total_bytes = excluded.total_bytes - `, state.ProjectID, state.BrokerID, - nullableTimePtr(state.LastSyncTime), - nullableString(state.LastCommitSHA), - state.FileCount, state.TotalBytes, - ) - return err -} - -// GetProjectSyncState retrieves sync state for a project and optional broker. -func (s *SQLiteStore) GetProjectSyncState(ctx context.Context, projectID, brokerID string) (*store.ProjectSyncState, error) { - state := &store.ProjectSyncState{} - var lastSyncTime sql.NullTime - var lastCommitSHA sql.NullString - - err := s.db.QueryRowContext(ctx, ` - SELECT project_id, broker_id, last_sync_time, last_commit_sha, file_count, total_bytes - FROM project_sync_state - WHERE project_id = ? AND broker_id = ? - `, projectID, brokerID).Scan( - &state.ProjectID, &state.BrokerID, - &lastSyncTime, &lastCommitSHA, - &state.FileCount, &state.TotalBytes, - ) - if err != nil { - if err == sql.ErrNoRows { - return nil, store.ErrNotFound - } - return nil, err - } - - if lastSyncTime.Valid { - state.LastSyncTime = &lastSyncTime.Time - } - if lastCommitSHA.Valid { - state.LastCommitSHA = lastCommitSHA.String - } - - return state, nil -} - -// ListProjectSyncStates returns all sync states for a project. -func (s *SQLiteStore) ListProjectSyncStates(ctx context.Context, projectID string) ([]store.ProjectSyncState, error) { - rows, err := s.db.QueryContext(ctx, ` - SELECT project_id, broker_id, last_sync_time, last_commit_sha, file_count, total_bytes - FROM project_sync_state - WHERE project_id = ? - ORDER BY broker_id - `, projectID) - if err != nil { - return nil, err - } - defer rows.Close() - - var states []store.ProjectSyncState - for rows.Next() { - var state store.ProjectSyncState - var lastSyncTime sql.NullTime - var lastCommitSHA sql.NullString - - if err := rows.Scan( - &state.ProjectID, &state.BrokerID, - &lastSyncTime, &lastCommitSHA, - &state.FileCount, &state.TotalBytes, - ); err != nil { - return nil, err - } - - if lastSyncTime.Valid { - state.LastSyncTime = &lastSyncTime.Time - } - if lastCommitSHA.Valid { - state.LastCommitSHA = lastCommitSHA.String - } - - states = append(states, state) - } - - if states == nil { - states = []store.ProjectSyncState{} - } - return states, rows.Err() -} - -// DeleteProjectSyncState removes sync state for a project and optional broker. -func (s *SQLiteStore) DeleteProjectSyncState(ctx context.Context, projectID, brokerID string) error { - result, err := s.db.ExecContext(ctx, ` - DELETE FROM project_sync_state WHERE project_id = ? AND broker_id = ? - `, projectID, brokerID) - if err != nil { - return err - } - - rows, err := result.RowsAffected() - if err != nil { - return err - } - if rows == 0 { - return store.ErrNotFound - } - return nil -} diff --git a/pkg/store/sqlite/project_sync_state_test.go b/pkg/store/sqlite/project_sync_state_test.go deleted file mode 100644 index 4ce823d82..000000000 --- a/pkg/store/sqlite/project_sync_state_test.go +++ /dev/null @@ -1,138 +0,0 @@ -// Copyright 2026 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//go:build !no_sqlite - -package sqlite - -import ( - "context" - "testing" - "time" - - "github.com/GoogleCloudPlatform/scion/pkg/store" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestProjectSyncStateCRUD(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - projectID := createTestProject(t, s) - - now := time.Now().UTC().Truncate(time.Second) - - // Upsert (create) - state := &store.ProjectSyncState{ - ProjectID: projectID, - BrokerID: "", - LastSyncTime: &now, - LastCommitSHA: "abc123", - FileCount: 42, - TotalBytes: 123456, - } - err := s.UpsertProjectSyncState(ctx, state) - require.NoError(t, err) - - // Get - got, err := s.GetProjectSyncState(ctx, projectID, "") - require.NoError(t, err) - assert.Equal(t, projectID, got.ProjectID) - assert.Equal(t, "", got.BrokerID) - assert.NotNil(t, got.LastSyncTime) - assert.Equal(t, now, *got.LastSyncTime) - assert.Equal(t, "abc123", got.LastCommitSHA) - assert.Equal(t, 42, got.FileCount) - assert.Equal(t, int64(123456), got.TotalBytes) - - // Upsert (update) - later := now.Add(5 * time.Minute) - state.LastSyncTime = &later - state.FileCount = 50 - state.TotalBytes = 200000 - err = s.UpsertProjectSyncState(ctx, state) - require.NoError(t, err) - - got, err = s.GetProjectSyncState(ctx, projectID, "") - require.NoError(t, err) - assert.Equal(t, later, *got.LastSyncTime) - assert.Equal(t, 50, got.FileCount) - assert.Equal(t, int64(200000), got.TotalBytes) - - // Add a broker-scoped state - brokerState := &store.ProjectSyncState{ - ProjectID: projectID, - BrokerID: "broker-1", - FileCount: 10, - TotalBytes: 5000, - } - err = s.UpsertProjectSyncState(ctx, brokerState) - require.NoError(t, err) - - // List - states, err := s.ListProjectSyncStates(ctx, projectID) - require.NoError(t, err) - assert.Len(t, states, 2) - - // Delete hub-managed state - err = s.DeleteProjectSyncState(ctx, projectID, "") - require.NoError(t, err) - - // Verify only broker state remains - states, err = s.ListProjectSyncStates(ctx, projectID) - require.NoError(t, err) - assert.Len(t, states, 1) - assert.Equal(t, "broker-1", states[0].BrokerID) - - // Get not found - _, err = s.GetProjectSyncState(ctx, projectID, "") - assert.ErrorIs(t, err, store.ErrNotFound) - - // Delete not found - err = s.DeleteProjectSyncState(ctx, projectID, "nonexistent") - assert.ErrorIs(t, err, store.ErrNotFound) -} - -func TestProjectSyncStateValidation(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - // Empty project ID - err := s.UpsertProjectSyncState(ctx, &store.ProjectSyncState{}) - assert.ErrorIs(t, err, store.ErrInvalidInput) -} - -func TestProjectSyncStateCascadeDelete(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - projectID := createTestProject(t, s) - - now := time.Now().UTC().Truncate(time.Second) - state := &store.ProjectSyncState{ - ProjectID: projectID, - LastSyncTime: &now, - FileCount: 5, - TotalBytes: 1000, - } - err := s.UpsertProjectSyncState(ctx, state) - require.NoError(t, err) - - // Delete the project (project) - sync state should cascade - err = s.DeleteProject(ctx, projectID) - require.NoError(t, err) - - states, err := s.ListProjectSyncStates(ctx, projectID) - require.NoError(t, err) - assert.Empty(t, states) -} diff --git a/pkg/store/sqlite/schedule.go b/pkg/store/sqlite/schedule.go deleted file mode 100644 index 4349cf96e..000000000 --- a/pkg/store/sqlite/schedule.go +++ /dev/null @@ -1,365 +0,0 @@ -// Copyright 2026 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package sqlite - -import ( - "context" - "database/sql" - "errors" - "fmt" - "strings" - "time" - - "github.com/GoogleCloudPlatform/scion/pkg/store" -) - -// ============================================================================ -// Schedule Operations (Recurring Schedules) -// ============================================================================ - -// CreateSchedule creates a new recurring schedule. -func (s *SQLiteStore) CreateSchedule(ctx context.Context, schedule *store.Schedule) error { - if schedule.ID == "" || schedule.ProjectID == "" || schedule.Name == "" || schedule.CronExpr == "" { - return store.ErrInvalidInput - } - - now := time.Now() - if schedule.CreatedAt.IsZero() { - schedule.CreatedAt = now - } - if schedule.UpdatedAt.IsZero() { - schedule.UpdatedAt = now - } - if schedule.Status == "" { - schedule.Status = store.ScheduleStatusActive - } - - _, err := s.db.ExecContext(ctx, ` - INSERT INTO schedules ( - id, project_id, name, cron_expr, event_type, payload, status, - next_run_at, last_run_at, last_run_status, last_run_error, - run_count, error_count, created_at, created_by, updated_at - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - `, - schedule.ID, schedule.ProjectID, schedule.Name, schedule.CronExpr, - schedule.EventType, schedule.Payload, schedule.Status, - nullableTime(timeFromNullablePtr(schedule.NextRunAt)), - nullableTime(timeFromNullablePtr(schedule.LastRunAt)), - nullableString(schedule.LastRunStatus), nullableString(schedule.LastRunError), - schedule.RunCount, schedule.ErrorCount, - schedule.CreatedAt, nullableString(schedule.CreatedBy), schedule.UpdatedAt, - ) - if err != nil { - if strings.Contains(err.Error(), "UNIQUE constraint failed") { - return store.ErrAlreadyExists - } - if strings.Contains(err.Error(), "FOREIGN KEY constraint failed") { - return fmt.Errorf("project %s does not exist: %w", schedule.ProjectID, store.ErrInvalidInput) - } - return err - } - return nil -} - -// GetSchedule retrieves a schedule by ID. -func (s *SQLiteStore) GetSchedule(ctx context.Context, id string) (*store.Schedule, error) { - schedule := &store.Schedule{} - var nextRunAt, lastRunAt sql.NullTime - var lastRunStatus, lastRunError, createdBy sql.NullString - - err := s.db.QueryRowContext(ctx, ` - SELECT id, project_id, name, cron_expr, event_type, payload, status, - next_run_at, last_run_at, last_run_status, last_run_error, - run_count, error_count, created_at, created_by, updated_at - FROM schedules WHERE id = ? - `, id).Scan( - &schedule.ID, &schedule.ProjectID, &schedule.Name, &schedule.CronExpr, - &schedule.EventType, &schedule.Payload, &schedule.Status, - &nextRunAt, &lastRunAt, &lastRunStatus, &lastRunError, - &schedule.RunCount, &schedule.ErrorCount, - &schedule.CreatedAt, &createdBy, &schedule.UpdatedAt, - ) - if err != nil { - if errors.Is(err, sql.ErrNoRows) { - return nil, store.ErrNotFound - } - return nil, err - } - - if nextRunAt.Valid { - schedule.NextRunAt = &nextRunAt.Time - } - if lastRunAt.Valid { - schedule.LastRunAt = &lastRunAt.Time - } - if lastRunStatus.Valid { - schedule.LastRunStatus = lastRunStatus.String - } - if lastRunError.Valid { - schedule.LastRunError = lastRunError.String - } - if createdBy.Valid { - schedule.CreatedBy = createdBy.String - } - - return schedule, nil -} - -// ListSchedules returns schedules matching the filter criteria. -func (s *SQLiteStore) ListSchedules(ctx context.Context, filter store.ScheduleFilter, opts store.ListOptions) (*store.ListResult[store.Schedule], error) { - var conditions []string - var args []interface{} - - if filter.ProjectID != "" { - conditions = append(conditions, "project_id = ?") - args = append(args, filter.ProjectID) - } - if filter.Status != "" { - conditions = append(conditions, "status = ?") - args = append(args, filter.Status) - } else { - // By default, exclude deleted schedules - conditions = append(conditions, "status != ?") - args = append(args, store.ScheduleStatusDeleted) - } - if filter.Name != "" { - conditions = append(conditions, "name = ?") - args = append(args, filter.Name) - } - - whereClause := "" - if len(conditions) > 0 { - whereClause = "WHERE " + strings.Join(conditions, " AND ") - } - - // Get total count - var totalCount int - countQuery := fmt.Sprintf("SELECT COUNT(*) FROM schedules %s", whereClause) - if err := s.db.QueryRowContext(ctx, countQuery, args...).Scan(&totalCount); err != nil { - return nil, err - } - - limit := opts.Limit - if limit <= 0 { - limit = 50 - } - if limit > 200 { - limit = 200 - } - - query := fmt.Sprintf(` - SELECT id, project_id, name, cron_expr, event_type, payload, status, - next_run_at, last_run_at, last_run_status, last_run_error, - run_count, error_count, created_at, created_by, updated_at - FROM schedules %s - ORDER BY created_at DESC - LIMIT ? - `, whereClause) - - queryArgs := append(args, limit+1) //nolint:gocritic - - rows, err := s.db.QueryContext(ctx, query, queryArgs...) - if err != nil { - return nil, err - } - defer rows.Close() - - schedules, err := scanSchedules(rows) - if err != nil { - return nil, err - } - - result := &store.ListResult[store.Schedule]{ - TotalCount: totalCount, - } - - if len(schedules) > limit { - result.Items = schedules[:limit] - result.NextCursor = schedules[limit-1].ID - } else { - result.Items = schedules - } - - return result, nil -} - -// UpdateSchedule updates an existing schedule. -func (s *SQLiteStore) UpdateSchedule(ctx context.Context, schedule *store.Schedule) error { - schedule.UpdatedAt = time.Now() - - result, err := s.db.ExecContext(ctx, ` - UPDATE schedules SET - name = ?, cron_expr = ?, event_type = ?, payload = ?, - status = ?, next_run_at = ?, updated_at = ? - WHERE id = ? - `, - schedule.Name, schedule.CronExpr, schedule.EventType, schedule.Payload, - schedule.Status, nullableTime(timeFromNullablePtr(schedule.NextRunAt)), - schedule.UpdatedAt, schedule.ID, - ) - if err != nil { - return err - } - rows, err := result.RowsAffected() - if err != nil { - return err - } - if rows == 0 { - return store.ErrNotFound - } - return nil -} - -// UpdateScheduleStatus updates only the status of a schedule. -func (s *SQLiteStore) UpdateScheduleStatus(ctx context.Context, id string, status string) error { - result, err := s.db.ExecContext(ctx, ` - UPDATE schedules SET status = ?, updated_at = ? WHERE id = ? - `, status, time.Now(), id) - if err != nil { - return err - } - rows, err := result.RowsAffected() - if err != nil { - return err - } - if rows == 0 { - return store.ErrNotFound - } - return nil -} - -// UpdateScheduleAfterRun updates a schedule after a run completes. -func (s *SQLiteStore) UpdateScheduleAfterRun(ctx context.Context, id string, ranAt time.Time, nextRunAt time.Time, errMsg string) error { - var query string - var args []interface{} - - if errMsg != "" { - query = ` - UPDATE schedules SET - last_run_at = ?, next_run_at = ?, last_run_status = ?, last_run_error = ?, - run_count = run_count + 1, error_count = error_count + 1, updated_at = ? - WHERE id = ? - ` - args = []interface{}{ranAt, nextRunAt, store.ScheduleRunError, errMsg, time.Now(), id} - } else { - query = ` - UPDATE schedules SET - last_run_at = ?, next_run_at = ?, last_run_status = ?, last_run_error = NULL, - run_count = run_count + 1, updated_at = ? - WHERE id = ? - ` - args = []interface{}{ranAt, nextRunAt, store.ScheduleRunSuccess, time.Now(), id} - } - - result, err := s.db.ExecContext(ctx, query, args...) - if err != nil { - return err - } - rows, err := result.RowsAffected() - if err != nil { - return err - } - if rows == 0 { - return store.ErrNotFound - } - return nil -} - -// DeleteSchedule removes a schedule by ID (hard delete). -func (s *SQLiteStore) DeleteSchedule(ctx context.Context, id string) error { - result, err := s.db.ExecContext(ctx, "DELETE FROM schedules WHERE id = ?", id) - if err != nil { - return err - } - rows, err := result.RowsAffected() - if err != nil { - return err - } - if rows == 0 { - return store.ErrNotFound - } - return nil -} - -// ListDueSchedules returns active schedules whose next_run_at has passed. -func (s *SQLiteStore) ListDueSchedules(ctx context.Context, now time.Time) ([]store.Schedule, error) { - rows, err := s.db.QueryContext(ctx, ` - SELECT id, project_id, name, cron_expr, event_type, payload, status, - next_run_at, last_run_at, last_run_status, last_run_error, - run_count, error_count, created_at, created_by, updated_at - FROM schedules - WHERE status = ? AND next_run_at IS NOT NULL AND next_run_at <= ? - ORDER BY next_run_at ASC - `, store.ScheduleStatusActive, now) - if err != nil { - return nil, err - } - defer rows.Close() - - return scanSchedules(rows) -} - -// ============================================================================ -// Helpers -// ============================================================================ - -// timeFromNullablePtr returns the time from a pointer, or zero time if nil. -func timeFromNullablePtr(t *time.Time) time.Time { - if t == nil { - return time.Time{} - } - return *t -} - -// scanSchedules scans rows into Schedule slices. -func scanSchedules(rows *sql.Rows) ([]store.Schedule, error) { - var schedules []store.Schedule - for rows.Next() { - var schedule store.Schedule - var nextRunAt, lastRunAt sql.NullTime - var lastRunStatus, lastRunError, createdBy sql.NullString - - if err := rows.Scan( - &schedule.ID, &schedule.ProjectID, &schedule.Name, &schedule.CronExpr, - &schedule.EventType, &schedule.Payload, &schedule.Status, - &nextRunAt, &lastRunAt, &lastRunStatus, &lastRunError, - &schedule.RunCount, &schedule.ErrorCount, - &schedule.CreatedAt, &createdBy, &schedule.UpdatedAt, - ); err != nil { - return nil, err - } - - if nextRunAt.Valid { - schedule.NextRunAt = &nextRunAt.Time - } - if lastRunAt.Valid { - schedule.LastRunAt = &lastRunAt.Time - } - if lastRunStatus.Valid { - schedule.LastRunStatus = lastRunStatus.String - } - if lastRunError.Valid { - schedule.LastRunError = lastRunError.String - } - if createdBy.Valid { - schedule.CreatedBy = createdBy.String - } - schedules = append(schedules, schedule) - } - if err := rows.Err(); err != nil { - return nil, err - } - return schedules, nil -} diff --git a/pkg/store/sqlite/schedule_test.go b/pkg/store/sqlite/schedule_test.go deleted file mode 100644 index f79c7e5e6..000000000 --- a/pkg/store/sqlite/schedule_test.go +++ /dev/null @@ -1,290 +0,0 @@ -// Copyright 2026 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//go:build !no_sqlite - -package sqlite - -import ( - "context" - "testing" - "time" - - "github.com/GoogleCloudPlatform/scion/pkg/api" - "github.com/GoogleCloudPlatform/scion/pkg/store" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestScheduleCRUD(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - projectID := createTestProject(t, s) - - scheduleID := api.NewUUID() - nextRun := time.Now().Add(1 * time.Hour).UTC().Truncate(time.Second) - - sched := &store.Schedule{ - ID: scheduleID, - ProjectID: projectID, - Name: "daily-standup", - CronExpr: "0 9 * * 1-5", - EventType: "message", - Payload: `{"agentName":"all","message":"Status update please"}`, - NextRunAt: &nextRun, - CreatedBy: "user-123", - } - - // Create - err := s.CreateSchedule(ctx, sched) - require.NoError(t, err) - assert.False(t, sched.CreatedAt.IsZero()) - assert.Equal(t, store.ScheduleStatusActive, sched.Status) - - // Get - got, err := s.GetSchedule(ctx, scheduleID) - require.NoError(t, err) - assert.Equal(t, scheduleID, got.ID) - assert.Equal(t, projectID, got.ProjectID) - assert.Equal(t, "daily-standup", got.Name) - assert.Equal(t, "0 9 * * 1-5", got.CronExpr) - assert.Equal(t, "message", got.EventType) - assert.Equal(t, store.ScheduleStatusActive, got.Status) - assert.Equal(t, "user-123", got.CreatedBy) - assert.Equal(t, 0, got.RunCount) - assert.Equal(t, 0, got.ErrorCount) - assert.NotNil(t, got.NextRunAt) - - // Update - got.Name = "weekly-standup" - got.CronExpr = "0 9 * * 1" - err = s.UpdateSchedule(ctx, got) - require.NoError(t, err) - - updated, err := s.GetSchedule(ctx, scheduleID) - require.NoError(t, err) - assert.Equal(t, "weekly-standup", updated.Name) - assert.Equal(t, "0 9 * * 1", updated.CronExpr) - - // Update status - err = s.UpdateScheduleStatus(ctx, scheduleID, store.ScheduleStatusPaused) - require.NoError(t, err) - - paused, err := s.GetSchedule(ctx, scheduleID) - require.NoError(t, err) - assert.Equal(t, store.ScheduleStatusPaused, paused.Status) - - // Delete - err = s.DeleteSchedule(ctx, scheduleID) - require.NoError(t, err) - - _, err = s.GetSchedule(ctx, scheduleID) - assert.ErrorIs(t, err, store.ErrNotFound) -} - -func TestSchedule_DuplicateName(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - projectID := createTestProject(t, s) - - sched1 := &store.Schedule{ - ID: api.NewUUID(), - ProjectID: projectID, - Name: "duplicate-name", - CronExpr: "0 * * * *", - EventType: "message", - Payload: "{}", - } - require.NoError(t, s.CreateSchedule(ctx, sched1)) - - sched2 := &store.Schedule{ - ID: api.NewUUID(), - ProjectID: projectID, - Name: "duplicate-name", - CronExpr: "0 * * * *", - EventType: "message", - Payload: "{}", - } - err := s.CreateSchedule(ctx, sched2) - assert.ErrorIs(t, err, store.ErrAlreadyExists) -} - -func TestSchedule_List(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - projectID := createTestProject(t, s) - - // Create 3 schedules - for i, name := range []string{"sched-a", "sched-b", "sched-c"} { - status := store.ScheduleStatusActive - if i == 2 { - status = store.ScheduleStatusPaused - } - sched := &store.Schedule{ - ID: api.NewUUID(), - ProjectID: projectID, - Name: name, - CronExpr: "0 * * * *", - EventType: "message", - Payload: "{}", - Status: status, - } - require.NoError(t, s.CreateSchedule(ctx, sched)) - } - - // List all (excludes deleted) - result, err := s.ListSchedules(ctx, store.ScheduleFilter{ProjectID: projectID}, store.ListOptions{}) - require.NoError(t, err) - assert.Equal(t, 3, result.TotalCount) - assert.Len(t, result.Items, 3) - - // Filter by status - result, err = s.ListSchedules(ctx, store.ScheduleFilter{ProjectID: projectID, Status: store.ScheduleStatusActive}, store.ListOptions{}) - require.NoError(t, err) - assert.Equal(t, 2, result.TotalCount) - - result, err = s.ListSchedules(ctx, store.ScheduleFilter{ProjectID: projectID, Status: store.ScheduleStatusPaused}, store.ListOptions{}) - require.NoError(t, err) - assert.Equal(t, 1, result.TotalCount) -} - -func TestSchedule_UpdateAfterRun(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - projectID := createTestProject(t, s) - - nextRun := time.Now().Add(-1 * time.Minute).UTC().Truncate(time.Second) - sched := &store.Schedule{ - ID: api.NewUUID(), - ProjectID: projectID, - Name: "run-test", - CronExpr: "0 * * * *", - EventType: "message", - Payload: "{}", - NextRunAt: &nextRun, - } - require.NoError(t, s.CreateSchedule(ctx, sched)) - - // Successful run - ranAt := time.Now().UTC().Truncate(time.Second) - newNextRun := time.Now().Add(1 * time.Hour).UTC().Truncate(time.Second) - err := s.UpdateScheduleAfterRun(ctx, sched.ID, ranAt, newNextRun, "") - require.NoError(t, err) - - got, err := s.GetSchedule(ctx, sched.ID) - require.NoError(t, err) - assert.Equal(t, 1, got.RunCount) - assert.Equal(t, 0, got.ErrorCount) - assert.Equal(t, store.ScheduleRunSuccess, got.LastRunStatus) - assert.Empty(t, got.LastRunError) - assert.NotNil(t, got.LastRunAt) - assert.NotNil(t, got.NextRunAt) - - // Error run - err = s.UpdateScheduleAfterRun(ctx, sched.ID, ranAt, newNextRun, "agent not found") - require.NoError(t, err) - - got, err = s.GetSchedule(ctx, sched.ID) - require.NoError(t, err) - assert.Equal(t, 2, got.RunCount) - assert.Equal(t, 1, got.ErrorCount) - assert.Equal(t, store.ScheduleRunError, got.LastRunStatus) - assert.Equal(t, "agent not found", got.LastRunError) -} - -func TestSchedule_ListDue(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - projectID := createTestProject(t, s) - - now := time.Now().UTC() - - // Create a due schedule (next_run_at in the past) - pastRun := now.Add(-5 * time.Minute) - dueSchedule := &store.Schedule{ - ID: api.NewUUID(), - ProjectID: projectID, - Name: "due-schedule", - CronExpr: "0 * * * *", - EventType: "message", - Payload: "{}", - NextRunAt: &pastRun, - } - require.NoError(t, s.CreateSchedule(ctx, dueSchedule)) - - // Create a future schedule (not due yet) - futureRun := now.Add(1 * time.Hour) - futureSchedule := &store.Schedule{ - ID: api.NewUUID(), - ProjectID: projectID, - Name: "future-schedule", - CronExpr: "0 * * * *", - EventType: "message", - Payload: "{}", - NextRunAt: &futureRun, - } - require.NoError(t, s.CreateSchedule(ctx, futureSchedule)) - - // Create a paused schedule (should not be listed even if due) - pausedSchedule := &store.Schedule{ - ID: api.NewUUID(), - ProjectID: projectID, - Name: "paused-schedule", - CronExpr: "0 * * * *", - EventType: "message", - Payload: "{}", - Status: store.ScheduleStatusPaused, - NextRunAt: &pastRun, - } - require.NoError(t, s.CreateSchedule(ctx, pausedSchedule)) - - // List due schedules - dueSchedules, err := s.ListDueSchedules(ctx, now) - require.NoError(t, err) - assert.Len(t, dueSchedules, 1) - assert.Equal(t, "due-schedule", dueSchedules[0].Name) -} - -func TestScheduledEvent_WithScheduleID(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - projectID := createTestProject(t, s) - - scheduleID := api.NewUUID() - eventID := api.NewUUID() - - evt := &store.ScheduledEvent{ - ID: eventID, - ProjectID: projectID, - EventType: "message", - FireAt: time.Now().UTC(), - Payload: `{"message":"test"}`, - ScheduleID: scheduleID, - } - require.NoError(t, s.CreateScheduledEvent(ctx, evt)) - - // Verify schedule_id is persisted - got, err := s.GetScheduledEvent(ctx, eventID) - require.NoError(t, err) - assert.Equal(t, scheduleID, got.ScheduleID) - - // Filter by schedule_id - result, err := s.ListScheduledEvents(ctx, store.ScheduledEventFilter{ - ProjectID: projectID, - ScheduleID: scheduleID, - }, store.ListOptions{}) - require.NoError(t, err) - assert.Len(t, result.Items, 1) - assert.Equal(t, eventID, result.Items[0].ID) -} diff --git a/pkg/store/sqlite/scheduled_event.go b/pkg/store/sqlite/scheduled_event.go deleted file mode 100644 index ad78b29c2..000000000 --- a/pkg/store/sqlite/scheduled_event.go +++ /dev/null @@ -1,316 +0,0 @@ -// Copyright 2026 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package sqlite - -import ( - "context" - "database/sql" - "errors" - "fmt" - "strings" - "time" - - "github.com/GoogleCloudPlatform/scion/pkg/store" -) - -// ============================================================================ -// Scheduled Event Operations -// ============================================================================ - -// CreateScheduledEvent creates a new scheduled event. -func (s *SQLiteStore) CreateScheduledEvent(ctx context.Context, event *store.ScheduledEvent) error { - if event.ID == "" || event.ProjectID == "" || event.EventType == "" { - return store.ErrInvalidInput - } - - now := time.Now() - if event.CreatedAt.IsZero() { - event.CreatedAt = now - } - if event.Status == "" { - event.Status = store.ScheduledEventPending - } - - _, err := s.db.ExecContext(ctx, ` - INSERT INTO scheduled_events ( - id, project_id, event_type, fire_at, payload, status, - created_at, created_by, fired_at, error, schedule_id - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - `, - event.ID, event.ProjectID, event.EventType, event.FireAt, event.Payload, event.Status, - event.CreatedAt, nullableString(event.CreatedBy), nullableTime(timeFromPtr(event.FiredAt)), nullableString(event.Error), - nullableString(event.ScheduleID), - ) - if err != nil { - if strings.Contains(err.Error(), "UNIQUE constraint failed") { - return store.ErrAlreadyExists - } - if strings.Contains(err.Error(), "FOREIGN KEY constraint failed") { - return fmt.Errorf("project %s does not exist: %w", event.ProjectID, store.ErrInvalidInput) - } - return err - } - return nil -} - -// GetScheduledEvent retrieves a scheduled event by ID. -func (s *SQLiteStore) GetScheduledEvent(ctx context.Context, id string) (*store.ScheduledEvent, error) { - event := &store.ScheduledEvent{} - var createdBy sql.NullString - var firedAt sql.NullTime - var errMsg sql.NullString - var scheduleID sql.NullString - - err := s.db.QueryRowContext(ctx, ` - SELECT id, project_id, event_type, fire_at, payload, status, - created_at, created_by, fired_at, error, schedule_id - FROM scheduled_events WHERE id = ? - `, id).Scan( - &event.ID, &event.ProjectID, &event.EventType, &event.FireAt, &event.Payload, &event.Status, - &event.CreatedAt, &createdBy, &firedAt, &errMsg, &scheduleID, - ) - if err != nil { - if errors.Is(err, sql.ErrNoRows) { - return nil, store.ErrNotFound - } - return nil, err - } - - if createdBy.Valid { - event.CreatedBy = createdBy.String - } - if firedAt.Valid { - event.FiredAt = &firedAt.Time - } - if errMsg.Valid { - event.Error = errMsg.String - } - if scheduleID.Valid { - event.ScheduleID = scheduleID.String - } - - return event, nil -} - -// ListPendingScheduledEvents returns all events with status "pending", -// ordered by fire_at ASC. -func (s *SQLiteStore) ListPendingScheduledEvents(ctx context.Context) ([]store.ScheduledEvent, error) { - rows, err := s.db.QueryContext(ctx, ` - SELECT id, project_id, event_type, fire_at, payload, status, - created_at, created_by, fired_at, error, schedule_id - FROM scheduled_events - WHERE status = ? - ORDER BY fire_at ASC - `, store.ScheduledEventPending) - if err != nil { - return nil, err - } - defer rows.Close() - - return scanScheduledEvents(rows) -} - -// UpdateScheduledEventStatus updates the status and optional error for an event. -func (s *SQLiteStore) UpdateScheduledEventStatus(ctx context.Context, id string, status string, firedAt *time.Time, errMsg string) error { - _, err := s.db.ExecContext(ctx, ` - UPDATE scheduled_events SET status = ?, fired_at = ?, error = ? - WHERE id = ? - `, status, nullableTime(timeFromPtr(firedAt)), nullableString(errMsg), id) - return err -} - -// CancelScheduledEvent marks an event as cancelled. -// Returns ErrNotFound if the event doesn't exist or is not pending. -func (s *SQLiteStore) CancelScheduledEvent(ctx context.Context, id string) error { - result, err := s.db.ExecContext(ctx, ` - UPDATE scheduled_events SET status = ? - WHERE id = ? AND status = ? - `, store.ScheduledEventCancelled, id, store.ScheduledEventPending) - if err != nil { - return err - } - - rows, err := result.RowsAffected() - if err != nil { - return err - } - if rows == 0 { - return store.ErrNotFound - } - return nil -} - -// ListScheduledEvents returns events matching the filter criteria. -func (s *SQLiteStore) ListScheduledEvents(ctx context.Context, filter store.ScheduledEventFilter, opts store.ListOptions) (*store.ListResult[store.ScheduledEvent], error) { - var conditions []string - var args []interface{} - - if filter.ProjectID != "" { - conditions = append(conditions, "project_id = ?") - args = append(args, filter.ProjectID) - } - if filter.EventType != "" { - conditions = append(conditions, "event_type = ?") - args = append(args, filter.EventType) - } - if filter.Status != "" { - conditions = append(conditions, "status = ?") - args = append(args, filter.Status) - } - if filter.ScheduleID != "" { - conditions = append(conditions, "schedule_id = ?") - args = append(args, filter.ScheduleID) - } - - whereClause := "" - if len(conditions) > 0 { - whereClause = "WHERE " + strings.Join(conditions, " AND ") - } - - // Get total count - var totalCount int - countQuery := fmt.Sprintf("SELECT COUNT(*) FROM scheduled_events %s", whereClause) - if err := s.db.QueryRowContext(ctx, countQuery, args...).Scan(&totalCount); err != nil { - return nil, err - } - - // Apply pagination - limit := opts.Limit - if limit <= 0 { - limit = 50 - } - if limit > 200 { - limit = 200 - } - - query := fmt.Sprintf(` - SELECT id, project_id, event_type, fire_at, payload, status, - created_at, created_by, fired_at, error, schedule_id - FROM scheduled_events %s - ORDER BY created_at DESC - LIMIT ? - `, whereClause) - - queryArgs := append(args, limit+1) //nolint:gocritic // intentional append to copy - - if opts.Cursor != "" { - query = fmt.Sprintf(` - SELECT id, project_id, event_type, fire_at, payload, status, - created_at, created_by, fired_at, error, schedule_id - FROM scheduled_events %s AND id < ? - ORDER BY created_at DESC - LIMIT ? - `, whereClause) - if whereClause == "" { - query = ` - SELECT id, project_id, event_type, fire_at, payload, status, - created_at, created_by, fired_at, error, schedule_id - FROM scheduled_events WHERE id < ? - ORDER BY created_at DESC - LIMIT ? - ` - } - queryArgs = append(args, opts.Cursor, limit+1) //nolint:gocritic - } - - rows, err := s.db.QueryContext(ctx, query, queryArgs...) - if err != nil { - return nil, err - } - defer rows.Close() - - events, err := scanScheduledEvents(rows) - if err != nil { - return nil, err - } - - result := &store.ListResult[store.ScheduledEvent]{ - TotalCount: totalCount, - } - - if len(events) > limit { - result.Items = events[:limit] - result.NextCursor = events[limit-1].ID - } else { - result.Items = events - } - - return result, nil -} - -// PurgeOldScheduledEvents removes non-pending events older than cutoff. -func (s *SQLiteStore) PurgeOldScheduledEvents(ctx context.Context, cutoff time.Time) (int, error) { - result, err := s.db.ExecContext(ctx, - "DELETE FROM scheduled_events WHERE status != ? AND created_at < ?", - store.ScheduledEventPending, cutoff, - ) - if err != nil { - return 0, err - } - rowsAffected, err := result.RowsAffected() - if err != nil { - return 0, err - } - return int(rowsAffected), nil -} - -// ============================================================================ -// Helpers -// ============================================================================ - -// timeFromPtr returns the time from a pointer, or zero time if nil. -func timeFromPtr(t *time.Time) time.Time { - if t == nil { - return time.Time{} - } - return *t -} - -// scanScheduledEvents scans rows into ScheduledEvent slices. -func scanScheduledEvents(rows *sql.Rows) ([]store.ScheduledEvent, error) { - var events []store.ScheduledEvent - for rows.Next() { - var event store.ScheduledEvent - var createdBy sql.NullString - var firedAt sql.NullTime - var errMsg sql.NullString - var scheduleID sql.NullString - - if err := rows.Scan( - &event.ID, &event.ProjectID, &event.EventType, &event.FireAt, &event.Payload, &event.Status, - &event.CreatedAt, &createdBy, &firedAt, &errMsg, &scheduleID, - ); err != nil { - return nil, err - } - - if createdBy.Valid { - event.CreatedBy = createdBy.String - } - if firedAt.Valid { - event.FiredAt = &firedAt.Time - } - if errMsg.Valid { - event.Error = errMsg.String - } - if scheduleID.Valid { - event.ScheduleID = scheduleID.String - } - events = append(events, event) - } - if err := rows.Err(); err != nil { - return nil, err - } - return events, nil -} diff --git a/pkg/store/sqlite/scheduled_event_test.go b/pkg/store/sqlite/scheduled_event_test.go deleted file mode 100644 index 0447bc636..000000000 --- a/pkg/store/sqlite/scheduled_event_test.go +++ /dev/null @@ -1,372 +0,0 @@ -// Copyright 2026 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//go:build !no_sqlite - -package sqlite - -import ( - "context" - "testing" - "time" - - "github.com/GoogleCloudPlatform/scion/pkg/api" - "github.com/GoogleCloudPlatform/scion/pkg/store" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -// createTestProject creates a project for scheduled event tests. -func createTestProject(t *testing.T, s *SQLiteStore) string { - t.Helper() - ctx := context.Background() - - projectID := api.NewUUID() - project := &store.Project{ - ID: projectID, - Name: "Scheduled Event Test Project", - Slug: "sched-project-" + projectID[:8], - Visibility: store.VisibilityPrivate, - } - require.NoError(t, s.CreateProject(ctx, project)) - return projectID -} - -func TestScheduledEventCRUD(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - projectID := createTestProject(t, s) - - eventID := api.NewUUID() - fireAt := time.Now().Add(1 * time.Hour).UTC().Truncate(time.Second) - - evt := &store.ScheduledEvent{ - ID: eventID, - ProjectID: projectID, - EventType: "message", - FireAt: fireAt, - Payload: `{"text":"hello"}`, - CreatedBy: "user-123", - } - - // Create - err := s.CreateScheduledEvent(ctx, evt) - require.NoError(t, err) - assert.False(t, evt.CreatedAt.IsZero(), "CreatedAt should be set automatically") - assert.Equal(t, store.ScheduledEventPending, evt.Status) - - // Get - got, err := s.GetScheduledEvent(ctx, eventID) - require.NoError(t, err) - assert.Equal(t, eventID, got.ID) - assert.Equal(t, projectID, got.ProjectID) - assert.Equal(t, "message", got.EventType) - assert.Equal(t, fireAt, got.FireAt.UTC().Truncate(time.Second)) - assert.Equal(t, `{"text":"hello"}`, got.Payload) - assert.Equal(t, store.ScheduledEventPending, got.Status) - assert.Equal(t, "user-123", got.CreatedBy) - assert.Nil(t, got.FiredAt) - assert.Empty(t, got.Error) - - // Get not found - _, err = s.GetScheduledEvent(ctx, "nonexistent") - assert.ErrorIs(t, err, store.ErrNotFound) -} - -func TestScheduledEventCreateValidation(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - // Missing ID - err := s.CreateScheduledEvent(ctx, &store.ScheduledEvent{ - ProjectID: "project-1", - EventType: "message", - }) - assert.ErrorIs(t, err, store.ErrInvalidInput) - - // Missing ProjectID - err = s.CreateScheduledEvent(ctx, &store.ScheduledEvent{ - ID: api.NewUUID(), - EventType: "message", - }) - assert.ErrorIs(t, err, store.ErrInvalidInput) - - // Missing EventType - err = s.CreateScheduledEvent(ctx, &store.ScheduledEvent{ - ID: api.NewUUID(), - ProjectID: "project-1", - }) - assert.ErrorIs(t, err, store.ErrInvalidInput) - - // Non-existent project (FK constraint) - err = s.CreateScheduledEvent(ctx, &store.ScheduledEvent{ - ID: api.NewUUID(), - ProjectID: "nonexistent-project", - EventType: "message", - Payload: "{}", - }) - assert.ErrorIs(t, err, store.ErrInvalidInput) -} - -func TestScheduledEventListPending(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - projectID := createTestProject(t, s) - - // Create events with different statuses - pending1 := &store.ScheduledEvent{ - ID: api.NewUUID(), - ProjectID: projectID, - EventType: "message", - FireAt: time.Now().Add(2 * time.Hour), - Payload: "{}", - Status: store.ScheduledEventPending, - } - pending2 := &store.ScheduledEvent{ - ID: api.NewUUID(), - ProjectID: projectID, - EventType: "message", - FireAt: time.Now().Add(1 * time.Hour), // Fires sooner - Payload: "{}", - Status: store.ScheduledEventPending, - } - require.NoError(t, s.CreateScheduledEvent(ctx, pending1)) - require.NoError(t, s.CreateScheduledEvent(ctx, pending2)) - - // Mark one as fired to exclude it - now := time.Now() - require.NoError(t, s.UpdateScheduledEventStatus(ctx, pending1.ID, store.ScheduledEventFired, &now, "")) - - // ListPending should only return the pending one - events, err := s.ListPendingScheduledEvents(ctx) - require.NoError(t, err) - assert.Len(t, events, 1) - assert.Equal(t, pending2.ID, events[0].ID) -} - -func TestScheduledEventListPendingOrderByFireAt(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - projectID := createTestProject(t, s) - - // Create events in reverse fire_at order - later := &store.ScheduledEvent{ - ID: api.NewUUID(), - ProjectID: projectID, - EventType: "message", - FireAt: time.Now().Add(3 * time.Hour), - Payload: "{}", - } - sooner := &store.ScheduledEvent{ - ID: api.NewUUID(), - ProjectID: projectID, - EventType: "message", - FireAt: time.Now().Add(1 * time.Hour), - Payload: "{}", - } - require.NoError(t, s.CreateScheduledEvent(ctx, later)) - require.NoError(t, s.CreateScheduledEvent(ctx, sooner)) - - events, err := s.ListPendingScheduledEvents(ctx) - require.NoError(t, err) - require.Len(t, events, 2) - // Should be ordered by fire_at ASC (sooner first) - assert.Equal(t, sooner.ID, events[0].ID) - assert.Equal(t, later.ID, events[1].ID) -} - -func TestScheduledEventUpdateStatus(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - projectID := createTestProject(t, s) - - eventID := api.NewUUID() - evt := &store.ScheduledEvent{ - ID: eventID, - ProjectID: projectID, - EventType: "message", - FireAt: time.Now().Add(1 * time.Hour), - Payload: "{}", - } - require.NoError(t, s.CreateScheduledEvent(ctx, evt)) - - // Update to fired with firedAt - now := time.Now().UTC().Truncate(time.Second) - err := s.UpdateScheduledEventStatus(ctx, eventID, store.ScheduledEventFired, &now, "") - require.NoError(t, err) - - got, err := s.GetScheduledEvent(ctx, eventID) - require.NoError(t, err) - assert.Equal(t, store.ScheduledEventFired, got.Status) - require.NotNil(t, got.FiredAt) - assert.Equal(t, now, got.FiredAt.UTC().Truncate(time.Second)) - assert.Empty(t, got.Error) - - // Update with error - err = s.UpdateScheduledEventStatus(ctx, eventID, store.ScheduledEventExpired, &now, "handler failed") - require.NoError(t, err) - - got, err = s.GetScheduledEvent(ctx, eventID) - require.NoError(t, err) - assert.Equal(t, store.ScheduledEventExpired, got.Status) - assert.Equal(t, "handler failed", got.Error) -} - -func TestScheduledEventCancel(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - projectID := createTestProject(t, s) - - eventID := api.NewUUID() - evt := &store.ScheduledEvent{ - ID: eventID, - ProjectID: projectID, - EventType: "message", - FireAt: time.Now().Add(1 * time.Hour), - Payload: "{}", - } - require.NoError(t, s.CreateScheduledEvent(ctx, evt)) - - // Cancel pending event - err := s.CancelScheduledEvent(ctx, eventID) - require.NoError(t, err) - - got, err := s.GetScheduledEvent(ctx, eventID) - require.NoError(t, err) - assert.Equal(t, store.ScheduledEventCancelled, got.Status) - - // Cancel again (not pending anymore) — should return ErrNotFound - err = s.CancelScheduledEvent(ctx, eventID) - assert.ErrorIs(t, err, store.ErrNotFound) - - // Cancel non-existent event - err = s.CancelScheduledEvent(ctx, "nonexistent") - assert.ErrorIs(t, err, store.ErrNotFound) -} - -func TestScheduledEventListWithFilter(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - projectID1 := createTestProject(t, s) - projectID2 := createTestProject(t, s) - - // Create events across projects and types - events := []*store.ScheduledEvent{ - {ID: api.NewUUID(), ProjectID: projectID1, EventType: "message", FireAt: time.Now().Add(1 * time.Hour), Payload: "{}"}, - {ID: api.NewUUID(), ProjectID: projectID1, EventType: "status_update", FireAt: time.Now().Add(2 * time.Hour), Payload: "{}"}, - {ID: api.NewUUID(), ProjectID: projectID2, EventType: "message", FireAt: time.Now().Add(3 * time.Hour), Payload: "{}"}, - } - for _, evt := range events { - require.NoError(t, s.CreateScheduledEvent(ctx, evt)) - } - - // Filter by project - result, err := s.ListScheduledEvents(ctx, store.ScheduledEventFilter{ProjectID: projectID1}, store.ListOptions{}) - require.NoError(t, err) - assert.Len(t, result.Items, 2) - assert.Equal(t, 2, result.TotalCount) - - // Filter by event type - result, err = s.ListScheduledEvents(ctx, store.ScheduledEventFilter{EventType: "message"}, store.ListOptions{}) - require.NoError(t, err) - assert.Len(t, result.Items, 2) - - // Filter by status - result, err = s.ListScheduledEvents(ctx, store.ScheduledEventFilter{Status: store.ScheduledEventPending}, store.ListOptions{}) - require.NoError(t, err) - assert.Len(t, result.Items, 3) - - // No results - result, err = s.ListScheduledEvents(ctx, store.ScheduledEventFilter{Status: store.ScheduledEventFired}, store.ListOptions{}) - require.NoError(t, err) - assert.Len(t, result.Items, 0) -} - -func TestScheduledEventPurge(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - projectID := createTestProject(t, s) - - // Create events: one pending, one fired (old), one cancelled (old), one fired (recent) - pendingEvt := &store.ScheduledEvent{ - ID: api.NewUUID(), ProjectID: projectID, EventType: "message", - FireAt: time.Now().Add(1 * time.Hour), Payload: "{}", - } - firedOldEvt := &store.ScheduledEvent{ - ID: api.NewUUID(), ProjectID: projectID, EventType: "message", - FireAt: time.Now().Add(-48 * time.Hour), Payload: "{}", - CreatedAt: time.Now().Add(-48 * time.Hour), - } - cancelledOldEvt := &store.ScheduledEvent{ - ID: api.NewUUID(), ProjectID: projectID, EventType: "message", - FireAt: time.Now().Add(-48 * time.Hour), Payload: "{}", - CreatedAt: time.Now().Add(-48 * time.Hour), - } - firedRecentEvt := &store.ScheduledEvent{ - ID: api.NewUUID(), ProjectID: projectID, EventType: "message", - FireAt: time.Now().Add(-1 * time.Hour), Payload: "{}", - } - - require.NoError(t, s.CreateScheduledEvent(ctx, pendingEvt)) - require.NoError(t, s.CreateScheduledEvent(ctx, firedOldEvt)) - require.NoError(t, s.CreateScheduledEvent(ctx, cancelledOldEvt)) - require.NoError(t, s.CreateScheduledEvent(ctx, firedRecentEvt)) - - // Mark statuses - now := time.Now() - require.NoError(t, s.UpdateScheduledEventStatus(ctx, firedOldEvt.ID, store.ScheduledEventFired, &now, "")) - require.NoError(t, s.UpdateScheduledEventStatus(ctx, cancelledOldEvt.ID, store.ScheduledEventCancelled, nil, "")) - require.NoError(t, s.UpdateScheduledEventStatus(ctx, firedRecentEvt.ID, store.ScheduledEventFired, &now, "")) - - // Purge events older than 24 hours - cutoff := time.Now().Add(-24 * time.Hour) - purged, err := s.PurgeOldScheduledEvents(ctx, cutoff) - require.NoError(t, err) - // Should purge firedOldEvt and cancelledOldEvt (non-pending, created > 24h ago) - assert.Equal(t, 2, purged) - - // Pending event should still exist - _, err = s.GetScheduledEvent(ctx, pendingEvt.ID) - assert.NoError(t, err) - - // Recently fired event should still exist - _, err = s.GetScheduledEvent(ctx, firedRecentEvt.ID) - assert.NoError(t, err) - - // Old events should be gone - _, err = s.GetScheduledEvent(ctx, firedOldEvt.ID) - assert.ErrorIs(t, err, store.ErrNotFound) - _, err = s.GetScheduledEvent(ctx, cancelledOldEvt.ID) - assert.ErrorIs(t, err, store.ErrNotFound) -} - -func TestScheduledEventOptionalCreatedBy(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - projectID := createTestProject(t, s) - - evt := &store.ScheduledEvent{ - ID: api.NewUUID(), - ProjectID: projectID, - EventType: "message", - FireAt: time.Now().Add(1 * time.Hour), - Payload: "{}", - // No CreatedBy - } - require.NoError(t, s.CreateScheduledEvent(ctx, evt)) - - got, err := s.GetScheduledEvent(ctx, evt.ID) - require.NoError(t, err) - assert.Empty(t, got.CreatedBy) -} diff --git a/pkg/store/sqlite/secret_type_test.go b/pkg/store/sqlite/secret_type_test.go deleted file mode 100644 index e038d14c6..000000000 --- a/pkg/store/sqlite/secret_type_test.go +++ /dev/null @@ -1,219 +0,0 @@ -// Copyright 2026 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//go:build !no_sqlite - -package sqlite - -import ( - "context" - "testing" - - "github.com/GoogleCloudPlatform/scion/pkg/store" - "github.com/google/uuid" -) - -func TestSecretCRUDWithType(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - t.Run("create secret with default type", func(t *testing.T) { - secret := &store.Secret{ - ID: uuid.New().String(), - Key: "API_KEY", - EncryptedValue: "encrypted-value", - Scope: store.ScopeUser, - ScopeID: "user-1", - } - if err := s.CreateSecret(ctx, secret); err != nil { - t.Fatalf("CreateSecret failed: %v", err) - } - if secret.SecretType != store.SecretTypeEnvironment { - t.Errorf("expected default SecretType %q, got %q", store.SecretTypeEnvironment, secret.SecretType) - } - if secret.Target != "API_KEY" { - t.Errorf("expected default Target %q, got %q", "API_KEY", secret.Target) - } - }) - - t.Run("get secret returns type and target", func(t *testing.T) { - got, err := s.GetSecret(ctx, "API_KEY", store.ScopeUser, "user-1") - if err != nil { - t.Fatalf("GetSecret failed: %v", err) - } - if got.SecretType != store.SecretTypeEnvironment { - t.Errorf("expected SecretType %q, got %q", store.SecretTypeEnvironment, got.SecretType) - } - if got.Target != "API_KEY" { - t.Errorf("expected Target %q, got %q", "API_KEY", got.Target) - } - }) - - t.Run("create file secret with explicit type and target", func(t *testing.T) { - secret := &store.Secret{ - ID: uuid.New().String(), - Key: "TLS_CERT", - EncryptedValue: "cert-data", - SecretType: store.SecretTypeFile, - Target: "/etc/ssl/certs/cert.pem", - Scope: store.ScopeUser, - ScopeID: "user-1", - } - if err := s.CreateSecret(ctx, secret); err != nil { - t.Fatalf("CreateSecret failed: %v", err) - } - - got, err := s.GetSecret(ctx, "TLS_CERT", store.ScopeUser, "user-1") - if err != nil { - t.Fatalf("GetSecret failed: %v", err) - } - if got.SecretType != store.SecretTypeFile { - t.Errorf("expected SecretType %q, got %q", store.SecretTypeFile, got.SecretType) - } - if got.Target != "/etc/ssl/certs/cert.pem" { - t.Errorf("expected Target %q, got %q", "/etc/ssl/certs/cert.pem", got.Target) - } - }) - - t.Run("create variable secret", func(t *testing.T) { - secret := &store.Secret{ - ID: uuid.New().String(), - Key: "CONFIG_JSON", - EncryptedValue: `{"key":"value"}`, - SecretType: store.SecretTypeVariable, - Target: "config", - Scope: store.ScopeUser, - ScopeID: "user-1", - } - if err := s.CreateSecret(ctx, secret); err != nil { - t.Fatalf("CreateSecret failed: %v", err) - } - - got, err := s.GetSecret(ctx, "CONFIG_JSON", store.ScopeUser, "user-1") - if err != nil { - t.Fatalf("GetSecret failed: %v", err) - } - if got.SecretType != store.SecretTypeVariable { - t.Errorf("expected SecretType %q, got %q", store.SecretTypeVariable, got.SecretType) - } - if got.Target != "config" { - t.Errorf("expected Target %q, got %q", "config", got.Target) - } - }) - - t.Run("update secret preserves type and target", func(t *testing.T) { - got, err := s.GetSecret(ctx, "TLS_CERT", store.ScopeUser, "user-1") - if err != nil { - t.Fatalf("GetSecret failed: %v", err) - } - - got.EncryptedValue = "updated-cert-data" - got.Target = "/etc/ssl/certs/new-cert.pem" - if err := s.UpdateSecret(ctx, got); err != nil { - t.Fatalf("UpdateSecret failed: %v", err) - } - - updated, err := s.GetSecret(ctx, "TLS_CERT", store.ScopeUser, "user-1") - if err != nil { - t.Fatalf("GetSecret after update failed: %v", err) - } - if updated.SecretType != store.SecretTypeFile { - t.Errorf("expected SecretType %q after update, got %q", store.SecretTypeFile, updated.SecretType) - } - if updated.Target != "/etc/ssl/certs/new-cert.pem" { - t.Errorf("expected Target %q after update, got %q", "/etc/ssl/certs/new-cert.pem", updated.Target) - } - if updated.Version != 2 { - t.Errorf("expected Version 2, got %d", updated.Version) - } - }) - - t.Run("list secrets returns type and target", func(t *testing.T) { - secrets, err := s.ListSecrets(ctx, store.SecretFilter{ - Scope: store.ScopeUser, - ScopeID: "user-1", - }) - if err != nil { - t.Fatalf("ListSecrets failed: %v", err) - } - if len(secrets) != 3 { - t.Fatalf("expected 3 secrets, got %d", len(secrets)) - } - - // Secrets should be ordered by key - for _, s := range secrets { - if s.SecretType == "" { - t.Errorf("secret %q has empty SecretType", s.Key) - } - if s.Target == "" { - t.Errorf("secret %q has empty Target", s.Key) - } - } - }) - - t.Run("list secrets with type filter", func(t *testing.T) { - secrets, err := s.ListSecrets(ctx, store.SecretFilter{ - Scope: store.ScopeUser, - ScopeID: "user-1", - Type: store.SecretTypeFile, - }) - if err != nil { - t.Fatalf("ListSecrets with type filter failed: %v", err) - } - if len(secrets) != 1 { - t.Fatalf("expected 1 file secret, got %d", len(secrets)) - } - if secrets[0].Key != "TLS_CERT" { - t.Errorf("expected key %q, got %q", "TLS_CERT", secrets[0].Key) - } - }) - - t.Run("upsert secret with type", func(t *testing.T) { - secret := &store.Secret{ - ID: uuid.New().String(), - Key: "NEW_SECRET", - EncryptedValue: "value", - SecretType: store.SecretTypeVariable, - Target: "new_key", - Scope: store.ScopeUser, - ScopeID: "user-1", - } - - created, err := s.UpsertSecret(ctx, secret) - if err != nil { - t.Fatalf("UpsertSecret (create) failed: %v", err) - } - if !created { - t.Error("expected UpsertSecret to create a new secret") - } - - // Upsert again (update) - secret.EncryptedValue = "updated-value" - created, err = s.UpsertSecret(ctx, secret) - if err != nil { - t.Fatalf("UpsertSecret (update) failed: %v", err) - } - if created { - t.Error("expected UpsertSecret to update existing secret") - } - - got, err := s.GetSecret(ctx, "NEW_SECRET", store.ScopeUser, "user-1") - if err != nil { - t.Fatalf("GetSecret after upsert failed: %v", err) - } - if got.SecretType != store.SecretTypeVariable { - t.Errorf("expected SecretType %q, got %q", store.SecretTypeVariable, got.SecretType) - } - }) -} diff --git a/pkg/store/sqlite/sqlite.go b/pkg/store/sqlite/sqlite.go deleted file mode 100644 index d69d9f4ae..000000000 --- a/pkg/store/sqlite/sqlite.go +++ /dev/null @@ -1,6021 +0,0 @@ -// Copyright 2026 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package sqlite provides a SQLite implementation of the Store interface. -package sqlite - -import ( - "context" - "database/sql" - "encoding/json" - "errors" - "fmt" - "strconv" - "strings" - "sync/atomic" - "time" - - "github.com/GoogleCloudPlatform/scion/pkg/store" -) - -var memDBCounter atomic.Int64 - -// SQLiteStore implements the Store interface using SQLite. -type SQLiteStore struct { - db *sql.DB -} - -// New creates a new SQLite store with the given database path. -// Use ":memory:" for an in-memory database. -func New(dbPath string) (*SQLiteStore, error) { - dsn := buildDSN(dbPath) - db, err := sql.Open("sqlite", dsn) - if err != nil { - if strings.Contains(err.Error(), "unknown driver") { - return nil, fmt.Errorf("sqlite driver not registered; was the binary built with -tags no_sqlite? %w", err) - } - return nil, fmt.Errorf("failed to open database: %w", err) - } - - // WAL mode allows concurrent readers alongside a single writer. - // PRAGMAs are applied per-connection via DSN _pragma parameters, - // so each pooled connection gets them automatically. - db.SetMaxOpenConns(4) - db.SetMaxIdleConns(4) - - return &SQLiteStore{db: db}, nil -} - -// buildDSN converts a database path into a file: URI with per-connection -// PRAGMA parameters for the modernc.org/sqlite driver. -func buildDSN(dbPath string) string { - pragmas := "_pragma=busy_timeout(5000)&_pragma=foreign_keys(1)&_pragma=journal_mode(WAL)" - - switch { - case dbPath == ":memory:": - id := memDBCounter.Add(1) - return fmt.Sprintf("file:memdb%d?mode=memory&cache=shared&%s", id, pragmas) - case strings.HasPrefix(dbPath, "file:"): - if strings.Contains(dbPath, "?") { - return dbPath + "&" + pragmas - } - return dbPath + "?" + pragmas - default: - return "file:" + dbPath + "?" + pragmas - } -} - -// Close closes the database connection. -func (s *SQLiteStore) Close() error { - return s.db.Close() -} - -// DB returns the underlying *sql.DB for direct access in tests. -func (s *SQLiteStore) DB() *sql.DB { - return s.db -} - -// Ping checks database connectivity. -func (s *SQLiteStore) Ping(ctx context.Context) error { - return s.db.PingContext(ctx) -} - -// Migrate applies database migrations. -func (s *SQLiteStore) Migrate(ctx context.Context) error { - migrations := []any{ - migrationV1, - migrationV2, - migrationV3, - migrationV4, - migrationV5, - migrationV6, - migrationV7, - migrationV8, - migrationV9, - migrationV10, - migrationV11, - migrationV12, - migrationV13, - migrationV14, - migrationV15, - migrationV16, - migrationV17, - migrationV18, - migrationV19, - migrationV20, - migrationV21, - migrationV22, - migrationV23, - migrationV24, - migrationV25, - migrationV26, - migrationV27, - migrationV28, - migrationV29, - migrationV30, - migrationV31, - migrationV32, - migrationV33, - migrationV34, - migrationV35, - migrationV36, - migrationV37, - migrationV38, - migrationV39, - migrationV40, - migrationV41, - migrationV42, - migrationV43, - migrationV44, - migrationV45, - migrationV46, - migrationV47, - migrationV48, - migrationV49, - migrateV50, - migrationV51, - migrationV52, - migrationV53, - } - - // Create migrations table if not exists - if _, err := s.db.ExecContext(ctx, ` - CREATE TABLE IF NOT EXISTS schema_migrations ( - version INTEGER PRIMARY KEY, - applied_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP - ) - `); err != nil { - return fmt.Errorf("failed to create migrations table: %w", err) - } - - // Get current version - var currentVersion int - err := s.db.QueryRowContext(ctx, "SELECT COALESCE(MAX(version), 0) FROM schema_migrations").Scan(¤tVersion) - if err != nil { - return fmt.Errorf("failed to get current schema version: %w", err) - } - - // Migrations that require PRAGMA foreign_keys=OFF around the transaction. - // SQLite ignores PRAGMA changes inside transactions, so we must disable - // foreign keys before BeginTx and re-enable after Commit. Without this, - // DROP TABLE on a parent table triggers ON DELETE CASCADE on child tables. - foreignKeysOffMigrations := map[int]bool{ - 40: true, // V40 drops and recreates the projects table - } - - // Apply pending migrations - for i, migration := range migrations { - version := i + 1 - if version <= currentVersion { - continue - } - - switch m := migration.(type) { - case string: - needsFKOff := foreignKeysOffMigrations[version] - - if needsFKOff { - if err := s.applyMigrationWithFKOff(ctx, version, m); err != nil { - return err - } - continue - } - - tx, err := s.db.BeginTx(ctx, nil) - if err != nil { - return fmt.Errorf("failed to start transaction for migration %d: %w", version, err) - } - - if _, err := tx.ExecContext(ctx, m); err != nil { - tx.Rollback() - return fmt.Errorf("failed to apply migration %d: %w", version, err) - } - - if _, err := tx.ExecContext(ctx, "INSERT INTO schema_migrations (version) VALUES (?)", version); err != nil { - tx.Rollback() - return fmt.Errorf("failed to record migration %d: %w", version, err) - } - - if err := tx.Commit(); err != nil { - return fmt.Errorf("failed to commit migration %d: %w", version, err) - } - - case func(ctx context.Context, tx *sql.Tx) error: - tx, err := s.db.BeginTx(ctx, nil) - if err != nil { - return fmt.Errorf("failed to start transaction for migration %d: %w", version, err) - } - - if err := m(ctx, tx); err != nil { - tx.Rollback() - return fmt.Errorf("failed to apply migration %d: %w", version, err) - } - - if _, err := tx.ExecContext(ctx, "INSERT INTO schema_migrations (version) VALUES (?)", version); err != nil { - tx.Rollback() - return fmt.Errorf("failed to record migration %d: %w", version, err) - } - - if err := tx.Commit(); err != nil { - return fmt.Errorf("failed to commit migration %d: %w", version, err) - } - - default: - return fmt.Errorf("migration %d: unsupported type %T", version, migration) - } - } - - return nil -} - -// applyMigrationWithFKOff runs a migration that requires PRAGMA -// foreign_keys=OFF. It pins a single pooled connection to ensure the -// PRAGMA, transaction, and PRAGMA-restore all share the same connection. -func (s *SQLiteStore) applyMigrationWithFKOff(ctx context.Context, version int, migration string) error { - conn, err := s.db.Conn(ctx) - if err != nil { - return fmt.Errorf("failed to get connection for migration %d: %w", version, err) - } - defer conn.Close() - - if _, err := conn.ExecContext(ctx, "PRAGMA foreign_keys=OFF"); err != nil { - return fmt.Errorf("failed to disable foreign keys for migration %d: %w", version, err) - } - defer conn.ExecContext(ctx, "PRAGMA foreign_keys=ON") //nolint:errcheck - - tx, err := conn.BeginTx(ctx, nil) - if err != nil { - return fmt.Errorf("failed to start transaction for migration %d: %w", version, err) - } - - if _, err := tx.ExecContext(ctx, migration); err != nil { - tx.Rollback() - return fmt.Errorf("failed to apply migration %d: %w", version, err) - } - - if _, err := tx.ExecContext(ctx, "INSERT INTO schema_migrations (version) VALUES (?)", version); err != nil { - tx.Rollback() - return fmt.Errorf("failed to record migration %d: %w", version, err) - } - - if err := tx.Commit(); err != nil { - return fmt.Errorf("failed to commit migration %d: %w", version, err) - } - - return nil -} - -// Migration V1: Initial schema -const migrationV1 = ` --- Projects table -CREATE TABLE IF NOT EXISTS groves ( - id TEXT PRIMARY KEY, - name TEXT NOT NULL, - slug TEXT NOT NULL, - git_remote TEXT UNIQUE, - labels TEXT, - annotations TEXT, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - created_by TEXT, - owner_id TEXT, - visibility TEXT NOT NULL DEFAULT 'private' -); -CREATE INDEX IF NOT EXISTS idx_groves_slug ON groves(slug); -CREATE INDEX IF NOT EXISTS idx_groves_git_remote ON groves(git_remote); -CREATE INDEX IF NOT EXISTS idx_groves_owner ON groves(owner_id); - --- Runtime brokers table -CREATE TABLE IF NOT EXISTS runtime_brokers ( - id TEXT PRIMARY KEY, - name TEXT NOT NULL, - slug TEXT NOT NULL, - type TEXT NOT NULL, - mode TEXT NOT NULL DEFAULT 'connected', - version TEXT, - status TEXT NOT NULL DEFAULT 'offline', - connection_state TEXT DEFAULT 'disconnected', - last_heartbeat TIMESTAMP, - capabilities TEXT, - supported_harnesses TEXT, - resources TEXT, - runtimes TEXT, - labels TEXT, - annotations TEXT, - endpoint TEXT, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP -); -CREATE INDEX IF NOT EXISTS idx_runtime_brokers_slug ON runtime_brokers(slug); -CREATE INDEX IF NOT EXISTS idx_runtime_brokers_status ON runtime_brokers(status); - --- Project contributors (many-to-many relationship) -CREATE TABLE IF NOT EXISTS grove_contributors ( - grove_id TEXT NOT NULL, - broker_id TEXT NOT NULL, - broker_name TEXT NOT NULL, - mode TEXT NOT NULL DEFAULT 'connected', - status TEXT NOT NULL DEFAULT 'offline', - profiles TEXT, - last_seen TIMESTAMP, - PRIMARY KEY (grove_id, broker_id), - FOREIGN KEY (grove_id) REFERENCES groves(id) ON DELETE CASCADE, - FOREIGN KEY (broker_id) REFERENCES runtime_brokers(id) ON DELETE CASCADE -); - --- Agents table -CREATE TABLE IF NOT EXISTS agents ( - id TEXT PRIMARY KEY, - agent_id TEXT NOT NULL, - name TEXT NOT NULL, - template TEXT NOT NULL, - grove_id TEXT NOT NULL, - labels TEXT, - annotations TEXT, - status TEXT NOT NULL DEFAULT 'pending', - connection_state TEXT DEFAULT 'unknown', - container_status TEXT, - session_status TEXT, - runtime_state TEXT, - image TEXT, - detached INTEGER NOT NULL DEFAULT 1, - runtime TEXT, - runtime_broker_id TEXT, - web_pty_enabled INTEGER NOT NULL DEFAULT 0, - task_summary TEXT, - applied_config TEXT, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - last_seen TIMESTAMP, - created_by TEXT, - owner_id TEXT, - visibility TEXT NOT NULL DEFAULT 'private', - state_version INTEGER NOT NULL DEFAULT 1, - FOREIGN KEY (grove_id) REFERENCES groves(id) ON DELETE CASCADE, - FOREIGN KEY (runtime_broker_id) REFERENCES runtime_brokers(id) ON DELETE SET NULL -); --- Use (agent_id, grove_id) order to match Ent schema's (slug, project_id) -CREATE UNIQUE INDEX IF NOT EXISTS idx_agents_grove_slug ON agents(agent_id, grove_id); -CREATE INDEX IF NOT EXISTS idx_agents_grove ON agents(grove_id); -CREATE INDEX IF NOT EXISTS idx_agents_status ON agents(status); -CREATE INDEX IF NOT EXISTS idx_agents_runtime_broker ON agents(runtime_broker_id); - --- Templates table -CREATE TABLE IF NOT EXISTS templates ( - id TEXT PRIMARY KEY, - name TEXT NOT NULL, - slug TEXT NOT NULL, - harness TEXT NOT NULL, - image TEXT, - config TEXT, - scope TEXT NOT NULL DEFAULT 'global', - grove_id TEXT, - storage_uri TEXT, - owner_id TEXT, - visibility TEXT NOT NULL DEFAULT 'private', - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY (grove_id) REFERENCES groves(id) ON DELETE CASCADE -); -CREATE INDEX IF NOT EXISTS idx_templates_slug_scope ON templates(slug, scope); -CREATE INDEX IF NOT EXISTS idx_templates_harness ON templates(harness); - --- Users table -CREATE TABLE IF NOT EXISTS users ( - id TEXT PRIMARY KEY, - email TEXT UNIQUE NOT NULL, - display_name TEXT NOT NULL, - avatar_url TEXT, - role TEXT NOT NULL DEFAULT 'member', - status TEXT NOT NULL DEFAULT 'active', - preferences TEXT, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - last_login TIMESTAMP -); -CREATE INDEX IF NOT EXISTS idx_users_email ON users(email); -` - -// Migration V2: Add default_runtime_broker_id to groves -const migrationV2 = ` --- Add default runtime broker to groves -ALTER TABLE groves ADD COLUMN default_runtime_broker_id TEXT REFERENCES runtime_brokers(id) ON DELETE SET NULL; -CREATE INDEX IF NOT EXISTS idx_groves_default_runtime_broker ON groves(default_runtime_broker_id); -` - -// Migration V3: Add local_path to grove_contributors -const migrationV3 = ` --- Add local_path column to grove_contributors for tracking filesystem paths per broker -ALTER TABLE grove_contributors ADD COLUMN local_path TEXT; -` - -// Migration V4: Add environment variables and secrets tables -const migrationV4 = ` --- Environment variables table -CREATE TABLE IF NOT EXISTS env_vars ( - id TEXT PRIMARY KEY, - key TEXT NOT NULL, - value TEXT NOT NULL, - scope TEXT NOT NULL, - scope_id TEXT NOT NULL, - description TEXT, - sensitive INTEGER NOT NULL DEFAULT 0, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - created_by TEXT -); -CREATE UNIQUE INDEX IF NOT EXISTS idx_env_vars_key_scope ON env_vars(key, scope, scope_id); -CREATE INDEX IF NOT EXISTS idx_env_vars_scope ON env_vars(scope, scope_id); - --- Secrets table -CREATE TABLE IF NOT EXISTS secrets ( - id TEXT PRIMARY KEY, - key TEXT NOT NULL, - encrypted_value TEXT NOT NULL, - scope TEXT NOT NULL, - scope_id TEXT NOT NULL, - description TEXT, - version INTEGER NOT NULL DEFAULT 1, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - created_by TEXT, - updated_by TEXT -); -CREATE UNIQUE INDEX IF NOT EXISTS idx_secrets_key_scope ON secrets(key, scope, scope_id); -CREATE INDEX IF NOT EXISTS idx_secrets_scope ON secrets(scope, scope_id); -` - -// Migration V5: Groups and Policies (Hub Permissions System) -const migrationV5 = ` --- Groups table -CREATE TABLE IF NOT EXISTS groups ( - id TEXT PRIMARY KEY, - name TEXT NOT NULL, - slug TEXT UNIQUE NOT NULL, - description TEXT, - parent_id TEXT REFERENCES groups(id) ON DELETE SET NULL, - labels TEXT, - annotations TEXT, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - created_by TEXT, - owner_id TEXT -); -CREATE INDEX IF NOT EXISTS idx_groups_slug ON groups(slug); -CREATE INDEX IF NOT EXISTS idx_groups_parent ON groups(parent_id); -CREATE INDEX IF NOT EXISTS idx_groups_owner ON groups(owner_id); - --- Group members table (users and nested groups) -CREATE TABLE IF NOT EXISTS group_members ( - group_id TEXT NOT NULL, - member_type TEXT NOT NULL, -- 'user' or 'group' - member_id TEXT NOT NULL, - role TEXT NOT NULL DEFAULT 'member', - added_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - added_by TEXT, - PRIMARY KEY (group_id, member_type, member_id), - FOREIGN KEY (group_id) REFERENCES groups(id) ON DELETE CASCADE -); -CREATE INDEX IF NOT EXISTS idx_group_members_member ON group_members(member_type, member_id); - --- Policies table -CREATE TABLE IF NOT EXISTS policies ( - id TEXT PRIMARY KEY, - name TEXT NOT NULL, - description TEXT, - scope_type TEXT NOT NULL, - scope_id TEXT, - resource_type TEXT NOT NULL DEFAULT '*', - resource_id TEXT, - actions TEXT NOT NULL, -- JSON array - effect TEXT NOT NULL, - conditions TEXT, -- JSON object - priority INTEGER NOT NULL DEFAULT 0, - labels TEXT, - annotations TEXT, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - created_by TEXT -); -CREATE INDEX IF NOT EXISTS idx_policies_scope ON policies(scope_type, scope_id); -CREATE INDEX IF NOT EXISTS idx_policies_effect ON policies(effect); -CREATE INDEX IF NOT EXISTS idx_policies_priority ON policies(priority DESC); - --- Policy bindings table -CREATE TABLE IF NOT EXISTS policy_bindings ( - policy_id TEXT NOT NULL, - principal_type TEXT NOT NULL, -- 'user' or 'group' - principal_id TEXT NOT NULL, - PRIMARY KEY (policy_id, principal_type, principal_id), - FOREIGN KEY (policy_id) REFERENCES policies(id) ON DELETE CASCADE -); -CREATE INDEX IF NOT EXISTS idx_policy_bindings_principal ON policy_bindings(principal_type, principal_id); -` - -// Migration V6: Extend templates table for hosted template management -const migrationV6 = ` --- Add new columns to templates table -ALTER TABLE templates ADD COLUMN display_name TEXT; -ALTER TABLE templates ADD COLUMN description TEXT; -ALTER TABLE templates ADD COLUMN content_hash TEXT; -ALTER TABLE templates ADD COLUMN scope_id TEXT; -ALTER TABLE templates ADD COLUMN storage_bucket TEXT; -ALTER TABLE templates ADD COLUMN storage_path TEXT; -ALTER TABLE templates ADD COLUMN files TEXT; -ALTER TABLE templates ADD COLUMN base_template TEXT; -ALTER TABLE templates ADD COLUMN locked INTEGER NOT NULL DEFAULT 0; -ALTER TABLE templates ADD COLUMN status TEXT NOT NULL DEFAULT 'active'; -ALTER TABLE templates ADD COLUMN created_by TEXT; -ALTER TABLE templates ADD COLUMN updated_by TEXT; - --- Add indexes for new columns -CREATE INDEX IF NOT EXISTS idx_templates_status ON templates(status); -CREATE INDEX IF NOT EXISTS idx_templates_content_hash ON templates(content_hash); -CREATE INDEX IF NOT EXISTS idx_templates_scope_id ON templates(scope, scope_id); -` - -const migrationV7 = ` --- Add API keys table -CREATE TABLE IF NOT EXISTS api_keys ( - id TEXT PRIMARY KEY, - user_id TEXT NOT NULL, - name TEXT NOT NULL, - prefix TEXT NOT NULL, - key_hash TEXT NOT NULL UNIQUE, - scopes TEXT, - revoked INTEGER NOT NULL DEFAULT 0, - expires_at TIMESTAMP, - last_used TIMESTAMP, - created_at TIMESTAMP NOT NULL, - FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE -); - --- Add indexes for API keys -CREATE INDEX IF NOT EXISTS idx_api_keys_user_id ON api_keys(user_id); -CREATE INDEX IF NOT EXISTS idx_api_keys_key_hash ON api_keys(key_hash); -CREATE INDEX IF NOT EXISTS idx_api_keys_prefix ON api_keys(prefix); -` - -const migrationV8 = ` --- Add message column to agents table -ALTER TABLE agents ADD COLUMN message TEXT; -` - -// Migration V9: Broker secrets and join tokens for Runtime Broker authentication -const migrationV9 = ` --- Broker secrets table for HMAC-based authentication -CREATE TABLE IF NOT EXISTS broker_secrets ( - broker_id TEXT PRIMARY KEY, - secret_key BLOB NOT NULL, - algorithm TEXT NOT NULL DEFAULT 'hmac-sha256', - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - rotated_at TIMESTAMP, - expires_at TIMESTAMP, - status TEXT NOT NULL DEFAULT 'active', - FOREIGN KEY (broker_id) REFERENCES runtime_brokers(id) ON DELETE CASCADE -); - --- Broker join tokens table for registration bootstrap -CREATE TABLE IF NOT EXISTS broker_join_tokens ( - broker_id TEXT PRIMARY KEY, - token_hash TEXT NOT NULL UNIQUE, - expires_at TIMESTAMP NOT NULL, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - created_by TEXT NOT NULL, - FOREIGN KEY (broker_id) REFERENCES runtime_brokers(id) ON DELETE CASCADE -); -CREATE INDEX IF NOT EXISTS idx_broker_join_tokens_hash ON broker_join_tokens(token_hash); -CREATE INDEX IF NOT EXISTS idx_broker_join_tokens_expires ON broker_join_tokens(expires_at); -` - -// Migration V10: Add user tracking to grove_contributors and runtime_brokers -const migrationV10 = ` --- Add linked_by and linked_at columns to grove_contributors for tracking who linked a broker -ALTER TABLE grove_contributors ADD COLUMN linked_by TEXT; -ALTER TABLE grove_contributors ADD COLUMN linked_at TIMESTAMP; - --- Add created_by column to runtime_brokers for tracking who registered the broker -ALTER TABLE runtime_brokers ADD COLUMN created_by TEXT; -` - -// Migration V11: Add auto_provide column to runtime_brokers -const migrationV11 = ` --- Add auto_provide column to runtime_brokers for automatic project provider registration -ALTER TABLE runtime_brokers ADD COLUMN auto_provide INTEGER NOT NULL DEFAULT 0; -` - -// Migration V12: Add injection_mode and secret columns to env_vars -const migrationV12 = ` -ALTER TABLE env_vars ADD COLUMN injection_mode TEXT NOT NULL DEFAULT 'as_needed'; -ALTER TABLE env_vars ADD COLUMN secret INTEGER NOT NULL DEFAULT 0; -` - -const migrationV13 = ` -ALTER TABLE secrets ADD COLUMN secret_type TEXT NOT NULL DEFAULT 'environment'; -ALTER TABLE secrets ADD COLUMN target TEXT; -` - -const migrationV14 = ` -ALTER TABLE secrets ADD COLUMN secret_ref TEXT; -` - -const migrationV15 = ` -UPDATE agents SET status = session_status WHERE session_status IS NOT NULL AND session_status != ''; -ALTER TABLE agents DROP COLUMN session_status; -` - -// Migration V16: Add harness_configs table -const migrationV16 = ` -CREATE TABLE IF NOT EXISTS harness_configs ( - id TEXT PRIMARY KEY, - name TEXT NOT NULL, - slug TEXT NOT NULL, - display_name TEXT, - description TEXT, - harness TEXT NOT NULL, - config TEXT, - content_hash TEXT, - scope TEXT NOT NULL DEFAULT 'global', - scope_id TEXT, - storage_uri TEXT, - storage_bucket TEXT, - storage_path TEXT, - files TEXT, - locked INTEGER NOT NULL DEFAULT 0, - status TEXT NOT NULL DEFAULT 'active', - owner_id TEXT, - created_by TEXT, - updated_by TEXT, - visibility TEXT NOT NULL DEFAULT 'private', - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP -); -CREATE INDEX IF NOT EXISTS idx_harness_configs_slug_scope ON harness_configs(slug, scope); -CREATE INDEX IF NOT EXISTS idx_harness_configs_harness ON harness_configs(harness); -CREATE INDEX IF NOT EXISTS idx_harness_configs_status ON harness_configs(status); -CREATE INDEX IF NOT EXISTS idx_harness_configs_content_hash ON harness_configs(content_hash); -CREATE INDEX IF NOT EXISTS idx_harness_configs_scope_id ON harness_configs(scope, scope_id); -` - -// Migration V17: Add deleted_at column to agents for soft-delete support -const migrationV17 = ` -ALTER TABLE agents ADD COLUMN deleted_at TIMESTAMP; -CREATE INDEX IF NOT EXISTS idx_agents_deleted ON agents(status, deleted_at) WHERE status = 'deleted'; -` - -// Migration V18: Notification subscriptions and notifications tables -const migrationV18 = ` -CREATE TABLE IF NOT EXISTS notification_subscriptions ( - id TEXT PRIMARY KEY, - agent_id TEXT NOT NULL, - subscriber_type TEXT NOT NULL DEFAULT 'agent', - subscriber_id TEXT NOT NULL, - grove_id TEXT NOT NULL, - trigger_statuses TEXT NOT NULL, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - created_by TEXT NOT NULL, - FOREIGN KEY (agent_id) REFERENCES agents(id) ON DELETE CASCADE -); -CREATE INDEX IF NOT EXISTS idx_notification_subs_agent ON notification_subscriptions(agent_id); -CREATE INDEX IF NOT EXISTS idx_notification_subs_project ON notification_subscriptions(grove_id); - -CREATE TABLE IF NOT EXISTS notifications ( - id TEXT PRIMARY KEY, - subscription_id TEXT NOT NULL, - agent_id TEXT NOT NULL, - grove_id TEXT NOT NULL, - subscriber_type TEXT NOT NULL, - subscriber_id TEXT NOT NULL, - status TEXT NOT NULL, - message TEXT NOT NULL, - dispatched INTEGER NOT NULL DEFAULT 0, - acknowledged INTEGER NOT NULL DEFAULT 0, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY (subscription_id) REFERENCES notification_subscriptions(id) ON DELETE CASCADE -); -CREATE INDEX IF NOT EXISTS idx_notifications_subscriber ON notifications(subscriber_type, subscriber_id); -CREATE INDEX IF NOT EXISTS idx_notifications_project ON notifications(grove_id); -` - -const migrationV19 = ` -CREATE TABLE IF NOT EXISTS scheduled_events ( - id TEXT PRIMARY KEY, - grove_id TEXT NOT NULL, - event_type TEXT NOT NULL, - fire_at TIMESTAMP NOT NULL, - payload TEXT NOT NULL, - status TEXT NOT NULL DEFAULT 'pending', - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - created_by TEXT, - fired_at TIMESTAMP, - error TEXT, - - FOREIGN KEY (grove_id) REFERENCES groves(id) ON DELETE CASCADE -); -CREATE INDEX IF NOT EXISTS idx_scheduled_events_status ON scheduled_events(status); -CREATE INDEX IF NOT EXISTS idx_scheduled_events_fire_at ON scheduled_events(fire_at) WHERE status = 'pending'; -CREATE INDEX IF NOT EXISTS idx_scheduled_events_project ON scheduled_events(grove_id); -` - -const migrationV20 = ` -ALTER TABLE agents ADD COLUMN phase TEXT NOT NULL DEFAULT 'created'; -ALTER TABLE agents ADD COLUMN activity TEXT DEFAULT ''; -ALTER TABLE agents ADD COLUMN tool_name TEXT DEFAULT ''; - --- Backfill phase/activity from existing status values -UPDATE agents SET phase = 'created' WHERE status IN ('created', 'pending'); -UPDATE agents SET phase = 'provisioning' WHERE status = 'provisioning'; -UPDATE agents SET phase = 'cloning' WHERE status = 'cloning'; -UPDATE agents SET phase = 'running', activity = 'idle' WHERE status = 'running'; -UPDATE agents SET phase = 'stopped' WHERE status = 'stopped'; -UPDATE agents SET phase = 'error' WHERE status = 'error'; -UPDATE agents SET phase = 'running', activity = 'thinking' WHERE status = 'busy'; -UPDATE agents SET phase = 'running', activity = 'idle' WHERE status = 'idle'; -UPDATE agents SET phase = 'running', activity = 'waiting_for_input' WHERE status = 'waiting_for_input'; -UPDATE agents SET phase = 'running', activity = 'completed' WHERE status = 'completed'; -UPDATE agents SET phase = 'running', activity = 'limits_exceeded' WHERE status = 'limits_exceeded'; -UPDATE agents SET phase = 'stopped' WHERE status IN ('deleted', 'restored'); -UPDATE agents SET phase = 'running', activity = 'offline' WHERE status = 'undetermined'; - -CREATE INDEX IF NOT EXISTS idx_agents_phase ON agents(phase); -` - -// Migration V21: Remove legacy status column from agents table. -// Phase 6 of the agent state refactor — the status column is superseded by -// the phase/activity columns added in V20. -const migrationV21 = ` --- Backfill any remaining agents where phase was not set -UPDATE agents SET phase = status WHERE (phase = '' OR phase IS NULL) AND status IN ('created','provisioning','cloning','starting','running','stopping','stopped','error'); -UPDATE agents SET phase = 'created' WHERE (phase = '' OR phase IS NULL) AND status = 'pending'; -UPDATE agents SET phase = 'stopped' WHERE (phase = '' OR phase IS NULL) AND status = 'deleted'; - --- Backfill activity from status for running agents -UPDATE agents SET activity = status WHERE phase = 'running' AND (activity = '' OR activity IS NULL) AND status IN ('idle','waiting_for_input','completed','limits_exceeded','offline'); -UPDATE agents SET activity = 'thinking' WHERE phase = 'running' AND (activity = '' OR activity IS NULL) AND status = 'busy'; - --- Update soft-delete index: rely on deleted_at instead of status -DROP INDEX IF EXISTS idx_agents_deleted; -CREATE INDEX IF NOT EXISTS idx_agents_deleted ON agents(deleted_at) WHERE deleted_at IS NOT NULL; - --- Drop the status index before dropping the column -DROP INDEX IF EXISTS idx_agents_status; - --- Drop the status column (SQLite supports this from 3.35.0+) -ALTER TABLE agents DROP COLUMN status; -` - -// Migration V22: Rename trigger_statuses to trigger_activities in notification_subscriptions. -const migrationV22 = ` -ALTER TABLE notification_subscriptions RENAME COLUMN trigger_statuses TO trigger_activities; -` - -// Migration V23: Add injection_mode column to secrets -const migrationV23 = ` -ALTER TABLE secrets ADD COLUMN injection_mode TEXT NOT NULL DEFAULT 'as_needed'; -` - -// Migration V24: Add last_activity_event column to agents for stalled detection. -// Backfills existing agents to prevent false positives on upgrade. -const migrationV24 = ` -ALTER TABLE agents ADD COLUMN last_activity_event TIMESTAMP; -UPDATE agents SET last_activity_event = COALESCE(last_seen, updated_at, created_at); -` - -// Migration V25: Add stalled_from_activity column for stalled detection. -// Records the activity that was active when the agent was marked stalled, -// so heartbeats can distinguish "still stuck" from "genuinely recovered". -const migrationV25 = ` -ALTER TABLE agents ADD COLUMN stalled_from_activity TEXT DEFAULT ''; -` - -// Migration V26: Add limits tracking columns to agents table. -// These fields are updated by sciontool status reports from inside the container. -const migrationV26 = ` -ALTER TABLE agents ADD COLUMN current_turns INTEGER DEFAULT 0; -ALTER TABLE agents ADD COLUMN current_model_calls INTEGER DEFAULT 0; -ALTER TABLE agents ADD COLUMN started_at TIMESTAMP; -` - -const migrationV27 = ` -ALTER TABLE users ADD COLUMN last_seen TIMESTAMP; -` - -// Migration V28: Add shared_dirs column to groves table. -// Stores project-level shared directory configuration as JSON. -const migrationV28 = ` -ALTER TABLE groves ADD COLUMN shared_dirs TEXT DEFAULT ''; -` - -// Migration V29: Add group_type and grove_id columns to groups table. -// These enable filtering groups by type and project association. -const migrationV29 = ` -ALTER TABLE groups ADD COLUMN group_type TEXT NOT NULL DEFAULT 'explicit'; -ALTER TABLE groups ADD COLUMN grove_id TEXT DEFAULT ''; -CREATE INDEX IF NOT EXISTS idx_groups_project ON groups(grove_id); -` - -// Migration V30: Create gcp_service_accounts table for GCP identity management. -const migrationV30 = ` -CREATE TABLE IF NOT EXISTS gcp_service_accounts ( - id TEXT PRIMARY KEY, - scope TEXT NOT NULL, - scope_id TEXT NOT NULL, - email TEXT NOT NULL, - grove_id TEXT NOT NULL, - display_name TEXT NOT NULL DEFAULT '', - default_scopes TEXT NOT NULL DEFAULT '', - verified INTEGER NOT NULL DEFAULT 0, - verified_at TIMESTAMP, - created_by TEXT NOT NULL DEFAULT '', - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - UNIQUE(email, scope, scope_id) -); -CREATE INDEX IF NOT EXISTS idx_gcp_sa_scope ON gcp_service_accounts(scope, scope_id); -` - -// Migration V31: Add scope column to notification_subscriptions and make agent_id nullable. -// Enables project-scoped subscriptions (watch all agents in a project) in addition to -// agent-scoped subscriptions. Adds unique constraint for deduplication. -const migrationV31 = ` --- SQLite doesn't support ALTER COLUMN, so we recreate the table. -CREATE TABLE notification_subscriptions_new ( - id TEXT PRIMARY KEY, - scope TEXT NOT NULL DEFAULT 'agent', - agent_id TEXT, - subscriber_type TEXT NOT NULL DEFAULT 'agent', - subscriber_id TEXT NOT NULL, - grove_id TEXT NOT NULL, - trigger_activities TEXT NOT NULL, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - created_by TEXT NOT NULL, - FOREIGN KEY (agent_id) REFERENCES agents(id) ON DELETE CASCADE -); - --- Copy existing data (all existing subscriptions are agent-scoped) -INSERT INTO notification_subscriptions_new - (id, scope, agent_id, subscriber_type, subscriber_id, grove_id, trigger_activities, created_at, created_by) -SELECT id, 'agent', agent_id, subscriber_type, subscriber_id, grove_id, trigger_activities, created_at, created_by -FROM notification_subscriptions; - -DROP TABLE notification_subscriptions; -ALTER TABLE notification_subscriptions_new RENAME TO notification_subscriptions; - --- Recreate indexes -CREATE INDEX IF NOT EXISTS idx_notification_subs_agent ON notification_subscriptions(agent_id); -CREATE INDEX IF NOT EXISTS idx_notification_subs_project ON notification_subscriptions(grove_id); -CREATE INDEX IF NOT EXISTS idx_notification_subs_subscriber ON notification_subscriptions(subscriber_type, subscriber_id); - --- Unique constraint: one subscription per (scope, target, subscriber, project) -CREATE UNIQUE INDEX IF NOT EXISTS idx_notification_subs_unique - ON notification_subscriptions(scope, COALESCE(agent_id, ''), subscriber_type, subscriber_id, grove_id); -` - -// Migration V32: Recurring schedules table and schedule_id FK on scheduled_events. -const migrationV32 = ` -CREATE TABLE IF NOT EXISTS schedules ( - id TEXT PRIMARY KEY, - grove_id TEXT NOT NULL, - name TEXT NOT NULL, - cron_expr TEXT NOT NULL, - event_type TEXT NOT NULL, - payload TEXT NOT NULL DEFAULT '{}', - status TEXT NOT NULL DEFAULT 'active', - next_run_at TIMESTAMP, - last_run_at TIMESTAMP, - last_run_status TEXT, - last_run_error TEXT, - run_count INTEGER NOT NULL DEFAULT 0, - error_count INTEGER NOT NULL DEFAULT 0, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - created_by TEXT, - updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY (grove_id) REFERENCES groves(id) ON DELETE CASCADE, - UNIQUE(grove_id, name) -); -CREATE INDEX IF NOT EXISTS idx_schedules_project ON schedules(grove_id); -CREATE INDEX IF NOT EXISTS idx_schedules_next_run ON schedules(next_run_at) WHERE status = 'active'; - -ALTER TABLE scheduled_events ADD COLUMN schedule_id TEXT DEFAULT ''; -` - -// Migration V33: Subscription templates table. -const migrationV33 = ` -CREATE TABLE IF NOT EXISTS subscription_templates ( - id TEXT PRIMARY KEY, - name TEXT NOT NULL, - scope TEXT NOT NULL DEFAULT 'project', - trigger_activities TEXT NOT NULL, - grove_id TEXT NOT NULL DEFAULT '', - created_by TEXT NOT NULL, - UNIQUE(grove_id, name) -); -CREATE INDEX IF NOT EXISTS idx_sub_templates_project ON subscription_templates(grove_id); -` - -// Migration V34: User access tokens table (replaces api_keys). -const migrationV34 = ` -CREATE TABLE IF NOT EXISTS user_access_tokens ( - id TEXT PRIMARY KEY, - user_id TEXT NOT NULL, - name TEXT NOT NULL, - prefix TEXT NOT NULL, - key_hash TEXT NOT NULL UNIQUE, - grove_id TEXT NOT NULL, - scopes TEXT NOT NULL, - revoked INTEGER NOT NULL DEFAULT 0, - expires_at TIMESTAMP, - last_used TIMESTAMP, - created_at TIMESTAMP NOT NULL, - FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE, - FOREIGN KEY (grove_id) REFERENCES groves(id) ON DELETE CASCADE -); -CREATE INDEX IF NOT EXISTS idx_uat_user_id ON user_access_tokens(user_id); -CREATE INDEX IF NOT EXISTS idx_uat_key_hash ON user_access_tokens(key_hash); -` - -// Migration V35: GitHub App installations and project GitHub App fields. -const migrationV35 = ` -CREATE TABLE IF NOT EXISTS github_installations ( - installation_id INTEGER PRIMARY KEY, - account_login TEXT NOT NULL, - account_type TEXT NOT NULL DEFAULT 'Organization', - app_id INTEGER NOT NULL, - repositories TEXT NOT NULL DEFAULT '[]', - status TEXT NOT NULL DEFAULT 'active', - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP -); -CREATE INDEX IF NOT EXISTS idx_github_installations_account ON github_installations(account_login); -CREATE INDEX IF NOT EXISTS idx_github_installations_status ON github_installations(status); - -ALTER TABLE groves ADD COLUMN github_installation_id INTEGER; -ALTER TABLE groves ADD COLUMN github_permissions TEXT; -ALTER TABLE groves ADD COLUMN github_app_status TEXT; -` - -// Migration V36: Git identity configuration for commit attribution. -const migrationV36 = ` -ALTER TABLE groves ADD COLUMN git_identity TEXT; -` - -// Migration V37: Add ancestry column for transitive access control. -const migrationV37 = ` -ALTER TABLE agents ADD COLUMN ancestry TEXT; -` - -// Migration V38: Backfill ancestry for existing agents from created_by. -const migrationV38 = ` -UPDATE agents SET ancestry = json_array(created_by) -WHERE created_by IS NOT NULL AND created_by != '' AND ancestry IS NULL; -` - -// Migration V39: Messages table for bidirectional human-agent messaging. -const migrationV39 = ` -CREATE TABLE IF NOT EXISTS messages ( - id TEXT PRIMARY KEY, - grove_id TEXT NOT NULL, - sender TEXT NOT NULL, - sender_id TEXT NOT NULL DEFAULT '', - recipient TEXT NOT NULL, - recipient_id TEXT NOT NULL DEFAULT '', - msg TEXT NOT NULL, - type TEXT NOT NULL DEFAULT 'instruction', - urgent INTEGER NOT NULL DEFAULT 0, - broadcasted INTEGER NOT NULL DEFAULT 0, - read INTEGER NOT NULL DEFAULT 0, - agent_id TEXT NOT NULL DEFAULT '', - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP -); - -CREATE INDEX IF NOT EXISTS idx_messages_project ON messages(grove_id); -CREATE INDEX IF NOT EXISTS idx_messages_recipient ON messages(recipient_id, read); -CREATE INDEX IF NOT EXISTS idx_messages_agent ON messages(agent_id); -CREATE INDEX IF NOT EXISTS idx_messages_sender ON messages(sender_id); -CREATE INDEX IF NOT EXISTS idx_messages_created ON messages(created_at DESC); -` - -// Migration V40: Allow multiple groves per git remote (drop UNIQUE on git_remote), -// and enforce slug uniqueness (add UNIQUE on slug). Requires table recreation -// because SQLite does not support ALTER TABLE DROP CONSTRAINT. -// -// IMPORTANT: This migration requires foreign_keys=OFF around the DROP TABLE. -// SQLite ignores PRAGMA changes inside transactions, so the migration runner -// handles this via the foreignKeysOffMigrations set. The PRAGMA statements are -// intentionally NOT included in the SQL string. -const migrationV40 = ` -CREATE TABLE IF NOT EXISTS groves_new ( - id TEXT PRIMARY KEY, - name TEXT NOT NULL, - slug TEXT NOT NULL UNIQUE, - git_remote TEXT, - labels TEXT, - annotations TEXT, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - created_by TEXT, - owner_id TEXT, - visibility TEXT NOT NULL DEFAULT 'private', - default_runtime_broker_id TEXT REFERENCES runtime_brokers(id) ON DELETE SET NULL, - shared_dirs TEXT, - github_installation_id INTEGER REFERENCES github_installations(installation_id), - github_permissions TEXT, - github_app_status TEXT, - git_identity TEXT -); - -INSERT OR IGNORE INTO groves_new SELECT - id, name, slug, git_remote, labels, annotations, - created_at, updated_at, created_by, owner_id, visibility, - default_runtime_broker_id, shared_dirs, - github_installation_id, github_permissions, github_app_status, - git_identity -FROM groves; - -DROP TABLE IF EXISTS groves; -ALTER TABLE groves_new RENAME TO groves; - -CREATE INDEX IF NOT EXISTS idx_groves_slug ON groves(slug); -CREATE INDEX IF NOT EXISTS idx_groves_git_remote ON groves(git_remote); -CREATE INDEX IF NOT EXISTS idx_groves_owner ON groves(owner_id); -CREATE INDEX IF NOT EXISTS idx_groves_default_runtime_broker ON groves(default_runtime_broker_id); -` - -// Migration V41: Maintenance operations tables for the admin maintenance panel. -// Tracks one-time migrations and repeatable operations with execution history. -const migrationV41 = ` -CREATE TABLE IF NOT EXISTS maintenance_operations ( - id TEXT PRIMARY KEY, - key TEXT NOT NULL UNIQUE, - title TEXT NOT NULL, - description TEXT NOT NULL DEFAULT '', - category TEXT NOT NULL, - status TEXT NOT NULL DEFAULT 'pending', - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - started_at TIMESTAMP, - completed_at TIMESTAMP, - started_by TEXT, - result TEXT, - metadata TEXT NOT NULL DEFAULT '{}' -); -CREATE INDEX IF NOT EXISTS idx_maintenance_ops_category ON maintenance_operations(category); -CREATE INDEX IF NOT EXISTS idx_maintenance_ops_status ON maintenance_operations(status); - -CREATE TABLE IF NOT EXISTS maintenance_operation_runs ( - id TEXT PRIMARY KEY, - operation_key TEXT NOT NULL, - status TEXT NOT NULL DEFAULT 'running', - started_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - completed_at TIMESTAMP, - started_by TEXT, - result TEXT, - log TEXT NOT NULL DEFAULT '', - FOREIGN KEY (operation_key) REFERENCES maintenance_operations(key) -); -CREATE INDEX IF NOT EXISTS idx_maintenance_runs_key ON maintenance_operation_runs(operation_key); -CREATE INDEX IF NOT EXISTS idx_maintenance_runs_started ON maintenance_operation_runs(started_at DESC); - --- Seed: one-time migrations -INSERT INTO maintenance_operations (id, key, title, description, category, status) -VALUES ( - lower(hex(randomblob(4)) || '-' || hex(randomblob(2)) || '-4' || substr(hex(randomblob(2)),2) || '-' || substr('89ab', abs(random()) % 4 + 1, 1) || substr(hex(randomblob(2)),2) || '-' || hex(randomblob(6))), - 'secret-hub-id-migration', - 'Secret Hub ID Namespace Migration', - 'Migrates hub-scoped secrets from the legacy fixed "hub" scope ID to the per-instance hub ID. Required when upgrading a hub that was created before the hub ID namespacing feature. Only needed for GCP Secret Manager backend.', - 'migration', - 'pending' -); - --- Seed: repeatable operations -INSERT INTO maintenance_operations (id, key, title, description, category, status) -VALUES ( - lower(hex(randomblob(4)) || '-' || hex(randomblob(2)) || '-4' || substr(hex(randomblob(2)),2) || '-' || substr('89ab', abs(random()) % 4 + 1, 1) || substr(hex(randomblob(2)),2) || '-' || hex(randomblob(6))), - 'pull-images', - 'Pull Container Images', - 'Pulls the latest container images for all configured harnesses from the image registry.', - 'operation', - 'pending' -); - -INSERT INTO maintenance_operations (id, key, title, description, category, status) -VALUES ( - lower(hex(randomblob(4)) || '-' || hex(randomblob(2)) || '-4' || substr(hex(randomblob(2)),2) || '-' || substr('89ab', abs(random()) % 4 + 1, 1) || substr(hex(randomblob(2)),2) || '-' || hex(randomblob(6))), - 'rebuild-server', - 'Rebuild Server from Git', - 'Pulls latest code from the repository, rebuilds the server binary and web assets, then restarts the hub service. Equivalent to the fast-deploy mode of gce-start-hub.sh.', - 'operation', - 'pending' -); - -INSERT INTO maintenance_operations (id, key, title, description, category, status) -VALUES ( - lower(hex(randomblob(4)) || '-' || hex(randomblob(2)) || '-4' || substr(hex(randomblob(2)),2) || '-' || substr('89ab', abs(random()) % 4 + 1, 1) || substr(hex(randomblob(2)),2) || '-' || hex(randomblob(6))), - 'rebuild-web', - 'Rebuild Web Frontend', - 'Rebuilds only the web frontend assets from source without restarting the server binary. Changes take effect on the next page load.', - 'operation', - 'pending' -); -` - -const migrationV42 = ` -CREATE TABLE IF NOT EXISTS grove_sync_state ( - grove_id TEXT NOT NULL, - broker_id TEXT NOT NULL DEFAULT '', - last_sync_time TIMESTAMP, - last_commit_sha TEXT, - file_count INTEGER NOT NULL DEFAULT 0, - total_bytes INTEGER NOT NULL DEFAULT 0, - PRIMARY KEY (grove_id, broker_id), - FOREIGN KEY (grove_id) REFERENCES groves(id) ON DELETE CASCADE -); -CREATE INDEX IF NOT EXISTS idx_grove_sync_state_project ON grove_sync_state(grove_id); -` - -// migrationV43 fixes pre-existing signing key secrets that were stored with -// the default secret_type ('environment' or ”) instead of 'internal'. Without -// this, stale rows created before the fix would still be resolved and injected -// into agent containers. -const migrationV43 = ` -UPDATE secrets SET secret_type = 'internal' -WHERE key IN ('agent_signing_key', 'user_signing_key') - AND scope = 'hub' - AND secret_type != 'internal'; -` - -// Migration V44: Add managed and managed_by columns to gcp_service_accounts table -// for hub-minted service accounts. -const migrationV44 = ` -ALTER TABLE gcp_service_accounts ADD COLUMN managed INTEGER NOT NULL DEFAULT 0; -ALTER TABLE gcp_service_accounts ADD COLUMN managed_by TEXT NOT NULL DEFAULT ''; -` - -// Migration V45: Add allow_progeny column to secrets table -const migrationV45 = ` -ALTER TABLE secrets ADD COLUMN allow_progeny INTEGER NOT NULL DEFAULT 0; -` - -const migrationV46 = ` -ALTER TABLE templates ADD COLUMN default_harness_config TEXT; -` - -const migrationV47 = ` -INSERT INTO maintenance_operations (id, key, title, description, category, status) -VALUES ( - lower(hex(randomblob(4)) || '-' || hex(randomblob(2)) || '-4' || substr(hex(randomblob(2)),2) || '-' || substr('89ab', abs(random()) % 4 + 1, 1) || substr(hex(randomblob(2)),2) || '-' || hex(randomblob(6))), - 'rebuild-container-binaries', - 'Rebuild Container Binaries', - 'Rebuilds scion and sciontool binaries for Linux containers (make container-binaries). Only available when SCION_DEV_BINARIES is set. Binaries are written to .build/container/ in the source checkout.', - 'operation', - 'pending' -); -` - -const migrationV48 = ` -CREATE TABLE IF NOT EXISTS allow_list ( - id TEXT PRIMARY KEY, - email TEXT NOT NULL UNIQUE COLLATE NOCASE, - note TEXT NOT NULL DEFAULT '', - added_by TEXT NOT NULL, - invite_id TEXT NOT NULL DEFAULT '', - created DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP -); -` - -const migrationV49 = ` -CREATE TABLE IF NOT EXISTS invite_codes ( - id TEXT PRIMARY KEY, - code_hash TEXT NOT NULL UNIQUE, - code_prefix TEXT NOT NULL, - max_uses INTEGER NOT NULL DEFAULT 1, - use_count INTEGER NOT NULL DEFAULT 0, - expires_at DATETIME NOT NULL, - revoked INTEGER NOT NULL DEFAULT 0, - created_by TEXT NOT NULL, - note TEXT NOT NULL DEFAULT '', - created DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP -); -CREATE INDEX IF NOT EXISTS idx_invite_codes_expires ON invite_codes(expires_at); -` - -// migrateV50 renames 'grove' entities to 'project' idempotently. -// This is Phase 4 of the grove-to-project rename strategy. -// Each rename operation checks whether the old name still exists before -// attempting the rename, so the migration can be re-run safely on databases -// that partially applied an earlier (non-idempotent) version of V50. -func migrateV50(ctx context.Context, tx *sql.Tx) error { - // 1. Rename Tables (check before renaming) - tableRenames := [][2]string{ - {"groves", "projects"}, - {"grove_contributors", "project_contributors"}, - {"grove_sync_state", "project_sync_state"}, - } - for _, r := range tableRenames { - exists, err := tableExists(ctx, tx, r[0]) - if err != nil { - return fmt.Errorf("checking table %s: %w", r[0], err) - } - if exists { - if _, err := tx.ExecContext(ctx, fmt.Sprintf("ALTER TABLE %s RENAME TO %s", r[0], r[1])); err != nil { - return fmt.Errorf("renaming table %s to %s: %w", r[0], r[1], err) - } - } - } - - // 2. Rename Columns (check before renaming) - // After step 1, tables are at their new names. If step 1 was already - // applied in a prior run, the tables are also at their new names. - columnRenames := [][3]string{ - {"project_contributors", "grove_id", "project_id"}, - {"project_sync_state", "grove_id", "project_id"}, - {"agents", "grove_id", "project_id"}, - {"templates", "grove_id", "project_id"}, - {"notification_subscriptions", "grove_id", "project_id"}, - {"notifications", "grove_id", "project_id"}, - {"scheduled_events", "grove_id", "project_id"}, - {"schedules", "grove_id", "project_id"}, - {"subscription_templates", "grove_id", "project_id"}, - {"user_access_tokens", "grove_id", "project_id"}, - {"messages", "grove_id", "project_id"}, - {"groups", "grove_id", "project_id"}, - {"gcp_service_accounts", "grove_id", "project_id"}, - } - for _, r := range columnRenames { - exists, err := columnExists(ctx, tx, r[0], r[1]) - if err != nil { - return fmt.Errorf("checking column %s.%s: %w", r[0], r[1], err) - } - if exists { - if _, err := tx.ExecContext(ctx, fmt.Sprintf("ALTER TABLE %s RENAME COLUMN %s TO %s", r[0], r[1], r[2])); err != nil { - return fmt.Errorf("renaming column %s.%s to %s: %w", r[0], r[1], r[2], err) - } - } - } - - // 3. Update Data Values (already idempotent — UPDATE WHERE is a no-op - // when the old value no longer exists) - dataUpdates := ` -UPDATE env_vars SET scope = 'project' WHERE scope = 'grove'; -UPDATE secrets SET scope = 'project' WHERE scope = 'grove'; -UPDATE policies SET scope_type = 'project' WHERE scope_type = 'grove'; -UPDATE gcp_service_accounts SET scope = 'project' WHERE scope = 'grove'; -UPDATE groups SET group_type = 'project_agents' WHERE group_type = 'grove_agents'; -UPDATE notification_subscriptions SET scope = 'project' WHERE scope = 'grove'; -UPDATE subscription_templates SET scope = 'project' WHERE scope = 'grove'; -UPDATE templates SET scope = 'project' WHERE scope = 'grove'; -UPDATE harness_configs SET scope = 'project' WHERE scope = 'grove'; -` - if _, err := tx.ExecContext(ctx, dataUpdates); err != nil { - return fmt.Errorf("updating data values: %w", err) - } - - // 4. Rename/Recreate Indexes (already idempotent — DROP IF EXISTS / CREATE IF NOT EXISTS) - indexSQL := ` -DROP INDEX IF EXISTS idx_groves_slug; -CREATE UNIQUE INDEX IF NOT EXISTS idx_projects_slug ON projects(slug); -DROP INDEX IF EXISTS idx_groves_git_remote; -CREATE INDEX IF NOT EXISTS idx_projects_git_remote ON projects(git_remote); -DROP INDEX IF EXISTS idx_groves_owner; -CREATE INDEX IF NOT EXISTS idx_projects_owner ON projects(owner_id); -DROP INDEX IF EXISTS idx_groves_default_runtime_broker; -CREATE INDEX IF NOT EXISTS idx_projects_default_runtime_broker ON projects(default_runtime_broker_id); - -DROP INDEX IF EXISTS idx_agents_grove_slug; -DROP INDEX IF EXISTS idx_agents_project_slug; -CREATE UNIQUE INDEX IF NOT EXISTS idx_agents_project_slug ON agents(agent_id, project_id); -DROP INDEX IF EXISTS idx_agents_grove; -CREATE INDEX IF NOT EXISTS idx_agents_project ON agents(project_id); - -DROP INDEX IF EXISTS idx_grove_sync_state_grove; -CREATE INDEX IF NOT EXISTS idx_project_sync_state_project ON project_sync_state(project_id); - -DROP INDEX IF EXISTS idx_notification_subs_grove; -CREATE INDEX IF NOT EXISTS idx_notification_subs_project ON notification_subscriptions(project_id); - -DROP INDEX IF EXISTS idx_notifications_grove; -CREATE INDEX IF NOT EXISTS idx_notifications_project ON notifications(project_id); - -DROP INDEX IF EXISTS idx_scheduled_events_grove; -CREATE INDEX IF NOT EXISTS idx_scheduled_events_project ON scheduled_events(project_id); - -DROP INDEX IF EXISTS idx_schedules_grove; -CREATE INDEX IF NOT EXISTS idx_schedules_project ON schedules(project_id); - -DROP INDEX IF EXISTS idx_sub_templates_grove; -CREATE INDEX IF NOT EXISTS idx_sub_templates_project ON subscription_templates(project_id); - -DROP INDEX IF EXISTS idx_messages_grove; -CREATE INDEX IF NOT EXISTS idx_messages_project ON messages(project_id); - -DROP INDEX IF EXISTS idx_groups_grove; -CREATE INDEX IF NOT EXISTS idx_groups_project ON groups(project_id); - -DROP INDEX IF EXISTS idx_gcp_sa_grove; -CREATE INDEX IF NOT EXISTS idx_gcp_sa_project ON gcp_service_accounts(project_id); -` - if _, err := tx.ExecContext(ctx, indexSQL); err != nil { - return fmt.Errorf("updating indexes: %w", err) - } - - return nil -} - -// migrationV51 adds group_id to messages for correlating set[] deliveries. -const migrationV51 = ` -ALTER TABLE messages ADD COLUMN group_id TEXT NOT NULL DEFAULT ''; -` - -// migrationV52 renames the idle activity to working for clearer agent state reporting. -const migrationV52 = ` -UPDATE agents SET activity = 'working' WHERE activity = 'idle'; -UPDATE agents SET stalled_from_activity = 'working' WHERE stalled_from_activity = 'idle'; -` - -// migrationV53 adds an index on (created, id) to allow_list for efficient keyset pagination. -// It also ensures the allow_list table exists, because databases created before V48/V49 were -// inserted into the migration sequence already have version 48 recorded with different content -// (the grove-to-project rename that is now V50). On those databases V48 is skipped, so the -// allow_list table was never created. -const migrationV53 = ` -CREATE TABLE IF NOT EXISTS allow_list ( - id TEXT PRIMARY KEY, - email TEXT NOT NULL UNIQUE COLLATE NOCASE, - note TEXT NOT NULL DEFAULT '', - added_by TEXT NOT NULL, - invite_id TEXT NOT NULL DEFAULT '', - created DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP -); -CREATE TABLE IF NOT EXISTS invite_codes ( - id TEXT PRIMARY KEY, - code_hash TEXT NOT NULL UNIQUE, - code_prefix TEXT NOT NULL, - max_uses INTEGER NOT NULL DEFAULT 1, - use_count INTEGER NOT NULL DEFAULT 0, - expires_at DATETIME NOT NULL, - revoked INTEGER NOT NULL DEFAULT 0, - created_by TEXT NOT NULL, - note TEXT NOT NULL DEFAULT '', - created DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP -); -CREATE INDEX IF NOT EXISTS idx_invite_codes_expires ON invite_codes(expires_at); -CREATE INDEX IF NOT EXISTS idx_allow_list_created_id ON allow_list (created DESC, id DESC); -` - -// tableExists checks whether a table with the given name exists in the database. -func tableExists(ctx context.Context, tx *sql.Tx, tableName string) (bool, error) { - var name string - err := tx.QueryRowContext(ctx, - "SELECT name FROM sqlite_master WHERE type='table' AND name=?", tableName, - ).Scan(&name) - if errors.Is(err, sql.ErrNoRows) { - return false, nil - } - if err != nil { - return false, err - } - return true, nil -} - -// columnExists checks whether a column with the given name exists in the specified table. -func columnExists(ctx context.Context, tx *sql.Tx, tableName, columnName string) (bool, error) { - rows, err := tx.QueryContext(ctx, fmt.Sprintf("PRAGMA table_info(%s)", tableName)) - if err != nil { - return false, err - } - defer rows.Close() - - for rows.Next() { - var cid int - var name, ctype string - var notnull int - var dfltValue sql.NullString - var pk int - if err := rows.Scan(&cid, &name, &ctype, ¬null, &dfltValue, &pk); err != nil { - return false, err - } - if name == columnName { - return true, nil - } - } - return false, rows.Err() -} - -// Helper functions for JSON marshaling/unmarshaling -func marshalJSON(v interface{}) string { - if v == nil { - return "" - } - data, err := json.Marshal(v) - if err != nil { - return "" - } - return string(data) -} - -func unmarshalJSON[T any](data string, v *T) { - if data == "" { - return - } - json.Unmarshal([]byte(data), v) -} - -// nullableString returns a sql.NullString for database insertion. -// Empty strings become NULL, which is important for UNIQUE and FK constraints. -func nullableString(s string) sql.NullString { - if s == "" { - return sql.NullString{Valid: false} - } - return sql.NullString{String: s, Valid: true} -} - -// nullableTime returns a sql.NullTime for database insertion. -// Zero time values become NULL. -func nullableTime(t time.Time) sql.NullTime { - if t.IsZero() { - return sql.NullTime{Valid: false} - } - return sql.NullTime{Time: t, Valid: true} -} - -// nullableInt64 returns a sql.NullInt64 for database insertion. -// Nil pointers become NULL. -func nullableInt64(v *int64) sql.NullInt64 { - if v == nil { - return sql.NullInt64{Valid: false} - } - return sql.NullInt64{Int64: *v, Valid: true} -} - -// marshalJSONPtr marshals a pointer value to JSON string, returning empty string for nil pointers. -// Unlike marshalJSON, this correctly detects nil typed pointers. -func marshalJSONPtr[T any](v *T) string { - if v == nil { - return "" - } - data, err := json.Marshal(v) - if err != nil { - return "" - } - return string(data) -} - -// ============================================================================ -// Agent Operations -// ============================================================================ - -func (s *SQLiteStore) CreateAgent(ctx context.Context, agent *store.Agent) error { - now := time.Now() - agent.Created = now - agent.Updated = now - agent.StateVersion = 1 - - _, err := s.db.ExecContext(ctx, ` - INSERT INTO agents ( - id, agent_id, name, template, project_id, - labels, annotations, - phase, activity, tool_name, - connection_state, container_status, runtime_state, - stalled_from_activity, - image, detached, runtime, runtime_broker_id, web_pty_enabled, task_summary, message, - applied_config, - created_at, updated_at, last_seen, last_activity_event, deleted_at, - created_by, owner_id, visibility, state_version, ancestry - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - `, - agent.ID, agent.Slug, agent.Name, agent.Template, agent.ProjectID, - marshalJSON(agent.Labels), marshalJSON(agent.Annotations), - agent.Phase, agent.Activity, agent.ToolName, - agent.ConnectionState, agent.ContainerStatus, agent.RuntimeState, - agent.StalledFromActivity, - agent.Image, agent.Detached, agent.Runtime, nullableString(agent.RuntimeBrokerID), agent.WebPTYEnabled, agent.TaskSummary, agent.Message, - marshalJSON(agent.AppliedConfig), - agent.Created, agent.Updated, nullableTime(agent.LastSeen), nullableTime(agent.LastActivityEvent), nullableTime(agent.DeletedAt), - agent.CreatedBy, agent.OwnerID, agent.Visibility, agent.StateVersion, marshalJSON(agent.Ancestry), - ) - if err != nil { - if strings.Contains(err.Error(), "UNIQUE constraint failed") { - return store.ErrAlreadyExists - } - return err - } - return nil -} - -func (s *SQLiteStore) GetAgent(ctx context.Context, id string) (*store.Agent, error) { - agent := &store.Agent{} - var labels, annotations, appliedConfig string - var lastSeen, lastActivityEvent, deletedAt, startedAt sql.NullTime - var runtimeBrokerID, message, toolName, ancestry sql.NullString - - err := s.db.QueryRowContext(ctx, ` - SELECT id, agent_id, name, template, project_id, - labels, annotations, - phase, activity, tool_name, - connection_state, container_status, runtime_state, - stalled_from_activity, - current_turns, current_model_calls, - image, detached, runtime, runtime_broker_id, web_pty_enabled, task_summary, message, - applied_config, - created_at, updated_at, last_seen, last_activity_event, deleted_at, started_at, - created_by, owner_id, visibility, state_version, ancestry - FROM agents WHERE id = ? - `, id).Scan( - &agent.ID, &agent.Slug, &agent.Name, &agent.Template, &agent.ProjectID, - &labels, &annotations, - &agent.Phase, &agent.Activity, &toolName, - &agent.ConnectionState, &agent.ContainerStatus, &agent.RuntimeState, - &agent.StalledFromActivity, - &agent.CurrentTurns, &agent.CurrentModelCalls, - &agent.Image, &agent.Detached, &agent.Runtime, &runtimeBrokerID, &agent.WebPTYEnabled, &agent.TaskSummary, &message, - &appliedConfig, - &agent.Created, &agent.Updated, &lastSeen, &lastActivityEvent, &deletedAt, &startedAt, - &agent.CreatedBy, &agent.OwnerID, &agent.Visibility, &agent.StateVersion, &ancestry, - ) - if err != nil { - if errors.Is(err, sql.ErrNoRows) { - return nil, store.ErrNotFound - } - return nil, err - } - - unmarshalJSON(labels, &agent.Labels) - unmarshalJSON(annotations, &agent.Annotations) - unmarshalJSON(appliedConfig, &agent.AppliedConfig) - unmarshalJSON(ancestry.String, &agent.Ancestry) - if lastSeen.Valid { - agent.LastSeen = lastSeen.Time - } - if lastActivityEvent.Valid { - agent.LastActivityEvent = lastActivityEvent.Time - } - if deletedAt.Valid { - agent.DeletedAt = deletedAt.Time - } - if startedAt.Valid { - agent.StartedAt = startedAt.Time - } - if runtimeBrokerID.Valid { - agent.RuntimeBrokerID = runtimeBrokerID.String - } - if message.Valid { - agent.Message = message.String - } - if toolName.Valid { - agent.ToolName = toolName.String - } - - return agent, nil -} - -func (s *SQLiteStore) GetAgentBySlug(ctx context.Context, projectID, slug string) (*store.Agent, error) { - agent := &store.Agent{} - var labels, annotations, appliedConfig string - var lastSeen, lastActivityEvent, deletedAt, startedAt sql.NullTime - var runtimeBrokerID, message, toolName, ancestry sql.NullString - - err := s.db.QueryRowContext(ctx, ` - SELECT id, agent_id, name, template, project_id, - labels, annotations, - phase, activity, tool_name, - connection_state, container_status, runtime_state, - stalled_from_activity, - current_turns, current_model_calls, - image, detached, runtime, runtime_broker_id, web_pty_enabled, task_summary, message, - applied_config, - created_at, updated_at, last_seen, last_activity_event, deleted_at, started_at, - created_by, owner_id, visibility, state_version, ancestry - FROM agents WHERE project_id = ? AND agent_id = ? - `, projectID, slug).Scan( - &agent.ID, &agent.Slug, &agent.Name, &agent.Template, &agent.ProjectID, - &labels, &annotations, - &agent.Phase, &agent.Activity, &toolName, - &agent.ConnectionState, &agent.ContainerStatus, &agent.RuntimeState, - &agent.StalledFromActivity, - &agent.CurrentTurns, &agent.CurrentModelCalls, - &agent.Image, &agent.Detached, &agent.Runtime, &runtimeBrokerID, &agent.WebPTYEnabled, &agent.TaskSummary, &message, - &appliedConfig, - &agent.Created, &agent.Updated, &lastSeen, &lastActivityEvent, &deletedAt, &startedAt, - &agent.CreatedBy, &agent.OwnerID, &agent.Visibility, &agent.StateVersion, &ancestry, - ) - if err != nil { - if errors.Is(err, sql.ErrNoRows) { - return nil, store.ErrNotFound - } - return nil, err - } - - unmarshalJSON(labels, &agent.Labels) - unmarshalJSON(annotations, &agent.Annotations) - unmarshalJSON(appliedConfig, &agent.AppliedConfig) - unmarshalJSON(ancestry.String, &agent.Ancestry) - if lastSeen.Valid { - agent.LastSeen = lastSeen.Time - } - if lastActivityEvent.Valid { - agent.LastActivityEvent = lastActivityEvent.Time - } - if deletedAt.Valid { - agent.DeletedAt = deletedAt.Time - } - if startedAt.Valid { - agent.StartedAt = startedAt.Time - } - if runtimeBrokerID.Valid { - agent.RuntimeBrokerID = runtimeBrokerID.String - } - if message.Valid { - agent.Message = message.String - } - if toolName.Valid { - agent.ToolName = toolName.String - } - - return agent, nil -} - -func (s *SQLiteStore) UpdateAgent(ctx context.Context, agent *store.Agent) error { - agent.Updated = time.Now() - newVersion := agent.StateVersion + 1 - - result, err := s.db.ExecContext(ctx, ` - UPDATE agents SET - agent_id = ?, name = ?, template = ?, - labels = ?, annotations = ?, - phase = ?, activity = ?, tool_name = ?, - connection_state = ?, container_status = ?, runtime_state = ?, - stalled_from_activity = ?, - image = ?, detached = ?, runtime = ?, runtime_broker_id = ?, web_pty_enabled = ?, task_summary = ?, message = ?, - applied_config = ?, - updated_at = ?, last_seen = ?, last_activity_event = ?, deleted_at = ?, - owner_id = ?, visibility = ?, state_version = ? - WHERE id = ? AND state_version = ? - `, - agent.Slug, agent.Name, agent.Template, - marshalJSON(agent.Labels), marshalJSON(agent.Annotations), - agent.Phase, agent.Activity, agent.ToolName, - agent.ConnectionState, agent.ContainerStatus, agent.RuntimeState, - agent.StalledFromActivity, - agent.Image, agent.Detached, agent.Runtime, nullableString(agent.RuntimeBrokerID), agent.WebPTYEnabled, agent.TaskSummary, agent.Message, - marshalJSON(agent.AppliedConfig), - agent.Updated, nullableTime(agent.LastSeen), nullableTime(agent.LastActivityEvent), nullableTime(agent.DeletedAt), - agent.OwnerID, agent.Visibility, newVersion, - agent.ID, agent.StateVersion, - ) - if err != nil { - return err - } - - rowsAffected, err := result.RowsAffected() - if err != nil { - return err - } - if rowsAffected == 0 { - // Check if agent exists - var exists bool - s.db.QueryRowContext(ctx, "SELECT 1 FROM agents WHERE id = ?", agent.ID).Scan(&exists) - if !exists { - return store.ErrNotFound - } - return store.ErrVersionConflict - } - - agent.StateVersion = newVersion - return nil -} - -func (s *SQLiteStore) DeleteAgent(ctx context.Context, id string) error { - result, err := s.db.ExecContext(ctx, "DELETE FROM agents WHERE id = ?", id) - if err != nil { - return err - } - rowsAffected, err := result.RowsAffected() - if err != nil { - return err - } - if rowsAffected == 0 { - return store.ErrNotFound - } - return nil -} - -func (s *SQLiteStore) ListAgents(ctx context.Context, filter store.AgentFilter, opts store.ListOptions) (*store.ListResult[store.Agent], error) { - var conditions []string - var args []interface{} - - if len(filter.MemberOrOwnerProjectIDs) > 0 { - // Combine project_id membership with owner_id match using OR - placeholders := make([]string, len(filter.MemberOrOwnerProjectIDs)) - for i, id := range filter.MemberOrOwnerProjectIDs { - placeholders[i] = "?" - args = append(args, id) - } - orParts := []string{"project_id IN (" + strings.Join(placeholders, ",") + ")"} - if filter.OwnerID != "" { - orParts = append(orParts, "owner_id = ?") - args = append(args, filter.OwnerID) - } - conditions = append(conditions, "("+strings.Join(orParts, " OR ")+")") - } else if len(filter.MemberProjectIDs) > 0 { - placeholders := make([]string, len(filter.MemberProjectIDs)) - for i, id := range filter.MemberProjectIDs { - placeholders[i] = "?" - args = append(args, id) - } - conditions = append(conditions, "project_id IN ("+strings.Join(placeholders, ",")+")") - } else if filter.OwnerID != "" { - conditions = append(conditions, "owner_id = ?") - args = append(args, filter.OwnerID) - } - if filter.ExcludeOwnerID != "" { - conditions = append(conditions, "owner_id != ?") - args = append(args, filter.ExcludeOwnerID) - } - if filter.ProjectID != "" { - conditions = append(conditions, "project_id = ?") - args = append(args, filter.ProjectID) - } - if filter.RuntimeBrokerID != "" { - conditions = append(conditions, "runtime_broker_id = ?") - args = append(args, filter.RuntimeBrokerID) - } - if filter.Phase != "" { - conditions = append(conditions, "phase = ?") - args = append(args, filter.Phase) - } - if filter.AncestorID != "" { - conditions = append(conditions, "EXISTS (SELECT 1 FROM json_each(ancestry) WHERE json_each.value = ?)") - args = append(args, filter.AncestorID) - } - - // Exclude soft-deleted agents unless explicitly requested - if !filter.IncludeDeleted { - conditions = append(conditions, "deleted_at IS NULL") - } - - whereClause := "" - if len(conditions) > 0 { - whereClause = "WHERE " + strings.Join(conditions, " AND ") - } - - // Get total count - var totalCount int - countQuery := fmt.Sprintf("SELECT COUNT(*) FROM agents %s", whereClause) - if err := s.db.QueryRowContext(ctx, countQuery, args...).Scan(&totalCount); err != nil { - return nil, err - } - - // Apply pagination - limit := opts.Limit - if limit <= 0 { - limit = 50 - } - if limit > 200 { - limit = 200 - } - - query := fmt.Sprintf(` - SELECT id, agent_id, name, template, project_id, - labels, annotations, - phase, activity, tool_name, - connection_state, container_status, runtime_state, - stalled_from_activity, - current_turns, current_model_calls, - image, detached, runtime, runtime_broker_id, web_pty_enabled, task_summary, message, - applied_config, - created_at, updated_at, last_seen, last_activity_event, deleted_at, started_at, - created_by, owner_id, visibility, state_version, ancestry - FROM agents %s ORDER BY created_at DESC LIMIT ? - `, whereClause) - args = append(args, limit+1) // Fetch one extra to determine if there's a next page - - rows, err := s.db.QueryContext(ctx, query, args...) - if err != nil { - return nil, err - } - defer rows.Close() - - var agents []store.Agent - for rows.Next() { - var agent store.Agent - var labels, annotations, appliedConfig string - var lastSeen, lastActivityEvent, deletedAt, startedAt sql.NullTime - var runtimeBrokerID, message, toolName, ancestry sql.NullString - - if err := rows.Scan( - &agent.ID, &agent.Slug, &agent.Name, &agent.Template, &agent.ProjectID, - &labels, &annotations, - &agent.Phase, &agent.Activity, &toolName, - &agent.ConnectionState, &agent.ContainerStatus, &agent.RuntimeState, - &agent.StalledFromActivity, - &agent.CurrentTurns, &agent.CurrentModelCalls, - &agent.Image, &agent.Detached, &agent.Runtime, &runtimeBrokerID, &agent.WebPTYEnabled, &agent.TaskSummary, &message, - &appliedConfig, - &agent.Created, &agent.Updated, &lastSeen, &lastActivityEvent, &deletedAt, &startedAt, - &agent.CreatedBy, &agent.OwnerID, &agent.Visibility, &agent.StateVersion, &ancestry, - ); err != nil { - return nil, err - } - - unmarshalJSON(labels, &agent.Labels) - unmarshalJSON(annotations, &agent.Annotations) - unmarshalJSON(appliedConfig, &agent.AppliedConfig) - unmarshalJSON(ancestry.String, &agent.Ancestry) - if lastSeen.Valid { - agent.LastSeen = lastSeen.Time - } - if lastActivityEvent.Valid { - agent.LastActivityEvent = lastActivityEvent.Time - } - if deletedAt.Valid { - agent.DeletedAt = deletedAt.Time - } - if startedAt.Valid { - agent.StartedAt = startedAt.Time - } - if runtimeBrokerID.Valid { - agent.RuntimeBrokerID = runtimeBrokerID.String - } - if message.Valid { - agent.Message = message.String - } - if toolName.Valid { - agent.ToolName = toolName.String - } - - agents = append(agents, agent) - } - - result := &store.ListResult[store.Agent]{ - Items: agents, - TotalCount: totalCount, - } - - // Handle pagination - if len(agents) > limit { - result.Items = agents[:limit] - result.NextCursor = agents[limit-1].ID - } - - return result, nil -} - -func (s *SQLiteStore) UpdateAgentStatus(ctx context.Context, id string, su store.AgentStatusUpdate) error { - now := time.Now() - - // When activity is being updated to something other than "executing", - // clear tool_name (it's only meaningful during execution). - // We signal this by setting the activity-provided flag. - activityProvided := su.Activity != "" - - // Prepare nullable values for limits tracking fields - var currentTurnsProvided bool - var currentTurnsVal int - if su.CurrentTurns != nil { - currentTurnsProvided = true - currentTurnsVal = *su.CurrentTurns - } - var currentModelCallsProvided bool - var currentModelCallsVal int - if su.CurrentModelCalls != nil { - currentModelCallsProvided = true - currentModelCallsVal = *su.CurrentModelCalls - } - - result, err := s.db.ExecContext(ctx, ` - UPDATE agents SET - phase = COALESCE(NULLIF(?, ''), phase), - activity = CASE WHEN ? != '' THEN - CASE WHEN phase = 'stopped' - AND activity IN ('crashed', 'limits_exceeded') - AND ? NOT IN ('crashed', 'limits_exceeded') - THEN activity ELSE ? END - ELSE activity END, - tool_name = CASE WHEN ? THEN ? ELSE tool_name END, - message = COALESCE(NULLIF(?, ''), message), - connection_state = COALESCE(NULLIF(?, ''), connection_state), - container_status = COALESCE(NULLIF(?, ''), container_status), - runtime_state = COALESCE(NULLIF(?, ''), runtime_state), - task_summary = COALESCE(NULLIF(?, ''), task_summary), - stalled_from_activity = CASE WHEN ? != '' THEN '' ELSE stalled_from_activity END, - last_activity_event = CASE WHEN ? != '' THEN ? ELSE last_activity_event END, - current_turns = CASE WHEN ? THEN ? ELSE current_turns END, - current_model_calls = CASE WHEN ? THEN ? ELSE current_model_calls END, - started_at = COALESCE(NULLIF(?, ''), started_at), - updated_at = ?, - last_seen = ? - WHERE id = ? - `, - su.Phase, - su.Activity, su.Activity, su.Activity, - activityProvided, su.ToolName, - su.Message, su.ConnectionState, su.ContainerStatus, - su.RuntimeState, su.TaskSummary, - su.Activity, - su.Activity, now, - currentTurnsProvided, currentTurnsVal, - currentModelCallsProvided, currentModelCallsVal, - su.StartedAt, - now, now, id, - ) - if err != nil { - return err - } - - rowsAffected, err := result.RowsAffected() - if err != nil { - return err - } - if rowsAffected == 0 { - return store.ErrNotFound - } - return nil -} - -func (s *SQLiteStore) PurgeDeletedAgents(ctx context.Context, cutoff time.Time) (int, error) { - result, err := s.db.ExecContext(ctx, - "DELETE FROM agents WHERE deleted_at IS NOT NULL AND deleted_at < ?", - cutoff, - ) - if err != nil { - return 0, err - } - rowsAffected, err := result.RowsAffected() - if err != nil { - return 0, err - } - return int(rowsAffected), nil -} - -func (s *SQLiteStore) MarkStaleAgentsOffline(ctx context.Context, threshold time.Time) ([]store.Agent, error) { - tx, err := s.db.BeginTx(ctx, nil) - if err != nil { - return nil, err - } - defer tx.Rollback() - - now := time.Now() - - // Update stale agents to offline activity. - // Only affects agents that: - // - Have reported at least one heartbeat (last_seen IS NOT NULL) - // - Are in the running phase - // - Are not already in a terminal/sticky activity (completed, limits_exceeded, offline) - _, err = tx.ExecContext(ctx, ` - UPDATE agents SET - activity = 'offline', - updated_at = ? - WHERE last_seen < ? - AND last_seen IS NOT NULL - AND phase = 'running' - AND activity NOT IN ('completed', 'limits_exceeded', 'blocked', 'offline') - `, now, threshold) - if err != nil { - return nil, err - } - - // Fetch the agents that were just updated. - rows, err := tx.QueryContext(ctx, ` - SELECT id, agent_id, name, template, project_id, - labels, annotations, - phase, activity, tool_name, - connection_state, container_status, runtime_state, - stalled_from_activity, - current_turns, current_model_calls, - image, detached, runtime, runtime_broker_id, web_pty_enabled, task_summary, message, - applied_config, - created_at, updated_at, last_seen, last_activity_event, deleted_at, started_at, - created_by, owner_id, visibility, state_version, ancestry - FROM agents - WHERE activity = 'offline' AND updated_at = ? - AND last_seen < ? - AND last_seen IS NOT NULL - AND phase = 'running' - `, now, threshold) - if err != nil { - return nil, err - } - defer rows.Close() - - var agents []store.Agent - for rows.Next() { - var agent store.Agent - var labels, annotations, appliedConfig string - var lastSeen, lastActivityEvent, deletedAt, startedAt sql.NullTime - var runtimeBrokerID, message, toolName, ancestry sql.NullString - - if err := rows.Scan( - &agent.ID, &agent.Slug, &agent.Name, &agent.Template, &agent.ProjectID, - &labels, &annotations, - &agent.Phase, &agent.Activity, &toolName, - &agent.ConnectionState, &agent.ContainerStatus, &agent.RuntimeState, - &agent.StalledFromActivity, - &agent.CurrentTurns, &agent.CurrentModelCalls, - &agent.Image, &agent.Detached, &agent.Runtime, &runtimeBrokerID, &agent.WebPTYEnabled, &agent.TaskSummary, &message, - &appliedConfig, - &agent.Created, &agent.Updated, &lastSeen, &lastActivityEvent, &deletedAt, &startedAt, - &agent.CreatedBy, &agent.OwnerID, &agent.Visibility, &agent.StateVersion, &ancestry, - ); err != nil { - return nil, err - } - - unmarshalJSON(labels, &agent.Labels) - unmarshalJSON(annotations, &agent.Annotations) - unmarshalJSON(appliedConfig, &agent.AppliedConfig) - unmarshalJSON(ancestry.String, &agent.Ancestry) - if lastSeen.Valid { - agent.LastSeen = lastSeen.Time - } - if lastActivityEvent.Valid { - agent.LastActivityEvent = lastActivityEvent.Time - } - if deletedAt.Valid { - agent.DeletedAt = deletedAt.Time - } - if startedAt.Valid { - agent.StartedAt = startedAt.Time - } - if runtimeBrokerID.Valid { - agent.RuntimeBrokerID = runtimeBrokerID.String - } - if message.Valid { - agent.Message = message.String - } - if toolName.Valid { - agent.ToolName = toolName.String - } - - agents = append(agents, agent) - } - - if err := tx.Commit(); err != nil { - return nil, err - } - - return agents, nil -} - -func (s *SQLiteStore) MarkStalledAgents(ctx context.Context, activityThreshold, heartbeatRecency time.Time) ([]store.Agent, error) { - tx, err := s.db.BeginTx(ctx, nil) - if err != nil { - return nil, err - } - defer tx.Rollback() - - now := time.Now() - - // Update agents to stalled activity. - // Only affects agents that: - // - Have a stale last_activity_event (older than activityThreshold) - // - Have a recent heartbeat (last_seen >= heartbeatRecency) — process is alive - // - Are in the running phase - // - Are not already in a terminal/sticky/waiting activity or already stalled/offline - _, err = tx.ExecContext(ctx, ` - UPDATE agents SET - stalled_from_activity = activity, - activity = 'stalled', - updated_at = ? - WHERE last_activity_event < ? - AND last_activity_event IS NOT NULL - AND last_seen >= ? - AND last_seen IS NOT NULL - AND phase = 'running' - AND activity NOT IN ('completed', 'limits_exceeded', 'blocked', 'stalled', 'offline', 'waiting_for_input') - `, now, activityThreshold, heartbeatRecency) - if err != nil { - return nil, err - } - - // Fetch the agents that were just updated. - rows, err := tx.QueryContext(ctx, ` - SELECT id, agent_id, name, template, project_id, - labels, annotations, - phase, activity, tool_name, - connection_state, container_status, runtime_state, - stalled_from_activity, - current_turns, current_model_calls, - image, detached, runtime, runtime_broker_id, web_pty_enabled, task_summary, message, - applied_config, - created_at, updated_at, last_seen, last_activity_event, deleted_at, started_at, - created_by, owner_id, visibility, state_version, ancestry - FROM agents - WHERE activity = 'stalled' AND updated_at = ? - AND last_activity_event < ? - AND last_activity_event IS NOT NULL - AND last_seen >= ? - AND last_seen IS NOT NULL - AND phase = 'running' - `, now, activityThreshold, heartbeatRecency) - if err != nil { - return nil, err - } - defer rows.Close() - - var agents []store.Agent - for rows.Next() { - var agent store.Agent - var labels, annotations, appliedConfig string - var lastSeen, lastActivityEvent, deletedAt, startedAt sql.NullTime - var runtimeBrokerID, message, toolName, ancestry sql.NullString - - if err := rows.Scan( - &agent.ID, &agent.Slug, &agent.Name, &agent.Template, &agent.ProjectID, - &labels, &annotations, - &agent.Phase, &agent.Activity, &toolName, - &agent.ConnectionState, &agent.ContainerStatus, &agent.RuntimeState, - &agent.StalledFromActivity, - &agent.CurrentTurns, &agent.CurrentModelCalls, - &agent.Image, &agent.Detached, &agent.Runtime, &runtimeBrokerID, &agent.WebPTYEnabled, &agent.TaskSummary, &message, - &appliedConfig, - &agent.Created, &agent.Updated, &lastSeen, &lastActivityEvent, &deletedAt, &startedAt, - &agent.CreatedBy, &agent.OwnerID, &agent.Visibility, &agent.StateVersion, &ancestry, - ); err != nil { - return nil, err - } - - unmarshalJSON(labels, &agent.Labels) - unmarshalJSON(annotations, &agent.Annotations) - unmarshalJSON(appliedConfig, &agent.AppliedConfig) - unmarshalJSON(ancestry.String, &agent.Ancestry) - if lastSeen.Valid { - agent.LastSeen = lastSeen.Time - } - if lastActivityEvent.Valid { - agent.LastActivityEvent = lastActivityEvent.Time - } - if deletedAt.Valid { - agent.DeletedAt = deletedAt.Time - } - if startedAt.Valid { - agent.StartedAt = startedAt.Time - } - if runtimeBrokerID.Valid { - agent.RuntimeBrokerID = runtimeBrokerID.String - } - if message.Valid { - agent.Message = message.String - } - if toolName.Valid { - agent.ToolName = toolName.String - } - - agents = append(agents, agent) - } - - if err := tx.Commit(); err != nil { - return nil, err - } - - return agents, nil -} - -// ============================================================================ -// Project Operations -// ============================================================================ - -func (s *SQLiteStore) CreateProject(ctx context.Context, project *store.Project) error { - now := time.Now() - project.Created = now - project.Updated = now - - _, err := s.db.ExecContext(ctx, ` - INSERT INTO projects (id, name, slug, git_remote, default_runtime_broker_id, labels, annotations, shared_dirs, created_at, updated_at, created_by, owner_id, visibility, github_installation_id, github_permissions, github_app_status, git_identity) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - `, - project.ID, project.Name, project.Slug, nullableString(project.GitRemote), nullableString(project.DefaultRuntimeBrokerID), - marshalJSON(project.Labels), marshalJSON(project.Annotations), marshalJSON(project.SharedDirs), - project.Created, project.Updated, project.CreatedBy, project.OwnerID, project.Visibility, - nullableInt64(project.GitHubInstallationID), marshalJSONPtr(project.GitHubPermissions), marshalJSONPtr(project.GitHubAppStatus), - marshalJSONPtr(project.GitIdentity), - ) - if err != nil { - if strings.Contains(err.Error(), "UNIQUE constraint failed") { - return store.ErrAlreadyExists - } - return err - } - return nil -} - -func (s *SQLiteStore) GetProject(ctx context.Context, id string) (*store.Project, error) { - project := &store.Project{} - var labels, annotations, sharedDirs string - var gitRemote, defaultRuntimeBrokerID sql.NullString - var githubInstallationID sql.NullInt64 - var githubPermissions, githubAppStatus, gitIdentity string - - err := s.db.QueryRowContext(ctx, ` - SELECT id, name, slug, git_remote, default_runtime_broker_id, labels, annotations, shared_dirs, created_at, updated_at, created_by, owner_id, visibility, github_installation_id, COALESCE(github_permissions, ''), COALESCE(github_app_status, ''), COALESCE(git_identity, '') - FROM projects WHERE id = ? - `, id).Scan( - &project.ID, &project.Name, &project.Slug, &gitRemote, &defaultRuntimeBrokerID, - &labels, &annotations, &sharedDirs, - &project.Created, &project.Updated, &project.CreatedBy, &project.OwnerID, &project.Visibility, - &githubInstallationID, &githubPermissions, &githubAppStatus, &gitIdentity, - ) - if err != nil { - if errors.Is(err, sql.ErrNoRows) { - return nil, store.ErrNotFound - } - return nil, err - } - - if gitRemote.Valid { - project.GitRemote = gitRemote.String - } - if defaultRuntimeBrokerID.Valid { - project.DefaultRuntimeBrokerID = defaultRuntimeBrokerID.String - } - if githubInstallationID.Valid { - id := githubInstallationID.Int64 - project.GitHubInstallationID = &id - } - unmarshalJSON(labels, &project.Labels) - unmarshalJSON(annotations, &project.Annotations) - unmarshalJSON(sharedDirs, &project.SharedDirs) - if githubPermissions != "" { - project.GitHubPermissions = &store.GitHubTokenPermissions{} - unmarshalJSON(githubPermissions, project.GitHubPermissions) - } - if githubAppStatus != "" { - project.GitHubAppStatus = &store.GitHubAppProjectStatus{} - unmarshalJSON(githubAppStatus, project.GitHubAppStatus) - } - if gitIdentity != "" { - project.GitIdentity = &store.GitIdentityConfig{} - unmarshalJSON(gitIdentity, project.GitIdentity) - } - - // Populate computed fields - s.db.QueryRowContext(ctx, "SELECT COUNT(*) FROM agents WHERE project_id = ?", id).Scan(&project.AgentCount) - s.db.QueryRowContext(ctx, ` - SELECT (SELECT COUNT(*) FROM project_contributors WHERE project_id = ? AND status = 'online') - + (SELECT COUNT(*) FROM runtime_brokers WHERE auto_provide = 1 AND status = 'online' - AND id NOT IN (SELECT broker_id FROM project_contributors WHERE project_id = ?)) - `, id, id).Scan(&project.ActiveBrokerCount) - s.populateProjectType(ctx, project) - - return project, nil -} - -// populateProjectType sets the computed ProjectType field based on how the project was established. -// Type is "linked" (pre-existing local project linked to Hub) or "hub-managed" (created via Hub). -// Whether a project is git-backed is orthogonal — indicated by the GitRemote field. -func (s *SQLiteStore) populateProjectType(ctx context.Context, project *store.Project) { - // Check if any provider has a local_path not under ~/.scion/projects/ (i.e. broker-linked) - var linkedCount int - s.db.QueryRowContext(ctx, - "SELECT COUNT(*) FROM project_contributors WHERE project_id = ? AND local_path != '' AND local_path NOT LIKE '%/.scion/projects/%'", - project.ID).Scan(&linkedCount) - if linkedCount > 0 { - project.ProjectType = store.ProjectTypeLinked - return - } - project.ProjectType = store.ProjectTypeHubManaged -} - -func (s *SQLiteStore) GetProjectBySlug(ctx context.Context, slug string) (*store.Project, error) { - var id string - err := s.db.QueryRowContext(ctx, "SELECT id FROM projects WHERE slug = ?", slug).Scan(&id) - if err != nil { - if errors.Is(err, sql.ErrNoRows) { - return nil, store.ErrNotFound - } - return nil, err - } - return s.GetProject(ctx, id) -} - -func (s *SQLiteStore) GetProjectBySlugCaseInsensitive(ctx context.Context, slug string) (*store.Project, error) { - var id string - err := s.db.QueryRowContext(ctx, "SELECT id FROM projects WHERE LOWER(slug) = LOWER(?)", slug).Scan(&id) - if err != nil { - if errors.Is(err, sql.ErrNoRows) { - return nil, store.ErrNotFound - } - return nil, err - } - return s.GetProject(ctx, id) -} - -func (s *SQLiteStore) GetProjectsByGitRemote(ctx context.Context, gitRemote string) ([]*store.Project, error) { - rows, err := s.db.QueryContext(ctx, "SELECT id FROM projects WHERE git_remote = ? ORDER BY created_at ASC", gitRemote) - if err != nil { - return nil, err - } - - // Collect all IDs first, then close the cursor before calling GetProject - // (SQLite single-connection can't serve a new query while rows are open). - var ids []string - for rows.Next() { - var id string - if err := rows.Scan(&id); err != nil { - rows.Close() - return nil, err - } - ids = append(ids, id) - } - if err := rows.Err(); err != nil { - rows.Close() - return nil, err - } - rows.Close() - - projects := make([]*store.Project, 0, len(ids)) - for _, id := range ids { - project, err := s.GetProject(ctx, id) - if err != nil { - return nil, err - } - projects = append(projects, project) - } - return projects, nil -} - -func (s *SQLiteStore) NextAvailableSlug(ctx context.Context, baseSlug string) (string, error) { - // Check if the base slug is available - var count int - if err := s.db.QueryRowContext(ctx, "SELECT COUNT(*) FROM projects WHERE slug = ?", baseSlug).Scan(&count); err != nil { - return "", err - } - if count == 0 { - return baseSlug, nil - } - - // Find the next available serial suffix - for i := 1; ; i++ { - candidate := fmt.Sprintf("%s-%d", baseSlug, i) - if err := s.db.QueryRowContext(ctx, "SELECT COUNT(*) FROM projects WHERE slug = ?", candidate).Scan(&count); err != nil { - return "", err - } - if count == 0 { - return candidate, nil - } - } -} - -func (s *SQLiteStore) UpdateProject(ctx context.Context, project *store.Project) error { - project.Updated = time.Now() - - result, err := s.db.ExecContext(ctx, ` - UPDATE projects SET - name = ?, slug = ?, git_remote = ?, default_runtime_broker_id = ?, - labels = ?, annotations = ?, shared_dirs = ?, - updated_at = ?, owner_id = ?, visibility = ?, - github_installation_id = ?, github_permissions = ?, github_app_status = ?, - git_identity = ? - WHERE id = ? - `, - project.Name, project.Slug, nullableString(project.GitRemote), nullableString(project.DefaultRuntimeBrokerID), - marshalJSON(project.Labels), marshalJSON(project.Annotations), marshalJSON(project.SharedDirs), - project.Updated, project.OwnerID, project.Visibility, - nullableInt64(project.GitHubInstallationID), marshalJSONPtr(project.GitHubPermissions), marshalJSONPtr(project.GitHubAppStatus), - marshalJSONPtr(project.GitIdentity), - project.ID, - ) - if err != nil { - return err - } - - rowsAffected, err := result.RowsAffected() - if err != nil { - return err - } - if rowsAffected == 0 { - return store.ErrNotFound - } - return nil -} - -func (s *SQLiteStore) DeleteProject(ctx context.Context, id string) error { - result, err := s.db.ExecContext(ctx, "DELETE FROM projects WHERE id = ?", id) - if err != nil { - return err - } - rowsAffected, err := result.RowsAffected() - if err != nil { - return err - } - if rowsAffected == 0 { - return store.ErrNotFound - } - return nil -} - -func (s *SQLiteStore) ListProjects(ctx context.Context, filter store.ProjectFilter, opts store.ListOptions) (*store.ListResult[store.Project], error) { - var conditions []string - var args []interface{} - - if len(filter.MemberOrOwnerIDs) > 0 { - // Combine owner_id match with project ID membership using OR - placeholders := make([]string, len(filter.MemberOrOwnerIDs)) - for i, id := range filter.MemberOrOwnerIDs { - placeholders[i] = "?" - args = append(args, id) - } - orParts := []string{"id IN (" + strings.Join(placeholders, ",") + ")"} - if filter.OwnerID != "" { - orParts = append(orParts, "owner_id = ?") - args = append(args, filter.OwnerID) - } - conditions = append(conditions, "("+strings.Join(orParts, " OR ")+")") - } else if len(filter.MemberProjectIDs) > 0 { - // Strict project ID membership (no owner OR) - placeholders := make([]string, len(filter.MemberProjectIDs)) - for i, id := range filter.MemberProjectIDs { - placeholders[i] = "?" - args = append(args, id) - } - conditions = append(conditions, "id IN ("+strings.Join(placeholders, ",")+")") - } else if filter.OwnerID != "" { - conditions = append(conditions, "owner_id = ?") - args = append(args, filter.OwnerID) - } - if filter.ExcludeOwnerID != "" { - conditions = append(conditions, "owner_id != ?") - args = append(args, filter.ExcludeOwnerID) - } - if filter.Visibility != "" { - conditions = append(conditions, "visibility = ?") - args = append(args, filter.Visibility) - } - if filter.GitRemote != "" { - conditions = append(conditions, "git_remote = ?") - args = append(args, filter.GitRemote) - } else if filter.GitRemotePrefix != "" { - conditions = append(conditions, "git_remote LIKE ?") - args = append(args, filter.GitRemotePrefix+"%") - } - if filter.BrokerID != "" { - conditions = append(conditions, "id IN (SELECT project_id FROM project_contributors WHERE broker_id = ?)") - args = append(args, filter.BrokerID) - } - if filter.Name != "" { - conditions = append(conditions, "LOWER(name) = LOWER(?)") - args = append(args, filter.Name) - } - if filter.Slug != "" { - conditions = append(conditions, "LOWER(slug) = LOWER(?)") - args = append(args, filter.Slug) - } - - whereClause := "" - if len(conditions) > 0 { - whereClause = "WHERE " + strings.Join(conditions, " AND ") - } - - var totalCount int - countQuery := fmt.Sprintf("SELECT COUNT(*) FROM projects %s", whereClause) - if err := s.db.QueryRowContext(ctx, countQuery, args...).Scan(&totalCount); err != nil { - return nil, err - } - - limit := opts.Limit - if limit <= 0 { - limit = 50 - } - - query := fmt.Sprintf(` - SELECT id, name, slug, git_remote, default_runtime_broker_id, labels, annotations, shared_dirs, created_at, updated_at, created_by, owner_id, visibility, - github_installation_id, COALESCE(github_permissions, ''), COALESCE(github_app_status, ''), COALESCE(git_identity, '') - FROM projects %s ORDER BY created_at DESC LIMIT ? - `, whereClause) - args = append(args, limit) - - rows, err := s.db.QueryContext(ctx, query, args...) - if err != nil { - return nil, err - } - defer rows.Close() - - var projects []store.Project - type projectRow struct { - project store.Project - labels string - annotations string - sharedDirs string - gitRemote sql.NullString - brokerID sql.NullString - githubInstallationID sql.NullInt64 - githubPermissions string - githubAppStatus string - gitIdentity string - } - var rowData []projectRow - - for rows.Next() { - var r projectRow - if err := rows.Scan( - &r.project.ID, &r.project.Name, &r.project.Slug, &r.gitRemote, &r.brokerID, - &r.labels, &r.annotations, &r.sharedDirs, - &r.project.Created, &r.project.Updated, &r.project.CreatedBy, &r.project.OwnerID, &r.project.Visibility, - &r.githubInstallationID, &r.githubPermissions, &r.githubAppStatus, &r.gitIdentity, - ); err != nil { - return nil, err - } - rowData = append(rowData, r) - } - rows.Close() // Close early to release connection for nested queries - - for _, r := range rowData { - project := r.project - if r.gitRemote.Valid { - project.GitRemote = r.gitRemote.String - } - if r.brokerID.Valid { - project.DefaultRuntimeBrokerID = r.brokerID.String - } - if r.githubInstallationID.Valid { - id := r.githubInstallationID.Int64 - project.GitHubInstallationID = &id - } - unmarshalJSON(r.labels, &project.Labels) - unmarshalJSON(r.annotations, &project.Annotations) - unmarshalJSON(r.sharedDirs, &project.SharedDirs) - if r.githubPermissions != "" { - project.GitHubPermissions = &store.GitHubTokenPermissions{} - unmarshalJSON(r.githubPermissions, project.GitHubPermissions) - } - if r.githubAppStatus != "" { - project.GitHubAppStatus = &store.GitHubAppProjectStatus{} - unmarshalJSON(r.githubAppStatus, project.GitHubAppStatus) - } - if r.gitIdentity != "" { - project.GitIdentity = &store.GitIdentityConfig{} - unmarshalJSON(r.gitIdentity, project.GitIdentity) - } - - // Populate computed fields - these now have a connection available - s.db.QueryRowContext(ctx, "SELECT COUNT(*) FROM agents WHERE project_id = ?", project.ID).Scan(&project.AgentCount) - s.db.QueryRowContext(ctx, ` - SELECT (SELECT COUNT(*) FROM project_contributors WHERE project_id = ? AND status = 'online') - + (SELECT COUNT(*) FROM runtime_brokers WHERE auto_provide = 1 AND status = 'online' - AND id NOT IN (SELECT broker_id FROM project_contributors WHERE project_id = ?)) - `, project.ID, project.ID).Scan(&project.ActiveBrokerCount) - s.populateProjectType(ctx, &project) - - projects = append(projects, project) - } - - return &store.ListResult[store.Project]{ - Items: projects, - TotalCount: totalCount, - }, nil -} - -// ============================================================================ -// RuntimeBroker Operations -// ============================================================================ - -func (s *SQLiteStore) CreateRuntimeBroker(ctx context.Context, broker *store.RuntimeBroker) error { - now := time.Now() - broker.Created = now - broker.Updated = now - - _, err := s.db.ExecContext(ctx, ` - INSERT INTO runtime_brokers ( - id, name, slug, type, mode, version, - status, connection_state, last_heartbeat, - capabilities, supported_harnesses, resources, runtimes, - labels, annotations, endpoint, - created_at, updated_at, created_by, auto_provide - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - `, - broker.ID, broker.Name, broker.Slug, "", "", broker.Version, - broker.Status, broker.ConnectionState, broker.LastHeartbeat, - marshalJSON(broker.Capabilities), "[]", - "{}", marshalJSON(broker.Profiles), - marshalJSON(broker.Labels), marshalJSON(broker.Annotations), broker.Endpoint, - broker.Created, broker.Updated, nullableString(broker.CreatedBy), broker.AutoProvide, - ) - if err != nil { - if strings.Contains(err.Error(), "UNIQUE constraint failed") { - return store.ErrAlreadyExists - } - return err - } - return nil -} - -func (s *SQLiteStore) GetRuntimeBroker(ctx context.Context, id string) (*store.RuntimeBroker, error) { - broker := &store.RuntimeBroker{} - var capabilities, profiles, labels, annotations string - var brokerType, brokerMode, harnesses, resources string // unused columns kept for schema compatibility - var lastHeartbeat sql.NullTime - var createdBy sql.NullString - - err := s.db.QueryRowContext(ctx, ` - SELECT id, name, slug, type, mode, version, - status, connection_state, last_heartbeat, - capabilities, supported_harnesses, resources, runtimes, - labels, annotations, endpoint, - created_at, updated_at, created_by, auto_provide - FROM runtime_brokers WHERE id = ? - `, id).Scan( - &broker.ID, &broker.Name, &broker.Slug, &brokerType, &brokerMode, &broker.Version, - &broker.Status, &broker.ConnectionState, &lastHeartbeat, - &capabilities, &harnesses, &resources, &profiles, - &labels, &annotations, &broker.Endpoint, - &broker.Created, &broker.Updated, &createdBy, &broker.AutoProvide, - ) - if err != nil { - if errors.Is(err, sql.ErrNoRows) { - return nil, store.ErrNotFound - } - return nil, err - } - - if lastHeartbeat.Valid { - broker.LastHeartbeat = lastHeartbeat.Time - } - if createdBy.Valid { - broker.CreatedBy = createdBy.String - } - unmarshalJSON(capabilities, &broker.Capabilities) - unmarshalJSON(profiles, &broker.Profiles) - unmarshalJSON(labels, &broker.Labels) - unmarshalJSON(annotations, &broker.Annotations) - - return broker, nil -} - -func (s *SQLiteStore) GetRuntimeBrokerByName(ctx context.Context, name string) (*store.RuntimeBroker, error) { - var id string - err := s.db.QueryRowContext(ctx, "SELECT id FROM runtime_brokers WHERE LOWER(name) = LOWER(?)", name).Scan(&id) - if err != nil { - if errors.Is(err, sql.ErrNoRows) { - return nil, store.ErrNotFound - } - return nil, err - } - return s.GetRuntimeBroker(ctx, id) -} - -func (s *SQLiteStore) UpdateRuntimeBroker(ctx context.Context, broker *store.RuntimeBroker) error { - broker.Updated = time.Now() - - result, err := s.db.ExecContext(ctx, ` - UPDATE runtime_brokers SET - name = ?, slug = ?, type = ?, version = ?, - status = ?, connection_state = ?, last_heartbeat = ?, - capabilities = ?, supported_harnesses = ?, resources = ?, runtimes = ?, - labels = ?, annotations = ?, endpoint = ?, - updated_at = ?, auto_provide = ? - WHERE id = ? - `, - broker.Name, broker.Slug, "", broker.Version, - broker.Status, broker.ConnectionState, broker.LastHeartbeat, - marshalJSON(broker.Capabilities), "[]", - "{}", marshalJSON(broker.Profiles), - marshalJSON(broker.Labels), marshalJSON(broker.Annotations), broker.Endpoint, - broker.Updated, broker.AutoProvide, - broker.ID, - ) - if err != nil { - return err - } - - rowsAffected, err := result.RowsAffected() - if err != nil { - return err - } - if rowsAffected == 0 { - return store.ErrNotFound - } - return nil -} - -func (s *SQLiteStore) DeleteRuntimeBroker(ctx context.Context, id string) error { - result, err := s.db.ExecContext(ctx, "DELETE FROM runtime_brokers WHERE id = ?", id) - if err != nil { - return err - } - rowsAffected, err := result.RowsAffected() - if err != nil { - return err - } - if rowsAffected == 0 { - return store.ErrNotFound - } - return nil -} - -func (s *SQLiteStore) ListRuntimeBrokers(ctx context.Context, filter store.RuntimeBrokerFilter, opts store.ListOptions) (*store.ListResult[store.RuntimeBroker], error) { - var conditions []string - var args []interface{} - - if filter.Status != "" { - conditions = append(conditions, "status = ?") - args = append(args, filter.Status) - } - if filter.ProjectID != "" { - conditions = append(conditions, "(id IN (SELECT broker_id FROM project_contributors WHERE project_id = ?) OR auto_provide = 1)") - args = append(args, filter.ProjectID) - } - if filter.Name != "" { - conditions = append(conditions, "LOWER(name) = LOWER(?)") - args = append(args, filter.Name) - } - if filter.AutoProvide != nil { - conditions = append(conditions, "auto_provide = ?") - args = append(args, *filter.AutoProvide) - } - - whereClause := "" - if len(conditions) > 0 { - whereClause = "WHERE " + strings.Join(conditions, " AND ") - } - - var totalCount int - countQuery := fmt.Sprintf("SELECT COUNT(*) FROM runtime_brokers %s", whereClause) - if err := s.db.QueryRowContext(ctx, countQuery, args...).Scan(&totalCount); err != nil { - return nil, err - } - - limit := opts.Limit - if limit <= 0 { - limit = 50 - } - - query := fmt.Sprintf(` - SELECT id, name, slug, type, mode, version, - status, connection_state, last_heartbeat, - capabilities, supported_harnesses, resources, runtimes, - labels, annotations, endpoint, - created_at, updated_at, created_by, auto_provide - FROM runtime_brokers %s ORDER BY created_at DESC LIMIT ? - `, whereClause) - args = append(args, limit) - - rows, err := s.db.QueryContext(ctx, query, args...) - if err != nil { - return nil, err - } - defer rows.Close() - - var hosts []store.RuntimeBroker - for rows.Next() { - var broker store.RuntimeBroker - var capabilities, profiles, labels, annotations string - var brokerType, brokerMode, harnesses, resources string // unused columns kept for schema compatibility - var lastHeartbeat sql.NullTime - var createdBy sql.NullString - - if err := rows.Scan( - &broker.ID, &broker.Name, &broker.Slug, &brokerType, &brokerMode, &broker.Version, - &broker.Status, &broker.ConnectionState, &lastHeartbeat, - &capabilities, &harnesses, &resources, &profiles, - &labels, &annotations, &broker.Endpoint, - &broker.Created, &broker.Updated, &createdBy, &broker.AutoProvide, - ); err != nil { - return nil, err - } - - if lastHeartbeat.Valid { - broker.LastHeartbeat = lastHeartbeat.Time - } - if createdBy.Valid { - broker.CreatedBy = createdBy.String - } - unmarshalJSON(capabilities, &broker.Capabilities) - unmarshalJSON(profiles, &broker.Profiles) - unmarshalJSON(labels, &broker.Labels) - unmarshalJSON(annotations, &broker.Annotations) - - hosts = append(hosts, broker) - } - - return &store.ListResult[store.RuntimeBroker]{ - Items: hosts, - TotalCount: totalCount, - }, nil -} - -func (s *SQLiteStore) UpdateRuntimeBrokerHeartbeat(ctx context.Context, id string, status string) error { - now := time.Now() - - result, err := s.db.ExecContext(ctx, ` - UPDATE runtime_brokers SET - status = ?, - last_heartbeat = ?, - updated_at = ? - WHERE id = ? - `, status, now, now, id) - if err != nil { - return err - } - - rowsAffected, err := result.RowsAffected() - if err != nil { - return err - } - if rowsAffected == 0 { - return store.ErrNotFound - } - return nil -} - -// ============================================================================ -// Template Operations -// ============================================================================ - -func (s *SQLiteStore) CreateTemplate(ctx context.Context, template *store.Template) error { - now := time.Now() - template.Created = now - template.Updated = now - - // Set default status if not provided - if template.Status == "" { - template.Status = store.TemplateStatusActive - } - - _, err := s.db.ExecContext(ctx, ` - INSERT INTO templates ( - id, name, slug, display_name, description, harness, default_harness_config, image, config, - content_hash, scope, scope_id, project_id, - storage_uri, storage_bucket, storage_path, files, - base_template, locked, status, - owner_id, created_by, updated_by, visibility, - created_at, updated_at - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - `, - template.ID, template.Name, template.Slug, nullableString(template.DisplayName), nullableString(template.Description), - template.Harness, nullableString(template.DefaultHarnessConfig), template.Image, marshalJSON(template.Config), - nullableString(template.ContentHash), template.Scope, nullableString(template.ScopeID), nullableString(template.ProjectID), - nullableString(template.StorageURI), nullableString(template.StorageBucket), nullableString(template.StoragePath), marshalJSON(template.Files), - nullableString(template.BaseTemplate), template.Locked, template.Status, - nullableString(template.OwnerID), nullableString(template.CreatedBy), nullableString(template.UpdatedBy), template.Visibility, - template.Created, template.Updated, - ) - if err != nil { - if strings.Contains(err.Error(), "UNIQUE constraint failed") { - return store.ErrAlreadyExists - } - return err - } - return nil -} - -func (s *SQLiteStore) GetTemplate(ctx context.Context, id string) (*store.Template, error) { - template := &store.Template{} - var config, files string - var displayName, description, contentHash, scopeID, projectID sql.NullString - var storageURI, storageBucket, storagePath, baseTemplate sql.NullString - var createdBy, updatedBy, ownerID, visibility sql.NullString - var defaultHarnessConfig sql.NullString - - err := s.db.QueryRowContext(ctx, ` - SELECT id, name, slug, display_name, description, harness, default_harness_config, image, config, - content_hash, scope, scope_id, project_id, - storage_uri, storage_bucket, storage_path, files, - base_template, locked, status, - owner_id, created_by, updated_by, visibility, - created_at, updated_at - FROM templates WHERE id = ? - `, id).Scan( - &template.ID, &template.Name, &template.Slug, &displayName, &description, - &template.Harness, &defaultHarnessConfig, &template.Image, &config, - &contentHash, &template.Scope, &scopeID, &projectID, - &storageURI, &storageBucket, &storagePath, &files, - &baseTemplate, &template.Locked, &template.Status, - &ownerID, &createdBy, &updatedBy, &visibility, - &template.Created, &template.Updated, - ) - if err != nil { - if errors.Is(err, sql.ErrNoRows) { - return nil, store.ErrNotFound - } - return nil, err - } - - if displayName.Valid { - template.DisplayName = displayName.String - } - if description.Valid { - template.Description = description.String - } - if defaultHarnessConfig.Valid { - template.DefaultHarnessConfig = defaultHarnessConfig.String - } - if contentHash.Valid { - template.ContentHash = contentHash.String - } - if scopeID.Valid { - template.ScopeID = scopeID.String - } - if projectID.Valid { - template.ProjectID = projectID.String - } - if storageURI.Valid { - template.StorageURI = storageURI.String - } - if storageBucket.Valid { - template.StorageBucket = storageBucket.String - } - if storagePath.Valid { - template.StoragePath = storagePath.String - } - if baseTemplate.Valid { - template.BaseTemplate = baseTemplate.String - } - if ownerID.Valid { - template.OwnerID = ownerID.String - } - if createdBy.Valid { - template.CreatedBy = createdBy.String - } - if updatedBy.Valid { - template.UpdatedBy = updatedBy.String - } - if visibility.Valid { - template.Visibility = visibility.String - } - unmarshalJSON(config, &template.Config) - unmarshalJSON(files, &template.Files) - - return template, nil -} - -func (s *SQLiteStore) GetTemplateBySlug(ctx context.Context, slug, scope, scopeID string) (*store.Template, error) { - var id string - var err error - - if scope == "project" && scopeID != "" { - // Try scope_id first, then fall back to project_id for backwards compatibility - err = s.db.QueryRowContext(ctx, "SELECT id FROM templates WHERE slug = ? AND scope = ? AND (scope_id = ? OR project_id = ?)", slug, scope, scopeID, scopeID).Scan(&id) - } else if scope == "user" && scopeID != "" { - err = s.db.QueryRowContext(ctx, "SELECT id FROM templates WHERE slug = ? AND scope = ? AND scope_id = ?", slug, scope, scopeID).Scan(&id) - } else { - err = s.db.QueryRowContext(ctx, "SELECT id FROM templates WHERE slug = ? AND scope = ?", slug, scope).Scan(&id) - } - - if err != nil { - if errors.Is(err, sql.ErrNoRows) { - return nil, store.ErrNotFound - } - return nil, err - } - return s.GetTemplate(ctx, id) -} - -func (s *SQLiteStore) UpdateTemplate(ctx context.Context, template *store.Template) error { - template.Updated = time.Now() - - result, err := s.db.ExecContext(ctx, ` - UPDATE templates SET - name = ?, slug = ?, display_name = ?, description = ?, - harness = ?, default_harness_config = ?, image = ?, config = ?, - content_hash = ?, scope = ?, scope_id = ?, project_id = ?, - storage_uri = ?, storage_bucket = ?, storage_path = ?, files = ?, - base_template = ?, locked = ?, status = ?, - owner_id = ?, updated_by = ?, visibility = ?, - updated_at = ? - WHERE id = ? - `, - template.Name, template.Slug, nullableString(template.DisplayName), nullableString(template.Description), - template.Harness, nullableString(template.DefaultHarnessConfig), template.Image, marshalJSON(template.Config), - nullableString(template.ContentHash), template.Scope, nullableString(template.ScopeID), nullableString(template.ProjectID), - nullableString(template.StorageURI), nullableString(template.StorageBucket), nullableString(template.StoragePath), marshalJSON(template.Files), - nullableString(template.BaseTemplate), template.Locked, template.Status, - nullableString(template.OwnerID), nullableString(template.UpdatedBy), template.Visibility, - template.Updated, - template.ID, - ) - if err != nil { - return err - } - - rowsAffected, err := result.RowsAffected() - if err != nil { - return err - } - if rowsAffected == 0 { - return store.ErrNotFound - } - return nil -} - -func (s *SQLiteStore) DeleteTemplate(ctx context.Context, id string) error { - result, err := s.db.ExecContext(ctx, "DELETE FROM templates WHERE id = ?", id) - if err != nil { - return err - } - rowsAffected, err := result.RowsAffected() - if err != nil { - return err - } - if rowsAffected == 0 { - return store.ErrNotFound - } - return nil -} - -func (s *SQLiteStore) DeleteTemplatesByScope(ctx context.Context, scope, scopeID string) (int, error) { - result, err := s.db.ExecContext(ctx, "DELETE FROM templates WHERE scope = ? AND scope_id = ?", scope, scopeID) - if err != nil { - return 0, err - } - n, err := result.RowsAffected() - if err != nil { - return 0, err - } - return int(n), nil -} - -func (s *SQLiteStore) ListTemplates(ctx context.Context, filter store.TemplateFilter, opts store.ListOptions) (*store.ListResult[store.Template], error) { - var conditions []string - var args []interface{} - - if filter.Name != "" { - // Exact match on name or slug - conditions = append(conditions, "(name = ? OR slug = ?)") - args = append(args, filter.Name, filter.Name) - } - if filter.Scope != "" { - conditions = append(conditions, "scope = ?") - args = append(args, filter.Scope) - } - if filter.ScopeID != "" { - conditions = append(conditions, "(scope_id = ? OR project_id = ?)") - args = append(args, filter.ScopeID, filter.ScopeID) - } else if filter.ProjectID != "" && filter.Scope == "" { - // When projectId is set without scope, return global + project-scoped templates for this project - conditions = append(conditions, "(scope = 'global' OR (scope = 'project' AND (scope_id = ? OR project_id = ?)))") - args = append(args, filter.ProjectID, filter.ProjectID) - } else if filter.ProjectID != "" { - // Backwards compatibility: projectId with explicit scope - conditions = append(conditions, "(scope_id = ? OR project_id = ?)") - args = append(args, filter.ProjectID, filter.ProjectID) - } - if filter.Harness != "" { - conditions = append(conditions, "harness = ?") - args = append(args, filter.Harness) - } - if filter.OwnerID != "" { - conditions = append(conditions, "owner_id = ?") - args = append(args, filter.OwnerID) - } - if filter.Status != "" { - conditions = append(conditions, "status = ?") - args = append(args, filter.Status) - } - if filter.Search != "" { - conditions = append(conditions, "(name LIKE ? OR description LIKE ?)") - searchPattern := "%" + filter.Search + "%" - args = append(args, searchPattern, searchPattern) - } - - whereClause := "" - if len(conditions) > 0 { - whereClause = "WHERE " + strings.Join(conditions, " AND ") - } - - var totalCount int - countQuery := fmt.Sprintf("SELECT COUNT(*) FROM templates %s", whereClause) - if err := s.db.QueryRowContext(ctx, countQuery, args...).Scan(&totalCount); err != nil { - return nil, err - } - - limit := opts.Limit - if limit <= 0 { - limit = 50 - } - - query := fmt.Sprintf(` - SELECT id, name, slug, display_name, description, harness, default_harness_config, image, config, - content_hash, scope, scope_id, project_id, - storage_uri, storage_bucket, storage_path, files, - base_template, locked, status, - owner_id, created_by, updated_by, visibility, - created_at, updated_at - FROM templates %s ORDER BY created_at DESC LIMIT ? - `, whereClause) - args = append(args, limit) - - rows, err := s.db.QueryContext(ctx, query, args...) - if err != nil { - return nil, err - } - defer rows.Close() - - var templates []store.Template - for rows.Next() { - var template store.Template - var config, files string - var displayName, description, contentHash, scopeID, projectID sql.NullString - var storageURI, storageBucket, storagePath, baseTemplate sql.NullString - var createdBy, updatedBy, ownerID, visibility sql.NullString - var defaultHarnessConfig sql.NullString - - if err := rows.Scan( - &template.ID, &template.Name, &template.Slug, &displayName, &description, - &template.Harness, &defaultHarnessConfig, &template.Image, &config, - &contentHash, &template.Scope, &scopeID, &projectID, - &storageURI, &storageBucket, &storagePath, &files, - &baseTemplate, &template.Locked, &template.Status, - &ownerID, &createdBy, &updatedBy, &visibility, - &template.Created, &template.Updated, - ); err != nil { - return nil, err - } - - if displayName.Valid { - template.DisplayName = displayName.String - } - if description.Valid { - template.Description = description.String - } - if defaultHarnessConfig.Valid { - template.DefaultHarnessConfig = defaultHarnessConfig.String - } - if contentHash.Valid { - template.ContentHash = contentHash.String - } - if scopeID.Valid { - template.ScopeID = scopeID.String - } - if projectID.Valid { - template.ProjectID = projectID.String - } - if storageURI.Valid { - template.StorageURI = storageURI.String - } - if storageBucket.Valid { - template.StorageBucket = storageBucket.String - } - if storagePath.Valid { - template.StoragePath = storagePath.String - } - if baseTemplate.Valid { - template.BaseTemplate = baseTemplate.String - } - if ownerID.Valid { - template.OwnerID = ownerID.String - } - if createdBy.Valid { - template.CreatedBy = createdBy.String - } - if updatedBy.Valid { - template.UpdatedBy = updatedBy.String - } - if visibility.Valid { - template.Visibility = visibility.String - } - unmarshalJSON(config, &template.Config) - unmarshalJSON(files, &template.Files) - - templates = append(templates, template) - } - - return &store.ListResult[store.Template]{ - Items: templates, - TotalCount: totalCount, - }, nil -} - -// ============================================================================ -// HarnessConfig Operations -// ============================================================================ - -func (s *SQLiteStore) CreateHarnessConfig(ctx context.Context, hc *store.HarnessConfig) error { - now := time.Now() - hc.Created = now - hc.Updated = now - - if hc.Status == "" { - hc.Status = store.HarnessConfigStatusActive - } - - _, err := s.db.ExecContext(ctx, ` - INSERT INTO harness_configs ( - id, name, slug, display_name, description, harness, config, - content_hash, scope, scope_id, - storage_uri, storage_bucket, storage_path, files, - locked, status, - owner_id, created_by, updated_by, visibility, - created_at, updated_at - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - `, - hc.ID, hc.Name, hc.Slug, nullableString(hc.DisplayName), nullableString(hc.Description), - hc.Harness, marshalJSON(hc.Config), - nullableString(hc.ContentHash), hc.Scope, nullableString(hc.ScopeID), - nullableString(hc.StorageURI), nullableString(hc.StorageBucket), nullableString(hc.StoragePath), marshalJSON(hc.Files), - hc.Locked, hc.Status, - nullableString(hc.OwnerID), nullableString(hc.CreatedBy), nullableString(hc.UpdatedBy), hc.Visibility, - hc.Created, hc.Updated, - ) - if err != nil { - if strings.Contains(err.Error(), "UNIQUE constraint failed") { - return store.ErrAlreadyExists - } - return err - } - return nil -} - -func (s *SQLiteStore) GetHarnessConfig(ctx context.Context, id string) (*store.HarnessConfig, error) { - hc := &store.HarnessConfig{} - var configJSON, filesJSON string - var displayName, description, contentHash, scopeID sql.NullString - var storageURI, storageBucket, storagePath sql.NullString - var createdBy, updatedBy, ownerID, visibility sql.NullString - - err := s.db.QueryRowContext(ctx, ` - SELECT id, name, slug, display_name, description, harness, config, - content_hash, scope, scope_id, - storage_uri, storage_bucket, storage_path, files, - locked, status, - owner_id, created_by, updated_by, visibility, - created_at, updated_at - FROM harness_configs WHERE id = ? - `, id).Scan( - &hc.ID, &hc.Name, &hc.Slug, &displayName, &description, - &hc.Harness, &configJSON, - &contentHash, &hc.Scope, &scopeID, - &storageURI, &storageBucket, &storagePath, &filesJSON, - &hc.Locked, &hc.Status, - &ownerID, &createdBy, &updatedBy, &visibility, - &hc.Created, &hc.Updated, - ) - if err != nil { - if errors.Is(err, sql.ErrNoRows) { - return nil, store.ErrNotFound - } - return nil, err - } - - if displayName.Valid { - hc.DisplayName = displayName.String - } - if description.Valid { - hc.Description = description.String - } - if contentHash.Valid { - hc.ContentHash = contentHash.String - } - if scopeID.Valid { - hc.ScopeID = scopeID.String - } - if storageURI.Valid { - hc.StorageURI = storageURI.String - } - if storageBucket.Valid { - hc.StorageBucket = storageBucket.String - } - if storagePath.Valid { - hc.StoragePath = storagePath.String - } - if ownerID.Valid { - hc.OwnerID = ownerID.String - } - if createdBy.Valid { - hc.CreatedBy = createdBy.String - } - if updatedBy.Valid { - hc.UpdatedBy = updatedBy.String - } - if visibility.Valid { - hc.Visibility = visibility.String - } - unmarshalJSON(configJSON, &hc.Config) - unmarshalJSON(filesJSON, &hc.Files) - - return hc, nil -} - -func (s *SQLiteStore) GetHarnessConfigBySlug(ctx context.Context, slug, scope, scopeID string) (*store.HarnessConfig, error) { - var id string - var err error - - if scopeID != "" { - err = s.db.QueryRowContext(ctx, "SELECT id FROM harness_configs WHERE slug = ? AND scope = ? AND scope_id = ?", slug, scope, scopeID).Scan(&id) - } else { - err = s.db.QueryRowContext(ctx, "SELECT id FROM harness_configs WHERE slug = ? AND scope = ?", slug, scope).Scan(&id) - } - - if err != nil { - if errors.Is(err, sql.ErrNoRows) { - return nil, store.ErrNotFound - } - return nil, err - } - return s.GetHarnessConfig(ctx, id) -} - -func (s *SQLiteStore) UpdateHarnessConfig(ctx context.Context, hc *store.HarnessConfig) error { - hc.Updated = time.Now() - - result, err := s.db.ExecContext(ctx, ` - UPDATE harness_configs SET - name = ?, slug = ?, display_name = ?, description = ?, - harness = ?, config = ?, - content_hash = ?, scope = ?, scope_id = ?, - storage_uri = ?, storage_bucket = ?, storage_path = ?, files = ?, - locked = ?, status = ?, - owner_id = ?, updated_by = ?, visibility = ?, - updated_at = ? - WHERE id = ? - `, - hc.Name, hc.Slug, nullableString(hc.DisplayName), nullableString(hc.Description), - hc.Harness, marshalJSON(hc.Config), - nullableString(hc.ContentHash), hc.Scope, nullableString(hc.ScopeID), - nullableString(hc.StorageURI), nullableString(hc.StorageBucket), nullableString(hc.StoragePath), marshalJSON(hc.Files), - hc.Locked, hc.Status, - nullableString(hc.OwnerID), nullableString(hc.UpdatedBy), hc.Visibility, - hc.Updated, - hc.ID, - ) - if err != nil { - return err - } - - rowsAffected, err := result.RowsAffected() - if err != nil { - return err - } - if rowsAffected == 0 { - return store.ErrNotFound - } - return nil -} - -func (s *SQLiteStore) DeleteHarnessConfig(ctx context.Context, id string) error { - result, err := s.db.ExecContext(ctx, "DELETE FROM harness_configs WHERE id = ?", id) - if err != nil { - return err - } - rowsAffected, err := result.RowsAffected() - if err != nil { - return err - } - if rowsAffected == 0 { - return store.ErrNotFound - } - return nil -} - -func (s *SQLiteStore) DeleteHarnessConfigsByScope(ctx context.Context, scope, scopeID string) (int, error) { - result, err := s.db.ExecContext(ctx, "DELETE FROM harness_configs WHERE scope = ? AND scope_id = ?", scope, scopeID) - if err != nil { - return 0, err - } - n, err := result.RowsAffected() - if err != nil { - return 0, err - } - return int(n), nil -} - -func (s *SQLiteStore) ListHarnessConfigs(ctx context.Context, filter store.HarnessConfigFilter, opts store.ListOptions) (*store.ListResult[store.HarnessConfig], error) { - var conditions []string - var args []interface{} - - if filter.Name != "" { - conditions = append(conditions, "(name = ? OR slug = ?)") - args = append(args, filter.Name, filter.Name) - } - if filter.Scope != "" { - conditions = append(conditions, "scope = ?") - args = append(args, filter.Scope) - } - if filter.ScopeID != "" { - conditions = append(conditions, "scope_id = ?") - args = append(args, filter.ScopeID) - } else if filter.ProjectID != "" && filter.Scope == "" { - // When projectId is set without scope, return global + project-scoped configs for this project - conditions = append(conditions, "(scope = 'global' OR (scope = 'project' AND scope_id = ?))") - args = append(args, filter.ProjectID) - } else if (filter.Scope == "project" || filter.Scope == "grove") && filter.ProjectID != "" { - // projectId combined with an explicit scope filter — narrow to that project. - conditions = append(conditions, "scope_id = ?") - args = append(args, filter.ProjectID) - } - if filter.Harness != "" { - conditions = append(conditions, "harness = ?") - args = append(args, filter.Harness) - } - if filter.OwnerID != "" { - conditions = append(conditions, "owner_id = ?") - args = append(args, filter.OwnerID) - } - if filter.Status != "" { - conditions = append(conditions, "status = ?") - args = append(args, filter.Status) - } - if filter.Search != "" { - conditions = append(conditions, "(name LIKE ? OR description LIKE ?)") - searchPattern := "%" + filter.Search + "%" - args = append(args, searchPattern, searchPattern) - } - - whereClause := "" - if len(conditions) > 0 { - whereClause = "WHERE " + strings.Join(conditions, " AND ") - } - - var totalCount int - countQuery := fmt.Sprintf("SELECT COUNT(*) FROM harness_configs %s", whereClause) - if err := s.db.QueryRowContext(ctx, countQuery, args...).Scan(&totalCount); err != nil { - return nil, err - } - - limit := opts.Limit - if limit <= 0 { - limit = 50 - } - - query := fmt.Sprintf(` - SELECT id, name, slug, display_name, description, harness, config, - content_hash, scope, scope_id, - storage_uri, storage_bucket, storage_path, files, - locked, status, - owner_id, created_by, updated_by, visibility, - created_at, updated_at - FROM harness_configs %s ORDER BY created_at DESC LIMIT ? - `, whereClause) - args = append(args, limit) - - rows, err := s.db.QueryContext(ctx, query, args...) - if err != nil { - return nil, err - } - defer rows.Close() - - var harnessConfigs []store.HarnessConfig - for rows.Next() { - var hc store.HarnessConfig - var configJSON, filesJSON string - var displayName, description, contentHash, scopeID sql.NullString - var storageURI, storageBucket, storagePath sql.NullString - var createdBy, updatedBy, ownerID, visibility sql.NullString - - if err := rows.Scan( - &hc.ID, &hc.Name, &hc.Slug, &displayName, &description, - &hc.Harness, &configJSON, - &contentHash, &hc.Scope, &scopeID, - &storageURI, &storageBucket, &storagePath, &filesJSON, - &hc.Locked, &hc.Status, - &ownerID, &createdBy, &updatedBy, &visibility, - &hc.Created, &hc.Updated, - ); err != nil { - return nil, err - } - - if displayName.Valid { - hc.DisplayName = displayName.String - } - if description.Valid { - hc.Description = description.String - } - if contentHash.Valid { - hc.ContentHash = contentHash.String - } - if scopeID.Valid { - hc.ScopeID = scopeID.String - } - if storageURI.Valid { - hc.StorageURI = storageURI.String - } - if storageBucket.Valid { - hc.StorageBucket = storageBucket.String - } - if storagePath.Valid { - hc.StoragePath = storagePath.String - } - if ownerID.Valid { - hc.OwnerID = ownerID.String - } - if createdBy.Valid { - hc.CreatedBy = createdBy.String - } - if updatedBy.Valid { - hc.UpdatedBy = updatedBy.String - } - if visibility.Valid { - hc.Visibility = visibility.String - } - unmarshalJSON(configJSON, &hc.Config) - unmarshalJSON(filesJSON, &hc.Files) - - harnessConfigs = append(harnessConfigs, hc) - } - - // When querying by ProjectID without explicit Scope, the query returns both - // global and project-scoped configs. Deduplicate by slug, preferring the more - // specific scope (project > global). - if filter.ProjectID != "" && filter.Scope == "" { - seen := make(map[string]int, len(harnessConfigs)) - deduped := make([]store.HarnessConfig, 0, len(harnessConfigs)) - for _, hc := range harnessConfigs { - if idx, exists := seen[hc.Slug]; exists { - if hc.Scope == "project" && deduped[idx].Scope == "global" { - deduped[idx] = hc - } - } else { - seen[hc.Slug] = len(deduped) - deduped = append(deduped, hc) - } - } - harnessConfigs = deduped - totalCount = len(deduped) - } - - return &store.ListResult[store.HarnessConfig]{ - Items: harnessConfigs, - TotalCount: totalCount, - }, nil -} - -// ============================================================================ -// User Operations -// ============================================================================ - -func (s *SQLiteStore) CreateUser(ctx context.Context, user *store.User) error { - now := time.Now() - user.Created = now - - _, err := s.db.ExecContext(ctx, ` - INSERT INTO users (id, email, display_name, avatar_url, role, status, preferences, created_at, last_login) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) - `, - user.ID, user.Email, user.DisplayName, user.AvatarURL, user.Role, user.Status, - marshalJSON(user.Preferences), user.Created, user.LastLogin, - ) - if err != nil { - if strings.Contains(err.Error(), "UNIQUE constraint failed") { - return store.ErrAlreadyExists - } - return err - } - return nil -} - -func (s *SQLiteStore) GetUser(ctx context.Context, id string) (*store.User, error) { - user := &store.User{} - var preferences string - var lastLogin, lastSeen sql.NullTime - - err := s.db.QueryRowContext(ctx, ` - SELECT id, email, display_name, avatar_url, role, status, preferences, created_at, last_login, last_seen - FROM users WHERE id = ? - `, id).Scan( - &user.ID, &user.Email, &user.DisplayName, &user.AvatarURL, &user.Role, &user.Status, - &preferences, &user.Created, &lastLogin, &lastSeen, - ) - if err != nil { - if errors.Is(err, sql.ErrNoRows) { - return nil, store.ErrNotFound - } - return nil, err - } - - if lastLogin.Valid { - user.LastLogin = lastLogin.Time - } - if lastSeen.Valid { - user.LastSeen = lastSeen.Time - } - unmarshalJSON(preferences, &user.Preferences) - - return user, nil -} - -func (s *SQLiteStore) GetUserByEmail(ctx context.Context, email string) (*store.User, error) { - var id string - err := s.db.QueryRowContext(ctx, "SELECT id FROM users WHERE email = ?", email).Scan(&id) - if err != nil { - if errors.Is(err, sql.ErrNoRows) { - return nil, store.ErrNotFound - } - return nil, err - } - return s.GetUser(ctx, id) -} - -func (s *SQLiteStore) UpdateUser(ctx context.Context, user *store.User) error { - result, err := s.db.ExecContext(ctx, ` - UPDATE users SET - email = ?, display_name = ?, avatar_url = ?, - role = ?, status = ?, preferences = ?, last_login = ?, last_seen = ? - WHERE id = ? - `, - user.Email, user.DisplayName, user.AvatarURL, - user.Role, user.Status, marshalJSON(user.Preferences), user.LastLogin, user.LastSeen, - user.ID, - ) - if err != nil { - return err - } - - rowsAffected, err := result.RowsAffected() - if err != nil { - return err - } - if rowsAffected == 0 { - return store.ErrNotFound - } - return nil -} - -func (s *SQLiteStore) UpdateUserLastSeen(ctx context.Context, id string, t time.Time) error { - _, err := s.db.ExecContext(ctx, `UPDATE users SET last_seen = ? WHERE id = ?`, t, id) - return err -} - -func (s *SQLiteStore) DeleteUser(ctx context.Context, id string) error { - result, err := s.db.ExecContext(ctx, "DELETE FROM users WHERE id = ?", id) - if err != nil { - return err - } - rowsAffected, err := result.RowsAffected() - if err != nil { - return err - } - if rowsAffected == 0 { - return store.ErrNotFound - } - return nil -} - -func (s *SQLiteStore) ListUsers(ctx context.Context, filter store.UserFilter, opts store.ListOptions) (*store.ListResult[store.User], error) { - var conditions []string - var args []interface{} - - if filter.Role != "" { - conditions = append(conditions, "role = ?") - args = append(args, filter.Role) - } - if filter.Status != "" { - conditions = append(conditions, "status = ?") - args = append(args, filter.Status) - } - if filter.Search != "" { - pattern := "%" + filter.Search + "%" - conditions = append(conditions, "(email LIKE ? OR display_name LIKE ?)") - args = append(args, pattern, pattern) - } - - whereClause := "" - if len(conditions) > 0 { - whereClause = "WHERE " + strings.Join(conditions, " AND ") - } - - var totalCount int - countQuery := fmt.Sprintf("SELECT COUNT(*) FROM users %s", whereClause) - if err := s.db.QueryRowContext(ctx, countQuery, args...).Scan(&totalCount); err != nil { - return nil, err - } - - limit := opts.Limit - if limit <= 0 { - limit = 50 - } - - if limit > 200 { - limit = 200 - } - - offset := 0 - if opts.Cursor != "" { - if parsed, err := strconv.Atoi(opts.Cursor); err == nil && parsed > 0 { - offset = parsed - } - } - - // Map sort field to column name (whitelist to prevent SQL injection) - orderColumn := "created_at" - orderDir := "DESC" - switch opts.SortBy { - case "name": - orderColumn = "display_name" - orderDir = "ASC" // default ascending for name - case "lastSeen": - orderColumn = "last_seen" - case "created": - orderColumn = "created_at" - } - if opts.SortDir == "asc" { - orderDir = "ASC" - } else if opts.SortDir == "desc" { - orderDir = "DESC" - } - - query := fmt.Sprintf(` - SELECT id, email, display_name, avatar_url, role, status, preferences, created_at, last_login, last_seen - FROM users %s ORDER BY %s %s LIMIT ? OFFSET ? - `, whereClause, orderColumn, orderDir) - args = append(args, limit+1, offset) - - rows, err := s.db.QueryContext(ctx, query, args...) - if err != nil { - return nil, err - } - defer rows.Close() - - var users []store.User - for rows.Next() { - var user store.User - var preferences string - var lastLogin, lastSeen sql.NullTime - - if err := rows.Scan( - &user.ID, &user.Email, &user.DisplayName, &user.AvatarURL, &user.Role, &user.Status, - &preferences, &user.Created, &lastLogin, &lastSeen, - ); err != nil { - return nil, err - } - - if lastLogin.Valid { - user.LastLogin = lastLogin.Time - } - if lastSeen.Valid { - user.LastSeen = lastSeen.Time - } - unmarshalJSON(preferences, &user.Preferences) - - users = append(users, user) - } - - result := &store.ListResult[store.User]{ - Items: users, - TotalCount: totalCount, - } - - // Handle pagination: if we got more than limit, there's a next page - if len(users) > limit { - result.Items = users[:limit] - result.NextCursor = strconv.Itoa(offset + limit) - } - - return result, nil -} - -// ============================================================================ -// Allow List Operations -// ============================================================================ - -func (s *SQLiteStore) AddAllowListEntry(ctx context.Context, entry *store.AllowListEntry) error { - if entry.Created.IsZero() { - entry.Created = time.Now() - } - entry.Email = strings.ToLower(entry.Email) - - _, err := s.db.ExecContext(ctx, ` - INSERT INTO allow_list (id, email, note, added_by, invite_id, created) - VALUES (?, ?, ?, ?, ?, ?) - `, entry.ID, entry.Email, entry.Note, entry.AddedBy, entry.InviteID, entry.Created) - if err != nil { - if strings.Contains(err.Error(), "UNIQUE constraint failed") { - return store.ErrAlreadyExists - } - return err - } - return nil -} - -func (s *SQLiteStore) RemoveAllowListEntry(ctx context.Context, email string) error { - result, err := s.db.ExecContext(ctx, "DELETE FROM allow_list WHERE email = ?", strings.ToLower(email)) - if err != nil { - return err - } - rowsAffected, err := result.RowsAffected() - if err != nil { - return err - } - if rowsAffected == 0 { - return store.ErrNotFound - } - return nil -} - -func (s *SQLiteStore) GetAllowListEntry(ctx context.Context, email string) (*store.AllowListEntry, error) { - entry := &store.AllowListEntry{} - err := s.db.QueryRowContext(ctx, ` - SELECT id, email, note, added_by, invite_id, created - FROM allow_list WHERE email = ? - `, strings.ToLower(email)).Scan( - &entry.ID, &entry.Email, &entry.Note, &entry.AddedBy, &entry.InviteID, &entry.Created, - ) - if err != nil { - if errors.Is(err, sql.ErrNoRows) { - return nil, store.ErrNotFound - } - return nil, err - } - return entry, nil -} - -func (s *SQLiteStore) ListAllowListEntries(ctx context.Context, opts store.ListOptions) (*store.ListResult[store.AllowListEntry], error) { - var totalCount int - if err := s.db.QueryRowContext(ctx, "SELECT COUNT(*) FROM allow_list").Scan(&totalCount); err != nil { - return nil, err - } - - limit := opts.Limit - if limit <= 0 { - limit = 50 - } - - var conditions []string - var args []interface{} - - if opts.Cursor != "" { - var cursorCreated time.Time - if err := s.db.QueryRowContext(ctx, "SELECT created FROM allow_list WHERE id = ?", opts.Cursor).Scan(&cursorCreated); err != nil { - return nil, fmt.Errorf("invalid cursor: %w", err) - } - conditions = append(conditions, `(created < ? OR (created = ? AND id < ?))`) - args = append(args, cursorCreated, cursorCreated, opts.Cursor) - } - - whereClause := "" - if len(conditions) > 0 { - whereClause = "WHERE " + strings.Join(conditions, " AND ") - } - - query := fmt.Sprintf(` - SELECT id, email, note, added_by, invite_id, created - FROM allow_list %s ORDER BY created DESC, id DESC LIMIT ? - `, whereClause) - args = append(args, limit+1) - - rows, err := s.db.QueryContext(ctx, query, args...) - if err != nil { - return nil, err - } - defer rows.Close() - - var entries []store.AllowListEntry - for rows.Next() { - var entry store.AllowListEntry - if err := rows.Scan(&entry.ID, &entry.Email, &entry.Note, &entry.AddedBy, &entry.InviteID, &entry.Created); err != nil { - return nil, err - } - entries = append(entries, entry) - } - if err := rows.Err(); err != nil { - return nil, err - } - if entries == nil { - entries = []store.AllowListEntry{} - } - - var nextCursor string - if len(entries) > limit { - nextCursor = entries[limit-1].ID - entries = entries[:limit] - } - - return &store.ListResult[store.AllowListEntry]{ - Items: entries, - TotalCount: totalCount, - NextCursor: nextCursor, - }, nil -} - -func (s *SQLiteStore) IsEmailAllowListed(ctx context.Context, email string) (bool, error) { - var count int - err := s.db.QueryRowContext(ctx, "SELECT COUNT(*) FROM allow_list WHERE email = ?", strings.ToLower(email)).Scan(&count) - if err != nil { - return false, err - } - return count > 0, nil -} - -func (s *SQLiteStore) UpdateAllowListEntryInviteID(ctx context.Context, email string, inviteID string) error { - result, err := s.db.ExecContext(ctx, - "UPDATE allow_list SET invite_id = ? WHERE email = ?", - inviteID, strings.ToLower(email)) - if err != nil { - return err - } - rowsAffected, err := result.RowsAffected() - if err != nil { - return err - } - if rowsAffected == 0 { - return store.ErrNotFound - } - return nil -} - -func (s *SQLiteStore) ListAllowListEntriesWithInvites(ctx context.Context, opts store.ListOptions) (*store.ListResult[store.AllowListEntryWithInvite], error) { - var totalCount int - if err := s.db.QueryRowContext(ctx, "SELECT COUNT(*) FROM allow_list").Scan(&totalCount); err != nil { - return nil, err - } - - limit := opts.Limit - if limit <= 0 { - limit = 50 - } - - var conditions []string - var args []interface{} - - if opts.Cursor != "" { - var cursorCreated time.Time - if err := s.db.QueryRowContext(ctx, "SELECT created FROM allow_list WHERE id = ?", opts.Cursor).Scan(&cursorCreated); err != nil { - return nil, fmt.Errorf("invalid cursor: %w", err) - } - conditions = append(conditions, `(a.created < ? OR (a.created = ? AND a.id < ?))`) - args = append(args, cursorCreated, cursorCreated, opts.Cursor) - } - - whereClause := "" - if len(conditions) > 0 { - whereClause = "WHERE " + strings.Join(conditions, " AND ") - } - - query := fmt.Sprintf(` - SELECT a.id, a.email, a.note, a.added_by, a.invite_id, a.created, - i.code_prefix, i.max_uses, i.use_count, i.expires_at, i.revoked - FROM allow_list a - LEFT JOIN invite_codes i ON a.invite_id = i.id AND a.invite_id != '' - %s ORDER BY a.created DESC, a.id DESC LIMIT ? - `, whereClause) - args = append(args, limit+1) - - rows, err := s.db.QueryContext(ctx, query, args...) - if err != nil { - return nil, err - } - defer rows.Close() - - var entries []store.AllowListEntryWithInvite - for rows.Next() { - var entry store.AllowListEntryWithInvite - var codePrefix sql.NullString - var maxUses, useCount, revoked sql.NullInt64 - var expiresAt sql.NullTime - if err := rows.Scan( - &entry.ID, &entry.Email, &entry.Note, &entry.AddedBy, &entry.InviteID, &entry.Created, - &codePrefix, &maxUses, &useCount, &expiresAt, &revoked, - ); err != nil { - return nil, err - } - if codePrefix.Valid { - entry.InviteCodePrefix = codePrefix.String - } - if maxUses.Valid { - entry.InviteMaxUses = int(maxUses.Int64) - } - if useCount.Valid { - entry.InviteUseCount = int(useCount.Int64) - } - if expiresAt.Valid { - entry.InviteExpiresAt = expiresAt.Time - } - if revoked.Valid { - entry.InviteRevoked = revoked.Int64 != 0 - } - entries = append(entries, entry) - } - if err := rows.Err(); err != nil { - return nil, err - } - if entries == nil { - entries = []store.AllowListEntryWithInvite{} - } - - var nextCursor string - if len(entries) > limit { - nextCursor = entries[limit-1].ID - entries = entries[:limit] - } - - return &store.ListResult[store.AllowListEntryWithInvite]{ - Items: entries, - TotalCount: totalCount, - NextCursor: nextCursor, - }, nil -} - -func (s *SQLiteStore) BulkAddAllowListEntries(ctx context.Context, entries []*store.AllowListEntry) (int, int, error) { - tx, err := s.db.BeginTx(ctx, nil) - if err != nil { - return 0, 0, err - } - defer tx.Rollback() - - stmt, err := tx.PrepareContext(ctx, ` - INSERT OR IGNORE INTO allow_list (id, email, note, added_by, invite_id, created) - VALUES (?, ?, ?, ?, ?, ?) - `) - if err != nil { - return 0, 0, err - } - defer stmt.Close() - - added := 0 - skipped := 0 - now := time.Now() - - for _, entry := range entries { - entry.Email = strings.ToLower(entry.Email) - if entry.Created.IsZero() { - entry.Created = now - } - result, err := stmt.ExecContext(ctx, entry.ID, entry.Email, entry.Note, entry.AddedBy, entry.InviteID, entry.Created) - if err != nil { - return added, skipped, err - } - rows, _ := result.RowsAffected() - if rows > 0 { - added++ - } else { - skipped++ - } - } - - if err := tx.Commit(); err != nil { - return 0, 0, err - } - return added, skipped, nil -} - -func (s *SQLiteStore) ListEmailDomains(ctx context.Context) ([]string, error) { - rows, err := s.db.QueryContext(ctx, ` - SELECT DISTINCT SUBSTR(email, INSTR(email, '@') + 1) AS domain - FROM users - WHERE email LIKE '%@%' - ORDER BY domain - `) - if err != nil { - return nil, err - } - defer rows.Close() - - var domains []string - for rows.Next() { - var domain string - if err := rows.Scan(&domain); err != nil { - return nil, err - } - domains = append(domains, domain) - } - return domains, rows.Err() -} - -// ============================================================================ -// Invite Code Operations -// ============================================================================ - -func (s *SQLiteStore) CreateInviteCode(ctx context.Context, invite *store.InviteCode) error { - if invite.Created.IsZero() { - invite.Created = time.Now() - } - - _, err := s.db.ExecContext(ctx, ` - INSERT INTO invite_codes (id, code_hash, code_prefix, max_uses, use_count, expires_at, revoked, created_by, note, created) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - `, invite.ID, invite.CodeHash, invite.CodePrefix, invite.MaxUses, invite.UseCount, - invite.ExpiresAt, invite.Revoked, invite.CreatedBy, invite.Note, invite.Created) - if err != nil { - if strings.Contains(err.Error(), "UNIQUE constraint failed") { - return store.ErrAlreadyExists - } - return err - } - return nil -} - -func (s *SQLiteStore) GetInviteCodeByHash(ctx context.Context, codeHash string) (*store.InviteCode, error) { - invite := &store.InviteCode{} - var revoked int - err := s.db.QueryRowContext(ctx, ` - SELECT id, code_hash, code_prefix, max_uses, use_count, expires_at, revoked, created_by, note, created - FROM invite_codes WHERE code_hash = ? - `, codeHash).Scan( - &invite.ID, &invite.CodeHash, &invite.CodePrefix, &invite.MaxUses, &invite.UseCount, - &invite.ExpiresAt, &revoked, &invite.CreatedBy, &invite.Note, &invite.Created, - ) - if err != nil { - if errors.Is(err, sql.ErrNoRows) { - return nil, store.ErrNotFound - } - return nil, err - } - invite.Revoked = revoked != 0 - return invite, nil -} - -func (s *SQLiteStore) GetInviteCode(ctx context.Context, id string) (*store.InviteCode, error) { - invite := &store.InviteCode{} - var revoked int - err := s.db.QueryRowContext(ctx, ` - SELECT id, code_hash, code_prefix, max_uses, use_count, expires_at, revoked, created_by, note, created - FROM invite_codes WHERE id = ? - `, id).Scan( - &invite.ID, &invite.CodeHash, &invite.CodePrefix, &invite.MaxUses, &invite.UseCount, - &invite.ExpiresAt, &revoked, &invite.CreatedBy, &invite.Note, &invite.Created, - ) - if err != nil { - if errors.Is(err, sql.ErrNoRows) { - return nil, store.ErrNotFound - } - return nil, err - } - invite.Revoked = revoked != 0 - return invite, nil -} - -func (s *SQLiteStore) ListInviteCodes(ctx context.Context, opts store.ListOptions) (*store.ListResult[store.InviteCode], error) { - var totalCount int - if err := s.db.QueryRowContext(ctx, "SELECT COUNT(*) FROM invite_codes").Scan(&totalCount); err != nil { - return nil, err - } - - limit := opts.Limit - if limit <= 0 { - limit = 50 - } - - var conditions []string - var args []interface{} - - if opts.Cursor != "" { - conditions = append(conditions, `(created < (SELECT created FROM invite_codes WHERE id = ?) - OR (created = (SELECT created FROM invite_codes WHERE id = ?) AND id < ?))`) - args = append(args, opts.Cursor, opts.Cursor, opts.Cursor) - } - - whereClause := "" - if len(conditions) > 0 { - whereClause = "WHERE " + strings.Join(conditions, " AND ") - } - - query := fmt.Sprintf(` - SELECT id, code_prefix, max_uses, use_count, expires_at, revoked, created_by, note, created - FROM invite_codes %s ORDER BY created DESC, id DESC LIMIT ? - `, whereClause) - args = append(args, limit+1) - - rows, err := s.db.QueryContext(ctx, query, args...) - if err != nil { - return nil, err - } - defer rows.Close() - - var invites []store.InviteCode - for rows.Next() { - var invite store.InviteCode - var revoked int - if err := rows.Scan( - &invite.ID, &invite.CodePrefix, &invite.MaxUses, &invite.UseCount, - &invite.ExpiresAt, &revoked, &invite.CreatedBy, &invite.Note, &invite.Created, - ); err != nil { - return nil, err - } - invite.Revoked = revoked != 0 - invites = append(invites, invite) - } - if err := rows.Err(); err != nil { - return nil, err - } - if invites == nil { - invites = []store.InviteCode{} - } - - var nextCursor string - if len(invites) > limit { - nextCursor = invites[limit-1].ID - invites = invites[:limit] - } - - return &store.ListResult[store.InviteCode]{ - Items: invites, - TotalCount: totalCount, - NextCursor: nextCursor, - }, nil -} - -func (s *SQLiteStore) IncrementInviteUseCount(ctx context.Context, id string) error { - result, err := s.db.ExecContext(ctx, ` - UPDATE invite_codes SET use_count = use_count + 1 - WHERE id = ? AND revoked = 0 AND expires_at > datetime('now') - AND (max_uses = 0 OR use_count < max_uses) - `, id) - if err != nil { - return err - } - rowsAffected, err := result.RowsAffected() - if err != nil { - return err - } - if rowsAffected == 0 { - return store.ErrNotFound - } - return nil -} - -func (s *SQLiteStore) RevokeInviteCode(ctx context.Context, id string) error { - result, err := s.db.ExecContext(ctx, "UPDATE invite_codes SET revoked = 1 WHERE id = ?", id) - if err != nil { - return err - } - rowsAffected, err := result.RowsAffected() - if err != nil { - return err - } - if rowsAffected == 0 { - return store.ErrNotFound - } - return nil -} - -func (s *SQLiteStore) DeleteInviteCode(ctx context.Context, id string) error { - result, err := s.db.ExecContext(ctx, "DELETE FROM invite_codes WHERE id = ?", id) - if err != nil { - return err - } - rowsAffected, err := result.RowsAffected() - if err != nil { - return err - } - if rowsAffected == 0 { - return store.ErrNotFound - } - return nil -} - -func (s *SQLiteStore) GetInviteStats(ctx context.Context) (*store.InviteStats, error) { - stats := &store.InviteStats{} - - // Count pending (active, not expired, not exhausted) invites - err := s.db.QueryRowContext(ctx, ` - SELECT COUNT(*) FROM invite_codes - WHERE revoked = 0 - AND expires_at > datetime('now') - AND (max_uses = 0 OR use_count < max_uses) - `).Scan(&stats.PendingInvites) - if err != nil { - return nil, err - } - - // Total redemptions across all invites - err = s.db.QueryRowContext(ctx, ` - SELECT COALESCE(SUM(use_count), 0) FROM invite_codes - `).Scan(&stats.TotalRedemptions) - if err != nil { - return nil, err - } - - // Allow list count - err = s.db.QueryRowContext(ctx, `SELECT COUNT(*) FROM allow_list`).Scan(&stats.AllowListCount) - if err != nil { - return nil, err - } - - // Recent invites that have been redeemed (use_count > 0), ordered by most recently created - rows, err := s.db.QueryContext(ctx, ` - SELECT id, code_prefix, use_count, max_uses, expires_at, note, created - FROM invite_codes - WHERE use_count > 0 - ORDER BY created DESC - LIMIT 10 - `) - if err != nil { - return nil, err - } - defer rows.Close() - - for rows.Next() { - var info store.InviteCodeInfo - if err := rows.Scan(&info.ID, &info.CodePrefix, &info.UseCount, &info.MaxUses, &info.ExpiresAt, &info.Note, &info.Created); err != nil { - return nil, err - } - stats.RecentRedemptions = append(stats.RecentRedemptions, info) - } - if stats.RecentRedemptions == nil { - stats.RecentRedemptions = []store.InviteCodeInfo{} - } - - return stats, rows.Err() -} - -// ============================================================================ -// ProjectProvider Operations - -// ============================================================================ - -func (s *SQLiteStore) AddProjectProvider(ctx context.Context, provider *store.ProjectProvider) error { - // Set LinkedAt to now if not already set - if provider.LinkedAt.IsZero() && provider.LinkedBy != "" { - provider.LinkedAt = time.Now() - } - - _, err := s.db.ExecContext(ctx, ` - INSERT OR REPLACE INTO project_contributors (project_id, broker_id, broker_name, local_path, mode, status, profiles, last_seen, linked_by, linked_at) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - `, - provider.ProjectID, provider.BrokerID, provider.BrokerName, provider.LocalPath, "", provider.Status, - "[]", provider.LastSeen, // profiles column kept for schema compat but no longer used - nullableString(provider.LinkedBy), nullableTime(provider.LinkedAt), - ) - return err -} - -func (s *SQLiteStore) RemoveProjectProvider(ctx context.Context, projectID, brokerID string) error { - result, err := s.db.ExecContext(ctx, "DELETE FROM project_contributors WHERE project_id = ? AND broker_id = ?", projectID, brokerID) - if err != nil { - return err - } - rowsAffected, err := result.RowsAffected() - if err != nil { - return err - } - if rowsAffected == 0 { - return store.ErrNotFound - } - return nil -} - -func (s *SQLiteStore) GetProjectProvider(ctx context.Context, projectID, brokerID string) (*store.ProjectProvider, error) { - var provider store.ProjectProvider - var localPath, linkedBy sql.NullString - var providerMode, profiles string // unused columns kept for schema compat - var lastSeen, linkedAt sql.NullTime - - err := s.db.QueryRowContext(ctx, ` - SELECT project_id, broker_id, broker_name, local_path, mode, status, profiles, last_seen, linked_by, linked_at - FROM project_contributors WHERE project_id = ? AND broker_id = ? - `, projectID, brokerID).Scan( - &provider.ProjectID, &provider.BrokerID, &provider.BrokerName, &localPath, &providerMode, &provider.Status, - &profiles, &lastSeen, &linkedBy, &linkedAt, - ) - if err != nil { - if errors.Is(err, sql.ErrNoRows) { - return nil, store.ErrNotFound - } - return nil, err - } - - if localPath.Valid { - provider.LocalPath = localPath.String - } - if lastSeen.Valid { - provider.LastSeen = lastSeen.Time - } - if linkedBy.Valid { - provider.LinkedBy = linkedBy.String - } - if linkedAt.Valid { - provider.LinkedAt = linkedAt.Time - } - // profiles column no longer used - lookup from RuntimeBroker.Profiles instead - - return &provider, nil -} - -func (s *SQLiteStore) GetProjectProviders(ctx context.Context, projectID string) ([]store.ProjectProvider, error) { - rows, err := s.db.QueryContext(ctx, ` - SELECT project_id, broker_id, broker_name, local_path, mode, status, profiles, last_seen, linked_by, linked_at - FROM project_contributors WHERE project_id = ? - `, projectID) - if err != nil { - return nil, err - } - defer rows.Close() - - var providers []store.ProjectProvider - for rows.Next() { - var provider store.ProjectProvider - var localPath, linkedBy sql.NullString - var providerMode, profiles string // unused columns kept for schema compat - var lastSeen, linkedAt sql.NullTime - - if err := rows.Scan( - &provider.ProjectID, &provider.BrokerID, &provider.BrokerName, &localPath, &providerMode, &provider.Status, - &profiles, &lastSeen, &linkedBy, &linkedAt, - ); err != nil { - return nil, err - } - - if localPath.Valid { - provider.LocalPath = localPath.String - } - if lastSeen.Valid { - provider.LastSeen = lastSeen.Time - } - if linkedBy.Valid { - provider.LinkedBy = linkedBy.String - } - if linkedAt.Valid { - provider.LinkedAt = linkedAt.Time - } - // profiles column no longer used - lookup from RuntimeBroker.Profiles instead - - providers = append(providers, provider) - } - - return providers, nil -} - -func (s *SQLiteStore) GetBrokerProjects(ctx context.Context, brokerID string) ([]store.ProjectProvider, error) { - rows, err := s.db.QueryContext(ctx, ` - SELECT project_id, broker_id, broker_name, local_path, mode, status, profiles, last_seen, linked_by, linked_at - FROM project_contributors WHERE broker_id = ? - `, brokerID) - if err != nil { - return nil, err - } - defer rows.Close() - - var providers []store.ProjectProvider - for rows.Next() { - var provider store.ProjectProvider - var localPath, linkedBy sql.NullString - var providerMode, profiles string // unused columns kept for schema compat - var lastSeen, linkedAt sql.NullTime - - if err := rows.Scan( - &provider.ProjectID, &provider.BrokerID, &provider.BrokerName, &localPath, &providerMode, &provider.Status, - &profiles, &lastSeen, &linkedBy, &linkedAt, - ); err != nil { - return nil, err - } - - if localPath.Valid { - provider.LocalPath = localPath.String - } - if lastSeen.Valid { - provider.LastSeen = lastSeen.Time - } - if linkedBy.Valid { - provider.LinkedBy = linkedBy.String - } - if linkedAt.Valid { - provider.LinkedAt = linkedAt.Time - } - // profiles column no longer used - lookup from RuntimeBroker.Profiles instead - - providers = append(providers, provider) - } - - return providers, nil -} - -func (s *SQLiteStore) UpdateProviderStatus(ctx context.Context, projectID, brokerID, status string) error { - now := time.Now() - - result, err := s.db.ExecContext(ctx, ` - UPDATE project_contributors SET status = ?, last_seen = ? WHERE project_id = ? AND broker_id = ? - `, status, now, projectID, brokerID) - if err != nil { - return err - } - - rowsAffected, err := result.RowsAffected() - if err != nil { - return err - } - if rowsAffected == 0 { - return store.ErrNotFound - } - return nil -} - -// ============================================================================ -// EnvVar Operations -// ============================================================================ - -func (s *SQLiteStore) CreateEnvVar(ctx context.Context, envVar *store.EnvVar) error { - now := time.Now() - envVar.Created = now - envVar.Updated = now - - _, err := s.db.ExecContext(ctx, ` - INSERT INTO env_vars (id, key, value, scope, scope_id, description, sensitive, injection_mode, secret, created_at, updated_at, created_by) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - `, - envVar.ID, envVar.Key, envVar.Value, envVar.Scope, envVar.ScopeID, - envVar.Description, envVar.Sensitive, envVar.InjectionMode, envVar.Secret, - envVar.Created, envVar.Updated, envVar.CreatedBy, - ) - if err != nil { - if strings.Contains(err.Error(), "UNIQUE constraint failed") { - return store.ErrAlreadyExists - } - return err - } - return nil -} - -func (s *SQLiteStore) GetEnvVar(ctx context.Context, key, scope, scopeID string) (*store.EnvVar, error) { - envVar := &store.EnvVar{} - - err := s.db.QueryRowContext(ctx, ` - SELECT id, key, value, scope, scope_id, description, sensitive, injection_mode, secret, created_at, updated_at, created_by - FROM env_vars WHERE key = ? AND scope = ? AND scope_id = ? - `, key, scope, scopeID).Scan( - &envVar.ID, &envVar.Key, &envVar.Value, &envVar.Scope, &envVar.ScopeID, - &envVar.Description, &envVar.Sensitive, &envVar.InjectionMode, &envVar.Secret, - &envVar.Created, &envVar.Updated, &envVar.CreatedBy, - ) - if err != nil { - if errors.Is(err, sql.ErrNoRows) { - return nil, store.ErrNotFound - } - return nil, err - } - - return envVar, nil -} - -func (s *SQLiteStore) UpdateEnvVar(ctx context.Context, envVar *store.EnvVar) error { - envVar.Updated = time.Now() - - result, err := s.db.ExecContext(ctx, ` - UPDATE env_vars SET - value = ?, description = ?, sensitive = ?, injection_mode = ?, secret = ?, updated_at = ? - WHERE key = ? AND scope = ? AND scope_id = ? - `, - envVar.Value, envVar.Description, envVar.Sensitive, envVar.InjectionMode, envVar.Secret, envVar.Updated, - envVar.Key, envVar.Scope, envVar.ScopeID, - ) - if err != nil { - return err - } - - rowsAffected, err := result.RowsAffected() - if err != nil { - return err - } - if rowsAffected == 0 { - return store.ErrNotFound - } - return nil -} - -func (s *SQLiteStore) UpsertEnvVar(ctx context.Context, envVar *store.EnvVar) (bool, error) { - now := time.Now() - envVar.Updated = now - - // Check if it already exists - existing, err := s.GetEnvVar(ctx, envVar.Key, envVar.Scope, envVar.ScopeID) - if err != nil && err != store.ErrNotFound { - return false, err - } - - if existing != nil { - // Update existing - envVar.ID = existing.ID - envVar.Created = existing.Created - envVar.CreatedBy = existing.CreatedBy - if err := s.UpdateEnvVar(ctx, envVar); err != nil { - return false, err - } - return false, nil - } - - // Create new - envVar.Created = now - if err := s.CreateEnvVar(ctx, envVar); err != nil { - return false, err - } - return true, nil -} - -func (s *SQLiteStore) DeleteEnvVar(ctx context.Context, key, scope, scopeID string) error { - result, err := s.db.ExecContext(ctx, "DELETE FROM env_vars WHERE key = ? AND scope = ? AND scope_id = ?", key, scope, scopeID) - if err != nil { - return err - } - rowsAffected, err := result.RowsAffected() - if err != nil { - return err - } - if rowsAffected == 0 { - return store.ErrNotFound - } - return nil -} - -func (s *SQLiteStore) DeleteEnvVarsByScope(ctx context.Context, scope, scopeID string) (int, error) { - result, err := s.db.ExecContext(ctx, "DELETE FROM env_vars WHERE scope = ? AND scope_id = ?", scope, scopeID) - if err != nil { - return 0, err - } - n, err := result.RowsAffected() - if err != nil { - return 0, err - } - return int(n), nil -} - -func (s *SQLiteStore) ListEnvVars(ctx context.Context, filter store.EnvVarFilter) ([]store.EnvVar, error) { - var conditions []string - var args []interface{} - - if filter.Scope != "" { - conditions = append(conditions, "scope = ?") - args = append(args, filter.Scope) - } - if filter.ScopeID != "" { - conditions = append(conditions, "scope_id = ?") - args = append(args, filter.ScopeID) - } - if filter.Key != "" { - conditions = append(conditions, "key = ?") - args = append(args, filter.Key) - } - - whereClause := "" - if len(conditions) > 0 { - whereClause = "WHERE " + strings.Join(conditions, " AND ") - } - - query := fmt.Sprintf(` - SELECT id, key, value, scope, scope_id, description, sensitive, injection_mode, secret, created_at, updated_at, created_by - FROM env_vars %s ORDER BY key - `, whereClause) - - rows, err := s.db.QueryContext(ctx, query, args...) - if err != nil { - return nil, err - } - defer rows.Close() - - var envVars []store.EnvVar - for rows.Next() { - var envVar store.EnvVar - if err := rows.Scan( - &envVar.ID, &envVar.Key, &envVar.Value, &envVar.Scope, &envVar.ScopeID, - &envVar.Description, &envVar.Sensitive, &envVar.InjectionMode, &envVar.Secret, - &envVar.Created, &envVar.Updated, &envVar.CreatedBy, - ); err != nil { - return nil, err - } - envVars = append(envVars, envVar) - } - - return envVars, nil -} - -// ============================================================================ -// Secret Operations -// ============================================================================ - -func (s *SQLiteStore) CreateSecret(ctx context.Context, secret *store.Secret) error { - now := time.Now() - secret.Created = now - secret.Updated = now - secret.Version = 1 - - if secret.SecretType == "" { - secret.SecretType = store.SecretTypeEnvironment - } - if secret.Target == "" { - secret.Target = secret.Key - } - if secret.InjectionMode == "" { - secret.InjectionMode = store.InjectionModeAsNeeded - } - - _, err := s.db.ExecContext(ctx, ` - INSERT INTO secrets (id, key, encrypted_value, secret_ref, secret_type, target, scope, scope_id, description, injection_mode, allow_progeny, version, created_at, updated_at, created_by, updated_by) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - `, - secret.ID, secret.Key, secret.EncryptedValue, nullableString(secret.SecretRef), - secret.SecretType, nullableString(secret.Target), - secret.Scope, secret.ScopeID, - secret.Description, secret.InjectionMode, boolToInt(secret.AllowProgeny), secret.Version, - secret.Created, secret.Updated, secret.CreatedBy, secret.UpdatedBy, - ) - if err != nil { - if strings.Contains(err.Error(), "UNIQUE constraint failed") { - return store.ErrAlreadyExists - } - return err - } - return nil -} - -func (s *SQLiteStore) GetSecret(ctx context.Context, key, scope, scopeID string) (*store.Secret, error) { - secret := &store.Secret{} - var target sql.NullString - var secretRef sql.NullString - - var allowProgeny int - err := s.db.QueryRowContext(ctx, ` - SELECT id, key, encrypted_value, secret_ref, secret_type, COALESCE(target, key), scope, scope_id, description, injection_mode, allow_progeny, version, created_at, updated_at, created_by, updated_by - FROM secrets WHERE key = ? AND scope = ? AND scope_id = ? - `, key, scope, scopeID).Scan( - &secret.ID, &secret.Key, &secret.EncryptedValue, &secretRef, - &secret.SecretType, &target, - &secret.Scope, &secret.ScopeID, - &secret.Description, &secret.InjectionMode, &allowProgeny, &secret.Version, - &secret.Created, &secret.Updated, &secret.CreatedBy, &secret.UpdatedBy, - ) - if err != nil { - if errors.Is(err, sql.ErrNoRows) { - return nil, store.ErrNotFound - } - return nil, err - } - - if target.Valid { - secret.Target = target.String - } - if secretRef.Valid { - secret.SecretRef = secretRef.String - } - secret.AllowProgeny = allowProgeny != 0 - - return secret, nil -} - -func (s *SQLiteStore) UpdateSecret(ctx context.Context, secret *store.Secret) error { - secret.Updated = time.Now() - secret.Version++ // Increment version on each update - - if secret.SecretType == "" { - secret.SecretType = store.SecretTypeEnvironment - } - if secret.Target == "" { - secret.Target = secret.Key - } - if secret.InjectionMode == "" { - secret.InjectionMode = store.InjectionModeAsNeeded - } - - result, err := s.db.ExecContext(ctx, ` - UPDATE secrets SET - encrypted_value = ?, secret_ref = ?, secret_type = ?, target = ?, description = ?, injection_mode = ?, allow_progeny = ?, version = ?, updated_at = ?, updated_by = ? - WHERE key = ? AND scope = ? AND scope_id = ? - `, - secret.EncryptedValue, nullableString(secret.SecretRef), - secret.SecretType, nullableString(secret.Target), - secret.Description, secret.InjectionMode, boolToInt(secret.AllowProgeny), secret.Version, secret.Updated, secret.UpdatedBy, - secret.Key, secret.Scope, secret.ScopeID, - ) - if err != nil { - return err - } - - rowsAffected, err := result.RowsAffected() - if err != nil { - return err - } - if rowsAffected == 0 { - return store.ErrNotFound - } - return nil -} - -func (s *SQLiteStore) UpsertSecret(ctx context.Context, secret *store.Secret) (bool, error) { - now := time.Now() - secret.Updated = now - - // Check if it already exists - existing, err := s.GetSecret(ctx, secret.Key, secret.Scope, secret.ScopeID) - if err != nil && err != store.ErrNotFound { - return false, err - } - - if existing != nil { - // Update existing - secret.ID = existing.ID - secret.Created = existing.Created - secret.CreatedBy = existing.CreatedBy - secret.Version = existing.Version // Will be incremented in UpdateSecret - if err := s.UpdateSecret(ctx, secret); err != nil { - return false, err - } - return false, nil - } - - // Create new - secret.Created = now - if err := s.CreateSecret(ctx, secret); err != nil { - return false, err - } - return true, nil -} - -func (s *SQLiteStore) DeleteSecret(ctx context.Context, key, scope, scopeID string) error { - result, err := s.db.ExecContext(ctx, "DELETE FROM secrets WHERE key = ? AND scope = ? AND scope_id = ?", key, scope, scopeID) - if err != nil { - return err - } - rowsAffected, err := result.RowsAffected() - if err != nil { - return err - } - if rowsAffected == 0 { - return store.ErrNotFound - } - return nil -} - -func (s *SQLiteStore) DeleteSecretsByScope(ctx context.Context, scope, scopeID string) (int, error) { - result, err := s.db.ExecContext(ctx, "DELETE FROM secrets WHERE scope = ? AND scope_id = ?", scope, scopeID) - if err != nil { - return 0, err - } - n, err := result.RowsAffected() - if err != nil { - return 0, err - } - return int(n), nil -} - -func (s *SQLiteStore) ListSecrets(ctx context.Context, filter store.SecretFilter) ([]store.Secret, error) { - var conditions []string - var args []interface{} - - if filter.Scope != "" { - conditions = append(conditions, "scope = ?") - args = append(args, filter.Scope) - } - if filter.ScopeID != "" { - conditions = append(conditions, "scope_id = ?") - args = append(args, filter.ScopeID) - } - if filter.Key != "" { - conditions = append(conditions, "key = ?") - args = append(args, filter.Key) - } - if filter.Type != "" { - conditions = append(conditions, "secret_type = ?") - args = append(args, filter.Type) - } - - whereClause := "" - if len(conditions) > 0 { - whereClause = "WHERE " + strings.Join(conditions, " AND ") - } - - // Note: We do NOT select encrypted_value for listing - query := fmt.Sprintf(` - SELECT id, key, secret_ref, secret_type, COALESCE(target, key), scope, scope_id, description, injection_mode, allow_progeny, version, created_at, updated_at, created_by, updated_by - FROM secrets %s ORDER BY key - `, whereClause) - - rows, err := s.db.QueryContext(ctx, query, args...) - if err != nil { - return nil, err - } - defer rows.Close() - - var secrets []store.Secret - for rows.Next() { - var secret store.Secret - var target sql.NullString - var secretRef sql.NullString - var allowProgeny int - if err := rows.Scan( - &secret.ID, &secret.Key, &secretRef, &secret.SecretType, &target, - &secret.Scope, &secret.ScopeID, - &secret.Description, &secret.InjectionMode, &allowProgeny, &secret.Version, - &secret.Created, &secret.Updated, &secret.CreatedBy, &secret.UpdatedBy, - ); err != nil { - return nil, err - } - if target.Valid { - secret.Target = target.String - } - if secretRef.Valid { - secret.SecretRef = secretRef.String - } - secret.AllowProgeny = allowProgeny != 0 - secrets = append(secrets, secret) - } - - return secrets, nil -} - -func (s *SQLiteStore) ListProgenySecrets(ctx context.Context, ancestorIDs []string) ([]store.Secret, error) { - if len(ancestorIDs) == 0 { - return nil, nil - } - - // Build placeholder list for IN clause - placeholders := make([]string, len(ancestorIDs)) - args := make([]interface{}, len(ancestorIDs)) - for i, id := range ancestorIDs { - placeholders[i] = "?" - args[i] = id - } - - query := fmt.Sprintf(` - SELECT id, key, secret_ref, secret_type, COALESCE(target, key), scope, scope_id, description, injection_mode, allow_progeny, version, created_at, updated_at, created_by, updated_by - FROM secrets - WHERE scope = 'user' AND allow_progeny = 1 AND created_by IN (%s) - ORDER BY key - `, strings.Join(placeholders, ", ")) - - rows, err := s.db.QueryContext(ctx, query, args...) - if err != nil { - return nil, err - } - defer rows.Close() - - var secrets []store.Secret - for rows.Next() { - var secret store.Secret - var target sql.NullString - var secretRef sql.NullString - var allowProgeny int - if err := rows.Scan( - &secret.ID, &secret.Key, &secretRef, &secret.SecretType, &target, - &secret.Scope, &secret.ScopeID, - &secret.Description, &secret.InjectionMode, &allowProgeny, &secret.Version, - &secret.Created, &secret.Updated, &secret.CreatedBy, &secret.UpdatedBy, - ); err != nil { - return nil, err - } - if target.Valid { - secret.Target = target.String - } - if secretRef.Valid { - secret.SecretRef = secretRef.String - } - secret.AllowProgeny = allowProgeny != 0 - secrets = append(secrets, secret) - } - - return secrets, nil -} - -func (s *SQLiteStore) GetSecretValue(ctx context.Context, key, scope, scopeID string) (string, error) { - var encryptedValue string - - err := s.db.QueryRowContext(ctx, ` - SELECT encrypted_value FROM secrets WHERE key = ? AND scope = ? AND scope_id = ? - `, key, scope, scopeID).Scan(&encryptedValue) - if err != nil { - if errors.Is(err, sql.ErrNoRows) { - return "", store.ErrNotFound - } - return "", err - } - - return encryptedValue, nil -} - -// ============================================================================ -// Group Operations -// ============================================================================ - -func (s *SQLiteStore) CreateGroup(ctx context.Context, group *store.Group) error { - now := time.Now() - group.Created = now - group.Updated = now - if group.GroupType == "" { - group.GroupType = store.GroupTypeExplicit - } - - _, err := s.db.ExecContext(ctx, ` - INSERT INTO groups (id, name, slug, description, group_type, project_id, parent_id, labels, annotations, created_at, updated_at, created_by, owner_id) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - `, - group.ID, group.Name, group.Slug, group.Description, - group.GroupType, nullableString(group.ProjectID), - nullableString(group.ParentID), - marshalJSON(group.Labels), marshalJSON(group.Annotations), - group.Created, group.Updated, group.CreatedBy, group.OwnerID, - ) - if err != nil { - if strings.Contains(err.Error(), "UNIQUE constraint failed") { - return store.ErrAlreadyExists - } - return err - } - return nil -} - -func (s *SQLiteStore) GetGroup(ctx context.Context, id string) (*store.Group, error) { - group := &store.Group{} - var labels, annotations string - var parentID, projectID sql.NullString - - err := s.db.QueryRowContext(ctx, ` - SELECT id, name, slug, description, group_type, project_id, parent_id, labels, annotations, created_at, updated_at, created_by, owner_id - FROM groups WHERE id = ? - `, id).Scan( - &group.ID, &group.Name, &group.Slug, &group.Description, - &group.GroupType, &projectID, - &parentID, - &labels, &annotations, - &group.Created, &group.Updated, &group.CreatedBy, &group.OwnerID, - ) - if err != nil { - if errors.Is(err, sql.ErrNoRows) { - return nil, store.ErrNotFound - } - return nil, err - } - - if parentID.Valid { - group.ParentID = parentID.String - } - if projectID.Valid { - group.ProjectID = projectID.String - } - unmarshalJSON(labels, &group.Labels) - unmarshalJSON(annotations, &group.Annotations) - if group.GroupType == "" { - group.GroupType = store.GroupTypeExplicit - } - - return group, nil -} - -func (s *SQLiteStore) GetGroupBySlug(ctx context.Context, slug string) (*store.Group, error) { - var id string - err := s.db.QueryRowContext(ctx, "SELECT id FROM groups WHERE slug = ?", slug).Scan(&id) - if err != nil { - if errors.Is(err, sql.ErrNoRows) { - return nil, store.ErrNotFound - } - return nil, err - } - return s.GetGroup(ctx, id) -} - -func (s *SQLiteStore) UpdateGroup(ctx context.Context, group *store.Group) error { - group.Updated = time.Now() - - result, err := s.db.ExecContext(ctx, ` - UPDATE groups SET - name = ?, slug = ?, description = ?, group_type = ?, project_id = ?, - parent_id = ?, labels = ?, annotations = ?, - updated_at = ?, owner_id = ? - WHERE id = ? - `, - group.Name, group.Slug, group.Description, - group.GroupType, nullableString(group.ProjectID), - nullableString(group.ParentID), - marshalJSON(group.Labels), marshalJSON(group.Annotations), - group.Updated, group.OwnerID, - group.ID, - ) - if err != nil { - return err - } - - rowsAffected, err := result.RowsAffected() - if err != nil { - return err - } - if rowsAffected == 0 { - return store.ErrNotFound - } - return nil -} - -func (s *SQLiteStore) DeleteGroup(ctx context.Context, id string) error { - result, err := s.db.ExecContext(ctx, "DELETE FROM groups WHERE id = ?", id) - if err != nil { - return err - } - rowsAffected, err := result.RowsAffected() - if err != nil { - return err - } - if rowsAffected == 0 { - return store.ErrNotFound - } - return nil -} - -func (s *SQLiteStore) ListGroups(ctx context.Context, filter store.GroupFilter, opts store.ListOptions) (*store.ListResult[store.Group], error) { - var conditions []string - var args []interface{} - - if filter.OwnerID != "" { - conditions = append(conditions, "owner_id = ?") - args = append(args, filter.OwnerID) - } - if filter.ParentID != "" { - conditions = append(conditions, "parent_id = ?") - args = append(args, filter.ParentID) - } - if filter.GroupType != "" { - conditions = append(conditions, "group_type = ?") - args = append(args, filter.GroupType) - } - if filter.ProjectID != "" { - conditions = append(conditions, "project_id = ?") - args = append(args, filter.ProjectID) - } - - whereClause := "" - if len(conditions) > 0 { - whereClause = "WHERE " + strings.Join(conditions, " AND ") - } - - var totalCount int - countQuery := fmt.Sprintf("SELECT COUNT(*) FROM groups %s", whereClause) - if err := s.db.QueryRowContext(ctx, countQuery, args...).Scan(&totalCount); err != nil { - return nil, err - } - - limit := opts.Limit - if limit <= 0 { - limit = 50 - } - - query := fmt.Sprintf(` - SELECT id, name, slug, description, group_type, project_id, parent_id, labels, annotations, created_at, updated_at, created_by, owner_id - FROM groups %s ORDER BY created_at DESC LIMIT ? - `, whereClause) - args = append(args, limit) - - rows, err := s.db.QueryContext(ctx, query, args...) - if err != nil { - return nil, err - } - defer rows.Close() - - var groups []store.Group - for rows.Next() { - var group store.Group - var labels, annotations string - var parentID, projectID sql.NullString - - if err := rows.Scan( - &group.ID, &group.Name, &group.Slug, &group.Description, - &group.GroupType, &projectID, - &parentID, - &labels, &annotations, - &group.Created, &group.Updated, &group.CreatedBy, &group.OwnerID, - ); err != nil { - return nil, err - } - - if parentID.Valid { - group.ParentID = parentID.String - } - if projectID.Valid { - group.ProjectID = projectID.String - } - unmarshalJSON(labels, &group.Labels) - unmarshalJSON(annotations, &group.Annotations) - if group.GroupType == "" { - group.GroupType = store.GroupTypeExplicit - } - - groups = append(groups, group) - } - - return &store.ListResult[store.Group]{ - Items: groups, - TotalCount: totalCount, - }, nil -} - -func (s *SQLiteStore) AddGroupMember(ctx context.Context, member *store.GroupMember) error { - if member.AddedAt.IsZero() { - member.AddedAt = time.Now() - } - - _, err := s.db.ExecContext(ctx, ` - INSERT INTO group_members (group_id, member_type, member_id, role, added_at, added_by) - VALUES (?, ?, ?, ?, ?, ?) - `, - member.GroupID, member.MemberType, member.MemberID, member.Role, member.AddedAt, member.AddedBy, - ) - if err != nil { - if strings.Contains(err.Error(), "UNIQUE constraint failed") || strings.Contains(err.Error(), "PRIMARY KEY constraint failed") { - return store.ErrAlreadyExists - } - return err - } - return nil -} - -func (s *SQLiteStore) UpdateGroupMemberRole(ctx context.Context, groupID, memberType, memberID, newRole string) error { - result, err := s.db.ExecContext(ctx, - `UPDATE group_members SET role = ? WHERE group_id = ? AND member_type = ? AND member_id = ?`, - newRole, groupID, memberType, memberID, - ) - if err != nil { - return err - } - rows, _ := result.RowsAffected() - if rows == 0 { - return store.ErrNotFound - } - return nil -} - -func (s *SQLiteStore) RemoveGroupMember(ctx context.Context, groupID, memberType, memberID string) error { - result, err := s.db.ExecContext(ctx, - "DELETE FROM group_members WHERE group_id = ? AND member_type = ? AND member_id = ?", - groupID, memberType, memberID, - ) - if err != nil { - return err - } - rowsAffected, err := result.RowsAffected() - if err != nil { - return err - } - if rowsAffected == 0 { - return store.ErrNotFound - } - return nil -} - -func (s *SQLiteStore) GetGroupMembers(ctx context.Context, groupID string) ([]store.GroupMember, error) { - rows, err := s.db.QueryContext(ctx, ` - SELECT group_id, member_type, member_id, role, added_at, added_by - FROM group_members WHERE group_id = ? - `, groupID) - if err != nil { - return nil, err - } - defer rows.Close() - - var members []store.GroupMember - for rows.Next() { - var member store.GroupMember - if err := rows.Scan( - &member.GroupID, &member.MemberType, &member.MemberID, &member.Role, &member.AddedAt, &member.AddedBy, - ); err != nil { - return nil, err - } - members = append(members, member) - } - - return members, nil -} - -func (s *SQLiteStore) GetUserGroups(ctx context.Context, userID string) ([]store.GroupMember, error) { - rows, err := s.db.QueryContext(ctx, ` - SELECT group_id, member_type, member_id, role, added_at, added_by - FROM group_members WHERE member_type = 'user' AND member_id = ? - `, userID) - if err != nil { - return nil, err - } - defer rows.Close() - - var memberships []store.GroupMember - for rows.Next() { - var member store.GroupMember - if err := rows.Scan( - &member.GroupID, &member.MemberType, &member.MemberID, &member.Role, &member.AddedAt, &member.AddedBy, - ); err != nil { - return nil, err - } - memberships = append(memberships, member) - } - - return memberships, nil -} - -func (s *SQLiteStore) GetGroupMembership(ctx context.Context, groupID, memberType, memberID string) (*store.GroupMember, error) { - member := &store.GroupMember{} - - err := s.db.QueryRowContext(ctx, ` - SELECT group_id, member_type, member_id, role, added_at, added_by - FROM group_members WHERE group_id = ? AND member_type = ? AND member_id = ? - `, groupID, memberType, memberID).Scan( - &member.GroupID, &member.MemberType, &member.MemberID, &member.Role, &member.AddedAt, &member.AddedBy, - ) - if err != nil { - if errors.Is(err, sql.ErrNoRows) { - return nil, store.ErrNotFound - } - return nil, err - } - - return member, nil -} - -// WouldCreateCycle checks if adding memberGroupID as a member of groupID would create a cycle. -// A cycle exists if groupID is reachable from memberGroupID by following the containment relationship. -// Example: if A contains B, and we try to add A as member of B, we'd have A->B->A (cycle). -func (s *SQLiteStore) WouldCreateCycle(ctx context.Context, groupID, memberGroupID string) (bool, error) { - // If they're the same, it's a direct cycle - if groupID == memberGroupID { - return true, nil - } - - // Check if groupID is reachable from memberGroupID by traversing DOWN the containment graph - // (i.e., checking what groups memberGroupID contains, and what those contain, etc.) - visited := make(map[string]bool) - return s.hasPathDown(ctx, memberGroupID, groupID, visited) -} - -// hasPathDown checks if 'target' is reachable from 'current' by following containment. -// It looks at what groups 'current' contains as members. -func (s *SQLiteStore) hasPathDown(ctx context.Context, current, target string, visited map[string]bool) (bool, error) { - if current == target { - return true, nil - } - if visited[current] { - return false, nil - } - visited[current] = true - - // Get all groups that 'current' contains (groups where current is the group_id) - rows, err := s.db.QueryContext(ctx, - "SELECT member_id FROM group_members WHERE member_type = 'group' AND group_id = ?", current) - if err != nil { - return false, err - } - defer rows.Close() - - for rows.Next() { - var childGroupID string - if err := rows.Scan(&childGroupID); err != nil { - return false, err - } - found, err := s.hasPathDown(ctx, childGroupID, target, visited) - if err != nil { - return false, err - } - if found { - return true, nil - } - } - - return false, nil -} - -// GetEffectiveGroups returns all groups a user belongs to, including transitive memberships. -func (s *SQLiteStore) GetEffectiveGroups(ctx context.Context, userID string) ([]string, error) { - // Start with direct group memberships - directMemberships, err := s.GetUserGroups(ctx, userID) - if err != nil { - return nil, err - } - - effectiveGroups := make(map[string]bool) - for _, m := range directMemberships { - effectiveGroups[m.GroupID] = true - // Add transitive group memberships - if err := s.addTransitiveGroups(ctx, m.GroupID, effectiveGroups); err != nil { - return nil, err - } - } - - result := make([]string, 0, len(effectiveGroups)) - for groupID := range effectiveGroups { - result = append(result, groupID) - } - - return result, nil -} - -// addTransitiveGroups recursively adds all groups that contain the given group. -func (s *SQLiteStore) addTransitiveGroups(ctx context.Context, groupID string, visited map[string]bool) error { - // Find all groups where this group is a member - rows, err := s.db.QueryContext(ctx, - "SELECT group_id FROM group_members WHERE member_type = 'group' AND member_id = ?", groupID) - if err != nil { - return err - } - - // Collect all parent group IDs first, then close rows before recursing - // This avoids issues with SQLite connections during recursive queries - var parentGroupIDs []string - for rows.Next() { - var parentGroupID string - if err := rows.Scan(&parentGroupID); err != nil { - rows.Close() - return err - } - parentGroupIDs = append(parentGroupIDs, parentGroupID) - } - rows.Close() - - // Now recurse after rows are closed - for _, parentGroupID := range parentGroupIDs { - if !visited[parentGroupID] { - visited[parentGroupID] = true - if err := s.addTransitiveGroups(ctx, parentGroupID, visited); err != nil { - return err - } - } - } - - return nil -} - -// GetGroupByProjectID retrieves the project_agents group associated with a project. -func (s *SQLiteStore) GetGroupByProjectID(ctx context.Context, projectID string) (*store.Group, error) { - var id string - err := s.db.QueryRowContext(ctx, "SELECT id FROM groups WHERE project_id = ? AND group_type = ? LIMIT 1", - projectID, store.GroupTypeProjectAgents).Scan(&id) - if err != nil { - if errors.Is(err, sql.ErrNoRows) { - return nil, store.ErrNotFound - } - return nil, err - } - return s.GetGroup(ctx, id) -} - -// GetEffectiveGroupsForAgent returns all groups an agent belongs to. -func (s *SQLiteStore) GetEffectiveGroupsForAgent(ctx context.Context, agentID string) ([]string, error) { - return nil, nil -} - -// CheckDelegatedAccess is a stub for the SQLite store. Delegation resolution -// is implemented in the Ent adapter. -func (s *SQLiteStore) CheckDelegatedAccess(ctx context.Context, agentID string, conditions *store.PolicyConditions) (bool, error) { - return false, nil -} - -// GetGroupsByIDs is a stub for the SQLite store. Group retrieval by IDs -// is implemented in the Ent adapter. -func (s *SQLiteStore) GetGroupsByIDs(ctx context.Context, ids []string) ([]store.Group, error) { - if len(ids) == 0 { - return nil, nil - } - - placeholders := make([]string, len(ids)) - args := make([]interface{}, len(ids)) - for i, id := range ids { - placeholders[i] = "?" - args[i] = id - } - - rows, err := s.db.QueryContext(ctx, - `SELECT id, name, slug, description, group_type, project_id, parent_id, labels, annotations, created_at, updated_at, created_by, owner_id - FROM groups WHERE id IN (`+strings.Join(placeholders, ",")+`)`, args...) - if err != nil { - return nil, err - } - defer rows.Close() - - var groups []store.Group - for rows.Next() { - var g store.Group - var labels, annotations string - var parentID, projectID sql.NullString - if err := rows.Scan( - &g.ID, &g.Name, &g.Slug, &g.Description, - &g.GroupType, &projectID, - &parentID, - &labels, &annotations, - &g.Created, &g.Updated, &g.CreatedBy, &g.OwnerID, - ); err != nil { - return nil, err - } - if parentID.Valid { - g.ParentID = parentID.String - } - if projectID.Valid { - g.ProjectID = projectID.String - } - unmarshalJSON(labels, &g.Labels) - unmarshalJSON(annotations, &g.Annotations) - if g.GroupType == "" { - g.GroupType = store.GroupTypeExplicit - } - groups = append(groups, g) - } - - return groups, rows.Err() -} - -func (s *SQLiteStore) CountGroupMembersByRole(ctx context.Context, groupID, role string) (int, error) { - var count int - err := s.db.QueryRowContext(ctx, - `SELECT COUNT(*) FROM group_members WHERE group_id = ? AND role = ?`, - groupID, role, - ).Scan(&count) - if err != nil { - return 0, err - } - return count, nil -} - -// ============================================================================ -// Policy Operations -// ============================================================================ - -func (s *SQLiteStore) CreatePolicy(ctx context.Context, policy *store.Policy) error { - now := time.Now() - policy.Created = now - policy.Updated = now - - _, err := s.db.ExecContext(ctx, ` - INSERT INTO policies (id, name, description, scope_type, scope_id, resource_type, resource_id, actions, effect, conditions, priority, labels, annotations, created_at, updated_at, created_by) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - `, - policy.ID, policy.Name, policy.Description, policy.ScopeType, policy.ScopeID, - policy.ResourceType, policy.ResourceID, - marshalJSON(policy.Actions), policy.Effect, marshalJSON(policy.Conditions), - policy.Priority, marshalJSON(policy.Labels), marshalJSON(policy.Annotations), - policy.Created, policy.Updated, policy.CreatedBy, - ) - if err != nil { - if strings.Contains(err.Error(), "UNIQUE constraint failed") { - return store.ErrAlreadyExists - } - return err - } - return nil -} - -func (s *SQLiteStore) GetPolicy(ctx context.Context, id string) (*store.Policy, error) { - policy := &store.Policy{} - var actions, conditions, labels, annotations string - - err := s.db.QueryRowContext(ctx, ` - SELECT id, name, description, scope_type, scope_id, resource_type, resource_id, actions, effect, conditions, priority, labels, annotations, created_at, updated_at, created_by - FROM policies WHERE id = ? - `, id).Scan( - &policy.ID, &policy.Name, &policy.Description, &policy.ScopeType, &policy.ScopeID, - &policy.ResourceType, &policy.ResourceID, - &actions, &policy.Effect, &conditions, - &policy.Priority, &labels, &annotations, - &policy.Created, &policy.Updated, &policy.CreatedBy, - ) - if err != nil { - if errors.Is(err, sql.ErrNoRows) { - return nil, store.ErrNotFound - } - return nil, err - } - - unmarshalJSON(actions, &policy.Actions) - unmarshalJSON(conditions, &policy.Conditions) - unmarshalJSON(labels, &policy.Labels) - unmarshalJSON(annotations, &policy.Annotations) - - return policy, nil -} - -func (s *SQLiteStore) UpdatePolicy(ctx context.Context, policy *store.Policy) error { - policy.Updated = time.Now() - - result, err := s.db.ExecContext(ctx, ` - UPDATE policies SET - name = ?, description = ?, scope_type = ?, scope_id = ?, - resource_type = ?, resource_id = ?, - actions = ?, effect = ?, conditions = ?, - priority = ?, labels = ?, annotations = ?, - updated_at = ? - WHERE id = ? - `, - policy.Name, policy.Description, policy.ScopeType, policy.ScopeID, - policy.ResourceType, policy.ResourceID, - marshalJSON(policy.Actions), policy.Effect, marshalJSON(policy.Conditions), - policy.Priority, marshalJSON(policy.Labels), marshalJSON(policy.Annotations), - policy.Updated, - policy.ID, - ) - if err != nil { - return err - } - - rowsAffected, err := result.RowsAffected() - if err != nil { - return err - } - if rowsAffected == 0 { - return store.ErrNotFound - } - return nil -} - -func (s *SQLiteStore) DeletePolicy(ctx context.Context, id string) error { - result, err := s.db.ExecContext(ctx, "DELETE FROM policies WHERE id = ?", id) - if err != nil { - return err - } - rowsAffected, err := result.RowsAffected() - if err != nil { - return err - } - if rowsAffected == 0 { - return store.ErrNotFound - } - return nil -} - -func (s *SQLiteStore) ListPolicies(ctx context.Context, filter store.PolicyFilter, opts store.ListOptions) (*store.ListResult[store.Policy], error) { - var conditions []string - var args []interface{} - - if filter.Name != "" { - conditions = append(conditions, "name = ?") - args = append(args, filter.Name) - } - if filter.ScopeType != "" { - conditions = append(conditions, "scope_type = ?") - args = append(args, filter.ScopeType) - } - if filter.ScopeID != "" { - conditions = append(conditions, "scope_id = ?") - args = append(args, filter.ScopeID) - } - if filter.ResourceType != "" { - conditions = append(conditions, "resource_type = ?") - args = append(args, filter.ResourceType) - } - if filter.Effect != "" { - conditions = append(conditions, "effect = ?") - args = append(args, filter.Effect) - } - - whereClause := "" - if len(conditions) > 0 { - whereClause = "WHERE " + strings.Join(conditions, " AND ") - } - - var totalCount int - countQuery := fmt.Sprintf("SELECT COUNT(*) FROM policies %s", whereClause) - if err := s.db.QueryRowContext(ctx, countQuery, args...).Scan(&totalCount); err != nil { - return nil, err - } - - limit := opts.Limit - if limit <= 0 { - limit = 50 - } - - query := fmt.Sprintf(` - SELECT id, name, description, scope_type, scope_id, resource_type, resource_id, actions, effect, conditions, priority, labels, annotations, created_at, updated_at, created_by - FROM policies %s ORDER BY priority DESC, created_at DESC LIMIT ? - `, whereClause) - args = append(args, limit) - - rows, err := s.db.QueryContext(ctx, query, args...) - if err != nil { - return nil, err - } - defer rows.Close() - - var policies []store.Policy - for rows.Next() { - var policy store.Policy - var actions, conditions, labels, annotations string - - if err := rows.Scan( - &policy.ID, &policy.Name, &policy.Description, &policy.ScopeType, &policy.ScopeID, - &policy.ResourceType, &policy.ResourceID, - &actions, &policy.Effect, &conditions, - &policy.Priority, &labels, &annotations, - &policy.Created, &policy.Updated, &policy.CreatedBy, - ); err != nil { - return nil, err - } - - unmarshalJSON(actions, &policy.Actions) - unmarshalJSON(conditions, &policy.Conditions) - unmarshalJSON(labels, &policy.Labels) - unmarshalJSON(annotations, &policy.Annotations) - - policies = append(policies, policy) - } - - return &store.ListResult[store.Policy]{ - Items: policies, - TotalCount: totalCount, - }, nil -} - -func (s *SQLiteStore) AddPolicyBinding(ctx context.Context, binding *store.PolicyBinding) error { - _, err := s.db.ExecContext(ctx, ` - INSERT INTO policy_bindings (policy_id, principal_type, principal_id) - VALUES (?, ?, ?) - `, - binding.PolicyID, binding.PrincipalType, binding.PrincipalID, - ) - if err != nil { - if strings.Contains(err.Error(), "UNIQUE constraint failed") || strings.Contains(err.Error(), "PRIMARY KEY constraint failed") { - return store.ErrAlreadyExists - } - return err - } - return nil -} - -func (s *SQLiteStore) RemovePolicyBinding(ctx context.Context, policyID, principalType, principalID string) error { - result, err := s.db.ExecContext(ctx, - "DELETE FROM policy_bindings WHERE policy_id = ? AND principal_type = ? AND principal_id = ?", - policyID, principalType, principalID, - ) - if err != nil { - return err - } - rowsAffected, err := result.RowsAffected() - if err != nil { - return err - } - if rowsAffected == 0 { - return store.ErrNotFound - } - return nil -} - -func (s *SQLiteStore) GetPolicyBindings(ctx context.Context, policyID string) ([]store.PolicyBinding, error) { - rows, err := s.db.QueryContext(ctx, ` - SELECT policy_id, principal_type, principal_id - FROM policy_bindings WHERE policy_id = ? - `, policyID) - if err != nil { - return nil, err - } - defer rows.Close() - - var bindings []store.PolicyBinding - for rows.Next() { - var binding store.PolicyBinding - if err := rows.Scan(&binding.PolicyID, &binding.PrincipalType, &binding.PrincipalID); err != nil { - return nil, err - } - bindings = append(bindings, binding) - } - - return bindings, nil -} - -func (s *SQLiteStore) GetPoliciesForPrincipal(ctx context.Context, principalType, principalID string) ([]store.Policy, error) { - rows, err := s.db.QueryContext(ctx, ` - SELECT p.id, p.name, p.description, p.scope_type, p.scope_id, p.resource_type, p.resource_id, p.actions, p.effect, p.conditions, p.priority, p.labels, p.annotations, p.created_at, p.updated_at, p.created_by - FROM policies p - INNER JOIN policy_bindings pb ON p.id = pb.policy_id - WHERE pb.principal_type = ? AND pb.principal_id = ? - ORDER BY p.priority DESC, p.created_at DESC - `, principalType, principalID) - if err != nil { - return nil, err - } - defer rows.Close() - - var policies []store.Policy - for rows.Next() { - var policy store.Policy - var actions, conditions, labels, annotations string - - if err := rows.Scan( - &policy.ID, &policy.Name, &policy.Description, &policy.ScopeType, &policy.ScopeID, - &policy.ResourceType, &policy.ResourceID, - &actions, &policy.Effect, &conditions, - &policy.Priority, &labels, &annotations, - &policy.Created, &policy.Updated, &policy.CreatedBy, - ); err != nil { - return nil, err - } - - unmarshalJSON(actions, &policy.Actions) - unmarshalJSON(conditions, &policy.Conditions) - unmarshalJSON(labels, &policy.Labels) - unmarshalJSON(annotations, &policy.Annotations) - - policies = append(policies, policy) - } - - return policies, nil -} - -func (s *SQLiteStore) GetPoliciesForPrincipals(ctx context.Context, principals []store.PrincipalRef) ([]store.Policy, error) { - if len(principals) == 0 { - return nil, nil - } - - // Build dynamic OR clauses for each principal - var clauses []string - var args []interface{} - for _, p := range principals { - clauses = append(clauses, "(pb.principal_type = ? AND pb.principal_id = ?)") - args = append(args, p.Type, p.ID) - } - - query := ` - SELECT DISTINCT p.id, p.name, p.description, p.scope_type, p.scope_id, p.resource_type, p.resource_id, p.actions, p.effect, p.conditions, p.priority, p.labels, p.annotations, p.created_at, p.updated_at, p.created_by - FROM policies p - INNER JOIN policy_bindings pb ON p.id = pb.policy_id - WHERE ` + strings.Join(clauses, " OR ") + ` - ORDER BY - CASE p.scope_type WHEN 'hub' THEN 0 WHEN 'project' THEN 1 WHEN 'resource' THEN 2 END, - p.priority ASC - ` - - rows, err := s.db.QueryContext(ctx, query, args...) - if err != nil { - return nil, err - } - defer rows.Close() - - var policies []store.Policy - for rows.Next() { - var policy store.Policy - var actions, conditions, labels, annotations string - - if err := rows.Scan( - &policy.ID, &policy.Name, &policy.Description, &policy.ScopeType, &policy.ScopeID, - &policy.ResourceType, &policy.ResourceID, - &actions, &policy.Effect, &conditions, - &policy.Priority, &labels, &annotations, - &policy.Created, &policy.Updated, &policy.CreatedBy, - ); err != nil { - return nil, err - } - - unmarshalJSON(actions, &policy.Actions) - unmarshalJSON(conditions, &policy.Conditions) - unmarshalJSON(labels, &policy.Labels) - unmarshalJSON(annotations, &policy.Annotations) - - policies = append(policies, policy) - } - - return policies, nil -} - -// ============================================================================ -// User Access Token Operations -// ============================================================================ - -func (s *SQLiteStore) CreateUserAccessToken(ctx context.Context, token *store.UserAccessToken) error { - _, err := s.db.ExecContext(ctx, ` - INSERT INTO user_access_tokens ( - id, user_id, name, prefix, key_hash, project_id, scopes, - revoked, expires_at, last_used, created_at - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - `, - token.ID, token.UserID, token.Name, token.Prefix, token.KeyHash, - token.ProjectID, marshalJSON(token.Scopes), - token.Revoked, nullableTimePtr(token.ExpiresAt), nullableTimePtr(token.LastUsed), token.Created, - ) - if err != nil { - if strings.Contains(err.Error(), "UNIQUE constraint failed") { - return store.ErrAlreadyExists - } - if strings.Contains(err.Error(), "FOREIGN KEY constraint failed") { - return store.ErrInvalidInput - } - return err - } - return nil -} - -func (s *SQLiteStore) GetUserAccessToken(ctx context.Context, id string) (*store.UserAccessToken, error) { - token := &store.UserAccessToken{} - var scopes string - var expiresAt, lastUsed sql.NullTime - - err := s.db.QueryRowContext(ctx, ` - SELECT id, user_id, name, prefix, key_hash, project_id, scopes, - revoked, expires_at, last_used, created_at - FROM user_access_tokens WHERE id = ? - `, id).Scan( - &token.ID, &token.UserID, &token.Name, &token.Prefix, &token.KeyHash, - &token.ProjectID, &scopes, - &token.Revoked, &expiresAt, &lastUsed, &token.Created, - ) - if err != nil { - if errors.Is(err, sql.ErrNoRows) { - return nil, store.ErrNotFound - } - return nil, err - } - - unmarshalJSON(scopes, &token.Scopes) - if expiresAt.Valid { - token.ExpiresAt = &expiresAt.Time - } - if lastUsed.Valid { - token.LastUsed = &lastUsed.Time - } - return token, nil -} - -func (s *SQLiteStore) GetUserAccessTokenByHash(ctx context.Context, hash string) (*store.UserAccessToken, error) { - token := &store.UserAccessToken{} - var scopes string - var expiresAt, lastUsed sql.NullTime - - err := s.db.QueryRowContext(ctx, ` - SELECT id, user_id, name, prefix, key_hash, project_id, scopes, - revoked, expires_at, last_used, created_at - FROM user_access_tokens WHERE key_hash = ? - `, hash).Scan( - &token.ID, &token.UserID, &token.Name, &token.Prefix, &token.KeyHash, - &token.ProjectID, &scopes, - &token.Revoked, &expiresAt, &lastUsed, &token.Created, - ) - if err != nil { - if errors.Is(err, sql.ErrNoRows) { - return nil, store.ErrNotFound - } - return nil, err - } - - unmarshalJSON(scopes, &token.Scopes) - if expiresAt.Valid { - token.ExpiresAt = &expiresAt.Time - } - if lastUsed.Valid { - token.LastUsed = &lastUsed.Time - } - return token, nil -} - -func (s *SQLiteStore) UpdateUserAccessTokenLastUsed(ctx context.Context, id string) error { - _, err := s.db.ExecContext(ctx, - "UPDATE user_access_tokens SET last_used = ? WHERE id = ?", - time.Now(), id, - ) - return err -} - -func (s *SQLiteStore) RevokeUserAccessToken(ctx context.Context, id string) error { - result, err := s.db.ExecContext(ctx, - "UPDATE user_access_tokens SET revoked = 1 WHERE id = ?", id, - ) - if err != nil { - return err - } - rowsAffected, err := result.RowsAffected() - if err != nil { - return err - } - if rowsAffected == 0 { - return store.ErrNotFound - } - return nil -} - -func (s *SQLiteStore) DeleteUserAccessToken(ctx context.Context, id string) error { - result, err := s.db.ExecContext(ctx, "DELETE FROM user_access_tokens WHERE id = ?", id) - if err != nil { - return err - } - rowsAffected, err := result.RowsAffected() - if err != nil { - return err - } - if rowsAffected == 0 { - return store.ErrNotFound - } - return nil -} - -func (s *SQLiteStore) ListUserAccessTokens(ctx context.Context, userID string) ([]store.UserAccessToken, error) { - rows, err := s.db.QueryContext(ctx, ` - SELECT id, user_id, name, prefix, project_id, scopes, - revoked, expires_at, last_used, created_at - FROM user_access_tokens WHERE user_id = ? - ORDER BY created_at DESC - `, userID) - if err != nil { - return nil, err - } - defer rows.Close() - - var tokens []store.UserAccessToken - for rows.Next() { - var token store.UserAccessToken - var scopes string - var expiresAt, lastUsed sql.NullTime - - if err := rows.Scan( - &token.ID, &token.UserID, &token.Name, &token.Prefix, - &token.ProjectID, &scopes, - &token.Revoked, &expiresAt, &lastUsed, &token.Created, - ); err != nil { - return nil, err - } - - unmarshalJSON(scopes, &token.Scopes) - if expiresAt.Valid { - token.ExpiresAt = &expiresAt.Time - } - if lastUsed.Valid { - token.LastUsed = &lastUsed.Time - } - tokens = append(tokens, token) - } - return tokens, nil -} - -func (s *SQLiteStore) CountUserAccessTokens(ctx context.Context, userID string) (int, error) { - var count int - err := s.db.QueryRowContext(ctx, - "SELECT COUNT(*) FROM user_access_tokens WHERE user_id = ? AND revoked = 0", - userID, - ).Scan(&count) - return count, err -} - -// nullableTimePtr returns a sql.NullTime for a time pointer. -func nullableTimePtr(t *time.Time) sql.NullTime { - if t == nil { - return sql.NullTime{Valid: false} - } - return sql.NullTime{Time: *t, Valid: true} -} - -// Ensure SQLiteStore implements Store interface -var _ store.Store = (*SQLiteStore)(nil) diff --git a/pkg/store/sqlite/sqlite_test.go b/pkg/store/sqlite/sqlite_test.go deleted file mode 100644 index 3c588e1da..000000000 --- a/pkg/store/sqlite/sqlite_test.go +++ /dev/null @@ -1,2927 +0,0 @@ -// Copyright 2026 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//go:build !no_sqlite - -package sqlite - -import ( - "context" - "database/sql" - "fmt" - "strings" - "testing" - "time" - - "github.com/GoogleCloudPlatform/scion/pkg/agent/state" - "github.com/GoogleCloudPlatform/scion/pkg/api" - "github.com/GoogleCloudPlatform/scion/pkg/store" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func setupTestStore(t *testing.T) *SQLiteStore { - t.Helper() - s, err := New(":memory:") - require.NoError(t, err) - - err = s.Migrate(context.Background()) - require.NoError(t, err) - - t.Cleanup(func() { - s.Close() - }) - - return s -} - -// ============================================================================ -// Agent Tests -// ============================================================================ - -func TestAgentCRUD(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - // First create a project for the agent - project := &store.Project{ - ID: api.NewUUID(), - Name: "Test Project", - Slug: "test-project", - Visibility: store.VisibilityPrivate, - } - require.NoError(t, s.CreateProject(ctx, project)) - - // Create agent - agent := &store.Agent{ - ID: api.NewUUID(), - Slug: "test-agent", - Name: "Test Agent", - Template: "claude", - ProjectID: project.ID, - Phase: string(state.PhaseCreated), - Visibility: store.VisibilityPrivate, - Labels: map[string]string{"env": "test"}, - } - - err := s.CreateAgent(ctx, agent) - require.NoError(t, err) - assert.NotZero(t, agent.Created) - assert.Equal(t, int64(1), agent.StateVersion) - - // Get agent - retrieved, err := s.GetAgent(ctx, agent.ID) - require.NoError(t, err) - assert.Equal(t, agent.ID, retrieved.ID) - assert.Equal(t, agent.Slug, retrieved.Slug) - assert.Equal(t, agent.Name, retrieved.Name) - assert.Equal(t, agent.Template, retrieved.Template) - assert.Equal(t, "test", retrieved.Labels["env"]) - - // Get by slug - retrieved, err = s.GetAgentBySlug(ctx, project.ID, "test-agent") - require.NoError(t, err) - assert.Equal(t, agent.ID, retrieved.ID) - - // Update agent - retrieved.Name = "Updated Agent" - retrieved.Phase = string(state.PhaseRunning) - err = s.UpdateAgent(ctx, retrieved) - require.NoError(t, err) - assert.Equal(t, int64(2), retrieved.StateVersion) - - // Verify update - retrieved, err = s.GetAgent(ctx, agent.ID) - require.NoError(t, err) - assert.Equal(t, "Updated Agent", retrieved.Name) - assert.Equal(t, string(state.PhaseRunning), retrieved.Phase) - - // Test version conflict - oldVersion := retrieved.StateVersion - retrieved.StateVersion = 1 // Use old version - err = s.UpdateAgent(ctx, retrieved) - assert.ErrorIs(t, err, store.ErrVersionConflict) - - // Restore correct version for delete - retrieved.StateVersion = oldVersion - - // Delete agent - err = s.DeleteAgent(ctx, agent.ID) - require.NoError(t, err) - - // Verify deleted - _, err = s.GetAgent(ctx, agent.ID) - assert.ErrorIs(t, err, store.ErrNotFound) -} - -func TestAgentList(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - // Create project - project := &store.Project{ - ID: api.NewUUID(), - Name: "Test Project", - Slug: "test-project", - Visibility: store.VisibilityPrivate, - } - require.NoError(t, s.CreateProject(ctx, project)) - - // Create multiple agents - for i := 0; i < 5; i++ { - agent := &store.Agent{ - ID: api.NewUUID(), - Slug: api.Slugify("agent-" + string(rune('a'+i))), - Name: "Agent " + string(rune('A'+i)), - Template: "claude", - ProjectID: project.ID, - Phase: string(state.PhaseRunning), - Visibility: store.VisibilityPrivate, - } - if i%2 == 0 { - agent.Phase = string(state.PhaseStopped) - } - require.NoError(t, s.CreateAgent(ctx, agent)) - } - - // List all - result, err := s.ListAgents(ctx, store.AgentFilter{}, store.ListOptions{}) - require.NoError(t, err) - assert.Equal(t, 5, result.TotalCount) - assert.Len(t, result.Items, 5) - - // List by status - result, err = s.ListAgents(ctx, store.AgentFilter{Phase: string(state.PhaseRunning)}, store.ListOptions{}) - require.NoError(t, err) - assert.Equal(t, 2, result.TotalCount) - - // List by project - result, err = s.ListAgents(ctx, store.AgentFilter{ProjectID: project.ID}, store.ListOptions{}) - require.NoError(t, err) - assert.Equal(t, 5, result.TotalCount) - - // Test pagination - result, err = s.ListAgents(ctx, store.AgentFilter{}, store.ListOptions{Limit: 2}) - require.NoError(t, err) - assert.Len(t, result.Items, 2) -} - -func TestAgentAncestry(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - project := &store.Project{ - ID: api.NewUUID(), Name: "Ancestry Project", Slug: "ancestry-project", - Visibility: store.VisibilityPrivate, - } - require.NoError(t, s.CreateProject(ctx, project)) - - userID := "user-root-123" - - // Agent A: created by user (ancestry = [userID]) - agentA := &store.Agent{ - ID: api.NewUUID(), Slug: "agent-a", Name: "Agent A", - Template: "claude", ProjectID: project.ID, - Phase: string(state.PhaseRunning), Visibility: store.VisibilityPrivate, - CreatedBy: userID, OwnerID: userID, - Ancestry: []string{userID}, - } - require.NoError(t, s.CreateAgent(ctx, agentA)) - - // Agent B: created by Agent A (ancestry = [userID, agentA.ID]) - agentB := &store.Agent{ - ID: api.NewUUID(), Slug: "agent-b", Name: "Agent B", - Template: "claude", ProjectID: project.ID, - Phase: string(state.PhaseRunning), Visibility: store.VisibilityPrivate, - CreatedBy: agentA.ID, OwnerID: agentA.ID, - Ancestry: []string{userID, agentA.ID}, - } - require.NoError(t, s.CreateAgent(ctx, agentB)) - - // Agent C: created by Agent B (ancestry = [userID, agentA.ID, agentB.ID]) - agentC := &store.Agent{ - ID: api.NewUUID(), Slug: "agent-c", Name: "Agent C", - Template: "claude", ProjectID: project.ID, - Phase: string(state.PhaseRunning), Visibility: store.VisibilityPrivate, - CreatedBy: agentB.ID, OwnerID: agentB.ID, - Ancestry: []string{userID, agentA.ID, agentB.ID}, - } - require.NoError(t, s.CreateAgent(ctx, agentC)) - - // Verify ancestry is persisted and retrieved correctly - t.Run("GetAgent preserves ancestry", func(t *testing.T) { - retrieved, err := s.GetAgent(ctx, agentC.ID) - require.NoError(t, err) - assert.Equal(t, []string{userID, agentA.ID, agentB.ID}, retrieved.Ancestry) - }) - - t.Run("GetAgentBySlug preserves ancestry", func(t *testing.T) { - retrieved, err := s.GetAgentBySlug(ctx, project.ID, "agent-b") - require.NoError(t, err) - assert.Equal(t, []string{userID, agentA.ID}, retrieved.Ancestry) - }) - - t.Run("ListAgents preserves ancestry", func(t *testing.T) { - result, err := s.ListAgents(ctx, store.AgentFilter{ProjectID: project.ID}, store.ListOptions{}) - require.NoError(t, err) - assert.Len(t, result.Items, 3) - for _, agent := range result.Items { - assert.NotEmpty(t, agent.Ancestry, "agent %s should have ancestry", agent.Slug) - } - }) - - t.Run("AncestorID filter - user sees all descendants", func(t *testing.T) { - result, err := s.ListAgents(ctx, store.AgentFilter{AncestorID: userID}, store.ListOptions{}) - require.NoError(t, err) - assert.Equal(t, 3, result.TotalCount) - }) - - t.Run("AncestorID filter - agentA sees B and C", func(t *testing.T) { - result, err := s.ListAgents(ctx, store.AgentFilter{AncestorID: agentA.ID}, store.ListOptions{}) - require.NoError(t, err) - assert.Equal(t, 2, result.TotalCount) - }) - - t.Run("AncestorID filter - agentB sees only C", func(t *testing.T) { - result, err := s.ListAgents(ctx, store.AgentFilter{AncestorID: agentB.ID}, store.ListOptions{}) - require.NoError(t, err) - assert.Equal(t, 1, result.TotalCount) - assert.Equal(t, agentC.ID, result.Items[0].ID) - }) - - t.Run("AncestorID filter - agentC sees none", func(t *testing.T) { - result, err := s.ListAgents(ctx, store.AgentFilter{AncestorID: agentC.ID}, store.ListOptions{}) - require.NoError(t, err) - assert.Equal(t, 0, result.TotalCount) - }) - - t.Run("AncestorID filter - unknown user sees none", func(t *testing.T) { - result, err := s.ListAgents(ctx, store.AgentFilter{AncestorID: "unknown-user"}, store.ListOptions{}) - require.NoError(t, err) - assert.Equal(t, 0, result.TotalCount) - }) - - t.Run("nil ancestry persists as empty", func(t *testing.T) { - agentNoAnc := &store.Agent{ - ID: api.NewUUID(), Slug: "agent-no-anc", Name: "No Ancestry", - Template: "claude", ProjectID: project.ID, - Phase: string(state.PhaseRunning), Visibility: store.VisibilityPrivate, - } - require.NoError(t, s.CreateAgent(ctx, agentNoAnc)) - retrieved, err := s.GetAgent(ctx, agentNoAnc.ID) - require.NoError(t, err) - assert.Nil(t, retrieved.Ancestry) - }) - - t.Run("NULL ancestry column does not crash scan", func(t *testing.T) { - // Create agent normally, then set ancestry to NULL to simulate pre-migration state - agentNullAnc := &store.Agent{ - ID: api.NewUUID(), Slug: "agent-null-anc", Name: "Null Ancestry", - Template: "claude", ProjectID: project.ID, - Phase: string(state.PhaseRunning), Visibility: store.VisibilityPrivate, - Ancestry: []string{"some-user"}, - } - require.NoError(t, s.CreateAgent(ctx, agentNullAnc)) - _, err := s.db.ExecContext(ctx, `UPDATE agents SET ancestry = NULL WHERE id = ?`, agentNullAnc.ID) - require.NoError(t, err) - agentID := agentNullAnc.ID - - retrieved, err := s.GetAgent(ctx, agentID) - require.NoError(t, err) - assert.Nil(t, retrieved.Ancestry) - - retrievedBySlug, err := s.GetAgentBySlug(ctx, project.ID, "agent-null-anc") - require.NoError(t, err) - assert.Nil(t, retrievedBySlug.Ancestry) - - result, err := s.ListAgents(ctx, store.AgentFilter{ProjectID: project.ID}, store.ListOptions{}) - require.NoError(t, err) - assert.True(t, result.TotalCount > 0) - }) -} - -func TestAgentStatusUpdate(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - // Create project and agent - project := &store.Project{ - ID: api.NewUUID(), - Name: "Test Project", - Slug: "test-project", - Visibility: store.VisibilityPrivate, - } - require.NoError(t, s.CreateProject(ctx, project)) - - agent := &store.Agent{ - ID: api.NewUUID(), - Slug: "test-agent", - Name: "Test Agent", - Template: "claude", - ProjectID: project.ID, - Phase: string(state.PhaseCreated), - Visibility: store.VisibilityPrivate, - } - require.NoError(t, s.CreateAgent(ctx, agent)) - - // Legacy path: update flat status only (backward compat) - err := s.UpdateAgentStatus(ctx, agent.ID, store.AgentStatusUpdate{ - Phase: string(state.PhaseRunning), - ContainerStatus: "Up 5 minutes", - }) - require.NoError(t, err) - - retrieved, err := s.GetAgent(ctx, agent.ID) - require.NoError(t, err) - assert.Equal(t, string(state.PhaseRunning), retrieved.Phase) - assert.Equal(t, "Up 5 minutes", retrieved.ContainerStatus) - - // Structured path: set phase + activity - err = s.UpdateAgentStatus(ctx, agent.ID, store.AgentStatusUpdate{ - Phase: "running", - Activity: "thinking", - }) - require.NoError(t, err) - - retrieved, err = s.GetAgent(ctx, agent.ID) - require.NoError(t, err) - assert.Equal(t, "running", retrieved.Phase) - assert.Equal(t, "thinking", retrieved.Activity) - - // Set activity=executing with toolName - err = s.UpdateAgentStatus(ctx, agent.ID, store.AgentStatusUpdate{ - Phase: "running", - Activity: "executing", - ToolName: "Bash", - }) - require.NoError(t, err) - - retrieved, err = s.GetAgent(ctx, agent.ID) - require.NoError(t, err) - assert.Equal(t, "executing", retrieved.Activity) - assert.Equal(t, "Bash", retrieved.ToolName) - - // Change activity from executing to working → toolName is cleared - err = s.UpdateAgentStatus(ctx, agent.ID, store.AgentStatusUpdate{ - Phase: "running", - Activity: "working", - }) - require.NoError(t, err) - - retrieved, err = s.GetAgent(ctx, agent.ID) - require.NoError(t, err) - assert.Equal(t, "working", retrieved.Activity) - assert.Equal(t, "", retrieved.ToolName, "toolName should be cleared when activity changes away from executing") - - // Set only activity (phase preserved from previous update) - err = s.UpdateAgentStatus(ctx, agent.ID, store.AgentStatusUpdate{ - Activity: "waiting_for_input", - }) - require.NoError(t, err) - - retrieved, err = s.GetAgent(ctx, agent.ID) - require.NoError(t, err) - assert.Equal(t, "running", retrieved.Phase, "phase should be preserved") - assert.Equal(t, "waiting_for_input", retrieved.Activity) - - // Non-running phase - err = s.UpdateAgentStatus(ctx, agent.ID, store.AgentStatusUpdate{ - Phase: "stopped", - }) - require.NoError(t, err) - - retrieved, err = s.GetAgent(ctx, agent.ID) - require.NoError(t, err) - assert.Equal(t, "stopped", retrieved.Phase) -} - -func TestAgentStatusUpdate_PhaseActivityRoundTrip(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - project := &store.Project{ - ID: api.NewUUID(), - Name: "Test Project", - Slug: "test-project-rt", - Visibility: store.VisibilityPrivate, - } - require.NoError(t, s.CreateProject(ctx, project)) - - // Create agent with initial phase/activity - agent := &store.Agent{ - ID: api.NewUUID(), - Slug: "roundtrip-agent", - Name: "Roundtrip Agent", - Template: "claude", - ProjectID: project.ID, - Phase: "running", - Activity: "working", - Visibility: store.VisibilityPrivate, - } - require.NoError(t, s.CreateAgent(ctx, agent)) - - // Verify round-trip through Get - retrieved, err := s.GetAgent(ctx, agent.ID) - require.NoError(t, err) - assert.Equal(t, "running", retrieved.Phase) - assert.Equal(t, "working", retrieved.Activity) - - // Verify round-trip through GetBySlug - retrieved, err = s.GetAgentBySlug(ctx, project.ID, "roundtrip-agent") - require.NoError(t, err) - assert.Equal(t, "running", retrieved.Phase) - assert.Equal(t, "working", retrieved.Activity) - - // Verify round-trip through List - result, err := s.ListAgents(ctx, store.AgentFilter{ProjectID: project.ID}, store.ListOptions{}) - require.NoError(t, err) - require.Len(t, result.Items, 1) - assert.Equal(t, "running", result.Items[0].Phase) - assert.Equal(t, "working", result.Items[0].Activity) -} - -func TestSoftDeleteFilterExclusion(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - // Create project - project := &store.Project{ - ID: api.NewUUID(), - Name: "Test Project", - Slug: "test-project-sd", - Visibility: store.VisibilityPrivate, - } - require.NoError(t, s.CreateProject(ctx, project)) - - // Create 3 agents: 2 running, 1 soft-deleted - for i := 0; i < 3; i++ { - agent := &store.Agent{ - ID: api.NewUUID(), - Slug: api.Slugify("sd-agent-" + string(rune('a'+i))), - Name: "SD Agent " + string(rune('A'+i)), - Template: "claude", - ProjectID: project.ID, - Phase: string(state.PhaseRunning), - Visibility: store.VisibilityPrivate, - } - if i == 2 { - agent.DeletedAt = time.Now() - } - require.NoError(t, s.CreateAgent(ctx, agent)) - } - - // List without IncludeDeleted: should see 2 - result, err := s.ListAgents(ctx, store.AgentFilter{}, store.ListOptions{}) - require.NoError(t, err) - assert.Equal(t, 2, result.TotalCount) - assert.Len(t, result.Items, 2) - for _, a := range result.Items { - assert.True(t, a.DeletedAt.IsZero(), "non-deleted agent should have zero DeletedAt") - } - - // List with IncludeDeleted: should see 3 - result, err = s.ListAgents(ctx, store.AgentFilter{IncludeDeleted: true}, store.ListOptions{}) - require.NoError(t, err) - assert.Equal(t, 3, result.TotalCount) - assert.Len(t, result.Items, 3) - - // List with IncludeDeleted: should see all 3 (including the deleted one) - // Verify we can find the soft-deleted agent - var deletedCount int - for _, a := range result.Items { - if !a.DeletedAt.IsZero() { - deletedCount++ - } - } - assert.Equal(t, 1, deletedCount, "should have exactly one soft-deleted agent") -} - -func TestPurgeDeletedAgents(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - project := &store.Project{ - ID: api.NewUUID(), - Name: "Test Project", - Slug: "test-project-purge", - Visibility: store.VisibilityPrivate, - } - require.NoError(t, s.CreateProject(ctx, project)) - - now := time.Now() - - // Create 2 deleted agents: one expired (old), one recent - oldAgent := &store.Agent{ - ID: api.NewUUID(), - Slug: "old-deleted", - Name: "Old Deleted", - Template: "claude", - ProjectID: project.ID, - Phase: string(state.PhaseStopped), - DeletedAt: now.Add(-48 * time.Hour), - Visibility: store.VisibilityPrivate, - } - recentAgent := &store.Agent{ - ID: api.NewUUID(), - Slug: "recent-deleted", - Name: "Recent Deleted", - Template: "claude", - ProjectID: project.ID, - Phase: string(state.PhaseStopped), - DeletedAt: now.Add(-1 * time.Hour), - Visibility: store.VisibilityPrivate, - } - activeAgent := &store.Agent{ - ID: api.NewUUID(), - Slug: "active-agent", - Name: "Active Agent", - Template: "claude", - ProjectID: project.ID, - Phase: string(state.PhaseRunning), - Visibility: store.VisibilityPrivate, - } - require.NoError(t, s.CreateAgent(ctx, oldAgent)) - require.NoError(t, s.CreateAgent(ctx, recentAgent)) - require.NoError(t, s.CreateAgent(ctx, activeAgent)) - - // Purge with cutoff of 24h ago: should only purge the old one - cutoff := now.Add(-24 * time.Hour) - purged, err := s.PurgeDeletedAgents(ctx, cutoff) - require.NoError(t, err) - assert.Equal(t, 1, purged) - - // Old agent should be gone - _, err = s.GetAgent(ctx, oldAgent.ID) - assert.ErrorIs(t, err, store.ErrNotFound) - - // Recent deleted agent should still exist - _, err = s.GetAgent(ctx, recentAgent.ID) - require.NoError(t, err) - - // Active agent should still exist - _, err = s.GetAgent(ctx, activeAgent.ID) - require.NoError(t, err) -} - -func TestDeletedAtPersistence(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - project := &store.Project{ - ID: api.NewUUID(), - Name: "Test Project", - Slug: "test-project-dat", - Visibility: store.VisibilityPrivate, - } - require.NoError(t, s.CreateProject(ctx, project)) - - // Create and soft-delete an agent - agent := &store.Agent{ - ID: api.NewUUID(), - Slug: "soft-del-test", - Name: "Soft Delete Test", - Template: "claude", - ProjectID: project.ID, - Phase: string(state.PhaseRunning), - Visibility: store.VisibilityPrivate, - } - require.NoError(t, s.CreateAgent(ctx, agent)) - - // Verify DeletedAt is zero initially - retrieved, err := s.GetAgent(ctx, agent.ID) - require.NoError(t, err) - assert.True(t, retrieved.DeletedAt.IsZero()) - - // Soft-delete - deletedAt := time.Now().Truncate(time.Second) - retrieved.DeletedAt = deletedAt - retrieved.Updated = time.Now() - require.NoError(t, s.UpdateAgent(ctx, retrieved)) - - // Retrieve and verify DeletedAt is set - retrieved2, err := s.GetAgent(ctx, agent.ID) - require.NoError(t, err) - assert.False(t, retrieved2.DeletedAt.IsZero(), "soft-deleted agent should have non-zero DeletedAt") - assert.WithinDuration(t, deletedAt, retrieved2.DeletedAt, time.Second) - - // Verify GetAgentBySlug also returns DeletedAt - bySlug, err := s.GetAgentBySlug(ctx, project.ID, "soft-del-test") - require.NoError(t, err) - assert.False(t, bySlug.DeletedAt.IsZero(), "soft-deleted agent fetched by slug should have non-zero DeletedAt") - - // Verify restore clears DeletedAt - bySlug.DeletedAt = time.Time{} - bySlug.Updated = time.Now() - require.NoError(t, s.UpdateAgent(ctx, bySlug)) - - restored, err := s.GetAgent(ctx, agent.ID) - require.NoError(t, err) - assert.True(t, restored.DeletedAt.IsZero(), "restored agent should have zero DeletedAt") -} - -// ============================================================================ -// Project Tests -// ============================================================================ - -func TestProjectCRUD(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - // Create project - project := &store.Project{ - ID: api.NewUUID(), - Name: "My Project", - Slug: "my-project", - GitRemote: "github.com/org/repo", - Visibility: store.VisibilityPrivate, - Labels: map[string]string{"team": "platform"}, - } - - err := s.CreateProject(ctx, project) - require.NoError(t, err) - assert.NotZero(t, project.Created) - - // Get project - retrieved, err := s.GetProject(ctx, project.ID) - require.NoError(t, err) - assert.Equal(t, project.Name, retrieved.Name) - assert.Equal(t, project.GitRemote, retrieved.GitRemote) - assert.Equal(t, "platform", retrieved.Labels["team"]) - - // Get by slug - retrieved, err = s.GetProjectBySlug(ctx, "my-project") - require.NoError(t, err) - assert.Equal(t, project.ID, retrieved.ID) - - // Get by git remote (plural) - projects, err := s.GetProjectsByGitRemote(ctx, "github.com/org/repo") - require.NoError(t, err) - require.Len(t, projects, 1) - assert.Equal(t, project.ID, projects[0].ID) - - // Duplicate git remotes are now allowed (slug must still be unique) - duplicate := &store.Project{ - ID: api.NewUUID(), - Name: "Duplicate", - Slug: "duplicate", - GitRemote: "github.com/org/repo", - Visibility: store.VisibilityPrivate, - } - err = s.CreateProject(ctx, duplicate) - require.NoError(t, err) - - // Verify both projects are returned - projects, err = s.GetProjectsByGitRemote(ctx, "github.com/org/repo") - require.NoError(t, err) - assert.Len(t, projects, 2) - - // Update project - retrieved.Name = "Updated Project" - err = s.UpdateProject(ctx, retrieved) - require.NoError(t, err) - - // Verify update - retrieved, err = s.GetProject(ctx, project.ID) - require.NoError(t, err) - assert.Equal(t, "Updated Project", retrieved.Name) - - // Delete project - err = s.DeleteProject(ctx, project.ID) - require.NoError(t, err) - - // Verify deleted - _, err = s.GetProject(ctx, project.ID) - assert.ErrorIs(t, err, store.ErrNotFound) -} - -func TestMultiProjectPerGitRemote(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - remote := "github.com/acme/widgets" - - // Create 3 projects with the same git remote but different slugs - slugs := []string{"acme-widgets", "acme-widgets-1", "acme-widgets-2"} - for i, slug := range slugs { - project := &store.Project{ - ID: api.NewUUID(), - Name: fmt.Sprintf("acme-widgets project %d", i), - Slug: slug, - GitRemote: remote, - Visibility: store.VisibilityPrivate, - } - require.NoError(t, s.CreateProject(ctx, project)) - } - - projects, err := s.GetProjectsByGitRemote(ctx, remote) - require.NoError(t, err) - assert.Len(t, projects, 3) - - // Verify ordering is by created_at ASC - assert.Equal(t, "acme-widgets", projects[0].Slug) - assert.Equal(t, "acme-widgets-1", projects[1].Slug) - assert.Equal(t, "acme-widgets-2", projects[2].Slug) -} - -func TestGetProjectsByGitRemoteEmpty(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - projects, err := s.GetProjectsByGitRemote(ctx, "github.com/nonexistent/repo") - require.NoError(t, err) - assert.Empty(t, projects) -} - -func TestSlugUniqueness(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - project1 := &store.Project{ - ID: api.NewUUID(), Name: "Test", Slug: "test-project", - Visibility: store.VisibilityPrivate, - } - require.NoError(t, s.CreateProject(ctx, project1)) - - // Duplicate slug should fail - project2 := &store.Project{ - ID: api.NewUUID(), Name: "Test 2", Slug: "test-project", - Visibility: store.VisibilityPrivate, - } - err := s.CreateProject(ctx, project2) - assert.ErrorIs(t, err, store.ErrAlreadyExists) -} - -func TestNextAvailableSlug(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - // Base slug available - slug, err := s.NextAvailableSlug(ctx, "acme-widgets") - require.NoError(t, err) - assert.Equal(t, "acme-widgets", slug) - - // Create the base slug - require.NoError(t, s.CreateProject(ctx, &store.Project{ - ID: api.NewUUID(), Name: "acme-widgets", Slug: "acme-widgets", - Visibility: store.VisibilityPrivate, - })) - - // Should get -1 - slug, err = s.NextAvailableSlug(ctx, "acme-widgets") - require.NoError(t, err) - assert.Equal(t, "acme-widgets-1", slug) - - // Create -1 - require.NoError(t, s.CreateProject(ctx, &store.Project{ - ID: api.NewUUID(), Name: "acme-widgets (1)", Slug: "acme-widgets-1", - Visibility: store.VisibilityPrivate, - })) - - // Should get -2 - slug, err = s.NextAvailableSlug(ctx, "acme-widgets") - require.NoError(t, err) - assert.Equal(t, "acme-widgets-2", slug) -} - -func TestGetInstallationForRepository(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - // Create an installation with repos - inst := &store.GitHubInstallation{ - InstallationID: 12345, - AccountLogin: "acme", - AccountType: "Organization", - AppID: 100, - Repositories: []string{"acme/widgets", "acme/gizmos"}, - Status: store.GitHubInstallationStatusActive, - } - require.NoError(t, s.CreateGitHubInstallation(ctx, inst)) - - // Look up by repo - found, err := s.GetInstallationForRepository(ctx, "acme/widgets") - require.NoError(t, err) - assert.Equal(t, int64(12345), found.InstallationID) - assert.Contains(t, found.Repositories, "acme/widgets") - - // Look up non-existent repo - _, err = s.GetInstallationForRepository(ctx, "acme/nonexistent") - assert.ErrorIs(t, err, store.ErrNotFound) - - // Suspended installation should not match - inst2 := &store.GitHubInstallation{ - InstallationID: 67890, - AccountLogin: "other", - AccountType: "User", - AppID: 100, - Repositories: []string{"other/project"}, - Status: store.GitHubInstallationStatusSuspended, - } - require.NoError(t, s.CreateGitHubInstallation(ctx, inst2)) - - _, err = s.GetInstallationForRepository(ctx, "other/project") - assert.ErrorIs(t, err, store.ErrNotFound) -} - -func TestDisplayNameWithSerial(t *testing.T) { - assert.Equal(t, "acme-widgets", api.DisplayNameWithSerial("acme-widgets", "acme-widgets", "acme-widgets")) - assert.Equal(t, "acme-widgets (1)", api.DisplayNameWithSerial("acme-widgets", "acme-widgets-1", "acme-widgets")) - assert.Equal(t, "acme-widgets (2)", api.DisplayNameWithSerial("acme-widgets", "acme-widgets-2", "acme-widgets")) - assert.Equal(t, "My Project (3)", api.DisplayNameWithSerial("My Project", "my-project-3", "my-project")) -} - -func TestProjectList(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - // Create a broker for ActiveBrokerCount - broker := &store.RuntimeBroker{ - ID: api.NewUUID(), - Name: "Test Broker", - Slug: "test-broker", - Status: store.BrokerStatusOnline, - } - require.NoError(t, s.CreateRuntimeBroker(ctx, broker)) - - // Create projects - for i := 0; i < 3; i++ { - project := &store.Project{ - ID: api.NewUUID(), - Name: "Project " + string(rune('A'+i)), - Slug: "project-" + string(rune('a'+i)), - Visibility: store.VisibilityPrivate, - } - if i == 0 { - project.Visibility = store.VisibilityPublic - } - require.NoError(t, s.CreateProject(ctx, project)) - - // Add an agent to the first project - if i == 0 { - agent := &store.Agent{ - ID: api.NewUUID(), - Slug: "test-agent", - Name: "Test Agent", - ProjectID: project.ID, - Phase: string(state.PhaseRunning), - } - require.NoError(t, s.CreateAgent(ctx, agent)) - - // Link the broker to the first project - require.NoError(t, s.AddProjectProvider(ctx, &store.ProjectProvider{ - ProjectID: project.ID, - BrokerID: broker.ID, - BrokerName: broker.Name, - Status: store.BrokerStatusOnline, - })) - } - } - - // List all - result, err := s.ListProjects(ctx, store.ProjectFilter{}, store.ListOptions{}) - require.NoError(t, err) - assert.Equal(t, 3, result.TotalCount) - - // Verify computed fields on the first project (index 2 due to DESC sort by created_at) - var firstProject store.Project - for _, g := range result.Items { - if g.Name == "Project A" { - firstProject = g - break - } - } - assert.Equal(t, 1, firstProject.AgentCount) - assert.Equal(t, 1, firstProject.ActiveBrokerCount) - - // List by visibility - result, err = s.ListProjects(ctx, store.ProjectFilter{Visibility: store.VisibilityPublic}, store.ListOptions{}) - require.NoError(t, err) - assert.Equal(t, 1, result.TotalCount) - assert.Equal(t, "Project A", result.Items[0].Name) -} - -// ============================================================================ -// RuntimeBroker Tests -// ============================================================================ - -func TestProjectLookupCaseInsensitive(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - // Create a project with mixed case name - project := &store.Project{ - ID: api.NewUUID(), - Name: "Global", - Slug: "global", - Visibility: store.VisibilityPrivate, - } - require.NoError(t, s.CreateProject(ctx, project)) - - // Look up with exact case - should work - retrieved, err := s.GetProjectBySlugCaseInsensitive(ctx, "global") - require.NoError(t, err) - assert.Equal(t, project.ID, retrieved.ID) - - // Look up with different case - should still work - retrieved, err = s.GetProjectBySlugCaseInsensitive(ctx, "GLOBAL") - require.NoError(t, err) - assert.Equal(t, project.ID, retrieved.ID) - - // Look up with mixed case - should still work - retrieved, err = s.GetProjectBySlugCaseInsensitive(ctx, "Global") - require.NoError(t, err) - assert.Equal(t, project.ID, retrieved.ID) - - // Look up non-existent - should return ErrNotFound - _, err = s.GetProjectBySlugCaseInsensitive(ctx, "nonexistent") - assert.ErrorIs(t, err, store.ErrNotFound) -} - -func TestProjectListBySlug(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - // Create two projects with distinct slugs - project1 := &store.Project{ - ID: api.NewUUID(), - Name: "Alpha Project", - Slug: "alpha-project", - Visibility: store.VisibilityPrivate, - } - project2 := &store.Project{ - ID: api.NewUUID(), - Name: "Beta Project", - Slug: "beta-project", - Visibility: store.VisibilityPrivate, - } - require.NoError(t, s.CreateProject(ctx, project1)) - require.NoError(t, s.CreateProject(ctx, project2)) - - // Filter by slug — exact match - result, err := s.ListProjects(ctx, store.ProjectFilter{Slug: "alpha-project"}, store.ListOptions{}) - require.NoError(t, err) - assert.Equal(t, 1, result.TotalCount) - assert.Equal(t, project1.ID, result.Items[0].ID) - - // Filter by slug — case-insensitive - result, err = s.ListProjects(ctx, store.ProjectFilter{Slug: "ALPHA-PROJECT"}, store.ListOptions{}) - require.NoError(t, err) - assert.Equal(t, 1, result.TotalCount) - assert.Equal(t, project1.ID, result.Items[0].ID) - - // Filter by slug — no match - result, err = s.ListProjects(ctx, store.ProjectFilter{Slug: "nonexistent"}, store.ListOptions{}) - require.NoError(t, err) - assert.Equal(t, 0, result.TotalCount) -} - -func TestListProjectsByGitRemoteExactMatch(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - project1 := &store.Project{ - ID: api.NewUUID(), - Name: "Repo", - Slug: "repo", - GitRemote: "github.com/org/repo", - Visibility: store.VisibilityPrivate, - } - project2 := &store.Project{ - ID: api.NewUUID(), - Name: "Repo Clone", - Slug: "repo-clone", - GitRemote: "github.com/org/repo-clone", - Visibility: store.VisibilityPrivate, - } - require.NoError(t, s.CreateProject(ctx, project1)) - require.NoError(t, s.CreateProject(ctx, project2)) - - // Exact match should return only the exact project, not the one with repo-clone - result, err := s.ListProjects(ctx, store.ProjectFilter{GitRemote: "github.com/org/repo"}, store.ListOptions{}) - require.NoError(t, err) - assert.Equal(t, 1, result.TotalCount) - assert.Equal(t, project1.ID, result.Items[0].ID) - - // Exact match on the clone URL should return only that project - result, err = s.ListProjects(ctx, store.ProjectFilter{GitRemote: "github.com/org/repo-clone"}, store.ListOptions{}) - require.NoError(t, err) - assert.Equal(t, 1, result.TotalCount) - assert.Equal(t, project2.ID, result.Items[0].ID) -} - -func TestListProjectsSharedScope(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - ownerID := api.NewUUID() - otherOwnerID := api.NewUUID() - - ownedProject := &store.Project{ - ID: api.NewUUID(), - Name: "Owned Project", - Slug: "owned-project", - OwnerID: ownerID, - Visibility: store.VisibilityPrivate, - } - sharedProject := &store.Project{ - ID: api.NewUUID(), - Name: "Shared Project", - Slug: "shared-project", - OwnerID: otherOwnerID, - Visibility: store.VisibilityPrivate, - } - unrelatedProject := &store.Project{ - ID: api.NewUUID(), - Name: "Unrelated Project", - Slug: "unrelated-project", - OwnerID: otherOwnerID, - Visibility: store.VisibilityPrivate, - } - require.NoError(t, s.CreateProject(ctx, ownedProject)) - require.NoError(t, s.CreateProject(ctx, sharedProject)) - require.NoError(t, s.CreateProject(ctx, unrelatedProject)) - - // scope=mine: only projects owned by the user - result, err := s.ListProjects(ctx, store.ProjectFilter{OwnerID: ownerID}, store.ListOptions{}) - require.NoError(t, err) - assert.Equal(t, 1, result.TotalCount) - assert.Equal(t, ownedProject.ID, result.Items[0].ID) - - // scope=shared: MemberProjectIDs includes both owned and shared project IDs, - // but ExcludeOwnerID removes the owned one - result, err = s.ListProjects(ctx, store.ProjectFilter{ - MemberProjectIDs: []string{ownedProject.ID, sharedProject.ID}, - ExcludeOwnerID: ownerID, - }, store.ListOptions{}) - require.NoError(t, err) - assert.Equal(t, 1, result.TotalCount) - assert.Equal(t, sharedProject.ID, result.Items[0].ID) - - // MemberProjectIDs without ExcludeOwnerID returns all matched projects - result, err = s.ListProjects(ctx, store.ProjectFilter{ - MemberProjectIDs: []string{ownedProject.ID, sharedProject.ID}, - }, store.ListOptions{}) - require.NoError(t, err) - assert.Equal(t, 2, result.TotalCount) - - // Empty MemberProjectIDs with ExcludeOwnerID is a no-op on membership filter - result, err = s.ListProjects(ctx, store.ProjectFilter{ - ExcludeOwnerID: ownerID, - }, store.ListOptions{}) - require.NoError(t, err) - // Returns all projects not owned by ownerID - assert.Equal(t, 2, result.TotalCount) -} - -func TestListProjectsSharedScopeTransitiveGroup(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - userID := "user_transitive" - otherOwnerID := api.NewUUID() - - // Create a project owned by someone else - sharedProject := &store.Project{ - ID: api.NewUUID(), - Name: "Transitively Shared Project", - Slug: "trans-shared-project", - OwnerID: otherOwnerID, - Visibility: store.VisibilityPrivate, - } - require.NoError(t, s.CreateProject(ctx, sharedProject)) - - // Create a project_agents group linked to the project (simulates the project - // membership group that is created when a project gains members). - projectGroup := &store.Group{ - ID: api.NewUUID(), - Name: "Project Agents", - Slug: "project-agents-trans", - GroupType: "project_agents", - ProjectID: sharedProject.ID, - Created: time.Now(), - Updated: time.Now(), - } - // Create an intermediate parent group that is a member of the project group - parentGroup := &store.Group{ - ID: api.NewUUID(), - Name: "Team Alpha", - Slug: "team-alpha", - Created: time.Now(), - Updated: time.Now(), - } - // Create the child group the user is a direct member of - childGroup := &store.Group{ - ID: api.NewUUID(), - Name: "Sub-Team", - Slug: "sub-team", - Created: time.Now(), - Updated: time.Now(), - } - - for _, g := range []*store.Group{projectGroup, parentGroup, childGroup} { - require.NoError(t, s.CreateGroup(ctx, g)) - } - - // parentGroup is a member of projectGroup (admin access to the project) - require.NoError(t, s.AddGroupMember(ctx, &store.GroupMember{ - GroupID: projectGroup.ID, - MemberType: "group", - MemberID: parentGroup.ID, - Role: "admin", - AddedAt: time.Now(), - })) - - // childGroup is a member of parentGroup - require.NoError(t, s.AddGroupMember(ctx, &store.GroupMember{ - GroupID: parentGroup.ID, - MemberType: "group", - MemberID: childGroup.ID, - Role: "member", - AddedAt: time.Now(), - })) - - // User is a direct member of childGroup only - require.NoError(t, s.AddGroupMember(ctx, &store.GroupMember{ - GroupID: childGroup.ID, - MemberType: "user", - MemberID: userID, - Role: "member", - AddedAt: time.Now(), - })) - - // GetEffectiveGroups should return all three groups (child, parent, project) - effectiveGroupIDs, err := s.GetEffectiveGroups(ctx, userID) - require.NoError(t, err) - assert.Len(t, effectiveGroupIDs, 3) - - // Resolve project IDs from effective groups (mirrors resolveUserProjectIDs) - groups, err := s.GetGroupsByIDs(ctx, effectiveGroupIDs) - require.NoError(t, err) - - var projectIDs []string - for _, g := range groups { - if g.ProjectID != "" { - projectIDs = append(projectIDs, g.ProjectID) - } - } - require.Len(t, projectIDs, 1, "should find project via transitive group membership") - assert.Equal(t, sharedProject.ID, projectIDs[0]) - - // Using the resolved project IDs in a shared scope filter should return the project - result, err := s.ListProjects(ctx, store.ProjectFilter{ - MemberProjectIDs: projectIDs, - ExcludeOwnerID: userID, - }, store.ListOptions{}) - require.NoError(t, err) - assert.Equal(t, 1, result.TotalCount) - assert.Equal(t, sharedProject.ID, result.Items[0].ID) -} - -func TestRuntimeBrokerLookupByName(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - // Create a broker - broker := &store.RuntimeBroker{ - ID: api.NewUUID(), - Name: "My-Laptop", - Slug: "my-laptop", - Status: store.BrokerStatusOnline, - } - require.NoError(t, s.CreateRuntimeBroker(ctx, broker)) - - // Look up with exact case - should work - retrieved, err := s.GetRuntimeBrokerByName(ctx, "My-Laptop") - require.NoError(t, err) - assert.Equal(t, broker.ID, retrieved.ID) - - // Look up with different case - should still work (case-insensitive) - retrieved, err = s.GetRuntimeBrokerByName(ctx, "my-laptop") - require.NoError(t, err) - assert.Equal(t, broker.ID, retrieved.ID) - - // Look up with all caps - should still work - retrieved, err = s.GetRuntimeBrokerByName(ctx, "MY-LAPTOP") - require.NoError(t, err) - assert.Equal(t, broker.ID, retrieved.ID) - - // Look up non-existent - should return ErrNotFound - _, err = s.GetRuntimeBrokerByName(ctx, "nonexistent") - assert.ErrorIs(t, err, store.ErrNotFound) -} - -// ============================================================================ -// RuntimeBroker Tests -// ============================================================================ - -func TestRuntimeBrokerCRUD(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - // Create broker with CreatedBy tracking - broker := &store.RuntimeBroker{ - ID: api.NewUUID(), - Name: "Dev Laptop", - Slug: "dev-laptop", - Version: "1.0.0", - Status: store.BrokerStatusOnline, - Capabilities: &store.BrokerCapabilities{ - WebPTY: true, - Sync: true, - Attach: true, - }, - Profiles: []store.BrokerProfile{ - {Name: "default", Type: "docker", Available: true}, - }, - CreatedBy: "admin-user-456", - } - - err := s.CreateRuntimeBroker(ctx, broker) - require.NoError(t, err) - assert.NotZero(t, broker.Created) - - // Get broker - retrieved, err := s.GetRuntimeBroker(ctx, broker.ID) - require.NoError(t, err) - assert.Equal(t, broker.Name, retrieved.Name) - assert.True(t, retrieved.Capabilities.WebPTY) - assert.Len(t, retrieved.Profiles, 1) - assert.Equal(t, "docker", retrieved.Profiles[0].Type) - assert.Equal(t, "admin-user-456", retrieved.CreatedBy) - - // Update broker - retrieved.Status = store.BrokerStatusOffline - err = s.UpdateRuntimeBroker(ctx, retrieved) - require.NoError(t, err) - - // Verify update - retrieved, err = s.GetRuntimeBroker(ctx, broker.ID) - require.NoError(t, err) - assert.Equal(t, store.BrokerStatusOffline, retrieved.Status) - - // Update heartbeat - err = s.UpdateRuntimeBrokerHeartbeat(ctx, broker.ID, store.BrokerStatusOnline) - require.NoError(t, err) - - // Verify heartbeat - retrieved, err = s.GetRuntimeBroker(ctx, broker.ID) - require.NoError(t, err) - assert.Equal(t, store.BrokerStatusOnline, retrieved.Status) - assert.NotZero(t, retrieved.LastHeartbeat) - - // Delete broker - err = s.DeleteRuntimeBroker(ctx, broker.ID) - require.NoError(t, err) - - _, err = s.GetRuntimeBroker(ctx, broker.ID) - assert.ErrorIs(t, err, store.ErrNotFound) -} - -func TestRuntimeBrokerList(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - // Create brokers - for i := 0; i < 3; i++ { - broker := &store.RuntimeBroker{ - ID: api.NewUUID(), - Name: "Host " + string(rune('A'+i)), - Slug: "host-" + string(rune('a'+i)), - Status: store.BrokerStatusOnline, - Profiles: []store.BrokerProfile{ - {Name: "default", Type: "docker", Available: true}, - }, - } - if i == 0 { - broker.Status = store.BrokerStatusOffline - } - require.NoError(t, s.CreateRuntimeBroker(ctx, broker)) - } - - // List all - result, err := s.ListRuntimeBrokers(ctx, store.RuntimeBrokerFilter{}, store.ListOptions{}) - require.NoError(t, err) - assert.Equal(t, 3, result.TotalCount) - - // List by status - result, err = s.ListRuntimeBrokers(ctx, store.RuntimeBrokerFilter{Status: store.BrokerStatusOffline}, store.ListOptions{}) - require.NoError(t, err) - assert.Equal(t, 1, result.TotalCount) - - // List by name (exact match, case-insensitive) - result, err = s.ListRuntimeBrokers(ctx, store.RuntimeBrokerFilter{Name: "Host A"}, store.ListOptions{}) - require.NoError(t, err) - assert.Equal(t, 1, result.TotalCount) - assert.Equal(t, "Host A", result.Items[0].Name) - - // List by name (case-insensitive) - result, err = s.ListRuntimeBrokers(ctx, store.RuntimeBrokerFilter{Name: "host b"}, store.ListOptions{}) - require.NoError(t, err) - assert.Equal(t, 1, result.TotalCount) - assert.Equal(t, "Host B", result.Items[0].Name) - - // List by name (no match) - result, err = s.ListRuntimeBrokers(ctx, store.RuntimeBrokerFilter{Name: "nonexistent"}, store.ListOptions{}) - require.NoError(t, err) - assert.Equal(t, 0, result.TotalCount) -} - -func TestRuntimeBrokerListByProjectIncludesAutoProvide(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - // Create a project - project := &store.Project{ - ID: "project-autoprovide-test", - Slug: "autoprovide-test", - Name: "AutoProvide Test", - Created: time.Now(), - Updated: time.Now(), - } - require.NoError(t, s.CreateProject(ctx, project)) - - // Create a regular broker explicitly linked to the project - linkedBroker := &store.RuntimeBroker{ - ID: "broker-linked", - Name: "Linked Broker", - Slug: "linked-broker", - Status: store.BrokerStatusOnline, - } - require.NoError(t, s.CreateRuntimeBroker(ctx, linkedBroker)) - require.NoError(t, s.AddProjectProvider(ctx, &store.ProjectProvider{ - ProjectID: project.ID, - BrokerID: linkedBroker.ID, - BrokerName: linkedBroker.Name, - Status: store.BrokerStatusOnline, - })) - - // Create an auto-provide broker (NOT explicitly linked to the project) - autoBroker := &store.RuntimeBroker{ - ID: "broker-auto", - Name: "Auto Broker", - Slug: "auto-broker", - Status: store.BrokerStatusOnline, - AutoProvide: true, - } - require.NoError(t, s.CreateRuntimeBroker(ctx, autoBroker)) - - // Create a regular broker NOT linked to the project - unlinkedBroker := &store.RuntimeBroker{ - ID: "broker-unlinked", - Name: "Unlinked Broker", - Slug: "unlinked-broker", - Status: store.BrokerStatusOnline, - } - require.NoError(t, s.CreateRuntimeBroker(ctx, unlinkedBroker)) - - // List brokers for the project — should include linked + auto-provide, but not unlinked - result, err := s.ListRuntimeBrokers(ctx, store.RuntimeBrokerFilter{ProjectID: project.ID}, store.ListOptions{}) - require.NoError(t, err) - assert.Equal(t, 2, result.TotalCount) - - ids := make(map[string]bool) - for _, b := range result.Items { - ids[b.ID] = true - } - assert.True(t, ids["broker-linked"], "linked broker should be included") - assert.True(t, ids["broker-auto"], "auto-provide broker should be included") - assert.False(t, ids["broker-unlinked"], "unlinked broker should not be included") -} - -// ============================================================================ -// Template Tests -// ============================================================================ - -func TestTemplateCRUD(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - // Create template - template := &store.Template{ - ID: api.NewUUID(), - Name: "Claude Default", - Slug: "claude-default", - Harness: "claude", - Image: "scion-claude:latest", - Scope: "global", - Visibility: store.VisibilityPublic, - Config: &store.TemplateConfig{ - Harness: "claude", - Detached: true, - }, - } - - err := s.CreateTemplate(ctx, template) - require.NoError(t, err) - assert.NotZero(t, template.Created) - - // Get template - retrieved, err := s.GetTemplate(ctx, template.ID) - require.NoError(t, err) - assert.Equal(t, template.Name, retrieved.Name) - assert.Equal(t, template.Harness, retrieved.Harness) - assert.True(t, retrieved.Config.Detached) - - // Get by slug - retrieved, err = s.GetTemplateBySlug(ctx, "claude-default", "global", "") - require.NoError(t, err) - assert.Equal(t, template.ID, retrieved.ID) - - // Update template - retrieved.Image = "scion-claude:v2" - err = s.UpdateTemplate(ctx, retrieved) - require.NoError(t, err) - - // Verify update - retrieved, err = s.GetTemplate(ctx, template.ID) - require.NoError(t, err) - assert.Equal(t, "scion-claude:v2", retrieved.Image) - - // Delete template - err = s.DeleteTemplate(ctx, template.ID) - require.NoError(t, err) - - _, err = s.GetTemplate(ctx, template.ID) - assert.ErrorIs(t, err, store.ErrNotFound) -} - -func TestTemplateList(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - // Create templates - for i := 0; i < 3; i++ { - template := &store.Template{ - ID: api.NewUUID(), - Name: "Template " + string(rune('A'+i)), - Slug: "template-" + string(rune('a'+i)), - Harness: "claude", - Scope: "global", - Visibility: store.VisibilityPublic, - } - if i == 0 { - template.Harness = "gemini" - } - require.NoError(t, s.CreateTemplate(ctx, template)) - } - - // List all - result, err := s.ListTemplates(ctx, store.TemplateFilter{}, store.ListOptions{}) - require.NoError(t, err) - assert.Equal(t, 3, result.TotalCount) - - // List by harness - result, err = s.ListTemplates(ctx, store.TemplateFilter{Harness: "gemini"}, store.ListOptions{}) - require.NoError(t, err) - assert.Equal(t, 1, result.TotalCount) -} - -// ============================================================================ -// HarnessConfig Tests -// ============================================================================ - -func TestHarnessConfigCRUD(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - // Create harness config - hc := &store.HarnessConfig{ - ID: api.NewUUID(), - Name: "Claude Default", - Slug: "claude-default", - Harness: "claude", - Scope: "global", - Visibility: store.VisibilityPublic, - Config: &store.HarnessConfigData{ - Harness: "claude", - Image: "scion-claude:latest", - }, - } - - err := s.CreateHarnessConfig(ctx, hc) - require.NoError(t, err) - assert.NotZero(t, hc.Created) - - // Get harness config - retrieved, err := s.GetHarnessConfig(ctx, hc.ID) - require.NoError(t, err) - assert.Equal(t, hc.Name, retrieved.Name) - assert.Equal(t, hc.Harness, retrieved.Harness) - assert.Equal(t, "claude", retrieved.Config.Harness) - assert.Equal(t, "scion-claude:latest", retrieved.Config.Image) - - // Get by slug - retrieved, err = s.GetHarnessConfigBySlug(ctx, "claude-default", "global", "") - require.NoError(t, err) - assert.Equal(t, hc.ID, retrieved.ID) - - // Update harness config - retrieved.Description = "Updated description" - err = s.UpdateHarnessConfig(ctx, retrieved) - require.NoError(t, err) - - // Verify update - retrieved, err = s.GetHarnessConfig(ctx, hc.ID) - require.NoError(t, err) - assert.Equal(t, "Updated description", retrieved.Description) - - // Delete harness config - err = s.DeleteHarnessConfig(ctx, hc.ID) - require.NoError(t, err) - - _, err = s.GetHarnessConfig(ctx, hc.ID) - assert.ErrorIs(t, err, store.ErrNotFound) -} - -func TestHarnessConfigList(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - // Create harness configs - for i := 0; i < 3; i++ { - hc := &store.HarnessConfig{ - ID: api.NewUUID(), - Name: "HC " + string(rune('A'+i)), - Slug: "hc-" + string(rune('a'+i)), - Harness: "claude", - Scope: "global", - Visibility: store.VisibilityPublic, - } - if i == 0 { - hc.Harness = "gemini" - } - require.NoError(t, s.CreateHarnessConfig(ctx, hc)) - } - - // List all - result, err := s.ListHarnessConfigs(ctx, store.HarnessConfigFilter{}, store.ListOptions{}) - require.NoError(t, err) - assert.Equal(t, 3, result.TotalCount) - - // List by harness - result, err = s.ListHarnessConfigs(ctx, store.HarnessConfigFilter{Harness: "gemini"}, store.ListOptions{}) - require.NoError(t, err) - assert.Equal(t, 1, result.TotalCount) -} - -func TestHarnessConfigListDeduplication(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - projectID := "project-dedup-test" - - globalHC := &store.HarnessConfig{ - ID: api.NewUUID(), - Name: "gemini", - Slug: "gemini", - Harness: "gemini", - Scope: "global", - } - projectHC := &store.HarnessConfig{ - ID: api.NewUUID(), - Name: "gemini", - Slug: "gemini", - Harness: "gemini", - Scope: "project", - ScopeID: projectID, - } - globalOnly := &store.HarnessConfig{ - ID: api.NewUUID(), - Name: "claude", - Slug: "claude", - Harness: "claude", - Scope: "global", - } - projectOnly := &store.HarnessConfig{ - ID: api.NewUUID(), - Name: "opencode", - Slug: "opencode", - Harness: "opencode", - Scope: "project", - ScopeID: projectID, - } - - for _, hc := range []*store.HarnessConfig{globalHC, projectHC, globalOnly, projectOnly} { - require.NoError(t, s.CreateHarnessConfig(ctx, hc)) - } - - // Without ProjectID filter: returns all 4 records (no dedup) - result, err := s.ListHarnessConfigs(ctx, store.HarnessConfigFilter{}, store.ListOptions{}) - require.NoError(t, err) - assert.Equal(t, 4, result.TotalCount) - - // With ProjectID filter: deduplicates, preferring project-scoped - result, err = s.ListHarnessConfigs(ctx, store.HarnessConfigFilter{ProjectID: projectID}, store.ListOptions{}) - require.NoError(t, err) - assert.Equal(t, 3, result.TotalCount) - - slugs := map[string]string{} - for _, hc := range result.Items { - slugs[hc.Slug] = hc.Scope - } - assert.Equal(t, "project", slugs["gemini"], "project-scoped should win over global") - assert.Equal(t, "global", slugs["claude"], "global-only config should still appear") - assert.Equal(t, "project", slugs["opencode"], "project-only config should still appear") -} - -// ============================================================================ -// User Tests -// ============================================================================ - -func TestUserCRUD(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - // Create user - user := &store.User{ - ID: api.NewUUID(), - Email: "test@example.com", - DisplayName: "Test User", - Role: store.UserRoleMember, - Status: "active", - Preferences: &store.UserPreferences{ - Theme: "dark", - }, - } - - err := s.CreateUser(ctx, user) - require.NoError(t, err) - assert.NotZero(t, user.Created) - - // Get user - retrieved, err := s.GetUser(ctx, user.ID) - require.NoError(t, err) - assert.Equal(t, user.Email, retrieved.Email) - assert.Equal(t, "dark", retrieved.Preferences.Theme) - - // Get by email - retrieved, err = s.GetUserByEmail(ctx, "test@example.com") - require.NoError(t, err) - assert.Equal(t, user.ID, retrieved.ID) - - // Test unique constraint on email - duplicate := &store.User{ - ID: api.NewUUID(), - Email: "test@example.com", - DisplayName: "Duplicate User", - Role: store.UserRoleMember, - Status: "active", - } - err = s.CreateUser(ctx, duplicate) - assert.ErrorIs(t, err, store.ErrAlreadyExists) - - // Update user - retrieved.DisplayName = "Updated User" - retrieved.LastLogin = time.Now() - err = s.UpdateUser(ctx, retrieved) - require.NoError(t, err) - - // Verify update - retrieved, err = s.GetUser(ctx, user.ID) - require.NoError(t, err) - assert.Equal(t, "Updated User", retrieved.DisplayName) - assert.NotZero(t, retrieved.LastLogin) - - // Delete user - err = s.DeleteUser(ctx, user.ID) - require.NoError(t, err) - - _, err = s.GetUser(ctx, user.ID) - assert.ErrorIs(t, err, store.ErrNotFound) -} - -func TestUserList(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - // Create users - for i := 0; i < 3; i++ { - user := &store.User{ - ID: api.NewUUID(), - Email: "user" + string(rune('a'+i)) + "@example.com", - DisplayName: "User " + string(rune('A'+i)), - Role: store.UserRoleMember, - Status: "active", - } - if i == 0 { - user.Role = store.UserRoleAdmin - } - require.NoError(t, s.CreateUser(ctx, user)) - } - - // List all - result, err := s.ListUsers(ctx, store.UserFilter{}, store.ListOptions{}) - require.NoError(t, err) - assert.Equal(t, 3, result.TotalCount) - - // List by role - result, err = s.ListUsers(ctx, store.UserFilter{Role: store.UserRoleAdmin}, store.ListOptions{}) - require.NoError(t, err) - assert.Equal(t, 1, result.TotalCount) -} - -func TestUserListSorting(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - // Create users with distinct names so sort order is deterministic - names := []string{"Charlie", "Alice", "Bob"} - for _, name := range names { - user := &store.User{ - ID: api.NewUUID(), - Email: strings.ToLower(name) + "@example.com", - DisplayName: name, - Role: store.UserRoleMember, - Status: "active", - } - require.NoError(t, s.CreateUser(ctx, user)) - } - - // Sort by name ascending - result, err := s.ListUsers(ctx, store.UserFilter{}, store.ListOptions{SortBy: "name", SortDir: "asc"}) - require.NoError(t, err) - require.Len(t, result.Items, 3) - assert.Equal(t, "Alice", result.Items[0].DisplayName) - assert.Equal(t, "Bob", result.Items[1].DisplayName) - assert.Equal(t, "Charlie", result.Items[2].DisplayName) - - // Sort by name descending - result, err = s.ListUsers(ctx, store.UserFilter{}, store.ListOptions{SortBy: "name", SortDir: "desc"}) - require.NoError(t, err) - require.Len(t, result.Items, 3) - assert.Equal(t, "Charlie", result.Items[0].DisplayName) - assert.Equal(t, "Bob", result.Items[1].DisplayName) - assert.Equal(t, "Alice", result.Items[2].DisplayName) - - // Sort by created descending (default) — most recent first - result, err = s.ListUsers(ctx, store.UserFilter{}, store.ListOptions{SortBy: "created", SortDir: "desc"}) - require.NoError(t, err) - require.Len(t, result.Items, 3) - // Last created should be first - assert.Equal(t, "Bob", result.Items[0].DisplayName) - - // Sorting should work across pages: page 1 with limit 2, sorted by name asc - result, err = s.ListUsers(ctx, store.UserFilter{}, store.ListOptions{Limit: 2, SortBy: "name", SortDir: "asc"}) - require.NoError(t, err) - require.Len(t, result.Items, 2) - assert.Equal(t, "Alice", result.Items[0].DisplayName) - assert.Equal(t, "Bob", result.Items[1].DisplayName) - assert.NotEmpty(t, result.NextCursor) - - // Page 2 should have the remaining user - result, err = s.ListUsers(ctx, store.UserFilter{}, store.ListOptions{Limit: 2, Cursor: result.NextCursor, SortBy: "name", SortDir: "asc"}) - require.NoError(t, err) - require.Len(t, result.Items, 1) - assert.Equal(t, "Charlie", result.Items[0].DisplayName) - assert.Empty(t, result.NextCursor) -} - -// ============================================================================ -// ProjectProvider Tests -// ============================================================================ - -func TestProjectProviders(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - // Create project - project := &store.Project{ - ID: api.NewUUID(), - Name: "Test Project", - Slug: "test-project", - Visibility: store.VisibilityPrivate, - } - require.NoError(t, s.CreateProject(ctx, project)) - - // Create brokers - broker1 := &store.RuntimeBroker{ - ID: api.NewUUID(), - Name: "Host 1", - Slug: "host-1", - Status: store.BrokerStatusOnline, - Profiles: []store.BrokerProfile{ - {Name: "docker", Type: "docker", Available: true}, - {Name: "dev", Type: "docker", Available: true}, - }, - } - require.NoError(t, s.CreateRuntimeBroker(ctx, broker1)) - - broker2 := &store.RuntimeBroker{ - ID: api.NewUUID(), - Name: "Host 2", - Slug: "host-2", - Status: store.BrokerStatusOnline, - Profiles: []store.BrokerProfile{ - {Name: "k8s-prod", Type: "kubernetes", Available: true}, - }, - } - require.NoError(t, s.CreateRuntimeBroker(ctx, broker2)) - - // Add providers with user tracking - provider1 := &store.ProjectProvider{ - ProjectID: project.ID, - BrokerID: broker1.ID, - BrokerName: broker1.Name, - Status: store.BrokerStatusOnline, - LinkedBy: "user-123", - } - require.NoError(t, s.AddProjectProvider(ctx, provider1)) - - provider2 := &store.ProjectProvider{ - ProjectID: project.ID, - BrokerID: broker2.ID, - BrokerName: broker2.Name, - Status: store.BrokerStatusOnline, - } - require.NoError(t, s.AddProjectProvider(ctx, provider2)) - - // Get project providers - providers, err := s.GetProjectProviders(ctx, project.ID) - require.NoError(t, err) - assert.Len(t, providers, 2) - - // Verify user tracking fields are stored - for _, p := range providers { - if p.BrokerID == broker1.ID { - assert.Equal(t, "user-123", p.LinkedBy) - assert.False(t, p.LinkedAt.IsZero(), "LinkedAt should be set") - } - } - - // Verify GetProjectProvider also returns user tracking fields - provider, err := s.GetProjectProvider(ctx, project.ID, broker1.ID) - require.NoError(t, err) - assert.Equal(t, "user-123", provider.LinkedBy) - assert.False(t, provider.LinkedAt.IsZero(), "LinkedAt should be set") - - // Get broker projects - projects, err := s.GetBrokerProjects(ctx, broker1.ID) - require.NoError(t, err) - assert.Len(t, projects, 1) - assert.Equal(t, project.ID, projects[0].ProjectID) - - // Update provider status - err = s.UpdateProviderStatus(ctx, project.ID, broker1.ID, store.BrokerStatusOffline) - require.NoError(t, err) - - // Verify update - providers, err = s.GetProjectProviders(ctx, project.ID) - require.NoError(t, err) - for _, p := range providers { - if p.BrokerID == broker1.ID { - assert.Equal(t, store.BrokerStatusOffline, p.Status) - } - } - - // Verify project's active broker count - retrievedProject, err := s.GetProject(ctx, project.ID) - require.NoError(t, err) - assert.Equal(t, 1, retrievedProject.ActiveBrokerCount) // Only broker2 is online - - // Remove provider - err = s.RemoveProjectProvider(ctx, project.ID, broker1.ID) - require.NoError(t, err) - - providers, err = s.GetProjectProviders(ctx, project.ID) - require.NoError(t, err) - assert.Len(t, providers, 1) - assert.Equal(t, broker2.ID, providers[0].BrokerID) -} - -// ============================================================================ -// Migration Tests -// ============================================================================ - -func TestMigration(t *testing.T) { - s, err := New(":memory:") - require.NoError(t, err) - defer s.Close() - - ctx := context.Background() - - // Run migrations - err = s.Migrate(ctx) - require.NoError(t, err) - - // Run again (should be idempotent) - err = s.Migrate(ctx) - require.NoError(t, err) - - // Verify tables exist by inserting data - project := &store.Project{ - ID: api.NewUUID(), - Name: "Test", - Slug: "test", - Visibility: store.VisibilityPrivate, - } - err = s.CreateProject(ctx, project) - require.NoError(t, err) -} - -func TestDropTableCascadesWithForeignKeysOn(t *testing.T) { - // Demonstrates the root cause: DROP TABLE with foreign_keys=ON - // triggers ON DELETE CASCADE, removing all child rows. - s, err := New(":memory:") - require.NoError(t, err) - defer s.Close() - - ctx := context.Background() - err = s.Migrate(ctx) - require.NoError(t, err) - - projectID := api.NewUUID() - err = s.CreateProject(ctx, &store.Project{ - ID: projectID, Name: "G", Slug: "g-cascade-test", Visibility: store.VisibilityPrivate, - }) - require.NoError(t, err) - - agentID := api.NewUUID() - err = s.CreateAgent(ctx, &store.Agent{ - ID: agentID, Slug: "a", Name: "A", ProjectID: projectID, Visibility: store.VisibilityPrivate, - }) - require.NoError(t, err) - - // With foreign_keys ON (default), DROP TABLE cascades - _, err = s.db.ExecContext(ctx, ` - CREATE TABLE projects_copy AS SELECT * FROM projects; - DROP TABLE projects; - ALTER TABLE projects_copy RENAME TO projects; - `) - require.NoError(t, err) - - // Agent was cascade-deleted — this is the bug V40 originally triggered - _, err = s.GetAgent(ctx, agentID) - assert.ErrorIs(t, err, store.ErrNotFound, "agent should be cascade-deleted when FK is ON") -} - -func TestMigrationV40PreservesAgents(t *testing.T) { - // Regression test: V40 drops and recreates the projects table. Without - // PRAGMA foreign_keys=OFF (which must be set OUTSIDE the transaction), - // DROP TABLE projects triggers ON DELETE CASCADE on agents, deleting all rows. - s, err := New(":memory:") - require.NoError(t, err) - defer s.Close() - - ctx := context.Background() - - err = s.Migrate(ctx) - require.NoError(t, err) - - // Create a project and an agent - projectID := api.NewUUID() - err = s.CreateProject(ctx, &store.Project{ - ID: projectID, - Name: "TestProject", - Slug: "test-project", - Visibility: store.VisibilityPrivate, - }) - require.NoError(t, err) - - agentID := api.NewUUID() - err = s.CreateAgent(ctx, &store.Agent{ - ID: agentID, - Slug: "test-agent", - Name: "Test Agent", - ProjectID: projectID, - Visibility: store.VisibilityPrivate, - }) - require.NoError(t, err) - - // Verify agent exists - agent, err := s.GetAgent(ctx, agentID) - require.NoError(t, err) - assert.Equal(t, "Test Agent", agent.Name) - - // Simulate re-running V40 by dropping and recreating projects table - // using the same pattern as the migration, with proper FK handling. - _, err = s.db.ExecContext(ctx, "PRAGMA foreign_keys=OFF") - require.NoError(t, err) - - _, err = s.db.ExecContext(ctx, ` - CREATE TABLE projects_new2 AS SELECT * FROM projects; - DROP TABLE projects; - ALTER TABLE projects_new2 RENAME TO projects; - `) - require.NoError(t, err) - - _, err = s.db.ExecContext(ctx, "PRAGMA foreign_keys=ON") - require.NoError(t, err) - - // Agent must still exist after table recreation - agent, err = s.GetAgent(ctx, agentID) - require.NoError(t, err) - assert.Equal(t, "Test Agent", agent.Name) -} - -func TestMigrationV53_AllowListMissing(t *testing.T) { - // Regression test: V48 and V49 were inserted into the migration sequence, - // pushing the grove-to-project rename from V48 to V50. Databases that - // already applied the old V48 (the rename) have version 48 recorded in - // schema_migrations, so the new V48 (CREATE TABLE allow_list) is skipped. - // V53 must create the allow_list table if it doesn't exist before adding - // the index. - s, err := New(":memory:") - require.NoError(t, err) - defer s.Close() - - ctx := context.Background() - - // Run all migrations normally first. - err = s.Migrate(ctx) - require.NoError(t, err) - - // Simulate the bug: drop allow_list (as if V48 was a different migration - // when it was originally applied) and roll back schema_migrations so V53 - // will re-run. - _, err = s.db.ExecContext(ctx, ` - DROP TABLE IF EXISTS allow_list; - DELETE FROM schema_migrations WHERE version >= 53; - `) - require.NoError(t, err) - - // Verify allow_list doesn't exist. - var tableName string - err = s.db.QueryRowContext(ctx, - "SELECT name FROM sqlite_master WHERE type='table' AND name='allow_list'", - ).Scan(&tableName) - require.ErrorIs(t, err, sql.ErrNoRows, - "allow_list should not exist before re-migration") - - // Re-run Migrate. V53 should succeed by creating the allow_list table - // before adding the index. - err = s.Migrate(ctx) - require.NoError(t, err, "Migrate must succeed even when allow_list was never created by V48") - - // Verify allow_list table now exists and is usable. - _, err = s.db.ExecContext(ctx, - "INSERT INTO allow_list (id, email, added_by) VALUES ('test-id', 'test@example.com', 'admin')") - require.NoError(t, err, "allow_list table should be usable after migration") - - // Verify the index exists. - var indexName string - err = s.db.QueryRowContext(ctx, - "SELECT name FROM sqlite_master WHERE type='index' AND name='idx_allow_list_created_id'", - ).Scan(&indexName) - require.NoError(t, err, "idx_allow_list_created_id index should exist") -} - -func TestPing(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - err := s.Ping(ctx) - require.NoError(t, err) -} - -// ============================================================================ -// Error Cases -// ============================================================================ - -func TestNotFoundErrors(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - nonExistentID := api.NewUUID() - - // Agent - _, err := s.GetAgent(ctx, nonExistentID) - assert.ErrorIs(t, err, store.ErrNotFound) - - err = s.DeleteAgent(ctx, nonExistentID) - assert.ErrorIs(t, err, store.ErrNotFound) - - // Project - _, err = s.GetProject(ctx, nonExistentID) - assert.ErrorIs(t, err, store.ErrNotFound) - - err = s.DeleteProject(ctx, nonExistentID) - assert.ErrorIs(t, err, store.ErrNotFound) - - // RuntimeBroker - _, err = s.GetRuntimeBroker(ctx, nonExistentID) - assert.ErrorIs(t, err, store.ErrNotFound) - - err = s.DeleteRuntimeBroker(ctx, nonExistentID) - assert.ErrorIs(t, err, store.ErrNotFound) - - // Template - _, err = s.GetTemplate(ctx, nonExistentID) - assert.ErrorIs(t, err, store.ErrNotFound) - - err = s.DeleteTemplate(ctx, nonExistentID) - assert.ErrorIs(t, err, store.ErrNotFound) - - // User - _, err = s.GetUser(ctx, nonExistentID) - assert.ErrorIs(t, err, store.ErrNotFound) - - err = s.DeleteUser(ctx, nonExistentID) - assert.ErrorIs(t, err, store.ErrNotFound) -} - -func TestCascadeDelete(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - // Create project with agent - project := &store.Project{ - ID: api.NewUUID(), - Name: "Test Project", - Slug: "test-project", - Visibility: store.VisibilityPrivate, - } - require.NoError(t, s.CreateProject(ctx, project)) - - agent := &store.Agent{ - ID: api.NewUUID(), - Slug: "test-agent", - Name: "Test Agent", - Template: "claude", - ProjectID: project.ID, - Phase: string(state.PhaseRunning), - Visibility: store.VisibilityPrivate, - } - require.NoError(t, s.CreateAgent(ctx, agent)) - - // Delete project - err := s.DeleteProject(ctx, project.ID) - require.NoError(t, err) - - // Verify agent was cascade deleted - _, err = s.GetAgent(ctx, agent.ID) - assert.ErrorIs(t, err, store.ErrNotFound) -} - -func TestCascadeDeleteEnvVarsSecrets(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - projectID := api.NewUUID() - require.NoError(t, s.CreateProject(ctx, &store.Project{ - ID: projectID, Name: "Cascade EV/S", Slug: "cascade-ev-s", - Visibility: store.VisibilityPrivate, - })) - - // Create project-scoped env vars - require.NoError(t, s.CreateEnvVar(ctx, &store.EnvVar{ - ID: api.NewUUID(), Key: "A", Value: "1", - Scope: store.ScopeProject, ScopeID: projectID, - })) - require.NoError(t, s.CreateEnvVar(ctx, &store.EnvVar{ - ID: api.NewUUID(), Key: "B", Value: "2", - Scope: store.ScopeProject, ScopeID: projectID, - })) - - // Create project-scoped secrets - require.NoError(t, s.CreateSecret(ctx, &store.Secret{ - ID: api.NewUUID(), Key: "S1", EncryptedValue: "enc1", - Scope: store.ScopeProject, ScopeID: projectID, Version: 1, - })) - - // Create a hub-scoped env var (should not be deleted) - require.NoError(t, s.CreateEnvVar(ctx, &store.EnvVar{ - ID: api.NewUUID(), Key: "HUB_VAR", Value: "hub", - Scope: store.ScopeHub, ScopeID: "test-hub-id", - })) - - // Delete by scope - n, err := s.DeleteEnvVarsByScope(ctx, store.ScopeProject, projectID) - require.NoError(t, err) - assert.Equal(t, 2, n) - - n, err = s.DeleteSecretsByScope(ctx, store.ScopeProject, projectID) - require.NoError(t, err) - assert.Equal(t, 1, n) - - // Verify project-scoped are gone - envVars, err := s.ListEnvVars(ctx, store.EnvVarFilter{Scope: store.ScopeProject, ScopeID: projectID}) - require.NoError(t, err) - assert.Empty(t, envVars) - - secrets, err := s.ListSecrets(ctx, store.SecretFilter{Scope: store.ScopeProject, ScopeID: projectID}) - require.NoError(t, err) - assert.Empty(t, secrets) - - // Verify hub-scoped env var still exists - hubVars, err := s.ListEnvVars(ctx, store.EnvVarFilter{Scope: store.ScopeHub, ScopeID: "test-hub-id"}) - require.NoError(t, err) - assert.Len(t, hubVars, 1) - - // Delete with no matches returns 0, no error - n, err = s.DeleteEnvVarsByScope(ctx, store.ScopeProject, "nonexistent") - require.NoError(t, err) - assert.Equal(t, 0, n) -} - -func TestDeleteHarnessConfigsByScope(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - projectID := api.NewUUID() - require.NoError(t, s.CreateProject(ctx, &store.Project{ - ID: projectID, Name: "HC Cascade", Slug: "hc-cascade", - Visibility: store.VisibilityPrivate, - })) - - require.NoError(t, s.CreateHarnessConfig(ctx, &store.HarnessConfig{ - ID: api.NewUUID(), Name: "hc1", Slug: "hc1", - Harness: "claude", Scope: store.ScopeProject, ScopeID: projectID, - Status: store.HarnessConfigStatusActive, Visibility: store.VisibilityPrivate, - })) - - n, err := s.DeleteHarnessConfigsByScope(ctx, store.ScopeProject, projectID) - require.NoError(t, err) - assert.Equal(t, 1, n) - - result, err := s.ListHarnessConfigs(ctx, store.HarnessConfigFilter{Scope: store.ScopeProject, ScopeID: projectID}, store.ListOptions{}) - require.NoError(t, err) - assert.Empty(t, result.Items) -} - -func TestDeleteTemplatesByScope(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - projectID := api.NewUUID() - require.NoError(t, s.CreateProject(ctx, &store.Project{ - ID: projectID, Name: "Tmpl Cascade", Slug: "tmpl-cascade", - Visibility: store.VisibilityPrivate, - })) - - require.NoError(t, s.CreateTemplate(ctx, &store.Template{ - ID: api.NewUUID(), Name: "tmpl1", Slug: "tmpl1", - Harness: "claude", Scope: store.ScopeProject, ScopeID: projectID, - Status: store.TemplateStatusActive, Visibility: store.VisibilityPrivate, - })) - require.NoError(t, s.CreateTemplate(ctx, &store.Template{ - ID: api.NewUUID(), Name: "tmpl2", Slug: "tmpl2", - Harness: "gemini", Scope: store.ScopeProject, ScopeID: projectID, - Status: store.TemplateStatusActive, Visibility: store.VisibilityPrivate, - })) - - n, err := s.DeleteTemplatesByScope(ctx, store.ScopeProject, projectID) - require.NoError(t, err) - assert.Equal(t, 2, n) - - result, err := s.ListTemplates(ctx, store.TemplateFilter{Scope: store.ScopeProject, ScopeID: projectID}, store.ListOptions{}) - require.NoError(t, err) - assert.Empty(t, result.Items) -} - -// ============================================================================ -// MarkStaleAgentsOffline Tests -// ============================================================================ - -func TestMarkStaleAgentsOffline(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - project := &store.Project{ - ID: api.NewUUID(), - Name: "Heartbeat Project", - Slug: "heartbeat-project", - Visibility: store.VisibilityPrivate, - } - require.NoError(t, s.CreateProject(ctx, project)) - - staleTime := time.Now().Add(-5 * time.Minute) - threshold := time.Now().Add(-2 * time.Minute) - - // These agents have phase=running with non-sticky activities → should be marked offline - activeActivities := []string{"working", "thinking", "executing", "waiting_for_input"} - - var expectedIDs []string - for i, activity := range activeActivities { - agent := &store.Agent{ - ID: api.NewUUID(), - Slug: "active-agent-" + activity, - Name: "Active Agent " + activity, - Template: "claude", - ProjectID: project.ID, - Phase: string(state.PhaseCreated), - Visibility: store.VisibilityPrivate, - } - require.NoError(t, s.CreateAgent(ctx, agent)) - - // Set to running phase with activity - err := s.UpdateAgentStatus(ctx, agent.ID, store.AgentStatusUpdate{ - Phase: "running", - Activity: activity, - }) - require.NoError(t, err, "setting activity for agent %d", i) - - // Manually set last_seen to stale time - _, err = s.db.ExecContext(ctx, "UPDATE agents SET last_seen = ? WHERE id = ?", staleTime, agent.ID) - require.NoError(t, err) - - expectedIDs = append(expectedIDs, agent.ID) - } - - // These agents should NOT be marked offline - - // Sticky activity: completed (phase=running) - completedAgent := &store.Agent{ - ID: api.NewUUID(), Slug: "completed-agent", Name: "Completed Agent", - Template: "claude", ProjectID: project.ID, Phase: string(state.PhaseCreated), - Visibility: store.VisibilityPrivate, - } - require.NoError(t, s.CreateAgent(ctx, completedAgent)) - require.NoError(t, s.UpdateAgentStatus(ctx, completedAgent.ID, store.AgentStatusUpdate{ - Phase: "running", Activity: "completed", - })) - _, err := s.db.ExecContext(ctx, "UPDATE agents SET last_seen = ? WHERE id = ?", staleTime, completedAgent.ID) - require.NoError(t, err) - - // Sticky activity: limits_exceeded (phase=running) - limitsAgent := &store.Agent{ - ID: api.NewUUID(), Slug: "limits-agent", Name: "Limits Agent", - Template: "claude", ProjectID: project.ID, Phase: string(state.PhaseCreated), - Visibility: store.VisibilityPrivate, - } - require.NoError(t, s.CreateAgent(ctx, limitsAgent)) - require.NoError(t, s.UpdateAgentStatus(ctx, limitsAgent.ID, store.AgentStatusUpdate{ - Phase: "running", Activity: "limits_exceeded", - })) - _, err = s.db.ExecContext(ctx, "UPDATE agents SET last_seen = ? WHERE id = ?", staleTime, limitsAgent.ID) - require.NoError(t, err) - - // Non-running phase: stopped - stoppedAgent := &store.Agent{ - ID: api.NewUUID(), Slug: "stopped-agent", Name: "Stopped Agent", - Template: "claude", ProjectID: project.ID, Phase: string(state.PhaseStopped), - Visibility: store.VisibilityPrivate, - } - require.NoError(t, s.CreateAgent(ctx, stoppedAgent)) - _, err = s.db.ExecContext(ctx, "UPDATE agents SET last_seen = ? WHERE id = ?", staleTime, stoppedAgent.ID) - require.NoError(t, err) - - // Recent heartbeat (should not be affected) - recentAgent := &store.Agent{ - ID: api.NewUUID(), Slug: "recent-agent", Name: "Recent Agent", - Template: "claude", ProjectID: project.ID, Phase: string(state.PhaseCreated), - Visibility: store.VisibilityPrivate, - } - require.NoError(t, s.CreateAgent(ctx, recentAgent)) - require.NoError(t, s.UpdateAgentStatus(ctx, recentAgent.ID, store.AgentStatusUpdate{ - Phase: "running", Activity: "working", - })) - // last_seen is set to now by UpdateAgentStatus, which is within the threshold - - // Execute - agents, err := s.MarkStaleAgentsOffline(ctx, threshold) - require.NoError(t, err) - assert.Len(t, agents, len(activeActivities), "should only mark running stale agents with non-sticky activities") - - // Verify the returned agents - returnedIDs := make(map[string]bool) - for _, a := range agents { - returnedIDs[a.ID] = true - assert.Equal(t, "offline", a.Activity, "returned agent should have offline activity") - assert.Equal(t, "running", a.Phase, "returned agent should still have running phase") - assert.Equal(t, string(state.ActivityOffline), a.Activity, "returned agent should have offline activity") - } - for _, id := range expectedIDs { - assert.True(t, returnedIDs[id], "expected agent %s to be in returned set", id) - } - - // Verify sticky activities were NOT affected - a, err := s.GetAgent(ctx, completedAgent.ID) - require.NoError(t, err) - assert.Equal(t, "completed", a.Activity) - - a, err = s.GetAgent(ctx, limitsAgent.ID) - require.NoError(t, err) - assert.Equal(t, "limits_exceeded", a.Activity) - - // Verify stopped agent was NOT affected - a, err = s.GetAgent(ctx, stoppedAgent.ID) - require.NoError(t, err) - assert.Equal(t, "stopped", a.Phase) - - // Verify recent agent was NOT affected - a, err = s.GetAgent(ctx, recentAgent.ID) - require.NoError(t, err) - assert.Equal(t, "working", a.Activity) -} - -func TestMarkStaleAgentsOffline_Idempotent(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - project := &store.Project{ - ID: api.NewUUID(), - Name: "Idempotent Project", - Slug: "idempotent-project", - Visibility: store.VisibilityPrivate, - } - require.NoError(t, s.CreateProject(ctx, project)) - - staleTime := time.Now().Add(-5 * time.Minute) - threshold := time.Now().Add(-2 * time.Minute) - - agent := &store.Agent{ - ID: api.NewUUID(), Slug: "stale-agent", Name: "Stale Agent", - Template: "claude", ProjectID: project.ID, Phase: string(state.PhaseCreated), - Visibility: store.VisibilityPrivate, - } - require.NoError(t, s.CreateAgent(ctx, agent)) - require.NoError(t, s.UpdateAgentStatus(ctx, agent.ID, store.AgentStatusUpdate{ - Phase: "running", Activity: "working", - })) - _, err := s.db.ExecContext(ctx, "UPDATE agents SET last_seen = ? WHERE id = ?", staleTime, agent.ID) - require.NoError(t, err) - - // First call should mark it offline - agents, err := s.MarkStaleAgentsOffline(ctx, threshold) - require.NoError(t, err) - assert.Len(t, agents, 1) - - // Second call should return empty (already offline) - agents, err = s.MarkStaleAgentsOffline(ctx, threshold) - require.NoError(t, err) - assert.Len(t, agents, 0, "should not re-mark already offline agents") -} - -func TestMarkStaleAgentsOffline_NoStaleAgents(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - threshold := time.Now().Add(-2 * time.Minute) - - // No agents at all - agents, err := s.MarkStaleAgentsOffline(ctx, threshold) - require.NoError(t, err) - assert.Len(t, agents, 0) -} - -// ============================================================================ -// Stalled Agent Detection Tests -// ============================================================================ - -func TestMarkStalledAgents(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - project := &store.Project{ - ID: api.NewUUID(), - Name: "Stalled Project", - Slug: "stalled-project", - Visibility: store.VisibilityPrivate, - } - require.NoError(t, s.CreateProject(ctx, project)) - - staleActivityTime := time.Now().Add(-10 * time.Minute) - recentHeartbeat := time.Now().Add(-30 * time.Second) - activityThreshold := time.Now().Add(-5 * time.Minute) - heartbeatRecency := time.Now().Add(-2 * time.Minute) - - // --- Should be marked stalled: stale activity + recent heartbeat --- - stalledActivities := []string{"thinking", "executing", "working"} - var expectedIDs []string - for _, activity := range stalledActivities { - agent := &store.Agent{ - ID: api.NewUUID(), Slug: "stalled-" + activity, Name: "Stalled " + activity, - Template: "claude", ProjectID: project.ID, Phase: string(state.PhaseCreated), - Visibility: store.VisibilityPrivate, - } - require.NoError(t, s.CreateAgent(ctx, agent)) - require.NoError(t, s.UpdateAgentStatus(ctx, agent.ID, store.AgentStatusUpdate{ - Phase: "running", Activity: activity, - })) - // Manually set stale activity time + recent heartbeat - _, err := s.db.ExecContext(ctx, - "UPDATE agents SET last_activity_event = ?, last_seen = ? WHERE id = ?", - staleActivityTime, recentHeartbeat, agent.ID) - require.NoError(t, err) - expectedIDs = append(expectedIDs, agent.ID) - } - - // --- Should NOT be marked stalled --- - - // Recent activity (within threshold) - recentAgent := &store.Agent{ - ID: api.NewUUID(), Slug: "recent-activity", Name: "Recent Activity", - Template: "claude", ProjectID: project.ID, Phase: string(state.PhaseCreated), - Visibility: store.VisibilityPrivate, - } - require.NoError(t, s.CreateAgent(ctx, recentAgent)) - require.NoError(t, s.UpdateAgentStatus(ctx, recentAgent.ID, store.AgentStatusUpdate{ - Phase: "running", Activity: "working", - })) - // last_activity_event is set to now by UpdateAgentStatus, which is within threshold - - // Stale activity + stale heartbeat (offline territory, not stalled) - offlineAgent := &store.Agent{ - ID: api.NewUUID(), Slug: "offline-territory", Name: "Offline Territory", - Template: "claude", ProjectID: project.ID, Phase: string(state.PhaseCreated), - Visibility: store.VisibilityPrivate, - } - require.NoError(t, s.CreateAgent(ctx, offlineAgent)) - require.NoError(t, s.UpdateAgentStatus(ctx, offlineAgent.ID, store.AgentStatusUpdate{ - Phase: "running", Activity: "working", - })) - staleHeartbeat := time.Now().Add(-5 * time.Minute) - _, err := s.db.ExecContext(ctx, - "UPDATE agents SET last_activity_event = ?, last_seen = ? WHERE id = ?", - staleActivityTime, staleHeartbeat, offlineAgent.ID) - require.NoError(t, err) - - // Completed activity (sticky — should not be stalled) - completedAgent := &store.Agent{ - ID: api.NewUUID(), Slug: "completed-stall", Name: "Completed Stall", - Template: "claude", ProjectID: project.ID, Phase: string(state.PhaseCreated), - Visibility: store.VisibilityPrivate, - } - require.NoError(t, s.CreateAgent(ctx, completedAgent)) - require.NoError(t, s.UpdateAgentStatus(ctx, completedAgent.ID, store.AgentStatusUpdate{ - Phase: "running", Activity: "completed", - })) - _, err = s.db.ExecContext(ctx, - "UPDATE agents SET last_activity_event = ?, last_seen = ? WHERE id = ?", - staleActivityTime, recentHeartbeat, completedAgent.ID) - require.NoError(t, err) - - // limits_exceeded activity (sticky) - limitsAgent := &store.Agent{ - ID: api.NewUUID(), Slug: "limits-stall", Name: "Limits Stall", - Template: "claude", ProjectID: project.ID, Phase: string(state.PhaseCreated), - Visibility: store.VisibilityPrivate, - } - require.NoError(t, s.CreateAgent(ctx, limitsAgent)) - require.NoError(t, s.UpdateAgentStatus(ctx, limitsAgent.ID, store.AgentStatusUpdate{ - Phase: "running", Activity: "limits_exceeded", - })) - _, err = s.db.ExecContext(ctx, - "UPDATE agents SET last_activity_event = ?, last_seen = ? WHERE id = ?", - staleActivityTime, recentHeartbeat, limitsAgent.ID) - require.NoError(t, err) - - // Stopped phase (not running) - stoppedAgent := &store.Agent{ - ID: api.NewUUID(), Slug: "stopped-stall", Name: "Stopped Stall", - Template: "claude", ProjectID: project.ID, Phase: string(state.PhaseStopped), - Visibility: store.VisibilityPrivate, - } - require.NoError(t, s.CreateAgent(ctx, stoppedAgent)) - _, err = s.db.ExecContext(ctx, - "UPDATE agents SET last_activity_event = ?, last_seen = ? WHERE id = ?", - staleActivityTime, recentHeartbeat, stoppedAgent.ID) - require.NoError(t, err) - - // Already offline - alreadyOfflineAgent := &store.Agent{ - ID: api.NewUUID(), Slug: "already-offline", Name: "Already Offline", - Template: "claude", ProjectID: project.ID, Phase: string(state.PhaseCreated), - Visibility: store.VisibilityPrivate, - } - require.NoError(t, s.CreateAgent(ctx, alreadyOfflineAgent)) - require.NoError(t, s.UpdateAgentStatus(ctx, alreadyOfflineAgent.ID, store.AgentStatusUpdate{ - Phase: "running", Activity: "offline", - })) - _, err = s.db.ExecContext(ctx, - "UPDATE agents SET last_activity_event = ?, last_seen = ? WHERE id = ?", - staleActivityTime, recentHeartbeat, alreadyOfflineAgent.ID) - require.NoError(t, err) - - // waiting_for_input activity (sticky waiting state — must NOT stall) - waitingAgent := &store.Agent{ - ID: api.NewUUID(), Slug: "waiting-for-input", Name: "Waiting For Input", - Template: "claude", ProjectID: project.ID, Phase: string(state.PhaseCreated), - Visibility: store.VisibilityPrivate, - } - require.NoError(t, s.CreateAgent(ctx, waitingAgent)) - require.NoError(t, s.UpdateAgentStatus(ctx, waitingAgent.ID, store.AgentStatusUpdate{ - Phase: "running", Activity: "waiting_for_input", - })) - _, err = s.db.ExecContext(ctx, - "UPDATE agents SET last_activity_event = ?, last_seen = ? WHERE id = ?", - staleActivityTime, recentHeartbeat, waitingAgent.ID) - require.NoError(t, err) - - // Execute - agents, err := s.MarkStalledAgents(ctx, activityThreshold, heartbeatRecency) - require.NoError(t, err) - assert.Len(t, agents, len(stalledActivities), "should only mark running agents with stale activity and recent heartbeat") - - // Verify the returned agents - returnedIDs := make(map[string]bool) - // Build a map from ID to pre-stall activity for validation - expectedPreStall := make(map[string]string) - for i, id := range expectedIDs { - expectedPreStall[id] = stalledActivities[i] - } - for _, a := range agents { - returnedIDs[a.ID] = true - assert.Equal(t, "stalled", a.Activity, "returned agent should have stalled activity") - assert.Equal(t, "running", a.Phase, "returned agent should still have running phase") - if expected, ok := expectedPreStall[a.ID]; ok { - assert.Equal(t, expected, a.StalledFromActivity, - "stalled_from_activity should record the pre-stall activity for agent %s", a.Slug) - } - } - for _, id := range expectedIDs { - assert.True(t, returnedIDs[id], "expected agent %s to be in returned set", id) - } - - // Verify excluded agents were NOT affected - a, err := s.GetAgent(ctx, recentAgent.ID) - require.NoError(t, err) - assert.Equal(t, "working", a.Activity) - - a, err = s.GetAgent(ctx, offlineAgent.ID) - require.NoError(t, err) - assert.Equal(t, "working", a.Activity, "stale heartbeat agent should not be stalled") - - a, err = s.GetAgent(ctx, completedAgent.ID) - require.NoError(t, err) - assert.Equal(t, "completed", a.Activity) - - a, err = s.GetAgent(ctx, limitsAgent.ID) - require.NoError(t, err) - assert.Equal(t, "limits_exceeded", a.Activity) - - a, err = s.GetAgent(ctx, stoppedAgent.ID) - require.NoError(t, err) - assert.Equal(t, string(state.PhaseStopped), a.Phase) - - a, err = s.GetAgent(ctx, alreadyOfflineAgent.ID) - require.NoError(t, err) - assert.Equal(t, "offline", a.Activity) - - a, err = s.GetAgent(ctx, waitingAgent.ID) - require.NoError(t, err) - assert.Equal(t, "waiting_for_input", a.Activity, "waiting_for_input agent should not be marked stalled") -} - -func TestMarkStalledAgents_Idempotent(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - project := &store.Project{ - ID: api.NewUUID(), - Name: "Idempotent Stalled Project", - Slug: "idempotent-stalled", - Visibility: store.VisibilityPrivate, - } - require.NoError(t, s.CreateProject(ctx, project)) - - staleActivityTime := time.Now().Add(-10 * time.Minute) - recentHeartbeat := time.Now().Add(-30 * time.Second) - activityThreshold := time.Now().Add(-5 * time.Minute) - heartbeatRecency := time.Now().Add(-2 * time.Minute) - - agent := &store.Agent{ - ID: api.NewUUID(), Slug: "stalled-idem", Name: "Stalled Idem", - Template: "claude", ProjectID: project.ID, Phase: string(state.PhaseCreated), - Visibility: store.VisibilityPrivate, - } - require.NoError(t, s.CreateAgent(ctx, agent)) - require.NoError(t, s.UpdateAgentStatus(ctx, agent.ID, store.AgentStatusUpdate{ - Phase: "running", Activity: "thinking", - })) - _, err := s.db.ExecContext(ctx, - "UPDATE agents SET last_activity_event = ?, last_seen = ? WHERE id = ?", - staleActivityTime, recentHeartbeat, agent.ID) - require.NoError(t, err) - - // First call should mark it stalled - agents, err := s.MarkStalledAgents(ctx, activityThreshold, heartbeatRecency) - require.NoError(t, err) - assert.Len(t, agents, 1) - - // Second call should return empty (already stalled) - agents, err = s.MarkStalledAgents(ctx, activityThreshold, heartbeatRecency) - require.NoError(t, err) - assert.Len(t, agents, 0, "should not re-mark already stalled agents") -} - -func TestMarkStalledAgents_NoAgents(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - activityThreshold := time.Now().Add(-5 * time.Minute) - heartbeatRecency := time.Now().Add(-2 * time.Minute) - - agents, err := s.MarkStalledAgents(ctx, activityThreshold, heartbeatRecency) - require.NoError(t, err) - assert.Len(t, agents, 0) -} - -func TestUpdateAgentStatus_SetsLastActivityEvent(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - project := &store.Project{ - ID: api.NewUUID(), - Name: "Activity Event Project", - Slug: "activity-event-project", - Visibility: store.VisibilityPrivate, - } - require.NoError(t, s.CreateProject(ctx, project)) - - agent := &store.Agent{ - ID: api.NewUUID(), Slug: "activity-tracker", Name: "Activity Tracker", - Template: "claude", ProjectID: project.ID, Phase: string(state.PhaseCreated), - Visibility: store.VisibilityPrivate, - } - require.NoError(t, s.CreateAgent(ctx, agent)) - - // Activity update should set last_activity_event - require.NoError(t, s.UpdateAgentStatus(ctx, agent.ID, store.AgentStatusUpdate{ - Phase: "running", Activity: "thinking", - })) - - a, err := s.GetAgent(ctx, agent.ID) - require.NoError(t, err) - assert.False(t, a.LastActivityEvent.IsZero(), "last_activity_event should be set after activity update") - activityTime := a.LastActivityEvent - - // Heartbeat-only update (no activity) should NOT change last_activity_event - // Manually set last_activity_event to a known past time first - pastTime := time.Now().Add(-10 * time.Minute) - _, err = s.db.ExecContext(ctx, "UPDATE agents SET last_activity_event = ? WHERE id = ?", pastTime, agent.ID) - require.NoError(t, err) - - require.NoError(t, s.UpdateAgentStatus(ctx, agent.ID, store.AgentStatusUpdate{ - Heartbeat: true, - })) - - a, err = s.GetAgent(ctx, agent.ID) - require.NoError(t, err) - // last_activity_event should still be the past time, not updated - assert.True(t, a.LastActivityEvent.Before(activityTime), - "heartbeat-only update should not change last_activity_event") - - // Another activity update should update last_activity_event - require.NoError(t, s.UpdateAgentStatus(ctx, agent.ID, store.AgentStatusUpdate{ - Activity: "executing", - })) - - a, err = s.GetAgent(ctx, agent.ID) - require.NoError(t, err) - assert.True(t, a.LastActivityEvent.After(pastTime), - "activity update should update last_activity_event") -} - -func TestUpdateAgentStatus_ProtectsTerminalActivity(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - grove := &store.Project{ - ID: api.NewUUID(), - Name: "Terminal Guard Grove", - Slug: "terminal-guard-grove", - Visibility: store.VisibilityPrivate, - } - require.NoError(t, s.CreateProject(ctx, grove)) - - agent := &store.Agent{ - ID: api.NewUUID(), Slug: "terminal-guard", Name: "Terminal Guard", - Template: "claude", ProjectID: grove.ID, Phase: string(state.PhaseStopped), - Activity: string(state.ActivityCrashed), - Visibility: store.VisibilityPrivate, - } - require.NoError(t, s.CreateAgent(ctx, agent)) - - // Non-terminal activity should not overwrite crashed - require.NoError(t, s.UpdateAgentStatus(ctx, agent.ID, store.AgentStatusUpdate{ - Activity: string(state.ActivityWorking), - })) - - a, err := s.GetAgent(ctx, agent.ID) - require.NoError(t, err) - assert.Equal(t, string(state.ActivityCrashed), a.Activity, - "non-terminal activity should not overwrite crashed") - - // Another terminal activity should overwrite - require.NoError(t, s.UpdateAgentStatus(ctx, agent.ID, store.AgentStatusUpdate{ - Activity: string(state.ActivityLimitsExceeded), - })) - - a, err = s.GetAgent(ctx, agent.ID) - require.NoError(t, err) - assert.Equal(t, string(state.ActivityLimitsExceeded), a.Activity, - "terminal activity should be able to overwrite another terminal activity") - - // Empty activity should keep current (standard behavior) - require.NoError(t, s.UpdateAgentStatus(ctx, agent.ID, store.AgentStatusUpdate{ - Heartbeat: true, - })) - - a, err = s.GetAgent(ctx, agent.ID) - require.NoError(t, err) - assert.Equal(t, string(state.ActivityLimitsExceeded), a.Activity, - "empty activity should keep current terminal activity") -} - -// ============================================================================ -// DSN Construction Tests -// ============================================================================ - -func TestBuildDSN(t *testing.T) { - tests := []struct { - name string - input string - wantExact string - wantRegex string - }{ - { - name: "memory", - input: ":memory:", - wantRegex: `^file:memdb\d+\?mode=memory&cache=shared&_pragma=busy_timeout\(5000\)&_pragma=foreign_keys\(1\)&_pragma=journal_mode\(WAL\)$`, - }, - { - name: "plain path", - input: "/data/scion.db", - wantExact: "file:/data/scion.db?_pragma=busy_timeout(5000)&_pragma=foreign_keys(1)&_pragma=journal_mode(WAL)", - }, - { - name: "file URI without params", - input: "file:/data/scion.db", - wantExact: "file:/data/scion.db?_pragma=busy_timeout(5000)&_pragma=foreign_keys(1)&_pragma=journal_mode(WAL)", - }, - { - name: "file URI with existing params", - input: "file:/data/scion.db?mode=rwc", - wantExact: "file:/data/scion.db?mode=rwc&_pragma=busy_timeout(5000)&_pragma=foreign_keys(1)&_pragma=journal_mode(WAL)", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := buildDSN(tt.input) - if tt.wantExact != "" { - assert.Equal(t, tt.wantExact, got) - } else { - assert.Regexp(t, tt.wantRegex, got) - } - }) - } -} - -func TestConcurrentReadDuringWrite(t *testing.T) { - s := setupTestStore(t) - ctx := context.Background() - - project := &store.Project{ - ID: api.NewUUID(), - Name: "Concurrency Test", - Slug: "concurrency-test", - } - require.NoError(t, s.CreateProject(ctx, project)) - - // Create several agents to write to - const agentCount = 10 - agentIDs := make([]string, agentCount) - for i := range agentCount { - slug := fmt.Sprintf("agent-%d", i) - agent := &store.Agent{ - ID: api.NewUUID(), - Slug: slug, - Name: slug, - ProjectID: project.ID, - } - require.NoError(t, s.CreateAgent(ctx, agent)) - agentIDs[i] = agent.ID - } - - // Simulate heartbeat: write status updates for all agents sequentially - writerDone := make(chan struct{}) - go func() { - defer close(writerDone) - for _, id := range agentIDs { - _ = s.UpdateAgentStatus(ctx, id, store.AgentStatusUpdate{ - Phase: "running", - Activity: "thinking", - Heartbeat: true, - }) - } - }() - - // Concurrent reader should not block behind the writer - readerDone := make(chan struct{}) - go func() { - defer close(readerDone) - _, _ = s.GetAgent(ctx, agentIDs[0]) - }() - - // Both should complete quickly — if the reader blocks behind all - // writes (old MaxOpenConns=1 behavior), this would be much slower. - select { - case <-readerDone: - case <-time.After(5 * time.Second): - t.Fatal("concurrent read timed out — likely blocked behind writes") - } - - <-writerDone -} diff --git a/pkg/store/storetest/domains.go b/pkg/store/storetest/domains.go new file mode 100644 index 000000000..84ec742bb --- /dev/null +++ b/pkg/store/storetest/domains.go @@ -0,0 +1,593 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package storetest + +import ( + "context" + "fmt" + "testing" + "time" + + "github.com/GoogleCloudPlatform/scion/pkg/store" + "github.com/google/uuid" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// missingID returns a syntactically valid identifier that is guaranteed not to +// exist in a freshly created store. It is a UUID so backends that parse IDs +// (e.g. the Ent adapter) accept it and report ErrNotFound rather than a parse +// error. +func missingID() string { + return uuid.NewString() +} + +// RunStoreSuite runs the full CRUD-parity suite for every currently supported +// domain against stores produced by factory. As new domains are ported to the +// shared store interface, add their Domain descriptor here and they are covered +// automatically across all backends. +func RunStoreSuite(t *testing.T, factory Factory) { + t.Helper() + RunDomain(t, factory, GroupDomain()) + RunDomain(t, factory, PolicyDomain()) + RunDomain(t, factory, GCPServiceAccountDomain()) + RunDomain(t, factory, SubscriptionTemplateDomain()) + RunDomain(t, factory, NotificationSubscriptionDomain()) + RunDomain(t, factory, ProjectDomain()) + RunDomain(t, factory, RuntimeBrokerDomain()) + RunDomain(t, factory, BrokerSecretDomain()) + RunDomain(t, factory, BrokerJoinTokenDomain()) + RunDomain(t, factory, TemplateDomain()) + RunDomain(t, factory, HarnessConfigDomain()) + RunDomain(t, factory, SecretDomain()) + RunDomain(t, factory, EnvVarDomain()) + RunDomain(t, factory, AgentDomain()) + RunDomain(t, factory, UserDomain()) + RunDomain(t, factory, AllowListDomain()) + RunDomain(t, factory, InviteCodeDomain()) + + // Agent optimistic locking is not expressible through the generic CRUD + // categories, so it gets a dedicated backend-agnostic check. + t.Run("agent/OptimisticLock", func(t *testing.T) { runAgentOptimisticLock(t, factory) }) +} + +func listFrom[T any](items []T, err error) (*store.ListResult[T], error) { + if err != nil { + return nil, err + } + return &store.ListResult[T]{Items: items, TotalCount: len(items)}, nil +} + +// GroupDomain describes the group entity for the CRUD-parity oracle. +func GroupDomain() Domain[store.Group] { + return Domain[store.Group]{ + Name: "group", + Make: func(seq int) *store.Group { + id := uuid.NewString() + return &store.Group{ + ID: id, + Name: fmt.Sprintf("Group %d", seq), + Slug: fmt.Sprintf("group-%d-%s", seq, id[:8]), + Description: fmt.Sprintf("description %d", seq), + GroupType: store.GroupTypeExplicit, + Labels: map[string]string{"seq": fmt.Sprintf("%d", seq)}, + } + }, + GetID: func(g *store.Group) string { return g.ID }, + Create: func(ctx context.Context, s store.Store, g *store.Group) error { + return s.CreateGroup(ctx, g) + }, + Get: func(ctx context.Context, s store.Store, id string) (*store.Group, error) { + return s.GetGroup(ctx, id) + }, + List: func(ctx context.Context, s store.Store, opts store.ListOptions) (*store.ListResult[store.Group], error) { + return s.ListGroups(ctx, store.GroupFilter{}, opts) + }, + VerifyEqual: func(t *testing.T, want, got *store.Group) { + assert.Equal(t, want.ID, got.ID) + assert.Equal(t, want.Name, got.Name) + assert.Equal(t, want.Slug, got.Slug) + assert.Equal(t, want.Description, got.Description) + assert.Equal(t, store.GroupTypeExplicit, got.GroupType) + assert.False(t, got.Created.IsZero(), "Created timestamp should be set") + }, + Mutate: func(g *store.Group) { + g.Name = "Renamed " + g.Name + g.Description = "updated description" + }, + Update: func(ctx context.Context, s store.Store, g *store.Group) error { + return s.UpdateGroup(ctx, g) + }, + VerifyMutated: func(t *testing.T, got *store.Group) { + assert.Contains(t, got.Name, "Renamed ") + assert.Equal(t, "updated description", got.Description) + }, + Delete: func(ctx context.Context, s store.Store, id string) error { + return s.DeleteGroup(ctx, id) + }, + // Groups are hard-deleted (no SoftDelete spec). + Filters: []FilterCase[store.Group]{ + { + Name: "ByGroupType", + Seed: func(t *testing.T, ctx context.Context, s store.Store) { + require.NoError(t, s.CreateGroup(ctx, &store.Group{ + ID: uuid.NewString(), Name: "Explicit", Slug: "explicit-" + uuid.NewString()[:8], + GroupType: store.GroupTypeExplicit, + })) + require.NoError(t, s.CreateGroup(ctx, &store.Group{ + ID: uuid.NewString(), Name: "Project Agents", Slug: "project-agents-" + uuid.NewString()[:8], + GroupType: store.GroupTypeProjectAgents, + })) + }, + List: func(ctx context.Context, s store.Store) (*store.ListResult[store.Group], error) { + return s.ListGroups(ctx, store.GroupFilter{GroupType: store.GroupTypeExplicit}, store.ListOptions{}) + }, + WantCount: 1, + }, + }, + } +} + +// PolicyDomain describes the policy entity for the CRUD-parity oracle. +func PolicyDomain() Domain[store.Policy] { + return Domain[store.Policy]{ + Name: "policy", + Make: func(seq int) *store.Policy { + return &store.Policy{ + ID: uuid.NewString(), + Name: fmt.Sprintf("Policy %d", seq), + Description: fmt.Sprintf("policy description %d", seq), + ScopeType: store.PolicyScopeHub, + ResourceType: "agent", + Actions: []string{"read"}, + Effect: store.PolicyEffectAllow, + Priority: seq, + } + }, + GetID: func(p *store.Policy) string { return p.ID }, + Create: func(ctx context.Context, s store.Store, p *store.Policy) error { + return s.CreatePolicy(ctx, p) + }, + Get: func(ctx context.Context, s store.Store, id string) (*store.Policy, error) { + return s.GetPolicy(ctx, id) + }, + List: func(ctx context.Context, s store.Store, opts store.ListOptions) (*store.ListResult[store.Policy], error) { + return s.ListPolicies(ctx, store.PolicyFilter{}, opts) + }, + VerifyEqual: func(t *testing.T, want, got *store.Policy) { + assert.Equal(t, want.ID, got.ID) + assert.Equal(t, want.Name, got.Name) + assert.Equal(t, want.ScopeType, got.ScopeType) + assert.Equal(t, want.ResourceType, got.ResourceType) + assert.Equal(t, want.Actions, got.Actions) + assert.Equal(t, want.Effect, got.Effect) + assert.False(t, got.Created.IsZero(), "Created timestamp should be set") + }, + Mutate: func(p *store.Policy) { + p.Name = "Renamed " + p.Name + p.Actions = []string{"read", "update"} + }, + Update: func(ctx context.Context, s store.Store, p *store.Policy) error { + return s.UpdatePolicy(ctx, p) + }, + VerifyMutated: func(t *testing.T, got *store.Policy) { + assert.Contains(t, got.Name, "Renamed ") + assert.Equal(t, []string{"read", "update"}, got.Actions) + }, + Delete: func(ctx context.Context, s store.Store, id string) error { + return s.DeletePolicy(ctx, id) + }, + // Policies are hard-deleted (no SoftDelete spec). + Filters: []FilterCase[store.Policy]{ + { + Name: "ByEffect", + Seed: func(t *testing.T, ctx context.Context, s store.Store) { + require.NoError(t, s.CreatePolicy(ctx, &store.Policy{ + ID: uuid.NewString(), Name: "Allow", ScopeType: store.PolicyScopeHub, + ResourceType: "*", Actions: []string{"*"}, Effect: store.PolicyEffectAllow, + })) + require.NoError(t, s.CreatePolicy(ctx, &store.Policy{ + ID: uuid.NewString(), Name: "Deny", ScopeType: store.PolicyScopeHub, + ResourceType: "*", Actions: []string{"*"}, Effect: store.PolicyEffectDeny, + })) + }, + List: func(ctx context.Context, s store.Store) (*store.ListResult[store.Policy], error) { + return s.ListPolicies(ctx, store.PolicyFilter{Effect: store.PolicyEffectDeny}, store.ListOptions{}) + }, + WantCount: 1, + }, + }, + } +} + +// GCPServiceAccountDomain describes the GCP service account entity for the +// CRUD-parity oracle. The store's List methods are unpaginated, so the generic +// List (pagination) category is omitted and listing is exercised via filters. +func GCPServiceAccountDomain() Domain[store.GCPServiceAccount] { + return Domain[store.GCPServiceAccount]{ + Name: "gcp_service_account", + Make: func(seq int) *store.GCPServiceAccount { + id := uuid.NewString() + return &store.GCPServiceAccount{ + ID: id, + Scope: "project", + ScopeID: uuid.NewString(), + Email: fmt.Sprintf("sa-%d-%s@proj.iam.gserviceaccount.com", seq, id[:8]), + ProjectID: uuid.NewString(), + DisplayName: fmt.Sprintf("SA %d", seq), + DefaultScopes: []string{"https://www.googleapis.com/auth/cloud-platform"}, + CreatedBy: "tester", + } + }, + GetID: func(sa *store.GCPServiceAccount) string { return sa.ID }, + Create: func(ctx context.Context, s store.Store, sa *store.GCPServiceAccount) error { + return s.CreateGCPServiceAccount(ctx, sa) + }, + Get: func(ctx context.Context, s store.Store, id string) (*store.GCPServiceAccount, error) { + return s.GetGCPServiceAccount(ctx, id) + }, + VerifyEqual: func(t *testing.T, want, got *store.GCPServiceAccount) { + assert.Equal(t, want.ID, got.ID) + assert.Equal(t, want.Email, got.Email) + assert.Equal(t, want.Scope, got.Scope) + assert.Equal(t, want.ScopeID, got.ScopeID) + assert.Equal(t, want.DefaultScopes, got.DefaultScopes) + assert.False(t, got.CreatedAt.IsZero(), "CreatedAt should be set") + }, + Mutate: func(sa *store.GCPServiceAccount) { + sa.DisplayName = "Renamed " + sa.DisplayName + sa.Verified = true + }, + Update: func(ctx context.Context, s store.Store, sa *store.GCPServiceAccount) error { + return s.UpdateGCPServiceAccount(ctx, sa) + }, + VerifyMutated: func(t *testing.T, got *store.GCPServiceAccount) { + assert.Contains(t, got.DisplayName, "Renamed ") + assert.True(t, got.Verified) + }, + Delete: func(ctx context.Context, s store.Store, id string) error { + return s.DeleteGCPServiceAccount(ctx, id) + }, + // GCP service accounts are hard-deleted (no SoftDelete spec). + Filters: []FilterCase[store.GCPServiceAccount]{ + { + Name: "ByManaged", + Seed: func(t *testing.T, ctx context.Context, s store.Store) { + require.NoError(t, s.CreateGCPServiceAccount(ctx, &store.GCPServiceAccount{ + ID: uuid.NewString(), Scope: "project", ScopeID: uuid.NewString(), + Email: "managed-" + uuid.NewString()[:8] + "@p.iam.gserviceaccount.com", + ProjectID: uuid.NewString(), Managed: true, CreatedBy: "t", + })) + require.NoError(t, s.CreateGCPServiceAccount(ctx, &store.GCPServiceAccount{ + ID: uuid.NewString(), Scope: "project", ScopeID: uuid.NewString(), + Email: "byosa-" + uuid.NewString()[:8] + "@p.iam.gserviceaccount.com", + ProjectID: uuid.NewString(), Managed: false, CreatedBy: "t", + })) + }, + List: func(ctx context.Context, s store.Store) (*store.ListResult[store.GCPServiceAccount], error) { + managed := true + return listFrom(s.ListGCPServiceAccounts(ctx, store.GCPServiceAccountFilter{Managed: &managed})) + }, + WantCount: 1, + }, + }, + } +} + +// SubscriptionTemplateDomain describes the subscription template entity for the +// CRUD-parity oracle. Templates have no update method and an unpaginated list, +// so only Create/Read/Delete plus a filter scenario are exercised. +func SubscriptionTemplateDomain() Domain[store.SubscriptionTemplate] { + return Domain[store.SubscriptionTemplate]{ + Name: "subscription_template", + Make: func(seq int) *store.SubscriptionTemplate { + id := uuid.NewString() + return &store.SubscriptionTemplate{ + ID: id, + Name: fmt.Sprintf("template-%d-%s", seq, id[:8]), + Scope: "project", + TriggerActivities: []string{"COMPLETED", "FAILED"}, + CreatedBy: "tester", + } + }, + GetID: func(tmpl *store.SubscriptionTemplate) string { return tmpl.ID }, + Create: func(ctx context.Context, s store.Store, tmpl *store.SubscriptionTemplate) error { + return s.CreateSubscriptionTemplate(ctx, tmpl) + }, + Get: func(ctx context.Context, s store.Store, id string) (*store.SubscriptionTemplate, error) { + return s.GetSubscriptionTemplate(ctx, id) + }, + VerifyEqual: func(t *testing.T, want, got *store.SubscriptionTemplate) { + assert.Equal(t, want.ID, got.ID) + assert.Equal(t, want.Name, got.Name) + assert.Equal(t, want.Scope, got.Scope) + assert.Equal(t, want.TriggerActivities, got.TriggerActivities) + }, + Delete: func(ctx context.Context, s store.Store, id string) error { + return s.DeleteSubscriptionTemplate(ctx, id) + }, + // Templates are hard-deleted (no SoftDelete spec). + Filters: []FilterCase[store.SubscriptionTemplate]{ + { + Name: "GlobalOnly", + Seed: func(t *testing.T, ctx context.Context, s store.Store) { + require.NoError(t, s.CreateSubscriptionTemplate(ctx, &store.SubscriptionTemplate{ + ID: uuid.NewString(), Name: "global-" + uuid.NewString()[:8], + Scope: "project", TriggerActivities: []string{"COMPLETED"}, CreatedBy: "t", + })) + require.NoError(t, s.CreateSubscriptionTemplate(ctx, &store.SubscriptionTemplate{ + ID: uuid.NewString(), Name: "scoped-" + uuid.NewString()[:8], + Scope: "project", TriggerActivities: []string{"FAILED"}, + ProjectID: uuid.NewString(), CreatedBy: "t", + })) + }, + List: func(ctx context.Context, s store.Store) (*store.ListResult[store.SubscriptionTemplate], error) { + return listFrom(s.ListSubscriptionTemplates(ctx, "")) + }, + WantCount: 1, + }, + }, + } +} + +// NotificationSubscriptionDomain describes the notification subscription entity +// for the CRUD-parity oracle. Project-scoped subscriptions are used so the +// fixtures do not depend on a pre-existing agent (agent-scoped subscriptions +// carry a foreign key to agents). The store's list methods are unpaginated, so +// the generic pagination category is omitted. +func NotificationSubscriptionDomain() Domain[store.NotificationSubscription] { + return Domain[store.NotificationSubscription]{ + Name: "notification_subscription", + Make: func(seq int) *store.NotificationSubscription { + return &store.NotificationSubscription{ + ID: uuid.NewString(), + Scope: store.SubscriptionScopeProject, + SubscriberType: "user", + SubscriberID: fmt.Sprintf("user-%d", seq), + ProjectID: uuid.NewString(), + TriggerActivities: []string{"COMPLETED"}, + CreatedBy: "tester", + } + }, + GetID: func(sub *store.NotificationSubscription) string { return sub.ID }, + Create: func(ctx context.Context, s store.Store, sub *store.NotificationSubscription) error { + return s.CreateNotificationSubscription(ctx, sub) + }, + Get: func(ctx context.Context, s store.Store, id string) (*store.NotificationSubscription, error) { + return s.GetNotificationSubscription(ctx, id) + }, + VerifyEqual: func(t *testing.T, want, got *store.NotificationSubscription) { + assert.Equal(t, want.ID, got.ID) + assert.Equal(t, want.Scope, got.Scope) + assert.Equal(t, want.SubscriberID, got.SubscriberID) + assert.Equal(t, want.TriggerActivities, got.TriggerActivities) + assert.False(t, got.CreatedAt.IsZero(), "CreatedAt should be set") + }, + Mutate: func(sub *store.NotificationSubscription) { + sub.TriggerActivities = []string{"COMPLETED", "FAILED"} + }, + Update: func(ctx context.Context, s store.Store, sub *store.NotificationSubscription) error { + return s.UpdateNotificationSubscriptionTriggers(ctx, sub.ID, sub.TriggerActivities) + }, + VerifyMutated: func(t *testing.T, got *store.NotificationSubscription) { + assert.Equal(t, []string{"COMPLETED", "FAILED"}, got.TriggerActivities) + }, + Delete: func(ctx context.Context, s store.Store, id string) error { + return s.DeleteNotificationSubscription(ctx, id) + }, + // Notification subscriptions are hard-deleted (no SoftDelete spec). + Filters: []FilterCase[store.NotificationSubscription]{ + { + Name: "ByProjectScope", + Seed: func(t *testing.T, ctx context.Context, s store.Store) { + projectID := uuid.NewString() + require.NoError(t, s.CreateNotificationSubscription(ctx, &store.NotificationSubscription{ + ID: uuid.NewString(), Scope: store.SubscriptionScopeProject, + SubscriberType: "user", SubscriberID: "u1", ProjectID: projectID, + TriggerActivities: []string{"COMPLETED"}, CreatedBy: "t", + })) + require.NoError(t, s.CreateNotificationSubscription(ctx, &store.NotificationSubscription{ + ID: uuid.NewString(), Scope: store.SubscriptionScopeProject, + SubscriberType: "user", SubscriberID: "u2", ProjectID: projectID, + TriggerActivities: []string{"FAILED"}, CreatedBy: "t", + })) + }, + List: func(ctx context.Context, s store.Store) (*store.ListResult[store.NotificationSubscription], error) { + // Both seeded subscriptions are project-scoped under distinct + // projects; query project scope for the first project only. + all, err := s.GetSubscriptionsForSubscriber(ctx, "user", "u1") + return listFrom(all, err) + }, + WantCount: 1, + }, + }, + } +} + +// agentDomainProjectID is the project every agent oracle entity references. It +// is seeded by AgentDomain.Prepare so the required project foreign key resolves +// across backends. +const agentDomainProjectID = "30000000-0000-0000-0000-0000000000d1" + +// seedAgentProject creates the shared project agents reference. It is called +// once per fresh store before the agent categories run. +func seedAgentProject(t *testing.T, ctx context.Context, s store.Store) { + t.Helper() + require.NoError(t, s.CreateProject(ctx, &store.Project{ + ID: agentDomainProjectID, + Name: "agent-oracle-project", + Slug: "agent-oracle-" + agentDomainProjectID[:8], + Visibility: "private", + })) +} + +// newOracleAgent builds a minimal valid agent referencing the seeded project. +func newOracleAgent(slug string) *store.Agent { + id := uuid.NewString() + return &store.Agent{ + ID: id, + Slug: slug + "-" + id[:8], + Name: slug, + Template: "default", + ProjectID: agentDomainProjectID, + Phase: "running", + Visibility: "private", + } +} + +// seedLiveAndDeleted inserts one live agent and one soft-deleted agent. +func seedLiveAndDeleted(t *testing.T, ctx context.Context, s store.Store) { + t.Helper() + live := newOracleAgent("live") + require.NoError(t, s.CreateAgent(ctx, live)) + + gone := newOracleAgent("gone") + require.NoError(t, s.CreateAgent(ctx, gone)) + gone.DeletedAt = time.Now() + require.NoError(t, s.UpdateAgent(ctx, gone)) +} + +// AgentDomain describes the agent entity for the CRUD-parity oracle. Beyond the +// standard categories it covers the agent-specific behaviors that must hold +// identically across backends: the ancestry membership filter, soft-delete +// exclusion, and (via runAgentOptimisticLock) state_version conflict handling. +func AgentDomain() Domain[store.Agent] { + return Domain[store.Agent]{ + Name: "agent", + Prepare: func(t *testing.T, ctx context.Context, s store.Store) { + seedAgentProject(t, ctx, s) + }, + Make: func(seq int) *store.Agent { + id := uuid.NewString() + return &store.Agent{ + ID: id, + Slug: fmt.Sprintf("agent-%d-%s", seq, id[:8]), + Name: fmt.Sprintf("Agent %d", seq), + Template: "default", + ProjectID: agentDomainProjectID, + Phase: "running", + Activity: "thinking", + Visibility: "private", + Labels: map[string]string{"seq": fmt.Sprintf("%d", seq)}, + } + }, + GetID: func(a *store.Agent) string { return a.ID }, + Create: func(ctx context.Context, s store.Store, a *store.Agent) error { + return s.CreateAgent(ctx, a) + }, + Get: func(ctx context.Context, s store.Store, id string) (*store.Agent, error) { + return s.GetAgent(ctx, id) + }, + List: func(ctx context.Context, s store.Store, opts store.ListOptions) (*store.ListResult[store.Agent], error) { + return s.ListAgents(ctx, store.AgentFilter{}, opts) + }, + VerifyEqual: func(t *testing.T, want, got *store.Agent) { + assert.Equal(t, want.ID, got.ID) + assert.Equal(t, want.Slug, got.Slug) + assert.Equal(t, want.Name, got.Name) + assert.Equal(t, want.ProjectID, got.ProjectID) + assert.Equal(t, want.Phase, got.Phase) + assert.Equal(t, int64(1), got.StateVersion, "CreateAgent should initialize state_version to 1") + assert.False(t, got.Created.IsZero(), "Created timestamp should be set") + }, + Mutate: func(a *store.Agent) { + a.Name = "Renamed " + a.Name + a.Phase = "stopped" + }, + Update: func(ctx context.Context, s store.Store, a *store.Agent) error { + return s.UpdateAgent(ctx, a) + }, + VerifyMutated: func(t *testing.T, got *store.Agent) { + assert.Contains(t, got.Name, "Renamed ") + assert.Equal(t, "stopped", got.Phase) + assert.Equal(t, int64(2), got.StateVersion, "UpdateAgent should bump state_version") + }, + // DeleteAgent is a hard delete; soft-delete (deleted_at via UpdateAgent) + // is covered by the filter cases below. + Delete: func(ctx context.Context, s store.Store, id string) error { + return s.DeleteAgent(ctx, id) + }, + Filters: []FilterCase[store.Agent]{ + { + // Ancestry membership: only agents whose ancestry chain contains + // the queried principal are returned. + Name: "ByAncestor", + Seed: func(t *testing.T, ctx context.Context, s store.Store) { + child := newOracleAgent("child") + child.Ancestry = []string{"root-user", "mid-agent"} + require.NoError(t, s.CreateAgent(ctx, child)) + + sibling := newOracleAgent("sibling") + sibling.Ancestry = []string{"root-user"} + require.NoError(t, s.CreateAgent(ctx, sibling)) + + orphan := newOracleAgent("orphan") + require.NoError(t, s.CreateAgent(ctx, orphan)) + }, + List: func(ctx context.Context, s store.Store) (*store.ListResult[store.Agent], error) { + return s.ListAgents(ctx, store.AgentFilter{AncestorID: "root-user"}, store.ListOptions{}) + }, + WantCount: 2, + }, + { + // Soft-deleted agents are excluded from the default listing. + Name: "ExcludeSoftDeleted", + Seed: seedLiveAndDeleted, + List: func(ctx context.Context, s store.Store) (*store.ListResult[store.Agent], error) { + return s.ListAgents(ctx, store.AgentFilter{}, store.ListOptions{}) + }, + WantCount: 1, + }, + { + // ... but reappear when explicitly included. + Name: "IncludeSoftDeleted", + Seed: seedLiveAndDeleted, + List: func(ctx context.Context, s store.Store) (*store.ListResult[store.Agent], error) { + return s.ListAgents(ctx, store.AgentFilter{IncludeDeleted: true}, store.ListOptions{}) + }, + WantCount: 2, + }, + }, + } +} + +// runAgentOptimisticLock verifies that a stale UpdateAgent (one carrying an +// out-of-date StateVersion) is rejected with ErrVersionConflict rather than +// silently overwriting a concurrent winner. +func runAgentOptimisticLock(t *testing.T, factory Factory) { + ctx := context.Background() + s := factory(t) + seedAgentProject(t, ctx, s) + + a := newOracleAgent("locked") + require.NoError(t, s.CreateAgent(ctx, a)) + + first, err := s.GetAgent(ctx, a.ID) + require.NoError(t, err) + second, err := s.GetAgent(ctx, a.ID) + require.NoError(t, err) + + // First writer wins, advancing the version. + first.Name = "Winner" + require.NoError(t, s.UpdateAgent(ctx, first)) + + // Second writer holds the now-stale version and must conflict. + second.Name = "Loser" + assert.ErrorIs(t, s.UpdateAgent(ctx, second), store.ErrVersionConflict) + + final, err := s.GetAgent(ctx, a.ID) + require.NoError(t, err) + assert.Equal(t, "Winner", final.Name) +} diff --git a/pkg/store/storetest/domains_project_broker.go b/pkg/store/storetest/domains_project_broker.go new file mode 100644 index 000000000..9af66c8dc --- /dev/null +++ b/pkg/store/storetest/domains_project_broker.go @@ -0,0 +1,258 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package storetest + +import ( + "context" + "fmt" + "testing" + "time" + + "github.com/GoogleCloudPlatform/scion/pkg/store" + "github.com/google/uuid" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// ProjectDomain describes the project (grove) entity for the CRUD-parity oracle. +func ProjectDomain() Domain[store.Project] { + return Domain[store.Project]{ + Name: "project", + Make: func(seq int) *store.Project { + id := uuid.NewString() + return &store.Project{ + ID: id, + Name: fmt.Sprintf("Project %d", seq), + Slug: fmt.Sprintf("project-%d-%s", seq, id[:8]), + Visibility: store.VisibilityPrivate, + Labels: map[string]string{"seq": fmt.Sprintf("%d", seq)}, + } + }, + GetID: func(p *store.Project) string { return p.ID }, + Create: func(ctx context.Context, s store.Store, p *store.Project) error { + return s.CreateProject(ctx, p) + }, + Get: func(ctx context.Context, s store.Store, id string) (*store.Project, error) { + return s.GetProject(ctx, id) + }, + List: func(ctx context.Context, s store.Store, opts store.ListOptions) (*store.ListResult[store.Project], error) { + return s.ListProjects(ctx, store.ProjectFilter{}, opts) + }, + VerifyEqual: func(t *testing.T, want, got *store.Project) { + assert.Equal(t, want.ID, got.ID) + assert.Equal(t, want.Name, got.Name) + assert.Equal(t, want.Slug, got.Slug) + assert.Equal(t, want.Visibility, got.Visibility) + assert.False(t, got.Created.IsZero(), "Created timestamp should be set") + }, + Mutate: func(p *store.Project) { + p.Name = "Renamed " + p.Name + p.Visibility = "public" + }, + Update: func(ctx context.Context, s store.Store, p *store.Project) error { + return s.UpdateProject(ctx, p) + }, + VerifyMutated: func(t *testing.T, got *store.Project) { + assert.Contains(t, got.Name, "Renamed ") + assert.Equal(t, "public", got.Visibility) + }, + Delete: func(ctx context.Context, s store.Store, id string) error { + return s.DeleteProject(ctx, id) + }, + Filters: []FilterCase[store.Project]{ + { + Name: "ByVisibility", + Seed: func(t *testing.T, ctx context.Context, s store.Store) { + require.NoError(t, s.CreateProject(ctx, &store.Project{ + ID: uuid.NewString(), Name: "Public", Slug: "public-" + uuid.NewString()[:8], Visibility: "public", + })) + require.NoError(t, s.CreateProject(ctx, &store.Project{ + ID: uuid.NewString(), Name: "Private", Slug: "private-" + uuid.NewString()[:8], Visibility: store.VisibilityPrivate, + })) + }, + List: func(ctx context.Context, s store.Store) (*store.ListResult[store.Project], error) { + return s.ListProjects(ctx, store.ProjectFilter{Visibility: "public"}, store.ListOptions{}) + }, + WantCount: 1, + }, + }, + } +} + +// RuntimeBrokerDomain describes the runtime broker entity for the oracle. +func RuntimeBrokerDomain() Domain[store.RuntimeBroker] { + return Domain[store.RuntimeBroker]{ + Name: "runtime_broker", + Make: func(seq int) *store.RuntimeBroker { + id := uuid.NewString() + return &store.RuntimeBroker{ + ID: id, + Name: fmt.Sprintf("Broker %d", seq), + Slug: fmt.Sprintf("broker-%d-%s", seq, id[:8]), + Version: "1.0.0", + Status: store.BrokerStatusOffline, + } + }, + GetID: func(b *store.RuntimeBroker) string { return b.ID }, + Create: func(ctx context.Context, s store.Store, b *store.RuntimeBroker) error { + return s.CreateRuntimeBroker(ctx, b) + }, + Get: func(ctx context.Context, s store.Store, id string) (*store.RuntimeBroker, error) { + return s.GetRuntimeBroker(ctx, id) + }, + List: func(ctx context.Context, s store.Store, opts store.ListOptions) (*store.ListResult[store.RuntimeBroker], error) { + return s.ListRuntimeBrokers(ctx, store.RuntimeBrokerFilter{}, opts) + }, + VerifyEqual: func(t *testing.T, want, got *store.RuntimeBroker) { + assert.Equal(t, want.ID, got.ID) + assert.Equal(t, want.Name, got.Name) + assert.Equal(t, want.Slug, got.Slug) + assert.Equal(t, want.Version, got.Version) + assert.False(t, got.Created.IsZero(), "Created timestamp should be set") + }, + Mutate: func(b *store.RuntimeBroker) { + b.Name = "Renamed " + b.Name + b.Version = "2.0.0" + b.Status = store.BrokerStatusOnline + }, + Update: func(ctx context.Context, s store.Store, b *store.RuntimeBroker) error { + return s.UpdateRuntimeBroker(ctx, b) + }, + VerifyMutated: func(t *testing.T, got *store.RuntimeBroker) { + assert.Contains(t, got.Name, "Renamed ") + assert.Equal(t, "2.0.0", got.Version) + }, + Delete: func(ctx context.Context, s store.Store, id string) error { + return s.DeleteRuntimeBroker(ctx, id) + }, + Filters: []FilterCase[store.RuntimeBroker]{ + { + Name: "ByStatus", + Seed: func(t *testing.T, ctx context.Context, s store.Store) { + require.NoError(t, s.CreateRuntimeBroker(ctx, &store.RuntimeBroker{ + ID: uuid.NewString(), Name: "Online", Slug: "online-" + uuid.NewString()[:8], Status: store.BrokerStatusOnline, + })) + require.NoError(t, s.CreateRuntimeBroker(ctx, &store.RuntimeBroker{ + ID: uuid.NewString(), Name: "Offline", Slug: "offline-" + uuid.NewString()[:8], Status: store.BrokerStatusOffline, + })) + }, + List: func(ctx context.Context, s store.Store) (*store.ListResult[store.RuntimeBroker], error) { + return s.ListRuntimeBrokers(ctx, store.RuntimeBrokerFilter{Status: store.BrokerStatusOnline}, store.ListOptions{}) + }, + WantCount: 1, + }, + }, + } +} + +// ensureBroker creates the runtime_brokers row a broker-scoped entity references +// via foreign key (broker_secrets / broker_join_tokens on the SQLite backend). +// It is idempotent: an already-existing broker is not an error. It keeps these +// domains self-contained without relying on a shared Prepare hook. +func ensureBroker(ctx context.Context, s store.Store, brokerID string) error { + err := s.CreateRuntimeBroker(ctx, &store.RuntimeBroker{ + ID: brokerID, + Name: "fk-broker-" + brokerID[:8], + Slug: "fk-broker-" + brokerID[:8], + }) + if err != nil && err != store.ErrAlreadyExists { + return err + } + return nil +} + +// BrokerSecretDomain describes the broker secret entity for the oracle. It has +// no List operation (one secret per broker, keyed on broker_id), so the +// pagination and filter categories are skipped. +func BrokerSecretDomain() Domain[store.BrokerSecret] { + return Domain[store.BrokerSecret]{ + Name: "broker_secret", + Make: func(seq int) *store.BrokerSecret { + return &store.BrokerSecret{ + BrokerID: uuid.NewString(), + SecretKey: []byte(fmt.Sprintf("hmac-key-%d", seq)), + Algorithm: store.BrokerSecretAlgorithmHMACSHA256, + Status: store.BrokerSecretStatusActive, + } + }, + GetID: func(b *store.BrokerSecret) string { return b.BrokerID }, + Create: func(ctx context.Context, s store.Store, b *store.BrokerSecret) error { + if err := ensureBroker(ctx, s, b.BrokerID); err != nil { + return err + } + return s.CreateBrokerSecret(ctx, b) + }, + Get: func(ctx context.Context, s store.Store, id string) (*store.BrokerSecret, error) { + return s.GetBrokerSecret(ctx, id) + }, + VerifyEqual: func(t *testing.T, want, got *store.BrokerSecret) { + assert.Equal(t, want.BrokerID, got.BrokerID) + assert.Equal(t, want.SecretKey, got.SecretKey) + assert.Equal(t, want.Algorithm, got.Algorithm) + assert.Equal(t, want.Status, got.Status) + assert.False(t, got.CreatedAt.IsZero(), "CreatedAt timestamp should be set") + }, + Mutate: func(b *store.BrokerSecret) { + b.SecretKey = []byte("rotated-key") + b.Status = store.BrokerSecretStatusDeprecated + }, + Update: func(ctx context.Context, s store.Store, b *store.BrokerSecret) error { + return s.UpdateBrokerSecret(ctx, b) + }, + VerifyMutated: func(t *testing.T, got *store.BrokerSecret) { + assert.Equal(t, []byte("rotated-key"), got.SecretKey) + assert.Equal(t, store.BrokerSecretStatusDeprecated, got.Status) + }, + Delete: func(ctx context.Context, s store.Store, id string) error { + return s.DeleteBrokerSecret(ctx, id) + }, + } +} + +// BrokerJoinTokenDomain describes the broker join token entity for the oracle. +// Join tokens are immutable (no Update) and have no List, so only the Create, +// Read and Delete categories apply. +func BrokerJoinTokenDomain() Domain[store.BrokerJoinToken] { + return Domain[store.BrokerJoinToken]{ + Name: "broker_join_token", + Make: func(seq int) *store.BrokerJoinToken { + return &store.BrokerJoinToken{ + BrokerID: uuid.NewString(), + TokenHash: fmt.Sprintf("token-hash-%d-%s", seq, uuid.NewString()), + ExpiresAt: time.Now().Add(time.Hour), + CreatedBy: uuid.NewString(), + } + }, + GetID: func(tok *store.BrokerJoinToken) string { return tok.BrokerID }, + Create: func(ctx context.Context, s store.Store, tok *store.BrokerJoinToken) error { + if err := ensureBroker(ctx, s, tok.BrokerID); err != nil { + return err + } + return s.CreateJoinToken(ctx, tok) + }, + Get: func(ctx context.Context, s store.Store, id string) (*store.BrokerJoinToken, error) { + return s.GetJoinTokenByBrokerID(ctx, id) + }, + VerifyEqual: func(t *testing.T, want, got *store.BrokerJoinToken) { + assert.Equal(t, want.BrokerID, got.BrokerID) + assert.Equal(t, want.TokenHash, got.TokenHash) + assert.Equal(t, want.CreatedBy, got.CreatedBy) + assert.False(t, got.CreatedAt.IsZero(), "CreatedAt timestamp should be set") + }, + Delete: func(ctx context.Context, s store.Store, id string) error { + return s.DeleteJoinToken(ctx, id) + }, + } +} diff --git a/pkg/store/storetest/domains_secret_template.go b/pkg/store/storetest/domains_secret_template.go new file mode 100644 index 000000000..6c650b322 --- /dev/null +++ b/pkg/store/storetest/domains_secret_template.go @@ -0,0 +1,317 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package storetest + +import ( + "context" + "fmt" + "testing" + + "github.com/GoogleCloudPlatform/scion/pkg/store" + "github.com/google/uuid" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// secretTestScopeID is a fixed scope identifier shared by all entities a single +// Secret/EnvVar domain run creates, so the harness's (key, scope, scope_id) +// lookups resolve consistently within one store. +const secretTestScopeID = "00000000-0000-0000-0000-0000000000aa" + +// listResultFrom wraps a plain slice from a non-paginated list method into a +// ListResult so it can satisfy a FilterCase. TotalCount mirrors the slice +// length, which is the contract the filter oracle checks. +func listResultFrom[T any](items []T, err error) (*store.ListResult[T], error) { + if err != nil { + return nil, err + } + return &store.ListResult[T]{Items: items, TotalCount: len(items)}, nil +} + +// TemplateDomain describes the template entity for the CRUD-parity oracle. +func TemplateDomain() Domain[store.Template] { + return Domain[store.Template]{ + Name: "template", + Make: func(seq int) *store.Template { + id := uuid.NewString() + return &store.Template{ + ID: id, + Name: fmt.Sprintf("Template %d", seq), + Slug: fmt.Sprintf("template-%d-%s", seq, id[:8]), + Harness: "claude", + Image: "img:latest", + Scope: store.TemplateScopeGlobal, + Visibility: "private", + Status: store.TemplateStatusActive, + ContentHash: fmt.Sprintf("hash-%d", seq), + } + }, + GetID: func(e *store.Template) string { return e.ID }, + Create: func(ctx context.Context, s store.Store, e *store.Template) error { + return s.CreateTemplate(ctx, e) + }, + Get: func(ctx context.Context, s store.Store, id string) (*store.Template, error) { + return s.GetTemplate(ctx, id) + }, + List: func(ctx context.Context, s store.Store, opts store.ListOptions) (*store.ListResult[store.Template], error) { + return s.ListTemplates(ctx, store.TemplateFilter{}, opts) + }, + VerifyEqual: func(t *testing.T, want, got *store.Template) { + assert.Equal(t, want.ID, got.ID) + assert.Equal(t, want.Name, got.Name) + assert.Equal(t, want.Slug, got.Slug) + assert.Equal(t, want.Harness, got.Harness) + assert.Equal(t, want.Scope, got.Scope) + assert.False(t, got.Created.IsZero(), "Created timestamp should be set") + }, + Mutate: func(e *store.Template) { + e.Name = "Renamed " + e.Name + e.Status = store.TemplateStatusArchived + }, + Update: func(ctx context.Context, s store.Store, e *store.Template) error { + return s.UpdateTemplate(ctx, e) + }, + VerifyMutated: func(t *testing.T, got *store.Template) { + assert.Contains(t, got.Name, "Renamed ") + assert.Equal(t, store.TemplateStatusArchived, got.Status) + }, + Delete: func(ctx context.Context, s store.Store, id string) error { + return s.DeleteTemplate(ctx, id) + }, + Filters: []FilterCase[store.Template]{ + { + Name: "ByHarness", + Seed: func(t *testing.T, ctx context.Context, s store.Store) { + require.NoError(t, s.CreateTemplate(ctx, &store.Template{ + ID: uuid.NewString(), Name: "Claude", Slug: "claude-" + uuid.NewString()[:8], + Harness: "claude", Scope: store.TemplateScopeGlobal, Status: store.TemplateStatusActive, + })) + require.NoError(t, s.CreateTemplate(ctx, &store.Template{ + ID: uuid.NewString(), Name: "Gemini", Slug: "gemini-" + uuid.NewString()[:8], + Harness: "gemini", Scope: store.TemplateScopeGlobal, Status: store.TemplateStatusActive, + })) + }, + List: func(ctx context.Context, s store.Store) (*store.ListResult[store.Template], error) { + return s.ListTemplates(ctx, store.TemplateFilter{Harness: "gemini"}, store.ListOptions{}) + }, + WantCount: 1, + }, + }, + } +} + +// HarnessConfigDomain describes the harness config entity for the CRUD-parity oracle. +func HarnessConfigDomain() Domain[store.HarnessConfig] { + return Domain[store.HarnessConfig]{ + Name: "harness_config", + Make: func(seq int) *store.HarnessConfig { + id := uuid.NewString() + return &store.HarnessConfig{ + ID: id, + Name: fmt.Sprintf("Harness %d", seq), + Slug: fmt.Sprintf("harness-%d-%s", seq, id[:8]), + Harness: "claude", + Scope: store.HarnessConfigScopeGlobal, + Visibility: "private", + Status: store.HarnessConfigStatusActive, + ContentHash: fmt.Sprintf("hash-%d", seq), + } + }, + GetID: func(e *store.HarnessConfig) string { return e.ID }, + Create: func(ctx context.Context, s store.Store, e *store.HarnessConfig) error { + return s.CreateHarnessConfig(ctx, e) + }, + Get: func(ctx context.Context, s store.Store, id string) (*store.HarnessConfig, error) { + return s.GetHarnessConfig(ctx, id) + }, + List: func(ctx context.Context, s store.Store, opts store.ListOptions) (*store.ListResult[store.HarnessConfig], error) { + return s.ListHarnessConfigs(ctx, store.HarnessConfigFilter{}, opts) + }, + VerifyEqual: func(t *testing.T, want, got *store.HarnessConfig) { + assert.Equal(t, want.ID, got.ID) + assert.Equal(t, want.Name, got.Name) + assert.Equal(t, want.Slug, got.Slug) + assert.Equal(t, want.Harness, got.Harness) + assert.Equal(t, want.Scope, got.Scope) + assert.False(t, got.Created.IsZero(), "Created timestamp should be set") + }, + Mutate: func(e *store.HarnessConfig) { + e.Name = "Renamed " + e.Name + e.Status = store.HarnessConfigStatusArchived + }, + Update: func(ctx context.Context, s store.Store, e *store.HarnessConfig) error { + return s.UpdateHarnessConfig(ctx, e) + }, + VerifyMutated: func(t *testing.T, got *store.HarnessConfig) { + assert.Contains(t, got.Name, "Renamed ") + assert.Equal(t, store.HarnessConfigStatusArchived, got.Status) + }, + Delete: func(ctx context.Context, s store.Store, id string) error { + return s.DeleteHarnessConfig(ctx, id) + }, + Filters: []FilterCase[store.HarnessConfig]{ + { + Name: "ByHarness", + Seed: func(t *testing.T, ctx context.Context, s store.Store) { + require.NoError(t, s.CreateHarnessConfig(ctx, &store.HarnessConfig{ + ID: uuid.NewString(), Name: "Claude", Slug: "claude-" + uuid.NewString()[:8], + Harness: "claude", Scope: store.HarnessConfigScopeGlobal, Status: store.HarnessConfigStatusActive, + })) + require.NoError(t, s.CreateHarnessConfig(ctx, &store.HarnessConfig{ + ID: uuid.NewString(), Name: "Gemini", Slug: "gemini-" + uuid.NewString()[:8], + Harness: "gemini", Scope: store.HarnessConfigScopeGlobal, Status: store.HarnessConfigStatusActive, + })) + }, + List: func(ctx context.Context, s store.Store) (*store.ListResult[store.HarnessConfig], error) { + return s.ListHarnessConfigs(ctx, store.HarnessConfigFilter{Harness: "gemini"}, store.ListOptions{}) + }, + WantCount: 1, + }, + }, + } +} + +// SecretDomain describes the secret entity for the CRUD-parity oracle. Secrets +// are addressed by (key, scope, scope_id) rather than a surrogate ID, so the +// harness's id parameter carries the key and a fixed scope/scope_id pair is used +// throughout a run. Listing is non-paginated, so only the filter category (not +// pagination) is exercised here. +func SecretDomain() Domain[store.Secret] { + return Domain[store.Secret]{ + Name: "secret", + Make: func(seq int) *store.Secret { + return &store.Secret{ + ID: uuid.NewString(), + Key: fmt.Sprintf("SECRET_%d", seq), + EncryptedValue: fmt.Sprintf("enc-%d", seq), + Scope: store.ScopeUser, + ScopeID: secretTestScopeID, + Description: fmt.Sprintf("secret %d", seq), + } + }, + GetID: func(e *store.Secret) string { return e.Key }, + Create: func(ctx context.Context, s store.Store, e *store.Secret) error { + return s.CreateSecret(ctx, e) + }, + Get: func(ctx context.Context, s store.Store, id string) (*store.Secret, error) { + return s.GetSecret(ctx, id, store.ScopeUser, secretTestScopeID) + }, + VerifyEqual: func(t *testing.T, want, got *store.Secret) { + assert.Equal(t, want.Key, got.Key) + assert.Equal(t, want.EncryptedValue, got.EncryptedValue) + assert.Equal(t, want.Scope, got.Scope) + assert.Equal(t, 1, got.Version, "new secret starts at version 1") + assert.False(t, got.Created.IsZero(), "Created timestamp should be set") + }, + Mutate: func(e *store.Secret) { + e.EncryptedValue = "rotated" + e.Description = "changed" + }, + Update: func(ctx context.Context, s store.Store, e *store.Secret) error { + return s.UpdateSecret(ctx, e) + }, + VerifyMutated: func(t *testing.T, got *store.Secret) { + assert.Equal(t, "rotated", got.EncryptedValue) + assert.GreaterOrEqual(t, got.Version, 2, "update should bump version") + }, + Delete: func(ctx context.Context, s store.Store, id string) error { + return s.DeleteSecret(ctx, id, store.ScopeUser, secretTestScopeID) + }, + Filters: []FilterCase[store.Secret]{ + { + Name: "ByType", + Seed: func(t *testing.T, ctx context.Context, s store.Store) { + require.NoError(t, s.CreateSecret(ctx, &store.Secret{ + ID: uuid.NewString(), Key: "ENV_SECRET", EncryptedValue: "v", + SecretType: store.SecretTypeEnvironment, Scope: store.ScopeUser, ScopeID: secretTestScopeID, + })) + require.NoError(t, s.CreateSecret(ctx, &store.Secret{ + ID: uuid.NewString(), Key: "FILE_SECRET", EncryptedValue: "v", + SecretType: store.SecretTypeFile, Target: "/etc/x", Scope: store.ScopeUser, ScopeID: secretTestScopeID, + })) + }, + List: func(ctx context.Context, s store.Store) (*store.ListResult[store.Secret], error) { + return listResultFrom(s.ListSecrets(ctx, store.SecretFilter{ + Scope: store.ScopeUser, ScopeID: secretTestScopeID, Type: store.SecretTypeFile, + })) + }, + WantCount: 1, + }, + }, + } +} + +// EnvVarDomain describes the env var entity for the CRUD-parity oracle. Like +// secrets, env vars are addressed by (key, scope, scope_id); see SecretDomain. +func EnvVarDomain() Domain[store.EnvVar] { + return Domain[store.EnvVar]{ + Name: "env_var", + Make: func(seq int) *store.EnvVar { + return &store.EnvVar{ + ID: uuid.NewString(), + Key: fmt.Sprintf("ENV_%d", seq), + Value: fmt.Sprintf("val-%d", seq), + Scope: store.ScopeUser, + ScopeID: secretTestScopeID, + InjectionMode: store.InjectionModeAsNeeded, + Description: fmt.Sprintf("env %d", seq), + } + }, + GetID: func(e *store.EnvVar) string { return e.Key }, + Create: func(ctx context.Context, s store.Store, e *store.EnvVar) error { + return s.CreateEnvVar(ctx, e) + }, + Get: func(ctx context.Context, s store.Store, id string) (*store.EnvVar, error) { + return s.GetEnvVar(ctx, id, store.ScopeUser, secretTestScopeID) + }, + VerifyEqual: func(t *testing.T, want, got *store.EnvVar) { + assert.Equal(t, want.Key, got.Key) + assert.Equal(t, want.Value, got.Value) + assert.Equal(t, want.Scope, got.Scope) + assert.False(t, got.Created.IsZero(), "Created timestamp should be set") + }, + Mutate: func(e *store.EnvVar) { + e.Value = "updated" + }, + Update: func(ctx context.Context, s store.Store, e *store.EnvVar) error { + return s.UpdateEnvVar(ctx, e) + }, + VerifyMutated: func(t *testing.T, got *store.EnvVar) { + assert.Equal(t, "updated", got.Value) + }, + Delete: func(ctx context.Context, s store.Store, id string) error { + return s.DeleteEnvVar(ctx, id, store.ScopeUser, secretTestScopeID) + }, + Filters: []FilterCase[store.EnvVar]{ + { + Name: "ByKey", + Seed: func(t *testing.T, ctx context.Context, s store.Store) { + require.NoError(t, s.CreateEnvVar(ctx, &store.EnvVar{ + ID: uuid.NewString(), Key: "KEEP", Value: "v", Scope: store.ScopeUser, ScopeID: secretTestScopeID, + })) + require.NoError(t, s.CreateEnvVar(ctx, &store.EnvVar{ + ID: uuid.NewString(), Key: "OTHER", Value: "v", Scope: store.ScopeUser, ScopeID: secretTestScopeID, + })) + }, + List: func(ctx context.Context, s store.Store) (*store.ListResult[store.EnvVar], error) { + return listResultFrom(s.ListEnvVars(ctx, store.EnvVarFilter{ + Scope: store.ScopeUser, ScopeID: secretTestScopeID, Key: "KEEP", + })) + }, + WantCount: 1, + }, + }, + } +} diff --git a/pkg/store/storetest/domains_user.go b/pkg/store/storetest/domains_user.go new file mode 100644 index 000000000..94dd3a263 --- /dev/null +++ b/pkg/store/storetest/domains_user.go @@ -0,0 +1,201 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package storetest + +import ( + "context" + "fmt" + "testing" + "time" + + "github.com/GoogleCloudPlatform/scion/pkg/store" + "github.com/google/uuid" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// UserDomain describes the user entity for the CRUD-parity oracle. +// +// Add `RunDomain(t, factory, UserDomain())` to RunStoreSuite (in domains.go) +// to cover it across all backends. It is kept in this separate file so the +// user/allowlist port can land without contending on domains.go. +func UserDomain() Domain[store.User] { + return Domain[store.User]{ + Name: "user", + Make: func(seq int) *store.User { + id := uuid.NewString() + return &store.User{ + ID: id, + Email: fmt.Sprintf("user-%d-%s@example.com", seq, id[:8]), + DisplayName: fmt.Sprintf("User %d", seq), + Role: store.UserRoleMember, + Status: "active", + } + }, + GetID: func(u *store.User) string { return u.ID }, + Create: func(ctx context.Context, s store.Store, u *store.User) error { + return s.CreateUser(ctx, u) + }, + Get: func(ctx context.Context, s store.Store, id string) (*store.User, error) { + return s.GetUser(ctx, id) + }, + List: func(ctx context.Context, s store.Store, opts store.ListOptions) (*store.ListResult[store.User], error) { + return s.ListUsers(ctx, store.UserFilter{}, opts) + }, + VerifyEqual: func(t *testing.T, want, got *store.User) { + assert.Equal(t, want.ID, got.ID) + assert.Equal(t, want.Email, got.Email) + assert.Equal(t, want.DisplayName, got.DisplayName) + assert.Equal(t, want.Role, got.Role) + assert.Equal(t, want.Status, got.Status) + assert.False(t, got.Created.IsZero(), "Created timestamp should be set") + }, + Mutate: func(u *store.User) { + u.DisplayName = "Renamed " + u.DisplayName + u.Role = store.UserRoleAdmin + }, + Update: func(ctx context.Context, s store.Store, u *store.User) error { + return s.UpdateUser(ctx, u) + }, + VerifyMutated: func(t *testing.T, got *store.User) { + assert.Contains(t, got.DisplayName, "Renamed ") + assert.Equal(t, store.UserRoleAdmin, got.Role) + }, + Delete: func(ctx context.Context, s store.Store, id string) error { + return s.DeleteUser(ctx, id) + }, + // Users are hard-deleted (no SoftDelete spec). + Filters: []FilterCase[store.User]{ + { + Name: "ByRole", + Seed: func(t *testing.T, ctx context.Context, s store.Store) { + require.NoError(t, s.CreateUser(ctx, &store.User{ + ID: uuid.NewString(), Email: "admin-" + uuid.NewString()[:8] + "@example.com", + DisplayName: "Admin", Role: store.UserRoleAdmin, Status: "active", + })) + require.NoError(t, s.CreateUser(ctx, &store.User{ + ID: uuid.NewString(), Email: "member-" + uuid.NewString()[:8] + "@example.com", + DisplayName: "Member", Role: store.UserRoleMember, Status: "active", + })) + }, + List: func(ctx context.Context, s store.Store) (*store.ListResult[store.User], error) { + return s.ListUsers(ctx, store.UserFilter{Role: store.UserRoleAdmin}, store.ListOptions{}) + }, + WantCount: 1, + }, + { + Name: "ByStatus", + Seed: func(t *testing.T, ctx context.Context, s store.Store) { + require.NoError(t, s.CreateUser(ctx, &store.User{ + ID: uuid.NewString(), Email: "active-" + uuid.NewString()[:8] + "@example.com", + DisplayName: "Active", Role: store.UserRoleMember, Status: "active", + })) + require.NoError(t, s.CreateUser(ctx, &store.User{ + ID: uuid.NewString(), Email: "suspended-" + uuid.NewString()[:8] + "@example.com", + DisplayName: "Suspended", Role: store.UserRoleMember, Status: "suspended", + })) + }, + List: func(ctx context.Context, s store.Store) (*store.ListResult[store.User], error) { + return s.ListUsers(ctx, store.UserFilter{Status: "suspended"}, store.ListOptions{}) + }, + WantCount: 1, + }, + }, + } +} + +// AllowListDomain describes the email allow-list entry for the CRUD-parity +// oracle. The allow list is keyed by email rather than ID: Get and Delete +// operate on the (normalized) email address, so GetID returns the email. +func AllowListDomain() Domain[store.AllowListEntry] { + return Domain[store.AllowListEntry]{ + Name: "allowlist", + Make: func(seq int) *store.AllowListEntry { + id := uuid.NewString() + return &store.AllowListEntry{ + ID: id, + Email: fmt.Sprintf("allow-%d-%s@example.com", seq, id[:8]), + Note: fmt.Sprintf("note %d", seq), + AddedBy: "admin", + } + }, + GetID: func(e *store.AllowListEntry) string { return e.Email }, + Create: func(ctx context.Context, s store.Store, e *store.AllowListEntry) error { + return s.AddAllowListEntry(ctx, e) + }, + Get: func(ctx context.Context, s store.Store, email string) (*store.AllowListEntry, error) { + return s.GetAllowListEntry(ctx, email) + }, + List: func(ctx context.Context, s store.Store, opts store.ListOptions) (*store.ListResult[store.AllowListEntry], error) { + return s.ListAllowListEntries(ctx, opts) + }, + VerifyEqual: func(t *testing.T, want, got *store.AllowListEntry) { + assert.Equal(t, want.ID, got.ID) + assert.Equal(t, want.Email, got.Email) + assert.Equal(t, want.Note, got.Note) + assert.Equal(t, want.AddedBy, got.AddedBy) + assert.False(t, got.Created.IsZero(), "Created timestamp should be set") + }, + // AllowListStore has no general-purpose update (only invite-id linking), + // so the Update category is skipped. + Delete: func(ctx context.Context, s store.Store, email string) error { + return s.RemoveAllowListEntry(ctx, email) + }, + // Allow-list entries are hard-deleted (no SoftDelete spec). + } +} + +// InviteCodeDomain describes the invite-code entity for the CRUD-parity oracle. +func InviteCodeDomain() Domain[store.InviteCode] { + return Domain[store.InviteCode]{ + Name: "invitecode", + Make: func(seq int) *store.InviteCode { + id := uuid.NewString() + return &store.InviteCode{ + ID: id, + CodeHash: fmt.Sprintf("hash-%d-%s", seq, id), + CodePrefix: fmt.Sprintf("scion_in%d", seq), + MaxUses: 5, + UseCount: 0, + ExpiresAt: time.Now().Add(24 * time.Hour), + CreatedBy: "admin", + Note: fmt.Sprintf("invite %d", seq), + } + }, + GetID: func(i *store.InviteCode) string { return i.ID }, + Create: func(ctx context.Context, s store.Store, i *store.InviteCode) error { + return s.CreateInviteCode(ctx, i) + }, + Get: func(ctx context.Context, s store.Store, id string) (*store.InviteCode, error) { + return s.GetInviteCode(ctx, id) + }, + List: func(ctx context.Context, s store.Store, opts store.ListOptions) (*store.ListResult[store.InviteCode], error) { + return s.ListInviteCodes(ctx, opts) + }, + VerifyEqual: func(t *testing.T, want, got *store.InviteCode) { + assert.Equal(t, want.ID, got.ID) + assert.Equal(t, want.CodePrefix, got.CodePrefix) + assert.Equal(t, want.MaxUses, got.MaxUses) + assert.Equal(t, want.CreatedBy, got.CreatedBy) + assert.False(t, got.Created.IsZero(), "Created timestamp should be set") + }, + // InviteCodeStore exposes targeted mutators (revoke, increment) rather + // than a general update, so the Update category is skipped. + Delete: func(ctx context.Context, s store.Store, id string) error { + return s.DeleteInviteCode(ctx, id) + }, + // Invite codes are hard-deleted (no SoftDelete spec). + } +} diff --git a/pkg/store/storetest/main_test.go b/pkg/store/storetest/main_test.go new file mode 100644 index 000000000..063bf8bba --- /dev/null +++ b/pkg/store/storetest/main_test.go @@ -0,0 +1,34 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build !no_sqlite + +package storetest_test + +import ( + "os" + "testing" + + "github.com/GoogleCloudPlatform/scion/pkg/store/enttest" +) + +// TestMain wires the enttest backend lifecycle so the Postgres integration +// backend can create and drop its per-package ephemeral database. Both calls are +// no-ops in the default SQLite build. +func TestMain(m *testing.M) { + enttest.MainSetup() + code := m.Run() + enttest.MainTeardown() + os.Exit(code) +} diff --git a/pkg/store/storetest/storetest.go b/pkg/store/storetest/storetest.go new file mode 100644 index 000000000..160402e07 --- /dev/null +++ b/pkg/store/storetest/storetest.go @@ -0,0 +1,296 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package storetest provides a backend-agnostic, table-driven CRUD-parity test +// oracle for implementations of store.Store. +// +// The harness is the cornerstone of the Postgres integration effort: every +// store backend (SQLite today, Postgres later) must produce identical +// observable results for the same operations. Rather than re-write the same +// CRUD assertions per backend, a test provides a Factory that returns a fresh, +// migrated store.Store, and the harness drives the standardized test categories +// against it: +// +// - Create: insert an entity, verify the returned/persisted fields. +// - Read: get by ID, verify all fields; missing ID -> ErrNotFound. +// - Update: modify fields, verify the change is persisted. +// - Delete: delete an entity, verify it is excluded from the default +// list and Get returns ErrNotFound. For domains that support +// soft-delete, additionally verify it is still returned when +// deleted entities are explicitly included. +// - List-paginate: insert N entities, verify limit/pagination behavior. +// - List-filter: verify filtering returns only matching entities. +// +// Each entity type is described by a generic Domain[T]. Because every domain +// has different method signatures on store.Store, the Domain captures each +// operation as a closure. This keeps the harness itself entity-agnostic while +// letting new domains be onboarded by adding a single Domain descriptor (see +// domains.go for the group and policy examples). +package storetest + +import ( + "context" + "testing" + + "github.com/GoogleCloudPlatform/scion/pkg/store" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// Factory builds a fresh, migrated store.Store for a single test. It receives +// the subtest's *testing.T so the implementation can register cleanup (closing +// connections, dropping temp databases) via t.Cleanup. Each test category gets +// its own store so cases are isolated from one another. +type Factory func(t *testing.T) store.Store + +// Domain describes how to exercise one entity type (T) through the standardized +// CRUD test categories. Required fields must be set; optional fields enable +// additional categories when the entity supports them. +type Domain[T any] struct { + // Name is the entity name, used as the subtest group name (e.g. "group"). + Name string + + // Make builds a fresh, valid entity. seq is a monotonically increasing + // counter the implementation should weave into unique fields (slug, name) + // so that many entities can be created in the same store without collisions. + Make func(seq int) *T + + // GetID returns the primary identifier used for Get/Delete. + GetID func(*T) string + + // Prepare, when non-nil, runs once against each fresh store before a test + // category exercises it. It seeds prerequisite rows that entities of this + // domain depend on (e.g. the project an agent references via a required + // foreign key). It must be idempotent with respect to a fresh store. + Prepare func(t *testing.T, ctx context.Context, s store.Store) + + // Create persists a new entity. + Create func(ctx context.Context, s store.Store, e *T) error + + // Get retrieves an entity by ID. It must return store.ErrNotFound when the + // entity does not exist. + Get func(ctx context.Context, s store.Store, id string) (*T, error) + + // List returns entities honoring ListOptions (in particular Limit). For the + // Delete and Paginate categories this is the default, non-filtered listing. + List func(ctx context.Context, s store.Store, opts store.ListOptions) (*store.ListResult[T], error) + + // VerifyEqual asserts that a freshly-read entity (got) matches the one that + // was created (want). Implementations compare the fields they care about. + VerifyEqual func(t *testing.T, want, got *T) + + // --- Optional: Update category --- + + // Mutate applies an in-place modification used to verify Update persists. + // When nil (together with Update), the Update category is skipped. + Mutate func(e *T) + + // Update persists modifications to an existing entity. + Update func(ctx context.Context, s store.Store, e *T) error + + // VerifyMutated asserts that the change applied by Mutate is present on a + // freshly-read entity. + VerifyMutated func(t *testing.T, got *T) + + // --- Optional: Delete category --- + + // Delete removes an entity by ID. When nil, the Delete category is skipped. + Delete func(ctx context.Context, s store.Store, id string) error + + // SoftDelete, when non-nil, marks the domain as soft-deleting: after Delete + // the entity is excluded from the default List but must still be returned + // when deleted entities are explicitly included. + SoftDelete *SoftDeleteSpec[T] + + // --- Optional: List-filter category --- + + // Filters enumerates filter scenarios to verify. When empty, the + // List-filter category is skipped. + Filters []FilterCase[T] +} + +// SoftDeleteSpec captures the extra behavior of domains that soft-delete rather +// than hard-delete. ListIncludeDeleted must list entities including any that +// have been soft-deleted. +type SoftDeleteSpec[T any] struct { + ListIncludeDeleted func(ctx context.Context, s store.Store, opts store.ListOptions) (*store.ListResult[T], error) +} + +// FilterCase describes one List-filter scenario. Seed inserts a known mix of +// entities into a fresh store; List applies the filter under test; WantCount is +// the number of entities expected to match. +type FilterCase[T any] struct { + Name string + Seed func(t *testing.T, ctx context.Context, s store.Store) + List func(ctx context.Context, s store.Store) (*store.ListResult[T], error) + WantCount int +} + +// RunDomain runs every applicable test category for a single domain against +// stores produced by factory. Each category obtains its own fresh store. +func RunDomain[T any](t *testing.T, factory Factory, d Domain[T]) { + t.Helper() + t.Run(d.Name, func(t *testing.T) { + t.Run("Create", func(t *testing.T) { testCreate(t, factory, d) }) + t.Run("Read", func(t *testing.T) { testRead(t, factory, d) }) + + if d.Update != nil && d.Mutate != nil { + t.Run("Update", func(t *testing.T) { testUpdate(t, factory, d) }) + } + if d.Delete != nil { + t.Run("Delete", func(t *testing.T) { testDelete(t, factory, d) }) + } + if d.List != nil { + t.Run("ListPaginate", func(t *testing.T) { testPaginate(t, factory, d) }) + } + if len(d.Filters) > 0 { + t.Run("ListFilter", func(t *testing.T) { testFilter(t, factory, d) }) + } + }) +} + +// prepareStore runs the domain's Prepare hook (if any) against a fresh store. +func prepareStore[T any](t *testing.T, ctx context.Context, s store.Store, d Domain[T]) { + if d.Prepare != nil { + d.Prepare(t, ctx, s) + } +} + +func testCreate[T any](t *testing.T, factory Factory, d Domain[T]) { + ctx := context.Background() + s := factory(t) + prepareStore(t, ctx, s, d) + + e := d.Make(1) + require.NoError(t, d.Create(ctx, s, e), "Create should succeed") + + // The created entity must be retrievable and have the fields we set. + got, err := d.Get(ctx, s, d.GetID(e)) + require.NoError(t, err, "Get after Create should succeed") + d.VerifyEqual(t, e, got) +} + +func testRead[T any](t *testing.T, factory Factory, d Domain[T]) { + ctx := context.Background() + s := factory(t) + prepareStore(t, ctx, s, d) + + e := d.Make(1) + require.NoError(t, d.Create(ctx, s, e)) + + got, err := d.Get(ctx, s, d.GetID(e)) + require.NoError(t, err) + d.VerifyEqual(t, e, got) + + // A non-existent ID must surface as ErrNotFound across all backends. + _, err = d.Get(ctx, s, missingID()) + assert.ErrorIs(t, err, store.ErrNotFound, "Get of missing entity should return ErrNotFound") +} + +func testUpdate[T any](t *testing.T, factory Factory, d Domain[T]) { + ctx := context.Background() + s := factory(t) + prepareStore(t, ctx, s, d) + + e := d.Make(1) + require.NoError(t, d.Create(ctx, s, e)) + + d.Mutate(e) + require.NoError(t, d.Update(ctx, s, e), "Update should succeed") + + got, err := d.Get(ctx, s, d.GetID(e)) + require.NoError(t, err) + d.VerifyMutated(t, got) +} + +func testDelete[T any](t *testing.T, factory Factory, d Domain[T]) { + ctx := context.Background() + s := factory(t) + prepareStore(t, ctx, s, d) + + e := d.Make(1) + require.NoError(t, d.Create(ctx, s, e)) + id := d.GetID(e) + + require.NoError(t, d.Delete(ctx, s, id), "Delete should succeed") + + // Excluded from the default listing. + if d.List != nil { + res, err := d.List(ctx, s, store.ListOptions{}) + require.NoError(t, err) + assert.False(t, containsID(d, res.Items, id), "deleted entity must be excluded from default List") + } + + if d.SoftDelete != nil { + // Soft-deleted: Get still treats it as gone by default, but it must + // remain visible when deleted entities are explicitly included. + incl, err := d.SoftDelete.ListIncludeDeleted(ctx, s, store.ListOptions{}) + require.NoError(t, err) + assert.True(t, containsID(d, incl.Items, id), "soft-deleted entity must be returned with IncludeDeleted") + } else { + // Hard-deleted: Get must report ErrNotFound. + _, err := d.Get(ctx, s, id) + assert.ErrorIs(t, err, store.ErrNotFound, "Get of hard-deleted entity should return ErrNotFound") + } +} + +func testPaginate[T any](t *testing.T, factory Factory, d Domain[T]) { + ctx := context.Background() + s := factory(t) + prepareStore(t, ctx, s, d) + + const n = 5 + for i := 0; i < n; i++ { + require.NoError(t, d.Create(ctx, s, d.Make(i+1))) + } + + // No limit: all entities returned, TotalCount reflects the full set. + all, err := d.List(ctx, s, store.ListOptions{}) + require.NoError(t, err) + assert.Len(t, all.Items, n, "unbounded List should return every entity") + assert.Equal(t, n, all.TotalCount, "TotalCount should reflect the full set") + + // Limited: at most `limit` items, but TotalCount still reflects the full set. + const limit = 2 + page, err := d.List(ctx, s, store.ListOptions{Limit: limit}) + require.NoError(t, err) + assert.Len(t, page.Items, limit, "limited List should return exactly `limit` items") + assert.Equal(t, n, page.TotalCount, "TotalCount should be independent of Limit") +} + +func testFilter[T any](t *testing.T, factory Factory, d Domain[T]) { + ctx := context.Background() + for _, fc := range d.Filters { + fc := fc + t.Run(fc.Name, func(t *testing.T) { + s := factory(t) + prepareStore(t, ctx, s, d) + fc.Seed(t, ctx, s) + res, err := fc.List(ctx, s) + require.NoError(t, err) + assert.Equal(t, fc.WantCount, len(res.Items), "filtered List item count") + assert.Equal(t, fc.WantCount, res.TotalCount, "filtered List TotalCount") + }) + } +} + +// containsID reports whether any item in items has the given ID. +func containsID[T any](d Domain[T], items []T, id string) bool { + for i := range items { + if d.GetID(&items[i]) == id { + return true + } + } + return false +} diff --git a/pkg/store/storetest/storetest_test.go b/pkg/store/storetest/storetest_test.go new file mode 100644 index 000000000..d72ccd041 --- /dev/null +++ b/pkg/store/storetest/storetest_test.go @@ -0,0 +1,48 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build !no_sqlite + +package storetest_test + +import ( + "testing" + + "github.com/GoogleCloudPlatform/scion/pkg/store" + "github.com/GoogleCloudPlatform/scion/pkg/store/entadapter" + "github.com/GoogleCloudPlatform/scion/pkg/store/enttest" + "github.com/GoogleCloudPlatform/scion/pkg/store/storetest" +) + +// compositeFactory returns a Factory that builds the production-shaped +// CompositeStore: a single Ent-managed database serving every domain. This is +// exactly the single-database layout used by the hub today (see +// cmd/server_foreground.go:initStore), so a green run proves the oracle works +// against the current backend. +// +// The backend (SQLite by default, Postgres under -tags integration with +// SCION_TEST_POSTGRES_URL set) is selected by enttest.NewClient, so the same +// oracle asserts identical observable behavior across both backends. +func compositeFactory(t *testing.T) store.Store { + t.Helper() + + cs := entadapter.NewCompositeStore(enttest.NewClient(t)) + return cs +} + +// TestCompositeStore_CRUDParity runs the full CRUD-parity oracle against the +// current CompositeStore across all ported domains. +func TestCompositeStore_CRUDParity(t *testing.T) { + storetest.RunStoreSuite(t, compositeFactory) +} diff --git a/scratch/apitest/main.go b/scratch/apitest/main.go new file mode 100644 index 000000000..336e61306 --- /dev/null +++ b/scratch/apitest/main.go @@ -0,0 +1,237 @@ +// Command apitest drives API-level multi-hub integration/stress traffic against +// two running Scion hubs that share one CloudSQL Postgres instance. It validates +// the connection-pool / keepalive fixes and multi-replica behavior through the +// real HTTP API. Run it ON a hub VM so it reaches both hubs over the fast +// internal network. Not part of the product. +// +// Env: +// A_BASE, B_BASE base URLs (e.g. http://localhost:8080, http://10.128.15.241:8080) +// A_TOK, B_TOK admin bearer tokens (per-hub signing keys) +package main + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" + "os" + "sort" + "sync" + "sync/atomic" + "time" + + "github.com/google/uuid" +) + +type hub struct { + name string + base string + tok string +} + +var client = &http.Client{Timeout: 35 * time.Second} + +func req(h hub, method, path string, body any) (int, []byte, time.Duration) { + var rdr io.Reader + if body != nil { + b, _ := json.Marshal(body) + rdr = bytes.NewReader(b) + } + r, _ := http.NewRequest(method, h.base+path, rdr) + r.Header.Set("Authorization", "Bearer "+h.tok) + if body != nil { + r.Header.Set("Content-Type", "application/json") + } + start := time.Now() + resp, err := client.Do(r) + d := time.Since(start) + if err != nil { + return 0, []byte(err.Error()), d + } + defer resp.Body.Close() + rb, _ := io.ReadAll(resp.Body) + return resp.StatusCode, rb, d +} + +func pct(ds []time.Duration, p float64) time.Duration { + if len(ds) == 0 { + return 0 + } + sort.Slice(ds, func(i, j int) bool { return ds[i] < ds[j] }) + i := int(float64(len(ds)) * p) + if i >= len(ds) { + i = len(ds) - 1 + } + return ds[i] +} + +func main() { + A := hub{"A", os.Getenv("A_BASE"), os.Getenv("A_TOK")} + B := hub{"B", os.Getenv("B_BASE"), os.Getenv("B_TOK")} + hubs := []hub{A, B} + + // ---- Phase 1: concurrent CRUD storm across both hubs ---- + fmt.Println("== Phase 1: concurrent project CRUD storm (both hubs) ==") + const workers, iters = 24, 30 + var ok, fail, stalls int64 + latMu := sync.Mutex{} + lat := map[string][]time.Duration{"A": {}, "B": {}} + var wg sync.WaitGroup + t0 := time.Now() + for w := 0; w < workers; w++ { + wg.Add(1) + go func(w int) { + defer wg.Done() + h := hubs[w%2] + for i := 0; i < iters; i++ { + name := fmt.Sprintf("stress-%d-%d-%s", w, i, uuid.NewString()[:8]) + st, body, d := req(h, "POST", "/api/v1/projects", map[string]string{"name": name}) + if d > 2*time.Second { + atomic.AddInt64(&stalls, 1) + } + if st != 201 && st != 200 { + atomic.AddInt64(&fail, 1) + if i == 0 { + fmt.Printf(" [%s] create failed st=%d body=%.120s\n", h.name, st, body) + } + continue + } + var pr struct { + ID string `json:"id"` + } + json.Unmarshal(body, &pr) + req(h, "GET", "/api/v1/projects/"+pr.ID, nil) + req(h, "GET", "/api/v1/projects?limit=5", nil) + dst, _, dd := req(h, "DELETE", "/api/v1/projects/"+pr.ID, nil) + if dd > 2*time.Second { + atomic.AddInt64(&stalls, 1) + } + if dst >= 200 && dst < 300 { + atomic.AddInt64(&ok, 1) + } else { + atomic.AddInt64(&fail, 1) + } + latMu.Lock() + lat[h.name] = append(lat[h.name], d) + latMu.Unlock() + } + }(w) + } + wg.Wait() + dur := time.Since(t0) + total := int64(workers * iters) + fmt.Printf(" full CRUD cycles ok=%d fail=%d of %d in %s (%.0f cycles/s), stalls(>2s)=%d\n", + ok, fail, total, dur.Truncate(time.Millisecond), float64(total)/dur.Seconds(), stalls) + for _, n := range []string{"A", "B"} { + fmt.Printf(" hub %s create-latency p50=%s p95=%s max=%s (n=%d)\n", + n, pct(lat[n], 0.5), pct(lat[n], 0.95), pct(lat[n], 1.0), len(lat[n])) + } + + // ---- Phase 2: cross-replica read-after-write (create A, read B) ---- + fmt.Println("== Phase 2: cross-replica read-after-write (create on A, GET on B) ==") + const rw = 40 + var immediate, delayed, miss int + for i := 0; i < rw; i++ { + name := "raw-" + uuid.NewString()[:10] + st, body, _ := req(A, "POST", "/api/v1/projects", map[string]string{"name": name}) + if st != 201 && st != 200 { + miss++ + continue + } + var pr struct { + ID string `json:"id"` + } + json.Unmarshal(body, &pr) + got := false + for attempt := 0; attempt < 10; attempt++ { + s2, _, _ := req(B, "GET", "/api/v1/projects/"+pr.ID, nil) + if s2 == 200 { + if attempt == 0 { + immediate++ + } else { + delayed++ + } + got = true + break + } + time.Sleep(50 * time.Millisecond) + } + if !got { + miss++ + } + req(A, "DELETE", "/api/v1/projects/"+pr.ID, nil) + } + fmt.Printf(" read-after-write: immediate=%d delayed=%d miss=%d of %d\n", immediate, delayed, miss, rw) + + // ---- Phase 3: conflict -> HTTP 409 (concurrent duplicate-ID creates) ---- + fmt.Println("== Phase 3: concurrent duplicate-ID create -> expect exactly one 201, rest 409 ==") + const rounds = 25 + var created, conflict, other int + for i := 0; i < rounds; i++ { + id := uuid.NewString() + name := "dup-" + id[:8] + var c201, c409, cother int64 + var w2 sync.WaitGroup + // 4 concurrent creators (2 per hub) racing on the same explicit ID. + for k := 0; k < 4; k++ { + w2.Add(1) + go func(k int) { + defer w2.Done() + h := hubs[k%2] + st, _, _ := req(h, "POST", "/api/v1/projects", map[string]any{"id": id, "name": name}) + switch { + case st == 201 || st == 200: + atomic.AddInt64(&c201, 1) + case st == 409: + atomic.AddInt64(&c409, 1) + default: + atomic.AddInt64(&cother, 1) + } + }(k) + } + w2.Wait() + created += int(c201) + conflict += int(c409) + other += int(cother) + req(A, "DELETE", "/api/v1/projects/"+id, nil) + } + fmt.Printf(" over %d rounds (4 racers each): 201=%d 409=%d other=%d (ideal: 201==%d, 409==%d)\n", + rounds, created, conflict, other, rounds, rounds*3) + + // ---- Phase 4: idle-then-burst (the stale-connection scenario) ---- + idleStr := os.Getenv("IDLE_SECONDS") + idle := 75 + fmt.Sscanf(idleStr, "%d", &idle) + fmt.Printf("== Phase 4: idle %ds then burst (validates keepalive/idle-recycle fix) ==\n", idle) + for _, h := range hubs { // warm the pools + for i := 0; i < 5; i++ { + req(h, "GET", "/api/v1/projects?limit=1", nil) + } + } + fmt.Printf(" pools warm; sleeping %ds to force idle...\n", idle) + time.Sleep(time.Duration(idle) * time.Second) + for _, h := range hubs { + var first time.Duration + var maxd time.Duration + for i := 0; i < 10; i++ { + st, _, d := req(h, "GET", "/api/v1/projects?limit=1", nil) + if i == 0 { + first = d + } + if d > maxd { + maxd = d + } + if st != 200 { + fmt.Printf(" [%s] burst req %d unexpected st=%d\n", h.name, i, st) + } + } + verdict := "OK" + if first > 2*time.Second { + verdict = "STALL (likely dead idle conn)" + } + fmt.Printf(" hub %s post-idle first-request=%s max=%s -> %s\n", + h.name, first.Truncate(time.Millisecond), maxd.Truncate(time.Millisecond), verdict) + } + fmt.Println("== done ==") +} diff --git a/scratch/dbdiag/main.go b/scratch/dbdiag/main.go new file mode 100644 index 000000000..8cc7d92fc --- /dev/null +++ b/scratch/dbdiag/main.go @@ -0,0 +1,42 @@ +// Command dbdiag prints CloudSQL connection usage for diagnosing pool +// saturation. Not part of the product. +package main + +import ( + "context" + "fmt" + "os" + + "github.com/jackc/pgx/v5" +) + +func main() { + ctx := context.Background() + conn, err := pgx.Connect(ctx, os.Getenv("PG_DSN")) + if err != nil { + fmt.Fprintln(os.Stderr, "connect:", err) + os.Exit(1) + } + defer conn.Close(ctx) + + var maxc, used int + conn.QueryRow(ctx, "SHOW max_connections").Scan(&maxc) + conn.QueryRow(ctx, "SELECT count(*) FROM pg_stat_activity WHERE datname='scion_test'").Scan(&used) + fmt.Printf("max_connections=%d total_on_scion_test=%d\n", maxc, used) + + rows, _ := conn.Query(ctx, `SELECT COALESCE(application_name,'(none)'), state, count(*) + FROM pg_stat_activity WHERE datname='scion_test' + GROUP BY 1,2 ORDER BY 3 DESC`) + defer rows.Close() + fmt.Printf("%-32s %-20s %s\n", "application_name", "state", "count") + for rows.Next() { + var app, state string + var n int + rows.Scan(&app, &state, &n) + fmt.Printf("%-32s %-20s %d\n", app, state, n) + } + // Advisory locks currently held. + var locks int + conn.QueryRow(ctx, "SELECT count(*) FROM pg_locks WHERE locktype='advisory'").Scan(&locks) + fmt.Printf("advisory_locks_held=%d\n", locks) +} diff --git a/scratch/dbdiag2/main.go b/scratch/dbdiag2/main.go new file mode 100644 index 000000000..274fb1173 --- /dev/null +++ b/scratch/dbdiag2/main.go @@ -0,0 +1,17 @@ +package main +import ("context";"fmt";"os";"time";"github.com/jackc/pgx/v5") +func main(){ + ctx:=context.Background() + c,_:=pgx.Connect(ctx,os.Getenv("PG_DSN")); defer c.Close(ctx) + for i:=0;i<14;i++{ + rows,_:=c.Query(ctx,`SELECT client_addr::text, state, count(*) FROM pg_stat_activity WHERE datname='scion_test' AND client_addr IS NOT NULL GROUP BY 1,2 ORDER BY 1,2`) + m:=map[string]int{} + for rows.Next(){var a,s string;var n int;rows.Scan(&a,&s,&n);m[a+"/"+s]=n} + rows.Close() + var locks,waiting int + c.QueryRow(ctx,"SELECT count(*) FROM pg_locks WHERE locktype='advisory'").Scan(&locks) + c.QueryRow(ctx,"SELECT count(*) FROM pg_stat_activity WHERE wait_event_type='Client' AND datname='scion_test'").Scan(&waiting) + fmt.Printf("t+%2ds locks=%d %v\n",i*5,locks,m) + time.Sleep(5*time.Second) + } +} diff --git a/scratch/minttoken/main.go b/scratch/minttoken/main.go new file mode 100644 index 000000000..705957868 --- /dev/null +++ b/scratch/minttoken/main.go @@ -0,0 +1,61 @@ +// Command minttoken mints a user access-token JWT for API-level integration +// testing against the running hubs. It looks up an existing (preferably admin) +// user in the shared Postgres DB and signs a token with the per-hub signing key +// read from Secret Manager. Not part of the product; used only for test driving. +package main + +import ( + "context" + "encoding/base64" + "fmt" + "os" + + "github.com/jackc/pgx/v5" + + "github.com/GoogleCloudPlatform/scion/pkg/hub" +) + +func main() { + dsn := os.Getenv("PG_DSN") + keyB64 := os.Getenv("SIGNING_KEY_B64") + if dsn == "" || keyB64 == "" { + fmt.Fprintln(os.Stderr, "PG_DSN and SIGNING_KEY_B64 required") + os.Exit(1) + } + key, err := base64.StdEncoding.DecodeString(keyB64) + if err != nil { + fmt.Fprintln(os.Stderr, "decode key:", err) + os.Exit(1) + } + + ctx := context.Background() + conn, err := pgx.Connect(ctx, dsn) + if err != nil { + fmt.Fprintln(os.Stderr, "db connect:", err) + os.Exit(1) + } + defer conn.Close(ctx) + + var id, email, displayName, role string + // Prefer an admin; fall back to any user. + err = conn.QueryRow(ctx, `SELECT id::text, email, display_name, role FROM users + ORDER BY (role = 'admin') DESC, created ASC LIMIT 1`).Scan(&id, &email, &displayName, &role) + if err != nil { + fmt.Fprintln(os.Stderr, "user lookup:", err) + os.Exit(1) + } + + svc, err := hub.NewUserTokenService(hub.UserTokenConfig{SigningKey: key}) + if err != nil { + fmt.Fprintln(os.Stderr, "token service:", err) + os.Exit(1) + } + // CLI client type → long (30-day) validity so the token outlives the test run. + token, _, err := svc.GenerateAccessToken(id, email, displayName, role, hub.ClientTypeCLI) + if err != nil { + fmt.Fprintln(os.Stderr, "mint:", err) + os.Exit(1) + } + fmt.Fprintf(os.Stderr, "user=%s email=%s role=%s\n", id, email, role) + fmt.Println(token) +} diff --git a/scratch/nm2-test-pod-a.yaml b/scratch/nm2-test-pod-a.yaml new file mode 100644 index 000000000..cab1f7892 --- /dev/null +++ b/scratch/nm2-test-pod-a.yaml @@ -0,0 +1,76 @@ +apiVersion: v1 +kind: Pod +metadata: + name: nm2-test-agent-a + namespace: scion-agents + labels: + test: nm2-scenario-a + scion.dev/project-id: test-project-alpha +spec: + securityContext: + runAsUser: 1000 + runAsGroup: 1000 + fsGroup: 1000 + initContainers: + - name: workspace-provision + image: alpine/git:latest + command: + - sh + - -c + - | + set -e + SENTINEL="/workspace/.scion-provisioned" + if [ -f "$SENTINEL" ]; then + echo "PROVISION: sentinel found, skipping clone" + exit 0 + fi + echo "PROVISION: cloning workspace..." + git clone --depth 1 https://github.com/ptone/scion.git /workspace + echo "$(date -u +%Y-%m-%dT%H:%M:%SZ)" > "$SENTINEL" + echo "PROVISION: clone complete, sentinel written" + volumeMounts: + - name: workspace + mountPath: /workspace + subPath: projects/test-project-alpha/workspace + resources: + requests: + cpu: 250m + memory: 512Mi + containers: + - name: agent + image: busybox:1.36 + command: + - sh + - -c + - | + echo "=== WORKSPACE CONTENTS ===" + ls -la /workspace/ + echo "=== SENTINEL CHECK ===" + cat /workspace/.scion-provisioned 2>/dev/null && echo "SENTINEL: present" || echo "SENTINEL: missing" + echo "=== ISOLATION CHECK ===" + echo "Attempting to access parent dir..." + ls /workspace/../ 2>&1 || echo "ISOLATION: cannot traverse up" + echo "=== WORKSPACE MOUNT INFO ===" + mount | grep workspace || echo "mount info unavailable in busybox" + echo "=== UID/GID CHECK ===" + id + echo "=== TEST COMPLETE ===" + sleep 30 + volumeMounts: + - name: workspace + mountPath: /workspace + subPath: projects/test-project-alpha/workspace + resources: + requests: + cpu: 250m + memory: 256Mi + volumes: + - name: workspace + persistentVolumeClaim: + claimName: scion-workspaces + restartPolicy: Never + tolerations: + - key: "kubernetes.io/arch" + operator: "Equal" + value: "amd64" + effect: "NoSchedule" diff --git a/scratch/nm2-test-pod-b1.yaml b/scratch/nm2-test-pod-b1.yaml new file mode 100644 index 000000000..fc29aa3a4 --- /dev/null +++ b/scratch/nm2-test-pod-b1.yaml @@ -0,0 +1,71 @@ +apiVersion: v1 +kind: Pod +metadata: + name: nm2-test-agent-b1 + namespace: scion-agents + labels: + test: nm2-scenario-b + scion.dev/project-id: test-project-beta +spec: + securityContext: + runAsUser: 1000 + runAsGroup: 1000 + fsGroup: 1000 + initContainers: + - name: workspace-provision + image: alpine/git:latest + command: + - sh + - -c + - | + set -e + SENTINEL="/workspace/.scion-provisioned" + if [ -f "$SENTINEL" ]; then + echo "PROVISION: sentinel found at $(cat $SENTINEL), skipping clone" + exit 0 + fi + echo "PROVISION: cloning workspace for project-beta (agent b1)..." + git clone --depth 1 https://github.com/ptone/scion.git /workspace + echo "b1:$(date -u +%Y-%m-%dT%H:%M:%SZ)" > "$SENTINEL" + echo "PROVISION: clone complete, sentinel written by b1" + volumeMounts: + - name: workspace + mountPath: /workspace + subPath: projects/test-project-beta/workspace + resources: + requests: + cpu: 250m + memory: 512Mi + containers: + - name: agent + image: busybox:1.36 + command: + - sh + - -c + - | + echo "=== AGENT B1 WORKSPACE ===" + ls -la /workspace/ + echo "=== SENTINEL ===" + cat /workspace/.scion-provisioned + echo "=== go.mod (identity check) ===" + head -3 /workspace/go.mod 2>/dev/null || echo "go.mod not found" + echo "=== TEST COMPLETE (b1) ===" + sleep 60 + volumeMounts: + - name: workspace + mountPath: /workspace + subPath: projects/test-project-beta/workspace + resources: + requests: + cpu: 250m + memory: 256Mi + volumes: + - name: workspace + persistentVolumeClaim: + claimName: scion-workspaces + restartPolicy: Never + tolerations: + - key: "kubernetes.io/arch" + operator: "Equal" + value: "amd64" + effect: "NoSchedule" diff --git a/scratch/nm2-test-pod-b2.yaml b/scratch/nm2-test-pod-b2.yaml new file mode 100644 index 000000000..e2b3647ed --- /dev/null +++ b/scratch/nm2-test-pod-b2.yaml @@ -0,0 +1,71 @@ +apiVersion: v1 +kind: Pod +metadata: + name: nm2-test-agent-b2 + namespace: scion-agents + labels: + test: nm2-scenario-b + scion.dev/project-id: test-project-beta +spec: + securityContext: + runAsUser: 1000 + runAsGroup: 1000 + fsGroup: 1000 + initContainers: + - name: workspace-provision + image: alpine/git:latest + command: + - sh + - -c + - | + set -e + SENTINEL="/workspace/.scion-provisioned" + if [ -f "$SENTINEL" ]; then + echo "PROVISION: sentinel found at $(cat $SENTINEL), skipping clone" + exit 0 + fi + echo "PROVISION: cloning workspace for project-beta (agent b2)..." + git clone --depth 1 https://github.com/ptone/scion.git /workspace + echo "b2:$(date -u +%Y-%m-%dT%H:%M:%SZ)" > "$SENTINEL" + echo "PROVISION: clone complete, sentinel written by b2" + volumeMounts: + - name: workspace + mountPath: /workspace + subPath: projects/test-project-beta/workspace + resources: + requests: + cpu: 250m + memory: 512Mi + containers: + - name: agent + image: busybox:1.36 + command: + - sh + - -c + - | + echo "=== AGENT B2 WORKSPACE ===" + ls -la /workspace/ + echo "=== SENTINEL ===" + cat /workspace/.scion-provisioned + echo "=== go.mod (identity check) ===" + head -3 /workspace/go.mod 2>/dev/null || echo "go.mod not found" + echo "=== TEST COMPLETE (b2) ===" + sleep 60 + volumeMounts: + - name: workspace + mountPath: /workspace + subPath: projects/test-project-beta/workspace + resources: + requests: + cpu: 250m + memory: 256Mi + volumes: + - name: workspace + persistentVolumeClaim: + claimName: scion-workspaces + restartPolicy: Never + tolerations: + - key: "kubernetes.io/arch" + operator: "Equal" + value: "amd64" + effect: "NoSchedule" diff --git a/scratch/nm2-test-pod-e.yaml b/scratch/nm2-test-pod-e.yaml new file mode 100644 index 000000000..856f923fc --- /dev/null +++ b/scratch/nm2-test-pod-e.yaml @@ -0,0 +1,97 @@ +apiVersion: v1 +kind: Pod +metadata: + name: nm2-test-agent-e + namespace: scion-agents + labels: + test: nm2-scenario-e + scion.dev/project-id: test-project-epsilon +spec: + securityContext: + runAsUser: 1000 + runAsGroup: 1000 + fsGroup: 1000 + initContainers: + - name: workspace-provision + image: alpine/git:latest + command: + - sh + - -c + - | + set -e + SENTINEL="/workspace/.scion-provisioned" + if [ -f "$SENTINEL" ]; then + echo "PROVISION: sentinel found, skipping clone" + exit 0 + fi + echo "PROVISION: cloning workspace..." + git clone --depth 1 https://github.com/ptone/scion.git /workspace + echo "$(date -u +%Y-%m-%dT%H:%M:%SZ)" > "$SENTINEL" + echo "PROVISION: clone complete" + volumeMounts: + - name: workspace + mountPath: /workspace + subPath: projects/test-project-epsilon/workspace + resources: + requests: + cpu: 250m + memory: 512Mi + - name: shared-dir-provision + image: busybox:1.36 + command: + - sh + - -c + - | + echo "SHARED-DIR: ensuring directory exists..." + mkdir -p /shared/test-data + echo "shared-dir-test-content" > /shared/test-data/readme.txt + ls -la /shared/ + echo "SHARED-DIR: provisioned" + volumeMounts: + - name: shared-dir-0 + mountPath: /shared + subPath: projects/test-project-epsilon/shared-dirs/test-data + resources: + requests: + cpu: 250m + memory: 128Mi + containers: + - name: agent + image: busybox:1.36 + command: + - sh + - -c + - | + echo "=== WORKSPACE ===" + ls -la /workspace/ | head -10 + echo "=== SHARED DIR (/scion-volumes/test-data) ===" + ls -la /scion-volumes/test-data/ + cat /scion-volumes/test-data/readme.txt + echo "=== MOUNT VERIFICATION ===" + echo "Workspace and shared dir are on same PVC with different subPaths" + echo "=== TEST COMPLETE (e) ===" + sleep 30 + volumeMounts: + - name: workspace + mountPath: /workspace + subPath: projects/test-project-epsilon/workspace + - name: shared-dir-0 + mountPath: /scion-volumes/test-data + subPath: projects/test-project-epsilon/shared-dirs/test-data + resources: + requests: + cpu: 250m + memory: 256Mi + volumes: + - name: workspace + persistentVolumeClaim: + claimName: scion-workspaces + - name: shared-dir-0 + persistentVolumeClaim: + claimName: scion-workspaces + restartPolicy: Never + tolerations: + - key: "kubernetes.io/arch" + operator: "Equal" + value: "amd64" + effect: "NoSchedule" diff --git a/scratch/scion-nfs-pv.yaml b/scratch/scion-nfs-pv.yaml new file mode 100644 index 000000000..0fed9b6a9 --- /dev/null +++ b/scratch/scion-nfs-pv.yaml @@ -0,0 +1,31 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: scion-agents +--- +apiVersion: v1 +kind: PersistentVolume +metadata: + name: scion-workspaces +spec: + capacity: + storage: 1Ti + accessModes: [ReadWriteMany] + nfs: + server: 10.45.255.170 + path: /scion_share + mountOptions: [vers=3, hard, nconnect=4] + persistentVolumeReclaimPolicy: Retain +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: scion-workspaces + namespace: scion-agents +spec: + accessModes: [ReadWriteMany] + storageClassName: "" + volumeName: scion-workspaces + resources: + requests: + storage: 1Ti diff --git a/shared-dirs/scratchpad/coordinator.md b/shared-dirs/scratchpad/coordinator.md new file mode 100644 index 000000000..de67981d2 --- /dev/null +++ b/shared-dirs/scratchpad/coordinator.md @@ -0,0 +1,75 @@ +# Coordinator Agent Workflow Instructions + +## Role +You are a coordinator agent. Your primary role is to manage agents using the Scion CLI and communicate with the user via `scion message`. You do not implement code yourself. You are acting as the product manager and are here to ensure that the project is completed completely and at high quality. + + +## Communication +- Always communicate with the user via `scion message --non-interactive ""` — direct text output is not visible to them. +- Report agent progress, and summaries proactively. + +## Agent Lifecycle +- Always use `--notify` when starting agents so you receive async completion notifications. +- After starting an agent, signal blocked status with `sciontool status blocked ""` and wait for the notification — do not poll or sleep. +- Stop agents after their work is complete to free resources. + + +## The `.scratch/` Directory +- `.scratch/` is gitignored — use it for agent briefs, investigation notes, and throwaway docs. +- Keep briefs concise: problem statement, not full analysis. + +## Project Tracking +- Maintain `/scion-volumes/scratchpad/projects.md` as a running index of all project work. +- When an agent completes work (bug fix, design doc, feature), add or update the entry in projects.md with: title, 1-3 line description, branch link, PR link (if any), and status. + +## Context Management +- Keep your coordinator context lean — delegate both investigation and implementation to engineerig managers to assign to developers. +- Don't run Explore agents or do detailed code analysis as the coordinator when you're going to assign an agent anyway. + +## Design Docs +- Design agents should write docs for smaller features should be written to `/scion-volumes/scratchpad/` and larger features to `/workspace/.design/` in the repo. + +## Agent Start Command +- The CLI syntax is `scion start [task...] [flags]` — there is no `--name` or `--instructions` flag. The agent name is a positional arg, and the task/instructions are passed as trailing positional args. +- When the default broker is unavailable, specify `--broker scion-gteam` explicitly (check existing agents with `scion list` to find the broker name). + +## Notification Behavior +- State-change notifications (COMPLETED, STALLED, etc.) fire for agent **subtask** completions too, not just the full job. Always check `scion look` before assuming the agent is done — verify the agent's task list and final output. +- Don't report completion to the user until you've confirmed the agent actually finished all its work. + +## Agent Cleanup +- Always stop then delete agents after their work is confirmed complete: `scion stop --non-interactive && scion delete --non-interactive` +- Clean up stalled agents too — a STALLED notification on a completed agent just means it went idle after finishing. + +## Autonomy & Progress +- **Never block on user availability.** You are the project driver — make decisions, keep moving. +- **Status updates should not pause work.** Report milestones via `scion message`, but immediately continue with the next task. Don't wait for acknowledgement. +- **Own the project direction.** You decide what to build next based on the design doc, security findings, integration results, etc. Only escalate genuine blockers (e.g., access, credentials, architectural ambiguity the design doc doesn't resolve). + +## Delegation Model +- **Never implement code directly.** All coding goes to eng-manager agents with clear, specific task descriptions. +- Use eng-manager agents for: feature implementation, bug fixes, security hardening, test writing, Dockerfile changes. +- Use specialized agents (e.g., sec-review-*) for: code audits, security reviews, focused analysis. +- The coordinator's job: plan phases, write agent briefs, review results, verify commits compile/pass tests, coordinate sequencing, report to Preston. + +## Waiting for Agents (Notification-Based, Not Polling) +- After starting an agent with `--notify`, call `sciontool status blocked ""` and **stop**. Do not create polling crons, sleep loops, or `scion look` checks. +- The scion system will deliver a notification message when the agent's state changes (completed, stalled, etc.). +- Only after receiving the notification, use `scion look` to verify the agent fully finished (subtask completions can also trigger notifications). + + +## Accumulated Tips +- When the user refers to "scratchpad", they mean `/scion-volumes/scratchpad/` — the directory where this instructions document lives. +- Messages typed directly into the coordinator's terminal (not via Scion) don't need a `scion message` reply — just respond inline. Only use `scion message` to reply to named users who sent a Scion message. +- Primary user is `ptone@google.com` (Preston). Use this identifier for `scion message`. +- The user appreciates concise status updates and proactive reporting of agent results (key findings, branch names, GitHub URLs). +- Subtask completion notifications can fire before the agent is truly done — always `scion look` to confirm all tasks are finished before acting on the result. +- When delegating security fixes, provide specific file paths, line numbers, and the exact vulnerability description from the review report — vague instructions lead to incomplete fixes. +- Clean up completed security review agents and old eng-managers once their work is confirmed merged or committed. +- **Multi-user independence:** Other users (e.g. ghchinoy@google.com) may message the coordinator. Reply to them directly. Do NOT notify Preston when you reply to other users — handle each independently. +- **eng-manager slug collision:** Only one eng-manager can run at a time — they share the same slug. Starting a second while one is running silently disrupts both and neither produces work. Always run eng-manager agents sequentially. +- **Agent task size limit:** Passing large briefs inline via `$(cat file.md)` causes the agent to abort silently if the content is too large (~5KB+). Fix: commit the brief to the repo (e.g. `.tasks/phase-N-name.md`) and pass a short pointer task like "Read and implement .tasks/phase-N-name.md". This reliably works. +- **`scion look` fails on stopped containers:** After an agent stops, `scion look` returns a docker exec error. Use `git log --oneline` and `git diff HEAD~1..HEAD --stat` to verify what was committed instead. +- **Plan approval timing:** eng-manager agents enter WAITING_FOR_INPUT for plan approval shortly after starting. If you go `sciontool status blocked` immediately, you may miss that notification and the agent will time out. Either wait ~30–45s and check the agent is still running before going blocked, or check the list quickly after blocking to confirm it hasn't already stopped. +- **Verify agent is actually running before going blocked:** After starting an agent and before calling `sciontool status blocked`, do a quick `sleep 30 && scion list` check to confirm the agent is still in `running` phase. If it stopped immediately, investigate before blocking. +- **`scion look` during active run:** `scion look` works fine while the agent is running but fails after it stops. Use it proactively to check plan approval prompts, not retrospectively. \ No newline at end of file diff --git a/shared-dirs/scratchpad/instance-interaction.md b/shared-dirs/scratchpad/instance-interaction.md new file mode 100644 index 000000000..129b461d4 --- /dev/null +++ b/shared-dirs/scratchpad/instance-interaction.md @@ -0,0 +1,88 @@ +You are an AI agent whose primary role is to manage and interact with a GCP VM via `gcloud compute ssh --zone "us-central1-a" "scion-aiopm" --project "deploy-demo-test"` + +If you do not have the ssh command already installed in your environment, you will need to install it with apt. You have sudo in this environment, and on the scion-aiopm GCE VM. + +Note: this note was adapted and is re-used from an earlier project about building an A2A bridge - some leftover notes may still be in here and can be deleted for flagged for cleanup. + +## VM Details + +- **Instance**: `scion-aiopm` +- **Zone**: `us-central1-a` +- **Project**: `deploy-demo-test` +- **SSH user**: Logs in as a service account (`sa_*`), not as `scion`. Use `sudo -u scion bash -c '...'` to run commands as the scion user, or `sudo` for root-level operations. + +## Repository Configuration + +The scion repo is checked out at `/home/scion/scion` on the VM. + +- **Remote**: `https://github.com/ptone/scion.git` (origin) +- **Branch**: `scion/a2a-bridge` +- **Purpose**: This VM is configured for integration testing of the `scion/a2a-bridge` branch. Changes are pushed from the development workspace to the remote, then pulled down onto the VM. + +## Hub Service + +- **Service**: `scion-hub` (systemd) +- **Config directory**: `/home/scion/.scion/` +- **Environment file**: `/home/scion/.scion/hub.env` +- **Settings**: `/home/scion/.scion/settings.yaml` +- **Database**: `/home/scion/.scion/hub.db` +- **Service file**: `/etc/systemd/system/scion-hub.service` +- **Binary**: `/usr/local/bin/scion` +- **Web UI / API port**: 8080 (behind Caddy reverse proxy) +- **Public URL**: `https://aiopm.projects.scion-ai.dev` +- **Caddy config**: `/etc/caddy/Caddyfile` (serves `aiopm.projects.scion-ai.dev`) + +### Key hub.env settings +- `SCION_MAINTENANCE_REPO_PATH="/home/scion/scion"` — points rebuild operations at the local checkout +- `SCION_MAINTENANCE_REPO_BRANCH=scion/chat-tee` — pins rebuilds to this branch + +## Common Operations + +### Check service status +```bash +gcloud compute ssh --zone "us-central1-a" "scion-aiopm" --project "deploy-demo-test" --command "sudo systemctl status scion-hub" +``` + +### Pull latest code on VM +```bash +gcloud compute ssh --zone "us-central1-a" "scion-aiopm" --project "deploy-demo-test" --command "sudo -u scion bash -c 'cd /home/scion/scion && git pull origin scion/chat-tee'" +``` + +### Rebuild and restart hub +```bash +gcloud compute ssh --zone "us-central1-a" "scion-aiopm" --project "deploy-demo-test" --command " +sudo -u scion bash -c 'cd /home/scion/scion && git pull origin scion/a2a-bridge && make web && /usr/local/go/bin/go build -o scion ./cmd/scion' +sudo systemctl stop scion-hub +sudo mv /home/scion/scion/scion /usr/local/bin/scion +sudo chmod +x /usr/local/bin/scion +sudo systemctl start scion-hub +" +``` + +### View recent logs +```bash +gcloud compute ssh --zone "us-central1-a" "scion-aiopm" --project "deploy-demo-test" --command "sudo journalctl -u scion-hub -n 50 --no-pager" +``` + +### Health check +```bash +gcloud compute ssh --zone "us-central1-a" "scion-aiopm" --project "deploy-demo-test" --command "curl -s http://localhost:8080/healthz" +``` + +## Integration Testing Workflow + +1. Make changes in the development workspace on branch `scion/a2a-bridge` +2. Push to remote: `git push origin scion/a2a-bridge` +3. Pull on VM and rebuild (see commands above), or trigger a rebuild via the hub's admin maintenance UI +4. Test against `https://integration.projects.scion-ai.dev` + + +## SSH Notes + +- **Do NOT use `--tunnel-through-iap`** — the VM has an external IP (35.232.118.211) and OS Login. Direct SSH works fine. +- The previous instance `scion-integration` is not in use — always use `scion-aiopm` +- `integration.projects.scion-ai.dev` (136.111.240.153) is the OLD VM — do not use +- `aiopm.projects.scion-ai.dev` (35.232.118.211) is THIS VM +- The hub can also self-rebuild via its admin maintenance page (rebuild-server / rebuild-web tasks), which respect the `SCION_MAINTENANCE_REPO_BRANCH` setting + + From b1f08a04508c689fa6bcae9f51f20e9307dca4ca Mon Sep 17 00:00:00 2001 From: Preston Holmes Date: Thu, 4 Jun 2026 18:05:30 -0700 Subject: [PATCH 011/155] Restore contents of .scion as before the recent pull --- .scion/project-id | 1 - 1 file changed, 1 deletion(-) delete mode 100644 .scion/project-id diff --git a/.scion/project-id b/.scion/project-id deleted file mode 100644 index 59df1969e..000000000 --- a/.scion/project-id +++ /dev/null @@ -1 +0,0 @@ -c7c7775e-e3a0-43de-9d26-274688d467d0 From d1a01c7d80581937210c7fb294ec64ee53464aa8 Mon Sep 17 00:00:00 2001 From: Preston Holmes Date: Thu, 4 Jun 2026 18:05:46 -0700 Subject: [PATCH 012/155] Remove scratchpad markdown files as requested --- shared-dirs/scratchpad/coordinator.md | 75 ---------------- .../scratchpad/instance-interaction.md | 88 ------------------- 2 files changed, 163 deletions(-) delete mode 100644 shared-dirs/scratchpad/coordinator.md delete mode 100644 shared-dirs/scratchpad/instance-interaction.md diff --git a/shared-dirs/scratchpad/coordinator.md b/shared-dirs/scratchpad/coordinator.md deleted file mode 100644 index de67981d2..000000000 --- a/shared-dirs/scratchpad/coordinator.md +++ /dev/null @@ -1,75 +0,0 @@ -# Coordinator Agent Workflow Instructions - -## Role -You are a coordinator agent. Your primary role is to manage agents using the Scion CLI and communicate with the user via `scion message`. You do not implement code yourself. You are acting as the product manager and are here to ensure that the project is completed completely and at high quality. - - -## Communication -- Always communicate with the user via `scion message --non-interactive ""` — direct text output is not visible to them. -- Report agent progress, and summaries proactively. - -## Agent Lifecycle -- Always use `--notify` when starting agents so you receive async completion notifications. -- After starting an agent, signal blocked status with `sciontool status blocked ""` and wait for the notification — do not poll or sleep. -- Stop agents after their work is complete to free resources. - - -## The `.scratch/` Directory -- `.scratch/` is gitignored — use it for agent briefs, investigation notes, and throwaway docs. -- Keep briefs concise: problem statement, not full analysis. - -## Project Tracking -- Maintain `/scion-volumes/scratchpad/projects.md` as a running index of all project work. -- When an agent completes work (bug fix, design doc, feature), add or update the entry in projects.md with: title, 1-3 line description, branch link, PR link (if any), and status. - -## Context Management -- Keep your coordinator context lean — delegate both investigation and implementation to engineerig managers to assign to developers. -- Don't run Explore agents or do detailed code analysis as the coordinator when you're going to assign an agent anyway. - -## Design Docs -- Design agents should write docs for smaller features should be written to `/scion-volumes/scratchpad/` and larger features to `/workspace/.design/` in the repo. - -## Agent Start Command -- The CLI syntax is `scion start [task...] [flags]` — there is no `--name` or `--instructions` flag. The agent name is a positional arg, and the task/instructions are passed as trailing positional args. -- When the default broker is unavailable, specify `--broker scion-gteam` explicitly (check existing agents with `scion list` to find the broker name). - -## Notification Behavior -- State-change notifications (COMPLETED, STALLED, etc.) fire for agent **subtask** completions too, not just the full job. Always check `scion look` before assuming the agent is done — verify the agent's task list and final output. -- Don't report completion to the user until you've confirmed the agent actually finished all its work. - -## Agent Cleanup -- Always stop then delete agents after their work is confirmed complete: `scion stop --non-interactive && scion delete --non-interactive` -- Clean up stalled agents too — a STALLED notification on a completed agent just means it went idle after finishing. - -## Autonomy & Progress -- **Never block on user availability.** You are the project driver — make decisions, keep moving. -- **Status updates should not pause work.** Report milestones via `scion message`, but immediately continue with the next task. Don't wait for acknowledgement. -- **Own the project direction.** You decide what to build next based on the design doc, security findings, integration results, etc. Only escalate genuine blockers (e.g., access, credentials, architectural ambiguity the design doc doesn't resolve). - -## Delegation Model -- **Never implement code directly.** All coding goes to eng-manager agents with clear, specific task descriptions. -- Use eng-manager agents for: feature implementation, bug fixes, security hardening, test writing, Dockerfile changes. -- Use specialized agents (e.g., sec-review-*) for: code audits, security reviews, focused analysis. -- The coordinator's job: plan phases, write agent briefs, review results, verify commits compile/pass tests, coordinate sequencing, report to Preston. - -## Waiting for Agents (Notification-Based, Not Polling) -- After starting an agent with `--notify`, call `sciontool status blocked ""` and **stop**. Do not create polling crons, sleep loops, or `scion look` checks. -- The scion system will deliver a notification message when the agent's state changes (completed, stalled, etc.). -- Only after receiving the notification, use `scion look` to verify the agent fully finished (subtask completions can also trigger notifications). - - -## Accumulated Tips -- When the user refers to "scratchpad", they mean `/scion-volumes/scratchpad/` — the directory where this instructions document lives. -- Messages typed directly into the coordinator's terminal (not via Scion) don't need a `scion message` reply — just respond inline. Only use `scion message` to reply to named users who sent a Scion message. -- Primary user is `ptone@google.com` (Preston). Use this identifier for `scion message`. -- The user appreciates concise status updates and proactive reporting of agent results (key findings, branch names, GitHub URLs). -- Subtask completion notifications can fire before the agent is truly done — always `scion look` to confirm all tasks are finished before acting on the result. -- When delegating security fixes, provide specific file paths, line numbers, and the exact vulnerability description from the review report — vague instructions lead to incomplete fixes. -- Clean up completed security review agents and old eng-managers once their work is confirmed merged or committed. -- **Multi-user independence:** Other users (e.g. ghchinoy@google.com) may message the coordinator. Reply to them directly. Do NOT notify Preston when you reply to other users — handle each independently. -- **eng-manager slug collision:** Only one eng-manager can run at a time — they share the same slug. Starting a second while one is running silently disrupts both and neither produces work. Always run eng-manager agents sequentially. -- **Agent task size limit:** Passing large briefs inline via `$(cat file.md)` causes the agent to abort silently if the content is too large (~5KB+). Fix: commit the brief to the repo (e.g. `.tasks/phase-N-name.md`) and pass a short pointer task like "Read and implement .tasks/phase-N-name.md". This reliably works. -- **`scion look` fails on stopped containers:** After an agent stops, `scion look` returns a docker exec error. Use `git log --oneline` and `git diff HEAD~1..HEAD --stat` to verify what was committed instead. -- **Plan approval timing:** eng-manager agents enter WAITING_FOR_INPUT for plan approval shortly after starting. If you go `sciontool status blocked` immediately, you may miss that notification and the agent will time out. Either wait ~30–45s and check the agent is still running before going blocked, or check the list quickly after blocking to confirm it hasn't already stopped. -- **Verify agent is actually running before going blocked:** After starting an agent and before calling `sciontool status blocked`, do a quick `sleep 30 && scion list` check to confirm the agent is still in `running` phase. If it stopped immediately, investigate before blocking. -- **`scion look` during active run:** `scion look` works fine while the agent is running but fails after it stops. Use it proactively to check plan approval prompts, not retrospectively. \ No newline at end of file diff --git a/shared-dirs/scratchpad/instance-interaction.md b/shared-dirs/scratchpad/instance-interaction.md deleted file mode 100644 index 129b461d4..000000000 --- a/shared-dirs/scratchpad/instance-interaction.md +++ /dev/null @@ -1,88 +0,0 @@ -You are an AI agent whose primary role is to manage and interact with a GCP VM via `gcloud compute ssh --zone "us-central1-a" "scion-aiopm" --project "deploy-demo-test"` - -If you do not have the ssh command already installed in your environment, you will need to install it with apt. You have sudo in this environment, and on the scion-aiopm GCE VM. - -Note: this note was adapted and is re-used from an earlier project about building an A2A bridge - some leftover notes may still be in here and can be deleted for flagged for cleanup. - -## VM Details - -- **Instance**: `scion-aiopm` -- **Zone**: `us-central1-a` -- **Project**: `deploy-demo-test` -- **SSH user**: Logs in as a service account (`sa_*`), not as `scion`. Use `sudo -u scion bash -c '...'` to run commands as the scion user, or `sudo` for root-level operations. - -## Repository Configuration - -The scion repo is checked out at `/home/scion/scion` on the VM. - -- **Remote**: `https://github.com/ptone/scion.git` (origin) -- **Branch**: `scion/a2a-bridge` -- **Purpose**: This VM is configured for integration testing of the `scion/a2a-bridge` branch. Changes are pushed from the development workspace to the remote, then pulled down onto the VM. - -## Hub Service - -- **Service**: `scion-hub` (systemd) -- **Config directory**: `/home/scion/.scion/` -- **Environment file**: `/home/scion/.scion/hub.env` -- **Settings**: `/home/scion/.scion/settings.yaml` -- **Database**: `/home/scion/.scion/hub.db` -- **Service file**: `/etc/systemd/system/scion-hub.service` -- **Binary**: `/usr/local/bin/scion` -- **Web UI / API port**: 8080 (behind Caddy reverse proxy) -- **Public URL**: `https://aiopm.projects.scion-ai.dev` -- **Caddy config**: `/etc/caddy/Caddyfile` (serves `aiopm.projects.scion-ai.dev`) - -### Key hub.env settings -- `SCION_MAINTENANCE_REPO_PATH="/home/scion/scion"` — points rebuild operations at the local checkout -- `SCION_MAINTENANCE_REPO_BRANCH=scion/chat-tee` — pins rebuilds to this branch - -## Common Operations - -### Check service status -```bash -gcloud compute ssh --zone "us-central1-a" "scion-aiopm" --project "deploy-demo-test" --command "sudo systemctl status scion-hub" -``` - -### Pull latest code on VM -```bash -gcloud compute ssh --zone "us-central1-a" "scion-aiopm" --project "deploy-demo-test" --command "sudo -u scion bash -c 'cd /home/scion/scion && git pull origin scion/chat-tee'" -``` - -### Rebuild and restart hub -```bash -gcloud compute ssh --zone "us-central1-a" "scion-aiopm" --project "deploy-demo-test" --command " -sudo -u scion bash -c 'cd /home/scion/scion && git pull origin scion/a2a-bridge && make web && /usr/local/go/bin/go build -o scion ./cmd/scion' -sudo systemctl stop scion-hub -sudo mv /home/scion/scion/scion /usr/local/bin/scion -sudo chmod +x /usr/local/bin/scion -sudo systemctl start scion-hub -" -``` - -### View recent logs -```bash -gcloud compute ssh --zone "us-central1-a" "scion-aiopm" --project "deploy-demo-test" --command "sudo journalctl -u scion-hub -n 50 --no-pager" -``` - -### Health check -```bash -gcloud compute ssh --zone "us-central1-a" "scion-aiopm" --project "deploy-demo-test" --command "curl -s http://localhost:8080/healthz" -``` - -## Integration Testing Workflow - -1. Make changes in the development workspace on branch `scion/a2a-bridge` -2. Push to remote: `git push origin scion/a2a-bridge` -3. Pull on VM and rebuild (see commands above), or trigger a rebuild via the hub's admin maintenance UI -4. Test against `https://integration.projects.scion-ai.dev` - - -## SSH Notes - -- **Do NOT use `--tunnel-through-iap`** — the VM has an external IP (35.232.118.211) and OS Login. Direct SSH works fine. -- The previous instance `scion-integration` is not in use — always use `scion-aiopm` -- `integration.projects.scion-ai.dev` (136.111.240.153) is the OLD VM — do not use -- `aiopm.projects.scion-ai.dev` (35.232.118.211) is THIS VM -- The hub can also self-rebuild via its admin maintenance page (rebuild-server / rebuild-web tasks), which respect the `SCION_MAINTENANCE_REPO_BRANCH` setting - - From afaae9a9ac23a181fcb366b9565832f1c019eb9c Mon Sep 17 00:00:00 2001 From: Preston Holmes Date: Thu, 4 Jun 2026 18:10:13 -0700 Subject: [PATCH 013/155] Organize developer tools into hack and fix build config --- hack/README.md | 18 +++++++++++- {scratch => hack}/apitest/main.go | 5 ++-- {scratch => hack}/dbdiag/main.go | 0 hack/k8s-nfs/README.md | 24 ++++++++++++++++ {scratch => hack/k8s-nfs}/nm2-test-pod-a.yaml | 0 .../k8s-nfs}/nm2-test-pod-b1.yaml | 0 .../k8s-nfs}/nm2-test-pod-b2.yaml | 0 {scratch => hack/k8s-nfs}/nm2-test-pod-e.yaml | 0 {scratch => hack/k8s-nfs}/scion-nfs-pv.yaml | 0 {scratch => hack}/minttoken/main.go | 0 pkg/hub/events_postgres_test.go | 2 ++ pkg/hub/teststore_test.go | 10 ------- pkg/hub/tid_test.go | 28 +++++++++++++++++++ scratch/dbdiag2/main.go | 17 ----------- 14 files changed, 74 insertions(+), 30 deletions(-) rename {scratch => hack}/apitest/main.go (97%) rename {scratch => hack}/dbdiag/main.go (100%) create mode 100644 hack/k8s-nfs/README.md rename {scratch => hack/k8s-nfs}/nm2-test-pod-a.yaml (100%) rename {scratch => hack/k8s-nfs}/nm2-test-pod-b1.yaml (100%) rename {scratch => hack/k8s-nfs}/nm2-test-pod-b2.yaml (100%) rename {scratch => hack/k8s-nfs}/nm2-test-pod-e.yaml (100%) rename {scratch => hack/k8s-nfs}/scion-nfs-pv.yaml (100%) rename {scratch => hack}/minttoken/main.go (100%) create mode 100644 pkg/hub/tid_test.go delete mode 100644 scratch/dbdiag2/main.go diff --git a/hack/README.md b/hack/README.md index a2821c9cf..16170f255 100644 --- a/hack/README.md +++ b/hack/README.md @@ -4,6 +4,8 @@ Developer convenience scripts for local development, testing, and infrastructure ## Contents +### Scripts + | Script | Purpose | |--------|---------| | `setup.sh` | Set up an isolated test environment | @@ -16,4 +18,18 @@ Developer convenience scripts for local development, testing, and infrastructure | `merge-work.sh` | Merge agent work branches | | `version.sh` | Display version information | -These scripts are for development and operations -- not end-user tooling. +### Go Tools + +| Tool | Purpose | +|------|---------| +| `go run ./hack/apitest` | Stress tests API-level multi-hub integration against shared Postgres DB | +| `go run ./hack/dbdiag` | Diagnoses database connection pool usage and active advisory locks | +| `go run ./hack/minttoken` | Mints a long-lived user access-token JWT for local API integration testing | + +### Kubernetes Test Manifests + +| Manifests | Purpose | +|-----------|---------| +| `k8s-nfs/` | Pod and PV configurations for testing GKE NFS shared workspace mount scenarios | + +These scripts and tools are for development and operations -- not end-user tooling. diff --git a/scratch/apitest/main.go b/hack/apitest/main.go similarity index 97% rename from scratch/apitest/main.go rename to hack/apitest/main.go index 336e61306..79c625d6a 100644 --- a/scratch/apitest/main.go +++ b/hack/apitest/main.go @@ -5,8 +5,9 @@ // internal network. Not part of the product. // // Env: -// A_BASE, B_BASE base URLs (e.g. http://localhost:8080, http://10.128.15.241:8080) -// A_TOK, B_TOK admin bearer tokens (per-hub signing keys) +// +// A_BASE, B_BASE base URLs (e.g. http://localhost:8080, http://10.128.15.241:8080) +// A_TOK, B_TOK admin bearer tokens (per-hub signing keys) package main import ( diff --git a/scratch/dbdiag/main.go b/hack/dbdiag/main.go similarity index 100% rename from scratch/dbdiag/main.go rename to hack/dbdiag/main.go diff --git a/hack/k8s-nfs/README.md b/hack/k8s-nfs/README.md new file mode 100644 index 000000000..dede6eaba --- /dev/null +++ b/hack/k8s-nfs/README.md @@ -0,0 +1,24 @@ +# Kubernetes NFS Shared Workspace Test Manifests + +These manifests are developer convenience resources used for testing and validating NFS-based shared workspace coordination under Kubernetes (such as GKE). They correspond to the architecture and design guidelines described in `.design/nfs-workspace.md`. + +## Contents + +- `scion-nfs-pv.yaml`: Configures the PersistentVolume (PV) and PersistentVolumeClaim (PVC) targeting a shared NFS storage server. +- `nm2-test-pod-a.yaml`: Scenario A. A single pod that mounts the PVC at a project-specific subpath, provisions a Git workspace using an init container, and performs permission and filesystem isolation checks. +- `nm2-test-pod-b1.yaml` / `nm2-test-pod-b2.yaml`: Scenario B. Concurrent pods sharing the same PVC on different subpaths, validating parallel provisioning and runtime isolation. +- `nm2-test-pod-e.yaml`: Scenario E. An advanced multi-container pod template verifying volume mounts, mount boundaries, and runtime execution behavior. + +## How to Use + +Apply the volume configurations followed by the test scenarios to verify your cluster's NFS volume mount and isolation mechanics: + +```bash +# Setup PV and PVC +kubectl apply -f scion-nfs-pv.yaml + +# Run test scenario A +kubectl apply -f nm2-test-pod-a.yaml +kubectl get pod nm2-test-agent-a -n scion-agents -w +kubectl logs nm2-test-agent-a -n scion-agents +``` diff --git a/scratch/nm2-test-pod-a.yaml b/hack/k8s-nfs/nm2-test-pod-a.yaml similarity index 100% rename from scratch/nm2-test-pod-a.yaml rename to hack/k8s-nfs/nm2-test-pod-a.yaml diff --git a/scratch/nm2-test-pod-b1.yaml b/hack/k8s-nfs/nm2-test-pod-b1.yaml similarity index 100% rename from scratch/nm2-test-pod-b1.yaml rename to hack/k8s-nfs/nm2-test-pod-b1.yaml diff --git a/scratch/nm2-test-pod-b2.yaml b/hack/k8s-nfs/nm2-test-pod-b2.yaml similarity index 100% rename from scratch/nm2-test-pod-b2.yaml rename to hack/k8s-nfs/nm2-test-pod-b2.yaml diff --git a/scratch/nm2-test-pod-e.yaml b/hack/k8s-nfs/nm2-test-pod-e.yaml similarity index 100% rename from scratch/nm2-test-pod-e.yaml rename to hack/k8s-nfs/nm2-test-pod-e.yaml diff --git a/scratch/scion-nfs-pv.yaml b/hack/k8s-nfs/scion-nfs-pv.yaml similarity index 100% rename from scratch/scion-nfs-pv.yaml rename to hack/k8s-nfs/scion-nfs-pv.yaml diff --git a/scratch/minttoken/main.go b/hack/minttoken/main.go similarity index 100% rename from scratch/minttoken/main.go rename to hack/minttoken/main.go diff --git a/pkg/hub/events_postgres_test.go b/pkg/hub/events_postgres_test.go index 6309a08c8..e5aa48f1e 100644 --- a/pkg/hub/events_postgres_test.go +++ b/pkg/hub/events_postgres_test.go @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +//go:build !no_sqlite + package hub import ( diff --git a/pkg/hub/teststore_test.go b/pkg/hub/teststore_test.go index 7e68c5298..5ffe299f5 100644 --- a/pkg/hub/teststore_test.go +++ b/pkg/hub/teststore_test.go @@ -24,18 +24,8 @@ import ( "github.com/GoogleCloudPlatform/scion/pkg/ent/entc" "github.com/GoogleCloudPlatform/scion/pkg/store" "github.com/GoogleCloudPlatform/scion/pkg/store/entadapter" - "github.com/google/uuid" ) -// tid deterministically maps a human-readable test identifier (e.g. "user-1") -// to a stable UUID string. The Ent-backed store uses UUID primary keys, so test -// fixtures cannot use arbitrary strings as IDs; wrapping a readable name in tid -// preserves test legibility and cross-reference consistency (tid("user-1") -// always returns the same UUID) while satisfying the UUID requirement. -func tid(name string) string { - return uuid.NewSHA1(uuid.NameSpaceOID, []byte(name)).String() -} - // testStoreSeq generates unique in-memory database names so each call to // newTestStore(":memory:") gets an isolated database. var testStoreSeq atomic.Int64 diff --git a/pkg/hub/tid_test.go b/pkg/hub/tid_test.go new file mode 100644 index 000000000..370b3c1d2 --- /dev/null +++ b/pkg/hub/tid_test.go @@ -0,0 +1,28 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package hub + +import ( + "github.com/google/uuid" +) + +// tid deterministically maps a human-readable test identifier (e.g. "user-1") +// to a stable UUID string. The Ent-backed store uses UUID primary keys, so test +// fixtures cannot use arbitrary strings as IDs; wrapping a readable name in tid +// preserves test legibility and cross-reference consistency (tid("user-1") +// always returns the same UUID) while satisfying the UUID requirement. +func tid(name string) string { + return uuid.NewSHA1(uuid.NameSpaceOID, []byte(name)).String() +} diff --git a/scratch/dbdiag2/main.go b/scratch/dbdiag2/main.go deleted file mode 100644 index 274fb1173..000000000 --- a/scratch/dbdiag2/main.go +++ /dev/null @@ -1,17 +0,0 @@ -package main -import ("context";"fmt";"os";"time";"github.com/jackc/pgx/v5") -func main(){ - ctx:=context.Background() - c,_:=pgx.Connect(ctx,os.Getenv("PG_DSN")); defer c.Close(ctx) - for i:=0;i<14;i++{ - rows,_:=c.Query(ctx,`SELECT client_addr::text, state, count(*) FROM pg_stat_activity WHERE datname='scion_test' AND client_addr IS NOT NULL GROUP BY 1,2 ORDER BY 1,2`) - m:=map[string]int{} - for rows.Next(){var a,s string;var n int;rows.Scan(&a,&s,&n);m[a+"/"+s]=n} - rows.Close() - var locks,waiting int - c.QueryRow(ctx,"SELECT count(*) FROM pg_locks WHERE locktype='advisory'").Scan(&locks) - c.QueryRow(ctx,"SELECT count(*) FROM pg_stat_activity WHERE wait_event_type='Client' AND datname='scion_test'").Scan(&waiting) - fmt.Printf("t+%2ds locks=%d %v\n",i*5,locks,m) - time.Sleep(5*time.Second) - } -} From 36efc1630990aa48c2d771c91d49f994a31f5c52 Mon Sep 17 00:00:00 2001 From: Preston Holmes Date: Fri, 5 Jun 2026 01:02:00 +0000 Subject: [PATCH 014/155] fix(sciontool/hub): prevent token-file test from clobbering live agent token TestClient_StartTokenRefresh exercised RefreshToken -> WriteTokenFile without isolating the token home, so running the suite inside a live agent container overwrote the real ~/.scion/scion-token with the test stub "refreshed-token". Every subsequent Hub call then 401'd with "compact JWS format must have three parts" / "unrecognized token format". - Add SetTokenHome(t.TempDir()) to the test, matching its siblings. - Guard WriteTokenFile: panic under `go test` unless SetTokenHome was called, so a forgotten isolation can never corrupt live state again. Reads remain unguarded (harmless; return empty when absent). --- pkg/sciontool/hub/client.go | 26 +++++++++++++++++++++++++- pkg/sciontool/hub/client_test.go | 6 ++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/pkg/sciontool/hub/client.go b/pkg/sciontool/hub/client.go index a65a7ee2e..20834adc5 100644 --- a/pkg/sciontool/hub/client.go +++ b/pkg/sciontool/hub/client.go @@ -29,6 +29,7 @@ import ( "path/filepath" "strings" "sync" + "testing" "time" state "github.com/GoogleCloudPlatform/scion/pkg/agent/state" @@ -856,12 +857,24 @@ func resolveTokenHome() string { return home } +// tokenHomeOverridden reports whether SetTokenHome has installed a test +// override. WriteTokenFile refuses to write under `go test` unless this is set, +// so a test that forgets SetTokenHome can never clobber a live +// ~/.scion/scion-token (as happened when the suite was run inside an agent +// container, where resolveTokenHome finds the real scion user). +var tokenHomeOverridden bool + // SetTokenHome overrides the token home directory for testing. // Returns a cleanup function that restores the original resolver. func SetTokenHome(dir string) func() { orig := tokenHomeResolver + origOverridden := tokenHomeOverridden tokenHomeResolver = func() string { return dir } - return func() { tokenHomeResolver = orig } + tokenHomeOverridden = true + return func() { + tokenHomeResolver = orig + tokenHomeOverridden = origOverridden + } } // TokenFilePath returns the path to the canonical token file. @@ -876,6 +889,17 @@ func TokenFilePath() string { // Called by sciontool init to seed the initial value and by the refresh // loop to persist updated tokens. Written atomically via temp file + rename. func WriteTokenFile(token string) error { + // Guardrail: under `go test`, refuse to write the real token file unless a + // test has explicitly isolated it via SetTokenHome. resolveTokenHome + // resolves to the live scion user's home inside agent containers, so a test + // that forgets to isolate would silently overwrite a running agent's token + // (seen in the wild: a refresh test persisted the literal "refreshed-token", + // 401-ing the agent). Fail loudly instead of corrupting live state. + if testing.Testing() && !tokenHomeOverridden { + panic("scion/hub: WriteTokenFile called during a test without SetTokenHome(); " + + "call SetTokenHome(t.TempDir()) so tests never overwrite the real ~/.scion/scion-token") + } + path := TokenFilePath() dir := filepath.Dir(path) diff --git a/pkg/sciontool/hub/client_test.go b/pkg/sciontool/hub/client_test.go index 63839263b..2e9a2004d 100644 --- a/pkg/sciontool/hub/client_test.go +++ b/pkg/sciontool/hub/client_test.go @@ -616,6 +616,12 @@ func TestClient_GetToken(t *testing.T) { func TestClient_StartTokenRefresh(t *testing.T) { t.Run("refreshes token at scheduled time", func(t *testing.T) { + // Isolate the token file to a temp dir. Without this, RefreshToken's + // WriteTokenFile would clobber the real ~/.scion/scion-token when the + // suite is run inside an agent container (where the scion user exists). + cleanup := SetTokenHome(t.TempDir()) + defer cleanup() + refreshed := false server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { refreshed = true From eeee331635217af6cb9751c9ac0f97c998099e38 Mon Sep 17 00:00:00 2001 From: Preston Holmes Date: Thu, 4 Jun 2026 21:30:39 -0700 Subject: [PATCH 015/155] =?UTF-8?q?feat(hub):=20multi-node=20broker=20disp?= =?UTF-8?q?atch=20=E2=80=94=20affinity,=20durable=20intent,=20lifecycle=20?= =?UTF-8?q?+=20message=20routing=20(#305)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add canonical engineering glossary (GLOSSARY.md) (#102) * Add engineering glossary (GLOSSARY.md) with canonical terms and cleanup tracker Add a root-level GLOSSARY.md capturing canonical Scion terminology in the ubiquitous-language format (preferred term + synonyms to avoid), grouped by domain cluster, plus an Exceptions & Future Cleanup section tracking known naming-convergence work. Link it from agents.md as the canonical engineering glossary. * Revise glossary: broker reframe, Event Bus, Hub-managed, and term refinements Refine entries from review: redefine Message Broker as the pluggable messaging-integration system (add Broker plugin, Built-in broker); add Event Bus for the NATS real-time/event capability; collapse hub-native/Hub Workspace into Hub-managed project/workspace; tighten Template (harness-agnostic, optional default harness-config), Skill (template-only, Agent Skills link), Profile (named runtime-broker settings bundle), Harness/Harness-config; reframe Hub as the control plane in both modes; add Group and Message Group. Expand Exceptions & Future Cleanup to nine tracked items. * Glossary: restructure headings, add cross-refs, modes table, and new terms - Retitle to "Scion Glossary"; drop the "Language" wrapper and promote the thematic categories to top-level sections - Add an Operations section (Attach, Dispatch) and move Profile next to Runtime Broker - Add a Local/Workstation/Hosted comparison table and "See also" cross-refs across the main confusable term clusters - Reframe the intro around the three-way broker collision (incl. Event Bus) and defer to the disambiguation rule; sentence-case "Shared directory" - Add canonical entries for Secret, Notification, and Schedule - Add a "Potential Future Additions" section cataloguing candidate terms * Glossary: remove Exceptions & Future Cleanup tracker The cleanup items are now tracked by dedicated agents that open GitHub issues and implementation PRs, so the staged tracker no longer lives in the glossary. Reword the two intro/disambiguation references that pointed at the removed section to point at GitHub issues instead. --------- Co-authored-by: Preston Holmes * P0-1: switch Postgres driver from lib/pq to pgx/v5 stdlib - Add github.com/jackc/pgx/v5/stdlib (registers as "pgx") - driver_postgres.go: blank import pgx stdlib instead of lib/pq - OpenPostgres: open via sql.Open("pgx", dsn) + entsql.OpenDB - Introduce PoolConfig (applied to *sql.DB); thread through OpenSQLite/OpenPostgres and update all callers - go mod tidy drops lib/pq * P0-2: add connection pool config to DatabaseConfig - DatabaseConfig gains MaxOpenConns / MaxIdleConns / ConnMaxLifetime plus ConnMaxLifetimeDuration() helper - DefaultGlobalConfig sets sqlite pool defaults (MaxOpenConns=1, load-bearing for write serialization) - applyDatabasePoolDefaults fills postgres defaults (20/5/30m) and forces sqlite MaxOpenConns=1; called in both load paths - Mirror fields in V1DatabaseConfig + both conversion directions - Wire pool settings into entc.OpenSQLite in initStore * P0-3/P0-4: CRUD-parity test harness + spec-driven fixture generator P0-3: pkg/store/storetest/ — backend-agnostic, table-driven CRUD oracle. A Factory(t) -> store.Store is injected; generic Domain[T] descriptors drive Create/Read/Update/Delete (+optional soft-delete)/List-paginate/List-filter. Ships group + policy domains and runs green against today's CompositeStore (SQLite base + Ent DB). Ready to accept a postgresFactory for P3-2. P0-4: internal/fixturegen/ — Go-defined spec seeding >=1 row per table across all 30 domain tables, with edge cases (NULL optionals, max-length strings, nested/unicode JSON, soft-deleted agent, BLOB). Deterministic. 'go run ./internal/fixturegen' emits testdata/hub-v46-fixture.db, prints a 30-table coverage report, and caches the blob to the scratchpad mount. CI gate fails if any table has zero rows. * feat(ent): add 23 new Ent schemas for full table parity (P1-2 + P1-3) * P2: port notification + gcp/github/token domains to Ent entadapter Add Ent-backed implementations of the notification, GCP service account, GitHub App installation, and user access token store sub-interfaces: - notification_store.go: NotificationStore (subscriptions, notifications, templates). Dispatch uses an atomic conditional update as the multi-replica claim primitive, and an optional NotificationPublisher designs in the LISTEN/NOTIFY fan-out for created/dispatched events. - external_store.go: GCPServiceAccountStore + GitHubInstallationStore + UserAccessTokenStore. GitHub create is idempotent (INSERT OR IGNORE semantics), repositories/scopes are JSON, default_scopes is CSV, and tokens support key-hash lookup. Legacy api_keys is intentionally not surfaced. - storetest: add GCPServiceAccount, SubscriptionTemplate, and NotificationSubscription CRUD-parity domains. Does not modify composite.go. * P2: port schedule, maintenance, message domains to Ent entadapter - schedule_store.go: ScheduleStore + ScheduledEventStore sub-interfaces with dialect-aware SELECT FOR UPDATE SKIP LOCKED claim helper for the ListDueSchedules / ListPendingScheduledEvents job-claim paths (plain SELECT on SQLite, SKIP LOCKED on Postgres). - maintenance_store.go: run-state RMW, AbortRunningMaintenanceOps, Go-side seed (uuid.New) replacing SQLite randomblob() UUID seeds. - message_store.go: CRUD, read flags, PurgeOldMessages, design-in PublishUserMessage hook for Postgres LISTEN/NOTIFY. - pkg/ent/client_driver.go: hand-written Client.Driver() accessor for dialect detection + raw locking queries. * feat(entadapter): port user + allowlist/invite domains to Ent (P2) Implements the Ent-backed store adapters for the user and allowlist/invite domains, plus their CRUD-parity oracle descriptors. pkg/store/entadapter/user_store.go (store.UserStore): - CreateUser/GetUser/GetUserByEmail/UpdateUser/UpdateUserLastSeen/ DeleteUser/ListUsers. - Case-insensitive email: emails are normalized to lower case on write (so the plain unique index enforces case-insensitive uniqueness, equivalent to the legacy UNIQUE COLLATE NOCASE) and matched with EmailEqualFold (lower(email)=lower($1)) on read. ent codegen + AutoMigrate cannot emit a real lower(email) functional index across both SQLite (tests) and Postgres, so the invariant is enforced at the port layer. - Offset-based pagination matching the legacy SQLite store. pkg/store/entadapter/allowlist_store.go (store.AllowListStore + store.InviteCodeStore): - Full allow-list + invite-code CRUD. - BulkAddAllowListEntries uses CreateBulk + OnConflictColumns(email). Ignore() for race-safe INSERT-OR-IGNORE; added/skipped counts mirror the legacy per-row semantics (existing + within-batch dups skipped). - IncrementInviteUseCount is a single atomic conditional UPDATE (revoked=false AND not expired AND (max_uses=0 OR use_countkey projection, version bump on update, get-then-update upsert, and transitive ListProgenySecrets via a created_by IN-list over the ancestor set (user scope + allow_progeny only; encrypted value withheld). - entadapter/template_store.go: TemplateStore implementing store.TemplateStore + store.HarnessConfigStore. base_template hierarchy, scope/project_id backwards-compat lookups, content_hash, JSON config/files columns, DeleteByScope. Subscription templates are owned by NotificationStore. - Direct Ent unit tests incl. a progeny-inheritance parity test. - storetest: Template/HarnessConfig/Secret/EnvVar domain descriptors wired into RunStoreSuite for cross-backend CRUD parity. * P2: port project/broker + brokersecret domains to Ent Port the project/broker domain (projects, runtime_brokers, project_contributors, project_sync_state) and the broker-auth domain (broker_secrets, broker_join_tokens) from raw SQL to Ent adapters. - pkg/store/entadapter/project_store.go: implements ProjectStore, RuntimeBrokerStore, ProjectProviderStore and ProjectSyncStateStore. * provider + sync-state upserts use Ent OnConflict().UpdateNewValues() (sql/upsert) keyed on the (project_id, broker_id) unique index. * runtime broker heartbeat/update use an optimistic version-CAS loop on a new internal lock_version token, serializing concurrent writers portably across SQLite (tests) and Postgres without SELECT ... FOR UPDATE. * slug lookups support case-insensitive matching (EqualFold). * project computed fields (AgentCount, ActiveBrokerCount, ProjectType) are derived via Ent queries, matching the legacy SQLite store. - pkg/store/entadapter/brokersecret_store.go: implements BrokerSecretStore (per-broker HMAC secrets + short-lived join tokens, expiry cleanup). - Project Ent schema: add operational fields for full parity (default_runtime_broker_id, shared_dirs, github_*, git_identity). - RuntimeBroker Ent schema: relax vestigial type column to Optional, add internal lock_version concurrency token. - Regenerate Ent with sql/upsert,sql/lock features. - storetest: add Project, RuntimeBroker, BrokerSecret and BrokerJoinToken CRUD-parity domains. - Unit tests for both adapters. Per the integration plan, composite.go wiring and ensureEntProject shadow removal are deferred to P2-collapse. * P2: port agent domain to Ent entadapter (XL) * chore(ent): regenerate Ent code for all 30 entity schemas Regenerated with --feature sql/upsert,sql/lock to support OnConflict upserts and ForUpdate/SKIP LOCKED job claims. * P2-collapse: collapse dual-DB into single Ent store Wire all Ent-backed sub-stores into CompositeStore via embedding, removing the raw-SQL base store and the User/Agent/Project shadow-sync machinery (ensureEntUser/ensureEntAgent/ensureEntProject). CompositeStore now serves every domain from a single Ent client and implements Close/Ping/Migrate directly. Collapse initStore() to open one Ent SQLite DB (no _ent shadow DSN, no MigrateGroveToProjectData, no raw sqlite.New). Register the User, AllowList, and InviteCode domains in the storetest CRUD-parity suite. Update entadapter tests for the single-DB NewCompositeStore(client) signature. go build ./... green; go test ./pkg/store/entadapter/... ./pkg/store/storetest/... green. * P2-delete: remove raw-SQL store implementation Delete the ~6k-LOC raw-SQL store (sqlite.go) and its per-domain sibling files (brokersecret, gcp_service_account, github_installation, maintenance, messages, notification, project_sync_state, schedule, scheduled_event) plus their tests, including the inline schema-migration scaffold. Keep driver.go, which registers the pure-Go SQLite driver used by Ent's SQLite backend. Repoint the two non-test consumers to the Ent-backed store: - cmd/hub_secret_migrate.go now opens an Ent client + CompositeStore. - internal/fixturegen opens via entc and seeds the Ent schema's *sql.DB. go build ./... green; no remaining production references to the raw store. * test: compile-migrate downstream suites to Ent store + fix signing-key PK Replace the removed raw-SQL store in downstream tests with an Ent-backed newTestStore helper (pkg/hub, pkg/secret) and update cmd/server_test.go and internal/fixturegen tests. Port the 8 raw-SQL DB() access sites in hub tests via a new CompositeStore.DB() escape-hatch accessor. Fix a production bug surfaced by the collapse: hub/server.go signingKeySecretID generated a non-UUID secret primary key, which the Ent secret store rejects; it now derives a deterministic UUIDv5. go build ./... green; entadapter and storetest suites green. NOTE: hub/secret/fixturegen suites now COMPILE but many tests still fail because their fixtures seed non-UUID string IDs that the UUID-PK Ent schema rejects; addressed in follow-up commits (tid() helper). * test(hub): map non-UUID fixture IDs to UUIDs via tid() helper Wrap human-readable test identifiers in tid() (deterministic UUIDv5) so the UUID-PK Ent store accepts them while preserving cross-reference consistency and ID-equality assertions. Reduces pkg/hub failures from 611 to 79; remaining failures are behavioral, not ID-format, and are addressed separately. * fix(store): seed maintenance ops in Migrate; initStore uses Migrate Restore raw-SQL parity: CompositeStore.Migrate now runs AutoMigrate and seeds built-in maintenance operations (the raw store seeded these in its migrations). initStore and hub test helpers call s.Migrate() so production and tests seed consistently. Fixes the maintenance-operation hub tests (404 'Operation not found'). pkg/hub failures 79 -> 71. * test(hub): satisfy Ent NotEmpty validators in fixtures Add slugs/broker names to test fixtures that previously relied on the raw store's lenient (no-validator) inserts: project/agent slugs in the logs test helper, broker slugs in embedded/profile/authz fixtures, and BrokerName on envgather ProjectProvider literals. pkg/hub failures 71 -> 57. * fix(entadapter): Get-by-id returns ErrNotFound for non-UUID identifiers Restore raw-SQL store parity: a malformed identifier cannot match any UUID primary key, so get-by-id lookups now report store.ErrNotFound instead of store.ErrInvalidInput. This matches the raw store (a lookup with a bad id simply returned no row) and is what callers depend on — e.g. resolveTemplate passes a template *name* to GetTemplate and relies on ErrNotFound to fall back to slug-based resolution. New parseGetID helper applied across all 17 get-by-id methods. pkg/hub failures 56 -> 40; entadapter/storetest stay green. * test(hub): fix store-less id wraps and project-route URL paths - controlchannel_client_test: revert tid() wraps (store-less path-builder test; IDs must match the expected literal paths). - github/envgather: project-scoped route handlers resolve the project by UUID id, so build paths with tid(rawID) via fmt.Sprintf instead of the old raw-id literal. pkg/hub failures 40 -> 32. * test(hub): unwrap projectIDFromServiceAccountEmail expectation The tid() sweep over-wrapped a non-ID expected value in a pure-function test; restore the literal GCP project id. * fix(ent): GCPServiceAccount.project_id is a string, not a UUID The GCP service account project_id holds the GCP *cloud project* identifier (e.g. 'my-project-123'), a free-form string — not a UUID. The schema declared it field.UUID, so entadapter CreateGCPServiceAccount/Update did parseUUID(sa.ProjectID) and rejected real GCP project ids, breaking SA mint/create with a 400 in production (storetest masked it by passing a UUID). Change the schema field to field.String, regenerate Ent, and store/read project_id as a string in external_store.go. Fixes ~7 hub GCP tests; pkg/hub 31 -> 23. * test(hub): fix GCP SA project-id assertion and project-settings id Unwrap the over-wrapped 'my-project' expectation now that project_id is a string, and wrap the dynamic project-settings project ID with tid(). * test(hub): revert tid() over-wraps in store-less events_test events_test exercises the in-memory ChannelEventPublisher directly; its ProjectID/IDs are subject-string components, not stored UUIDs. The tid() sweep wrongly rewrote them so published subjects no longer matched the subscriptions (timeouts). Restore the literal values. pkg/hub 19 -> 12. * test(hub): fix maintenance-run path and notifications agentId queries Use tid() UUIDs in the maintenance run-detail path and the notifications agentId query params; guard list indexing with require.Len so a mismatch fails cleanly instead of panicking (panics truncate the package run). * test(hub): wrap remaining fixture IDs revealed after panic-cascade cleared Panics ([0] on empty lists) had been truncating the package run, hiding many failures and starving the tid() sweep. With those guarded, sweep the newly reached tests: wrap dynamic rune-suffix IDs and the setupProjectWithBroker / seedCreatedAgentForHarnessTest helper IDs, and convert raw query-param project IDs to tid(). No UUID-parse errors remain in pkg/hub. * test(hub): unwrap tid() in scheduler_test (mock store, raw ids) scheduler_test uses an in-memory mockScheduledEventStore, not the Ent store, so its ids need no UUIDs; the erroneous tid() wraps broke raw getEvent lookups and caused a nil-pointer panic that truncated the package run. * fix(ent): Template.harness may be empty (raw-store parity) A template imported from a directory that declares no harness type has an empty harness; the raw-SQL store stored it, but the Ent NotEmpty validator made BootstrapTemplatesFromDir silently skip such templates. Drop NotEmpty and regenerate. Removing the [0]-on-empty panics this caused un-truncates the hub package run (true failure count now visible). * test(hub): wrap dynamic fixture IDs in wake/workspace/signing-key tests Wrap tid() around the wake_test, setupWorkspaceProject, and empty-value signing-key secret IDs now reachable after panic removal. No panics in the hub package run. * test(hub): convert raw-id URL path segments to tid() Build GET/PUT/DELETE paths for agents/projects/brokers/templates/harness-configs and workspace sync routes from tid(rawID) so the by-id handlers resolve the entity (raw ids no longer match the UUID PKs). pkg/hub 93 -> 80. * fix(hub): seed creator users for agent-created agents; cascade-delete subscriptions on hard agent delete * test(hub): seed broker slug/name in dispatcher and project_cache fixtures (Ent validators) * fix(entadapter): cascade-delete agents on project delete (raw-store parity); test(hub): seed FK users, broker_name, deterministic UUIDs * test(hub): MaxOpenConns=1 for SQLite test store (serialize writes); tid() URLs + FK user seeds in events/stopall * test(hub): unwrap over-wrapped tid() in unit tests (workspace/logfilter/gcp/web); valid-UUID NotFound cases; tid() scheduled-event URLs * fix(ent): allow empty display_name (raw-store NOT NULL parity, email fallback); test(hub): seed FK owner users, UUID policy/broker/agent IDs in authz remediation * feat(migrate): add Migration β tool (Ent-SQLite → Ent-Postgres) Implements 'scion server migrate --from sqlite://... --to postgres://...' per postgres-strategy.md §7.3. - entc.OpenSQLiteReadOnly: opens source with PRAGMA query_only=ON (no WAL write), MaxOpenConns=1 so the source is never mutated. - entc.MigrateData: generic reflection-based, dependency-ordered copy of all 30 Ent entities (FK-ordered core first), idempotent (skips rows whose PK already exists), atomic per entity (txn), chunked CreateBulk, source/dest row-count verification after each entity, plus the Group.child_groups M2M edge. FK columns are plain fields so edges are preserved via setters. - cmd/server migrate: DSN parsing (sqlite://, file:, bare path; postgres URL or keyword form), --keep-source default / --drop-source cutover, progress logging. Verified end-to-end against live CloudSQL Postgres 16 (integration test + real CLI run): full copy, idempotent re-run, FK + M2M + value round-trips, --drop-source removal. * feat(concurrency): dialect-aware multi-replica primitives for Postgres (P3-3..6) Add cluster-coordination primitives so N stateless hub processes can share one Postgres, each degrading to a no-op on single-writer SQLite: - store.AdvisoryLocker + entadapter TryAdvisoryLock (pg_try_advisory_lock on a dedicated conn); Scheduler.RegisterRecurringSingleton gates the heartbeat, stalled, purge, schedule-evaluator and github-health sweeps to one replica/tick. - store.ScheduledEventClaimer + ClaimScheduledEvent atomic claim; fireEvent claims one-shot events before side effects (dedup across replica startup recovery). - CompositeStore.RunSerializable: SERIALIZABLE + retry on 40001/40P01 (single run on SQLite) for future multi-row invariants. - dbmetrics.StartPoolSampler feeds DB connection-pool gauges to the P0-5 scaffold; wired into StartBackgroundServices via SetDBMetrics. Verified existing primitives correct (agent StateVersion CAS, FOR UPDATE sweeps, notification atomic dispatch). Found and documented the schedule SKIP LOCKED early-commit gap (lock released before the status transition), closed by the singleton evaluator. Audit + budget docs in scratchpad. Tests: locking_test.go (advisory no-op, serializable, claim exactly-once incl. 8-way concurrent), pool_sampler_test.go. * feat(hub): widen events to EventPublisher interface + Postgres LISTEN/NOTIFY publisher P3-7: Decouple call sites from the concrete *ChannelEventPublisher. - Add Subscribe(patterns...) (<-chan Event, func()) to the EventPublisher interface; implement it on noopEventPublisher (nil channel) — *ChannelEventPublisher already had it. - Factor the Publish* methods into a shared eventBuilder (sink func) so every backend emits identical subjects/payloads; ChannelEventPublisher embeds it. - web.go (field + SetEventPublisher), messagebroker.go and notifications.go (field + constructor) now take EventPublisher; handlers_messages.go gates SSE on "not the no-op publisher" instead of a concrete type assertion. P3-8: PostgresEventPublisher over pgx LISTEN/NOTIFY (cross-replica delivery). - Per-grove channels plus a global channel (flat exact-match); event type in the JSON envelope. Grove-scoped subjects publish to both the grove channel and the global channel; subscriptions group their patterns by resolved channel so an event is matched only against patterns that opted into the arriving channel (no double delivery). - 8 KB NOTIFY limit handled by reference-and-refetch via scion_event_payloads (TTL-swept so every replica can refetch). - PublishTx enrolls the NOTIFY in a caller transaction (atomic write+publish; rollback => no deliver). Delivery flows exclusively through the listener. - Listener goroutine reconnects with backoff and re-LISTENs (resubscribe); dynamic LISTEN/UNLISTEN applied on a poll (WaitForNotification timeout does not invalidate the pgconn connection). - Emits pkg/observability/dbmetrics signals (published/delivered/dropped, payload size, publish->deliver latency, reconnects, pool stats). - cmd: newEventPublisher selects the backend by database driver (postgres => PostgresEventPublisher, else ChannelEventPublisher) with safe fallback. Tests: routing/registry/payload-offload/metrics/transactional-executor unit tests run without a DB; cross-replica delivery, oversized round-trip, transactional rollback, and reconnect+resubscribe are gated behind SCION_TEST_POSTGRES_DSN. go build ./... green; full pkg/hub suite green. Note: server.go's equivalent type-assertion cleanup is left in the working tree (co-edited with concurrent P0-5/scheduler work) and is functionally optional — HEAD server.go already compiles against the widened interface. * test(store): parameterize store suites over {sqlite, postgres} (P3-2) Add pkg/store/enttest: a backend-selecting Ent client factory for the store test suites. Default is in-memory SQLite; built with -tags integration and SCION_TEST_POSTGRES_URL set, it provisions a per-package ephemeral Postgres database (created/dropped via TestMain) and isolates each test in its own schema (search_path) so tests never observe each other's rows. Falls back to SQLite when the env var is unset. Route all entadapter and storetest helpers through enttest.NewClient so the same CRUD-parity oracle runs unchanged against either backend. Fix two real Postgres bugs surfaced by the new path: - entadapter/dialect.go ancestryContains: emit the bind parameter via Builder.Arg ($n on Postgres) instead of a literal '?' through ExprP, which was not rebound and produced a syntax error; and use jsonb_array_elements_text (the column is jsonb on Postgres, not json). - schedule_store_test ClaimPath: make the concurrent-claim assertion backend-aware. SQLite serializes (MaxOpenConns=1, no SKIP LOCKED) so every caller sees both due rows; Postgres uses FOR UPDATE SKIP LOCKED so concurrent callers may observe a disjoint subset (0..2) and must only never error or exceed 2. Verified: full SQLite suite green; storetest CRUD parity green on CloudSQL Postgres; entadapter green on Postgres (schedule ClaimPath fix confirmed). * fix(hub): harden Postgres event publish + verify wiring; lower PG pool default Task 1 — LISTEN/NOTIFY publish path: - Add TestPostgresIntegration_HandlerCreateProjectEmitsNotify: drives the real POST /api/v1/projects handler with a PostgresEventPublisher and asserts a pg_notify lands on scion_ev_global via an independent raw LISTEN — the exact capability the multi-replica live test probed. Verified PASSING against live CloudSQL, proving the handler -> s.events -> pg_notify wiring is correct end to end (the four pre-existing SCION_TEST_POSTGRES_DSN integration tests also pass). The multi-hub 'no NOTIFY' symptom was not reproducible against the current tree. - Bound the autocommit publish (Publish* methods) with publishTimeout (5s). These run synchronously on the caller's (request handler) goroutine and acquire from the event pool; on a connection-starved instance that acquire could block indefinitely, stalling CRUD and silently never emitting NOTIFY. The timeout converts that into a logged error + dropped event (publishing is fire-and-forget). PublishTx (transactional path) is unaffected. Task 2 — connection budget: - Lower the default Postgres MaxOpenConns 20 -> 10 so multiple replicas fit a modest connection budget (see CONNECTION-BUDGET.md). CloudSQL instance scion-postgres-test resized db-f1-micro -> db-g1-small and max_connections set to 100 (out of band). * test(store): add Postgres stress/integration suite (contention, isolation, pool, NOTIFY, migration, schema, multi-process) Add pkg/store/integrationtest/: a Postgres-only suite that exercises behavior the SQLite parity suites cannot reach. Gated by //go:build integration and SCION_TEST_POSTGRES_URL; skips cleanly otherwise. Coverage: - Contention: state_version CAS race (no lost updates, >=N-1 retries, final version==1+N), SKIP LOCKED / conditional-UPDATE event claim (single winner + disjoint drain), unique-key races (project slug, user email, agent slug). - Isolation: SERIALIZABLE conflict + RunSerializable retry recovery, REPEATABLE READ no-phantom snapshot, READ COMMITTED dirty-read prevention. - Pool: exhaustion + queued recovery, saturated pool honoring context deadline, long txn not starving short queries, healing after pg_terminate_backend. - LISTEN/NOTIFY: ordered burst no-drop, 8000B payload limit, listener reconnect/resume, cross-channel isolation. - Migration: 1000+ row counts + bounded-memory listing, idempotent re-migration. - Schema: NULL semantics, unicode/emoji, nested JSON + special chars, large-text non-truncation, TIMESTAMPTZ microsecond precision. - Multi-process: forks the test binary for cross-process advisory-lock exclusivity and cross-process NOTIFY delivery. Configurable concurrency via SCION_TEST_CONCURRENCY (default 10). Extend pkg/store/enttest with Active() and NewSchemaURL() so tests can open custom-pool clients and share a DSN with forked child processes; non-integration stubs keep the package API stable. * fix(db): recycle stale conns + keepalives; skip singleton tick on lock error Stale-connection pool stalls (CloudSQL drops idle conns after ~10m): - Add ConnMaxIdleTime to DatabaseConfig/PoolConfig (default 5m pg, 0 sqlite) and apply SetConnMaxIdleTime on the database/sql pool. - OpenPostgres now parses the DSN with pgx and opens via stdlib.OpenDB with TCP keepalive GUCs (idle 60s / interval 15s / count 4) and a 10s connect timeout, so a silently-dropped peer is detected instead of the first query after idle hanging on a dead socket. - pgx event pool (events_postgres.go): set keepalives + connect timeout on both the pool's ConnConfig and the dedicated listener connection, plus MaxConnIdleTime 5m / MaxConnLifetime 30m. Advisory-lock leader election (scheduler.go): - A lock-acquisition error no longer falls open to running the handler unguarded (which would duplicate singleton work across replicas); the tick is skipped and retried next interval. Added regression tests. Test harness (enttest/integrationtest): - Accept libpq keyword/value DSNs (not just URL form) when deriving the ephemeral db/schema/params; add WithConnParam helper. - Fix migration idempotency test's per-pass row-count expectation. * fix(store): bound advisory-lock conn checkout + unlock with short timeout TryAdvisoryLock checked a connection out of the pool and ran the unlock on the full 55s scheduler-handler context (acquire) and an unbounded context.Background() (release). On a pool that could not promptly serve a healthy connection, db.Conn() blocked for the entire 55s before failing with 'context deadline exceeded' on every tick; with several singleton handlers firing each 60s tick, those long-blocked goroutines and their pending pool connection requests piled up across ticks and kept the pool jammed (checked out client-side, idle server-side). The unbounded unlock was a second leak vector: if the held connection died mid critical-section, ExecContext could hang forever, so conn.Close() never ran and the connection leaked out of the pool permanently. Bind both the acquire (db.Conn + pg_try_advisory_lock) and the release (pg_advisory_unlock) to a 5s timeout so a bad tick fails fast and retries next tick instead of parking a goroutine for ~55s, and so a dead connection can never block release from freeing the conn. Lock semantics are unchanged: cancelling the acquire context tears down only that context, not the checked-out session that holds the lock. * feat(migrate): in-process migration α (legacy raw-SQL hub.db → Ent) Upgrade a legacy raw-SQL Hub database (the ~53-migration, 30-table schema from the removed pkg/store/sqlite store) to the consolidated Ent-backed SQLite schema, in-process on first boot, behind an automatic backup. pkg/ent/entc/migrate_alpha.go: - IsLegacyRawSQLSchema: detect via the schema_migrations sentinel + the legacy-only agents.agent_id column (no-op for an Ent/empty/absent file). - MigrateAlphaSQLite: backup (checkpoint WAL + copy to hub.db.bak.), AutoMigrate a fresh Ent schema, ATTACH the legacy file, copy every table with INSERT…SELECT (foreign_keys OFF), verify per-table row counts, then atomically swap the migrated file into place. - Data-driven column mapping (created_at→created, updated_at→updated, agents.agent_id→slug, policies→access_policies); bespoke SQL for the group_members/policy_bindings polymorphic splits and surrogate ids; groups.parent_id→group_child_groups edge. - Deterministic UUIDv5 remap for legacy non-UUID primary keys (internal signing-key secrets; plugin runtime-broker ids) with consistent rewrite of every foreign-key reference via a TEMP _id_remap table. - Tolerates missing legacy tables (older schema versions). cmd/server_foreground.go: detect + migrate in initStore's sqlite path, with a --no-auto-migrate operator opt-out (cmd/server.go). Validated end-to-end against four production hub.db files (scion-integration, -integration2, -demo, -gteam): exact row-count parity (up to ~19k rows), every entity reads back through the live Ent store, idempotent re-runs, and broker FK references resolve post-remap. Pre-existing dangling agent created_by/owner_id refs are faithfully preserved (loader runs FK-off). * fix(config): apply real Postgres pool size (leaked SQLite default of 1 starved the pool) The struct-level default for Database.MaxOpenConns/MaxIdleConns is 1 — the value SQLite REQUIRES to serialize writes. applyDatabasePoolDefaults only bumped postgres to a real pool when the value was <= 0, but a postgres deployment configured via env/driver override inherits the embedded default of 1, so the guard never fired and the Ent pool ran with a SINGLE connection. Effect in production (both integration hubs): every singleton scheduler tick checks out the lone pool connection to hold its advisory lock, then blocks waiting for a second connection to do its work — a self-deadlock that resolves only at the 55s handler context deadline. All API requests serialize behind the one connection, so GET /api/v1/* served in ~55s across the board. Note env overrides could not paper over this: envKeyToConfigKey splits on every underscore, so SCION_SERVER_DATABASE_MAX_OPEN_CONNS maps to database.max.open.conns, not database.max_open_conns — silently ignored. Treat the leaked SQLite default (<= 1) as 'unset' for postgres so the pool default (10) applies; explicit sizing of 2+ is still respected. SQLite remains pinned to 1. Adds regression tests for all three cases. * feat(hub): per-process instanceID on Server (B1-1) Add a unique per-process instanceID to Server, generated at construction via uuid.NewString(). Optionally prefixed with POD_NAME env var for log readability, but uniqueness is always guaranteed by the UUID. This ID serves as the affinity key for broker dispatch (design §4.1) and is intentionally distinct from config.ResolveHubID, which is shareable across replicas. * feat(schema): affinity columns on runtime_brokers (B1-2) Add 3 nullable fields to the runtime_brokers ent schema and store model for tracking which hub instance holds the control-channel socket: - connected_hub_id (TEXT, optional/nullable) - connected_session_id (TEXT, optional/nullable) - connected_at (TIMESTAMPTZ, optional/nullable) Dialect-neutral (no Postgres-only annotations) — AutoMigrate works on both SQLite and CloudSQL Postgres per postgres-strategy.md §6.4. Wire the fields through the ent<->store conversion code in both directions (entBrokerToStore, CreateRuntimeBroker, UpdateRuntimeBroker). Regenerated ent code included. * feat(store): Claim/Release runtime-broker affinity CAS methods (B1-3) Mirrors UpdateRuntimeBrokerHeartbeat's lock_version CAS loop. - ClaimRuntimeBrokerConnection: newest-wins, sets affinity + status=online + heartbeat in one write - ReleaseRuntimeBrokerConnection: compare-and-clear, returns cleared=false (no-op) if affinity moved (disconnect-race fix) Tests cover claim/overwrite/clear/no-op + A->B flap (design 9.4). * fix(hub): thread sessionID through connect + fix onDisconnect clobber race (B1-4, B1-5) B1-4: HandleUpgrade returns sessionID; markBrokerOnline(brokerID, sessionID) now calls ClaimRuntimeBrokerConnection(brokerID, instanceID, sessionID), recording affinity + online + heartbeat in one CAS write. B1-5: SetOnDisconnect callback gains sessionID; the handler compare-and-clears via ReleaseRuntimeBrokerConnection and skips the offline stamp when affinity has moved (flap). removeConnection now only removes/fires for the matching session, so an old connection's teardown can't drop a newer live socket. * feat(schema): broker_dispatch intent table + messages dispatch-state (B2-1, B2-2) B2-1: new BrokerDispatch ent entity (table broker_dispatch) — id, broker_id, agent_id(null), agent_slug, project_id(null), op, args(JSON), state, result, claimed_by, attempts, error, created_at/updated_at, deadline_at(null); index (broker_id,state). store.BrokerDispatch model + state constants. B2-2: messages.dispatch_state (default 'pending') + dispatched_at; wired through store.Message + entadapter conversion/create. Dialect-neutral. * feat(hub): PostgresCommandBus LISTEN/NOTIFY signal listener on scion_broker_cmd (B2-4) Introduce a CommandBus interface and PostgresCommandBus implementation that listens on the new global channel scion_broker_cmd for broker dispatch wakeup signals. This is a sibling of PostgresEventPublisher, reusing the same connect/reconnect/keepalive helpers but maintaining its own independent pgx connection and pool (design §5.1). Key components: - PostgresCommandBus: LISTEN loop with backoff-reconnect on its own dedicated connection; filters signals by local broker ownership via an injected ownsLocally func (wired to ControlChannelManager.IsConnected); invokes an injected onSignal reconcile callback (to be wired to the reconcile drain in B2-5). - NotifyBrokerCmd: issues NOTIFY inside the caller's transaction so the signal commits atomically with the durable intent row (mirrors PublishTx). - NoopCommandBus: safe no-op for the SQLite backend (single-process, all brokers are local). - Backend selection in newCommandBus mirrors newEventPublisher: Postgres driver → PostgresCommandBus; otherwise → NoopCommandBus. - Server.SetCommandBus/CommandBus() setter/getter; cleanup in both Shutdown and CleanupResources paths. * feat(store): BrokerDispatch store methods + message dispatch CAS (B2-3) BrokerDispatchStore: Insert/Claim(CAS pending->in_progress)/Complete/Fail/ ListPendingDispatch + MarkMessageDispatched(CAS)/ListPendingMessages (via agent runtime_broker_id). Wired into CompositeStore + store.Store. Tests: concurrent claim single-winner (exactly-once), drain pending-only, message CAS dedupe, complete/fail transitions, pending-messages-by-broker-agent. * feat(hub): reconcile-on-connect drain wired to bus + markBrokerOnline (B2-5) Server.reconcileBroker drains pending broker_dispatch rows (CAS-claim -> exec -> done/fail) and pending messages (CAS MarkMessageDispatched -> deliver) for a broker this node owns. Exactly-once via store CAS; idempotent + concurrent-safe. Wired as durability backstop into markBrokerOnline (async on reconnect) and as the command-bus signal handler (SetOnSignal -> ReconcileBroker). Op executors are seams (executeDispatch/deliverMessage) that Phase 3/4 fill with local tunnel ops. * feat(hub): route() decision in HybridBrokerClient (B3-1) routeLocal (IsConnected, unchanged fast path) | routeForward (affinity owner alive) | routeHTTP (broker endpoint set) | routeUndeliverable. Affinity is a hint only (StoreAffinityLookup over connected_hub_id + last_heartbeat freshness), injectable for testing. Not yet wired into dispatch (B3-2 wires message path). Table-driven tests over all branches incl. local-precedence + nil-affinity. * feat(hub): cross-node message dispatch via route()+intent+signal+owner drain (B3-2, B3-3) Route-gate the message send path: HybridBrokerClient.MessageAgent now uses route(brokerID, endpoint) to decide delivery. routeLocal and routeHTTP follow existing paths unchanged. routeForward/routeUndeliverable return ErrMessageDeferred — the message row (already persisted with dispatch_state=pending) is the durable intent. All call sites (handleAgentMessage, set[], broadcastDirect, messagebroker, notifications, scheduler) catch the sentinel, emit a best-effort NOTIFY wakeup via SignalBrokerCmd, and return 202 Accepted (or log as deferred). Fill the deliverMessage seam in reconcile.go: resolves the agent from the message's AgentID, obtains the dispatcher, and calls DispatchAgentMessage for local tunnel delivery. reconcileBroker already CAS-marks dispatched before calling this. Wire SetAffinityLookup(StoreAffinityLookup(store, 0)) on the HybridBrokerClient in CreateAuthenticatedDispatcher so route() can return routeForward when another node owns the broker. Add SignalBrokerCmd to the CommandBus interface — a best-effort NOTIFY using the bus's own pool, used by the message path where the durable intent is the message row itself and the NOTIFY is only a wakeup hint. * feat(hub): lifecycle dispatch (rolling-timeout wait + cross-node start/stop/restart) (B4-1, B4-2) B4-1: Rolling-timeout wait helper (dispatch_wait.go) - waitForAgentTransition subscribes to agent..status events and loops with a rolling window (dispatchRollingTimeout=90s) that resets on ANY AgentStatusEvent (phase/activity/detail change). - Terminal phase → return phase, nil. Window expiry → ErrDispatchFailed. Context cancellation → ctx.Err(). - Caller subscribes BEFORE writing intent, passes the channel + unsub. B4-2: Cross-node start/stop/restart dispatch - Route-gated HybridBrokerClient.StartAgent/StopAgent/RestartAgent exactly like MessageAgent: routeLocal → control-channel tunnel (unchanged fast path), routeHTTP → HTTP fallback, routeForward/routeUndeliverable → ErrLifecycleDeferred. - Dispatch args structs (dispatch_args.go): StartDispatchArgs captures task, resolvedEnv, resolvedSecrets, inlineConfig, sharedDirs, sharedWorkspace, projectPath, projectSlug, harnessConfig. RestartDispatchArgs captures resolvedEnv. StopDispatchArgs is empty. All JSON-serializable for broker_dispatch.args column. - Owner-side executeDispatch (reconcile.go): start/stop/restart cases deserialize args, load agent from store, call local DispatchAgentStart/Stop/Restart via the dispatcher. Unknown ops (delete, finalize_env, etc.) still fail cleanly for B4-3/B4-4. Tests: waitForAgentTransition (terminal, error, rolling reset, silence expiry, context cancel, unsub); route-gating of Start/Stop/Restart returns ErrLifecycleDeferred when non-local; executeDispatch lifecycle cases invoke the local dispatcher; args round-trip (serialize→deserialize) is lossless; reconcile end-to-end lifecycle path. * feat(hub): wire originator-side cross-node lifecycle dispatch (B4-2 complete) The originator-side orchestration was missing: ErrLifecycleDeferred was returned by HybridBrokerClient but nothing caught it. Now the full cross-node start/stop/restart flow works transparently to all handler call sites. Originator side (HTTPAgentDispatcher): - DispatchAgentStart/Stop/Restart catch ErrLifecycleDeferred after env/secret resolution and invoke deferredLifecycle: 1. Subscribe("agent..status") BEFORE writing intent 2. InsertBrokerDispatch{op, agent_id, broker_id, args} 3. Best-effort SignalBrokerCmd (row is durable backstop) 4. waitForAgentTransition with terminal set per op 5. Return nil on success, error on error-phase/timeout - SetCrossNodeDeps(events, commandBus) wired in server.go's getOrCreateDispatcher, so all handler call sites get cross-node for free with synchronous semantics preserved. - Local path (routeLocal) is unchanged at zero added latency — no subscribe, no intent row, no wait. Args decision: owner RE-RESOLVES env/secrets via DispatchAgentStart (all hub instances share the same store + secret backend), so StartDispatchArgs carries only {Task}. RestartDispatchArgs and StopDispatchArgs are empty. This avoids serializing potentially large env/secrets into the DB while remaining correct because all hubs read from the same shared store. waitForAgentTransition refactored to a standalone function (no Server receiver) so the dispatcher can call it directly. Tests: - TestDeferredStart_WritesIntentAndWaits: deferred start writes a broker_dispatch row, waits, returns success on "running" event - TestDeferredStart_ReturnsErrorOnErrorPhase: error phase → error - TestLocalStart_SkipsIntentRow: local path calls tunnel directly, no intent row written - All existing tests pass (no regressions) * fix(hub): make web session replica-portable to fix OAuth state_mismatch OAuth login behind the load balancer intermittently failed with state_mismatch: the CSRF state token (and the entire web session) was stored in a gorilla FilesystemStore on the handling replica's local disk, while the browser only carried a session-ID cookie. When the LB routed /auth/login and /auth/callback to different replicas, the callback replica had no matching session file -> empty state -> state_mismatch. It only "worked" when both hops happened to hit the same backend. The same flaw affected the post-login session: sessionToBearerMiddleware reads the Hub access/refresh JWTs from that disk-local store on every API request, so sessions silently dropped whenever a follow-up request landed on a different replica. Replace the FilesystemStore with an encrypted, signed gorilla CookieStore so the whole session lives in the client's cookie and any replica sharing SESSION_SECRET can read it. Keys are derived deterministically from SESSION_SECRET (32-byte HMAC auth key + 32-byte AES-256 encryption key, domain-separated). No DB, no migration; works with N replicas. The original switch to disk was motivated by a "JWT tokens exceed 4096 bytes" concern. Measured against the current compact HS256 tokens the full session (identity + access + refresh) encodes to ~2.6 KB, well under the browser's ~4 KB per-cookie cap, so the securecookie length limit is left in force (oversize would now error+log, not silently drop). Tests: replace the obsolete NoMaxLengthLimit test with a cross-replica round-trip regression test (cookie minted by replica A decodes on replica B with the same secret; carries OAuth state + post-login tokens) plus a negative test (a different secret cannot decode the cookie). * feat(hub): cross-node delete + create-time data ops dispatch (B4-3, B4-4) Route-gate HybridBrokerClient.DeleteAgent, CheckAgentPrompt, CreateAgentWithGather, and FinalizeEnv through route() so routeForward/routeUndeliverable return ErrLifecycleDeferred (matching start/stop/restart pattern from B4-2). B4-3 (delete dispatch): - deferredDelete on ErrLifecycleDeferred: subscribe broker.dispatch..done → InsertBrokerDispatch{op:delete} → SignalBrokerCmd → waitForDispatchDone (reads DB row, authoritative). - Owner executeDispatch case "delete": deserializes DeleteDispatchArgs → local DispatchAgentDelete (idempotent, 404 ok). - DeleteDispatchArgs struct + UnmarshalDeleteArgs for args round-trip. B4-4 (create-time data ops): - deferredDataOp/deferredDataOpResult: common originator flow for ops that return results via the dispatch row (design §6.3). Subscribe to broker.dispatch..done BEFORE writing intent, insert dispatch, signal, waitForDispatchDone, read result from GetBrokerDispatch. - deferredCheckPrompt: returns bool from CheckPromptResult in row. - deferredFinalizeEnv: fire-and-forget via deferredDataOp. - deferredCreateWithGather: returns envRequirements from row result. - Owner executeDispatch cases: check_prompt, finalize_env, create — run local op, marshal result JSON, return it. - PublishDispatchDone on EventPublisher: slim completion event broker.dispatch..done emitted by reconcile loop on complete/fail. - waitForDispatchDone: event-driven wait with bounded re-read at rolling timeout (missed event recovery, design §6.3). - GetBrokerDispatch added to BrokerDispatchStore interface + entadapter. Local fast path unchanged (routeLocal → zero added latency). * feat(hub): stale-affinity + stuck-dispatch reaper singleton (B5-1) * feat(hub): pending-message sweep + dispatch metrics (B5-2) Add observability for the multi-node broker dispatch pipeline: Sweep: - CountStuckPendingMessages store method (messages pending > threshold) - brokerMessageSweepHandler registered as RecurringSingleton with LockBrokerMessageSweep (0x5C100007), runs every 1m Metrics (pkg/observability/dispatchmetrics): - Counters: dispatch published/claimed/done/failed, message dispatched - Gauge: message stuck (pending beyond 5m threshold) - Histograms: intent-to-done latency, reconcile drain duration - Counter: command bus reconnects Emit sites: - InsertBrokerDispatch → IncPublished (httpdispatcher.go) - ClaimBrokerDispatch → IncClaimed (reconcile.go) - CompleteBrokerDispatch → IncDone + RecordDispatchLatency (reconcile.go) - FailBrokerDispatch → IncFailed (reconcile.go) - MarkMessageDispatched → IncMessageDispatched (reconcile.go) - reconcileBroker → RecordReconcileDrainDuration (reconcile.go) - command bus reconnect → IncCmdBusReconnects (command_bus.go) - sweep handler → ObserveMessageStuck (sweep.go) * fix(hub): derive JWT signing keys from shared SESSION_SECRET to fix cross-replica login loop The cookie-store fix (0515e2a8) made the web session replica-portable, but the Hub JWT *inside* the cookie is still signed with a per-replica key: ensureSigningKey scopes signing keys to (scope=hub, scope_id=hubID) and hubID = sha256(hostname)[:12]. The integration env runs two replicas of one logical hub behind a single LB, sharing one Postgres DB and one SESSION_SECRET but with different hostnames -> different hubIDs -> different HS256 signing keys. So a user JWT minted on replica A failed signature verification on replica B (go-jose: error in cryptographic primitive); refresh failed too (refresh token signed with the same foreign key), so sessionToBearerMiddleware declared the session irrecoverably invalid, DELETED the cookie (MaxAge=-1) and returned session_expired. The cookie deletion turns it into a redirect loop: dashboard flashes, then /login?error=session_expired. Fix: extend the 0515e2a8 approach (replica-portable via the shared secret) from the cookie to the keys inside it. Add ServerConfig.SharedSigningSecret; when set, ensureSigningKey derives the agent and user signing keys deterministically from it (domain-separated by key name) and bypasses per-host secret-backend storage. cmd feeds the same --session-secret / SESSION_SECRET value into both the web cookie store and the hub config via a new resolveSessionSecret() helper. Empty secret keeps the existing per-hub behavior (no regression for single-node/local dev). Tests: cross-replica round trip (different hubID + same secret -> identical keys, token minted on A validates on B; different secret cannot) plus pre-configured-key precedence. Note: rollout rotates the signing keys (now derived from SESSION_SECRET), so existing web/CLI tokens are invalidated once and users re-login. * docs: project log for B5-3 chaos gate — GB5 PASSED (GA gate for broker dispatch) * fix(hub): align fakeHTTPClient.CleanupProject with interface (3 params, not 4) * fix(hub): address PR #305 review feedback - server_migrate.go: use nil-checked deferred close for src DB, and explicitly close src before dropSQLiteFile to prevent Windows sharing violations - server_migrate.go: handle file:// prefix before file: to correctly parse file:///path/to/db URLs - server_foreground.go: evaluate GetControlChannelManager() inside the ownsLocally closure to avoid capturing a stale nil value - server_migrate_test.go: add test case for file:/// URL format - server_test.go: sanitize t.Name() slashes in newTestStore to prevent SQLite path errors in subtests * docs: add project log for PR #305 review feedback fixes * fix(hub): prevent duplicate message delivery, guard dispatch state transitions C1: Call MarkMessageDispatched after successful local dispatch in messagebroker.go and handlers.go (single-recipient, set[], broadcast). Without this, successfully dispatched messages remained dispatch_state=pending and were re-delivered on every broker reconnect via reconcileBroker. C2: Return immediately in messagebroker.go deliverToAgent when CreateMessage fails — without a durable row, a deferred signal has nothing for the owning node to reconcile. C3: Guard CompleteBrokerDispatch and FailBrokerDispatch with state=in_progress CAS predicate so a done dispatch cannot be flipped to failed or vice versa. Update tests to claim before completing/failing to match the new CAS guard. * fix(hub): reconcile broker→eventbus and hub-native→hub-managed renames after rebase Post-rebase fixups to align the feature branch with main's refactoring: - broker package → eventbus package rename (types, imports, methods) - SetRecipient → GroupRecipient, SetMessageResponse → GroupMessageResponse - hubNativeProjectPath → hubManagedProjectPath - ProjectTypeHubNative → ProjectTypeHubManaged - populateAgentConfig gains ctx parameter - Add missing handleResourcesImport and handleMessageChannels handlers - Add ListChannels method to MessageBrokerProxy - Wire newCommandBus in server_foreground.go - Restore main's test fixtures for renamed APIs --------- Co-authored-by: scion-gteam[bot] <271067763+scion-gteam[bot]@users.noreply.github.com> Co-authored-by: Scion --- .../project-log/2026-06-03-b5-3-chaos-gate.md | 31 + ...03-cross-replica-signing-key-login-loop.md | 73 + .../2026-06-05-pr305-review-feedback.md | 32 + .scion/project-id | 1 + GLOSSARY.md | 2 +- agents.md | 7 + cmd/server_foreground.go | 27 + cmd/server_migrate.go | 9 +- cmd/server_migrate_test.go | 20 +- cmd/server_test.go | 4 +- pkg/ent/brokerdispatch.go | 263 +++ pkg/ent/brokerdispatch/brokerdispatch.go | 171 ++ pkg/ent/brokerdispatch/where.go | 956 ++++++++++ pkg/ent/brokerdispatch_create.go | 1437 ++++++++++++++ pkg/ent/brokerdispatch_delete.go | 88 + pkg/ent/brokerdispatch_query.go | 565 ++++++ pkg/ent/brokerdispatch_update.go | 811 ++++++++ pkg/ent/client.go | 197 +- pkg/ent/ent.go | 2 + pkg/ent/hook/hook.go | 12 + pkg/ent/message.go | 29 +- pkg/ent/message/message.go | 18 + pkg/ent/message/where.go | 125 ++ pkg/ent/message_create.go | 143 ++ pkg/ent/message_update.go | 87 + pkg/ent/migrate/schema.go | 42 +- pkg/ent/mutation.go | 1673 ++++++++++++++++- pkg/ent/predicate/predicate.go | 3 + pkg/ent/runtime.go | 39 +- pkg/ent/runtimebroker.go | 46 +- pkg/ent/runtimebroker/runtimebroker.go | 24 + pkg/ent/runtimebroker/where.go | 215 +++ pkg/ent/runtimebroker_create.go | 234 +++ pkg/ent/runtimebroker_update.go | 156 ++ pkg/ent/schema/brokerdispatch.go | 99 + pkg/ent/schema/message.go | 8 + pkg/ent/schema/runtimebroker.go | 9 + pkg/ent/tx.go | 3 + pkg/hub/broker_routing.go | 122 ++ pkg/hub/broker_routing_test.go | 160 ++ pkg/hub/command_bus.go | 337 ++++ pkg/hub/command_bus_test.go | 476 +++++ pkg/hub/controlchannel.go | 41 +- pkg/hub/controlchannel_client.go | 107 +- pkg/hub/controlchannel_test.go | 45 +- pkg/hub/dispatch_args.go | 111 ++ pkg/hub/dispatch_exec_test.go | 711 +++++++ pkg/hub/dispatch_lifecycle_test.go | 252 +++ pkg/hub/dispatch_wait.go | 148 ++ pkg/hub/dispatch_wait_test.go | 312 +++ pkg/hub/events.go | 22 + pkg/hub/handlers.go | 394 +--- pkg/hub/handlers_broker_inbound.go | 7 +- pkg/hub/httpdispatcher.go | 312 ++- pkg/hub/messagebroker.go | 61 +- pkg/hub/notifications.go | 10 +- pkg/hub/reaper.go | 64 + pkg/hub/reconcile.go | 351 ++++ pkg/hub/reconcile_test.go | 245 +++ pkg/hub/server.go | 199 +- pkg/hub/server_instanceid_test.go | 41 + pkg/hub/signing_key_shared_test.go | 126 ++ pkg/hub/sweep.go | 45 + pkg/hub/web.go | 62 +- .../dispatchmetrics/dispatchmetrics.go | 206 ++ .../dispatchmetrics/dispatchmetrics_test.go | 116 ++ pkg/store/concurrency.go | 4 + pkg/store/entadapter/broker_affinity_test.go | 135 ++ .../entadapter/broker_dispatch_store_test.go | 252 +++ pkg/store/entadapter/brokerdispatch_store.go | 337 ++++ pkg/store/entadapter/composite.go | 8 +- pkg/store/entadapter/message_store.go | 15 +- pkg/store/entadapter/project_store.go | 135 +- pkg/store/entadapter/reaper_test.go | 218 +++ pkg/store/models.go | 46 + pkg/store/store.go | 67 + scratch/apitest/main.go | 237 +++ scratch/dbdiag/main.go | 42 + scratch/dbdiag2/main.go | 17 + scratch/minttoken/main.go | 61 + scratch/nm2-test-pod-a.yaml | 76 + scratch/nm2-test-pod-b1.yaml | 71 + scratch/nm2-test-pod-b2.yaml | 71 + scratch/nm2-test-pod-e.yaml | 97 + scratch/scion-nfs-pv.yaml | 31 + shared-dirs/scratchpad/coordinator.md | 75 + .../scratchpad/instance-interaction.md | 88 + 87 files changed, 14279 insertions(+), 548 deletions(-) create mode 100644 .design/project-log/2026-06-03-b5-3-chaos-gate.md create mode 100644 .design/project-log/2026-06-03-cross-replica-signing-key-login-loop.md create mode 100644 .design/project-log/2026-06-05-pr305-review-feedback.md create mode 100644 .scion/project-id create mode 100644 pkg/ent/brokerdispatch.go create mode 100644 pkg/ent/brokerdispatch/brokerdispatch.go create mode 100644 pkg/ent/brokerdispatch/where.go create mode 100644 pkg/ent/brokerdispatch_create.go create mode 100644 pkg/ent/brokerdispatch_delete.go create mode 100644 pkg/ent/brokerdispatch_query.go create mode 100644 pkg/ent/brokerdispatch_update.go create mode 100644 pkg/ent/schema/brokerdispatch.go create mode 100644 pkg/hub/broker_routing.go create mode 100644 pkg/hub/broker_routing_test.go create mode 100644 pkg/hub/command_bus.go create mode 100644 pkg/hub/command_bus_test.go create mode 100644 pkg/hub/dispatch_args.go create mode 100644 pkg/hub/dispatch_exec_test.go create mode 100644 pkg/hub/dispatch_lifecycle_test.go create mode 100644 pkg/hub/dispatch_wait.go create mode 100644 pkg/hub/dispatch_wait_test.go create mode 100644 pkg/hub/reaper.go create mode 100644 pkg/hub/reconcile.go create mode 100644 pkg/hub/reconcile_test.go create mode 100644 pkg/hub/server_instanceid_test.go create mode 100644 pkg/hub/signing_key_shared_test.go create mode 100644 pkg/hub/sweep.go create mode 100644 pkg/observability/dispatchmetrics/dispatchmetrics.go create mode 100644 pkg/observability/dispatchmetrics/dispatchmetrics_test.go create mode 100644 pkg/store/entadapter/broker_affinity_test.go create mode 100644 pkg/store/entadapter/broker_dispatch_store_test.go create mode 100644 pkg/store/entadapter/brokerdispatch_store.go create mode 100644 pkg/store/entadapter/reaper_test.go create mode 100644 scratch/apitest/main.go create mode 100644 scratch/dbdiag/main.go create mode 100644 scratch/dbdiag2/main.go create mode 100644 scratch/minttoken/main.go create mode 100644 scratch/nm2-test-pod-a.yaml create mode 100644 scratch/nm2-test-pod-b1.yaml create mode 100644 scratch/nm2-test-pod-b2.yaml create mode 100644 scratch/nm2-test-pod-e.yaml create mode 100644 scratch/scion-nfs-pv.yaml create mode 100644 shared-dirs/scratchpad/coordinator.md create mode 100644 shared-dirs/scratchpad/instance-interaction.md diff --git a/.design/project-log/2026-06-03-b5-3-chaos-gate.md b/.design/project-log/2026-06-03-b5-3-chaos-gate.md new file mode 100644 index 000000000..f92a9a769 --- /dev/null +++ b/.design/project-log/2026-06-03-b5-3-chaos-gate.md @@ -0,0 +1,31 @@ +# B5-3 Chaos Gate — GB5 PASSED + +**Date:** 2026-06-03 +**Agent:** qa-agent +**Branch:** `postgres/wave-b-integration` @ `62186381` +**Gate:** GB5 (GA gate for broker dispatch) + +## Result: PASS + +All five chaos scenarios completed against two-VM + CloudSQL topology. +Full results at `/scion-volumes/scratchpad/B5-3-CHAOS-GATE-RESULTS.md`. + +| Scenario | Result | +|----------|--------| +| A: Kill owning hub mid-start | **PASS** — Hub B claimed and completed dispatch in 1.3s; no double-execution; `state=done, attempts=0` | +| B: Broker flap A→B | **PARTIAL/PASS** — Co-located topology prevents literal A→B flap; CAS claim, reconcile drain, and reaper all verified via equivalent tests | +| C: Pool saturation during PublishTx | **PASS** — Message dispatched 60ms post-creation despite external pool pressure; no corruption, no orphaned pending rows | +| D: Command-bus listener drop | **PASS** — Reconnected in ~280ms; cross-node dispatch succeeded immediately after | +| E: Reaper correctness | **PASS** — Stuck `in_progress` dispatch re-driven within 1 min of threshold; stale `connected_hub_id` cleared within 1 min of stale window | + +## Key Timing Evidence (Scenario E) + +- Hub killed: 22:48:02; last heartbeat: 22:49:38 +- Dispatch requeued (`in_progress→pending`): 22:50:26 — within 1 min of `dispatchStuckAge` +- Affinity cleared: 22:53:26 — within 1 min of `affinityStaleAge` (180s from last heartbeat) + +## Notes + +- Scenario B limitation: in this deployment brokers are co-located with their hubs (same process). A cross-hub broker reconnect can't be manually induced. The mechanisms that handle it (CAS claim on reconnect, reconcile drain, reaper) were each independently verified. +- `ConnectionMaxIdleTime` fix (from LIVE-RETEST-RESULTS.md §7) not yet implemented. No stall observed during chaos recovery — command-bus reconnect was clean. Recommend as a follow-up hardening item, not a blocker. +- VMs left running `62186381` on Postgres (healthy) after gate. diff --git a/.design/project-log/2026-06-03-cross-replica-signing-key-login-loop.md b/.design/project-log/2026-06-03-cross-replica-signing-key-login-loop.md new file mode 100644 index 000000000..541d88dfc --- /dev/null +++ b/.design/project-log/2026-06-03-cross-replica-signing-key-login-loop.md @@ -0,0 +1,73 @@ +# Fix: cross-replica login loop (`session_expired`) after cookie-store fix + +**Date:** 2026-06-03 +**Branch:** postgres/wave-b-integration +**Symptom:** After OAuth login the dashboard flashes, then the browser is +redirected to `/login?error=session_expired&returnTo=/`, repeatedly. + +## Background + +Commit `0515e2a8` replaced the per-replica gorilla `FilesystemStore` with an +encrypted+signed `CookieStore` whose keys derive from the shared +`SESSION_SECRET`, so the whole web session (OAuth state + Hub JWTs) rides in the +client cookie and any replica can read it. That fixed the OAuth `state_mismatch` +and made the *session container* replica-portable. + +## Root cause (one layer deeper) + +The cookie is portable, but the **Hub JWT inside it is signed with a per-replica +key**. Signing keys are resolved by `ensureSigningKey()` scoped to +`(scope=hub, scope_id=hubID)`, and `hubID = sha256(hostname)[:12]` +(`DefaultHubID`). The integration deployment runs **two replicas of one logical +hub** behind a single LB (`multi.demo.scion-ai.dev`), sharing one Postgres DB +and one `SESSION_SECRET`, but with different hostnames: + +| Replica | hub_id | user_signing_key fp | +|---|---|---| +| scion-integration | `ca39430276ee` | `9a35ae24cfeedba0` | +| scion-integration2 | `9662ebe99da4` | `97d3f30a36554d7a` | + +So each replica minted/validated user JWTs with a *different* HS256 key. When a +post-login request landed on the replica that did **not** mint the token, +`ValidateUserToken` failed (`go-jose: error in cryptographic primitive`), +refresh failed too (the refresh token is signed with the same foreign key), and +`sessionToBearerMiddleware` declared the session "irrecoverably invalid", +**deleted the cookie** (`MaxAge=-1`) and returned `session_expired`. The cookie +deletion is what turns it into a loop. Logs show the same user alternating +between "User authenticated" and "Hub token irrecoverably invalid, clearing +session" depending on which replica served the request. + +## Fix + +Extend the `0515e2a8` philosophy from the cookie to the keys inside it: derive +the agent and user JWT signing keys deterministically from the shared +`SESSION_SECRET`. + +- `ServerConfig.SharedSigningSecret` (new field). +- `ensureSigningKey()`: when `SharedSigningSecret != ""`, return + `deriveSharedSigningKey(secret, keyName)` (domain-separated by key name), + bypassing per-host secret-backend storage. Empty secret → unchanged per-hub + behavior (no regression for single-node/local dev). +- `cmd/server_foreground.go`: new `resolveSessionSecret()` helper feeds the same + value into both the web cookie store and `hubCfg.SharedSigningSecret`. + +Now every replica with the same `SESSION_SECRET` agrees on the signing keys, +regardless of hostname/hubID — no operator coordination (matching HubID) needed. + +## Tests + +`pkg/hub/signing_key_shared_test.go`: +- derivation is deterministic, 32 bytes, domain-separated, secret-sensitive; +- two servers with **different hubID, same secret** derive identical keys and a + token minted on one validates on the other; a different secret cannot; +- an explicit pre-configured key still wins over derivation. + +## Deploy note + +Rolling out the new binary changes the signing keys (they now derive from +`SESSION_SECRET` instead of the stored per-host keys), so existing web sessions +and CLI tokens are invalidated **once** — users log in again, CLI/agents +re-auth. Both replicas already share `SESSION_SECRET`, so no config change is +required. (Faster stopgap without a rebuild: pin the same +`SCION_SERVER_HUB_HUBID` on both VMs to an existing hub ID so they share the +already-stored keys.) diff --git a/.design/project-log/2026-06-05-pr305-review-feedback.md b/.design/project-log/2026-06-05-pr305-review-feedback.md new file mode 100644 index 000000000..73d1195ba --- /dev/null +++ b/.design/project-log/2026-06-05-pr305-review-feedback.md @@ -0,0 +1,32 @@ +# PR #305 Review Feedback — First Fix Round + +**Date:** 2026-06-05 +**PR:** #305 — feat(hub): multi-node broker dispatch +**Branch:** pr/broker-dispatch +**Commit:** c5f8b3c + +## Summary + +Addressed all 6 review comments from gemini-code-assist on PR #305. + +### HIGH Priority Fixes + +1. **server_migrate.go — nil-checked deferred close**: Changed `defer src.Close()` to a nil-checked closure so the source DB can be manually closed and set to nil before `dropSQLiteFile`, preventing Windows sharing violations. + +2. **server_migrate.go — close before drop**: Added explicit `src.Close()` + `src = nil` before the `dropSQLiteFile` call in the `migrateDropSource` path. + +3. **server_foreground.go — stale closure capture**: Moved `mgr := hubSrv.GetControlChannelManager()` inside the `ownsLocally` closure. Previously it was captured once at closure creation time, so if the manager was nil at that point but initialized later, `ownsLocally` would permanently return false. + +### MEDIUM Priority Fixes + +4. **server_migrate.go — file:// prefix handling**: Added a `file://` case before the `file:` case in `parseSQLiteSourceDSN` so that `file:///tmp/hub.db` correctly resolves to `/tmp/hub.db` instead of `//tmp/hub.db`. + +5. **server_migrate_test.go — triple-slash test**: Added a test case verifying `file:///tmp/hub.db` is parsed correctly. + +6. **server_test.go — subtest name sanitization**: Used `strings.ReplaceAll(t.Name(), "/", "_")` in `newTestStore` to prevent SQLite from interpreting subtest slashes as directory paths. + +## Verification + +- `gofmt` clean on all changed files +- `go vet ./cmd/` passes +- All relevant tests pass including the new `file_url_with_triple_slashes` test case diff --git a/.scion/project-id b/.scion/project-id new file mode 100644 index 000000000..59df1969e --- /dev/null +++ b/.scion/project-id @@ -0,0 +1 @@ +c7c7775e-e3a0-43de-9d26-274688d467d0 diff --git a/GLOSSARY.md b/GLOSSARY.md index fe929d28f..d221851d6 100644 --- a/GLOSSARY.md +++ b/GLOSSARY.md @@ -200,7 +200,7 @@ The three run modes at a glance — distinguish them by whether a server runs an | Mode | Server | Tenancy | State & isolation | Canonical use | |------|--------|---------|-------------------|----------------| | **Local mode** | None | Single user | Local machine; isolation via git worktrees | Agents launched directly via the `scion` CLI, no server | -| **Workstation mode** | Combo server (Hub + Runtime Broker + Web) on loopback | Single-tenant | Local machine; single-tenant state | The hosted experience locally, on your own machine | +| **Workstation mode** | Combo server (Hub + Runtime Broker + Web) on loopback | Single-tenant | That machine | The hosted experience locally, on your own machine | | **Hosted mode** | Multi-user server deployment | Multi-user | Hub-coordinated across brokers | Coordinating state across users, projects, and runtime brokers | **Local mode**: diff --git a/agents.md b/agents.md index 505c232ea..195f215db 100644 --- a/agents.md +++ b/agents.md @@ -86,6 +86,13 @@ All icons in the web frontend use the Shoelace `` component (Bootstrap > **Canonical engineering glossary:** See [`GLOSSARY.md`](./GLOSSARY.md) at the repo root for the canonical, opinionated terminology used throughout the codebase — the preferred term for each concept and the synonyms to avoid. Prefer these terms in new code, comments, and docs. +These terms may be used in shorthand with prompts + +- **hub-broker, combo server** References running the server command with both the hub function and the broker function running in the same invocation. +- **hub-native, hub-project** A special variant of a project/project space, that is created on a hub server for use by agents dispatched from clients. These live in ~/.scion/projects/ on any broker that is a provider to the hub project. This is in contrast to the arbitrary local path on a broker for a linked project. +- **agent-home** The directory that gets mounted as the home folder of the container user in the agent container +- **linked-project** A project and project folder that pre-existed on a broker machine, and is linked as a hub resource project for visibility, metadata, and agent management across other brokers that may have such a linked project. May be based on name or git-URI + ## Project use of the scion cli itself Do not commit changes in the project's own `.scion` folder to git as part of committing progress on code and docs. These are managed and committed manually when template defaults are intentionally updated. diff --git a/cmd/server_foreground.go b/cmd/server_foreground.go index 7519261e9..822d99a98 100644 --- a/cmd/server_foreground.go +++ b/cmd/server_foreground.go @@ -230,6 +230,10 @@ func runServerStart(cmd *cobra.Command, args []string) error { log.Fatalf("Hub server failed to start: %v", hubInitErr) } + // Wire command bus for cross-node dispatch (B2-4). + cmdBus := newCommandBus(ctx, cfg, hubSrv) + hubSrv.SetCommandBus(cmdBus) + if !enableWeb { // Hub runs its own HTTP server (standalone mode). eventPub := newEventPublisher(ctx, cfg) @@ -1083,6 +1087,29 @@ func newEventPublisher(ctx context.Context, cfg *config.GlobalConfig) hub.EventP return hub.NewChannelEventPublisher() } +// newCommandBus selects the command bus backend. With Postgres it returns a +// PostgresCommandBus (LISTEN/NOTIFY on scion_broker_cmd); otherwise it returns +// a no-op bus (single-process SQLite always owns all brokers locally). +func newCommandBus(ctx context.Context, cfg *config.GlobalConfig, hubSrv *hub.Server) hub.CommandBus { + if !strings.EqualFold(cfg.Database.Driver, "postgres") { + return hub.NoopCommandBus{} + } + ownsLocally := func(brokerID string) bool { + mgr := hubSrv.GetControlChannelManager() + if mgr == nil { + return false + } + return mgr.IsConnected(brokerID) + } + bus, err := hub.NewPostgresCommandBus(ctx, cfg.Database.URL, ownsLocally, hubSrv.ReconcileBroker, logging.Subsystem("hub.commandbus")) + if err != nil { + log.Printf("WARNING: failed to start Postgres command bus (%v); falling back to no-op. Cross-replica dispatch signals will not work.", err) + return hub.NoopCommandBus{} + } + log.Printf("Using Postgres command bus on channel scion_broker_cmd") + return bus +} + // initWebServer creates and configures the Web server. The provided context is // threaded to the event publisher so that the Postgres LISTEN/NOTIFY goroutine // is cancelled cleanly on shutdown, preventing connection leaks. diff --git a/cmd/server_migrate.go b/cmd/server_migrate.go index 5811a4bf3..02c89c594 100644 --- a/cmd/server_migrate.go +++ b/cmd/server_migrate.go @@ -100,7 +100,11 @@ func runServerMigrate(cmd *cobra.Command, _ []string) error { if err != nil { return fmt.Errorf("opening source sqlite: %w", err) } - defer src.Close() + defer func() { + if src != nil { + _ = src.Close() + } + }() fmt.Fprintln(out, "Opening destination PostgreSQL") dst, err := entc.OpenPostgres(dstDSN, entc.PoolConfig{MaxOpenConns: 10, MaxIdleConns: 5}) @@ -133,6 +137,8 @@ func runServerMigrate(cmd *cobra.Command, _ []string) error { len(report.Entities), total, report.ChildGroupEdgs) if migrateDropSource { + _ = src.Close() + src = nil fmt.Fprintf(out, "Dropping source SQLite file: %s\n", srcPath) if err := dropSQLiteFile(srcPath); err != nil { return fmt.Errorf("dropping source: %w", err) @@ -164,7 +170,6 @@ func parseSQLiteSourceDSN(raw string) (dsn, path string, err error) { path = strings.TrimPrefix(raw, "sqlite:") case strings.HasPrefix(raw, "file://"): path = strings.TrimPrefix(raw, "file://") - // file:///abs -> "/abs"; the third slash begins the absolute path. if i := strings.IndexByte(path, '?'); i >= 0 { path = path[:i] } diff --git a/cmd/server_migrate_test.go b/cmd/server_migrate_test.go index f94c8eb79..195f099b3 100644 --- a/cmd/server_migrate_test.go +++ b/cmd/server_migrate_test.go @@ -43,26 +43,14 @@ func TestParseSQLiteSourceDSN(t *testing.T) { wantPath: "/tmp/hub.db", }, { - name: "file triple-slash absolute url", - in: "file:///var/lib/scion/hub.db", - wantDSN: "file:/var/lib/scion/hub.db?cache=shared", - wantPath: "/var/lib/scion/hub.db", - }, - { - name: "file double-slash relative url", - in: "file://data/hub.db", - wantDSN: "file:data/hub.db?cache=shared", - wantPath: "data/hub.db", - }, - { - name: "file triple-slash with query", - in: "file:///tmp/hub.db?mode=ro", + name: "file url with query", + in: "file:/tmp/hub.db?cache=shared", wantDSN: "file:/tmp/hub.db?cache=shared", wantPath: "/tmp/hub.db", }, { - name: "file url with query", - in: "file:/tmp/hub.db?cache=shared", + name: "file url with triple slashes", + in: "file:///tmp/hub.db", wantDSN: "file:/tmp/hub.db?cache=shared", wantPath: "/tmp/hub.db", }, diff --git a/cmd/server_test.go b/cmd/server_test.go index cd74fe60d..47bd4184e 100644 --- a/cmd/server_test.go +++ b/cmd/server_test.go @@ -18,6 +18,7 @@ package cmd import ( "context" + "strings" "testing" "github.com/GoogleCloudPlatform/scion/pkg/config" @@ -30,7 +31,8 @@ import ( func newTestStore(t *testing.T) store.Store { t.Helper() - client, err := entc.OpenSQLite("file:"+t.Name()+"?mode=memory&cache=shared", entc.PoolConfig{}) + dbName := strings.ReplaceAll(t.Name(), "/", "_") + client, err := entc.OpenSQLite("file:"+dbName+"?mode=memory&cache=shared", entc.PoolConfig{}) require.NoError(t, err) require.NoError(t, entc.AutoMigrate(context.Background(), client)) s := entadapter.NewCompositeStore(client) diff --git a/pkg/ent/brokerdispatch.go b/pkg/ent/brokerdispatch.go new file mode 100644 index 000000000..5fe957779 --- /dev/null +++ b/pkg/ent/brokerdispatch.go @@ -0,0 +1,263 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "fmt" + "strings" + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "github.com/GoogleCloudPlatform/scion/pkg/ent/brokerdispatch" + "github.com/google/uuid" +) + +// BrokerDispatch is the model entity for the BrokerDispatch schema. +type BrokerDispatch struct { + config `json:"-"` + // ID of the ent. + ID uuid.UUID `json:"id,omitempty"` + // BrokerID holds the value of the "broker_id" field. + BrokerID uuid.UUID `json:"broker_id,omitempty"` + // AgentID holds the value of the "agent_id" field. + AgentID *uuid.UUID `json:"agent_id,omitempty"` + // AgentSlug holds the value of the "agent_slug" field. + AgentSlug string `json:"agent_slug,omitempty"` + // ProjectID holds the value of the "project_id" field. + ProjectID *uuid.UUID `json:"project_id,omitempty"` + // Op holds the value of the "op" field. + Op string `json:"op,omitempty"` + // Args holds the value of the "args" field. + Args string `json:"args,omitempty"` + // State holds the value of the "state" field. + State string `json:"state,omitempty"` + // Result holds the value of the "result" field. + Result string `json:"result,omitempty"` + // ClaimedBy holds the value of the "claimed_by" field. + ClaimedBy string `json:"claimed_by,omitempty"` + // Attempts holds the value of the "attempts" field. + Attempts int `json:"attempts,omitempty"` + // Error holds the value of the "error" field. + Error string `json:"error,omitempty"` + // CreatedAt holds the value of the "created_at" field. + CreatedAt time.Time `json:"created_at,omitempty"` + // UpdatedAt holds the value of the "updated_at" field. + UpdatedAt time.Time `json:"updated_at,omitempty"` + // DeadlineAt holds the value of the "deadline_at" field. + DeadlineAt *time.Time `json:"deadline_at,omitempty"` + selectValues sql.SelectValues +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*BrokerDispatch) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case brokerdispatch.FieldAgentID, brokerdispatch.FieldProjectID: + values[i] = &sql.NullScanner{S: new(uuid.UUID)} + case brokerdispatch.FieldAttempts: + values[i] = new(sql.NullInt64) + case brokerdispatch.FieldAgentSlug, brokerdispatch.FieldOp, brokerdispatch.FieldArgs, brokerdispatch.FieldState, brokerdispatch.FieldResult, brokerdispatch.FieldClaimedBy, brokerdispatch.FieldError: + values[i] = new(sql.NullString) + case brokerdispatch.FieldCreatedAt, brokerdispatch.FieldUpdatedAt, brokerdispatch.FieldDeadlineAt: + values[i] = new(sql.NullTime) + case brokerdispatch.FieldID, brokerdispatch.FieldBrokerID: + values[i] = new(uuid.UUID) + default: + values[i] = new(sql.UnknownType) + } + } + return values, nil +} + +// assignValues assigns the values that were returned from sql.Rows (after scanning) +// to the BrokerDispatch fields. +func (_m *BrokerDispatch) assignValues(columns []string, values []any) error { + if m, n := len(values), len(columns); m < n { + return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) + } + for i := range columns { + switch columns[i] { + case brokerdispatch.FieldID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field id", values[i]) + } else if value != nil { + _m.ID = *value + } + case brokerdispatch.FieldBrokerID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field broker_id", values[i]) + } else if value != nil { + _m.BrokerID = *value + } + case brokerdispatch.FieldAgentID: + if value, ok := values[i].(*sql.NullScanner); !ok { + return fmt.Errorf("unexpected type %T for field agent_id", values[i]) + } else if value.Valid { + _m.AgentID = new(uuid.UUID) + *_m.AgentID = *value.S.(*uuid.UUID) + } + case brokerdispatch.FieldAgentSlug: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field agent_slug", values[i]) + } else if value.Valid { + _m.AgentSlug = value.String + } + case brokerdispatch.FieldProjectID: + if value, ok := values[i].(*sql.NullScanner); !ok { + return fmt.Errorf("unexpected type %T for field project_id", values[i]) + } else if value.Valid { + _m.ProjectID = new(uuid.UUID) + *_m.ProjectID = *value.S.(*uuid.UUID) + } + case brokerdispatch.FieldOp: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field op", values[i]) + } else if value.Valid { + _m.Op = value.String + } + case brokerdispatch.FieldArgs: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field args", values[i]) + } else if value.Valid { + _m.Args = value.String + } + case brokerdispatch.FieldState: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field state", values[i]) + } else if value.Valid { + _m.State = value.String + } + case brokerdispatch.FieldResult: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field result", values[i]) + } else if value.Valid { + _m.Result = value.String + } + case brokerdispatch.FieldClaimedBy: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field claimed_by", values[i]) + } else if value.Valid { + _m.ClaimedBy = value.String + } + case brokerdispatch.FieldAttempts: + if value, ok := values[i].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for field attempts", values[i]) + } else if value.Valid { + _m.Attempts = int(value.Int64) + } + case brokerdispatch.FieldError: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field error", values[i]) + } else if value.Valid { + _m.Error = value.String + } + case brokerdispatch.FieldCreatedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field created_at", values[i]) + } else if value.Valid { + _m.CreatedAt = value.Time + } + case brokerdispatch.FieldUpdatedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field updated_at", values[i]) + } else if value.Valid { + _m.UpdatedAt = value.Time + } + case brokerdispatch.FieldDeadlineAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field deadline_at", values[i]) + } else if value.Valid { + _m.DeadlineAt = new(time.Time) + *_m.DeadlineAt = value.Time + } + default: + _m.selectValues.Set(columns[i], values[i]) + } + } + return nil +} + +// Value returns the ent.Value that was dynamically selected and assigned to the BrokerDispatch. +// This includes values selected through modifiers, order, etc. +func (_m *BrokerDispatch) Value(name string) (ent.Value, error) { + return _m.selectValues.Get(name) +} + +// Update returns a builder for updating this BrokerDispatch. +// Note that you need to call BrokerDispatch.Unwrap() before calling this method if this BrokerDispatch +// was returned from a transaction, and the transaction was committed or rolled back. +func (_m *BrokerDispatch) Update() *BrokerDispatchUpdateOne { + return NewBrokerDispatchClient(_m.config).UpdateOne(_m) +} + +// Unwrap unwraps the BrokerDispatch entity that was returned from a transaction after it was closed, +// so that all future queries will be executed through the driver which created the transaction. +func (_m *BrokerDispatch) Unwrap() *BrokerDispatch { + _tx, ok := _m.config.driver.(*txDriver) + if !ok { + panic("ent: BrokerDispatch is not a transactional entity") + } + _m.config.driver = _tx.drv + return _m +} + +// String implements the fmt.Stringer. +func (_m *BrokerDispatch) String() string { + var builder strings.Builder + builder.WriteString("BrokerDispatch(") + builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID)) + builder.WriteString("broker_id=") + builder.WriteString(fmt.Sprintf("%v", _m.BrokerID)) + builder.WriteString(", ") + if v := _m.AgentID; v != nil { + builder.WriteString("agent_id=") + builder.WriteString(fmt.Sprintf("%v", *v)) + } + builder.WriteString(", ") + builder.WriteString("agent_slug=") + builder.WriteString(_m.AgentSlug) + builder.WriteString(", ") + if v := _m.ProjectID; v != nil { + builder.WriteString("project_id=") + builder.WriteString(fmt.Sprintf("%v", *v)) + } + builder.WriteString(", ") + builder.WriteString("op=") + builder.WriteString(_m.Op) + builder.WriteString(", ") + builder.WriteString("args=") + builder.WriteString(_m.Args) + builder.WriteString(", ") + builder.WriteString("state=") + builder.WriteString(_m.State) + builder.WriteString(", ") + builder.WriteString("result=") + builder.WriteString(_m.Result) + builder.WriteString(", ") + builder.WriteString("claimed_by=") + builder.WriteString(_m.ClaimedBy) + builder.WriteString(", ") + builder.WriteString("attempts=") + builder.WriteString(fmt.Sprintf("%v", _m.Attempts)) + builder.WriteString(", ") + builder.WriteString("error=") + builder.WriteString(_m.Error) + builder.WriteString(", ") + builder.WriteString("created_at=") + builder.WriteString(_m.CreatedAt.Format(time.ANSIC)) + builder.WriteString(", ") + builder.WriteString("updated_at=") + builder.WriteString(_m.UpdatedAt.Format(time.ANSIC)) + builder.WriteString(", ") + if v := _m.DeadlineAt; v != nil { + builder.WriteString("deadline_at=") + builder.WriteString(v.Format(time.ANSIC)) + } + builder.WriteByte(')') + return builder.String() +} + +// BrokerDispatches is a parsable slice of BrokerDispatch. +type BrokerDispatches []*BrokerDispatch diff --git a/pkg/ent/brokerdispatch/brokerdispatch.go b/pkg/ent/brokerdispatch/brokerdispatch.go new file mode 100644 index 000000000..a7968ca0f --- /dev/null +++ b/pkg/ent/brokerdispatch/brokerdispatch.go @@ -0,0 +1,171 @@ +// Code generated by ent, DO NOT EDIT. + +package brokerdispatch + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "github.com/google/uuid" +) + +const ( + // Label holds the string label denoting the brokerdispatch type in the database. + Label = "broker_dispatch" + // FieldID holds the string denoting the id field in the database. + FieldID = "id" + // FieldBrokerID holds the string denoting the broker_id field in the database. + FieldBrokerID = "broker_id" + // FieldAgentID holds the string denoting the agent_id field in the database. + FieldAgentID = "agent_id" + // FieldAgentSlug holds the string denoting the agent_slug field in the database. + FieldAgentSlug = "agent_slug" + // FieldProjectID holds the string denoting the project_id field in the database. + FieldProjectID = "project_id" + // FieldOp holds the string denoting the op field in the database. + FieldOp = "op" + // FieldArgs holds the string denoting the args field in the database. + FieldArgs = "args" + // FieldState holds the string denoting the state field in the database. + FieldState = "state" + // FieldResult holds the string denoting the result field in the database. + FieldResult = "result" + // FieldClaimedBy holds the string denoting the claimed_by field in the database. + FieldClaimedBy = "claimed_by" + // FieldAttempts holds the string denoting the attempts field in the database. + FieldAttempts = "attempts" + // FieldError holds the string denoting the error field in the database. + FieldError = "error" + // FieldCreatedAt holds the string denoting the created_at field in the database. + FieldCreatedAt = "created_at" + // FieldUpdatedAt holds the string denoting the updated_at field in the database. + FieldUpdatedAt = "updated_at" + // FieldDeadlineAt holds the string denoting the deadline_at field in the database. + FieldDeadlineAt = "deadline_at" + // Table holds the table name of the brokerdispatch in the database. + Table = "broker_dispatch" +) + +// Columns holds all SQL columns for brokerdispatch fields. +var Columns = []string{ + FieldID, + FieldBrokerID, + FieldAgentID, + FieldAgentSlug, + FieldProjectID, + FieldOp, + FieldArgs, + FieldState, + FieldResult, + FieldClaimedBy, + FieldAttempts, + FieldError, + FieldCreatedAt, + FieldUpdatedAt, + FieldDeadlineAt, +} + +// ValidColumn reports if the column name is valid (part of the table columns). +func ValidColumn(column string) bool { + for i := range Columns { + if column == Columns[i] { + return true + } + } + return false +} + +var ( + // OpValidator is a validator for the "op" field. It is called by the builders before save. + OpValidator func(string) error + // DefaultState holds the default value on creation for the "state" field. + DefaultState string + // DefaultAttempts holds the default value on creation for the "attempts" field. + DefaultAttempts int + // DefaultCreatedAt holds the default value on creation for the "created_at" field. + DefaultCreatedAt func() time.Time + // DefaultUpdatedAt holds the default value on creation for the "updated_at" field. + DefaultUpdatedAt func() time.Time + // UpdateDefaultUpdatedAt holds the default value on update for the "updated_at" field. + UpdateDefaultUpdatedAt func() time.Time + // DefaultID holds the default value on creation for the "id" field. + DefaultID func() uuid.UUID +) + +// OrderOption defines the ordering options for the BrokerDispatch queries. +type OrderOption func(*sql.Selector) + +// ByID orders the results by the id field. +func ByID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldID, opts...).ToFunc() +} + +// ByBrokerID orders the results by the broker_id field. +func ByBrokerID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldBrokerID, opts...).ToFunc() +} + +// ByAgentID orders the results by the agent_id field. +func ByAgentID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldAgentID, opts...).ToFunc() +} + +// ByAgentSlug orders the results by the agent_slug field. +func ByAgentSlug(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldAgentSlug, opts...).ToFunc() +} + +// ByProjectID orders the results by the project_id field. +func ByProjectID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldProjectID, opts...).ToFunc() +} + +// ByOp orders the results by the op field. +func ByOp(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldOp, opts...).ToFunc() +} + +// ByArgs orders the results by the args field. +func ByArgs(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldArgs, opts...).ToFunc() +} + +// ByState orders the results by the state field. +func ByState(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldState, opts...).ToFunc() +} + +// ByResult orders the results by the result field. +func ByResult(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldResult, opts...).ToFunc() +} + +// ByClaimedBy orders the results by the claimed_by field. +func ByClaimedBy(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldClaimedBy, opts...).ToFunc() +} + +// ByAttempts orders the results by the attempts field. +func ByAttempts(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldAttempts, opts...).ToFunc() +} + +// ByError orders the results by the error field. +func ByError(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldError, opts...).ToFunc() +} + +// ByCreatedAt orders the results by the created_at field. +func ByCreatedAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCreatedAt, opts...).ToFunc() +} + +// ByUpdatedAt orders the results by the updated_at field. +func ByUpdatedAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldUpdatedAt, opts...).ToFunc() +} + +// ByDeadlineAt orders the results by the deadline_at field. +func ByDeadlineAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldDeadlineAt, opts...).ToFunc() +} diff --git a/pkg/ent/brokerdispatch/where.go b/pkg/ent/brokerdispatch/where.go new file mode 100644 index 000000000..459128180 --- /dev/null +++ b/pkg/ent/brokerdispatch/where.go @@ -0,0 +1,956 @@ +// Code generated by ent, DO NOT EDIT. + +package brokerdispatch + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/google/uuid" +) + +// ID filters vertices based on their ID field. +func ID(id uuid.UUID) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldEQ(FieldID, id)) +} + +// IDEQ applies the EQ predicate on the ID field. +func IDEQ(id uuid.UUID) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldEQ(FieldID, id)) +} + +// IDNEQ applies the NEQ predicate on the ID field. +func IDNEQ(id uuid.UUID) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldNEQ(FieldID, id)) +} + +// IDIn applies the In predicate on the ID field. +func IDIn(ids ...uuid.UUID) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldIn(FieldID, ids...)) +} + +// IDNotIn applies the NotIn predicate on the ID field. +func IDNotIn(ids ...uuid.UUID) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldNotIn(FieldID, ids...)) +} + +// IDGT applies the GT predicate on the ID field. +func IDGT(id uuid.UUID) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldGT(FieldID, id)) +} + +// IDGTE applies the GTE predicate on the ID field. +func IDGTE(id uuid.UUID) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldGTE(FieldID, id)) +} + +// IDLT applies the LT predicate on the ID field. +func IDLT(id uuid.UUID) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldLT(FieldID, id)) +} + +// IDLTE applies the LTE predicate on the ID field. +func IDLTE(id uuid.UUID) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldLTE(FieldID, id)) +} + +// BrokerID applies equality check predicate on the "broker_id" field. It's identical to BrokerIDEQ. +func BrokerID(v uuid.UUID) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldEQ(FieldBrokerID, v)) +} + +// AgentID applies equality check predicate on the "agent_id" field. It's identical to AgentIDEQ. +func AgentID(v uuid.UUID) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldEQ(FieldAgentID, v)) +} + +// AgentSlug applies equality check predicate on the "agent_slug" field. It's identical to AgentSlugEQ. +func AgentSlug(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldEQ(FieldAgentSlug, v)) +} + +// ProjectID applies equality check predicate on the "project_id" field. It's identical to ProjectIDEQ. +func ProjectID(v uuid.UUID) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldEQ(FieldProjectID, v)) +} + +// Op applies equality check predicate on the "op" field. It's identical to OpEQ. +func Op(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldEQ(FieldOp, v)) +} + +// Args applies equality check predicate on the "args" field. It's identical to ArgsEQ. +func Args(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldEQ(FieldArgs, v)) +} + +// State applies equality check predicate on the "state" field. It's identical to StateEQ. +func State(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldEQ(FieldState, v)) +} + +// Result applies equality check predicate on the "result" field. It's identical to ResultEQ. +func Result(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldEQ(FieldResult, v)) +} + +// ClaimedBy applies equality check predicate on the "claimed_by" field. It's identical to ClaimedByEQ. +func ClaimedBy(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldEQ(FieldClaimedBy, v)) +} + +// Attempts applies equality check predicate on the "attempts" field. It's identical to AttemptsEQ. +func Attempts(v int) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldEQ(FieldAttempts, v)) +} + +// Error applies equality check predicate on the "error" field. It's identical to ErrorEQ. +func Error(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldEQ(FieldError, v)) +} + +// CreatedAt applies equality check predicate on the "created_at" field. It's identical to CreatedAtEQ. +func CreatedAt(v time.Time) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldEQ(FieldCreatedAt, v)) +} + +// UpdatedAt applies equality check predicate on the "updated_at" field. It's identical to UpdatedAtEQ. +func UpdatedAt(v time.Time) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldEQ(FieldUpdatedAt, v)) +} + +// DeadlineAt applies equality check predicate on the "deadline_at" field. It's identical to DeadlineAtEQ. +func DeadlineAt(v time.Time) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldEQ(FieldDeadlineAt, v)) +} + +// BrokerIDEQ applies the EQ predicate on the "broker_id" field. +func BrokerIDEQ(v uuid.UUID) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldEQ(FieldBrokerID, v)) +} + +// BrokerIDNEQ applies the NEQ predicate on the "broker_id" field. +func BrokerIDNEQ(v uuid.UUID) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldNEQ(FieldBrokerID, v)) +} + +// BrokerIDIn applies the In predicate on the "broker_id" field. +func BrokerIDIn(vs ...uuid.UUID) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldIn(FieldBrokerID, vs...)) +} + +// BrokerIDNotIn applies the NotIn predicate on the "broker_id" field. +func BrokerIDNotIn(vs ...uuid.UUID) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldNotIn(FieldBrokerID, vs...)) +} + +// BrokerIDGT applies the GT predicate on the "broker_id" field. +func BrokerIDGT(v uuid.UUID) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldGT(FieldBrokerID, v)) +} + +// BrokerIDGTE applies the GTE predicate on the "broker_id" field. +func BrokerIDGTE(v uuid.UUID) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldGTE(FieldBrokerID, v)) +} + +// BrokerIDLT applies the LT predicate on the "broker_id" field. +func BrokerIDLT(v uuid.UUID) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldLT(FieldBrokerID, v)) +} + +// BrokerIDLTE applies the LTE predicate on the "broker_id" field. +func BrokerIDLTE(v uuid.UUID) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldLTE(FieldBrokerID, v)) +} + +// AgentIDEQ applies the EQ predicate on the "agent_id" field. +func AgentIDEQ(v uuid.UUID) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldEQ(FieldAgentID, v)) +} + +// AgentIDNEQ applies the NEQ predicate on the "agent_id" field. +func AgentIDNEQ(v uuid.UUID) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldNEQ(FieldAgentID, v)) +} + +// AgentIDIn applies the In predicate on the "agent_id" field. +func AgentIDIn(vs ...uuid.UUID) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldIn(FieldAgentID, vs...)) +} + +// AgentIDNotIn applies the NotIn predicate on the "agent_id" field. +func AgentIDNotIn(vs ...uuid.UUID) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldNotIn(FieldAgentID, vs...)) +} + +// AgentIDGT applies the GT predicate on the "agent_id" field. +func AgentIDGT(v uuid.UUID) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldGT(FieldAgentID, v)) +} + +// AgentIDGTE applies the GTE predicate on the "agent_id" field. +func AgentIDGTE(v uuid.UUID) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldGTE(FieldAgentID, v)) +} + +// AgentIDLT applies the LT predicate on the "agent_id" field. +func AgentIDLT(v uuid.UUID) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldLT(FieldAgentID, v)) +} + +// AgentIDLTE applies the LTE predicate on the "agent_id" field. +func AgentIDLTE(v uuid.UUID) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldLTE(FieldAgentID, v)) +} + +// AgentIDIsNil applies the IsNil predicate on the "agent_id" field. +func AgentIDIsNil() predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldIsNull(FieldAgentID)) +} + +// AgentIDNotNil applies the NotNil predicate on the "agent_id" field. +func AgentIDNotNil() predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldNotNull(FieldAgentID)) +} + +// AgentSlugEQ applies the EQ predicate on the "agent_slug" field. +func AgentSlugEQ(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldEQ(FieldAgentSlug, v)) +} + +// AgentSlugNEQ applies the NEQ predicate on the "agent_slug" field. +func AgentSlugNEQ(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldNEQ(FieldAgentSlug, v)) +} + +// AgentSlugIn applies the In predicate on the "agent_slug" field. +func AgentSlugIn(vs ...string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldIn(FieldAgentSlug, vs...)) +} + +// AgentSlugNotIn applies the NotIn predicate on the "agent_slug" field. +func AgentSlugNotIn(vs ...string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldNotIn(FieldAgentSlug, vs...)) +} + +// AgentSlugGT applies the GT predicate on the "agent_slug" field. +func AgentSlugGT(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldGT(FieldAgentSlug, v)) +} + +// AgentSlugGTE applies the GTE predicate on the "agent_slug" field. +func AgentSlugGTE(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldGTE(FieldAgentSlug, v)) +} + +// AgentSlugLT applies the LT predicate on the "agent_slug" field. +func AgentSlugLT(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldLT(FieldAgentSlug, v)) +} + +// AgentSlugLTE applies the LTE predicate on the "agent_slug" field. +func AgentSlugLTE(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldLTE(FieldAgentSlug, v)) +} + +// AgentSlugContains applies the Contains predicate on the "agent_slug" field. +func AgentSlugContains(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldContains(FieldAgentSlug, v)) +} + +// AgentSlugHasPrefix applies the HasPrefix predicate on the "agent_slug" field. +func AgentSlugHasPrefix(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldHasPrefix(FieldAgentSlug, v)) +} + +// AgentSlugHasSuffix applies the HasSuffix predicate on the "agent_slug" field. +func AgentSlugHasSuffix(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldHasSuffix(FieldAgentSlug, v)) +} + +// AgentSlugIsNil applies the IsNil predicate on the "agent_slug" field. +func AgentSlugIsNil() predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldIsNull(FieldAgentSlug)) +} + +// AgentSlugNotNil applies the NotNil predicate on the "agent_slug" field. +func AgentSlugNotNil() predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldNotNull(FieldAgentSlug)) +} + +// AgentSlugEqualFold applies the EqualFold predicate on the "agent_slug" field. +func AgentSlugEqualFold(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldEqualFold(FieldAgentSlug, v)) +} + +// AgentSlugContainsFold applies the ContainsFold predicate on the "agent_slug" field. +func AgentSlugContainsFold(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldContainsFold(FieldAgentSlug, v)) +} + +// ProjectIDEQ applies the EQ predicate on the "project_id" field. +func ProjectIDEQ(v uuid.UUID) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldEQ(FieldProjectID, v)) +} + +// ProjectIDNEQ applies the NEQ predicate on the "project_id" field. +func ProjectIDNEQ(v uuid.UUID) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldNEQ(FieldProjectID, v)) +} + +// ProjectIDIn applies the In predicate on the "project_id" field. +func ProjectIDIn(vs ...uuid.UUID) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldIn(FieldProjectID, vs...)) +} + +// ProjectIDNotIn applies the NotIn predicate on the "project_id" field. +func ProjectIDNotIn(vs ...uuid.UUID) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldNotIn(FieldProjectID, vs...)) +} + +// ProjectIDGT applies the GT predicate on the "project_id" field. +func ProjectIDGT(v uuid.UUID) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldGT(FieldProjectID, v)) +} + +// ProjectIDGTE applies the GTE predicate on the "project_id" field. +func ProjectIDGTE(v uuid.UUID) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldGTE(FieldProjectID, v)) +} + +// ProjectIDLT applies the LT predicate on the "project_id" field. +func ProjectIDLT(v uuid.UUID) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldLT(FieldProjectID, v)) +} + +// ProjectIDLTE applies the LTE predicate on the "project_id" field. +func ProjectIDLTE(v uuid.UUID) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldLTE(FieldProjectID, v)) +} + +// ProjectIDIsNil applies the IsNil predicate on the "project_id" field. +func ProjectIDIsNil() predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldIsNull(FieldProjectID)) +} + +// ProjectIDNotNil applies the NotNil predicate on the "project_id" field. +func ProjectIDNotNil() predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldNotNull(FieldProjectID)) +} + +// OpEQ applies the EQ predicate on the "op" field. +func OpEQ(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldEQ(FieldOp, v)) +} + +// OpNEQ applies the NEQ predicate on the "op" field. +func OpNEQ(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldNEQ(FieldOp, v)) +} + +// OpIn applies the In predicate on the "op" field. +func OpIn(vs ...string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldIn(FieldOp, vs...)) +} + +// OpNotIn applies the NotIn predicate on the "op" field. +func OpNotIn(vs ...string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldNotIn(FieldOp, vs...)) +} + +// OpGT applies the GT predicate on the "op" field. +func OpGT(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldGT(FieldOp, v)) +} + +// OpGTE applies the GTE predicate on the "op" field. +func OpGTE(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldGTE(FieldOp, v)) +} + +// OpLT applies the LT predicate on the "op" field. +func OpLT(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldLT(FieldOp, v)) +} + +// OpLTE applies the LTE predicate on the "op" field. +func OpLTE(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldLTE(FieldOp, v)) +} + +// OpContains applies the Contains predicate on the "op" field. +func OpContains(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldContains(FieldOp, v)) +} + +// OpHasPrefix applies the HasPrefix predicate on the "op" field. +func OpHasPrefix(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldHasPrefix(FieldOp, v)) +} + +// OpHasSuffix applies the HasSuffix predicate on the "op" field. +func OpHasSuffix(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldHasSuffix(FieldOp, v)) +} + +// OpEqualFold applies the EqualFold predicate on the "op" field. +func OpEqualFold(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldEqualFold(FieldOp, v)) +} + +// OpContainsFold applies the ContainsFold predicate on the "op" field. +func OpContainsFold(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldContainsFold(FieldOp, v)) +} + +// ArgsEQ applies the EQ predicate on the "args" field. +func ArgsEQ(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldEQ(FieldArgs, v)) +} + +// ArgsNEQ applies the NEQ predicate on the "args" field. +func ArgsNEQ(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldNEQ(FieldArgs, v)) +} + +// ArgsIn applies the In predicate on the "args" field. +func ArgsIn(vs ...string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldIn(FieldArgs, vs...)) +} + +// ArgsNotIn applies the NotIn predicate on the "args" field. +func ArgsNotIn(vs ...string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldNotIn(FieldArgs, vs...)) +} + +// ArgsGT applies the GT predicate on the "args" field. +func ArgsGT(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldGT(FieldArgs, v)) +} + +// ArgsGTE applies the GTE predicate on the "args" field. +func ArgsGTE(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldGTE(FieldArgs, v)) +} + +// ArgsLT applies the LT predicate on the "args" field. +func ArgsLT(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldLT(FieldArgs, v)) +} + +// ArgsLTE applies the LTE predicate on the "args" field. +func ArgsLTE(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldLTE(FieldArgs, v)) +} + +// ArgsContains applies the Contains predicate on the "args" field. +func ArgsContains(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldContains(FieldArgs, v)) +} + +// ArgsHasPrefix applies the HasPrefix predicate on the "args" field. +func ArgsHasPrefix(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldHasPrefix(FieldArgs, v)) +} + +// ArgsHasSuffix applies the HasSuffix predicate on the "args" field. +func ArgsHasSuffix(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldHasSuffix(FieldArgs, v)) +} + +// ArgsIsNil applies the IsNil predicate on the "args" field. +func ArgsIsNil() predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldIsNull(FieldArgs)) +} + +// ArgsNotNil applies the NotNil predicate on the "args" field. +func ArgsNotNil() predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldNotNull(FieldArgs)) +} + +// ArgsEqualFold applies the EqualFold predicate on the "args" field. +func ArgsEqualFold(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldEqualFold(FieldArgs, v)) +} + +// ArgsContainsFold applies the ContainsFold predicate on the "args" field. +func ArgsContainsFold(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldContainsFold(FieldArgs, v)) +} + +// StateEQ applies the EQ predicate on the "state" field. +func StateEQ(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldEQ(FieldState, v)) +} + +// StateNEQ applies the NEQ predicate on the "state" field. +func StateNEQ(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldNEQ(FieldState, v)) +} + +// StateIn applies the In predicate on the "state" field. +func StateIn(vs ...string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldIn(FieldState, vs...)) +} + +// StateNotIn applies the NotIn predicate on the "state" field. +func StateNotIn(vs ...string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldNotIn(FieldState, vs...)) +} + +// StateGT applies the GT predicate on the "state" field. +func StateGT(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldGT(FieldState, v)) +} + +// StateGTE applies the GTE predicate on the "state" field. +func StateGTE(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldGTE(FieldState, v)) +} + +// StateLT applies the LT predicate on the "state" field. +func StateLT(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldLT(FieldState, v)) +} + +// StateLTE applies the LTE predicate on the "state" field. +func StateLTE(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldLTE(FieldState, v)) +} + +// StateContains applies the Contains predicate on the "state" field. +func StateContains(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldContains(FieldState, v)) +} + +// StateHasPrefix applies the HasPrefix predicate on the "state" field. +func StateHasPrefix(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldHasPrefix(FieldState, v)) +} + +// StateHasSuffix applies the HasSuffix predicate on the "state" field. +func StateHasSuffix(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldHasSuffix(FieldState, v)) +} + +// StateEqualFold applies the EqualFold predicate on the "state" field. +func StateEqualFold(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldEqualFold(FieldState, v)) +} + +// StateContainsFold applies the ContainsFold predicate on the "state" field. +func StateContainsFold(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldContainsFold(FieldState, v)) +} + +// ResultEQ applies the EQ predicate on the "result" field. +func ResultEQ(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldEQ(FieldResult, v)) +} + +// ResultNEQ applies the NEQ predicate on the "result" field. +func ResultNEQ(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldNEQ(FieldResult, v)) +} + +// ResultIn applies the In predicate on the "result" field. +func ResultIn(vs ...string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldIn(FieldResult, vs...)) +} + +// ResultNotIn applies the NotIn predicate on the "result" field. +func ResultNotIn(vs ...string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldNotIn(FieldResult, vs...)) +} + +// ResultGT applies the GT predicate on the "result" field. +func ResultGT(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldGT(FieldResult, v)) +} + +// ResultGTE applies the GTE predicate on the "result" field. +func ResultGTE(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldGTE(FieldResult, v)) +} + +// ResultLT applies the LT predicate on the "result" field. +func ResultLT(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldLT(FieldResult, v)) +} + +// ResultLTE applies the LTE predicate on the "result" field. +func ResultLTE(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldLTE(FieldResult, v)) +} + +// ResultContains applies the Contains predicate on the "result" field. +func ResultContains(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldContains(FieldResult, v)) +} + +// ResultHasPrefix applies the HasPrefix predicate on the "result" field. +func ResultHasPrefix(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldHasPrefix(FieldResult, v)) +} + +// ResultHasSuffix applies the HasSuffix predicate on the "result" field. +func ResultHasSuffix(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldHasSuffix(FieldResult, v)) +} + +// ResultIsNil applies the IsNil predicate on the "result" field. +func ResultIsNil() predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldIsNull(FieldResult)) +} + +// ResultNotNil applies the NotNil predicate on the "result" field. +func ResultNotNil() predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldNotNull(FieldResult)) +} + +// ResultEqualFold applies the EqualFold predicate on the "result" field. +func ResultEqualFold(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldEqualFold(FieldResult, v)) +} + +// ResultContainsFold applies the ContainsFold predicate on the "result" field. +func ResultContainsFold(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldContainsFold(FieldResult, v)) +} + +// ClaimedByEQ applies the EQ predicate on the "claimed_by" field. +func ClaimedByEQ(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldEQ(FieldClaimedBy, v)) +} + +// ClaimedByNEQ applies the NEQ predicate on the "claimed_by" field. +func ClaimedByNEQ(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldNEQ(FieldClaimedBy, v)) +} + +// ClaimedByIn applies the In predicate on the "claimed_by" field. +func ClaimedByIn(vs ...string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldIn(FieldClaimedBy, vs...)) +} + +// ClaimedByNotIn applies the NotIn predicate on the "claimed_by" field. +func ClaimedByNotIn(vs ...string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldNotIn(FieldClaimedBy, vs...)) +} + +// ClaimedByGT applies the GT predicate on the "claimed_by" field. +func ClaimedByGT(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldGT(FieldClaimedBy, v)) +} + +// ClaimedByGTE applies the GTE predicate on the "claimed_by" field. +func ClaimedByGTE(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldGTE(FieldClaimedBy, v)) +} + +// ClaimedByLT applies the LT predicate on the "claimed_by" field. +func ClaimedByLT(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldLT(FieldClaimedBy, v)) +} + +// ClaimedByLTE applies the LTE predicate on the "claimed_by" field. +func ClaimedByLTE(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldLTE(FieldClaimedBy, v)) +} + +// ClaimedByContains applies the Contains predicate on the "claimed_by" field. +func ClaimedByContains(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldContains(FieldClaimedBy, v)) +} + +// ClaimedByHasPrefix applies the HasPrefix predicate on the "claimed_by" field. +func ClaimedByHasPrefix(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldHasPrefix(FieldClaimedBy, v)) +} + +// ClaimedByHasSuffix applies the HasSuffix predicate on the "claimed_by" field. +func ClaimedByHasSuffix(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldHasSuffix(FieldClaimedBy, v)) +} + +// ClaimedByIsNil applies the IsNil predicate on the "claimed_by" field. +func ClaimedByIsNil() predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldIsNull(FieldClaimedBy)) +} + +// ClaimedByNotNil applies the NotNil predicate on the "claimed_by" field. +func ClaimedByNotNil() predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldNotNull(FieldClaimedBy)) +} + +// ClaimedByEqualFold applies the EqualFold predicate on the "claimed_by" field. +func ClaimedByEqualFold(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldEqualFold(FieldClaimedBy, v)) +} + +// ClaimedByContainsFold applies the ContainsFold predicate on the "claimed_by" field. +func ClaimedByContainsFold(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldContainsFold(FieldClaimedBy, v)) +} + +// AttemptsEQ applies the EQ predicate on the "attempts" field. +func AttemptsEQ(v int) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldEQ(FieldAttempts, v)) +} + +// AttemptsNEQ applies the NEQ predicate on the "attempts" field. +func AttemptsNEQ(v int) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldNEQ(FieldAttempts, v)) +} + +// AttemptsIn applies the In predicate on the "attempts" field. +func AttemptsIn(vs ...int) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldIn(FieldAttempts, vs...)) +} + +// AttemptsNotIn applies the NotIn predicate on the "attempts" field. +func AttemptsNotIn(vs ...int) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldNotIn(FieldAttempts, vs...)) +} + +// AttemptsGT applies the GT predicate on the "attempts" field. +func AttemptsGT(v int) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldGT(FieldAttempts, v)) +} + +// AttemptsGTE applies the GTE predicate on the "attempts" field. +func AttemptsGTE(v int) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldGTE(FieldAttempts, v)) +} + +// AttemptsLT applies the LT predicate on the "attempts" field. +func AttemptsLT(v int) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldLT(FieldAttempts, v)) +} + +// AttemptsLTE applies the LTE predicate on the "attempts" field. +func AttemptsLTE(v int) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldLTE(FieldAttempts, v)) +} + +// ErrorEQ applies the EQ predicate on the "error" field. +func ErrorEQ(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldEQ(FieldError, v)) +} + +// ErrorNEQ applies the NEQ predicate on the "error" field. +func ErrorNEQ(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldNEQ(FieldError, v)) +} + +// ErrorIn applies the In predicate on the "error" field. +func ErrorIn(vs ...string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldIn(FieldError, vs...)) +} + +// ErrorNotIn applies the NotIn predicate on the "error" field. +func ErrorNotIn(vs ...string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldNotIn(FieldError, vs...)) +} + +// ErrorGT applies the GT predicate on the "error" field. +func ErrorGT(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldGT(FieldError, v)) +} + +// ErrorGTE applies the GTE predicate on the "error" field. +func ErrorGTE(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldGTE(FieldError, v)) +} + +// ErrorLT applies the LT predicate on the "error" field. +func ErrorLT(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldLT(FieldError, v)) +} + +// ErrorLTE applies the LTE predicate on the "error" field. +func ErrorLTE(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldLTE(FieldError, v)) +} + +// ErrorContains applies the Contains predicate on the "error" field. +func ErrorContains(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldContains(FieldError, v)) +} + +// ErrorHasPrefix applies the HasPrefix predicate on the "error" field. +func ErrorHasPrefix(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldHasPrefix(FieldError, v)) +} + +// ErrorHasSuffix applies the HasSuffix predicate on the "error" field. +func ErrorHasSuffix(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldHasSuffix(FieldError, v)) +} + +// ErrorIsNil applies the IsNil predicate on the "error" field. +func ErrorIsNil() predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldIsNull(FieldError)) +} + +// ErrorNotNil applies the NotNil predicate on the "error" field. +func ErrorNotNil() predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldNotNull(FieldError)) +} + +// ErrorEqualFold applies the EqualFold predicate on the "error" field. +func ErrorEqualFold(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldEqualFold(FieldError, v)) +} + +// ErrorContainsFold applies the ContainsFold predicate on the "error" field. +func ErrorContainsFold(v string) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldContainsFold(FieldError, v)) +} + +// CreatedAtEQ applies the EQ predicate on the "created_at" field. +func CreatedAtEQ(v time.Time) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldEQ(FieldCreatedAt, v)) +} + +// CreatedAtNEQ applies the NEQ predicate on the "created_at" field. +func CreatedAtNEQ(v time.Time) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldNEQ(FieldCreatedAt, v)) +} + +// CreatedAtIn applies the In predicate on the "created_at" field. +func CreatedAtIn(vs ...time.Time) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldIn(FieldCreatedAt, vs...)) +} + +// CreatedAtNotIn applies the NotIn predicate on the "created_at" field. +func CreatedAtNotIn(vs ...time.Time) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldNotIn(FieldCreatedAt, vs...)) +} + +// CreatedAtGT applies the GT predicate on the "created_at" field. +func CreatedAtGT(v time.Time) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldGT(FieldCreatedAt, v)) +} + +// CreatedAtGTE applies the GTE predicate on the "created_at" field. +func CreatedAtGTE(v time.Time) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldGTE(FieldCreatedAt, v)) +} + +// CreatedAtLT applies the LT predicate on the "created_at" field. +func CreatedAtLT(v time.Time) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldLT(FieldCreatedAt, v)) +} + +// CreatedAtLTE applies the LTE predicate on the "created_at" field. +func CreatedAtLTE(v time.Time) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldLTE(FieldCreatedAt, v)) +} + +// UpdatedAtEQ applies the EQ predicate on the "updated_at" field. +func UpdatedAtEQ(v time.Time) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldEQ(FieldUpdatedAt, v)) +} + +// UpdatedAtNEQ applies the NEQ predicate on the "updated_at" field. +func UpdatedAtNEQ(v time.Time) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldNEQ(FieldUpdatedAt, v)) +} + +// UpdatedAtIn applies the In predicate on the "updated_at" field. +func UpdatedAtIn(vs ...time.Time) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldIn(FieldUpdatedAt, vs...)) +} + +// UpdatedAtNotIn applies the NotIn predicate on the "updated_at" field. +func UpdatedAtNotIn(vs ...time.Time) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldNotIn(FieldUpdatedAt, vs...)) +} + +// UpdatedAtGT applies the GT predicate on the "updated_at" field. +func UpdatedAtGT(v time.Time) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldGT(FieldUpdatedAt, v)) +} + +// UpdatedAtGTE applies the GTE predicate on the "updated_at" field. +func UpdatedAtGTE(v time.Time) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldGTE(FieldUpdatedAt, v)) +} + +// UpdatedAtLT applies the LT predicate on the "updated_at" field. +func UpdatedAtLT(v time.Time) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldLT(FieldUpdatedAt, v)) +} + +// UpdatedAtLTE applies the LTE predicate on the "updated_at" field. +func UpdatedAtLTE(v time.Time) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldLTE(FieldUpdatedAt, v)) +} + +// DeadlineAtEQ applies the EQ predicate on the "deadline_at" field. +func DeadlineAtEQ(v time.Time) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldEQ(FieldDeadlineAt, v)) +} + +// DeadlineAtNEQ applies the NEQ predicate on the "deadline_at" field. +func DeadlineAtNEQ(v time.Time) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldNEQ(FieldDeadlineAt, v)) +} + +// DeadlineAtIn applies the In predicate on the "deadline_at" field. +func DeadlineAtIn(vs ...time.Time) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldIn(FieldDeadlineAt, vs...)) +} + +// DeadlineAtNotIn applies the NotIn predicate on the "deadline_at" field. +func DeadlineAtNotIn(vs ...time.Time) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldNotIn(FieldDeadlineAt, vs...)) +} + +// DeadlineAtGT applies the GT predicate on the "deadline_at" field. +func DeadlineAtGT(v time.Time) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldGT(FieldDeadlineAt, v)) +} + +// DeadlineAtGTE applies the GTE predicate on the "deadline_at" field. +func DeadlineAtGTE(v time.Time) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldGTE(FieldDeadlineAt, v)) +} + +// DeadlineAtLT applies the LT predicate on the "deadline_at" field. +func DeadlineAtLT(v time.Time) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldLT(FieldDeadlineAt, v)) +} + +// DeadlineAtLTE applies the LTE predicate on the "deadline_at" field. +func DeadlineAtLTE(v time.Time) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldLTE(FieldDeadlineAt, v)) +} + +// DeadlineAtIsNil applies the IsNil predicate on the "deadline_at" field. +func DeadlineAtIsNil() predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldIsNull(FieldDeadlineAt)) +} + +// DeadlineAtNotNil applies the NotNil predicate on the "deadline_at" field. +func DeadlineAtNotNil() predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.FieldNotNull(FieldDeadlineAt)) +} + +// And groups predicates with the AND operator between them. +func And(predicates ...predicate.BrokerDispatch) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.AndPredicates(predicates...)) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.BrokerDispatch) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.OrPredicates(predicates...)) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.BrokerDispatch) predicate.BrokerDispatch { + return predicate.BrokerDispatch(sql.NotPredicates(p)) +} diff --git a/pkg/ent/brokerdispatch_create.go b/pkg/ent/brokerdispatch_create.go new file mode 100644 index 000000000..599839130 --- /dev/null +++ b/pkg/ent/brokerdispatch_create.go @@ -0,0 +1,1437 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/brokerdispatch" + "github.com/google/uuid" +) + +// BrokerDispatchCreate is the builder for creating a BrokerDispatch entity. +type BrokerDispatchCreate struct { + config + mutation *BrokerDispatchMutation + hooks []Hook + conflict []sql.ConflictOption +} + +// SetBrokerID sets the "broker_id" field. +func (_c *BrokerDispatchCreate) SetBrokerID(v uuid.UUID) *BrokerDispatchCreate { + _c.mutation.SetBrokerID(v) + return _c +} + +// SetAgentID sets the "agent_id" field. +func (_c *BrokerDispatchCreate) SetAgentID(v uuid.UUID) *BrokerDispatchCreate { + _c.mutation.SetAgentID(v) + return _c +} + +// SetNillableAgentID sets the "agent_id" field if the given value is not nil. +func (_c *BrokerDispatchCreate) SetNillableAgentID(v *uuid.UUID) *BrokerDispatchCreate { + if v != nil { + _c.SetAgentID(*v) + } + return _c +} + +// SetAgentSlug sets the "agent_slug" field. +func (_c *BrokerDispatchCreate) SetAgentSlug(v string) *BrokerDispatchCreate { + _c.mutation.SetAgentSlug(v) + return _c +} + +// SetNillableAgentSlug sets the "agent_slug" field if the given value is not nil. +func (_c *BrokerDispatchCreate) SetNillableAgentSlug(v *string) *BrokerDispatchCreate { + if v != nil { + _c.SetAgentSlug(*v) + } + return _c +} + +// SetProjectID sets the "project_id" field. +func (_c *BrokerDispatchCreate) SetProjectID(v uuid.UUID) *BrokerDispatchCreate { + _c.mutation.SetProjectID(v) + return _c +} + +// SetNillableProjectID sets the "project_id" field if the given value is not nil. +func (_c *BrokerDispatchCreate) SetNillableProjectID(v *uuid.UUID) *BrokerDispatchCreate { + if v != nil { + _c.SetProjectID(*v) + } + return _c +} + +// SetOp sets the "op" field. +func (_c *BrokerDispatchCreate) SetOp(v string) *BrokerDispatchCreate { + _c.mutation.SetOpField(v) + return _c +} + +// SetArgs sets the "args" field. +func (_c *BrokerDispatchCreate) SetArgs(v string) *BrokerDispatchCreate { + _c.mutation.SetArgs(v) + return _c +} + +// SetNillableArgs sets the "args" field if the given value is not nil. +func (_c *BrokerDispatchCreate) SetNillableArgs(v *string) *BrokerDispatchCreate { + if v != nil { + _c.SetArgs(*v) + } + return _c +} + +// SetState sets the "state" field. +func (_c *BrokerDispatchCreate) SetState(v string) *BrokerDispatchCreate { + _c.mutation.SetState(v) + return _c +} + +// SetNillableState sets the "state" field if the given value is not nil. +func (_c *BrokerDispatchCreate) SetNillableState(v *string) *BrokerDispatchCreate { + if v != nil { + _c.SetState(*v) + } + return _c +} + +// SetResult sets the "result" field. +func (_c *BrokerDispatchCreate) SetResult(v string) *BrokerDispatchCreate { + _c.mutation.SetResult(v) + return _c +} + +// SetNillableResult sets the "result" field if the given value is not nil. +func (_c *BrokerDispatchCreate) SetNillableResult(v *string) *BrokerDispatchCreate { + if v != nil { + _c.SetResult(*v) + } + return _c +} + +// SetClaimedBy sets the "claimed_by" field. +func (_c *BrokerDispatchCreate) SetClaimedBy(v string) *BrokerDispatchCreate { + _c.mutation.SetClaimedBy(v) + return _c +} + +// SetNillableClaimedBy sets the "claimed_by" field if the given value is not nil. +func (_c *BrokerDispatchCreate) SetNillableClaimedBy(v *string) *BrokerDispatchCreate { + if v != nil { + _c.SetClaimedBy(*v) + } + return _c +} + +// SetAttempts sets the "attempts" field. +func (_c *BrokerDispatchCreate) SetAttempts(v int) *BrokerDispatchCreate { + _c.mutation.SetAttempts(v) + return _c +} + +// SetNillableAttempts sets the "attempts" field if the given value is not nil. +func (_c *BrokerDispatchCreate) SetNillableAttempts(v *int) *BrokerDispatchCreate { + if v != nil { + _c.SetAttempts(*v) + } + return _c +} + +// SetError sets the "error" field. +func (_c *BrokerDispatchCreate) SetError(v string) *BrokerDispatchCreate { + _c.mutation.SetError(v) + return _c +} + +// SetNillableError sets the "error" field if the given value is not nil. +func (_c *BrokerDispatchCreate) SetNillableError(v *string) *BrokerDispatchCreate { + if v != nil { + _c.SetError(*v) + } + return _c +} + +// SetCreatedAt sets the "created_at" field. +func (_c *BrokerDispatchCreate) SetCreatedAt(v time.Time) *BrokerDispatchCreate { + _c.mutation.SetCreatedAt(v) + return _c +} + +// SetNillableCreatedAt sets the "created_at" field if the given value is not nil. +func (_c *BrokerDispatchCreate) SetNillableCreatedAt(v *time.Time) *BrokerDispatchCreate { + if v != nil { + _c.SetCreatedAt(*v) + } + return _c +} + +// SetUpdatedAt sets the "updated_at" field. +func (_c *BrokerDispatchCreate) SetUpdatedAt(v time.Time) *BrokerDispatchCreate { + _c.mutation.SetUpdatedAt(v) + return _c +} + +// SetNillableUpdatedAt sets the "updated_at" field if the given value is not nil. +func (_c *BrokerDispatchCreate) SetNillableUpdatedAt(v *time.Time) *BrokerDispatchCreate { + if v != nil { + _c.SetUpdatedAt(*v) + } + return _c +} + +// SetDeadlineAt sets the "deadline_at" field. +func (_c *BrokerDispatchCreate) SetDeadlineAt(v time.Time) *BrokerDispatchCreate { + _c.mutation.SetDeadlineAt(v) + return _c +} + +// SetNillableDeadlineAt sets the "deadline_at" field if the given value is not nil. +func (_c *BrokerDispatchCreate) SetNillableDeadlineAt(v *time.Time) *BrokerDispatchCreate { + if v != nil { + _c.SetDeadlineAt(*v) + } + return _c +} + +// SetID sets the "id" field. +func (_c *BrokerDispatchCreate) SetID(v uuid.UUID) *BrokerDispatchCreate { + _c.mutation.SetID(v) + return _c +} + +// SetNillableID sets the "id" field if the given value is not nil. +func (_c *BrokerDispatchCreate) SetNillableID(v *uuid.UUID) *BrokerDispatchCreate { + if v != nil { + _c.SetID(*v) + } + return _c +} + +// Mutation returns the BrokerDispatchMutation object of the builder. +func (_c *BrokerDispatchCreate) Mutation() *BrokerDispatchMutation { + return _c.mutation +} + +// Save creates the BrokerDispatch in the database. +func (_c *BrokerDispatchCreate) Save(ctx context.Context) (*BrokerDispatch, error) { + _c.defaults() + return withHooks(ctx, _c.sqlSave, _c.mutation, _c.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (_c *BrokerDispatchCreate) SaveX(ctx context.Context) *BrokerDispatch { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *BrokerDispatchCreate) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *BrokerDispatchCreate) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_c *BrokerDispatchCreate) defaults() { + if _, ok := _c.mutation.State(); !ok { + v := brokerdispatch.DefaultState + _c.mutation.SetState(v) + } + if _, ok := _c.mutation.Attempts(); !ok { + v := brokerdispatch.DefaultAttempts + _c.mutation.SetAttempts(v) + } + if _, ok := _c.mutation.CreatedAt(); !ok { + v := brokerdispatch.DefaultCreatedAt() + _c.mutation.SetCreatedAt(v) + } + if _, ok := _c.mutation.UpdatedAt(); !ok { + v := brokerdispatch.DefaultUpdatedAt() + _c.mutation.SetUpdatedAt(v) + } + if _, ok := _c.mutation.ID(); !ok { + v := brokerdispatch.DefaultID() + _c.mutation.SetID(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_c *BrokerDispatchCreate) check() error { + if _, ok := _c.mutation.BrokerID(); !ok { + return &ValidationError{Name: "broker_id", err: errors.New(`ent: missing required field "BrokerDispatch.broker_id"`)} + } + if _, ok := _c.mutation.GetOp(); !ok { + return &ValidationError{Name: "op", err: errors.New(`ent: missing required field "BrokerDispatch.op"`)} + } + if v, ok := _c.mutation.GetOp(); ok { + if err := brokerdispatch.OpValidator(v); err != nil { + return &ValidationError{Name: "op", err: fmt.Errorf(`ent: validator failed for field "BrokerDispatch.op": %w`, err)} + } + } + if _, ok := _c.mutation.State(); !ok { + return &ValidationError{Name: "state", err: errors.New(`ent: missing required field "BrokerDispatch.state"`)} + } + if _, ok := _c.mutation.Attempts(); !ok { + return &ValidationError{Name: "attempts", err: errors.New(`ent: missing required field "BrokerDispatch.attempts"`)} + } + if _, ok := _c.mutation.CreatedAt(); !ok { + return &ValidationError{Name: "created_at", err: errors.New(`ent: missing required field "BrokerDispatch.created_at"`)} + } + if _, ok := _c.mutation.UpdatedAt(); !ok { + return &ValidationError{Name: "updated_at", err: errors.New(`ent: missing required field "BrokerDispatch.updated_at"`)} + } + return nil +} + +func (_c *BrokerDispatchCreate) sqlSave(ctx context.Context) (*BrokerDispatch, error) { + if err := _c.check(); err != nil { + return nil, err + } + _node, _spec := _c.createSpec() + if err := sqlgraph.CreateNode(ctx, _c.driver, _spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + if _spec.ID.Value != nil { + if id, ok := _spec.ID.Value.(*uuid.UUID); ok { + _node.ID = *id + } else if err := _node.ID.Scan(_spec.ID.Value); err != nil { + return nil, err + } + } + _c.mutation.id = &_node.ID + _c.mutation.done = true + return _node, nil +} + +func (_c *BrokerDispatchCreate) createSpec() (*BrokerDispatch, *sqlgraph.CreateSpec) { + var ( + _node = &BrokerDispatch{config: _c.config} + _spec = sqlgraph.NewCreateSpec(brokerdispatch.Table, sqlgraph.NewFieldSpec(brokerdispatch.FieldID, field.TypeUUID)) + ) + _spec.OnConflict = _c.conflict + if id, ok := _c.mutation.ID(); ok { + _node.ID = id + _spec.ID.Value = &id + } + if value, ok := _c.mutation.BrokerID(); ok { + _spec.SetField(brokerdispatch.FieldBrokerID, field.TypeUUID, value) + _node.BrokerID = value + } + if value, ok := _c.mutation.AgentID(); ok { + _spec.SetField(brokerdispatch.FieldAgentID, field.TypeUUID, value) + _node.AgentID = &value + } + if value, ok := _c.mutation.AgentSlug(); ok { + _spec.SetField(brokerdispatch.FieldAgentSlug, field.TypeString, value) + _node.AgentSlug = value + } + if value, ok := _c.mutation.ProjectID(); ok { + _spec.SetField(brokerdispatch.FieldProjectID, field.TypeUUID, value) + _node.ProjectID = &value + } + if value, ok := _c.mutation.GetOp(); ok { + _spec.SetField(brokerdispatch.FieldOp, field.TypeString, value) + _node.Op = value + } + if value, ok := _c.mutation.Args(); ok { + _spec.SetField(brokerdispatch.FieldArgs, field.TypeString, value) + _node.Args = value + } + if value, ok := _c.mutation.State(); ok { + _spec.SetField(brokerdispatch.FieldState, field.TypeString, value) + _node.State = value + } + if value, ok := _c.mutation.Result(); ok { + _spec.SetField(brokerdispatch.FieldResult, field.TypeString, value) + _node.Result = value + } + if value, ok := _c.mutation.ClaimedBy(); ok { + _spec.SetField(brokerdispatch.FieldClaimedBy, field.TypeString, value) + _node.ClaimedBy = value + } + if value, ok := _c.mutation.Attempts(); ok { + _spec.SetField(brokerdispatch.FieldAttempts, field.TypeInt, value) + _node.Attempts = value + } + if value, ok := _c.mutation.Error(); ok { + _spec.SetField(brokerdispatch.FieldError, field.TypeString, value) + _node.Error = value + } + if value, ok := _c.mutation.CreatedAt(); ok { + _spec.SetField(brokerdispatch.FieldCreatedAt, field.TypeTime, value) + _node.CreatedAt = value + } + if value, ok := _c.mutation.UpdatedAt(); ok { + _spec.SetField(brokerdispatch.FieldUpdatedAt, field.TypeTime, value) + _node.UpdatedAt = value + } + if value, ok := _c.mutation.DeadlineAt(); ok { + _spec.SetField(brokerdispatch.FieldDeadlineAt, field.TypeTime, value) + _node.DeadlineAt = &value + } + return _node, _spec +} + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.BrokerDispatch.Create(). +// SetBrokerID(v). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.BrokerDispatchUpsert) { +// SetBrokerID(v+v). +// }). +// Exec(ctx) +func (_c *BrokerDispatchCreate) OnConflict(opts ...sql.ConflictOption) *BrokerDispatchUpsertOne { + _c.conflict = opts + return &BrokerDispatchUpsertOne{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.BrokerDispatch.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *BrokerDispatchCreate) OnConflictColumns(columns ...string) *BrokerDispatchUpsertOne { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &BrokerDispatchUpsertOne{ + create: _c, + } +} + +type ( + // BrokerDispatchUpsertOne is the builder for "upsert"-ing + // one BrokerDispatch node. + BrokerDispatchUpsertOne struct { + create *BrokerDispatchCreate + } + + // BrokerDispatchUpsert is the "OnConflict" setter. + BrokerDispatchUpsert struct { + *sql.UpdateSet + } +) + +// SetBrokerID sets the "broker_id" field. +func (u *BrokerDispatchUpsert) SetBrokerID(v uuid.UUID) *BrokerDispatchUpsert { + u.Set(brokerdispatch.FieldBrokerID, v) + return u +} + +// UpdateBrokerID sets the "broker_id" field to the value that was provided on create. +func (u *BrokerDispatchUpsert) UpdateBrokerID() *BrokerDispatchUpsert { + u.SetExcluded(brokerdispatch.FieldBrokerID) + return u +} + +// SetAgentID sets the "agent_id" field. +func (u *BrokerDispatchUpsert) SetAgentID(v uuid.UUID) *BrokerDispatchUpsert { + u.Set(brokerdispatch.FieldAgentID, v) + return u +} + +// UpdateAgentID sets the "agent_id" field to the value that was provided on create. +func (u *BrokerDispatchUpsert) UpdateAgentID() *BrokerDispatchUpsert { + u.SetExcluded(brokerdispatch.FieldAgentID) + return u +} + +// ClearAgentID clears the value of the "agent_id" field. +func (u *BrokerDispatchUpsert) ClearAgentID() *BrokerDispatchUpsert { + u.SetNull(brokerdispatch.FieldAgentID) + return u +} + +// SetAgentSlug sets the "agent_slug" field. +func (u *BrokerDispatchUpsert) SetAgentSlug(v string) *BrokerDispatchUpsert { + u.Set(brokerdispatch.FieldAgentSlug, v) + return u +} + +// UpdateAgentSlug sets the "agent_slug" field to the value that was provided on create. +func (u *BrokerDispatchUpsert) UpdateAgentSlug() *BrokerDispatchUpsert { + u.SetExcluded(brokerdispatch.FieldAgentSlug) + return u +} + +// ClearAgentSlug clears the value of the "agent_slug" field. +func (u *BrokerDispatchUpsert) ClearAgentSlug() *BrokerDispatchUpsert { + u.SetNull(brokerdispatch.FieldAgentSlug) + return u +} + +// SetProjectID sets the "project_id" field. +func (u *BrokerDispatchUpsert) SetProjectID(v uuid.UUID) *BrokerDispatchUpsert { + u.Set(brokerdispatch.FieldProjectID, v) + return u +} + +// UpdateProjectID sets the "project_id" field to the value that was provided on create. +func (u *BrokerDispatchUpsert) UpdateProjectID() *BrokerDispatchUpsert { + u.SetExcluded(brokerdispatch.FieldProjectID) + return u +} + +// ClearProjectID clears the value of the "project_id" field. +func (u *BrokerDispatchUpsert) ClearProjectID() *BrokerDispatchUpsert { + u.SetNull(brokerdispatch.FieldProjectID) + return u +} + +// SetOp sets the "op" field. +func (u *BrokerDispatchUpsert) SetOp(v string) *BrokerDispatchUpsert { + u.Set(brokerdispatch.FieldOp, v) + return u +} + +// UpdateOp sets the "op" field to the value that was provided on create. +func (u *BrokerDispatchUpsert) UpdateOp() *BrokerDispatchUpsert { + u.SetExcluded(brokerdispatch.FieldOp) + return u +} + +// SetArgs sets the "args" field. +func (u *BrokerDispatchUpsert) SetArgs(v string) *BrokerDispatchUpsert { + u.Set(brokerdispatch.FieldArgs, v) + return u +} + +// UpdateArgs sets the "args" field to the value that was provided on create. +func (u *BrokerDispatchUpsert) UpdateArgs() *BrokerDispatchUpsert { + u.SetExcluded(brokerdispatch.FieldArgs) + return u +} + +// ClearArgs clears the value of the "args" field. +func (u *BrokerDispatchUpsert) ClearArgs() *BrokerDispatchUpsert { + u.SetNull(brokerdispatch.FieldArgs) + return u +} + +// SetState sets the "state" field. +func (u *BrokerDispatchUpsert) SetState(v string) *BrokerDispatchUpsert { + u.Set(brokerdispatch.FieldState, v) + return u +} + +// UpdateState sets the "state" field to the value that was provided on create. +func (u *BrokerDispatchUpsert) UpdateState() *BrokerDispatchUpsert { + u.SetExcluded(brokerdispatch.FieldState) + return u +} + +// SetResult sets the "result" field. +func (u *BrokerDispatchUpsert) SetResult(v string) *BrokerDispatchUpsert { + u.Set(brokerdispatch.FieldResult, v) + return u +} + +// UpdateResult sets the "result" field to the value that was provided on create. +func (u *BrokerDispatchUpsert) UpdateResult() *BrokerDispatchUpsert { + u.SetExcluded(brokerdispatch.FieldResult) + return u +} + +// ClearResult clears the value of the "result" field. +func (u *BrokerDispatchUpsert) ClearResult() *BrokerDispatchUpsert { + u.SetNull(brokerdispatch.FieldResult) + return u +} + +// SetClaimedBy sets the "claimed_by" field. +func (u *BrokerDispatchUpsert) SetClaimedBy(v string) *BrokerDispatchUpsert { + u.Set(brokerdispatch.FieldClaimedBy, v) + return u +} + +// UpdateClaimedBy sets the "claimed_by" field to the value that was provided on create. +func (u *BrokerDispatchUpsert) UpdateClaimedBy() *BrokerDispatchUpsert { + u.SetExcluded(brokerdispatch.FieldClaimedBy) + return u +} + +// ClearClaimedBy clears the value of the "claimed_by" field. +func (u *BrokerDispatchUpsert) ClearClaimedBy() *BrokerDispatchUpsert { + u.SetNull(brokerdispatch.FieldClaimedBy) + return u +} + +// SetAttempts sets the "attempts" field. +func (u *BrokerDispatchUpsert) SetAttempts(v int) *BrokerDispatchUpsert { + u.Set(brokerdispatch.FieldAttempts, v) + return u +} + +// UpdateAttempts sets the "attempts" field to the value that was provided on create. +func (u *BrokerDispatchUpsert) UpdateAttempts() *BrokerDispatchUpsert { + u.SetExcluded(brokerdispatch.FieldAttempts) + return u +} + +// AddAttempts adds v to the "attempts" field. +func (u *BrokerDispatchUpsert) AddAttempts(v int) *BrokerDispatchUpsert { + u.Add(brokerdispatch.FieldAttempts, v) + return u +} + +// SetError sets the "error" field. +func (u *BrokerDispatchUpsert) SetError(v string) *BrokerDispatchUpsert { + u.Set(brokerdispatch.FieldError, v) + return u +} + +// UpdateError sets the "error" field to the value that was provided on create. +func (u *BrokerDispatchUpsert) UpdateError() *BrokerDispatchUpsert { + u.SetExcluded(brokerdispatch.FieldError) + return u +} + +// ClearError clears the value of the "error" field. +func (u *BrokerDispatchUpsert) ClearError() *BrokerDispatchUpsert { + u.SetNull(brokerdispatch.FieldError) + return u +} + +// SetUpdatedAt sets the "updated_at" field. +func (u *BrokerDispatchUpsert) SetUpdatedAt(v time.Time) *BrokerDispatchUpsert { + u.Set(brokerdispatch.FieldUpdatedAt, v) + return u +} + +// UpdateUpdatedAt sets the "updated_at" field to the value that was provided on create. +func (u *BrokerDispatchUpsert) UpdateUpdatedAt() *BrokerDispatchUpsert { + u.SetExcluded(brokerdispatch.FieldUpdatedAt) + return u +} + +// SetDeadlineAt sets the "deadline_at" field. +func (u *BrokerDispatchUpsert) SetDeadlineAt(v time.Time) *BrokerDispatchUpsert { + u.Set(brokerdispatch.FieldDeadlineAt, v) + return u +} + +// UpdateDeadlineAt sets the "deadline_at" field to the value that was provided on create. +func (u *BrokerDispatchUpsert) UpdateDeadlineAt() *BrokerDispatchUpsert { + u.SetExcluded(brokerdispatch.FieldDeadlineAt) + return u +} + +// ClearDeadlineAt clears the value of the "deadline_at" field. +func (u *BrokerDispatchUpsert) ClearDeadlineAt() *BrokerDispatchUpsert { + u.SetNull(brokerdispatch.FieldDeadlineAt) + return u +} + +// UpdateNewValues updates the mutable fields using the new values that were set on create except the ID field. +// Using this option is equivalent to using: +// +// client.BrokerDispatch.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(brokerdispatch.FieldID) +// }), +// ). +// Exec(ctx) +func (u *BrokerDispatchUpsertOne) UpdateNewValues() *BrokerDispatchUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + if _, exists := u.create.mutation.ID(); exists { + s.SetIgnore(brokerdispatch.FieldID) + } + if _, exists := u.create.mutation.CreatedAt(); exists { + s.SetIgnore(brokerdispatch.FieldCreatedAt) + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.BrokerDispatch.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *BrokerDispatchUpsertOne) Ignore() *BrokerDispatchUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *BrokerDispatchUpsertOne) DoNothing() *BrokerDispatchUpsertOne { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the BrokerDispatchCreate.OnConflict +// documentation for more info. +func (u *BrokerDispatchUpsertOne) Update(set func(*BrokerDispatchUpsert)) *BrokerDispatchUpsertOne { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&BrokerDispatchUpsert{UpdateSet: update}) + })) + return u +} + +// SetBrokerID sets the "broker_id" field. +func (u *BrokerDispatchUpsertOne) SetBrokerID(v uuid.UUID) *BrokerDispatchUpsertOne { + return u.Update(func(s *BrokerDispatchUpsert) { + s.SetBrokerID(v) + }) +} + +// UpdateBrokerID sets the "broker_id" field to the value that was provided on create. +func (u *BrokerDispatchUpsertOne) UpdateBrokerID() *BrokerDispatchUpsertOne { + return u.Update(func(s *BrokerDispatchUpsert) { + s.UpdateBrokerID() + }) +} + +// SetAgentID sets the "agent_id" field. +func (u *BrokerDispatchUpsertOne) SetAgentID(v uuid.UUID) *BrokerDispatchUpsertOne { + return u.Update(func(s *BrokerDispatchUpsert) { + s.SetAgentID(v) + }) +} + +// UpdateAgentID sets the "agent_id" field to the value that was provided on create. +func (u *BrokerDispatchUpsertOne) UpdateAgentID() *BrokerDispatchUpsertOne { + return u.Update(func(s *BrokerDispatchUpsert) { + s.UpdateAgentID() + }) +} + +// ClearAgentID clears the value of the "agent_id" field. +func (u *BrokerDispatchUpsertOne) ClearAgentID() *BrokerDispatchUpsertOne { + return u.Update(func(s *BrokerDispatchUpsert) { + s.ClearAgentID() + }) +} + +// SetAgentSlug sets the "agent_slug" field. +func (u *BrokerDispatchUpsertOne) SetAgentSlug(v string) *BrokerDispatchUpsertOne { + return u.Update(func(s *BrokerDispatchUpsert) { + s.SetAgentSlug(v) + }) +} + +// UpdateAgentSlug sets the "agent_slug" field to the value that was provided on create. +func (u *BrokerDispatchUpsertOne) UpdateAgentSlug() *BrokerDispatchUpsertOne { + return u.Update(func(s *BrokerDispatchUpsert) { + s.UpdateAgentSlug() + }) +} + +// ClearAgentSlug clears the value of the "agent_slug" field. +func (u *BrokerDispatchUpsertOne) ClearAgentSlug() *BrokerDispatchUpsertOne { + return u.Update(func(s *BrokerDispatchUpsert) { + s.ClearAgentSlug() + }) +} + +// SetProjectID sets the "project_id" field. +func (u *BrokerDispatchUpsertOne) SetProjectID(v uuid.UUID) *BrokerDispatchUpsertOne { + return u.Update(func(s *BrokerDispatchUpsert) { + s.SetProjectID(v) + }) +} + +// UpdateProjectID sets the "project_id" field to the value that was provided on create. +func (u *BrokerDispatchUpsertOne) UpdateProjectID() *BrokerDispatchUpsertOne { + return u.Update(func(s *BrokerDispatchUpsert) { + s.UpdateProjectID() + }) +} + +// ClearProjectID clears the value of the "project_id" field. +func (u *BrokerDispatchUpsertOne) ClearProjectID() *BrokerDispatchUpsertOne { + return u.Update(func(s *BrokerDispatchUpsert) { + s.ClearProjectID() + }) +} + +// SetOp sets the "op" field. +func (u *BrokerDispatchUpsertOne) SetOp(v string) *BrokerDispatchUpsertOne { + return u.Update(func(s *BrokerDispatchUpsert) { + s.SetOp(v) + }) +} + +// UpdateOp sets the "op" field to the value that was provided on create. +func (u *BrokerDispatchUpsertOne) UpdateOp() *BrokerDispatchUpsertOne { + return u.Update(func(s *BrokerDispatchUpsert) { + s.UpdateOp() + }) +} + +// SetArgs sets the "args" field. +func (u *BrokerDispatchUpsertOne) SetArgs(v string) *BrokerDispatchUpsertOne { + return u.Update(func(s *BrokerDispatchUpsert) { + s.SetArgs(v) + }) +} + +// UpdateArgs sets the "args" field to the value that was provided on create. +func (u *BrokerDispatchUpsertOne) UpdateArgs() *BrokerDispatchUpsertOne { + return u.Update(func(s *BrokerDispatchUpsert) { + s.UpdateArgs() + }) +} + +// ClearArgs clears the value of the "args" field. +func (u *BrokerDispatchUpsertOne) ClearArgs() *BrokerDispatchUpsertOne { + return u.Update(func(s *BrokerDispatchUpsert) { + s.ClearArgs() + }) +} + +// SetState sets the "state" field. +func (u *BrokerDispatchUpsertOne) SetState(v string) *BrokerDispatchUpsertOne { + return u.Update(func(s *BrokerDispatchUpsert) { + s.SetState(v) + }) +} + +// UpdateState sets the "state" field to the value that was provided on create. +func (u *BrokerDispatchUpsertOne) UpdateState() *BrokerDispatchUpsertOne { + return u.Update(func(s *BrokerDispatchUpsert) { + s.UpdateState() + }) +} + +// SetResult sets the "result" field. +func (u *BrokerDispatchUpsertOne) SetResult(v string) *BrokerDispatchUpsertOne { + return u.Update(func(s *BrokerDispatchUpsert) { + s.SetResult(v) + }) +} + +// UpdateResult sets the "result" field to the value that was provided on create. +func (u *BrokerDispatchUpsertOne) UpdateResult() *BrokerDispatchUpsertOne { + return u.Update(func(s *BrokerDispatchUpsert) { + s.UpdateResult() + }) +} + +// ClearResult clears the value of the "result" field. +func (u *BrokerDispatchUpsertOne) ClearResult() *BrokerDispatchUpsertOne { + return u.Update(func(s *BrokerDispatchUpsert) { + s.ClearResult() + }) +} + +// SetClaimedBy sets the "claimed_by" field. +func (u *BrokerDispatchUpsertOne) SetClaimedBy(v string) *BrokerDispatchUpsertOne { + return u.Update(func(s *BrokerDispatchUpsert) { + s.SetClaimedBy(v) + }) +} + +// UpdateClaimedBy sets the "claimed_by" field to the value that was provided on create. +func (u *BrokerDispatchUpsertOne) UpdateClaimedBy() *BrokerDispatchUpsertOne { + return u.Update(func(s *BrokerDispatchUpsert) { + s.UpdateClaimedBy() + }) +} + +// ClearClaimedBy clears the value of the "claimed_by" field. +func (u *BrokerDispatchUpsertOne) ClearClaimedBy() *BrokerDispatchUpsertOne { + return u.Update(func(s *BrokerDispatchUpsert) { + s.ClearClaimedBy() + }) +} + +// SetAttempts sets the "attempts" field. +func (u *BrokerDispatchUpsertOne) SetAttempts(v int) *BrokerDispatchUpsertOne { + return u.Update(func(s *BrokerDispatchUpsert) { + s.SetAttempts(v) + }) +} + +// AddAttempts adds v to the "attempts" field. +func (u *BrokerDispatchUpsertOne) AddAttempts(v int) *BrokerDispatchUpsertOne { + return u.Update(func(s *BrokerDispatchUpsert) { + s.AddAttempts(v) + }) +} + +// UpdateAttempts sets the "attempts" field to the value that was provided on create. +func (u *BrokerDispatchUpsertOne) UpdateAttempts() *BrokerDispatchUpsertOne { + return u.Update(func(s *BrokerDispatchUpsert) { + s.UpdateAttempts() + }) +} + +// SetError sets the "error" field. +func (u *BrokerDispatchUpsertOne) SetError(v string) *BrokerDispatchUpsertOne { + return u.Update(func(s *BrokerDispatchUpsert) { + s.SetError(v) + }) +} + +// UpdateError sets the "error" field to the value that was provided on create. +func (u *BrokerDispatchUpsertOne) UpdateError() *BrokerDispatchUpsertOne { + return u.Update(func(s *BrokerDispatchUpsert) { + s.UpdateError() + }) +} + +// ClearError clears the value of the "error" field. +func (u *BrokerDispatchUpsertOne) ClearError() *BrokerDispatchUpsertOne { + return u.Update(func(s *BrokerDispatchUpsert) { + s.ClearError() + }) +} + +// SetUpdatedAt sets the "updated_at" field. +func (u *BrokerDispatchUpsertOne) SetUpdatedAt(v time.Time) *BrokerDispatchUpsertOne { + return u.Update(func(s *BrokerDispatchUpsert) { + s.SetUpdatedAt(v) + }) +} + +// UpdateUpdatedAt sets the "updated_at" field to the value that was provided on create. +func (u *BrokerDispatchUpsertOne) UpdateUpdatedAt() *BrokerDispatchUpsertOne { + return u.Update(func(s *BrokerDispatchUpsert) { + s.UpdateUpdatedAt() + }) +} + +// SetDeadlineAt sets the "deadline_at" field. +func (u *BrokerDispatchUpsertOne) SetDeadlineAt(v time.Time) *BrokerDispatchUpsertOne { + return u.Update(func(s *BrokerDispatchUpsert) { + s.SetDeadlineAt(v) + }) +} + +// UpdateDeadlineAt sets the "deadline_at" field to the value that was provided on create. +func (u *BrokerDispatchUpsertOne) UpdateDeadlineAt() *BrokerDispatchUpsertOne { + return u.Update(func(s *BrokerDispatchUpsert) { + s.UpdateDeadlineAt() + }) +} + +// ClearDeadlineAt clears the value of the "deadline_at" field. +func (u *BrokerDispatchUpsertOne) ClearDeadlineAt() *BrokerDispatchUpsertOne { + return u.Update(func(s *BrokerDispatchUpsert) { + s.ClearDeadlineAt() + }) +} + +// Exec executes the query. +func (u *BrokerDispatchUpsertOne) Exec(ctx context.Context) error { + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for BrokerDispatchCreate.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *BrokerDispatchUpsertOne) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} + +// Exec executes the UPSERT query and returns the inserted/updated ID. +func (u *BrokerDispatchUpsertOne) ID(ctx context.Context) (id uuid.UUID, err error) { + if u.create.driver.Dialect() == dialect.MySQL { + // In case of "ON CONFLICT", there is no way to get back non-numeric ID + // fields from the database since MySQL does not support the RETURNING clause. + return id, errors.New("ent: BrokerDispatchUpsertOne.ID is not supported by MySQL driver. Use BrokerDispatchUpsertOne.Exec instead") + } + node, err := u.create.Save(ctx) + if err != nil { + return id, err + } + return node.ID, nil +} + +// IDX is like ID, but panics if an error occurs. +func (u *BrokerDispatchUpsertOne) IDX(ctx context.Context) uuid.UUID { + id, err := u.ID(ctx) + if err != nil { + panic(err) + } + return id +} + +// BrokerDispatchCreateBulk is the builder for creating many BrokerDispatch entities in bulk. +type BrokerDispatchCreateBulk struct { + config + err error + builders []*BrokerDispatchCreate + conflict []sql.ConflictOption +} + +// Save creates the BrokerDispatch entities in the database. +func (_c *BrokerDispatchCreateBulk) Save(ctx context.Context) ([]*BrokerDispatch, error) { + if _c.err != nil { + return nil, _c.err + } + specs := make([]*sqlgraph.CreateSpec, len(_c.builders)) + nodes := make([]*BrokerDispatch, len(_c.builders)) + mutators := make([]Mutator, len(_c.builders)) + for i := range _c.builders { + func(i int, root context.Context) { + builder := _c.builders[i] + builder.defaults() + var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { + mutation, ok := m.(*BrokerDispatchMutation) + if !ok { + return nil, fmt.Errorf("unexpected mutation type %T", m) + } + if err := builder.check(); err != nil { + return nil, err + } + builder.mutation = mutation + var err error + nodes[i], specs[i] = builder.createSpec() + if i < len(mutators)-1 { + _, err = mutators[i+1].Mutate(root, _c.builders[i+1].mutation) + } else { + spec := &sqlgraph.BatchCreateSpec{Nodes: specs} + spec.OnConflict = _c.conflict + // Invoke the actual operation on the latest mutation in the chain. + if err = sqlgraph.BatchCreate(ctx, _c.driver, spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + } + } + if err != nil { + return nil, err + } + mutation.id = &nodes[i].ID + mutation.done = true + return nodes[i], nil + }) + for i := len(builder.hooks) - 1; i >= 0; i-- { + mut = builder.hooks[i](mut) + } + mutators[i] = mut + }(i, ctx) + } + if len(mutators) > 0 { + if _, err := mutators[0].Mutate(ctx, _c.builders[0].mutation); err != nil { + return nil, err + } + } + return nodes, nil +} + +// SaveX is like Save, but panics if an error occurs. +func (_c *BrokerDispatchCreateBulk) SaveX(ctx context.Context) []*BrokerDispatch { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *BrokerDispatchCreateBulk) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *BrokerDispatchCreateBulk) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} + +// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause +// of the `INSERT` statement. For example: +// +// client.BrokerDispatch.CreateBulk(builders...). +// OnConflict( +// // Update the row with the new values +// // the was proposed for insertion. +// sql.ResolveWithNewValues(), +// ). +// // Override some of the fields with custom +// // update values. +// Update(func(u *ent.BrokerDispatchUpsert) { +// SetBrokerID(v+v). +// }). +// Exec(ctx) +func (_c *BrokerDispatchCreateBulk) OnConflict(opts ...sql.ConflictOption) *BrokerDispatchUpsertBulk { + _c.conflict = opts + return &BrokerDispatchUpsertBulk{ + create: _c, + } +} + +// OnConflictColumns calls `OnConflict` and configures the columns +// as conflict target. Using this option is equivalent to using: +// +// client.BrokerDispatch.Create(). +// OnConflict(sql.ConflictColumns(columns...)). +// Exec(ctx) +func (_c *BrokerDispatchCreateBulk) OnConflictColumns(columns ...string) *BrokerDispatchUpsertBulk { + _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...)) + return &BrokerDispatchUpsertBulk{ + create: _c, + } +} + +// BrokerDispatchUpsertBulk is the builder for "upsert"-ing +// a bulk of BrokerDispatch nodes. +type BrokerDispatchUpsertBulk struct { + create *BrokerDispatchCreateBulk +} + +// UpdateNewValues updates the mutable fields using the new values that +// were set on create. Using this option is equivalent to using: +// +// client.BrokerDispatch.Create(). +// OnConflict( +// sql.ResolveWithNewValues(), +// sql.ResolveWith(func(u *sql.UpdateSet) { +// u.SetIgnore(brokerdispatch.FieldID) +// }), +// ). +// Exec(ctx) +func (u *BrokerDispatchUpsertBulk) UpdateNewValues() *BrokerDispatchUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues()) + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) { + for _, b := range u.create.builders { + if _, exists := b.mutation.ID(); exists { + s.SetIgnore(brokerdispatch.FieldID) + } + if _, exists := b.mutation.CreatedAt(); exists { + s.SetIgnore(brokerdispatch.FieldCreatedAt) + } + } + })) + return u +} + +// Ignore sets each column to itself in case of conflict. +// Using this option is equivalent to using: +// +// client.BrokerDispatch.Create(). +// OnConflict(sql.ResolveWithIgnore()). +// Exec(ctx) +func (u *BrokerDispatchUpsertBulk) Ignore() *BrokerDispatchUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore()) + return u +} + +// DoNothing configures the conflict_action to `DO NOTHING`. +// Supported only by SQLite and PostgreSQL. +func (u *BrokerDispatchUpsertBulk) DoNothing() *BrokerDispatchUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.DoNothing()) + return u +} + +// Update allows overriding fields `UPDATE` values. See the BrokerDispatchCreateBulk.OnConflict +// documentation for more info. +func (u *BrokerDispatchUpsertBulk) Update(set func(*BrokerDispatchUpsert)) *BrokerDispatchUpsertBulk { + u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) { + set(&BrokerDispatchUpsert{UpdateSet: update}) + })) + return u +} + +// SetBrokerID sets the "broker_id" field. +func (u *BrokerDispatchUpsertBulk) SetBrokerID(v uuid.UUID) *BrokerDispatchUpsertBulk { + return u.Update(func(s *BrokerDispatchUpsert) { + s.SetBrokerID(v) + }) +} + +// UpdateBrokerID sets the "broker_id" field to the value that was provided on create. +func (u *BrokerDispatchUpsertBulk) UpdateBrokerID() *BrokerDispatchUpsertBulk { + return u.Update(func(s *BrokerDispatchUpsert) { + s.UpdateBrokerID() + }) +} + +// SetAgentID sets the "agent_id" field. +func (u *BrokerDispatchUpsertBulk) SetAgentID(v uuid.UUID) *BrokerDispatchUpsertBulk { + return u.Update(func(s *BrokerDispatchUpsert) { + s.SetAgentID(v) + }) +} + +// UpdateAgentID sets the "agent_id" field to the value that was provided on create. +func (u *BrokerDispatchUpsertBulk) UpdateAgentID() *BrokerDispatchUpsertBulk { + return u.Update(func(s *BrokerDispatchUpsert) { + s.UpdateAgentID() + }) +} + +// ClearAgentID clears the value of the "agent_id" field. +func (u *BrokerDispatchUpsertBulk) ClearAgentID() *BrokerDispatchUpsertBulk { + return u.Update(func(s *BrokerDispatchUpsert) { + s.ClearAgentID() + }) +} + +// SetAgentSlug sets the "agent_slug" field. +func (u *BrokerDispatchUpsertBulk) SetAgentSlug(v string) *BrokerDispatchUpsertBulk { + return u.Update(func(s *BrokerDispatchUpsert) { + s.SetAgentSlug(v) + }) +} + +// UpdateAgentSlug sets the "agent_slug" field to the value that was provided on create. +func (u *BrokerDispatchUpsertBulk) UpdateAgentSlug() *BrokerDispatchUpsertBulk { + return u.Update(func(s *BrokerDispatchUpsert) { + s.UpdateAgentSlug() + }) +} + +// ClearAgentSlug clears the value of the "agent_slug" field. +func (u *BrokerDispatchUpsertBulk) ClearAgentSlug() *BrokerDispatchUpsertBulk { + return u.Update(func(s *BrokerDispatchUpsert) { + s.ClearAgentSlug() + }) +} + +// SetProjectID sets the "project_id" field. +func (u *BrokerDispatchUpsertBulk) SetProjectID(v uuid.UUID) *BrokerDispatchUpsertBulk { + return u.Update(func(s *BrokerDispatchUpsert) { + s.SetProjectID(v) + }) +} + +// UpdateProjectID sets the "project_id" field to the value that was provided on create. +func (u *BrokerDispatchUpsertBulk) UpdateProjectID() *BrokerDispatchUpsertBulk { + return u.Update(func(s *BrokerDispatchUpsert) { + s.UpdateProjectID() + }) +} + +// ClearProjectID clears the value of the "project_id" field. +func (u *BrokerDispatchUpsertBulk) ClearProjectID() *BrokerDispatchUpsertBulk { + return u.Update(func(s *BrokerDispatchUpsert) { + s.ClearProjectID() + }) +} + +// SetOp sets the "op" field. +func (u *BrokerDispatchUpsertBulk) SetOp(v string) *BrokerDispatchUpsertBulk { + return u.Update(func(s *BrokerDispatchUpsert) { + s.SetOp(v) + }) +} + +// UpdateOp sets the "op" field to the value that was provided on create. +func (u *BrokerDispatchUpsertBulk) UpdateOp() *BrokerDispatchUpsertBulk { + return u.Update(func(s *BrokerDispatchUpsert) { + s.UpdateOp() + }) +} + +// SetArgs sets the "args" field. +func (u *BrokerDispatchUpsertBulk) SetArgs(v string) *BrokerDispatchUpsertBulk { + return u.Update(func(s *BrokerDispatchUpsert) { + s.SetArgs(v) + }) +} + +// UpdateArgs sets the "args" field to the value that was provided on create. +func (u *BrokerDispatchUpsertBulk) UpdateArgs() *BrokerDispatchUpsertBulk { + return u.Update(func(s *BrokerDispatchUpsert) { + s.UpdateArgs() + }) +} + +// ClearArgs clears the value of the "args" field. +func (u *BrokerDispatchUpsertBulk) ClearArgs() *BrokerDispatchUpsertBulk { + return u.Update(func(s *BrokerDispatchUpsert) { + s.ClearArgs() + }) +} + +// SetState sets the "state" field. +func (u *BrokerDispatchUpsertBulk) SetState(v string) *BrokerDispatchUpsertBulk { + return u.Update(func(s *BrokerDispatchUpsert) { + s.SetState(v) + }) +} + +// UpdateState sets the "state" field to the value that was provided on create. +func (u *BrokerDispatchUpsertBulk) UpdateState() *BrokerDispatchUpsertBulk { + return u.Update(func(s *BrokerDispatchUpsert) { + s.UpdateState() + }) +} + +// SetResult sets the "result" field. +func (u *BrokerDispatchUpsertBulk) SetResult(v string) *BrokerDispatchUpsertBulk { + return u.Update(func(s *BrokerDispatchUpsert) { + s.SetResult(v) + }) +} + +// UpdateResult sets the "result" field to the value that was provided on create. +func (u *BrokerDispatchUpsertBulk) UpdateResult() *BrokerDispatchUpsertBulk { + return u.Update(func(s *BrokerDispatchUpsert) { + s.UpdateResult() + }) +} + +// ClearResult clears the value of the "result" field. +func (u *BrokerDispatchUpsertBulk) ClearResult() *BrokerDispatchUpsertBulk { + return u.Update(func(s *BrokerDispatchUpsert) { + s.ClearResult() + }) +} + +// SetClaimedBy sets the "claimed_by" field. +func (u *BrokerDispatchUpsertBulk) SetClaimedBy(v string) *BrokerDispatchUpsertBulk { + return u.Update(func(s *BrokerDispatchUpsert) { + s.SetClaimedBy(v) + }) +} + +// UpdateClaimedBy sets the "claimed_by" field to the value that was provided on create. +func (u *BrokerDispatchUpsertBulk) UpdateClaimedBy() *BrokerDispatchUpsertBulk { + return u.Update(func(s *BrokerDispatchUpsert) { + s.UpdateClaimedBy() + }) +} + +// ClearClaimedBy clears the value of the "claimed_by" field. +func (u *BrokerDispatchUpsertBulk) ClearClaimedBy() *BrokerDispatchUpsertBulk { + return u.Update(func(s *BrokerDispatchUpsert) { + s.ClearClaimedBy() + }) +} + +// SetAttempts sets the "attempts" field. +func (u *BrokerDispatchUpsertBulk) SetAttempts(v int) *BrokerDispatchUpsertBulk { + return u.Update(func(s *BrokerDispatchUpsert) { + s.SetAttempts(v) + }) +} + +// AddAttempts adds v to the "attempts" field. +func (u *BrokerDispatchUpsertBulk) AddAttempts(v int) *BrokerDispatchUpsertBulk { + return u.Update(func(s *BrokerDispatchUpsert) { + s.AddAttempts(v) + }) +} + +// UpdateAttempts sets the "attempts" field to the value that was provided on create. +func (u *BrokerDispatchUpsertBulk) UpdateAttempts() *BrokerDispatchUpsertBulk { + return u.Update(func(s *BrokerDispatchUpsert) { + s.UpdateAttempts() + }) +} + +// SetError sets the "error" field. +func (u *BrokerDispatchUpsertBulk) SetError(v string) *BrokerDispatchUpsertBulk { + return u.Update(func(s *BrokerDispatchUpsert) { + s.SetError(v) + }) +} + +// UpdateError sets the "error" field to the value that was provided on create. +func (u *BrokerDispatchUpsertBulk) UpdateError() *BrokerDispatchUpsertBulk { + return u.Update(func(s *BrokerDispatchUpsert) { + s.UpdateError() + }) +} + +// ClearError clears the value of the "error" field. +func (u *BrokerDispatchUpsertBulk) ClearError() *BrokerDispatchUpsertBulk { + return u.Update(func(s *BrokerDispatchUpsert) { + s.ClearError() + }) +} + +// SetUpdatedAt sets the "updated_at" field. +func (u *BrokerDispatchUpsertBulk) SetUpdatedAt(v time.Time) *BrokerDispatchUpsertBulk { + return u.Update(func(s *BrokerDispatchUpsert) { + s.SetUpdatedAt(v) + }) +} + +// UpdateUpdatedAt sets the "updated_at" field to the value that was provided on create. +func (u *BrokerDispatchUpsertBulk) UpdateUpdatedAt() *BrokerDispatchUpsertBulk { + return u.Update(func(s *BrokerDispatchUpsert) { + s.UpdateUpdatedAt() + }) +} + +// SetDeadlineAt sets the "deadline_at" field. +func (u *BrokerDispatchUpsertBulk) SetDeadlineAt(v time.Time) *BrokerDispatchUpsertBulk { + return u.Update(func(s *BrokerDispatchUpsert) { + s.SetDeadlineAt(v) + }) +} + +// UpdateDeadlineAt sets the "deadline_at" field to the value that was provided on create. +func (u *BrokerDispatchUpsertBulk) UpdateDeadlineAt() *BrokerDispatchUpsertBulk { + return u.Update(func(s *BrokerDispatchUpsert) { + s.UpdateDeadlineAt() + }) +} + +// ClearDeadlineAt clears the value of the "deadline_at" field. +func (u *BrokerDispatchUpsertBulk) ClearDeadlineAt() *BrokerDispatchUpsertBulk { + return u.Update(func(s *BrokerDispatchUpsert) { + s.ClearDeadlineAt() + }) +} + +// Exec executes the query. +func (u *BrokerDispatchUpsertBulk) Exec(ctx context.Context) error { + if u.create.err != nil { + return u.create.err + } + for i, b := range u.create.builders { + if len(b.conflict) != 0 { + return fmt.Errorf("ent: OnConflict was set for builder %d. Set it on the BrokerDispatchCreateBulk instead", i) + } + } + if len(u.create.conflict) == 0 { + return errors.New("ent: missing options for BrokerDispatchCreateBulk.OnConflict") + } + return u.create.Exec(ctx) +} + +// ExecX is like Exec, but panics if an error occurs. +func (u *BrokerDispatchUpsertBulk) ExecX(ctx context.Context) { + if err := u.create.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/pkg/ent/brokerdispatch_delete.go b/pkg/ent/brokerdispatch_delete.go new file mode 100644 index 000000000..88adbccfb --- /dev/null +++ b/pkg/ent/brokerdispatch_delete.go @@ -0,0 +1,88 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/brokerdispatch" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" +) + +// BrokerDispatchDelete is the builder for deleting a BrokerDispatch entity. +type BrokerDispatchDelete struct { + config + hooks []Hook + mutation *BrokerDispatchMutation +} + +// Where appends a list predicates to the BrokerDispatchDelete builder. +func (_d *BrokerDispatchDelete) Where(ps ...predicate.BrokerDispatch) *BrokerDispatchDelete { + _d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (_d *BrokerDispatchDelete) Exec(ctx context.Context) (int, error) { + return withHooks(ctx, _d.sqlExec, _d.mutation, _d.hooks) +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *BrokerDispatchDelete) ExecX(ctx context.Context) int { + n, err := _d.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (_d *BrokerDispatchDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(brokerdispatch.Table, sqlgraph.NewFieldSpec(brokerdispatch.FieldID, field.TypeUUID)) + if ps := _d.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + affected, err := sqlgraph.DeleteNodes(ctx, _d.driver, _spec) + if err != nil && sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + _d.mutation.done = true + return affected, err +} + +// BrokerDispatchDeleteOne is the builder for deleting a single BrokerDispatch entity. +type BrokerDispatchDeleteOne struct { + _d *BrokerDispatchDelete +} + +// Where appends a list predicates to the BrokerDispatchDelete builder. +func (_d *BrokerDispatchDeleteOne) Where(ps ...predicate.BrokerDispatch) *BrokerDispatchDeleteOne { + _d._d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query. +func (_d *BrokerDispatchDeleteOne) Exec(ctx context.Context) error { + n, err := _d._d.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{brokerdispatch.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *BrokerDispatchDeleteOne) ExecX(ctx context.Context) { + if err := _d.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/pkg/ent/brokerdispatch_query.go b/pkg/ent/brokerdispatch_query.go new file mode 100644 index 000000000..67bb47347 --- /dev/null +++ b/pkg/ent/brokerdispatch_query.go @@ -0,0 +1,565 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "fmt" + "math" + + "entgo.io/ent" + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/brokerdispatch" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/google/uuid" +) + +// BrokerDispatchQuery is the builder for querying BrokerDispatch entities. +type BrokerDispatchQuery struct { + config + ctx *QueryContext + order []brokerdispatch.OrderOption + inters []Interceptor + predicates []predicate.BrokerDispatch + modifiers []func(*sql.Selector) + // intermediate query (i.e. traversal path). + sql *sql.Selector + path func(context.Context) (*sql.Selector, error) +} + +// Where adds a new predicate for the BrokerDispatchQuery builder. +func (_q *BrokerDispatchQuery) Where(ps ...predicate.BrokerDispatch) *BrokerDispatchQuery { + _q.predicates = append(_q.predicates, ps...) + return _q +} + +// Limit the number of records to be returned by this query. +func (_q *BrokerDispatchQuery) Limit(limit int) *BrokerDispatchQuery { + _q.ctx.Limit = &limit + return _q +} + +// Offset to start from. +func (_q *BrokerDispatchQuery) Offset(offset int) *BrokerDispatchQuery { + _q.ctx.Offset = &offset + return _q +} + +// Unique configures the query builder to filter duplicate records on query. +// By default, unique is set to true, and can be disabled using this method. +func (_q *BrokerDispatchQuery) Unique(unique bool) *BrokerDispatchQuery { + _q.ctx.Unique = &unique + return _q +} + +// Order specifies how the records should be ordered. +func (_q *BrokerDispatchQuery) Order(o ...brokerdispatch.OrderOption) *BrokerDispatchQuery { + _q.order = append(_q.order, o...) + return _q +} + +// First returns the first BrokerDispatch entity from the query. +// Returns a *NotFoundError when no BrokerDispatch was found. +func (_q *BrokerDispatchQuery) First(ctx context.Context) (*BrokerDispatch, error) { + nodes, err := _q.Limit(1).All(setContextOp(ctx, _q.ctx, ent.OpQueryFirst)) + if err != nil { + return nil, err + } + if len(nodes) == 0 { + return nil, &NotFoundError{brokerdispatch.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (_q *BrokerDispatchQuery) FirstX(ctx context.Context) *BrokerDispatch { + node, err := _q.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// FirstID returns the first BrokerDispatch ID from the query. +// Returns a *NotFoundError when no BrokerDispatch ID was found. +func (_q *BrokerDispatchQuery) FirstID(ctx context.Context) (id uuid.UUID, err error) { + var ids []uuid.UUID + if ids, err = _q.Limit(1).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryFirstID)); err != nil { + return + } + if len(ids) == 0 { + err = &NotFoundError{brokerdispatch.Label} + return + } + return ids[0], nil +} + +// FirstIDX is like FirstID, but panics if an error occurs. +func (_q *BrokerDispatchQuery) FirstIDX(ctx context.Context) uuid.UUID { + id, err := _q.FirstID(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return id +} + +// Only returns a single BrokerDispatch entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one BrokerDispatch entity is found. +// Returns a *NotFoundError when no BrokerDispatch entities are found. +func (_q *BrokerDispatchQuery) Only(ctx context.Context) (*BrokerDispatch, error) { + nodes, err := _q.Limit(2).All(setContextOp(ctx, _q.ctx, ent.OpQueryOnly)) + if err != nil { + return nil, err + } + switch len(nodes) { + case 1: + return nodes[0], nil + case 0: + return nil, &NotFoundError{brokerdispatch.Label} + default: + return nil, &NotSingularError{brokerdispatch.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (_q *BrokerDispatchQuery) OnlyX(ctx context.Context) *BrokerDispatch { + node, err := _q.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// OnlyID is like Only, but returns the only BrokerDispatch ID in the query. +// Returns a *NotSingularError when more than one BrokerDispatch ID is found. +// Returns a *NotFoundError when no entities are found. +func (_q *BrokerDispatchQuery) OnlyID(ctx context.Context) (id uuid.UUID, err error) { + var ids []uuid.UUID + if ids, err = _q.Limit(2).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryOnlyID)); err != nil { + return + } + switch len(ids) { + case 1: + id = ids[0] + case 0: + err = &NotFoundError{brokerdispatch.Label} + default: + err = &NotSingularError{brokerdispatch.Label} + } + return +} + +// OnlyIDX is like OnlyID, but panics if an error occurs. +func (_q *BrokerDispatchQuery) OnlyIDX(ctx context.Context) uuid.UUID { + id, err := _q.OnlyID(ctx) + if err != nil { + panic(err) + } + return id +} + +// All executes the query and returns a list of BrokerDispatches. +func (_q *BrokerDispatchQuery) All(ctx context.Context) ([]*BrokerDispatch, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryAll) + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*BrokerDispatch, *BrokerDispatchQuery]() + return withInterceptors[[]*BrokerDispatch](ctx, _q, qr, _q.inters) +} + +// AllX is like All, but panics if an error occurs. +func (_q *BrokerDispatchQuery) AllX(ctx context.Context) []*BrokerDispatch { + nodes, err := _q.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// IDs executes the query and returns a list of BrokerDispatch IDs. +func (_q *BrokerDispatchQuery) IDs(ctx context.Context) (ids []uuid.UUID, err error) { + if _q.ctx.Unique == nil && _q.path != nil { + _q.Unique(true) + } + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryIDs) + if err = _q.Select(brokerdispatch.FieldID).Scan(ctx, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// IDsX is like IDs, but panics if an error occurs. +func (_q *BrokerDispatchQuery) IDsX(ctx context.Context) []uuid.UUID { + ids, err := _q.IDs(ctx) + if err != nil { + panic(err) + } + return ids +} + +// Count returns the count of the given query. +func (_q *BrokerDispatchQuery) Count(ctx context.Context) (int, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryCount) + if err := _q.prepareQuery(ctx); err != nil { + return 0, err + } + return withInterceptors[int](ctx, _q, querierCount[*BrokerDispatchQuery](), _q.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (_q *BrokerDispatchQuery) CountX(ctx context.Context) int { + count, err := _q.Count(ctx) + if err != nil { + panic(err) + } + return count +} + +// Exist returns true if the query has elements in the graph. +func (_q *BrokerDispatchQuery) Exist(ctx context.Context) (bool, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryExist) + switch _, err := _q.FirstID(ctx); { + case IsNotFound(err): + return false, nil + case err != nil: + return false, fmt.Errorf("ent: check existence: %w", err) + default: + return true, nil + } +} + +// ExistX is like Exist, but panics if an error occurs. +func (_q *BrokerDispatchQuery) ExistX(ctx context.Context) bool { + exist, err := _q.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the BrokerDispatchQuery builder, including all associated steps. It can be +// used to prepare common query builders and use them differently after the clone is made. +func (_q *BrokerDispatchQuery) Clone() *BrokerDispatchQuery { + if _q == nil { + return nil + } + return &BrokerDispatchQuery{ + config: _q.config, + ctx: _q.ctx.Clone(), + order: append([]brokerdispatch.OrderOption{}, _q.order...), + inters: append([]Interceptor{}, _q.inters...), + predicates: append([]predicate.BrokerDispatch{}, _q.predicates...), + // clone intermediate query. + sql: _q.sql.Clone(), + path: _q.path, + } +} + +// GroupBy is used to group vertices by one or more fields/columns. +// It is often used with aggregate functions, like: count, max, mean, min, sum. +// +// Example: +// +// var v []struct { +// BrokerID uuid.UUID `json:"broker_id,omitempty"` +// Count int `json:"count,omitempty"` +// } +// +// client.BrokerDispatch.Query(). +// GroupBy(brokerdispatch.FieldBrokerID). +// Aggregate(ent.Count()). +// Scan(ctx, &v) +func (_q *BrokerDispatchQuery) GroupBy(field string, fields ...string) *BrokerDispatchGroupBy { + _q.ctx.Fields = append([]string{field}, fields...) + grbuild := &BrokerDispatchGroupBy{build: _q} + grbuild.flds = &_q.ctx.Fields + grbuild.label = brokerdispatch.Label + grbuild.scan = grbuild.Scan + return grbuild +} + +// Select allows the selection one or more fields/columns for the given query, +// instead of selecting all fields in the entity. +// +// Example: +// +// var v []struct { +// BrokerID uuid.UUID `json:"broker_id,omitempty"` +// } +// +// client.BrokerDispatch.Query(). +// Select(brokerdispatch.FieldBrokerID). +// Scan(ctx, &v) +func (_q *BrokerDispatchQuery) Select(fields ...string) *BrokerDispatchSelect { + _q.ctx.Fields = append(_q.ctx.Fields, fields...) + sbuild := &BrokerDispatchSelect{BrokerDispatchQuery: _q} + sbuild.label = brokerdispatch.Label + sbuild.flds, sbuild.scan = &_q.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a BrokerDispatchSelect configured with the given aggregations. +func (_q *BrokerDispatchQuery) Aggregate(fns ...AggregateFunc) *BrokerDispatchSelect { + return _q.Select().Aggregate(fns...) +} + +func (_q *BrokerDispatchQuery) prepareQuery(ctx context.Context) error { + for _, inter := range _q.inters { + if inter == nil { + return fmt.Errorf("ent: uninitialized interceptor (forgotten import ent/runtime?)") + } + if trv, ok := inter.(Traverser); ok { + if err := trv.Traverse(ctx, _q); err != nil { + return err + } + } + } + for _, f := range _q.ctx.Fields { + if !brokerdispatch.ValidColumn(f) { + return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + } + if _q.path != nil { + prev, err := _q.path(ctx) + if err != nil { + return err + } + _q.sql = prev + } + return nil +} + +func (_q *BrokerDispatchQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*BrokerDispatch, error) { + var ( + nodes = []*BrokerDispatch{} + _spec = _q.querySpec() + ) + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*BrokerDispatch).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &BrokerDispatch{config: _q.config} + nodes = append(nodes, node) + return node.assignValues(columns, values) + } + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } + for i := range hooks { + hooks[i](ctx, _spec) + } + if err := sqlgraph.QueryNodes(ctx, _q.driver, _spec); err != nil { + return nil, err + } + if len(nodes) == 0 { + return nodes, nil + } + return nodes, nil +} + +func (_q *BrokerDispatchQuery) sqlCount(ctx context.Context) (int, error) { + _spec := _q.querySpec() + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } + _spec.Node.Columns = _q.ctx.Fields + if len(_q.ctx.Fields) > 0 { + _spec.Unique = _q.ctx.Unique != nil && *_q.ctx.Unique + } + return sqlgraph.CountNodes(ctx, _q.driver, _spec) +} + +func (_q *BrokerDispatchQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(brokerdispatch.Table, brokerdispatch.Columns, sqlgraph.NewFieldSpec(brokerdispatch.FieldID, field.TypeUUID)) + _spec.From = _q.sql + if unique := _q.ctx.Unique; unique != nil { + _spec.Unique = *unique + } else if _q.path != nil { + _spec.Unique = true + } + if fields := _q.ctx.Fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, brokerdispatch.FieldID) + for i := range fields { + if fields[i] != brokerdispatch.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, fields[i]) + } + } + } + if ps := _q.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if limit := _q.ctx.Limit; limit != nil { + _spec.Limit = *limit + } + if offset := _q.ctx.Offset; offset != nil { + _spec.Offset = *offset + } + if ps := _q.order; len(ps) > 0 { + _spec.Order = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + return _spec +} + +func (_q *BrokerDispatchQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(_q.driver.Dialect()) + t1 := builder.Table(brokerdispatch.Table) + columns := _q.ctx.Fields + if len(columns) == 0 { + columns = brokerdispatch.Columns + } + selector := builder.Select(t1.Columns(columns...)...).From(t1) + if _q.sql != nil { + selector = _q.sql + selector.Select(selector.Columns(columns...)...) + } + if _q.ctx.Unique != nil && *_q.ctx.Unique { + selector.Distinct() + } + for _, m := range _q.modifiers { + m(selector) + } + for _, p := range _q.predicates { + p(selector) + } + for _, p := range _q.order { + p(selector) + } + if offset := _q.ctx.Offset; offset != nil { + // limit is mandatory for offset clause. We start + // with default value, and override it below if needed. + selector.Offset(*offset).Limit(math.MaxInt32) + } + if limit := _q.ctx.Limit; limit != nil { + selector.Limit(*limit) + } + return selector +} + +// ForUpdate locks the selected rows against concurrent updates, and prevent them from being +// updated, deleted or "selected ... for update" by other sessions, until the transaction is +// either committed or rolled-back. +func (_q *BrokerDispatchQuery) ForUpdate(opts ...sql.LockOption) *BrokerDispatchQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForUpdate(opts...) + }) + return _q +} + +// ForShare behaves similarly to ForUpdate, except that it acquires a shared mode lock +// on any rows that are read. Other sessions can read the rows, but cannot modify them +// until your transaction commits. +func (_q *BrokerDispatchQuery) ForShare(opts ...sql.LockOption) *BrokerDispatchQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForShare(opts...) + }) + return _q +} + +// BrokerDispatchGroupBy is the group-by builder for BrokerDispatch entities. +type BrokerDispatchGroupBy struct { + selector + build *BrokerDispatchQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (_g *BrokerDispatchGroupBy) Aggregate(fns ...AggregateFunc) *BrokerDispatchGroupBy { + _g.fns = append(_g.fns, fns...) + return _g +} + +// Scan applies the selector query and scans the result into the given value. +func (_g *BrokerDispatchGroupBy) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, _g.build.ctx, ent.OpQueryGroupBy) + if err := _g.build.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*BrokerDispatchQuery, *BrokerDispatchGroupBy](ctx, _g.build, _g, _g.build.inters, v) +} + +func (_g *BrokerDispatchGroupBy) sqlScan(ctx context.Context, root *BrokerDispatchQuery, v any) error { + selector := root.sqlQuery(ctx).Select() + aggregation := make([]string, 0, len(_g.fns)) + for _, fn := range _g.fns { + aggregation = append(aggregation, fn(selector)) + } + if len(selector.SelectedColumns()) == 0 { + columns := make([]string, 0, len(*_g.flds)+len(_g.fns)) + for _, f := range *_g.flds { + columns = append(columns, selector.C(f)) + } + columns = append(columns, aggregation...) + selector.Select(columns...) + } + selector.GroupBy(selector.Columns(*_g.flds...)...) + if err := selector.Err(); err != nil { + return err + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := _g.build.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} + +// BrokerDispatchSelect is the builder for selecting fields of BrokerDispatch entities. +type BrokerDispatchSelect struct { + *BrokerDispatchQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (_s *BrokerDispatchSelect) Aggregate(fns ...AggregateFunc) *BrokerDispatchSelect { + _s.fns = append(_s.fns, fns...) + return _s +} + +// Scan applies the selector query and scans the result into the given value. +func (_s *BrokerDispatchSelect) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, _s.ctx, ent.OpQuerySelect) + if err := _s.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*BrokerDispatchQuery, *BrokerDispatchSelect](ctx, _s.BrokerDispatchQuery, _s, _s.inters, v) +} + +func (_s *BrokerDispatchSelect) sqlScan(ctx context.Context, root *BrokerDispatchQuery, v any) error { + selector := root.sqlQuery(ctx) + aggregation := make([]string, 0, len(_s.fns)) + for _, fn := range _s.fns { + aggregation = append(aggregation, fn(selector)) + } + switch n := len(*_s.selector.flds); { + case n == 0 && len(aggregation) > 0: + selector.Select(aggregation...) + case n != 0 && len(aggregation) > 0: + selector.AppendSelect(aggregation...) + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := _s.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} diff --git a/pkg/ent/brokerdispatch_update.go b/pkg/ent/brokerdispatch_update.go new file mode 100644 index 000000000..be039862b --- /dev/null +++ b/pkg/ent/brokerdispatch_update.go @@ -0,0 +1,811 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/GoogleCloudPlatform/scion/pkg/ent/brokerdispatch" + "github.com/GoogleCloudPlatform/scion/pkg/ent/predicate" + "github.com/google/uuid" +) + +// BrokerDispatchUpdate is the builder for updating BrokerDispatch entities. +type BrokerDispatchUpdate struct { + config + hooks []Hook + mutation *BrokerDispatchMutation +} + +// Where appends a list predicates to the BrokerDispatchUpdate builder. +func (_u *BrokerDispatchUpdate) Where(ps ...predicate.BrokerDispatch) *BrokerDispatchUpdate { + _u.mutation.Where(ps...) + return _u +} + +// SetBrokerID sets the "broker_id" field. +func (_u *BrokerDispatchUpdate) SetBrokerID(v uuid.UUID) *BrokerDispatchUpdate { + _u.mutation.SetBrokerID(v) + return _u +} + +// SetNillableBrokerID sets the "broker_id" field if the given value is not nil. +func (_u *BrokerDispatchUpdate) SetNillableBrokerID(v *uuid.UUID) *BrokerDispatchUpdate { + if v != nil { + _u.SetBrokerID(*v) + } + return _u +} + +// SetAgentID sets the "agent_id" field. +func (_u *BrokerDispatchUpdate) SetAgentID(v uuid.UUID) *BrokerDispatchUpdate { + _u.mutation.SetAgentID(v) + return _u +} + +// SetNillableAgentID sets the "agent_id" field if the given value is not nil. +func (_u *BrokerDispatchUpdate) SetNillableAgentID(v *uuid.UUID) *BrokerDispatchUpdate { + if v != nil { + _u.SetAgentID(*v) + } + return _u +} + +// ClearAgentID clears the value of the "agent_id" field. +func (_u *BrokerDispatchUpdate) ClearAgentID() *BrokerDispatchUpdate { + _u.mutation.ClearAgentID() + return _u +} + +// SetAgentSlug sets the "agent_slug" field. +func (_u *BrokerDispatchUpdate) SetAgentSlug(v string) *BrokerDispatchUpdate { + _u.mutation.SetAgentSlug(v) + return _u +} + +// SetNillableAgentSlug sets the "agent_slug" field if the given value is not nil. +func (_u *BrokerDispatchUpdate) SetNillableAgentSlug(v *string) *BrokerDispatchUpdate { + if v != nil { + _u.SetAgentSlug(*v) + } + return _u +} + +// ClearAgentSlug clears the value of the "agent_slug" field. +func (_u *BrokerDispatchUpdate) ClearAgentSlug() *BrokerDispatchUpdate { + _u.mutation.ClearAgentSlug() + return _u +} + +// SetProjectID sets the "project_id" field. +func (_u *BrokerDispatchUpdate) SetProjectID(v uuid.UUID) *BrokerDispatchUpdate { + _u.mutation.SetProjectID(v) + return _u +} + +// SetNillableProjectID sets the "project_id" field if the given value is not nil. +func (_u *BrokerDispatchUpdate) SetNillableProjectID(v *uuid.UUID) *BrokerDispatchUpdate { + if v != nil { + _u.SetProjectID(*v) + } + return _u +} + +// ClearProjectID clears the value of the "project_id" field. +func (_u *BrokerDispatchUpdate) ClearProjectID() *BrokerDispatchUpdate { + _u.mutation.ClearProjectID() + return _u +} + +// SetOp sets the "op" field. +func (_u *BrokerDispatchUpdate) SetOp(v string) *BrokerDispatchUpdate { + _u.mutation.SetOpField(v) + return _u +} + +// SetNillableOp sets the "op" field if the given value is not nil. +func (_u *BrokerDispatchUpdate) SetNillableOp(v *string) *BrokerDispatchUpdate { + if v != nil { + _u.SetOp(*v) + } + return _u +} + +// SetArgs sets the "args" field. +func (_u *BrokerDispatchUpdate) SetArgs(v string) *BrokerDispatchUpdate { + _u.mutation.SetArgs(v) + return _u +} + +// SetNillableArgs sets the "args" field if the given value is not nil. +func (_u *BrokerDispatchUpdate) SetNillableArgs(v *string) *BrokerDispatchUpdate { + if v != nil { + _u.SetArgs(*v) + } + return _u +} + +// ClearArgs clears the value of the "args" field. +func (_u *BrokerDispatchUpdate) ClearArgs() *BrokerDispatchUpdate { + _u.mutation.ClearArgs() + return _u +} + +// SetState sets the "state" field. +func (_u *BrokerDispatchUpdate) SetState(v string) *BrokerDispatchUpdate { + _u.mutation.SetState(v) + return _u +} + +// SetNillableState sets the "state" field if the given value is not nil. +func (_u *BrokerDispatchUpdate) SetNillableState(v *string) *BrokerDispatchUpdate { + if v != nil { + _u.SetState(*v) + } + return _u +} + +// SetResult sets the "result" field. +func (_u *BrokerDispatchUpdate) SetResult(v string) *BrokerDispatchUpdate { + _u.mutation.SetResult(v) + return _u +} + +// SetNillableResult sets the "result" field if the given value is not nil. +func (_u *BrokerDispatchUpdate) SetNillableResult(v *string) *BrokerDispatchUpdate { + if v != nil { + _u.SetResult(*v) + } + return _u +} + +// ClearResult clears the value of the "result" field. +func (_u *BrokerDispatchUpdate) ClearResult() *BrokerDispatchUpdate { + _u.mutation.ClearResult() + return _u +} + +// SetClaimedBy sets the "claimed_by" field. +func (_u *BrokerDispatchUpdate) SetClaimedBy(v string) *BrokerDispatchUpdate { + _u.mutation.SetClaimedBy(v) + return _u +} + +// SetNillableClaimedBy sets the "claimed_by" field if the given value is not nil. +func (_u *BrokerDispatchUpdate) SetNillableClaimedBy(v *string) *BrokerDispatchUpdate { + if v != nil { + _u.SetClaimedBy(*v) + } + return _u +} + +// ClearClaimedBy clears the value of the "claimed_by" field. +func (_u *BrokerDispatchUpdate) ClearClaimedBy() *BrokerDispatchUpdate { + _u.mutation.ClearClaimedBy() + return _u +} + +// SetAttempts sets the "attempts" field. +func (_u *BrokerDispatchUpdate) SetAttempts(v int) *BrokerDispatchUpdate { + _u.mutation.ResetAttempts() + _u.mutation.SetAttempts(v) + return _u +} + +// SetNillableAttempts sets the "attempts" field if the given value is not nil. +func (_u *BrokerDispatchUpdate) SetNillableAttempts(v *int) *BrokerDispatchUpdate { + if v != nil { + _u.SetAttempts(*v) + } + return _u +} + +// AddAttempts adds value to the "attempts" field. +func (_u *BrokerDispatchUpdate) AddAttempts(v int) *BrokerDispatchUpdate { + _u.mutation.AddAttempts(v) + return _u +} + +// SetError sets the "error" field. +func (_u *BrokerDispatchUpdate) SetError(v string) *BrokerDispatchUpdate { + _u.mutation.SetError(v) + return _u +} + +// SetNillableError sets the "error" field if the given value is not nil. +func (_u *BrokerDispatchUpdate) SetNillableError(v *string) *BrokerDispatchUpdate { + if v != nil { + _u.SetError(*v) + } + return _u +} + +// ClearError clears the value of the "error" field. +func (_u *BrokerDispatchUpdate) ClearError() *BrokerDispatchUpdate { + _u.mutation.ClearError() + return _u +} + +// SetUpdatedAt sets the "updated_at" field. +func (_u *BrokerDispatchUpdate) SetUpdatedAt(v time.Time) *BrokerDispatchUpdate { + _u.mutation.SetUpdatedAt(v) + return _u +} + +// SetDeadlineAt sets the "deadline_at" field. +func (_u *BrokerDispatchUpdate) SetDeadlineAt(v time.Time) *BrokerDispatchUpdate { + _u.mutation.SetDeadlineAt(v) + return _u +} + +// SetNillableDeadlineAt sets the "deadline_at" field if the given value is not nil. +func (_u *BrokerDispatchUpdate) SetNillableDeadlineAt(v *time.Time) *BrokerDispatchUpdate { + if v != nil { + _u.SetDeadlineAt(*v) + } + return _u +} + +// ClearDeadlineAt clears the value of the "deadline_at" field. +func (_u *BrokerDispatchUpdate) ClearDeadlineAt() *BrokerDispatchUpdate { + _u.mutation.ClearDeadlineAt() + return _u +} + +// Mutation returns the BrokerDispatchMutation object of the builder. +func (_u *BrokerDispatchUpdate) Mutation() *BrokerDispatchMutation { + return _u.mutation +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (_u *BrokerDispatchUpdate) Save(ctx context.Context) (int, error) { + _u.defaults() + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *BrokerDispatchUpdate) SaveX(ctx context.Context) int { + affected, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (_u *BrokerDispatchUpdate) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *BrokerDispatchUpdate) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_u *BrokerDispatchUpdate) defaults() { + if _, ok := _u.mutation.UpdatedAt(); !ok { + v := brokerdispatch.UpdateDefaultUpdatedAt() + _u.mutation.SetUpdatedAt(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_u *BrokerDispatchUpdate) check() error { + if v, ok := _u.mutation.GetOp(); ok { + if err := brokerdispatch.OpValidator(v); err != nil { + return &ValidationError{Name: "op", err: fmt.Errorf(`ent: validator failed for field "BrokerDispatch.op": %w`, err)} + } + } + return nil +} + +func (_u *BrokerDispatchUpdate) sqlSave(ctx context.Context) (_node int, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(brokerdispatch.Table, brokerdispatch.Columns, sqlgraph.NewFieldSpec(brokerdispatch.FieldID, field.TypeUUID)) + if ps := _u.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := _u.mutation.BrokerID(); ok { + _spec.SetField(brokerdispatch.FieldBrokerID, field.TypeUUID, value) + } + if value, ok := _u.mutation.AgentID(); ok { + _spec.SetField(brokerdispatch.FieldAgentID, field.TypeUUID, value) + } + if _u.mutation.AgentIDCleared() { + _spec.ClearField(brokerdispatch.FieldAgentID, field.TypeUUID) + } + if value, ok := _u.mutation.AgentSlug(); ok { + _spec.SetField(brokerdispatch.FieldAgentSlug, field.TypeString, value) + } + if _u.mutation.AgentSlugCleared() { + _spec.ClearField(brokerdispatch.FieldAgentSlug, field.TypeString) + } + if value, ok := _u.mutation.ProjectID(); ok { + _spec.SetField(brokerdispatch.FieldProjectID, field.TypeUUID, value) + } + if _u.mutation.ProjectIDCleared() { + _spec.ClearField(brokerdispatch.FieldProjectID, field.TypeUUID) + } + if value, ok := _u.mutation.GetOp(); ok { + _spec.SetField(brokerdispatch.FieldOp, field.TypeString, value) + } + if value, ok := _u.mutation.Args(); ok { + _spec.SetField(brokerdispatch.FieldArgs, field.TypeString, value) + } + if _u.mutation.ArgsCleared() { + _spec.ClearField(brokerdispatch.FieldArgs, field.TypeString) + } + if value, ok := _u.mutation.State(); ok { + _spec.SetField(brokerdispatch.FieldState, field.TypeString, value) + } + if value, ok := _u.mutation.Result(); ok { + _spec.SetField(brokerdispatch.FieldResult, field.TypeString, value) + } + if _u.mutation.ResultCleared() { + _spec.ClearField(brokerdispatch.FieldResult, field.TypeString) + } + if value, ok := _u.mutation.ClaimedBy(); ok { + _spec.SetField(brokerdispatch.FieldClaimedBy, field.TypeString, value) + } + if _u.mutation.ClaimedByCleared() { + _spec.ClearField(brokerdispatch.FieldClaimedBy, field.TypeString) + } + if value, ok := _u.mutation.Attempts(); ok { + _spec.SetField(brokerdispatch.FieldAttempts, field.TypeInt, value) + } + if value, ok := _u.mutation.AddedAttempts(); ok { + _spec.AddField(brokerdispatch.FieldAttempts, field.TypeInt, value) + } + if value, ok := _u.mutation.Error(); ok { + _spec.SetField(brokerdispatch.FieldError, field.TypeString, value) + } + if _u.mutation.ErrorCleared() { + _spec.ClearField(brokerdispatch.FieldError, field.TypeString) + } + if value, ok := _u.mutation.UpdatedAt(); ok { + _spec.SetField(brokerdispatch.FieldUpdatedAt, field.TypeTime, value) + } + if value, ok := _u.mutation.DeadlineAt(); ok { + _spec.SetField(brokerdispatch.FieldDeadlineAt, field.TypeTime, value) + } + if _u.mutation.DeadlineAtCleared() { + _spec.ClearField(brokerdispatch.FieldDeadlineAt, field.TypeTime) + } + if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{brokerdispatch.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + _u.mutation.done = true + return _node, nil +} + +// BrokerDispatchUpdateOne is the builder for updating a single BrokerDispatch entity. +type BrokerDispatchUpdateOne struct { + config + fields []string + hooks []Hook + mutation *BrokerDispatchMutation +} + +// SetBrokerID sets the "broker_id" field. +func (_u *BrokerDispatchUpdateOne) SetBrokerID(v uuid.UUID) *BrokerDispatchUpdateOne { + _u.mutation.SetBrokerID(v) + return _u +} + +// SetNillableBrokerID sets the "broker_id" field if the given value is not nil. +func (_u *BrokerDispatchUpdateOne) SetNillableBrokerID(v *uuid.UUID) *BrokerDispatchUpdateOne { + if v != nil { + _u.SetBrokerID(*v) + } + return _u +} + +// SetAgentID sets the "agent_id" field. +func (_u *BrokerDispatchUpdateOne) SetAgentID(v uuid.UUID) *BrokerDispatchUpdateOne { + _u.mutation.SetAgentID(v) + return _u +} + +// SetNillableAgentID sets the "agent_id" field if the given value is not nil. +func (_u *BrokerDispatchUpdateOne) SetNillableAgentID(v *uuid.UUID) *BrokerDispatchUpdateOne { + if v != nil { + _u.SetAgentID(*v) + } + return _u +} + +// ClearAgentID clears the value of the "agent_id" field. +func (_u *BrokerDispatchUpdateOne) ClearAgentID() *BrokerDispatchUpdateOne { + _u.mutation.ClearAgentID() + return _u +} + +// SetAgentSlug sets the "agent_slug" field. +func (_u *BrokerDispatchUpdateOne) SetAgentSlug(v string) *BrokerDispatchUpdateOne { + _u.mutation.SetAgentSlug(v) + return _u +} + +// SetNillableAgentSlug sets the "agent_slug" field if the given value is not nil. +func (_u *BrokerDispatchUpdateOne) SetNillableAgentSlug(v *string) *BrokerDispatchUpdateOne { + if v != nil { + _u.SetAgentSlug(*v) + } + return _u +} + +// ClearAgentSlug clears the value of the "agent_slug" field. +func (_u *BrokerDispatchUpdateOne) ClearAgentSlug() *BrokerDispatchUpdateOne { + _u.mutation.ClearAgentSlug() + return _u +} + +// SetProjectID sets the "project_id" field. +func (_u *BrokerDispatchUpdateOne) SetProjectID(v uuid.UUID) *BrokerDispatchUpdateOne { + _u.mutation.SetProjectID(v) + return _u +} + +// SetNillableProjectID sets the "project_id" field if the given value is not nil. +func (_u *BrokerDispatchUpdateOne) SetNillableProjectID(v *uuid.UUID) *BrokerDispatchUpdateOne { + if v != nil { + _u.SetProjectID(*v) + } + return _u +} + +// ClearProjectID clears the value of the "project_id" field. +func (_u *BrokerDispatchUpdateOne) ClearProjectID() *BrokerDispatchUpdateOne { + _u.mutation.ClearProjectID() + return _u +} + +// SetOp sets the "op" field. +func (_u *BrokerDispatchUpdateOne) SetOp(v string) *BrokerDispatchUpdateOne { + _u.mutation.SetOpField(v) + return _u +} + +// SetNillableOp sets the "op" field if the given value is not nil. +func (_u *BrokerDispatchUpdateOne) SetNillableOp(v *string) *BrokerDispatchUpdateOne { + if v != nil { + _u.SetOp(*v) + } + return _u +} + +// SetArgs sets the "args" field. +func (_u *BrokerDispatchUpdateOne) SetArgs(v string) *BrokerDispatchUpdateOne { + _u.mutation.SetArgs(v) + return _u +} + +// SetNillableArgs sets the "args" field if the given value is not nil. +func (_u *BrokerDispatchUpdateOne) SetNillableArgs(v *string) *BrokerDispatchUpdateOne { + if v != nil { + _u.SetArgs(*v) + } + return _u +} + +// ClearArgs clears the value of the "args" field. +func (_u *BrokerDispatchUpdateOne) ClearArgs() *BrokerDispatchUpdateOne { + _u.mutation.ClearArgs() + return _u +} + +// SetState sets the "state" field. +func (_u *BrokerDispatchUpdateOne) SetState(v string) *BrokerDispatchUpdateOne { + _u.mutation.SetState(v) + return _u +} + +// SetNillableState sets the "state" field if the given value is not nil. +func (_u *BrokerDispatchUpdateOne) SetNillableState(v *string) *BrokerDispatchUpdateOne { + if v != nil { + _u.SetState(*v) + } + return _u +} + +// SetResult sets the "result" field. +func (_u *BrokerDispatchUpdateOne) SetResult(v string) *BrokerDispatchUpdateOne { + _u.mutation.SetResult(v) + return _u +} + +// SetNillableResult sets the "result" field if the given value is not nil. +func (_u *BrokerDispatchUpdateOne) SetNillableResult(v *string) *BrokerDispatchUpdateOne { + if v != nil { + _u.SetResult(*v) + } + return _u +} + +// ClearResult clears the value of the "result" field. +func (_u *BrokerDispatchUpdateOne) ClearResult() *BrokerDispatchUpdateOne { + _u.mutation.ClearResult() + return _u +} + +// SetClaimedBy sets the "claimed_by" field. +func (_u *BrokerDispatchUpdateOne) SetClaimedBy(v string) *BrokerDispatchUpdateOne { + _u.mutation.SetClaimedBy(v) + return _u +} + +// SetNillableClaimedBy sets the "claimed_by" field if the given value is not nil. +func (_u *BrokerDispatchUpdateOne) SetNillableClaimedBy(v *string) *BrokerDispatchUpdateOne { + if v != nil { + _u.SetClaimedBy(*v) + } + return _u +} + +// ClearClaimedBy clears the value of the "claimed_by" field. +func (_u *BrokerDispatchUpdateOne) ClearClaimedBy() *BrokerDispatchUpdateOne { + _u.mutation.ClearClaimedBy() + return _u +} + +// SetAttempts sets the "attempts" field. +func (_u *BrokerDispatchUpdateOne) SetAttempts(v int) *BrokerDispatchUpdateOne { + _u.mutation.ResetAttempts() + _u.mutation.SetAttempts(v) + return _u +} + +// SetNillableAttempts sets the "attempts" field if the given value is not nil. +func (_u *BrokerDispatchUpdateOne) SetNillableAttempts(v *int) *BrokerDispatchUpdateOne { + if v != nil { + _u.SetAttempts(*v) + } + return _u +} + +// AddAttempts adds value to the "attempts" field. +func (_u *BrokerDispatchUpdateOne) AddAttempts(v int) *BrokerDispatchUpdateOne { + _u.mutation.AddAttempts(v) + return _u +} + +// SetError sets the "error" field. +func (_u *BrokerDispatchUpdateOne) SetError(v string) *BrokerDispatchUpdateOne { + _u.mutation.SetError(v) + return _u +} + +// SetNillableError sets the "error" field if the given value is not nil. +func (_u *BrokerDispatchUpdateOne) SetNillableError(v *string) *BrokerDispatchUpdateOne { + if v != nil { + _u.SetError(*v) + } + return _u +} + +// ClearError clears the value of the "error" field. +func (_u *BrokerDispatchUpdateOne) ClearError() *BrokerDispatchUpdateOne { + _u.mutation.ClearError() + return _u +} + +// SetUpdatedAt sets the "updated_at" field. +func (_u *BrokerDispatchUpdateOne) SetUpdatedAt(v time.Time) *BrokerDispatchUpdateOne { + _u.mutation.SetUpdatedAt(v) + return _u +} + +// SetDeadlineAt sets the "deadline_at" field. +func (_u *BrokerDispatchUpdateOne) SetDeadlineAt(v time.Time) *BrokerDispatchUpdateOne { + _u.mutation.SetDeadlineAt(v) + return _u +} + +// SetNillableDeadlineAt sets the "deadline_at" field if the given value is not nil. +func (_u *BrokerDispatchUpdateOne) SetNillableDeadlineAt(v *time.Time) *BrokerDispatchUpdateOne { + if v != nil { + _u.SetDeadlineAt(*v) + } + return _u +} + +// ClearDeadlineAt clears the value of the "deadline_at" field. +func (_u *BrokerDispatchUpdateOne) ClearDeadlineAt() *BrokerDispatchUpdateOne { + _u.mutation.ClearDeadlineAt() + return _u +} + +// Mutation returns the BrokerDispatchMutation object of the builder. +func (_u *BrokerDispatchUpdateOne) Mutation() *BrokerDispatchMutation { + return _u.mutation +} + +// Where appends a list predicates to the BrokerDispatchUpdate builder. +func (_u *BrokerDispatchUpdateOne) Where(ps ...predicate.BrokerDispatch) *BrokerDispatchUpdateOne { + _u.mutation.Where(ps...) + return _u +} + +// Select allows selecting one or more fields (columns) of the returned entity. +// The default is selecting all fields defined in the entity schema. +func (_u *BrokerDispatchUpdateOne) Select(field string, fields ...string) *BrokerDispatchUpdateOne { + _u.fields = append([]string{field}, fields...) + return _u +} + +// Save executes the query and returns the updated BrokerDispatch entity. +func (_u *BrokerDispatchUpdateOne) Save(ctx context.Context) (*BrokerDispatch, error) { + _u.defaults() + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *BrokerDispatchUpdateOne) SaveX(ctx context.Context) *BrokerDispatch { + node, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (_u *BrokerDispatchUpdateOne) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *BrokerDispatchUpdateOne) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_u *BrokerDispatchUpdateOne) defaults() { + if _, ok := _u.mutation.UpdatedAt(); !ok { + v := brokerdispatch.UpdateDefaultUpdatedAt() + _u.mutation.SetUpdatedAt(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_u *BrokerDispatchUpdateOne) check() error { + if v, ok := _u.mutation.GetOp(); ok { + if err := brokerdispatch.OpValidator(v); err != nil { + return &ValidationError{Name: "op", err: fmt.Errorf(`ent: validator failed for field "BrokerDispatch.op": %w`, err)} + } + } + return nil +} + +func (_u *BrokerDispatchUpdateOne) sqlSave(ctx context.Context) (_node *BrokerDispatch, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(brokerdispatch.Table, brokerdispatch.Columns, sqlgraph.NewFieldSpec(brokerdispatch.FieldID, field.TypeUUID)) + id, ok := _u.mutation.ID() + if !ok { + return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "BrokerDispatch.id" for update`)} + } + _spec.Node.ID.Value = id + if fields := _u.fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, brokerdispatch.FieldID) + for _, f := range fields { + if !brokerdispatch.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + if f != brokerdispatch.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, f) + } + } + } + if ps := _u.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := _u.mutation.BrokerID(); ok { + _spec.SetField(brokerdispatch.FieldBrokerID, field.TypeUUID, value) + } + if value, ok := _u.mutation.AgentID(); ok { + _spec.SetField(brokerdispatch.FieldAgentID, field.TypeUUID, value) + } + if _u.mutation.AgentIDCleared() { + _spec.ClearField(brokerdispatch.FieldAgentID, field.TypeUUID) + } + if value, ok := _u.mutation.AgentSlug(); ok { + _spec.SetField(brokerdispatch.FieldAgentSlug, field.TypeString, value) + } + if _u.mutation.AgentSlugCleared() { + _spec.ClearField(brokerdispatch.FieldAgentSlug, field.TypeString) + } + if value, ok := _u.mutation.ProjectID(); ok { + _spec.SetField(brokerdispatch.FieldProjectID, field.TypeUUID, value) + } + if _u.mutation.ProjectIDCleared() { + _spec.ClearField(brokerdispatch.FieldProjectID, field.TypeUUID) + } + if value, ok := _u.mutation.GetOp(); ok { + _spec.SetField(brokerdispatch.FieldOp, field.TypeString, value) + } + if value, ok := _u.mutation.Args(); ok { + _spec.SetField(brokerdispatch.FieldArgs, field.TypeString, value) + } + if _u.mutation.ArgsCleared() { + _spec.ClearField(brokerdispatch.FieldArgs, field.TypeString) + } + if value, ok := _u.mutation.State(); ok { + _spec.SetField(brokerdispatch.FieldState, field.TypeString, value) + } + if value, ok := _u.mutation.Result(); ok { + _spec.SetField(brokerdispatch.FieldResult, field.TypeString, value) + } + if _u.mutation.ResultCleared() { + _spec.ClearField(brokerdispatch.FieldResult, field.TypeString) + } + if value, ok := _u.mutation.ClaimedBy(); ok { + _spec.SetField(brokerdispatch.FieldClaimedBy, field.TypeString, value) + } + if _u.mutation.ClaimedByCleared() { + _spec.ClearField(brokerdispatch.FieldClaimedBy, field.TypeString) + } + if value, ok := _u.mutation.Attempts(); ok { + _spec.SetField(brokerdispatch.FieldAttempts, field.TypeInt, value) + } + if value, ok := _u.mutation.AddedAttempts(); ok { + _spec.AddField(brokerdispatch.FieldAttempts, field.TypeInt, value) + } + if value, ok := _u.mutation.Error(); ok { + _spec.SetField(brokerdispatch.FieldError, field.TypeString, value) + } + if _u.mutation.ErrorCleared() { + _spec.ClearField(brokerdispatch.FieldError, field.TypeString) + } + if value, ok := _u.mutation.UpdatedAt(); ok { + _spec.SetField(brokerdispatch.FieldUpdatedAt, field.TypeTime, value) + } + if value, ok := _u.mutation.DeadlineAt(); ok { + _spec.SetField(brokerdispatch.FieldDeadlineAt, field.TypeTime, value) + } + if _u.mutation.DeadlineAtCleared() { + _spec.ClearField(brokerdispatch.FieldDeadlineAt, field.TypeTime) + } + _node = &BrokerDispatch{config: _u.config} + _spec.Assign = _node.assignValues + _spec.ScanValues = _node.scanValues + if err = sqlgraph.UpdateNode(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{brokerdispatch.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + _u.mutation.done = true + return _node, nil +} diff --git a/pkg/ent/client.go b/pkg/ent/client.go index 8b87bf6a9..13fbfb24b 100644 --- a/pkg/ent/client.go +++ b/pkg/ent/client.go @@ -20,6 +20,7 @@ import ( "github.com/GoogleCloudPlatform/scion/pkg/ent/agent" "github.com/GoogleCloudPlatform/scion/pkg/ent/allowlistentry" "github.com/GoogleCloudPlatform/scion/pkg/ent/apikey" + "github.com/GoogleCloudPlatform/scion/pkg/ent/brokerdispatch" "github.com/GoogleCloudPlatform/scion/pkg/ent/brokerjointoken" "github.com/GoogleCloudPlatform/scion/pkg/ent/brokersecret" "github.com/GoogleCloudPlatform/scion/pkg/ent/envvar" @@ -61,6 +62,8 @@ type Client struct { AllowListEntry *AllowListEntryClient // ApiKey is the client for interacting with the ApiKey builders. ApiKey *ApiKeyClient + // BrokerDispatch is the client for interacting with the BrokerDispatch builders. + BrokerDispatch *BrokerDispatchClient // BrokerJoinToken is the client for interacting with the BrokerJoinToken builders. BrokerJoinToken *BrokerJoinTokenClient // BrokerSecret is the client for interacting with the BrokerSecret builders. @@ -128,6 +131,7 @@ func (c *Client) init() { c.Agent = NewAgentClient(c.config) c.AllowListEntry = NewAllowListEntryClient(c.config) c.ApiKey = NewApiKeyClient(c.config) + c.BrokerDispatch = NewBrokerDispatchClient(c.config) c.BrokerJoinToken = NewBrokerJoinTokenClient(c.config) c.BrokerSecret = NewBrokerSecretClient(c.config) c.EnvVar = NewEnvVarClient(c.config) @@ -250,6 +254,7 @@ func (c *Client) Tx(ctx context.Context) (*Tx, error) { Agent: NewAgentClient(cfg), AllowListEntry: NewAllowListEntryClient(cfg), ApiKey: NewApiKeyClient(cfg), + BrokerDispatch: NewBrokerDispatchClient(cfg), BrokerJoinToken: NewBrokerJoinTokenClient(cfg), BrokerSecret: NewBrokerSecretClient(cfg), EnvVar: NewEnvVarClient(cfg), @@ -299,6 +304,7 @@ func (c *Client) BeginTx(ctx context.Context, opts *sql.TxOptions) (*Tx, error) Agent: NewAgentClient(cfg), AllowListEntry: NewAllowListEntryClient(cfg), ApiKey: NewApiKeyClient(cfg), + BrokerDispatch: NewBrokerDispatchClient(cfg), BrokerJoinToken: NewBrokerJoinTokenClient(cfg), BrokerSecret: NewBrokerSecretClient(cfg), EnvVar: NewEnvVarClient(cfg), @@ -354,13 +360,14 @@ func (c *Client) Close() error { // In order to add hooks to a specific client, call: `client.Node.Use(...)`. func (c *Client) Use(hooks ...Hook) { for _, n := range []interface{ Use(...Hook) }{ - c.AccessPolicy, c.Agent, c.AllowListEntry, c.ApiKey, c.BrokerJoinToken, - c.BrokerSecret, c.EnvVar, c.GCPServiceAccount, c.GithubInstallation, c.Group, - c.GroupMembership, c.HarnessConfig, c.InviteCode, c.MaintenanceOperation, - c.MaintenanceOperationRun, c.Message, c.Notification, - c.NotificationSubscription, c.PolicyBinding, c.Project, c.ProjectContributor, - c.ProjectSyncState, c.RuntimeBroker, c.Schedule, c.ScheduledEvent, c.Secret, - c.SubscriptionTemplate, c.Template, c.User, c.UserAccessToken, + c.AccessPolicy, c.Agent, c.AllowListEntry, c.ApiKey, c.BrokerDispatch, + c.BrokerJoinToken, c.BrokerSecret, c.EnvVar, c.GCPServiceAccount, + c.GithubInstallation, c.Group, c.GroupMembership, c.HarnessConfig, + c.InviteCode, c.MaintenanceOperation, c.MaintenanceOperationRun, c.Message, + c.Notification, c.NotificationSubscription, c.PolicyBinding, c.Project, + c.ProjectContributor, c.ProjectSyncState, c.RuntimeBroker, c.Schedule, + c.ScheduledEvent, c.Secret, c.SubscriptionTemplate, c.Template, c.User, + c.UserAccessToken, } { n.Use(hooks...) } @@ -370,13 +377,14 @@ func (c *Client) Use(hooks ...Hook) { // In order to add interceptors to a specific client, call: `client.Node.Intercept(...)`. func (c *Client) Intercept(interceptors ...Interceptor) { for _, n := range []interface{ Intercept(...Interceptor) }{ - c.AccessPolicy, c.Agent, c.AllowListEntry, c.ApiKey, c.BrokerJoinToken, - c.BrokerSecret, c.EnvVar, c.GCPServiceAccount, c.GithubInstallation, c.Group, - c.GroupMembership, c.HarnessConfig, c.InviteCode, c.MaintenanceOperation, - c.MaintenanceOperationRun, c.Message, c.Notification, - c.NotificationSubscription, c.PolicyBinding, c.Project, c.ProjectContributor, - c.ProjectSyncState, c.RuntimeBroker, c.Schedule, c.ScheduledEvent, c.Secret, - c.SubscriptionTemplate, c.Template, c.User, c.UserAccessToken, + c.AccessPolicy, c.Agent, c.AllowListEntry, c.ApiKey, c.BrokerDispatch, + c.BrokerJoinToken, c.BrokerSecret, c.EnvVar, c.GCPServiceAccount, + c.GithubInstallation, c.Group, c.GroupMembership, c.HarnessConfig, + c.InviteCode, c.MaintenanceOperation, c.MaintenanceOperationRun, c.Message, + c.Notification, c.NotificationSubscription, c.PolicyBinding, c.Project, + c.ProjectContributor, c.ProjectSyncState, c.RuntimeBroker, c.Schedule, + c.ScheduledEvent, c.Secret, c.SubscriptionTemplate, c.Template, c.User, + c.UserAccessToken, } { n.Intercept(interceptors...) } @@ -393,6 +401,8 @@ func (c *Client) Mutate(ctx context.Context, m Mutation) (Value, error) { return c.AllowListEntry.mutate(ctx, m) case *ApiKeyMutation: return c.ApiKey.mutate(ctx, m) + case *BrokerDispatchMutation: + return c.BrokerDispatch.mutate(ctx, m) case *BrokerJoinTokenMutation: return c.BrokerJoinToken.mutate(ctx, m) case *BrokerSecretMutation: @@ -1078,6 +1088,139 @@ func (c *ApiKeyClient) mutate(ctx context.Context, m *ApiKeyMutation) (Value, er } } +// BrokerDispatchClient is a client for the BrokerDispatch schema. +type BrokerDispatchClient struct { + config +} + +// NewBrokerDispatchClient returns a client for the BrokerDispatch from the given config. +func NewBrokerDispatchClient(c config) *BrokerDispatchClient { + return &BrokerDispatchClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `brokerdispatch.Hooks(f(g(h())))`. +func (c *BrokerDispatchClient) Use(hooks ...Hook) { + c.hooks.BrokerDispatch = append(c.hooks.BrokerDispatch, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `brokerdispatch.Intercept(f(g(h())))`. +func (c *BrokerDispatchClient) Intercept(interceptors ...Interceptor) { + c.inters.BrokerDispatch = append(c.inters.BrokerDispatch, interceptors...) +} + +// Create returns a builder for creating a BrokerDispatch entity. +func (c *BrokerDispatchClient) Create() *BrokerDispatchCreate { + mutation := newBrokerDispatchMutation(c.config, OpCreate) + return &BrokerDispatchCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of BrokerDispatch entities. +func (c *BrokerDispatchClient) CreateBulk(builders ...*BrokerDispatchCreate) *BrokerDispatchCreateBulk { + return &BrokerDispatchCreateBulk{config: c.config, builders: builders} +} + +// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates +// a builder and applies setFunc on it. +func (c *BrokerDispatchClient) MapCreateBulk(slice any, setFunc func(*BrokerDispatchCreate, int)) *BrokerDispatchCreateBulk { + rv := reflect.ValueOf(slice) + if rv.Kind() != reflect.Slice { + return &BrokerDispatchCreateBulk{err: fmt.Errorf("calling to BrokerDispatchClient.MapCreateBulk with wrong type %T, need slice", slice)} + } + builders := make([]*BrokerDispatchCreate, rv.Len()) + for i := 0; i < rv.Len(); i++ { + builders[i] = c.Create() + setFunc(builders[i], i) + } + return &BrokerDispatchCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for BrokerDispatch. +func (c *BrokerDispatchClient) Update() *BrokerDispatchUpdate { + mutation := newBrokerDispatchMutation(c.config, OpUpdate) + return &BrokerDispatchUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *BrokerDispatchClient) UpdateOne(_m *BrokerDispatch) *BrokerDispatchUpdateOne { + mutation := newBrokerDispatchMutation(c.config, OpUpdateOne, withBrokerDispatch(_m)) + return &BrokerDispatchUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOneID returns an update builder for the given id. +func (c *BrokerDispatchClient) UpdateOneID(id uuid.UUID) *BrokerDispatchUpdateOne { + mutation := newBrokerDispatchMutation(c.config, OpUpdateOne, withBrokerDispatchID(id)) + return &BrokerDispatchUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for BrokerDispatch. +func (c *BrokerDispatchClient) Delete() *BrokerDispatchDelete { + mutation := newBrokerDispatchMutation(c.config, OpDelete) + return &BrokerDispatchDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// DeleteOne returns a builder for deleting the given entity. +func (c *BrokerDispatchClient) DeleteOne(_m *BrokerDispatch) *BrokerDispatchDeleteOne { + return c.DeleteOneID(_m.ID) +} + +// DeleteOneID returns a builder for deleting the given entity by its id. +func (c *BrokerDispatchClient) DeleteOneID(id uuid.UUID) *BrokerDispatchDeleteOne { + builder := c.Delete().Where(brokerdispatch.ID(id)) + builder.mutation.id = &id + builder.mutation.op = OpDeleteOne + return &BrokerDispatchDeleteOne{builder} +} + +// Query returns a query builder for BrokerDispatch. +func (c *BrokerDispatchClient) Query() *BrokerDispatchQuery { + return &BrokerDispatchQuery{ + config: c.config, + ctx: &QueryContext{Type: TypeBrokerDispatch}, + inters: c.Interceptors(), + } +} + +// Get returns a BrokerDispatch entity by its id. +func (c *BrokerDispatchClient) Get(ctx context.Context, id uuid.UUID) (*BrokerDispatch, error) { + return c.Query().Where(brokerdispatch.ID(id)).Only(ctx) +} + +// GetX is like Get, but panics if an error occurs. +func (c *BrokerDispatchClient) GetX(ctx context.Context, id uuid.UUID) *BrokerDispatch { + obj, err := c.Get(ctx, id) + if err != nil { + panic(err) + } + return obj +} + +// Hooks returns the client hooks. +func (c *BrokerDispatchClient) Hooks() []Hook { + return c.hooks.BrokerDispatch +} + +// Interceptors returns the client interceptors. +func (c *BrokerDispatchClient) Interceptors() []Interceptor { + return c.inters.BrokerDispatch +} + +func (c *BrokerDispatchClient) mutate(ctx context.Context, m *BrokerDispatchMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&BrokerDispatchCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&BrokerDispatchUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&BrokerDispatchUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&BrokerDispatchDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("ent: unknown BrokerDispatch mutation op: %q", m.Op()) + } +} + // BrokerJoinTokenClient is a client for the BrokerJoinToken schema. type BrokerJoinTokenClient struct { config @@ -4827,19 +4970,21 @@ func (c *UserAccessTokenClient) mutate(ctx context.Context, m *UserAccessTokenMu // hooks and interceptors per client, for fast access. type ( hooks struct { - AccessPolicy, Agent, AllowListEntry, ApiKey, BrokerJoinToken, BrokerSecret, - EnvVar, GCPServiceAccount, GithubInstallation, Group, GroupMembership, - HarnessConfig, InviteCode, MaintenanceOperation, MaintenanceOperationRun, - Message, Notification, NotificationSubscription, PolicyBinding, Project, - ProjectContributor, ProjectSyncState, RuntimeBroker, Schedule, ScheduledEvent, - Secret, SubscriptionTemplate, Template, User, UserAccessToken []ent.Hook + AccessPolicy, Agent, AllowListEntry, ApiKey, BrokerDispatch, BrokerJoinToken, + BrokerSecret, EnvVar, GCPServiceAccount, GithubInstallation, Group, + GroupMembership, HarnessConfig, InviteCode, MaintenanceOperation, + MaintenanceOperationRun, Message, Notification, NotificationSubscription, + PolicyBinding, Project, ProjectContributor, ProjectSyncState, RuntimeBroker, + Schedule, ScheduledEvent, Secret, SubscriptionTemplate, Template, User, + UserAccessToken []ent.Hook } inters struct { - AccessPolicy, Agent, AllowListEntry, ApiKey, BrokerJoinToken, BrokerSecret, - EnvVar, GCPServiceAccount, GithubInstallation, Group, GroupMembership, - HarnessConfig, InviteCode, MaintenanceOperation, MaintenanceOperationRun, - Message, Notification, NotificationSubscription, PolicyBinding, Project, - ProjectContributor, ProjectSyncState, RuntimeBroker, Schedule, ScheduledEvent, - Secret, SubscriptionTemplate, Template, User, UserAccessToken []ent.Interceptor + AccessPolicy, Agent, AllowListEntry, ApiKey, BrokerDispatch, BrokerJoinToken, + BrokerSecret, EnvVar, GCPServiceAccount, GithubInstallation, Group, + GroupMembership, HarnessConfig, InviteCode, MaintenanceOperation, + MaintenanceOperationRun, Message, Notification, NotificationSubscription, + PolicyBinding, Project, ProjectContributor, ProjectSyncState, RuntimeBroker, + Schedule, ScheduledEvent, Secret, SubscriptionTemplate, Template, User, + UserAccessToken []ent.Interceptor } ) diff --git a/pkg/ent/ent.go b/pkg/ent/ent.go index 2b9315510..3bae0f4c2 100644 --- a/pkg/ent/ent.go +++ b/pkg/ent/ent.go @@ -16,6 +16,7 @@ import ( "github.com/GoogleCloudPlatform/scion/pkg/ent/agent" "github.com/GoogleCloudPlatform/scion/pkg/ent/allowlistentry" "github.com/GoogleCloudPlatform/scion/pkg/ent/apikey" + "github.com/GoogleCloudPlatform/scion/pkg/ent/brokerdispatch" "github.com/GoogleCloudPlatform/scion/pkg/ent/brokerjointoken" "github.com/GoogleCloudPlatform/scion/pkg/ent/brokersecret" "github.com/GoogleCloudPlatform/scion/pkg/ent/envvar" @@ -106,6 +107,7 @@ func checkColumn(t, c string) error { agent.Table: agent.ValidColumn, allowlistentry.Table: allowlistentry.ValidColumn, apikey.Table: apikey.ValidColumn, + brokerdispatch.Table: brokerdispatch.ValidColumn, brokerjointoken.Table: brokerjointoken.ValidColumn, brokersecret.Table: brokersecret.ValidColumn, envvar.Table: envvar.ValidColumn, diff --git a/pkg/ent/hook/hook.go b/pkg/ent/hook/hook.go index 743dd42be..840f24a60 100644 --- a/pkg/ent/hook/hook.go +++ b/pkg/ent/hook/hook.go @@ -57,6 +57,18 @@ func (f ApiKeyFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, erro return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.ApiKeyMutation", m) } +// The BrokerDispatchFunc type is an adapter to allow the use of ordinary +// function as BrokerDispatch mutator. +type BrokerDispatchFunc func(context.Context, *ent.BrokerDispatchMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f BrokerDispatchFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if mv, ok := m.(*ent.BrokerDispatchMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.BrokerDispatchMutation", m) +} + // The BrokerJoinTokenFunc type is an adapter to allow the use of ordinary // function as BrokerJoinToken mutator. type BrokerJoinTokenFunc func(context.Context, *ent.BrokerJoinTokenMutation) (ent.Value, error) diff --git a/pkg/ent/message.go b/pkg/ent/message.go index d4aaee729..31cf52e9b 100644 --- a/pkg/ent/message.go +++ b/pkg/ent/message.go @@ -42,6 +42,10 @@ type Message struct { AgentID string `json:"agent_id,omitempty"` // GroupID holds the value of the "group_id" field. GroupID string `json:"group_id,omitempty"` + // DispatchState holds the value of the "dispatch_state" field. + DispatchState string `json:"dispatch_state,omitempty"` + // DispatchedAt holds the value of the "dispatched_at" field. + DispatchedAt *time.Time `json:"dispatched_at,omitempty"` // Created holds the value of the "created" field. Created time.Time `json:"created,omitempty"` selectValues sql.SelectValues @@ -54,9 +58,9 @@ func (*Message) scanValues(columns []string) ([]any, error) { switch columns[i] { case message.FieldUrgent, message.FieldBroadcasted, message.FieldRead: values[i] = new(sql.NullBool) - case message.FieldSender, message.FieldSenderID, message.FieldRecipient, message.FieldRecipientID, message.FieldMsg, message.FieldType, message.FieldAgentID, message.FieldGroupID: + case message.FieldSender, message.FieldSenderID, message.FieldRecipient, message.FieldRecipientID, message.FieldMsg, message.FieldType, message.FieldAgentID, message.FieldGroupID, message.FieldDispatchState: values[i] = new(sql.NullString) - case message.FieldCreated: + case message.FieldDispatchedAt, message.FieldCreated: values[i] = new(sql.NullTime) case message.FieldID, message.FieldProjectID: values[i] = new(uuid.UUID) @@ -153,6 +157,19 @@ func (_m *Message) assignValues(columns []string, values []any) error { } else if value.Valid { _m.GroupID = value.String } + case message.FieldDispatchState: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field dispatch_state", values[i]) + } else if value.Valid { + _m.DispatchState = value.String + } + case message.FieldDispatchedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field dispatched_at", values[i]) + } else if value.Valid { + _m.DispatchedAt = new(time.Time) + *_m.DispatchedAt = value.Time + } case message.FieldCreated: if value, ok := values[i].(*sql.NullTime); !ok { return fmt.Errorf("unexpected type %T for field created", values[i]) @@ -231,6 +248,14 @@ func (_m *Message) String() string { builder.WriteString("group_id=") builder.WriteString(_m.GroupID) builder.WriteString(", ") + builder.WriteString("dispatch_state=") + builder.WriteString(_m.DispatchState) + builder.WriteString(", ") + if v := _m.DispatchedAt; v != nil { + builder.WriteString("dispatched_at=") + builder.WriteString(v.Format(time.ANSIC)) + } + builder.WriteString(", ") builder.WriteString("created=") builder.WriteString(_m.Created.Format(time.ANSIC)) builder.WriteByte(')') diff --git a/pkg/ent/message/message.go b/pkg/ent/message/message.go index 13aa68ae6..c93f6e82c 100644 --- a/pkg/ent/message/message.go +++ b/pkg/ent/message/message.go @@ -38,6 +38,10 @@ const ( FieldAgentID = "agent_id" // FieldGroupID holds the string denoting the group_id field in the database. FieldGroupID = "group_id" + // FieldDispatchState holds the string denoting the dispatch_state field in the database. + FieldDispatchState = "dispatch_state" + // FieldDispatchedAt holds the string denoting the dispatched_at field in the database. + FieldDispatchedAt = "dispatched_at" // FieldCreated holds the string denoting the created field in the database. FieldCreated = "created" // Table holds the table name of the message in the database. @@ -59,6 +63,8 @@ var Columns = []string{ FieldRead, FieldAgentID, FieldGroupID, + FieldDispatchState, + FieldDispatchedAt, FieldCreated, } @@ -87,6 +93,8 @@ var ( DefaultBroadcasted bool // DefaultRead holds the default value on creation for the "read" field. DefaultRead bool + // DefaultDispatchState holds the default value on creation for the "dispatch_state" field. + DefaultDispatchState string // DefaultCreated holds the default value on creation for the "created" field. DefaultCreated func() time.Time // DefaultID holds the default value on creation for the "id" field. @@ -161,6 +169,16 @@ func ByGroupID(opts ...sql.OrderTermOption) OrderOption { return sql.OrderByField(FieldGroupID, opts...).ToFunc() } +// ByDispatchState orders the results by the dispatch_state field. +func ByDispatchState(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldDispatchState, opts...).ToFunc() +} + +// ByDispatchedAt orders the results by the dispatched_at field. +func ByDispatchedAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldDispatchedAt, opts...).ToFunc() +} + // ByCreated orders the results by the created field. func ByCreated(opts ...sql.OrderTermOption) OrderOption { return sql.OrderByField(FieldCreated, opts...).ToFunc() diff --git a/pkg/ent/message/where.go b/pkg/ent/message/where.go index 4cbf1a3a3..d38547160 100644 --- a/pkg/ent/message/where.go +++ b/pkg/ent/message/where.go @@ -115,6 +115,16 @@ func GroupID(v string) predicate.Message { return predicate.Message(sql.FieldEQ(FieldGroupID, v)) } +// DispatchState applies equality check predicate on the "dispatch_state" field. It's identical to DispatchStateEQ. +func DispatchState(v string) predicate.Message { + return predicate.Message(sql.FieldEQ(FieldDispatchState, v)) +} + +// DispatchedAt applies equality check predicate on the "dispatched_at" field. It's identical to DispatchedAtEQ. +func DispatchedAt(v time.Time) predicate.Message { + return predicate.Message(sql.FieldEQ(FieldDispatchedAt, v)) +} + // Created applies equality check predicate on the "created" field. It's identical to CreatedEQ. func Created(v time.Time) predicate.Message { return predicate.Message(sql.FieldEQ(FieldCreated, v)) @@ -750,6 +760,121 @@ func GroupIDContainsFold(v string) predicate.Message { return predicate.Message(sql.FieldContainsFold(FieldGroupID, v)) } +// DispatchStateEQ applies the EQ predicate on the "dispatch_state" field. +func DispatchStateEQ(v string) predicate.Message { + return predicate.Message(sql.FieldEQ(FieldDispatchState, v)) +} + +// DispatchStateNEQ applies the NEQ predicate on the "dispatch_state" field. +func DispatchStateNEQ(v string) predicate.Message { + return predicate.Message(sql.FieldNEQ(FieldDispatchState, v)) +} + +// DispatchStateIn applies the In predicate on the "dispatch_state" field. +func DispatchStateIn(vs ...string) predicate.Message { + return predicate.Message(sql.FieldIn(FieldDispatchState, vs...)) +} + +// DispatchStateNotIn applies the NotIn predicate on the "dispatch_state" field. +func DispatchStateNotIn(vs ...string) predicate.Message { + return predicate.Message(sql.FieldNotIn(FieldDispatchState, vs...)) +} + +// DispatchStateGT applies the GT predicate on the "dispatch_state" field. +func DispatchStateGT(v string) predicate.Message { + return predicate.Message(sql.FieldGT(FieldDispatchState, v)) +} + +// DispatchStateGTE applies the GTE predicate on the "dispatch_state" field. +func DispatchStateGTE(v string) predicate.Message { + return predicate.Message(sql.FieldGTE(FieldDispatchState, v)) +} + +// DispatchStateLT applies the LT predicate on the "dispatch_state" field. +func DispatchStateLT(v string) predicate.Message { + return predicate.Message(sql.FieldLT(FieldDispatchState, v)) +} + +// DispatchStateLTE applies the LTE predicate on the "dispatch_state" field. +func DispatchStateLTE(v string) predicate.Message { + return predicate.Message(sql.FieldLTE(FieldDispatchState, v)) +} + +// DispatchStateContains applies the Contains predicate on the "dispatch_state" field. +func DispatchStateContains(v string) predicate.Message { + return predicate.Message(sql.FieldContains(FieldDispatchState, v)) +} + +// DispatchStateHasPrefix applies the HasPrefix predicate on the "dispatch_state" field. +func DispatchStateHasPrefix(v string) predicate.Message { + return predicate.Message(sql.FieldHasPrefix(FieldDispatchState, v)) +} + +// DispatchStateHasSuffix applies the HasSuffix predicate on the "dispatch_state" field. +func DispatchStateHasSuffix(v string) predicate.Message { + return predicate.Message(sql.FieldHasSuffix(FieldDispatchState, v)) +} + +// DispatchStateEqualFold applies the EqualFold predicate on the "dispatch_state" field. +func DispatchStateEqualFold(v string) predicate.Message { + return predicate.Message(sql.FieldEqualFold(FieldDispatchState, v)) +} + +// DispatchStateContainsFold applies the ContainsFold predicate on the "dispatch_state" field. +func DispatchStateContainsFold(v string) predicate.Message { + return predicate.Message(sql.FieldContainsFold(FieldDispatchState, v)) +} + +// DispatchedAtEQ applies the EQ predicate on the "dispatched_at" field. +func DispatchedAtEQ(v time.Time) predicate.Message { + return predicate.Message(sql.FieldEQ(FieldDispatchedAt, v)) +} + +// DispatchedAtNEQ applies the NEQ predicate on the "dispatched_at" field. +func DispatchedAtNEQ(v time.Time) predicate.Message { + return predicate.Message(sql.FieldNEQ(FieldDispatchedAt, v)) +} + +// DispatchedAtIn applies the In predicate on the "dispatched_at" field. +func DispatchedAtIn(vs ...time.Time) predicate.Message { + return predicate.Message(sql.FieldIn(FieldDispatchedAt, vs...)) +} + +// DispatchedAtNotIn applies the NotIn predicate on the "dispatched_at" field. +func DispatchedAtNotIn(vs ...time.Time) predicate.Message { + return predicate.Message(sql.FieldNotIn(FieldDispatchedAt, vs...)) +} + +// DispatchedAtGT applies the GT predicate on the "dispatched_at" field. +func DispatchedAtGT(v time.Time) predicate.Message { + return predicate.Message(sql.FieldGT(FieldDispatchedAt, v)) +} + +// DispatchedAtGTE applies the GTE predicate on the "dispatched_at" field. +func DispatchedAtGTE(v time.Time) predicate.Message { + return predicate.Message(sql.FieldGTE(FieldDispatchedAt, v)) +} + +// DispatchedAtLT applies the LT predicate on the "dispatched_at" field. +func DispatchedAtLT(v time.Time) predicate.Message { + return predicate.Message(sql.FieldLT(FieldDispatchedAt, v)) +} + +// DispatchedAtLTE applies the LTE predicate on the "dispatched_at" field. +func DispatchedAtLTE(v time.Time) predicate.Message { + return predicate.Message(sql.FieldLTE(FieldDispatchedAt, v)) +} + +// DispatchedAtIsNil applies the IsNil predicate on the "dispatched_at" field. +func DispatchedAtIsNil() predicate.Message { + return predicate.Message(sql.FieldIsNull(FieldDispatchedAt)) +} + +// DispatchedAtNotNil applies the NotNil predicate on the "dispatched_at" field. +func DispatchedAtNotNil() predicate.Message { + return predicate.Message(sql.FieldNotNull(FieldDispatchedAt)) +} + // CreatedEQ applies the EQ predicate on the "created" field. func CreatedEQ(v time.Time) predicate.Message { return predicate.Message(sql.FieldEQ(FieldCreated, v)) diff --git a/pkg/ent/message_create.go b/pkg/ent/message_create.go index 198a9b98f..f29d7aa46 100644 --- a/pkg/ent/message_create.go +++ b/pkg/ent/message_create.go @@ -160,6 +160,34 @@ func (_c *MessageCreate) SetNillableGroupID(v *string) *MessageCreate { return _c } +// SetDispatchState sets the "dispatch_state" field. +func (_c *MessageCreate) SetDispatchState(v string) *MessageCreate { + _c.mutation.SetDispatchState(v) + return _c +} + +// SetNillableDispatchState sets the "dispatch_state" field if the given value is not nil. +func (_c *MessageCreate) SetNillableDispatchState(v *string) *MessageCreate { + if v != nil { + _c.SetDispatchState(*v) + } + return _c +} + +// SetDispatchedAt sets the "dispatched_at" field. +func (_c *MessageCreate) SetDispatchedAt(v time.Time) *MessageCreate { + _c.mutation.SetDispatchedAt(v) + return _c +} + +// SetNillableDispatchedAt sets the "dispatched_at" field if the given value is not nil. +func (_c *MessageCreate) SetNillableDispatchedAt(v *time.Time) *MessageCreate { + if v != nil { + _c.SetDispatchedAt(*v) + } + return _c +} + // SetCreated sets the "created" field. func (_c *MessageCreate) SetCreated(v time.Time) *MessageCreate { _c.mutation.SetCreated(v) @@ -239,6 +267,10 @@ func (_c *MessageCreate) defaults() { v := message.DefaultRead _c.mutation.SetRead(v) } + if _, ok := _c.mutation.DispatchState(); !ok { + v := message.DefaultDispatchState + _c.mutation.SetDispatchState(v) + } if _, ok := _c.mutation.Created(); !ok { v := message.DefaultCreated() _c.mutation.SetCreated(v) @@ -290,6 +322,9 @@ func (_c *MessageCreate) check() error { if _, ok := _c.mutation.Read(); !ok { return &ValidationError{Name: "read", err: errors.New(`ent: missing required field "Message.read"`)} } + if _, ok := _c.mutation.DispatchState(); !ok { + return &ValidationError{Name: "dispatch_state", err: errors.New(`ent: missing required field "Message.dispatch_state"`)} + } if _, ok := _c.mutation.Created(); !ok { return &ValidationError{Name: "created", err: errors.New(`ent: missing required field "Message.created"`)} } @@ -377,6 +412,14 @@ func (_c *MessageCreate) createSpec() (*Message, *sqlgraph.CreateSpec) { _spec.SetField(message.FieldGroupID, field.TypeString, value) _node.GroupID = value } + if value, ok := _c.mutation.DispatchState(); ok { + _spec.SetField(message.FieldDispatchState, field.TypeString, value) + _node.DispatchState = value + } + if value, ok := _c.mutation.DispatchedAt(); ok { + _spec.SetField(message.FieldDispatchedAt, field.TypeTime, value) + _node.DispatchedAt = &value + } if value, ok := _c.mutation.Created(); ok { _spec.SetField(message.FieldCreated, field.TypeTime, value) _node.Created = value @@ -601,6 +644,36 @@ func (u *MessageUpsert) ClearGroupID() *MessageUpsert { return u } +// SetDispatchState sets the "dispatch_state" field. +func (u *MessageUpsert) SetDispatchState(v string) *MessageUpsert { + u.Set(message.FieldDispatchState, v) + return u +} + +// UpdateDispatchState sets the "dispatch_state" field to the value that was provided on create. +func (u *MessageUpsert) UpdateDispatchState() *MessageUpsert { + u.SetExcluded(message.FieldDispatchState) + return u +} + +// SetDispatchedAt sets the "dispatched_at" field. +func (u *MessageUpsert) SetDispatchedAt(v time.Time) *MessageUpsert { + u.Set(message.FieldDispatchedAt, v) + return u +} + +// UpdateDispatchedAt sets the "dispatched_at" field to the value that was provided on create. +func (u *MessageUpsert) UpdateDispatchedAt() *MessageUpsert { + u.SetExcluded(message.FieldDispatchedAt) + return u +} + +// ClearDispatchedAt clears the value of the "dispatched_at" field. +func (u *MessageUpsert) ClearDispatchedAt() *MessageUpsert { + u.SetNull(message.FieldDispatchedAt) + return u +} + // UpdateNewValues updates the mutable fields using the new values that were set on create except the ID field. // Using this option is equivalent to using: // @@ -848,6 +921,41 @@ func (u *MessageUpsertOne) ClearGroupID() *MessageUpsertOne { }) } +// SetDispatchState sets the "dispatch_state" field. +func (u *MessageUpsertOne) SetDispatchState(v string) *MessageUpsertOne { + return u.Update(func(s *MessageUpsert) { + s.SetDispatchState(v) + }) +} + +// UpdateDispatchState sets the "dispatch_state" field to the value that was provided on create. +func (u *MessageUpsertOne) UpdateDispatchState() *MessageUpsertOne { + return u.Update(func(s *MessageUpsert) { + s.UpdateDispatchState() + }) +} + +// SetDispatchedAt sets the "dispatched_at" field. +func (u *MessageUpsertOne) SetDispatchedAt(v time.Time) *MessageUpsertOne { + return u.Update(func(s *MessageUpsert) { + s.SetDispatchedAt(v) + }) +} + +// UpdateDispatchedAt sets the "dispatched_at" field to the value that was provided on create. +func (u *MessageUpsertOne) UpdateDispatchedAt() *MessageUpsertOne { + return u.Update(func(s *MessageUpsert) { + s.UpdateDispatchedAt() + }) +} + +// ClearDispatchedAt clears the value of the "dispatched_at" field. +func (u *MessageUpsertOne) ClearDispatchedAt() *MessageUpsertOne { + return u.Update(func(s *MessageUpsert) { + s.ClearDispatchedAt() + }) +} + // Exec executes the query. func (u *MessageUpsertOne) Exec(ctx context.Context) error { if len(u.create.conflict) == 0 { @@ -1262,6 +1370,41 @@ func (u *MessageUpsertBulk) ClearGroupID() *MessageUpsertBulk { }) } +// SetDispatchState sets the "dispatch_state" field. +func (u *MessageUpsertBulk) SetDispatchState(v string) *MessageUpsertBulk { + return u.Update(func(s *MessageUpsert) { + s.SetDispatchState(v) + }) +} + +// UpdateDispatchState sets the "dispatch_state" field to the value that was provided on create. +func (u *MessageUpsertBulk) UpdateDispatchState() *MessageUpsertBulk { + return u.Update(func(s *MessageUpsert) { + s.UpdateDispatchState() + }) +} + +// SetDispatchedAt sets the "dispatched_at" field. +func (u *MessageUpsertBulk) SetDispatchedAt(v time.Time) *MessageUpsertBulk { + return u.Update(func(s *MessageUpsert) { + s.SetDispatchedAt(v) + }) +} + +// UpdateDispatchedAt sets the "dispatched_at" field to the value that was provided on create. +func (u *MessageUpsertBulk) UpdateDispatchedAt() *MessageUpsertBulk { + return u.Update(func(s *MessageUpsert) { + s.UpdateDispatchedAt() + }) +} + +// ClearDispatchedAt clears the value of the "dispatched_at" field. +func (u *MessageUpsertBulk) ClearDispatchedAt() *MessageUpsertBulk { + return u.Update(func(s *MessageUpsert) { + s.ClearDispatchedAt() + }) +} + // Exec executes the query. func (u *MessageUpsertBulk) Exec(ctx context.Context) error { if u.create.err != nil { diff --git a/pkg/ent/message_update.go b/pkg/ent/message_update.go index 0fdce57a7..5e925590f 100644 --- a/pkg/ent/message_update.go +++ b/pkg/ent/message_update.go @@ -6,6 +6,7 @@ import ( "context" "errors" "fmt" + "time" "entgo.io/ent/dialect/sql" "entgo.io/ent/dialect/sql/sqlgraph" @@ -220,6 +221,40 @@ func (_u *MessageUpdate) ClearGroupID() *MessageUpdate { return _u } +// SetDispatchState sets the "dispatch_state" field. +func (_u *MessageUpdate) SetDispatchState(v string) *MessageUpdate { + _u.mutation.SetDispatchState(v) + return _u +} + +// SetNillableDispatchState sets the "dispatch_state" field if the given value is not nil. +func (_u *MessageUpdate) SetNillableDispatchState(v *string) *MessageUpdate { + if v != nil { + _u.SetDispatchState(*v) + } + return _u +} + +// SetDispatchedAt sets the "dispatched_at" field. +func (_u *MessageUpdate) SetDispatchedAt(v time.Time) *MessageUpdate { + _u.mutation.SetDispatchedAt(v) + return _u +} + +// SetNillableDispatchedAt sets the "dispatched_at" field if the given value is not nil. +func (_u *MessageUpdate) SetNillableDispatchedAt(v *time.Time) *MessageUpdate { + if v != nil { + _u.SetDispatchedAt(*v) + } + return _u +} + +// ClearDispatchedAt clears the value of the "dispatched_at" field. +func (_u *MessageUpdate) ClearDispatchedAt() *MessageUpdate { + _u.mutation.ClearDispatchedAt() + return _u +} + // Mutation returns the MessageMutation object of the builder. func (_u *MessageUpdate) Mutation() *MessageMutation { return _u.mutation @@ -332,6 +367,15 @@ func (_u *MessageUpdate) sqlSave(ctx context.Context) (_node int, err error) { if _u.mutation.GroupIDCleared() { _spec.ClearField(message.FieldGroupID, field.TypeString) } + if value, ok := _u.mutation.DispatchState(); ok { + _spec.SetField(message.FieldDispatchState, field.TypeString, value) + } + if value, ok := _u.mutation.DispatchedAt(); ok { + _spec.SetField(message.FieldDispatchedAt, field.TypeTime, value) + } + if _u.mutation.DispatchedAtCleared() { + _spec.ClearField(message.FieldDispatchedAt, field.TypeTime) + } if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil { if _, ok := err.(*sqlgraph.NotFoundError); ok { err = &NotFoundError{message.Label} @@ -544,6 +588,40 @@ func (_u *MessageUpdateOne) ClearGroupID() *MessageUpdateOne { return _u } +// SetDispatchState sets the "dispatch_state" field. +func (_u *MessageUpdateOne) SetDispatchState(v string) *MessageUpdateOne { + _u.mutation.SetDispatchState(v) + return _u +} + +// SetNillableDispatchState sets the "dispatch_state" field if the given value is not nil. +func (_u *MessageUpdateOne) SetNillableDispatchState(v *string) *MessageUpdateOne { + if v != nil { + _u.SetDispatchState(*v) + } + return _u +} + +// SetDispatchedAt sets the "dispatched_at" field. +func (_u *MessageUpdateOne) SetDispatchedAt(v time.Time) *MessageUpdateOne { + _u.mutation.SetDispatchedAt(v) + return _u +} + +// SetNillableDispatchedAt sets the "dispatched_at" field if the given value is not nil. +func (_u *MessageUpdateOne) SetNillableDispatchedAt(v *time.Time) *MessageUpdateOne { + if v != nil { + _u.SetDispatchedAt(*v) + } + return _u +} + +// ClearDispatchedAt clears the value of the "dispatched_at" field. +func (_u *MessageUpdateOne) ClearDispatchedAt() *MessageUpdateOne { + _u.mutation.ClearDispatchedAt() + return _u +} + // Mutation returns the MessageMutation object of the builder. func (_u *MessageUpdateOne) Mutation() *MessageMutation { return _u.mutation @@ -686,6 +764,15 @@ func (_u *MessageUpdateOne) sqlSave(ctx context.Context) (_node *Message, err er if _u.mutation.GroupIDCleared() { _spec.ClearField(message.FieldGroupID, field.TypeString) } + if value, ok := _u.mutation.DispatchState(); ok { + _spec.SetField(message.FieldDispatchState, field.TypeString, value) + } + if value, ok := _u.mutation.DispatchedAt(); ok { + _spec.SetField(message.FieldDispatchedAt, field.TypeTime, value) + } + if _u.mutation.DispatchedAtCleared() { + _spec.ClearField(message.FieldDispatchedAt, field.TypeTime) + } _node = &Message{config: _u.config} _spec.Assign = _node.assignValues _spec.ScanValues = _node.scanValues diff --git a/pkg/ent/migrate/schema.go b/pkg/ent/migrate/schema.go index c12fac15e..a68e381ac 100644 --- a/pkg/ent/migrate/schema.go +++ b/pkg/ent/migrate/schema.go @@ -155,6 +155,37 @@ var ( }, }, } + // BrokerDispatchColumns holds the columns for the "broker_dispatch" table. + BrokerDispatchColumns = []*schema.Column{ + {Name: "id", Type: field.TypeUUID}, + {Name: "broker_id", Type: field.TypeUUID}, + {Name: "agent_id", Type: field.TypeUUID, Nullable: true}, + {Name: "agent_slug", Type: field.TypeString, Nullable: true}, + {Name: "project_id", Type: field.TypeUUID, Nullable: true}, + {Name: "op", Type: field.TypeString}, + {Name: "args", Type: field.TypeString, Nullable: true}, + {Name: "state", Type: field.TypeString, Default: "pending"}, + {Name: "result", Type: field.TypeString, Nullable: true}, + {Name: "claimed_by", Type: field.TypeString, Nullable: true}, + {Name: "attempts", Type: field.TypeInt, Default: 0}, + {Name: "error", Type: field.TypeString, Nullable: true}, + {Name: "created_at", Type: field.TypeTime}, + {Name: "updated_at", Type: field.TypeTime}, + {Name: "deadline_at", Type: field.TypeTime, Nullable: true}, + } + // BrokerDispatchTable holds the schema information for the "broker_dispatch" table. + BrokerDispatchTable = &schema.Table{ + Name: "broker_dispatch", + Columns: BrokerDispatchColumns, + PrimaryKey: []*schema.Column{BrokerDispatchColumns[0]}, + Indexes: []*schema.Index{ + { + Name: "brokerdispatch_broker_id_state", + Unique: false, + Columns: []*schema.Column{BrokerDispatchColumns[1], BrokerDispatchColumns[7]}, + }, + }, + } // BrokerJoinTokensColumns holds the columns for the "broker_join_tokens" table. BrokerJoinTokensColumns = []*schema.Column{ {Name: "broker_id", Type: field.TypeUUID}, @@ -492,6 +523,8 @@ var ( {Name: "read", Type: field.TypeBool, Default: false}, {Name: "agent_id", Type: field.TypeString, Nullable: true}, {Name: "group_id", Type: field.TypeString, Nullable: true}, + {Name: "dispatch_state", Type: field.TypeString, Default: "pending"}, + {Name: "dispatched_at", Type: field.TypeTime, Nullable: true}, {Name: "created", Type: field.TypeTime}, } // MessagesTable holds the schema information for the "messages" table. @@ -513,7 +546,7 @@ var ( { Name: "message_created", Unique: false, - Columns: []*schema.Column{MessagesColumns[13]}, + Columns: []*schema.Column{MessagesColumns[15]}, }, }, } @@ -724,6 +757,9 @@ var ( {Name: "endpoint", Type: field.TypeString, Nullable: true}, {Name: "created_by", Type: field.TypeString, Nullable: true}, {Name: "auto_provide", Type: field.TypeBool, Default: false}, + {Name: "connected_hub_id", Type: field.TypeString, Nullable: true}, + {Name: "connected_session_id", Type: field.TypeString, Nullable: true}, + {Name: "connected_at", Type: field.TypeTime, Nullable: true}, {Name: "created", Type: field.TypeTime}, {Name: "updated", Type: field.TypeTime}, } @@ -1024,6 +1060,7 @@ var ( AgentsTable, AllowListTable, APIKeysTable, + BrokerDispatchTable, BrokerJoinTokensTable, BrokerSecretsTable, EnvVarsTable, @@ -1064,6 +1101,9 @@ func init() { APIKeysTable.Annotation = &entsql.Annotation{ Table: "api_keys", } + BrokerDispatchTable.Annotation = &entsql.Annotation{ + Table: "broker_dispatch", + } BrokerJoinTokensTable.Annotation = &entsql.Annotation{ Table: "broker_join_tokens", } diff --git a/pkg/ent/mutation.go b/pkg/ent/mutation.go index 37ce86a44..514f2a0e8 100644 --- a/pkg/ent/mutation.go +++ b/pkg/ent/mutation.go @@ -15,6 +15,7 @@ import ( "github.com/GoogleCloudPlatform/scion/pkg/ent/agent" "github.com/GoogleCloudPlatform/scion/pkg/ent/allowlistentry" "github.com/GoogleCloudPlatform/scion/pkg/ent/apikey" + "github.com/GoogleCloudPlatform/scion/pkg/ent/brokerdispatch" "github.com/GoogleCloudPlatform/scion/pkg/ent/brokerjointoken" "github.com/GoogleCloudPlatform/scion/pkg/ent/brokersecret" "github.com/GoogleCloudPlatform/scion/pkg/ent/envvar" @@ -59,6 +60,7 @@ const ( TypeAgent = "Agent" TypeAllowListEntry = "AllowListEntry" TypeApiKey = "ApiKey" + TypeBrokerDispatch = "BrokerDispatch" TypeBrokerJoinToken = "BrokerJoinToken" TypeBrokerSecret = "BrokerSecret" TypeEnvVar = "EnvVar" @@ -6016,6 +6018,1231 @@ func (m *ApiKeyMutation) ResetEdge(name string) error { return fmt.Errorf("unknown ApiKey edge %s", name) } +// BrokerDispatchMutation represents an operation that mutates the BrokerDispatch nodes in the graph. +type BrokerDispatchMutation struct { + config + op Op + typ string + id *uuid.UUID + broker_id *uuid.UUID + agent_id *uuid.UUID + agent_slug *string + project_id *uuid.UUID + _op *string + args *string + state *string + result *string + claimed_by *string + attempts *int + addattempts *int + error *string + created_at *time.Time + updated_at *time.Time + deadline_at *time.Time + clearedFields map[string]struct{} + done bool + oldValue func(context.Context) (*BrokerDispatch, error) + predicates []predicate.BrokerDispatch +} + +var _ ent.Mutation = (*BrokerDispatchMutation)(nil) + +// brokerdispatchOption allows management of the mutation configuration using functional options. +type brokerdispatchOption func(*BrokerDispatchMutation) + +// newBrokerDispatchMutation creates new mutation for the BrokerDispatch entity. +func newBrokerDispatchMutation(c config, op Op, opts ...brokerdispatchOption) *BrokerDispatchMutation { + m := &BrokerDispatchMutation{ + config: c, + op: op, + typ: TypeBrokerDispatch, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// withBrokerDispatchID sets the ID field of the mutation. +func withBrokerDispatchID(id uuid.UUID) brokerdispatchOption { + return func(m *BrokerDispatchMutation) { + var ( + err error + once sync.Once + value *BrokerDispatch + ) + m.oldValue = func(ctx context.Context) (*BrokerDispatch, error) { + once.Do(func() { + if m.done { + err = errors.New("querying old values post mutation is not allowed") + } else { + value, err = m.Client().BrokerDispatch.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } +} + +// withBrokerDispatch sets the old BrokerDispatch of the mutation. +func withBrokerDispatch(node *BrokerDispatch) brokerdispatchOption { + return func(m *BrokerDispatchMutation) { + m.oldValue = func(context.Context) (*BrokerDispatch, error) { + return node, nil + } + m.id = &node.ID + } +} + +// Client returns a new `ent.Client` from the mutation. If the mutation was +// executed in a transaction (ent.Tx), a transactional client is returned. +func (m BrokerDispatchMutation) Client() *Client { + client := &Client{config: m.config} + client.init() + return client +} + +// Tx returns an `ent.Tx` for mutations that were executed in transactions; +// it returns an error otherwise. +func (m BrokerDispatchMutation) Tx() (*Tx, error) { + if _, ok := m.driver.(*txDriver); !ok { + return nil, errors.New("ent: mutation is not running in a transaction") + } + tx := &Tx{config: m.config} + tx.init() + return tx, nil +} + +// SetID sets the value of the id field. Note that this +// operation is only accepted on creation of BrokerDispatch entities. +func (m *BrokerDispatchMutation) SetID(id uuid.UUID) { + m.id = &id +} + +// ID returns the ID value in the mutation. Note that the ID is only available +// if it was provided to the builder or after it was returned from the database. +func (m *BrokerDispatchMutation) ID() (id uuid.UUID, exists bool) { + if m.id == nil { + return + } + return *m.id, true +} + +// IDs queries the database and returns the entity ids that match the mutation's predicate. +// That means, if the mutation is applied within a transaction with an isolation level such +// as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated +// or updated by the mutation. +func (m *BrokerDispatchMutation) IDs(ctx context.Context) ([]uuid.UUID, error) { + switch { + case m.op.Is(OpUpdateOne | OpDeleteOne): + id, exists := m.ID() + if exists { + return []uuid.UUID{id}, nil + } + fallthrough + case m.op.Is(OpUpdate | OpDelete): + return m.Client().BrokerDispatch.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } +} + +// SetBrokerID sets the "broker_id" field. +func (m *BrokerDispatchMutation) SetBrokerID(u uuid.UUID) { + m.broker_id = &u +} + +// BrokerID returns the value of the "broker_id" field in the mutation. +func (m *BrokerDispatchMutation) BrokerID() (r uuid.UUID, exists bool) { + v := m.broker_id + if v == nil { + return + } + return *v, true +} + +// OldBrokerID returns the old "broker_id" field's value of the BrokerDispatch entity. +// If the BrokerDispatch object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *BrokerDispatchMutation) OldBrokerID(ctx context.Context) (v uuid.UUID, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldBrokerID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldBrokerID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldBrokerID: %w", err) + } + return oldValue.BrokerID, nil +} + +// ResetBrokerID resets all changes to the "broker_id" field. +func (m *BrokerDispatchMutation) ResetBrokerID() { + m.broker_id = nil +} + +// SetAgentID sets the "agent_id" field. +func (m *BrokerDispatchMutation) SetAgentID(u uuid.UUID) { + m.agent_id = &u +} + +// AgentID returns the value of the "agent_id" field in the mutation. +func (m *BrokerDispatchMutation) AgentID() (r uuid.UUID, exists bool) { + v := m.agent_id + if v == nil { + return + } + return *v, true +} + +// OldAgentID returns the old "agent_id" field's value of the BrokerDispatch entity. +// If the BrokerDispatch object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *BrokerDispatchMutation) OldAgentID(ctx context.Context) (v *uuid.UUID, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldAgentID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldAgentID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldAgentID: %w", err) + } + return oldValue.AgentID, nil +} + +// ClearAgentID clears the value of the "agent_id" field. +func (m *BrokerDispatchMutation) ClearAgentID() { + m.agent_id = nil + m.clearedFields[brokerdispatch.FieldAgentID] = struct{}{} +} + +// AgentIDCleared returns if the "agent_id" field was cleared in this mutation. +func (m *BrokerDispatchMutation) AgentIDCleared() bool { + _, ok := m.clearedFields[brokerdispatch.FieldAgentID] + return ok +} + +// ResetAgentID resets all changes to the "agent_id" field. +func (m *BrokerDispatchMutation) ResetAgentID() { + m.agent_id = nil + delete(m.clearedFields, brokerdispatch.FieldAgentID) +} + +// SetAgentSlug sets the "agent_slug" field. +func (m *BrokerDispatchMutation) SetAgentSlug(s string) { + m.agent_slug = &s +} + +// AgentSlug returns the value of the "agent_slug" field in the mutation. +func (m *BrokerDispatchMutation) AgentSlug() (r string, exists bool) { + v := m.agent_slug + if v == nil { + return + } + return *v, true +} + +// OldAgentSlug returns the old "agent_slug" field's value of the BrokerDispatch entity. +// If the BrokerDispatch object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *BrokerDispatchMutation) OldAgentSlug(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldAgentSlug is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldAgentSlug requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldAgentSlug: %w", err) + } + return oldValue.AgentSlug, nil +} + +// ClearAgentSlug clears the value of the "agent_slug" field. +func (m *BrokerDispatchMutation) ClearAgentSlug() { + m.agent_slug = nil + m.clearedFields[brokerdispatch.FieldAgentSlug] = struct{}{} +} + +// AgentSlugCleared returns if the "agent_slug" field was cleared in this mutation. +func (m *BrokerDispatchMutation) AgentSlugCleared() bool { + _, ok := m.clearedFields[brokerdispatch.FieldAgentSlug] + return ok +} + +// ResetAgentSlug resets all changes to the "agent_slug" field. +func (m *BrokerDispatchMutation) ResetAgentSlug() { + m.agent_slug = nil + delete(m.clearedFields, brokerdispatch.FieldAgentSlug) +} + +// SetProjectID sets the "project_id" field. +func (m *BrokerDispatchMutation) SetProjectID(u uuid.UUID) { + m.project_id = &u +} + +// ProjectID returns the value of the "project_id" field in the mutation. +func (m *BrokerDispatchMutation) ProjectID() (r uuid.UUID, exists bool) { + v := m.project_id + if v == nil { + return + } + return *v, true +} + +// OldProjectID returns the old "project_id" field's value of the BrokerDispatch entity. +// If the BrokerDispatch object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *BrokerDispatchMutation) OldProjectID(ctx context.Context) (v *uuid.UUID, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldProjectID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldProjectID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldProjectID: %w", err) + } + return oldValue.ProjectID, nil +} + +// ClearProjectID clears the value of the "project_id" field. +func (m *BrokerDispatchMutation) ClearProjectID() { + m.project_id = nil + m.clearedFields[brokerdispatch.FieldProjectID] = struct{}{} +} + +// ProjectIDCleared returns if the "project_id" field was cleared in this mutation. +func (m *BrokerDispatchMutation) ProjectIDCleared() bool { + _, ok := m.clearedFields[brokerdispatch.FieldProjectID] + return ok +} + +// ResetProjectID resets all changes to the "project_id" field. +func (m *BrokerDispatchMutation) ResetProjectID() { + m.project_id = nil + delete(m.clearedFields, brokerdispatch.FieldProjectID) +} + +// SetOpField sets the "op" field. +func (m *BrokerDispatchMutation) SetOpField(s string) { + m._op = &s +} + +// GetOp returns the value of the "op" field in the mutation. +func (m *BrokerDispatchMutation) GetOp() (r string, exists bool) { + v := m._op + if v == nil { + return + } + return *v, true +} + +// OldOp returns the old "op" field's value of the BrokerDispatch entity. +// If the BrokerDispatch object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *BrokerDispatchMutation) OldOp(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldOp is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldOp requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldOp: %w", err) + } + return oldValue.Op, nil +} + +// ResetOp resets all changes to the "op" field. +func (m *BrokerDispatchMutation) ResetOp() { + m._op = nil +} + +// SetArgs sets the "args" field. +func (m *BrokerDispatchMutation) SetArgs(s string) { + m.args = &s +} + +// Args returns the value of the "args" field in the mutation. +func (m *BrokerDispatchMutation) Args() (r string, exists bool) { + v := m.args + if v == nil { + return + } + return *v, true +} + +// OldArgs returns the old "args" field's value of the BrokerDispatch entity. +// If the BrokerDispatch object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *BrokerDispatchMutation) OldArgs(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldArgs is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldArgs requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldArgs: %w", err) + } + return oldValue.Args, nil +} + +// ClearArgs clears the value of the "args" field. +func (m *BrokerDispatchMutation) ClearArgs() { + m.args = nil + m.clearedFields[brokerdispatch.FieldArgs] = struct{}{} +} + +// ArgsCleared returns if the "args" field was cleared in this mutation. +func (m *BrokerDispatchMutation) ArgsCleared() bool { + _, ok := m.clearedFields[brokerdispatch.FieldArgs] + return ok +} + +// ResetArgs resets all changes to the "args" field. +func (m *BrokerDispatchMutation) ResetArgs() { + m.args = nil + delete(m.clearedFields, brokerdispatch.FieldArgs) +} + +// SetState sets the "state" field. +func (m *BrokerDispatchMutation) SetState(s string) { + m.state = &s +} + +// State returns the value of the "state" field in the mutation. +func (m *BrokerDispatchMutation) State() (r string, exists bool) { + v := m.state + if v == nil { + return + } + return *v, true +} + +// OldState returns the old "state" field's value of the BrokerDispatch entity. +// If the BrokerDispatch object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *BrokerDispatchMutation) OldState(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldState is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldState requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldState: %w", err) + } + return oldValue.State, nil +} + +// ResetState resets all changes to the "state" field. +func (m *BrokerDispatchMutation) ResetState() { + m.state = nil +} + +// SetResult sets the "result" field. +func (m *BrokerDispatchMutation) SetResult(s string) { + m.result = &s +} + +// Result returns the value of the "result" field in the mutation. +func (m *BrokerDispatchMutation) Result() (r string, exists bool) { + v := m.result + if v == nil { + return + } + return *v, true +} + +// OldResult returns the old "result" field's value of the BrokerDispatch entity. +// If the BrokerDispatch object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *BrokerDispatchMutation) OldResult(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldResult is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldResult requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldResult: %w", err) + } + return oldValue.Result, nil +} + +// ClearResult clears the value of the "result" field. +func (m *BrokerDispatchMutation) ClearResult() { + m.result = nil + m.clearedFields[brokerdispatch.FieldResult] = struct{}{} +} + +// ResultCleared returns if the "result" field was cleared in this mutation. +func (m *BrokerDispatchMutation) ResultCleared() bool { + _, ok := m.clearedFields[brokerdispatch.FieldResult] + return ok +} + +// ResetResult resets all changes to the "result" field. +func (m *BrokerDispatchMutation) ResetResult() { + m.result = nil + delete(m.clearedFields, brokerdispatch.FieldResult) +} + +// SetClaimedBy sets the "claimed_by" field. +func (m *BrokerDispatchMutation) SetClaimedBy(s string) { + m.claimed_by = &s +} + +// ClaimedBy returns the value of the "claimed_by" field in the mutation. +func (m *BrokerDispatchMutation) ClaimedBy() (r string, exists bool) { + v := m.claimed_by + if v == nil { + return + } + return *v, true +} + +// OldClaimedBy returns the old "claimed_by" field's value of the BrokerDispatch entity. +// If the BrokerDispatch object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *BrokerDispatchMutation) OldClaimedBy(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldClaimedBy is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldClaimedBy requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldClaimedBy: %w", err) + } + return oldValue.ClaimedBy, nil +} + +// ClearClaimedBy clears the value of the "claimed_by" field. +func (m *BrokerDispatchMutation) ClearClaimedBy() { + m.claimed_by = nil + m.clearedFields[brokerdispatch.FieldClaimedBy] = struct{}{} +} + +// ClaimedByCleared returns if the "claimed_by" field was cleared in this mutation. +func (m *BrokerDispatchMutation) ClaimedByCleared() bool { + _, ok := m.clearedFields[brokerdispatch.FieldClaimedBy] + return ok +} + +// ResetClaimedBy resets all changes to the "claimed_by" field. +func (m *BrokerDispatchMutation) ResetClaimedBy() { + m.claimed_by = nil + delete(m.clearedFields, brokerdispatch.FieldClaimedBy) +} + +// SetAttempts sets the "attempts" field. +func (m *BrokerDispatchMutation) SetAttempts(i int) { + m.attempts = &i + m.addattempts = nil +} + +// Attempts returns the value of the "attempts" field in the mutation. +func (m *BrokerDispatchMutation) Attempts() (r int, exists bool) { + v := m.attempts + if v == nil { + return + } + return *v, true +} + +// OldAttempts returns the old "attempts" field's value of the BrokerDispatch entity. +// If the BrokerDispatch object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *BrokerDispatchMutation) OldAttempts(ctx context.Context) (v int, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldAttempts is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldAttempts requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldAttempts: %w", err) + } + return oldValue.Attempts, nil +} + +// AddAttempts adds i to the "attempts" field. +func (m *BrokerDispatchMutation) AddAttempts(i int) { + if m.addattempts != nil { + *m.addattempts += i + } else { + m.addattempts = &i + } +} + +// AddedAttempts returns the value that was added to the "attempts" field in this mutation. +func (m *BrokerDispatchMutation) AddedAttempts() (r int, exists bool) { + v := m.addattempts + if v == nil { + return + } + return *v, true +} + +// ResetAttempts resets all changes to the "attempts" field. +func (m *BrokerDispatchMutation) ResetAttempts() { + m.attempts = nil + m.addattempts = nil +} + +// SetError sets the "error" field. +func (m *BrokerDispatchMutation) SetError(s string) { + m.error = &s +} + +// Error returns the value of the "error" field in the mutation. +func (m *BrokerDispatchMutation) Error() (r string, exists bool) { + v := m.error + if v == nil { + return + } + return *v, true +} + +// OldError returns the old "error" field's value of the BrokerDispatch entity. +// If the BrokerDispatch object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *BrokerDispatchMutation) OldError(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldError is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldError requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldError: %w", err) + } + return oldValue.Error, nil +} + +// ClearError clears the value of the "error" field. +func (m *BrokerDispatchMutation) ClearError() { + m.error = nil + m.clearedFields[brokerdispatch.FieldError] = struct{}{} +} + +// ErrorCleared returns if the "error" field was cleared in this mutation. +func (m *BrokerDispatchMutation) ErrorCleared() bool { + _, ok := m.clearedFields[brokerdispatch.FieldError] + return ok +} + +// ResetError resets all changes to the "error" field. +func (m *BrokerDispatchMutation) ResetError() { + m.error = nil + delete(m.clearedFields, brokerdispatch.FieldError) +} + +// SetCreatedAt sets the "created_at" field. +func (m *BrokerDispatchMutation) SetCreatedAt(t time.Time) { + m.created_at = &t +} + +// CreatedAt returns the value of the "created_at" field in the mutation. +func (m *BrokerDispatchMutation) CreatedAt() (r time.Time, exists bool) { + v := m.created_at + if v == nil { + return + } + return *v, true +} + +// OldCreatedAt returns the old "created_at" field's value of the BrokerDispatch entity. +// If the BrokerDispatch object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *BrokerDispatchMutation) OldCreatedAt(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreatedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreatedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreatedAt: %w", err) + } + return oldValue.CreatedAt, nil +} + +// ResetCreatedAt resets all changes to the "created_at" field. +func (m *BrokerDispatchMutation) ResetCreatedAt() { + m.created_at = nil +} + +// SetUpdatedAt sets the "updated_at" field. +func (m *BrokerDispatchMutation) SetUpdatedAt(t time.Time) { + m.updated_at = &t +} + +// UpdatedAt returns the value of the "updated_at" field in the mutation. +func (m *BrokerDispatchMutation) UpdatedAt() (r time.Time, exists bool) { + v := m.updated_at + if v == nil { + return + } + return *v, true +} + +// OldUpdatedAt returns the old "updated_at" field's value of the BrokerDispatch entity. +// If the BrokerDispatch object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *BrokerDispatchMutation) OldUpdatedAt(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldUpdatedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldUpdatedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldUpdatedAt: %w", err) + } + return oldValue.UpdatedAt, nil +} + +// ResetUpdatedAt resets all changes to the "updated_at" field. +func (m *BrokerDispatchMutation) ResetUpdatedAt() { + m.updated_at = nil +} + +// SetDeadlineAt sets the "deadline_at" field. +func (m *BrokerDispatchMutation) SetDeadlineAt(t time.Time) { + m.deadline_at = &t +} + +// DeadlineAt returns the value of the "deadline_at" field in the mutation. +func (m *BrokerDispatchMutation) DeadlineAt() (r time.Time, exists bool) { + v := m.deadline_at + if v == nil { + return + } + return *v, true +} + +// OldDeadlineAt returns the old "deadline_at" field's value of the BrokerDispatch entity. +// If the BrokerDispatch object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *BrokerDispatchMutation) OldDeadlineAt(ctx context.Context) (v *time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldDeadlineAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldDeadlineAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldDeadlineAt: %w", err) + } + return oldValue.DeadlineAt, nil +} + +// ClearDeadlineAt clears the value of the "deadline_at" field. +func (m *BrokerDispatchMutation) ClearDeadlineAt() { + m.deadline_at = nil + m.clearedFields[brokerdispatch.FieldDeadlineAt] = struct{}{} +} + +// DeadlineAtCleared returns if the "deadline_at" field was cleared in this mutation. +func (m *BrokerDispatchMutation) DeadlineAtCleared() bool { + _, ok := m.clearedFields[brokerdispatch.FieldDeadlineAt] + return ok +} + +// ResetDeadlineAt resets all changes to the "deadline_at" field. +func (m *BrokerDispatchMutation) ResetDeadlineAt() { + m.deadline_at = nil + delete(m.clearedFields, brokerdispatch.FieldDeadlineAt) +} + +// Where appends a list predicates to the BrokerDispatchMutation builder. +func (m *BrokerDispatchMutation) Where(ps ...predicate.BrokerDispatch) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the BrokerDispatchMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *BrokerDispatchMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.BrokerDispatch, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *BrokerDispatchMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *BrokerDispatchMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (BrokerDispatch). +func (m *BrokerDispatchMutation) Type() string { + return m.typ +} + +// Fields returns all fields that were changed during this mutation. Note that in +// order to get all numeric fields that were incremented/decremented, call +// AddedFields(). +func (m *BrokerDispatchMutation) Fields() []string { + fields := make([]string, 0, 14) + if m.broker_id != nil { + fields = append(fields, brokerdispatch.FieldBrokerID) + } + if m.agent_id != nil { + fields = append(fields, brokerdispatch.FieldAgentID) + } + if m.agent_slug != nil { + fields = append(fields, brokerdispatch.FieldAgentSlug) + } + if m.project_id != nil { + fields = append(fields, brokerdispatch.FieldProjectID) + } + if m._op != nil { + fields = append(fields, brokerdispatch.FieldOp) + } + if m.args != nil { + fields = append(fields, brokerdispatch.FieldArgs) + } + if m.state != nil { + fields = append(fields, brokerdispatch.FieldState) + } + if m.result != nil { + fields = append(fields, brokerdispatch.FieldResult) + } + if m.claimed_by != nil { + fields = append(fields, brokerdispatch.FieldClaimedBy) + } + if m.attempts != nil { + fields = append(fields, brokerdispatch.FieldAttempts) + } + if m.error != nil { + fields = append(fields, brokerdispatch.FieldError) + } + if m.created_at != nil { + fields = append(fields, brokerdispatch.FieldCreatedAt) + } + if m.updated_at != nil { + fields = append(fields, brokerdispatch.FieldUpdatedAt) + } + if m.deadline_at != nil { + fields = append(fields, brokerdispatch.FieldDeadlineAt) + } + return fields +} + +// Field returns the value of a field with the given name. The second boolean +// return value indicates that this field was not set, or was not defined in the +// schema. +func (m *BrokerDispatchMutation) Field(name string) (ent.Value, bool) { + switch name { + case brokerdispatch.FieldBrokerID: + return m.BrokerID() + case brokerdispatch.FieldAgentID: + return m.AgentID() + case brokerdispatch.FieldAgentSlug: + return m.AgentSlug() + case brokerdispatch.FieldProjectID: + return m.ProjectID() + case brokerdispatch.FieldOp: + return m.GetOp() + case brokerdispatch.FieldArgs: + return m.Args() + case brokerdispatch.FieldState: + return m.State() + case brokerdispatch.FieldResult: + return m.Result() + case brokerdispatch.FieldClaimedBy: + return m.ClaimedBy() + case brokerdispatch.FieldAttempts: + return m.Attempts() + case brokerdispatch.FieldError: + return m.Error() + case brokerdispatch.FieldCreatedAt: + return m.CreatedAt() + case brokerdispatch.FieldUpdatedAt: + return m.UpdatedAt() + case brokerdispatch.FieldDeadlineAt: + return m.DeadlineAt() + } + return nil, false +} + +// OldField returns the old value of the field from the database. An error is +// returned if the mutation operation is not UpdateOne, or the query to the +// database failed. +func (m *BrokerDispatchMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + switch name { + case brokerdispatch.FieldBrokerID: + return m.OldBrokerID(ctx) + case brokerdispatch.FieldAgentID: + return m.OldAgentID(ctx) + case brokerdispatch.FieldAgentSlug: + return m.OldAgentSlug(ctx) + case brokerdispatch.FieldProjectID: + return m.OldProjectID(ctx) + case brokerdispatch.FieldOp: + return m.OldOp(ctx) + case brokerdispatch.FieldArgs: + return m.OldArgs(ctx) + case brokerdispatch.FieldState: + return m.OldState(ctx) + case brokerdispatch.FieldResult: + return m.OldResult(ctx) + case brokerdispatch.FieldClaimedBy: + return m.OldClaimedBy(ctx) + case brokerdispatch.FieldAttempts: + return m.OldAttempts(ctx) + case brokerdispatch.FieldError: + return m.OldError(ctx) + case brokerdispatch.FieldCreatedAt: + return m.OldCreatedAt(ctx) + case brokerdispatch.FieldUpdatedAt: + return m.OldUpdatedAt(ctx) + case brokerdispatch.FieldDeadlineAt: + return m.OldDeadlineAt(ctx) + } + return nil, fmt.Errorf("unknown BrokerDispatch field %s", name) +} + +// SetField sets the value of a field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *BrokerDispatchMutation) SetField(name string, value ent.Value) error { + switch name { + case brokerdispatch.FieldBrokerID: + v, ok := value.(uuid.UUID) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetBrokerID(v) + return nil + case brokerdispatch.FieldAgentID: + v, ok := value.(uuid.UUID) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetAgentID(v) + return nil + case brokerdispatch.FieldAgentSlug: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetAgentSlug(v) + return nil + case brokerdispatch.FieldProjectID: + v, ok := value.(uuid.UUID) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetProjectID(v) + return nil + case brokerdispatch.FieldOp: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetOpField(v) + return nil + case brokerdispatch.FieldArgs: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetArgs(v) + return nil + case brokerdispatch.FieldState: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetState(v) + return nil + case brokerdispatch.FieldResult: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetResult(v) + return nil + case brokerdispatch.FieldClaimedBy: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetClaimedBy(v) + return nil + case brokerdispatch.FieldAttempts: + v, ok := value.(int) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetAttempts(v) + return nil + case brokerdispatch.FieldError: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetError(v) + return nil + case brokerdispatch.FieldCreatedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreatedAt(v) + return nil + case brokerdispatch.FieldUpdatedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetUpdatedAt(v) + return nil + case brokerdispatch.FieldDeadlineAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetDeadlineAt(v) + return nil + } + return fmt.Errorf("unknown BrokerDispatch field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *BrokerDispatchMutation) AddedFields() []string { + var fields []string + if m.addattempts != nil { + fields = append(fields, brokerdispatch.FieldAttempts) + } + return fields +} + +// AddedField returns the numeric value that was incremented/decremented on a field +// with the given name. The second boolean return value indicates that this field +// was not set, or was not defined in the schema. +func (m *BrokerDispatchMutation) AddedField(name string) (ent.Value, bool) { + switch name { + case brokerdispatch.FieldAttempts: + return m.AddedAttempts() + } + return nil, false +} + +// AddField adds the value to the field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *BrokerDispatchMutation) AddField(name string, value ent.Value) error { + switch name { + case brokerdispatch.FieldAttempts: + v, ok := value.(int) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.AddAttempts(v) + return nil + } + return fmt.Errorf("unknown BrokerDispatch numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *BrokerDispatchMutation) ClearedFields() []string { + var fields []string + if m.FieldCleared(brokerdispatch.FieldAgentID) { + fields = append(fields, brokerdispatch.FieldAgentID) + } + if m.FieldCleared(brokerdispatch.FieldAgentSlug) { + fields = append(fields, brokerdispatch.FieldAgentSlug) + } + if m.FieldCleared(brokerdispatch.FieldProjectID) { + fields = append(fields, brokerdispatch.FieldProjectID) + } + if m.FieldCleared(brokerdispatch.FieldArgs) { + fields = append(fields, brokerdispatch.FieldArgs) + } + if m.FieldCleared(brokerdispatch.FieldResult) { + fields = append(fields, brokerdispatch.FieldResult) + } + if m.FieldCleared(brokerdispatch.FieldClaimedBy) { + fields = append(fields, brokerdispatch.FieldClaimedBy) + } + if m.FieldCleared(brokerdispatch.FieldError) { + fields = append(fields, brokerdispatch.FieldError) + } + if m.FieldCleared(brokerdispatch.FieldDeadlineAt) { + fields = append(fields, brokerdispatch.FieldDeadlineAt) + } + return fields +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *BrokerDispatchMutation) FieldCleared(name string) bool { + _, ok := m.clearedFields[name] + return ok +} + +// ClearField clears the value of the field with the given name. It returns an +// error if the field is not defined in the schema. +func (m *BrokerDispatchMutation) ClearField(name string) error { + switch name { + case brokerdispatch.FieldAgentID: + m.ClearAgentID() + return nil + case brokerdispatch.FieldAgentSlug: + m.ClearAgentSlug() + return nil + case brokerdispatch.FieldProjectID: + m.ClearProjectID() + return nil + case brokerdispatch.FieldArgs: + m.ClearArgs() + return nil + case brokerdispatch.FieldResult: + m.ClearResult() + return nil + case brokerdispatch.FieldClaimedBy: + m.ClearClaimedBy() + return nil + case brokerdispatch.FieldError: + m.ClearError() + return nil + case brokerdispatch.FieldDeadlineAt: + m.ClearDeadlineAt() + return nil + } + return fmt.Errorf("unknown BrokerDispatch nullable field %s", name) +} + +// ResetField resets all changes in the mutation for the field with the given name. +// It returns an error if the field is not defined in the schema. +func (m *BrokerDispatchMutation) ResetField(name string) error { + switch name { + case brokerdispatch.FieldBrokerID: + m.ResetBrokerID() + return nil + case brokerdispatch.FieldAgentID: + m.ResetAgentID() + return nil + case brokerdispatch.FieldAgentSlug: + m.ResetAgentSlug() + return nil + case brokerdispatch.FieldProjectID: + m.ResetProjectID() + return nil + case brokerdispatch.FieldOp: + m.ResetOp() + return nil + case brokerdispatch.FieldArgs: + m.ResetArgs() + return nil + case brokerdispatch.FieldState: + m.ResetState() + return nil + case brokerdispatch.FieldResult: + m.ResetResult() + return nil + case brokerdispatch.FieldClaimedBy: + m.ResetClaimedBy() + return nil + case brokerdispatch.FieldAttempts: + m.ResetAttempts() + return nil + case brokerdispatch.FieldError: + m.ResetError() + return nil + case brokerdispatch.FieldCreatedAt: + m.ResetCreatedAt() + return nil + case brokerdispatch.FieldUpdatedAt: + m.ResetUpdatedAt() + return nil + case brokerdispatch.FieldDeadlineAt: + m.ResetDeadlineAt() + return nil + } + return fmt.Errorf("unknown BrokerDispatch field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *BrokerDispatchMutation) AddedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *BrokerDispatchMutation) AddedIDs(name string) []ent.Value { + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *BrokerDispatchMutation) RemovedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *BrokerDispatchMutation) RemovedIDs(name string) []ent.Value { + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *BrokerDispatchMutation) ClearedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *BrokerDispatchMutation) EdgeCleared(name string) bool { + return false +} + +// ClearEdge clears the value of the edge with the given name. It returns an error +// if that edge is not defined in the schema. +func (m *BrokerDispatchMutation) ClearEdge(name string) error { + return fmt.Errorf("unknown BrokerDispatch unique edge %s", name) +} + +// ResetEdge resets all changes to the edge with the given name in this mutation. +// It returns an error if the edge is not defined in the schema. +func (m *BrokerDispatchMutation) ResetEdge(name string) error { + return fmt.Errorf("unknown BrokerDispatch edge %s", name) +} + // BrokerJoinTokenMutation represents an operation that mutates the BrokerJoinToken nodes in the graph. type BrokerJoinTokenMutation struct { config @@ -16037,26 +17264,28 @@ func (m *MaintenanceOperationRunMutation) ResetEdge(name string) error { // MessageMutation represents an operation that mutates the Message nodes in the graph. type MessageMutation struct { config - op Op - typ string - id *uuid.UUID - project_id *uuid.UUID - sender *string - sender_id *string - recipient *string - recipient_id *string - msg *string - _type *string - urgent *bool - broadcasted *bool - read *bool - agent_id *string - group_id *string - created *time.Time - clearedFields map[string]struct{} - done bool - oldValue func(context.Context) (*Message, error) - predicates []predicate.Message + op Op + typ string + id *uuid.UUID + project_id *uuid.UUID + sender *string + sender_id *string + recipient *string + recipient_id *string + msg *string + _type *string + urgent *bool + broadcasted *bool + read *bool + agent_id *string + group_id *string + dispatch_state *string + dispatched_at *time.Time + created *time.Time + clearedFields map[string]struct{} + done bool + oldValue func(context.Context) (*Message, error) + predicates []predicate.Message } var _ ent.Mutation = (*MessageMutation)(nil) @@ -16647,6 +17876,91 @@ func (m *MessageMutation) ResetGroupID() { delete(m.clearedFields, message.FieldGroupID) } +// SetDispatchState sets the "dispatch_state" field. +func (m *MessageMutation) SetDispatchState(s string) { + m.dispatch_state = &s +} + +// DispatchState returns the value of the "dispatch_state" field in the mutation. +func (m *MessageMutation) DispatchState() (r string, exists bool) { + v := m.dispatch_state + if v == nil { + return + } + return *v, true +} + +// OldDispatchState returns the old "dispatch_state" field's value of the Message entity. +// If the Message object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *MessageMutation) OldDispatchState(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldDispatchState is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldDispatchState requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldDispatchState: %w", err) + } + return oldValue.DispatchState, nil +} + +// ResetDispatchState resets all changes to the "dispatch_state" field. +func (m *MessageMutation) ResetDispatchState() { + m.dispatch_state = nil +} + +// SetDispatchedAt sets the "dispatched_at" field. +func (m *MessageMutation) SetDispatchedAt(t time.Time) { + m.dispatched_at = &t +} + +// DispatchedAt returns the value of the "dispatched_at" field in the mutation. +func (m *MessageMutation) DispatchedAt() (r time.Time, exists bool) { + v := m.dispatched_at + if v == nil { + return + } + return *v, true +} + +// OldDispatchedAt returns the old "dispatched_at" field's value of the Message entity. +// If the Message object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *MessageMutation) OldDispatchedAt(ctx context.Context) (v *time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldDispatchedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldDispatchedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldDispatchedAt: %w", err) + } + return oldValue.DispatchedAt, nil +} + +// ClearDispatchedAt clears the value of the "dispatched_at" field. +func (m *MessageMutation) ClearDispatchedAt() { + m.dispatched_at = nil + m.clearedFields[message.FieldDispatchedAt] = struct{}{} +} + +// DispatchedAtCleared returns if the "dispatched_at" field was cleared in this mutation. +func (m *MessageMutation) DispatchedAtCleared() bool { + _, ok := m.clearedFields[message.FieldDispatchedAt] + return ok +} + +// ResetDispatchedAt resets all changes to the "dispatched_at" field. +func (m *MessageMutation) ResetDispatchedAt() { + m.dispatched_at = nil + delete(m.clearedFields, message.FieldDispatchedAt) +} + // SetCreated sets the "created" field. func (m *MessageMutation) SetCreated(t time.Time) { m.created = &t @@ -16717,7 +18031,7 @@ func (m *MessageMutation) Type() string { // order to get all numeric fields that were incremented/decremented, call // AddedFields(). func (m *MessageMutation) Fields() []string { - fields := make([]string, 0, 13) + fields := make([]string, 0, 15) if m.project_id != nil { fields = append(fields, message.FieldProjectID) } @@ -16754,6 +18068,12 @@ func (m *MessageMutation) Fields() []string { if m.group_id != nil { fields = append(fields, message.FieldGroupID) } + if m.dispatch_state != nil { + fields = append(fields, message.FieldDispatchState) + } + if m.dispatched_at != nil { + fields = append(fields, message.FieldDispatchedAt) + } if m.created != nil { fields = append(fields, message.FieldCreated) } @@ -16789,6 +18109,10 @@ func (m *MessageMutation) Field(name string) (ent.Value, bool) { return m.AgentID() case message.FieldGroupID: return m.GroupID() + case message.FieldDispatchState: + return m.DispatchState() + case message.FieldDispatchedAt: + return m.DispatchedAt() case message.FieldCreated: return m.Created() } @@ -16824,6 +18148,10 @@ func (m *MessageMutation) OldField(ctx context.Context, name string) (ent.Value, return m.OldAgentID(ctx) case message.FieldGroupID: return m.OldGroupID(ctx) + case message.FieldDispatchState: + return m.OldDispatchState(ctx) + case message.FieldDispatchedAt: + return m.OldDispatchedAt(ctx) case message.FieldCreated: return m.OldCreated(ctx) } @@ -16919,6 +18247,20 @@ func (m *MessageMutation) SetField(name string, value ent.Value) error { } m.SetGroupID(v) return nil + case message.FieldDispatchState: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetDispatchState(v) + return nil + case message.FieldDispatchedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetDispatchedAt(v) + return nil case message.FieldCreated: v, ok := value.(time.Time) if !ok { @@ -16968,6 +18310,9 @@ func (m *MessageMutation) ClearedFields() []string { if m.FieldCleared(message.FieldGroupID) { fields = append(fields, message.FieldGroupID) } + if m.FieldCleared(message.FieldDispatchedAt) { + fields = append(fields, message.FieldDispatchedAt) + } return fields } @@ -16994,6 +18339,9 @@ func (m *MessageMutation) ClearField(name string) error { case message.FieldGroupID: m.ClearGroupID() return nil + case message.FieldDispatchedAt: + m.ClearDispatchedAt() + return nil } return fmt.Errorf("unknown Message nullable field %s", name) } @@ -17038,6 +18386,12 @@ func (m *MessageMutation) ResetField(name string) error { case message.FieldGroupID: m.ResetGroupID() return nil + case message.FieldDispatchState: + m.ResetDispatchState() + return nil + case message.FieldDispatchedAt: + m.ResetDispatchedAt() + return nil case message.FieldCreated: m.ResetCreated() return nil @@ -22704,34 +24058,37 @@ func (m *ProjectSyncStateMutation) ResetEdge(name string) error { // RuntimeBrokerMutation represents an operation that mutates the RuntimeBroker nodes in the graph. type RuntimeBrokerMutation struct { config - op Op - typ string - id *uuid.UUID - name *string - slug *string - _type *string - mode *string - version *string - lock_version *int64 - addlock_version *int64 - status *string - connection_state *string - last_heartbeat *time.Time - capabilities *string - supported_harnesses *string - resources *string - runtimes *string - labels *string - annotations *string - endpoint *string - created_by *string - auto_provide *bool - created *time.Time - updated *time.Time - clearedFields map[string]struct{} - done bool - oldValue func(context.Context) (*RuntimeBroker, error) - predicates []predicate.RuntimeBroker + op Op + typ string + id *uuid.UUID + name *string + slug *string + _type *string + mode *string + version *string + lock_version *int64 + addlock_version *int64 + status *string + connection_state *string + last_heartbeat *time.Time + capabilities *string + supported_harnesses *string + resources *string + runtimes *string + labels *string + annotations *string + endpoint *string + created_by *string + auto_provide *bool + connected_hub_id *string + connected_session_id *string + connected_at *time.Time + created *time.Time + updated *time.Time + clearedFields map[string]struct{} + done bool + oldValue func(context.Context) (*RuntimeBroker, error) + predicates []predicate.RuntimeBroker } var _ ent.Mutation = (*RuntimeBrokerMutation)(nil) @@ -23649,6 +25006,153 @@ func (m *RuntimeBrokerMutation) ResetAutoProvide() { m.auto_provide = nil } +// SetConnectedHubID sets the "connected_hub_id" field. +func (m *RuntimeBrokerMutation) SetConnectedHubID(s string) { + m.connected_hub_id = &s +} + +// ConnectedHubID returns the value of the "connected_hub_id" field in the mutation. +func (m *RuntimeBrokerMutation) ConnectedHubID() (r string, exists bool) { + v := m.connected_hub_id + if v == nil { + return + } + return *v, true +} + +// OldConnectedHubID returns the old "connected_hub_id" field's value of the RuntimeBroker entity. +// If the RuntimeBroker object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *RuntimeBrokerMutation) OldConnectedHubID(ctx context.Context) (v *string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldConnectedHubID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldConnectedHubID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldConnectedHubID: %w", err) + } + return oldValue.ConnectedHubID, nil +} + +// ClearConnectedHubID clears the value of the "connected_hub_id" field. +func (m *RuntimeBrokerMutation) ClearConnectedHubID() { + m.connected_hub_id = nil + m.clearedFields[runtimebroker.FieldConnectedHubID] = struct{}{} +} + +// ConnectedHubIDCleared returns if the "connected_hub_id" field was cleared in this mutation. +func (m *RuntimeBrokerMutation) ConnectedHubIDCleared() bool { + _, ok := m.clearedFields[runtimebroker.FieldConnectedHubID] + return ok +} + +// ResetConnectedHubID resets all changes to the "connected_hub_id" field. +func (m *RuntimeBrokerMutation) ResetConnectedHubID() { + m.connected_hub_id = nil + delete(m.clearedFields, runtimebroker.FieldConnectedHubID) +} + +// SetConnectedSessionID sets the "connected_session_id" field. +func (m *RuntimeBrokerMutation) SetConnectedSessionID(s string) { + m.connected_session_id = &s +} + +// ConnectedSessionID returns the value of the "connected_session_id" field in the mutation. +func (m *RuntimeBrokerMutation) ConnectedSessionID() (r string, exists bool) { + v := m.connected_session_id + if v == nil { + return + } + return *v, true +} + +// OldConnectedSessionID returns the old "connected_session_id" field's value of the RuntimeBroker entity. +// If the RuntimeBroker object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *RuntimeBrokerMutation) OldConnectedSessionID(ctx context.Context) (v *string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldConnectedSessionID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldConnectedSessionID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldConnectedSessionID: %w", err) + } + return oldValue.ConnectedSessionID, nil +} + +// ClearConnectedSessionID clears the value of the "connected_session_id" field. +func (m *RuntimeBrokerMutation) ClearConnectedSessionID() { + m.connected_session_id = nil + m.clearedFields[runtimebroker.FieldConnectedSessionID] = struct{}{} +} + +// ConnectedSessionIDCleared returns if the "connected_session_id" field was cleared in this mutation. +func (m *RuntimeBrokerMutation) ConnectedSessionIDCleared() bool { + _, ok := m.clearedFields[runtimebroker.FieldConnectedSessionID] + return ok +} + +// ResetConnectedSessionID resets all changes to the "connected_session_id" field. +func (m *RuntimeBrokerMutation) ResetConnectedSessionID() { + m.connected_session_id = nil + delete(m.clearedFields, runtimebroker.FieldConnectedSessionID) +} + +// SetConnectedAt sets the "connected_at" field. +func (m *RuntimeBrokerMutation) SetConnectedAt(t time.Time) { + m.connected_at = &t +} + +// ConnectedAt returns the value of the "connected_at" field in the mutation. +func (m *RuntimeBrokerMutation) ConnectedAt() (r time.Time, exists bool) { + v := m.connected_at + if v == nil { + return + } + return *v, true +} + +// OldConnectedAt returns the old "connected_at" field's value of the RuntimeBroker entity. +// If the RuntimeBroker object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *RuntimeBrokerMutation) OldConnectedAt(ctx context.Context) (v *time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldConnectedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldConnectedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldConnectedAt: %w", err) + } + return oldValue.ConnectedAt, nil +} + +// ClearConnectedAt clears the value of the "connected_at" field. +func (m *RuntimeBrokerMutation) ClearConnectedAt() { + m.connected_at = nil + m.clearedFields[runtimebroker.FieldConnectedAt] = struct{}{} +} + +// ConnectedAtCleared returns if the "connected_at" field was cleared in this mutation. +func (m *RuntimeBrokerMutation) ConnectedAtCleared() bool { + _, ok := m.clearedFields[runtimebroker.FieldConnectedAt] + return ok +} + +// ResetConnectedAt resets all changes to the "connected_at" field. +func (m *RuntimeBrokerMutation) ResetConnectedAt() { + m.connected_at = nil + delete(m.clearedFields, runtimebroker.FieldConnectedAt) +} + // SetCreated sets the "created" field. func (m *RuntimeBrokerMutation) SetCreated(t time.Time) { m.created = &t @@ -23755,7 +25259,7 @@ func (m *RuntimeBrokerMutation) Type() string { // order to get all numeric fields that were incremented/decremented, call // AddedFields(). func (m *RuntimeBrokerMutation) Fields() []string { - fields := make([]string, 0, 20) + fields := make([]string, 0, 23) if m.name != nil { fields = append(fields, runtimebroker.FieldName) } @@ -23810,6 +25314,15 @@ func (m *RuntimeBrokerMutation) Fields() []string { if m.auto_provide != nil { fields = append(fields, runtimebroker.FieldAutoProvide) } + if m.connected_hub_id != nil { + fields = append(fields, runtimebroker.FieldConnectedHubID) + } + if m.connected_session_id != nil { + fields = append(fields, runtimebroker.FieldConnectedSessionID) + } + if m.connected_at != nil { + fields = append(fields, runtimebroker.FieldConnectedAt) + } if m.created != nil { fields = append(fields, runtimebroker.FieldCreated) } @@ -23860,6 +25373,12 @@ func (m *RuntimeBrokerMutation) Field(name string) (ent.Value, bool) { return m.CreatedBy() case runtimebroker.FieldAutoProvide: return m.AutoProvide() + case runtimebroker.FieldConnectedHubID: + return m.ConnectedHubID() + case runtimebroker.FieldConnectedSessionID: + return m.ConnectedSessionID() + case runtimebroker.FieldConnectedAt: + return m.ConnectedAt() case runtimebroker.FieldCreated: return m.Created() case runtimebroker.FieldUpdated: @@ -23909,6 +25428,12 @@ func (m *RuntimeBrokerMutation) OldField(ctx context.Context, name string) (ent. return m.OldCreatedBy(ctx) case runtimebroker.FieldAutoProvide: return m.OldAutoProvide(ctx) + case runtimebroker.FieldConnectedHubID: + return m.OldConnectedHubID(ctx) + case runtimebroker.FieldConnectedSessionID: + return m.OldConnectedSessionID(ctx) + case runtimebroker.FieldConnectedAt: + return m.OldConnectedAt(ctx) case runtimebroker.FieldCreated: return m.OldCreated(ctx) case runtimebroker.FieldUpdated: @@ -24048,6 +25573,27 @@ func (m *RuntimeBrokerMutation) SetField(name string, value ent.Value) error { } m.SetAutoProvide(v) return nil + case runtimebroker.FieldConnectedHubID: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetConnectedHubID(v) + return nil + case runtimebroker.FieldConnectedSessionID: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetConnectedSessionID(v) + return nil + case runtimebroker.FieldConnectedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetConnectedAt(v) + return nil case runtimebroker.FieldCreated: v, ok := value.(time.Time) if !ok { @@ -24140,6 +25686,15 @@ func (m *RuntimeBrokerMutation) ClearedFields() []string { if m.FieldCleared(runtimebroker.FieldCreatedBy) { fields = append(fields, runtimebroker.FieldCreatedBy) } + if m.FieldCleared(runtimebroker.FieldConnectedHubID) { + fields = append(fields, runtimebroker.FieldConnectedHubID) + } + if m.FieldCleared(runtimebroker.FieldConnectedSessionID) { + fields = append(fields, runtimebroker.FieldConnectedSessionID) + } + if m.FieldCleared(runtimebroker.FieldConnectedAt) { + fields = append(fields, runtimebroker.FieldConnectedAt) + } return fields } @@ -24187,6 +25742,15 @@ func (m *RuntimeBrokerMutation) ClearField(name string) error { case runtimebroker.FieldCreatedBy: m.ClearCreatedBy() return nil + case runtimebroker.FieldConnectedHubID: + m.ClearConnectedHubID() + return nil + case runtimebroker.FieldConnectedSessionID: + m.ClearConnectedSessionID() + return nil + case runtimebroker.FieldConnectedAt: + m.ClearConnectedAt() + return nil } return fmt.Errorf("unknown RuntimeBroker nullable field %s", name) } @@ -24249,6 +25813,15 @@ func (m *RuntimeBrokerMutation) ResetField(name string) error { case runtimebroker.FieldAutoProvide: m.ResetAutoProvide() return nil + case runtimebroker.FieldConnectedHubID: + m.ResetConnectedHubID() + return nil + case runtimebroker.FieldConnectedSessionID: + m.ResetConnectedSessionID() + return nil + case runtimebroker.FieldConnectedAt: + m.ResetConnectedAt() + return nil case runtimebroker.FieldCreated: m.ResetCreated() return nil diff --git a/pkg/ent/predicate/predicate.go b/pkg/ent/predicate/predicate.go index 894f3d7e3..bb8ac70de 100644 --- a/pkg/ent/predicate/predicate.go +++ b/pkg/ent/predicate/predicate.go @@ -18,6 +18,9 @@ type AllowListEntry func(*sql.Selector) // ApiKey is the predicate function for apikey builders. type ApiKey func(*sql.Selector) +// BrokerDispatch is the predicate function for brokerdispatch builders. +type BrokerDispatch func(*sql.Selector) + // BrokerJoinToken is the predicate function for brokerjointoken builders. type BrokerJoinToken func(*sql.Selector) diff --git a/pkg/ent/runtime.go b/pkg/ent/runtime.go index df13e271e..060050f6d 100644 --- a/pkg/ent/runtime.go +++ b/pkg/ent/runtime.go @@ -9,6 +9,7 @@ import ( "github.com/GoogleCloudPlatform/scion/pkg/ent/agent" "github.com/GoogleCloudPlatform/scion/pkg/ent/allowlistentry" "github.com/GoogleCloudPlatform/scion/pkg/ent/apikey" + "github.com/GoogleCloudPlatform/scion/pkg/ent/brokerdispatch" "github.com/GoogleCloudPlatform/scion/pkg/ent/brokerjointoken" "github.com/GoogleCloudPlatform/scion/pkg/ent/brokersecret" "github.com/GoogleCloudPlatform/scion/pkg/ent/envvar" @@ -163,6 +164,34 @@ func init() { apikeyDescID := apikeyFields[0].Descriptor() // apikey.DefaultID holds the default value on creation for the id field. apikey.DefaultID = apikeyDescID.Default.(func() uuid.UUID) + brokerdispatchFields := schema.BrokerDispatch{}.Fields() + _ = brokerdispatchFields + // brokerdispatchDescOp is the schema descriptor for op field. + brokerdispatchDescOp := brokerdispatchFields[5].Descriptor() + // brokerdispatch.OpValidator is a validator for the "op" field. It is called by the builders before save. + brokerdispatch.OpValidator = brokerdispatchDescOp.Validators[0].(func(string) error) + // brokerdispatchDescState is the schema descriptor for state field. + brokerdispatchDescState := brokerdispatchFields[7].Descriptor() + // brokerdispatch.DefaultState holds the default value on creation for the state field. + brokerdispatch.DefaultState = brokerdispatchDescState.Default.(string) + // brokerdispatchDescAttempts is the schema descriptor for attempts field. + brokerdispatchDescAttempts := brokerdispatchFields[10].Descriptor() + // brokerdispatch.DefaultAttempts holds the default value on creation for the attempts field. + brokerdispatch.DefaultAttempts = brokerdispatchDescAttempts.Default.(int) + // brokerdispatchDescCreatedAt is the schema descriptor for created_at field. + brokerdispatchDescCreatedAt := brokerdispatchFields[12].Descriptor() + // brokerdispatch.DefaultCreatedAt holds the default value on creation for the created_at field. + brokerdispatch.DefaultCreatedAt = brokerdispatchDescCreatedAt.Default.(func() time.Time) + // brokerdispatchDescUpdatedAt is the schema descriptor for updated_at field. + brokerdispatchDescUpdatedAt := brokerdispatchFields[13].Descriptor() + // brokerdispatch.DefaultUpdatedAt holds the default value on creation for the updated_at field. + brokerdispatch.DefaultUpdatedAt = brokerdispatchDescUpdatedAt.Default.(func() time.Time) + // brokerdispatch.UpdateDefaultUpdatedAt holds the default value on update for the updated_at field. + brokerdispatch.UpdateDefaultUpdatedAt = brokerdispatchDescUpdatedAt.UpdateDefault.(func() time.Time) + // brokerdispatchDescID is the schema descriptor for id field. + brokerdispatchDescID := brokerdispatchFields[0].Descriptor() + // brokerdispatch.DefaultID holds the default value on creation for the id field. + brokerdispatch.DefaultID = brokerdispatchDescID.Default.(func() uuid.UUID) brokerjointokenFields := schema.BrokerJoinToken{}.Fields() _ = brokerjointokenFields // brokerjointokenDescTokenHash is the schema descriptor for token_hash field. @@ -503,8 +532,12 @@ func init() { messageDescRead := messageFields[10].Descriptor() // message.DefaultRead holds the default value on creation for the read field. message.DefaultRead = messageDescRead.Default.(bool) + // messageDescDispatchState is the schema descriptor for dispatch_state field. + messageDescDispatchState := messageFields[13].Descriptor() + // message.DefaultDispatchState holds the default value on creation for the dispatch_state field. + message.DefaultDispatchState = messageDescDispatchState.Default.(string) // messageDescCreated is the schema descriptor for created field. - messageDescCreated := messageFields[13].Descriptor() + messageDescCreated := messageFields[15].Descriptor() // message.DefaultCreated holds the default value on creation for the created field. message.DefaultCreated = messageDescCreated.Default.(func() time.Time) // messageDescID is the schema descriptor for id field. @@ -680,11 +713,11 @@ func init() { // runtimebroker.DefaultAutoProvide holds the default value on creation for the auto_provide field. runtimebroker.DefaultAutoProvide = runtimebrokerDescAutoProvide.Default.(bool) // runtimebrokerDescCreated is the schema descriptor for created field. - runtimebrokerDescCreated := runtimebrokerFields[19].Descriptor() + runtimebrokerDescCreated := runtimebrokerFields[22].Descriptor() // runtimebroker.DefaultCreated holds the default value on creation for the created field. runtimebroker.DefaultCreated = runtimebrokerDescCreated.Default.(func() time.Time) // runtimebrokerDescUpdated is the schema descriptor for updated field. - runtimebrokerDescUpdated := runtimebrokerFields[20].Descriptor() + runtimebrokerDescUpdated := runtimebrokerFields[23].Descriptor() // runtimebroker.DefaultUpdated holds the default value on creation for the updated field. runtimebroker.DefaultUpdated = runtimebrokerDescUpdated.Default.(func() time.Time) // runtimebroker.UpdateDefaultUpdated holds the default value on update for the updated field. diff --git a/pkg/ent/runtimebroker.go b/pkg/ent/runtimebroker.go index ddcba587b..41a41a795 100644 --- a/pkg/ent/runtimebroker.go +++ b/pkg/ent/runtimebroker.go @@ -54,6 +54,12 @@ type RuntimeBroker struct { CreatedBy string `json:"created_by,omitempty"` // AutoProvide holds the value of the "auto_provide" field. AutoProvide bool `json:"auto_provide,omitempty"` + // ConnectedHubID holds the value of the "connected_hub_id" field. + ConnectedHubID *string `json:"connected_hub_id,omitempty"` + // ConnectedSessionID holds the value of the "connected_session_id" field. + ConnectedSessionID *string `json:"connected_session_id,omitempty"` + // ConnectedAt holds the value of the "connected_at" field. + ConnectedAt *time.Time `json:"connected_at,omitempty"` // Created holds the value of the "created" field. Created time.Time `json:"created,omitempty"` // Updated holds the value of the "updated" field. @@ -70,9 +76,9 @@ func (*RuntimeBroker) scanValues(columns []string) ([]any, error) { values[i] = new(sql.NullBool) case runtimebroker.FieldLockVersion: values[i] = new(sql.NullInt64) - case runtimebroker.FieldName, runtimebroker.FieldSlug, runtimebroker.FieldType, runtimebroker.FieldMode, runtimebroker.FieldVersion, runtimebroker.FieldStatus, runtimebroker.FieldConnectionState, runtimebroker.FieldCapabilities, runtimebroker.FieldSupportedHarnesses, runtimebroker.FieldResources, runtimebroker.FieldRuntimes, runtimebroker.FieldLabels, runtimebroker.FieldAnnotations, runtimebroker.FieldEndpoint, runtimebroker.FieldCreatedBy: + case runtimebroker.FieldName, runtimebroker.FieldSlug, runtimebroker.FieldType, runtimebroker.FieldMode, runtimebroker.FieldVersion, runtimebroker.FieldStatus, runtimebroker.FieldConnectionState, runtimebroker.FieldCapabilities, runtimebroker.FieldSupportedHarnesses, runtimebroker.FieldResources, runtimebroker.FieldRuntimes, runtimebroker.FieldLabels, runtimebroker.FieldAnnotations, runtimebroker.FieldEndpoint, runtimebroker.FieldCreatedBy, runtimebroker.FieldConnectedHubID, runtimebroker.FieldConnectedSessionID: values[i] = new(sql.NullString) - case runtimebroker.FieldLastHeartbeat, runtimebroker.FieldCreated, runtimebroker.FieldUpdated: + case runtimebroker.FieldLastHeartbeat, runtimebroker.FieldConnectedAt, runtimebroker.FieldCreated, runtimebroker.FieldUpdated: values[i] = new(sql.NullTime) case runtimebroker.FieldID: values[i] = new(uuid.UUID) @@ -206,6 +212,27 @@ func (_m *RuntimeBroker) assignValues(columns []string, values []any) error { } else if value.Valid { _m.AutoProvide = value.Bool } + case runtimebroker.FieldConnectedHubID: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field connected_hub_id", values[i]) + } else if value.Valid { + _m.ConnectedHubID = new(string) + *_m.ConnectedHubID = value.String + } + case runtimebroker.FieldConnectedSessionID: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field connected_session_id", values[i]) + } else if value.Valid { + _m.ConnectedSessionID = new(string) + *_m.ConnectedSessionID = value.String + } + case runtimebroker.FieldConnectedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field connected_at", values[i]) + } else if value.Valid { + _m.ConnectedAt = new(time.Time) + *_m.ConnectedAt = value.Time + } case runtimebroker.FieldCreated: if value, ok := values[i].(*sql.NullTime); !ok { return fmt.Errorf("unexpected type %T for field created", values[i]) @@ -310,6 +337,21 @@ func (_m *RuntimeBroker) String() string { builder.WriteString("auto_provide=") builder.WriteString(fmt.Sprintf("%v", _m.AutoProvide)) builder.WriteString(", ") + if v := _m.ConnectedHubID; v != nil { + builder.WriteString("connected_hub_id=") + builder.WriteString(*v) + } + builder.WriteString(", ") + if v := _m.ConnectedSessionID; v != nil { + builder.WriteString("connected_session_id=") + builder.WriteString(*v) + } + builder.WriteString(", ") + if v := _m.ConnectedAt; v != nil { + builder.WriteString("connected_at=") + builder.WriteString(v.Format(time.ANSIC)) + } + builder.WriteString(", ") builder.WriteString("created=") builder.WriteString(_m.Created.Format(time.ANSIC)) builder.WriteString(", ") diff --git a/pkg/ent/runtimebroker/runtimebroker.go b/pkg/ent/runtimebroker/runtimebroker.go index 529fe8f01..eae7dc3ff 100644 --- a/pkg/ent/runtimebroker/runtimebroker.go +++ b/pkg/ent/runtimebroker/runtimebroker.go @@ -50,6 +50,12 @@ const ( FieldCreatedBy = "created_by" // FieldAutoProvide holds the string denoting the auto_provide field in the database. FieldAutoProvide = "auto_provide" + // FieldConnectedHubID holds the string denoting the connected_hub_id field in the database. + FieldConnectedHubID = "connected_hub_id" + // FieldConnectedSessionID holds the string denoting the connected_session_id field in the database. + FieldConnectedSessionID = "connected_session_id" + // FieldConnectedAt holds the string denoting the connected_at field in the database. + FieldConnectedAt = "connected_at" // FieldCreated holds the string denoting the created field in the database. FieldCreated = "created" // FieldUpdated holds the string denoting the updated field in the database. @@ -79,6 +85,9 @@ var Columns = []string{ FieldEndpoint, FieldCreatedBy, FieldAutoProvide, + FieldConnectedHubID, + FieldConnectedSessionID, + FieldConnectedAt, FieldCreated, FieldUpdated, } @@ -216,6 +225,21 @@ func ByAutoProvide(opts ...sql.OrderTermOption) OrderOption { return sql.OrderByField(FieldAutoProvide, opts...).ToFunc() } +// ByConnectedHubID orders the results by the connected_hub_id field. +func ByConnectedHubID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldConnectedHubID, opts...).ToFunc() +} + +// ByConnectedSessionID orders the results by the connected_session_id field. +func ByConnectedSessionID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldConnectedSessionID, opts...).ToFunc() +} + +// ByConnectedAt orders the results by the connected_at field. +func ByConnectedAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldConnectedAt, opts...).ToFunc() +} + // ByCreated orders the results by the created field. func ByCreated(opts ...sql.OrderTermOption) OrderOption { return sql.OrderByField(FieldCreated, opts...).ToFunc() diff --git a/pkg/ent/runtimebroker/where.go b/pkg/ent/runtimebroker/where.go index b67733216..f81468889 100644 --- a/pkg/ent/runtimebroker/where.go +++ b/pkg/ent/runtimebroker/where.go @@ -145,6 +145,21 @@ func AutoProvide(v bool) predicate.RuntimeBroker { return predicate.RuntimeBroker(sql.FieldEQ(FieldAutoProvide, v)) } +// ConnectedHubID applies equality check predicate on the "connected_hub_id" field. It's identical to ConnectedHubIDEQ. +func ConnectedHubID(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldEQ(FieldConnectedHubID, v)) +} + +// ConnectedSessionID applies equality check predicate on the "connected_session_id" field. It's identical to ConnectedSessionIDEQ. +func ConnectedSessionID(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldEQ(FieldConnectedSessionID, v)) +} + +// ConnectedAt applies equality check predicate on the "connected_at" field. It's identical to ConnectedAtEQ. +func ConnectedAt(v time.Time) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldEQ(FieldConnectedAt, v)) +} + // Created applies equality check predicate on the "created" field. It's identical to CreatedEQ. func Created(v time.Time) predicate.RuntimeBroker { return predicate.RuntimeBroker(sql.FieldEQ(FieldCreated, v)) @@ -1330,6 +1345,206 @@ func AutoProvideNEQ(v bool) predicate.RuntimeBroker { return predicate.RuntimeBroker(sql.FieldNEQ(FieldAutoProvide, v)) } +// ConnectedHubIDEQ applies the EQ predicate on the "connected_hub_id" field. +func ConnectedHubIDEQ(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldEQ(FieldConnectedHubID, v)) +} + +// ConnectedHubIDNEQ applies the NEQ predicate on the "connected_hub_id" field. +func ConnectedHubIDNEQ(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldNEQ(FieldConnectedHubID, v)) +} + +// ConnectedHubIDIn applies the In predicate on the "connected_hub_id" field. +func ConnectedHubIDIn(vs ...string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldIn(FieldConnectedHubID, vs...)) +} + +// ConnectedHubIDNotIn applies the NotIn predicate on the "connected_hub_id" field. +func ConnectedHubIDNotIn(vs ...string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldNotIn(FieldConnectedHubID, vs...)) +} + +// ConnectedHubIDGT applies the GT predicate on the "connected_hub_id" field. +func ConnectedHubIDGT(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldGT(FieldConnectedHubID, v)) +} + +// ConnectedHubIDGTE applies the GTE predicate on the "connected_hub_id" field. +func ConnectedHubIDGTE(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldGTE(FieldConnectedHubID, v)) +} + +// ConnectedHubIDLT applies the LT predicate on the "connected_hub_id" field. +func ConnectedHubIDLT(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldLT(FieldConnectedHubID, v)) +} + +// ConnectedHubIDLTE applies the LTE predicate on the "connected_hub_id" field. +func ConnectedHubIDLTE(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldLTE(FieldConnectedHubID, v)) +} + +// ConnectedHubIDContains applies the Contains predicate on the "connected_hub_id" field. +func ConnectedHubIDContains(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldContains(FieldConnectedHubID, v)) +} + +// ConnectedHubIDHasPrefix applies the HasPrefix predicate on the "connected_hub_id" field. +func ConnectedHubIDHasPrefix(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldHasPrefix(FieldConnectedHubID, v)) +} + +// ConnectedHubIDHasSuffix applies the HasSuffix predicate on the "connected_hub_id" field. +func ConnectedHubIDHasSuffix(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldHasSuffix(FieldConnectedHubID, v)) +} + +// ConnectedHubIDIsNil applies the IsNil predicate on the "connected_hub_id" field. +func ConnectedHubIDIsNil() predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldIsNull(FieldConnectedHubID)) +} + +// ConnectedHubIDNotNil applies the NotNil predicate on the "connected_hub_id" field. +func ConnectedHubIDNotNil() predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldNotNull(FieldConnectedHubID)) +} + +// ConnectedHubIDEqualFold applies the EqualFold predicate on the "connected_hub_id" field. +func ConnectedHubIDEqualFold(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldEqualFold(FieldConnectedHubID, v)) +} + +// ConnectedHubIDContainsFold applies the ContainsFold predicate on the "connected_hub_id" field. +func ConnectedHubIDContainsFold(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldContainsFold(FieldConnectedHubID, v)) +} + +// ConnectedSessionIDEQ applies the EQ predicate on the "connected_session_id" field. +func ConnectedSessionIDEQ(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldEQ(FieldConnectedSessionID, v)) +} + +// ConnectedSessionIDNEQ applies the NEQ predicate on the "connected_session_id" field. +func ConnectedSessionIDNEQ(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldNEQ(FieldConnectedSessionID, v)) +} + +// ConnectedSessionIDIn applies the In predicate on the "connected_session_id" field. +func ConnectedSessionIDIn(vs ...string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldIn(FieldConnectedSessionID, vs...)) +} + +// ConnectedSessionIDNotIn applies the NotIn predicate on the "connected_session_id" field. +func ConnectedSessionIDNotIn(vs ...string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldNotIn(FieldConnectedSessionID, vs...)) +} + +// ConnectedSessionIDGT applies the GT predicate on the "connected_session_id" field. +func ConnectedSessionIDGT(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldGT(FieldConnectedSessionID, v)) +} + +// ConnectedSessionIDGTE applies the GTE predicate on the "connected_session_id" field. +func ConnectedSessionIDGTE(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldGTE(FieldConnectedSessionID, v)) +} + +// ConnectedSessionIDLT applies the LT predicate on the "connected_session_id" field. +func ConnectedSessionIDLT(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldLT(FieldConnectedSessionID, v)) +} + +// ConnectedSessionIDLTE applies the LTE predicate on the "connected_session_id" field. +func ConnectedSessionIDLTE(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldLTE(FieldConnectedSessionID, v)) +} + +// ConnectedSessionIDContains applies the Contains predicate on the "connected_session_id" field. +func ConnectedSessionIDContains(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldContains(FieldConnectedSessionID, v)) +} + +// ConnectedSessionIDHasPrefix applies the HasPrefix predicate on the "connected_session_id" field. +func ConnectedSessionIDHasPrefix(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldHasPrefix(FieldConnectedSessionID, v)) +} + +// ConnectedSessionIDHasSuffix applies the HasSuffix predicate on the "connected_session_id" field. +func ConnectedSessionIDHasSuffix(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldHasSuffix(FieldConnectedSessionID, v)) +} + +// ConnectedSessionIDIsNil applies the IsNil predicate on the "connected_session_id" field. +func ConnectedSessionIDIsNil() predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldIsNull(FieldConnectedSessionID)) +} + +// ConnectedSessionIDNotNil applies the NotNil predicate on the "connected_session_id" field. +func ConnectedSessionIDNotNil() predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldNotNull(FieldConnectedSessionID)) +} + +// ConnectedSessionIDEqualFold applies the EqualFold predicate on the "connected_session_id" field. +func ConnectedSessionIDEqualFold(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldEqualFold(FieldConnectedSessionID, v)) +} + +// ConnectedSessionIDContainsFold applies the ContainsFold predicate on the "connected_session_id" field. +func ConnectedSessionIDContainsFold(v string) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldContainsFold(FieldConnectedSessionID, v)) +} + +// ConnectedAtEQ applies the EQ predicate on the "connected_at" field. +func ConnectedAtEQ(v time.Time) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldEQ(FieldConnectedAt, v)) +} + +// ConnectedAtNEQ applies the NEQ predicate on the "connected_at" field. +func ConnectedAtNEQ(v time.Time) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldNEQ(FieldConnectedAt, v)) +} + +// ConnectedAtIn applies the In predicate on the "connected_at" field. +func ConnectedAtIn(vs ...time.Time) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldIn(FieldConnectedAt, vs...)) +} + +// ConnectedAtNotIn applies the NotIn predicate on the "connected_at" field. +func ConnectedAtNotIn(vs ...time.Time) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldNotIn(FieldConnectedAt, vs...)) +} + +// ConnectedAtGT applies the GT predicate on the "connected_at" field. +func ConnectedAtGT(v time.Time) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldGT(FieldConnectedAt, v)) +} + +// ConnectedAtGTE applies the GTE predicate on the "connected_at" field. +func ConnectedAtGTE(v time.Time) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldGTE(FieldConnectedAt, v)) +} + +// ConnectedAtLT applies the LT predicate on the "connected_at" field. +func ConnectedAtLT(v time.Time) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldLT(FieldConnectedAt, v)) +} + +// ConnectedAtLTE applies the LTE predicate on the "connected_at" field. +func ConnectedAtLTE(v time.Time) predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldLTE(FieldConnectedAt, v)) +} + +// ConnectedAtIsNil applies the IsNil predicate on the "connected_at" field. +func ConnectedAtIsNil() predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldIsNull(FieldConnectedAt)) +} + +// ConnectedAtNotNil applies the NotNil predicate on the "connected_at" field. +func ConnectedAtNotNil() predicate.RuntimeBroker { + return predicate.RuntimeBroker(sql.FieldNotNull(FieldConnectedAt)) +} + // CreatedEQ applies the EQ predicate on the "created" field. func CreatedEQ(v time.Time) predicate.RuntimeBroker { return predicate.RuntimeBroker(sql.FieldEQ(FieldCreated, v)) diff --git a/pkg/ent/runtimebroker_create.go b/pkg/ent/runtimebroker_create.go index f59f58467..26f4aaa55 100644 --- a/pkg/ent/runtimebroker_create.go +++ b/pkg/ent/runtimebroker_create.go @@ -260,6 +260,48 @@ func (_c *RuntimeBrokerCreate) SetNillableAutoProvide(v *bool) *RuntimeBrokerCre return _c } +// SetConnectedHubID sets the "connected_hub_id" field. +func (_c *RuntimeBrokerCreate) SetConnectedHubID(v string) *RuntimeBrokerCreate { + _c.mutation.SetConnectedHubID(v) + return _c +} + +// SetNillableConnectedHubID sets the "connected_hub_id" field if the given value is not nil. +func (_c *RuntimeBrokerCreate) SetNillableConnectedHubID(v *string) *RuntimeBrokerCreate { + if v != nil { + _c.SetConnectedHubID(*v) + } + return _c +} + +// SetConnectedSessionID sets the "connected_session_id" field. +func (_c *RuntimeBrokerCreate) SetConnectedSessionID(v string) *RuntimeBrokerCreate { + _c.mutation.SetConnectedSessionID(v) + return _c +} + +// SetNillableConnectedSessionID sets the "connected_session_id" field if the given value is not nil. +func (_c *RuntimeBrokerCreate) SetNillableConnectedSessionID(v *string) *RuntimeBrokerCreate { + if v != nil { + _c.SetConnectedSessionID(*v) + } + return _c +} + +// SetConnectedAt sets the "connected_at" field. +func (_c *RuntimeBrokerCreate) SetConnectedAt(v time.Time) *RuntimeBrokerCreate { + _c.mutation.SetConnectedAt(v) + return _c +} + +// SetNillableConnectedAt sets the "connected_at" field if the given value is not nil. +func (_c *RuntimeBrokerCreate) SetNillableConnectedAt(v *time.Time) *RuntimeBrokerCreate { + if v != nil { + _c.SetConnectedAt(*v) + } + return _c +} + // SetCreated sets the "created" field. func (_c *RuntimeBrokerCreate) SetCreated(v time.Time) *RuntimeBrokerCreate { _c.mutation.SetCreated(v) @@ -518,6 +560,18 @@ func (_c *RuntimeBrokerCreate) createSpec() (*RuntimeBroker, *sqlgraph.CreateSpe _spec.SetField(runtimebroker.FieldAutoProvide, field.TypeBool, value) _node.AutoProvide = value } + if value, ok := _c.mutation.ConnectedHubID(); ok { + _spec.SetField(runtimebroker.FieldConnectedHubID, field.TypeString, value) + _node.ConnectedHubID = &value + } + if value, ok := _c.mutation.ConnectedSessionID(); ok { + _spec.SetField(runtimebroker.FieldConnectedSessionID, field.TypeString, value) + _node.ConnectedSessionID = &value + } + if value, ok := _c.mutation.ConnectedAt(); ok { + _spec.SetField(runtimebroker.FieldConnectedAt, field.TypeTime, value) + _node.ConnectedAt = &value + } if value, ok := _c.mutation.Created(); ok { _spec.SetField(runtimebroker.FieldCreated, field.TypeTime, value) _node.Created = value @@ -866,6 +920,60 @@ func (u *RuntimeBrokerUpsert) UpdateAutoProvide() *RuntimeBrokerUpsert { return u } +// SetConnectedHubID sets the "connected_hub_id" field. +func (u *RuntimeBrokerUpsert) SetConnectedHubID(v string) *RuntimeBrokerUpsert { + u.Set(runtimebroker.FieldConnectedHubID, v) + return u +} + +// UpdateConnectedHubID sets the "connected_hub_id" field to the value that was provided on create. +func (u *RuntimeBrokerUpsert) UpdateConnectedHubID() *RuntimeBrokerUpsert { + u.SetExcluded(runtimebroker.FieldConnectedHubID) + return u +} + +// ClearConnectedHubID clears the value of the "connected_hub_id" field. +func (u *RuntimeBrokerUpsert) ClearConnectedHubID() *RuntimeBrokerUpsert { + u.SetNull(runtimebroker.FieldConnectedHubID) + return u +} + +// SetConnectedSessionID sets the "connected_session_id" field. +func (u *RuntimeBrokerUpsert) SetConnectedSessionID(v string) *RuntimeBrokerUpsert { + u.Set(runtimebroker.FieldConnectedSessionID, v) + return u +} + +// UpdateConnectedSessionID sets the "connected_session_id" field to the value that was provided on create. +func (u *RuntimeBrokerUpsert) UpdateConnectedSessionID() *RuntimeBrokerUpsert { + u.SetExcluded(runtimebroker.FieldConnectedSessionID) + return u +} + +// ClearConnectedSessionID clears the value of the "connected_session_id" field. +func (u *RuntimeBrokerUpsert) ClearConnectedSessionID() *RuntimeBrokerUpsert { + u.SetNull(runtimebroker.FieldConnectedSessionID) + return u +} + +// SetConnectedAt sets the "connected_at" field. +func (u *RuntimeBrokerUpsert) SetConnectedAt(v time.Time) *RuntimeBrokerUpsert { + u.Set(runtimebroker.FieldConnectedAt, v) + return u +} + +// UpdateConnectedAt sets the "connected_at" field to the value that was provided on create. +func (u *RuntimeBrokerUpsert) UpdateConnectedAt() *RuntimeBrokerUpsert { + u.SetExcluded(runtimebroker.FieldConnectedAt) + return u +} + +// ClearConnectedAt clears the value of the "connected_at" field. +func (u *RuntimeBrokerUpsert) ClearConnectedAt() *RuntimeBrokerUpsert { + u.SetNull(runtimebroker.FieldConnectedAt) + return u +} + // SetUpdated sets the "updated" field. func (u *RuntimeBrokerUpsert) SetUpdated(v time.Time) *RuntimeBrokerUpsert { u.Set(runtimebroker.FieldUpdated, v) @@ -1265,6 +1373,69 @@ func (u *RuntimeBrokerUpsertOne) UpdateAutoProvide() *RuntimeBrokerUpsertOne { }) } +// SetConnectedHubID sets the "connected_hub_id" field. +func (u *RuntimeBrokerUpsertOne) SetConnectedHubID(v string) *RuntimeBrokerUpsertOne { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.SetConnectedHubID(v) + }) +} + +// UpdateConnectedHubID sets the "connected_hub_id" field to the value that was provided on create. +func (u *RuntimeBrokerUpsertOne) UpdateConnectedHubID() *RuntimeBrokerUpsertOne { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.UpdateConnectedHubID() + }) +} + +// ClearConnectedHubID clears the value of the "connected_hub_id" field. +func (u *RuntimeBrokerUpsertOne) ClearConnectedHubID() *RuntimeBrokerUpsertOne { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.ClearConnectedHubID() + }) +} + +// SetConnectedSessionID sets the "connected_session_id" field. +func (u *RuntimeBrokerUpsertOne) SetConnectedSessionID(v string) *RuntimeBrokerUpsertOne { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.SetConnectedSessionID(v) + }) +} + +// UpdateConnectedSessionID sets the "connected_session_id" field to the value that was provided on create. +func (u *RuntimeBrokerUpsertOne) UpdateConnectedSessionID() *RuntimeBrokerUpsertOne { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.UpdateConnectedSessionID() + }) +} + +// ClearConnectedSessionID clears the value of the "connected_session_id" field. +func (u *RuntimeBrokerUpsertOne) ClearConnectedSessionID() *RuntimeBrokerUpsertOne { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.ClearConnectedSessionID() + }) +} + +// SetConnectedAt sets the "connected_at" field. +func (u *RuntimeBrokerUpsertOne) SetConnectedAt(v time.Time) *RuntimeBrokerUpsertOne { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.SetConnectedAt(v) + }) +} + +// UpdateConnectedAt sets the "connected_at" field to the value that was provided on create. +func (u *RuntimeBrokerUpsertOne) UpdateConnectedAt() *RuntimeBrokerUpsertOne { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.UpdateConnectedAt() + }) +} + +// ClearConnectedAt clears the value of the "connected_at" field. +func (u *RuntimeBrokerUpsertOne) ClearConnectedAt() *RuntimeBrokerUpsertOne { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.ClearConnectedAt() + }) +} + // SetUpdated sets the "updated" field. func (u *RuntimeBrokerUpsertOne) SetUpdated(v time.Time) *RuntimeBrokerUpsertOne { return u.Update(func(s *RuntimeBrokerUpsert) { @@ -1833,6 +2004,69 @@ func (u *RuntimeBrokerUpsertBulk) UpdateAutoProvide() *RuntimeBrokerUpsertBulk { }) } +// SetConnectedHubID sets the "connected_hub_id" field. +func (u *RuntimeBrokerUpsertBulk) SetConnectedHubID(v string) *RuntimeBrokerUpsertBulk { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.SetConnectedHubID(v) + }) +} + +// UpdateConnectedHubID sets the "connected_hub_id" field to the value that was provided on create. +func (u *RuntimeBrokerUpsertBulk) UpdateConnectedHubID() *RuntimeBrokerUpsertBulk { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.UpdateConnectedHubID() + }) +} + +// ClearConnectedHubID clears the value of the "connected_hub_id" field. +func (u *RuntimeBrokerUpsertBulk) ClearConnectedHubID() *RuntimeBrokerUpsertBulk { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.ClearConnectedHubID() + }) +} + +// SetConnectedSessionID sets the "connected_session_id" field. +func (u *RuntimeBrokerUpsertBulk) SetConnectedSessionID(v string) *RuntimeBrokerUpsertBulk { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.SetConnectedSessionID(v) + }) +} + +// UpdateConnectedSessionID sets the "connected_session_id" field to the value that was provided on create. +func (u *RuntimeBrokerUpsertBulk) UpdateConnectedSessionID() *RuntimeBrokerUpsertBulk { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.UpdateConnectedSessionID() + }) +} + +// ClearConnectedSessionID clears the value of the "connected_session_id" field. +func (u *RuntimeBrokerUpsertBulk) ClearConnectedSessionID() *RuntimeBrokerUpsertBulk { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.ClearConnectedSessionID() + }) +} + +// SetConnectedAt sets the "connected_at" field. +func (u *RuntimeBrokerUpsertBulk) SetConnectedAt(v time.Time) *RuntimeBrokerUpsertBulk { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.SetConnectedAt(v) + }) +} + +// UpdateConnectedAt sets the "connected_at" field to the value that was provided on create. +func (u *RuntimeBrokerUpsertBulk) UpdateConnectedAt() *RuntimeBrokerUpsertBulk { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.UpdateConnectedAt() + }) +} + +// ClearConnectedAt clears the value of the "connected_at" field. +func (u *RuntimeBrokerUpsertBulk) ClearConnectedAt() *RuntimeBrokerUpsertBulk { + return u.Update(func(s *RuntimeBrokerUpsert) { + s.ClearConnectedAt() + }) +} + // SetUpdated sets the "updated" field. func (u *RuntimeBrokerUpsertBulk) SetUpdated(v time.Time) *RuntimeBrokerUpsertBulk { return u.Update(func(s *RuntimeBrokerUpsert) { diff --git a/pkg/ent/runtimebroker_update.go b/pkg/ent/runtimebroker_update.go index 680b13fa0..ebbd7ee22 100644 --- a/pkg/ent/runtimebroker_update.go +++ b/pkg/ent/runtimebroker_update.go @@ -353,6 +353,66 @@ func (_u *RuntimeBrokerUpdate) SetNillableAutoProvide(v *bool) *RuntimeBrokerUpd return _u } +// SetConnectedHubID sets the "connected_hub_id" field. +func (_u *RuntimeBrokerUpdate) SetConnectedHubID(v string) *RuntimeBrokerUpdate { + _u.mutation.SetConnectedHubID(v) + return _u +} + +// SetNillableConnectedHubID sets the "connected_hub_id" field if the given value is not nil. +func (_u *RuntimeBrokerUpdate) SetNillableConnectedHubID(v *string) *RuntimeBrokerUpdate { + if v != nil { + _u.SetConnectedHubID(*v) + } + return _u +} + +// ClearConnectedHubID clears the value of the "connected_hub_id" field. +func (_u *RuntimeBrokerUpdate) ClearConnectedHubID() *RuntimeBrokerUpdate { + _u.mutation.ClearConnectedHubID() + return _u +} + +// SetConnectedSessionID sets the "connected_session_id" field. +func (_u *RuntimeBrokerUpdate) SetConnectedSessionID(v string) *RuntimeBrokerUpdate { + _u.mutation.SetConnectedSessionID(v) + return _u +} + +// SetNillableConnectedSessionID sets the "connected_session_id" field if the given value is not nil. +func (_u *RuntimeBrokerUpdate) SetNillableConnectedSessionID(v *string) *RuntimeBrokerUpdate { + if v != nil { + _u.SetConnectedSessionID(*v) + } + return _u +} + +// ClearConnectedSessionID clears the value of the "connected_session_id" field. +func (_u *RuntimeBrokerUpdate) ClearConnectedSessionID() *RuntimeBrokerUpdate { + _u.mutation.ClearConnectedSessionID() + return _u +} + +// SetConnectedAt sets the "connected_at" field. +func (_u *RuntimeBrokerUpdate) SetConnectedAt(v time.Time) *RuntimeBrokerUpdate { + _u.mutation.SetConnectedAt(v) + return _u +} + +// SetNillableConnectedAt sets the "connected_at" field if the given value is not nil. +func (_u *RuntimeBrokerUpdate) SetNillableConnectedAt(v *time.Time) *RuntimeBrokerUpdate { + if v != nil { + _u.SetConnectedAt(*v) + } + return _u +} + +// ClearConnectedAt clears the value of the "connected_at" field. +func (_u *RuntimeBrokerUpdate) ClearConnectedAt() *RuntimeBrokerUpdate { + _u.mutation.ClearConnectedAt() + return _u +} + // SetUpdated sets the "updated" field. func (_u *RuntimeBrokerUpdate) SetUpdated(v time.Time) *RuntimeBrokerUpdate { _u.mutation.SetUpdated(v) @@ -517,6 +577,24 @@ func (_u *RuntimeBrokerUpdate) sqlSave(ctx context.Context) (_node int, err erro if value, ok := _u.mutation.AutoProvide(); ok { _spec.SetField(runtimebroker.FieldAutoProvide, field.TypeBool, value) } + if value, ok := _u.mutation.ConnectedHubID(); ok { + _spec.SetField(runtimebroker.FieldConnectedHubID, field.TypeString, value) + } + if _u.mutation.ConnectedHubIDCleared() { + _spec.ClearField(runtimebroker.FieldConnectedHubID, field.TypeString) + } + if value, ok := _u.mutation.ConnectedSessionID(); ok { + _spec.SetField(runtimebroker.FieldConnectedSessionID, field.TypeString, value) + } + if _u.mutation.ConnectedSessionIDCleared() { + _spec.ClearField(runtimebroker.FieldConnectedSessionID, field.TypeString) + } + if value, ok := _u.mutation.ConnectedAt(); ok { + _spec.SetField(runtimebroker.FieldConnectedAt, field.TypeTime, value) + } + if _u.mutation.ConnectedAtCleared() { + _spec.ClearField(runtimebroker.FieldConnectedAt, field.TypeTime) + } if value, ok := _u.mutation.Updated(); ok { _spec.SetField(runtimebroker.FieldUpdated, field.TypeTime, value) } @@ -865,6 +943,66 @@ func (_u *RuntimeBrokerUpdateOne) SetNillableAutoProvide(v *bool) *RuntimeBroker return _u } +// SetConnectedHubID sets the "connected_hub_id" field. +func (_u *RuntimeBrokerUpdateOne) SetConnectedHubID(v string) *RuntimeBrokerUpdateOne { + _u.mutation.SetConnectedHubID(v) + return _u +} + +// SetNillableConnectedHubID sets the "connected_hub_id" field if the given value is not nil. +func (_u *RuntimeBrokerUpdateOne) SetNillableConnectedHubID(v *string) *RuntimeBrokerUpdateOne { + if v != nil { + _u.SetConnectedHubID(*v) + } + return _u +} + +// ClearConnectedHubID clears the value of the "connected_hub_id" field. +func (_u *RuntimeBrokerUpdateOne) ClearConnectedHubID() *RuntimeBrokerUpdateOne { + _u.mutation.ClearConnectedHubID() + return _u +} + +// SetConnectedSessionID sets the "connected_session_id" field. +func (_u *RuntimeBrokerUpdateOne) SetConnectedSessionID(v string) *RuntimeBrokerUpdateOne { + _u.mutation.SetConnectedSessionID(v) + return _u +} + +// SetNillableConnectedSessionID sets the "connected_session_id" field if the given value is not nil. +func (_u *RuntimeBrokerUpdateOne) SetNillableConnectedSessionID(v *string) *RuntimeBrokerUpdateOne { + if v != nil { + _u.SetConnectedSessionID(*v) + } + return _u +} + +// ClearConnectedSessionID clears the value of the "connected_session_id" field. +func (_u *RuntimeBrokerUpdateOne) ClearConnectedSessionID() *RuntimeBrokerUpdateOne { + _u.mutation.ClearConnectedSessionID() + return _u +} + +// SetConnectedAt sets the "connected_at" field. +func (_u *RuntimeBrokerUpdateOne) SetConnectedAt(v time.Time) *RuntimeBrokerUpdateOne { + _u.mutation.SetConnectedAt(v) + return _u +} + +// SetNillableConnectedAt sets the "connected_at" field if the given value is not nil. +func (_u *RuntimeBrokerUpdateOne) SetNillableConnectedAt(v *time.Time) *RuntimeBrokerUpdateOne { + if v != nil { + _u.SetConnectedAt(*v) + } + return _u +} + +// ClearConnectedAt clears the value of the "connected_at" field. +func (_u *RuntimeBrokerUpdateOne) ClearConnectedAt() *RuntimeBrokerUpdateOne { + _u.mutation.ClearConnectedAt() + return _u +} + // SetUpdated sets the "updated" field. func (_u *RuntimeBrokerUpdateOne) SetUpdated(v time.Time) *RuntimeBrokerUpdateOne { _u.mutation.SetUpdated(v) @@ -1059,6 +1197,24 @@ func (_u *RuntimeBrokerUpdateOne) sqlSave(ctx context.Context) (_node *RuntimeBr if value, ok := _u.mutation.AutoProvide(); ok { _spec.SetField(runtimebroker.FieldAutoProvide, field.TypeBool, value) } + if value, ok := _u.mutation.ConnectedHubID(); ok { + _spec.SetField(runtimebroker.FieldConnectedHubID, field.TypeString, value) + } + if _u.mutation.ConnectedHubIDCleared() { + _spec.ClearField(runtimebroker.FieldConnectedHubID, field.TypeString) + } + if value, ok := _u.mutation.ConnectedSessionID(); ok { + _spec.SetField(runtimebroker.FieldConnectedSessionID, field.TypeString, value) + } + if _u.mutation.ConnectedSessionIDCleared() { + _spec.ClearField(runtimebroker.FieldConnectedSessionID, field.TypeString) + } + if value, ok := _u.mutation.ConnectedAt(); ok { + _spec.SetField(runtimebroker.FieldConnectedAt, field.TypeTime, value) + } + if _u.mutation.ConnectedAtCleared() { + _spec.ClearField(runtimebroker.FieldConnectedAt, field.TypeTime) + } if value, ok := _u.mutation.Updated(); ok { _spec.SetField(runtimebroker.FieldUpdated, field.TypeTime, value) } diff --git a/pkg/ent/schema/brokerdispatch.go b/pkg/ent/schema/brokerdispatch.go new file mode 100644 index 000000000..43ddf70e0 --- /dev/null +++ b/pkg/ent/schema/brokerdispatch.go @@ -0,0 +1,99 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package schema + +import ( + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/entsql" + "entgo.io/ent/schema" + "entgo.io/ent/schema/field" + "entgo.io/ent/schema/index" + "github.com/google/uuid" +) + +// BrokerDispatch holds the schema definition for the BrokerDispatch entity — the +// durable intent table for the "DB as state machine, NOTIFY as the signal" +// dispatch model (design §5.2). A row records a lifecycle/create-time command +// targeted at a broker; the socket-holding node reconciles it (claim → run local +// tunnel op → mark done/failed). `args`/`result` are TEXT (JSON) to stay +// dialect-neutral and keep secrets out of NOTIFY payloads. +type BrokerDispatch struct { + ent.Schema +} + +// Fields of the BrokerDispatch. +func (BrokerDispatch) Fields() []ent.Field { + return []ent.Field{ + field.UUID("id", uuid.UUID{}). + Default(uuid.New). + Immutable(), + field.UUID("broker_id", uuid.UUID{}), + // agent_id is null for project-scoped ops (e.g. create-with-gather). + field.UUID("agent_id", uuid.UUID{}). + Optional(). + Nillable(), + field.String("agent_slug"). + Optional(), + field.UUID("project_id", uuid.UUID{}). + Optional(). + Nillable(), + // op: start|stop|restart|delete|finalize_env|check_prompt|create|message + field.String("op"). + NotEmpty(), + // args: JSON; bulky/secret-bearing fields (resolvedEnv, resolvedSecrets, + // inlineConfig, structured bodies) live here, NOT in the NOTIFY payload. + field.String("args"). + Optional(), + // state: pending|in_progress|done|failed + field.String("state"). + Default("pending"), + // result: JSON; for ops that return data (check_prompt, env-gather). + field.String("result"). + Optional(), + // claimed_by: hub instanceID that reconciled this intent. + field.String("claimed_by"). + Optional(), + field.Int("attempts"). + Default(0), + field.String("error"). + Optional(), + field.Time("created_at"). + Default(time.Now). + Immutable(), + field.Time("updated_at"). + Default(time.Now). + UpdateDefault(time.Now), + field.Time("deadline_at"). + Optional(). + Nillable(), + } +} + +// Indexes of the BrokerDispatch. +func (BrokerDispatch) Indexes() []ent.Index { + return []ent.Index{ + // Drain query: WHERE broker_id=$X AND state='pending'. + index.Fields("broker_id", "state"), + } +} + +// Annotations of the BrokerDispatch. +func (BrokerDispatch) Annotations() []schema.Annotation { + return []schema.Annotation{ + entsql.Annotation{Table: "broker_dispatch"}, + } +} diff --git a/pkg/ent/schema/message.go b/pkg/ent/schema/message.go index a909e9884..8ff64e2cf 100644 --- a/pkg/ent/schema/message.go +++ b/pkg/ent/schema/message.go @@ -64,6 +64,14 @@ func (Message) Fields() []ent.Field { Optional(), field.String("group_id"). Optional(), + // dispatch_state tracks cross-node delivery (the message row IS the durable + // dispatch intent): pending|dispatched|failed. The owning node CAS-flips + // pending->dispatched after tunneling. Default keeps existing rows valid. + field.String("dispatch_state"). + Default("pending"), + field.Time("dispatched_at"). + Optional(). + Nillable(), field.Time("created"). Default(time.Now). Immutable(), diff --git a/pkg/ent/schema/runtimebroker.go b/pkg/ent/schema/runtimebroker.go index 5fcbc7fb2..e4e4290df 100644 --- a/pkg/ent/schema/runtimebroker.go +++ b/pkg/ent/schema/runtimebroker.go @@ -86,6 +86,15 @@ func (RuntimeBroker) Fields() []ent.Field { Optional(), field.Bool("auto_provide"). Default(false), + field.String("connected_hub_id"). + Optional(). + Nillable(), + field.String("connected_session_id"). + Optional(). + Nillable(), + field.Time("connected_at"). + Optional(). + Nillable(), field.Time("created"). Default(time.Now). Immutable(), diff --git a/pkg/ent/tx.go b/pkg/ent/tx.go index adf201cf2..64f55b502 100644 --- a/pkg/ent/tx.go +++ b/pkg/ent/tx.go @@ -20,6 +20,8 @@ type Tx struct { AllowListEntry *AllowListEntryClient // ApiKey is the client for interacting with the ApiKey builders. ApiKey *ApiKeyClient + // BrokerDispatch is the client for interacting with the BrokerDispatch builders. + BrokerDispatch *BrokerDispatchClient // BrokerJoinToken is the client for interacting with the BrokerJoinToken builders. BrokerJoinToken *BrokerJoinTokenClient // BrokerSecret is the client for interacting with the BrokerSecret builders. @@ -207,6 +209,7 @@ func (tx *Tx) init() { tx.Agent = NewAgentClient(tx.config) tx.AllowListEntry = NewAllowListEntryClient(tx.config) tx.ApiKey = NewApiKeyClient(tx.config) + tx.BrokerDispatch = NewBrokerDispatchClient(tx.config) tx.BrokerJoinToken = NewBrokerJoinTokenClient(tx.config) tx.BrokerSecret = NewBrokerSecretClient(tx.config) tx.EnvVar = NewEnvVarClient(tx.config) diff --git a/pkg/hub/broker_routing.go b/pkg/hub/broker_routing.go new file mode 100644 index 000000000..f90aa3d9c --- /dev/null +++ b/pkg/hub/broker_routing.go @@ -0,0 +1,122 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package hub + +import ( + "context" + "errors" + "time" + + "github.com/GoogleCloudPlatform/scion/pkg/store" +) + +// ErrMessageDeferred is returned by HybridBrokerClient.MessageAgent when the +// broker is not locally connected and has no HTTP endpoint: the message row +// (already persisted with dispatch_state=pending) is the durable intent, and +// the caller should emit a NOTIFY wakeup and return 202 Accepted. +var ErrMessageDeferred = errors.New("message deferred: broker not locally reachable") + +// ErrLifecycleDeferred is returned by HybridBrokerClient.StartAgent/StopAgent/ +// RestartAgent when the broker is not locally connected and has no HTTP +// endpoint. The caller should serialize resolved params into a broker_dispatch +// row, signal the owning node via the command bus, and wait for the resulting +// agent status transition (design §5.4, §6.2). +var ErrLifecycleDeferred = errors.New("lifecycle deferred: broker not locally reachable") + +// routeDecision is the outcome of HybridBrokerClient.route — how a dispatch for a +// broker should be delivered when this node does not hold the broker's socket. +type routeDecision int + +const ( + // routeLocal: this node holds the broker's control-channel socket — tunnel + // directly (the unchanged, zero-added-latency fast path). + routeLocal routeDecision = iota + // routeForward: some other node is believed to own the broker (affinity hint + // is alive) — write durable intent + NOTIFY and let the owner self-select. + routeForward + // routeHTTP: no live owner, but the broker exposes a direct HTTP endpoint + // (direct-mode broker; existing fallback — rare under NAT'd deployments). + routeHTTP + // routeUndeliverable: no owner and no endpoint — write durable pending intent + // and return a retryable status; reconciled on the broker's next reconnect. + routeUndeliverable +) + +func (d routeDecision) String() string { + switch d { + case routeLocal: + return "local" + case routeForward: + return "forward" + case routeHTTP: + return "http" + default: + return "undeliverable" + } +} + +// defaultAffinityFreshness bounds how long a broker's last_heartbeat is trusted +// as "owner alive" for routing. Generous (a multiple of the heartbeat interval); +// a stale hint only costs one dispatch timeout before falling through, and the +// reaper (B5-1) clears dead owners. +const defaultAffinityFreshness = 90 * time.Second + +// route decides how to deliver a dispatch for brokerID. The local fast path is +// checked first and unchanged; affinity is consulted only to choose between +// forwarding (durable intent + signal) and fast-failing (design §5.3). The +// affinity lookup is a hint — a wrong "alive" costs one timeout (intent stays +// durable and reconciles later); a wrong "dead" is reaped by §7.1. +func (c *HybridBrokerClient) route(ctx context.Context, brokerID, brokerEndpoint string) routeDecision { + if c.controlChannel.manager.IsConnected(brokerID) { + return routeLocal + } + var owner string + var alive bool + if c.affinity != nil { + owner, alive = c.affinity(ctx, brokerID) + } + switch { + case owner != "" && alive: + return routeForward + case brokerEndpoint != "": + return routeHTTP + default: + return routeUndeliverable + } +} + +// SetAffinityLookup injects the affinity hint used by route(). Wired by the +// server to a store-backed lookup (StoreAffinityLookup). +func (c *HybridBrokerClient) SetAffinityLookup(fn func(ctx context.Context, brokerID string) (owner string, alive bool)) { + c.affinity = fn +} + +// StoreAffinityLookup returns an affinity lookup backed by runtime_brokers: the +// owner is connected_hub_id, and "alive" means last_heartbeat is within +// freshness. (Liveness is inferred from heartbeat freshness because there is no +// hub-to-hub addressability to ping a peer — design §5.3.) +func StoreAffinityLookup(st store.Store, freshness time.Duration) func(ctx context.Context, brokerID string) (string, bool) { + if freshness <= 0 { + freshness = defaultAffinityFreshness + } + return func(ctx context.Context, brokerID string) (string, bool) { + b, err := st.GetRuntimeBroker(ctx, brokerID) + if err != nil || b == nil || b.ConnectedHubID == nil || *b.ConnectedHubID == "" { + return "", false + } + alive := !b.LastHeartbeat.IsZero() && time.Since(b.LastHeartbeat) < freshness + return *b.ConnectedHubID, alive + } +} diff --git a/pkg/hub/broker_routing_test.go b/pkg/hub/broker_routing_test.go new file mode 100644 index 000000000..a3cbe773b --- /dev/null +++ b/pkg/hub/broker_routing_test.go @@ -0,0 +1,160 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package hub + +import ( + "context" + "log/slog" + "testing" + "time" + + "github.com/GoogleCloudPlatform/scion/pkg/api" + "github.com/GoogleCloudPlatform/scion/pkg/messages" + "github.com/stretchr/testify/assert" +) + +// fakeHTTPClient records calls to MessageAgent so we can verify the HTTP +// fallback path. Other methods are stubs. +type fakeHTTPClient struct { + messageAgentCalled bool +} + +func (f *fakeHTTPClient) MessageAgent(context.Context, string, string, string, string, string, bool, *messages.StructuredMessage) error { + f.messageAgentCalled = true + return nil +} + +// Stub implementations for the RuntimeBrokerClient interface — only MessageAgent matters. +func (f *fakeHTTPClient) CreateAgent(context.Context, string, string, *RemoteCreateAgentRequest) (*RemoteAgentResponse, error) { + return nil, nil +} +func (f *fakeHTTPClient) StartAgent(context.Context, string, string, string, string, string, string, string, string, map[string]string, []ResolvedSecret, *api.ScionConfig, []api.SharedDir, bool) (*RemoteAgentResponse, error) { + return nil, nil +} +func (f *fakeHTTPClient) StopAgent(context.Context, string, string, string, string) error { + return nil +} +func (f *fakeHTTPClient) RestartAgent(context.Context, string, string, string, string, map[string]string) error { + return nil +} +func (f *fakeHTTPClient) DeleteAgent(context.Context, string, string, string, string, bool, bool, bool, time.Time) error { + return nil +} +func (f *fakeHTTPClient) CheckAgentPrompt(context.Context, string, string, string, string) (bool, error) { + return false, nil +} +func (f *fakeHTTPClient) CreateAgentWithGather(context.Context, string, string, *RemoteCreateAgentRequest) (*RemoteAgentResponse, *RemoteEnvRequirementsResponse, error) { + return nil, nil, nil +} +func (f *fakeHTTPClient) FinalizeEnv(context.Context, string, string, string, map[string]string) (*RemoteAgentResponse, error) { + return nil, nil +} +func (f *fakeHTTPClient) GetAgentLogs(context.Context, string, string, string, string, int) (string, error) { + return "", nil +} +func (f *fakeHTTPClient) ExecAgent(context.Context, string, string, string, string, []string, int) (string, int, error) { + return "", 0, nil +} +func (f *fakeHTTPClient) CleanupProject(context.Context, string, string, string) error { + return nil +} + +func TestHybridBrokerClient_Route(t *testing.T) { + ctx := context.Background() + const localBroker = "broker-local" + + mgr := NewControlChannelManager(DefaultControlChannelConfig(), slog.Default()) + // Seed a live local socket for localBroker only. + mgr.mu.Lock() + mgr.connections[localBroker] = &BrokerConnection{brokerID: localBroker, sessionID: "s1"} + mgr.mu.Unlock() + + c := NewHybridBrokerClient(mgr, nil, nil, false) + + cases := []struct { + name string + brokerID string + endpoint string + affOwner string + affAlive bool + want routeDecision + }{ + {"local socket wins", localBroker, "", "", false, routeLocal}, + {"local wins even over alive affinity", localBroker, "http://x", "hubA", true, routeLocal}, + {"alive owner -> forward", "b1", "", "hubA", true, routeForward}, + {"alive owner -> forward (endpoint ignored)", "b1", "http://x", "hubA", true, routeForward}, + {"no owner, endpoint set -> http", "b2", "http://x", "", false, routeHTTP}, + {"stale owner, endpoint set -> http", "b3", "http://x", "hubA", false, routeHTTP}, + {"stale owner, no endpoint -> undeliverable", "b4", "", "hubA", false, routeUndeliverable}, + {"no owner, no endpoint -> undeliverable", "b5", "", "", false, routeUndeliverable}, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + c.SetAffinityLookup(func(context.Context, string) (string, bool) { return tc.affOwner, tc.affAlive }) + got := c.route(ctx, tc.brokerID, tc.endpoint) + assert.Equal(t, tc.want, got, "route(%s, endpoint=%q, owner=%q alive=%v)", tc.brokerID, tc.endpoint, tc.affOwner, tc.affAlive) + }) + } +} + +func TestHybridBrokerClient_Route_NilAffinityIsSafe(t *testing.T) { + mgr := NewControlChannelManager(DefaultControlChannelConfig(), slog.Default()) + c := NewHybridBrokerClient(mgr, nil, nil, false) + // No affinity lookup set: a non-local broker with no endpoint is undeliverable. + assert.Equal(t, routeUndeliverable, c.route(context.Background(), "b-none", "")) + assert.Equal(t, routeHTTP, c.route(context.Background(), "b-ep", "http://x")) +} + +func TestHybridBrokerClient_MessageAgent_RouteGate(t *testing.T) { + const localBroker = "broker-local" + const remoteBroker = "broker-remote" + + mgr := NewControlChannelManager(DefaultControlChannelConfig(), slog.Default()) + mgr.mu.Lock() + mgr.connections[localBroker] = &BrokerConnection{brokerID: localBroker, sessionID: "s1"} + mgr.mu.Unlock() + + httpClient := &fakeHTTPClient{} + c := NewHybridBrokerClient(mgr, httpClient, nil, false) + + t.Run("routeLocal uses control channel (not deferred)", func(t *testing.T) { + // Verify route() returns routeLocal for the locally connected broker. + // We don't call MessageAgent directly because the stub BrokerConnection + // doesn't have a real tunnel; the route decision is what matters. + got := c.route(context.Background(), localBroker, "") + assert.Equal(t, routeLocal, got, "should pick local tunnel for connected broker") + }) + + t.Run("routeHTTP delivers via HTTP client", func(t *testing.T) { + httpClient.messageAgentCalled = false + c.SetAffinityLookup(func(context.Context, string) (string, bool) { return "", false }) + err := c.MessageAgent(context.Background(), remoteBroker, "http://endpoint", "a1", "p1", "hi", false, nil) + assert.NoError(t, err) + assert.True(t, httpClient.messageAgentCalled, "HTTP fallback should be used") + }) + + t.Run("routeForward returns ErrMessageDeferred", func(t *testing.T) { + c.SetAffinityLookup(func(context.Context, string) (string, bool) { return "hubA", true }) + err := c.MessageAgent(context.Background(), remoteBroker, "", "a1", "p1", "hi", false, nil) + assert.ErrorIs(t, err, ErrMessageDeferred) + }) + + t.Run("routeUndeliverable returns ErrMessageDeferred", func(t *testing.T) { + c.SetAffinityLookup(func(context.Context, string) (string, bool) { return "", false }) + err := c.MessageAgent(context.Background(), remoteBroker, "", "a1", "p1", "hi", false, nil) + assert.ErrorIs(t, err, ErrMessageDeferred) + }) +} diff --git a/pkg/hub/command_bus.go b/pkg/hub/command_bus.go new file mode 100644 index 000000000..f504bc25f --- /dev/null +++ b/pkg/hub/command_bus.go @@ -0,0 +1,337 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package hub + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "log/slog" + "sync" + "time" + + "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgxpool" +) + +// CommandBus abstracts the inter-node command signal channel. The Postgres +// implementation LISTENs on scion_broker_cmd; the no-op implementation is +// used for SQLite (single-process, all brokers are local). +type CommandBus interface { + // NotifyBrokerCmd issues a NOTIFY signal inside the caller's transaction, + // so the signal commits atomically with the durable intent row. + NotifyBrokerCmd(ctx context.Context, tx pgExecutor, brokerID string) error + // SignalBrokerCmd is a best-effort NOTIFY using the bus's own pool (not + // tx-scoped). Used by the message dispatch path where the durable intent + // is the message row itself and the NOTIFY is only a wakeup hint. + SignalBrokerCmd(ctx context.Context, brokerID string) error + Close() +} + +const ( + // pgCommandChannel is the global Postgres NOTIFY channel for broker + // command signals. Every hub instance LISTENs on this single channel and + // filters by local ownership. + pgCommandChannel = "scion_broker_cmd" +) + +// cmdSignal is the JSON wire format for the NOTIFY payload on scion_broker_cmd. +// It is intentionally tiny: the durable command lives in the DB; this is only +// a wakeup. +type cmdSignal struct { + BrokerID string `json:"broker_id"` + Kind string `json:"kind"` +} + +// PostgresCommandBus is a sibling of PostgresEventPublisher that LISTENs on +// scion_broker_cmd for dispatch wakeup signals. It maintains its OWN pgx +// connection (listener) and pool (publisher) so dispatch and event-fanout are +// independently pooled (design §5.1). +// +// On receiving a signal the bus checks local ownership via the injected +// ownsLocally func: if this node holds the broker's WebSocket, it invokes the +// onSignal callback (which will be wired to the reconcile drain in B2-5). +type PostgresCommandBus struct { + pool *pgxpool.Pool + dsn string + log *slog.Logger + + ctx context.Context + cancel context.CancelFunc + wg sync.WaitGroup + + mu sync.RWMutex + ownsLocally func(brokerID string) bool + onSignal func(ctx context.Context, brokerID string) + onReconnect func() + closed bool +} + +var _ CommandBus = (*PostgresCommandBus)(nil) + +// NewPostgresCommandBus creates a command bus backed by Postgres LISTEN/NOTIFY. +// ownsLocally should return true when this process holds the broker's control- +// channel WebSocket (typically controlChannel.manager.IsConnected). onSignal +// is the reconcile callback invoked when a signal arrives for a locally-owned +// broker. +func NewPostgresCommandBus( + ctx context.Context, + dsn string, + ownsLocally func(brokerID string) bool, + onSignal func(ctx context.Context, brokerID string), + log *slog.Logger, +) (*PostgresCommandBus, error) { + if log == nil { + log = slog.Default() + } + if ownsLocally == nil { + ownsLocally = func(string) bool { return false } + } + if onSignal == nil { + onSignal = func(context.Context, string) {} + } + + poolCfg, err := pgxpool.ParseConfig(dsn) + if err != nil { + return nil, fmt.Errorf("parsing command bus dsn: %w", err) + } + applyEventPoolKeepalives(poolCfg) + + pool, err := pgxpool.NewWithConfig(ctx, poolCfg) + if err != nil { + return nil, fmt.Errorf("creating command bus pool: %w", err) + } + if err := pool.Ping(ctx); err != nil { + pool.Close() + return nil, fmt.Errorf("pinging postgres for command bus: %w", err) + } + + busCtx, cancel := context.WithCancel(context.Background()) + b := &PostgresCommandBus{ + pool: pool, + dsn: dsn, + log: log, + ctx: busCtx, + cancel: cancel, + ownsLocally: ownsLocally, + onSignal: onSignal, + } + + b.wg.Add(1) + go b.runListener() + + log.Info("Postgres command bus started", "channel", pgCommandChannel) + return b, nil +} + +// SetOnReconnect sets a callback invoked each time the listener reconnects +// after a connection loss. Used by B5-2 to increment a reconnects counter. +func (b *PostgresCommandBus) SetOnReconnect(fn func()) { + b.mu.Lock() + defer b.mu.Unlock() + b.onReconnect = fn +} + +// SetOnSignal replaces the reconcile callback. This allows wiring the +// reconcile drain (B2-5) after construction. +func (b *PostgresCommandBus) SetOnSignal(fn func(ctx context.Context, brokerID string)) { + b.mu.Lock() + defer b.mu.Unlock() + if fn == nil { + fn = func(context.Context, string) {} + } + b.onSignal = fn +} + +// NotifyBrokerCmd issues NOTIFY scion_broker_cmd inside the caller's +// transaction, so the signal commits atomically with the durable intent. +func (b *PostgresCommandBus) NotifyBrokerCmd(ctx context.Context, tx pgExecutor, brokerID string) error { + sig := cmdSignal{BrokerID: brokerID, Kind: "dispatch"} + payload, err := json.Marshal(sig) + if err != nil { + return fmt.Errorf("marshaling command signal: %w", err) + } + _, err = tx.Exec(ctx, `SELECT pg_notify($1, $2)`, pgCommandChannel, string(payload)) + if err != nil { + return fmt.Errorf("pg_notify on %s: %w", pgCommandChannel, err) + } + return nil +} + +// SignalBrokerCmd issues a best-effort NOTIFY using the bus's own pool. +func (b *PostgresCommandBus) SignalBrokerCmd(ctx context.Context, brokerID string) error { + return b.NotifyBrokerCmd(ctx, b.pool, brokerID) +} + +// Close stops the listener and releases the pool. +func (b *PostgresCommandBus) Close() { + b.mu.Lock() + if b.closed { + b.mu.Unlock() + return + } + b.closed = true + b.mu.Unlock() + + b.cancel() + b.wg.Wait() + b.pool.Close() +} + +// runListener mirrors PostgresEventPublisher.runListener: maintain a dedicated +// LISTEN connection with backoff-reconnect. +func (b *PostgresCommandBus) runListener() { + defer b.wg.Done() + + const ( + minBackoff = 250 * time.Millisecond + maxBackoff = 10 * time.Second + ) + backoff := minBackoff + firstConnect := true + + for { + if b.ctx.Err() != nil { + return + } + + conn, err := b.connectListener(b.ctx) + if err != nil { + if b.ctx.Err() != nil { + return + } + b.log.Warn("Command bus listener connect failed, retrying", "error", err, "backoff", backoff) + if !b.sleep(backoff) { + return + } + backoff = nextBackoff(backoff, maxBackoff) + continue + } + + if !firstConnect { + b.mu.RLock() + fn := b.onReconnect + b.mu.RUnlock() + if fn != nil { + fn() + } + } + firstConnect = false + b.log.Info("Command bus listener connected") + backoff = minBackoff + + loopErr := b.listenLoop(conn) + conn.Close(context.Background()) + + if b.ctx.Err() != nil { + return + } + + b.log.Warn("Command bus listener connection lost, reconnecting", "error", loopErr, "backoff", backoff) + if !b.sleep(backoff) { + return + } + backoff = nextBackoff(backoff, maxBackoff) + } +} + +// connectListener opens a dedicated LISTEN connection with TCP keepalives, +// reusing the same helper as PostgresEventPublisher. +func (b *PostgresCommandBus) connectListener(ctx context.Context) (*pgx.Conn, error) { + cc, err := pgx.ParseConfig(b.dsn) + if err != nil { + return nil, fmt.Errorf("parsing command bus listener dsn: %w", err) + } + applyConnKeepalives(cc) + return pgx.ConnectConfig(ctx, cc) +} + +// listenLoop LISTENs on scion_broker_cmd and dispatches signals. +func (b *PostgresCommandBus) listenLoop(conn *pgx.Conn) error { + if err := execListen(b.ctx, conn, "LISTEN", pgCommandChannel); err != nil { + return fmt.Errorf("LISTEN %s: %w", pgCommandChannel, err) + } + + for { + if b.ctx.Err() != nil { + return b.ctx.Err() + } + + waitCtx, cancel := context.WithTimeout(b.ctx, listenPollInterval) + notif, err := conn.WaitForNotification(waitCtx) + cancel() + if err != nil { + if errors.Is(err, context.DeadlineExceeded) { + continue + } + return err + } + + b.handleSignal(notif.Payload) + } +} + +// handleSignal decodes a command signal and, if this node owns the broker, +// invokes the reconcile callback. +func (b *PostgresCommandBus) handleSignal(payload string) { + var sig cmdSignal + if err := json.Unmarshal([]byte(payload), &sig); err != nil { + b.log.Error("Failed to decode command signal", "error", err) + return + } + + if sig.BrokerID == "" { + b.log.Warn("Command signal missing broker_id, ignoring") + return + } + + b.mu.RLock() + owns := b.ownsLocally(sig.BrokerID) + onSig := b.onSignal + b.mu.RUnlock() + + if !owns { + return + } + + b.log.Info("Command signal received for local broker, invoking reconcile", + "broker_id", sig.BrokerID, "kind", sig.Kind) + onSig(b.ctx, sig.BrokerID) +} + +// sleep waits for d or until the bus context is canceled. +func (b *PostgresCommandBus) sleep(d time.Duration) bool { + t := time.NewTimer(d) + defer t.Stop() + select { + case <-b.ctx.Done(): + return false + case <-t.C: + return true + } +} + +// --- No-op command bus for SQLite (single-process) --- + +// NoopCommandBus is a no-op CommandBus for the SQLite backend. In single- +// process mode every broker is local; no inter-node signal is needed. +type NoopCommandBus struct{} + +var _ CommandBus = NoopCommandBus{} + +func (NoopCommandBus) NotifyBrokerCmd(context.Context, pgExecutor, string) error { return nil } +func (NoopCommandBus) SignalBrokerCmd(context.Context, string) error { return nil } +func (NoopCommandBus) Close() {} diff --git a/pkg/hub/command_bus_test.go b/pkg/hub/command_bus_test.go new file mode 100644 index 000000000..b3494b708 --- /dev/null +++ b/pkg/hub/command_bus_test.go @@ -0,0 +1,476 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package hub + +import ( + "context" + "encoding/json" + "log/slog" + "sync" + "testing" + "time" + + "github.com/jackc/pgx/v5" +) + +// --- pure unit tests (no database required) --- + +// TestNotifyBrokerCmd_Payload verifies the SQL and JSON shape of a NOTIFY call +// issued by NotifyBrokerCmd, using the same recExec test double as the event +// publisher tests. +func TestNotifyBrokerCmd_Payload(t *testing.T) { + bus := &PostgresCommandBus{ + ctx: context.Background(), + } + + tx := &recExec{} + if err := bus.NotifyBrokerCmd(context.Background(), tx, "broker-123"); err != nil { + t.Fatalf("NotifyBrokerCmd: %v", err) + } + + calls := tx.notifyCalls() + if len(calls) != 1 { + t.Fatalf("expected 1 pg_notify call, got %d", len(calls)) + } + + channel := calls[0].args[0].(string) + if channel != pgCommandChannel { + t.Fatalf("channel = %q, want %q", channel, pgCommandChannel) + } + + var sig cmdSignal + payload := calls[0].args[1].(string) + if err := json.Unmarshal([]byte(payload), &sig); err != nil { + t.Fatalf("decode signal payload: %v", err) + } + if sig.BrokerID != "broker-123" { + t.Fatalf("broker_id = %q, want %q", sig.BrokerID, "broker-123") + } + if sig.Kind != "dispatch" { + t.Fatalf("kind = %q, want %q", sig.Kind, "dispatch") + } +} + +// TestHandleSignal_OwnsLocally verifies that handleSignal invokes the onSignal +// callback only when ownsLocally returns true. +func TestHandleSignal_OwnsLocally(t *testing.T) { + var mu sync.Mutex + var reconciled []string + + bus := &PostgresCommandBus{ + ctx: context.Background(), + log: slog.Default(), + ownsLocally: func(brokerID string) bool { + return brokerID == "local-broker" + }, + onSignal: func(_ context.Context, brokerID string) { + mu.Lock() + defer mu.Unlock() + reconciled = append(reconciled, brokerID) + }, + } + + // Signal for a locally-owned broker -> should invoke callback. + sig1, _ := json.Marshal(cmdSignal{BrokerID: "local-broker", Kind: "dispatch"}) + bus.handleSignal(string(sig1)) + + // Signal for a remote broker -> should be ignored. + sig2, _ := json.Marshal(cmdSignal{BrokerID: "remote-broker", Kind: "dispatch"}) + bus.handleSignal(string(sig2)) + + mu.Lock() + defer mu.Unlock() + if len(reconciled) != 1 { + t.Fatalf("expected 1 reconcile call, got %d", len(reconciled)) + } + if reconciled[0] != "local-broker" { + t.Fatalf("reconciled broker = %q, want %q", reconciled[0], "local-broker") + } +} + +// TestHandleSignal_EmptyBrokerID verifies signals with a missing broker_id are +// silently ignored. +func TestHandleSignal_EmptyBrokerID(t *testing.T) { + called := false + bus := &PostgresCommandBus{ + ctx: context.Background(), + log: slog.Default(), + ownsLocally: func(string) bool { return true }, + onSignal: func(context.Context, string) { called = true }, + } + + sig, _ := json.Marshal(cmdSignal{Kind: "dispatch"}) + bus.handleSignal(string(sig)) + + if called { + t.Fatal("onSignal should not be called for an empty broker_id") + } +} + +// TestHandleSignal_MalformedJSON verifies malformed payloads don't panic. +func TestHandleSignal_MalformedJSON(t *testing.T) { + called := false + bus := &PostgresCommandBus{ + ctx: context.Background(), + log: slog.Default(), + ownsLocally: func(string) bool { return true }, + onSignal: func(context.Context, string) { called = true }, + } + + bus.handleSignal("not valid json{{{") + + if called { + t.Fatal("onSignal should not be called for malformed JSON") + } +} + +// TestSetOnSignal verifies the reconcile callback can be replaced after +// construction. +func TestSetOnSignal(t *testing.T) { + var mu sync.Mutex + var called string + + bus := &PostgresCommandBus{ + ctx: context.Background(), + log: slog.Default(), + ownsLocally: func(string) bool { return true }, + onSignal: func(_ context.Context, id string) { mu.Lock(); called = "original-" + id; mu.Unlock() }, + } + + bus.SetOnSignal(func(_ context.Context, id string) { + mu.Lock() + called = "replaced-" + id + mu.Unlock() + }) + + sig, _ := json.Marshal(cmdSignal{BrokerID: "b1", Kind: "dispatch"}) + bus.handleSignal(string(sig)) + + mu.Lock() + defer mu.Unlock() + if called != "replaced-b1" { + t.Fatalf("called = %q, want %q", called, "replaced-b1") + } +} + +// TestNoopCommandBus_NotifyBrokerCmd verifies the no-op bus is a safe no-op. +func TestNoopCommandBus_NotifyBrokerCmd(t *testing.T) { + bus := NoopCommandBus{} + tx := &recExec{} + + if err := bus.NotifyBrokerCmd(context.Background(), tx, "any-broker"); err != nil { + t.Fatalf("NoopCommandBus.NotifyBrokerCmd: %v", err) + } + + // No SQL should have been issued. + if len(tx.notifyCalls()) != 0 { + t.Fatalf("NoopCommandBus should not issue any SQL, got %d calls", len(tx.notifyCalls())) + } + + // Close is a safe no-op. + bus.Close() +} + +// --- integration tests (require a live Postgres via SCION_TEST_POSTGRES_DSN) --- + +// TestCommandBusIntegration_SignalDelivery starts a real PostgresCommandBus and +// verifies a NOTIFY on scion_broker_cmd is received and invokes the callback. +func TestCommandBusIntegration_SignalDelivery(t *testing.T) { + dsn := requirePostgres(t) + ctx := context.Background() + + var mu sync.Mutex + var reconciled []string + + bus, err := NewPostgresCommandBus(ctx, dsn, + func(brokerID string) bool { return brokerID == "owned-broker" }, + func(_ context.Context, brokerID string) { + mu.Lock() + defer mu.Unlock() + reconciled = append(reconciled, brokerID) + }, + nil, + ) + if err != nil { + t.Fatalf("NewPostgresCommandBus: %v", err) + } + defer bus.Close() + + // Give the listener time to LISTEN. + time.Sleep(2 * listenPollInterval) + + // Publish a signal via a direct NOTIFY (simulating the tx-scoped path). + conn, err := pgx.Connect(ctx, dsn) + if err != nil { + t.Fatalf("connect: %v", err) + } + defer conn.Close(context.Background()) + + sig, _ := json.Marshal(cmdSignal{BrokerID: "owned-broker", Kind: "dispatch"}) + if _, err := conn.Exec(ctx, `SELECT pg_notify($1, $2)`, pgCommandChannel, string(sig)); err != nil { + t.Fatalf("pg_notify: %v", err) + } + + // Also send a signal for a non-owned broker; it should be ignored. + sig2, _ := json.Marshal(cmdSignal{BrokerID: "remote-broker", Kind: "dispatch"}) + if _, err := conn.Exec(ctx, `SELECT pg_notify($1, $2)`, pgCommandChannel, string(sig2)); err != nil { + t.Fatalf("pg_notify: %v", err) + } + + deadline := time.Now().Add(5 * time.Second) + for time.Now().Before(deadline) { + mu.Lock() + n := len(reconciled) + mu.Unlock() + if n >= 1 { + break + } + time.Sleep(100 * time.Millisecond) + } + + mu.Lock() + defer mu.Unlock() + if len(reconciled) != 1 { + t.Fatalf("expected exactly 1 reconcile, got %d: %v", len(reconciled), reconciled) + } + if reconciled[0] != "owned-broker" { + t.Fatalf("reconciled %q, want %q", reconciled[0], "owned-broker") + } +} + +// TestCommandBusIntegration_NotifyBrokerCmd verifies NotifyBrokerCmd publishes a +// signal inside a caller's transaction that is received by the listener. +func TestCommandBusIntegration_NotifyBrokerCmd(t *testing.T) { + dsn := requirePostgres(t) + ctx := context.Background() + + var mu sync.Mutex + var reconciled []string + + bus, err := NewPostgresCommandBus(ctx, dsn, + func(string) bool { return true }, + func(_ context.Context, brokerID string) { + mu.Lock() + defer mu.Unlock() + reconciled = append(reconciled, brokerID) + }, + nil, + ) + if err != nil { + t.Fatalf("NewPostgresCommandBus: %v", err) + } + defer bus.Close() + + time.Sleep(2 * listenPollInterval) + + // Use the bus's own pool to create a transaction. + tx, err := bus.pool.Begin(ctx) + if err != nil { + t.Fatalf("begin tx: %v", err) + } + if err := bus.NotifyBrokerCmd(ctx, tx, "txn-broker"); err != nil { + t.Fatalf("NotifyBrokerCmd: %v", err) + } + if err := tx.Commit(ctx); err != nil { + t.Fatalf("commit: %v", err) + } + + deadline := time.Now().Add(5 * time.Second) + for time.Now().Before(deadline) { + mu.Lock() + n := len(reconciled) + mu.Unlock() + if n >= 1 { + break + } + time.Sleep(100 * time.Millisecond) + } + + mu.Lock() + defer mu.Unlock() + if len(reconciled) != 1 { + t.Fatalf("expected 1 reconcile, got %d", len(reconciled)) + } + if reconciled[0] != "txn-broker" { + t.Fatalf("reconciled %q, want %q", reconciled[0], "txn-broker") + } +} + +// TestCommandBusIntegration_TransactionalRollback verifies that a NOTIFY enrolled +// in a rolled-back transaction is never delivered (mirrors the event publisher's +// transactional rollback test). +func TestCommandBusIntegration_TransactionalRollback(t *testing.T) { + dsn := requirePostgres(t) + ctx := context.Background() + + var mu sync.Mutex + var reconciled []string + + bus, err := NewPostgresCommandBus(ctx, dsn, + func(string) bool { return true }, + func(_ context.Context, brokerID string) { + mu.Lock() + defer mu.Unlock() + reconciled = append(reconciled, brokerID) + }, + nil, + ) + if err != nil { + t.Fatalf("NewPostgresCommandBus: %v", err) + } + defer bus.Close() + + time.Sleep(2 * listenPollInterval) + + // Rolled-back publish: must NOT be delivered. + txRollback, err := bus.pool.Begin(ctx) + if err != nil { + t.Fatalf("begin: %v", err) + } + if err := bus.NotifyBrokerCmd(ctx, txRollback, "rolled-back-broker"); err != nil { + t.Fatalf("NotifyBrokerCmd: %v", err) + } + if err := txRollback.Rollback(ctx); err != nil { + t.Fatalf("rollback: %v", err) + } + + // Wait to ensure no spurious delivery. + time.Sleep(2 * time.Second) + + mu.Lock() + n := len(reconciled) + mu.Unlock() + if n != 0 { + t.Fatalf("rolled-back signal was delivered: %v", reconciled) + } + + // Committed publish: must be delivered. + txCommit, err := bus.pool.Begin(ctx) + if err != nil { + t.Fatalf("begin: %v", err) + } + if err := bus.NotifyBrokerCmd(ctx, txCommit, "committed-broker"); err != nil { + t.Fatalf("NotifyBrokerCmd: %v", err) + } + if err := txCommit.Commit(ctx); err != nil { + t.Fatalf("commit: %v", err) + } + + deadline := time.Now().Add(5 * time.Second) + for time.Now().Before(deadline) { + mu.Lock() + n = len(reconciled) + mu.Unlock() + if n >= 1 { + break + } + time.Sleep(100 * time.Millisecond) + } + + mu.Lock() + defer mu.Unlock() + if len(reconciled) != 1 || reconciled[0] != "committed-broker" { + t.Fatalf("expected [committed-broker], got %v", reconciled) + } +} + +// TestCommandBusIntegration_Reconnect terminates the listener's backend +// connection and verifies the bus reconnects and resumes delivery. +func TestCommandBusIntegration_Reconnect(t *testing.T) { + dsn := requirePostgres(t) + ctx := context.Background() + + var mu sync.Mutex + var reconciled []string + + bus, err := NewPostgresCommandBus(ctx, dsn, + func(string) bool { return true }, + func(_ context.Context, brokerID string) { + mu.Lock() + defer mu.Unlock() + reconciled = append(reconciled, brokerID) + }, + nil, + ) + if err != nil { + t.Fatalf("NewPostgresCommandBus: %v", err) + } + defer bus.Close() + + time.Sleep(2 * listenPollInterval) + + // Forcibly terminate all LISTENing backends for this database. + if _, err := bus.pool.Exec(ctx, + `SELECT pg_terminate_backend(pid) FROM pg_stat_activity + WHERE query ILIKE 'LISTEN %' AND pid <> pg_backend_pid()`); err != nil { + t.Fatalf("terminate backends: %v", err) + } + + // Wait for reconnect + resubscribe. + time.Sleep(3 * time.Second) + + // Publish a signal after reconnect. + conn, err := pgx.Connect(ctx, dsn) + if err != nil { + t.Fatalf("connect: %v", err) + } + defer conn.Close(context.Background()) + + sig, _ := json.Marshal(cmdSignal{BrokerID: "after-reconnect", Kind: "dispatch"}) + if _, err := conn.Exec(ctx, `SELECT pg_notify($1, $2)`, pgCommandChannel, string(sig)); err != nil { + t.Fatalf("pg_notify: %v", err) + } + + deadline := time.Now().Add(5 * time.Second) + for time.Now().Before(deadline) { + mu.Lock() + n := len(reconciled) + mu.Unlock() + if n >= 1 { + break + } + time.Sleep(200 * time.Millisecond) + } + + mu.Lock() + defer mu.Unlock() + if len(reconciled) == 0 { + t.Fatal("expected delivery after reconnect, got none") + } + found := false + for _, id := range reconciled { + if id == "after-reconnect" { + found = true + break + } + } + if !found { + t.Fatalf("expected after-reconnect in reconciled, got %v", reconciled) + } +} + +// TestCommandBusIntegration_CloseIsIdempotent verifies double-close is safe. +func TestCommandBusIntegration_CloseIsIdempotent(t *testing.T) { + dsn := requirePostgres(t) + ctx := context.Background() + + bus, err := NewPostgresCommandBus(ctx, dsn, nil, nil, nil) + if err != nil { + t.Fatalf("NewPostgresCommandBus: %v", err) + } + bus.Close() + bus.Close() // must not panic +} + diff --git a/pkg/hub/controlchannel.go b/pkg/hub/controlchannel.go index 83faa1007..3408179c1 100644 --- a/pkg/hub/controlchannel.go +++ b/pkg/hub/controlchannel.go @@ -65,7 +65,7 @@ type ControlChannelManager struct { config ControlChannelConfig log *slog.Logger upgrader websocket.Upgrader - onDisconnect func(brokerID string) + onDisconnect func(brokerID, sessionID string) } // NewControlChannelManager creates a new control channel manager. @@ -86,8 +86,10 @@ func NewControlChannelManager(config ControlChannelConfig, log *slog.Logger) *Co } // SetOnDisconnect sets a callback that is invoked when a broker disconnects. -// The callback is called asynchronously after the connection is removed. -func (m *ControlChannelManager) SetOnDisconnect(fn func(brokerID string)) { +// The callback is called asynchronously after the connection is removed and +// receives the sessionID of the connection that dropped, so the handler can +// compare-and-clear affinity (avoiding the flap clobber race). +func (m *ControlChannelManager) SetOnDisconnect(fn func(brokerID, sessionID string)) { m.mu.Lock() defer m.mu.Unlock() m.onDisconnect = fn @@ -183,10 +185,12 @@ func (s *StreamProxy) Close() { } // HandleUpgrade upgrades an HTTP connection to a WebSocket control channel. -func (m *ControlChannelManager) HandleUpgrade(w http.ResponseWriter, r *http.Request, brokerID string) error { +// It returns the sessionID generated for the new connection so the caller can +// claim broker affinity for this exact session. +func (m *ControlChannelManager) HandleUpgrade(w http.ResponseWriter, r *http.Request, brokerID string) (string, error) { conn, err := m.upgrader.Upgrade(w, r, nil) if err != nil { - return fmt.Errorf("websocket upgrade failed: %w", err) + return "", fmt.Errorf("websocket upgrade failed: %w", err) } wsConn := wsprotocol.NewConnection(conn, wsprotocol.ConnectionConfig{ @@ -232,19 +236,19 @@ func (m *ControlChannelManager) HandleUpgrade(w http.ResponseWriter, r *http.Req if err := wsConn.WriteJSON(connectedMsg); err != nil { m.log.Error("Failed to send connected message", "brokerID", brokerID, "error", err) brokerConn.Close() - m.removeConnection(brokerID) - return err + m.removeConnection(brokerID, sessionID) + return "", err } - return nil + return sessionID, nil } // handleConnection handles messages from a connected broker. func (m *ControlChannelManager) handleConnection(hc *BrokerConnection) { defer func() { hc.Close() - m.removeConnection(hc.brokerID) - m.log.Info("Broker control channel disconnected", "brokerID", hc.brokerID) + m.removeConnection(hc.brokerID, hc.sessionID) + m.log.Info("Broker control channel disconnected", "brokerID", hc.brokerID, "sessionID", hc.sessionID) }() // Set up pong handler @@ -450,16 +454,23 @@ func (m *ControlChannelManager) pingLoop(hc *BrokerConnection) { } } -// removeConnection removes a broker connection from the manager. -func (m *ControlChannelManager) removeConnection(brokerID string) { +// removeConnection removes a broker connection from the manager. It only +// removes (and fires onDisconnect for) the entry if it is still THIS session: +// when a broker flaps, HandleUpgrade replaces the map entry with a newer +// session, and the older connection's teardown must not drop the live socket or +// stamp a spurious disconnect for the session that already moved on. +func (m *ControlChannelManager) removeConnection(brokerID, sessionID string) { m.mu.Lock() - _, existed := m.connections[brokerID] - delete(m.connections, brokerID) + cur, ok := m.connections[brokerID] + existed := ok && cur.sessionID == sessionID + if existed { + delete(m.connections, brokerID) + } cb := m.onDisconnect m.mu.Unlock() if cb != nil && existed { - go cb(brokerID) + go cb(brokerID, sessionID) } } diff --git a/pkg/hub/controlchannel_client.go b/pkg/hub/controlchannel_client.go index 8878a6ec7..bba8e4e8a 100644 --- a/pkg/hub/controlchannel_client.go +++ b/pkg/hub/controlchannel_client.go @@ -456,6 +456,11 @@ type HybridBrokerClient struct { controlChannel *ControlChannelBrokerClient httpClient RuntimeBrokerClient debug bool + // affinity returns the believed owning hub instanceID for a broker and + // whether that owner is alive (last_heartbeat fresh). It is a routing HINT + // only (correctness comes from durable intent + drain); injected so route() + // is unit-testable. Nil means "no affinity info" (treated as no owner). + affinity func(ctx context.Context, brokerID string) (owner string, alive bool) } // NewHybridBrokerClient creates a hybrid client that prefers control channel. @@ -480,60 +485,107 @@ func (c *HybridBrokerClient) CreateAgent(ctx context.Context, brokerID, brokerEn return c.httpClient.CreateAgent(ctx, brokerID, brokerEndpoint, req) } -// StartAgent starts an agent, preferring control channel. +// StartAgent starts an agent, using route() to decide the delivery path. +// routeLocal uses the control-channel tunnel (unchanged fast path), routeHTTP +// falls back to the broker's HTTP endpoint, and routeForward/routeUndeliverable +// return ErrLifecycleDeferred so the caller can write durable intent + wait. func (c *HybridBrokerClient) StartAgent(ctx context.Context, brokerID, brokerEndpoint, agentID, projectID, task, projectPath, projectSlug, harnessConfig string, resolvedEnv map[string]string, resolvedSecrets []ResolvedSecret, inlineConfig *api.ScionConfig, sharedDirs []api.SharedDir, sharedWorkspace bool) (*RemoteAgentResponse, error) { - if c.useControlChannel(brokerID) { + switch c.route(ctx, brokerID, brokerEndpoint) { + case routeLocal: return c.controlChannel.StartAgent(ctx, brokerID, brokerEndpoint, agentID, projectID, task, projectPath, projectSlug, harnessConfig, resolvedEnv, resolvedSecrets, inlineConfig, sharedDirs, sharedWorkspace) + case routeHTTP: + return c.httpClient.StartAgent(ctx, brokerID, brokerEndpoint, agentID, projectID, task, projectPath, projectSlug, harnessConfig, resolvedEnv, resolvedSecrets, inlineConfig, sharedDirs, sharedWorkspace) + default: + return nil, ErrLifecycleDeferred } - return c.httpClient.StartAgent(ctx, brokerID, brokerEndpoint, agentID, projectID, task, projectPath, projectSlug, harnessConfig, resolvedEnv, resolvedSecrets, inlineConfig, sharedDirs, sharedWorkspace) } -// StopAgent stops an agent, preferring control channel. +// StopAgent stops an agent, using route() to decide the delivery path. +// routeLocal uses the control-channel tunnel, routeHTTP falls back to HTTP, +// and routeForward/routeUndeliverable return ErrLifecycleDeferred. func (c *HybridBrokerClient) StopAgent(ctx context.Context, brokerID, brokerEndpoint, agentID, projectID string) error { - if c.useControlChannel(brokerID) { + switch c.route(ctx, brokerID, brokerEndpoint) { + case routeLocal: return c.controlChannel.StopAgent(ctx, brokerID, brokerEndpoint, agentID, projectID) + case routeHTTP: + return c.httpClient.StopAgent(ctx, brokerID, brokerEndpoint, agentID, projectID) + default: + return ErrLifecycleDeferred } - return c.httpClient.StopAgent(ctx, brokerID, brokerEndpoint, agentID, projectID) } -// RestartAgent restarts an agent, preferring control channel. +// RestartAgent restarts an agent, using route() to decide the delivery path. +// routeLocal uses the control-channel tunnel, routeHTTP falls back to HTTP, +// and routeForward/routeUndeliverable return ErrLifecycleDeferred. func (c *HybridBrokerClient) RestartAgent(ctx context.Context, brokerID, brokerEndpoint, agentID, projectID string, resolvedEnv map[string]string) error { - if c.useControlChannel(brokerID) { + switch c.route(ctx, brokerID, brokerEndpoint) { + case routeLocal: return c.controlChannel.RestartAgent(ctx, brokerID, brokerEndpoint, agentID, projectID, resolvedEnv) + case routeHTTP: + return c.httpClient.RestartAgent(ctx, brokerID, brokerEndpoint, agentID, projectID, resolvedEnv) + default: + return ErrLifecycleDeferred } - return c.httpClient.RestartAgent(ctx, brokerID, brokerEndpoint, agentID, projectID, resolvedEnv) } -// DeleteAgent deletes an agent, preferring control channel. +// DeleteAgent deletes an agent, using route() to decide the delivery path. +// routeLocal uses the control-channel tunnel, routeHTTP falls back to HTTP, +// and routeForward/routeUndeliverable return ErrLifecycleDeferred. func (c *HybridBrokerClient) DeleteAgent(ctx context.Context, brokerID, brokerEndpoint, agentID, projectID string, deleteFiles, removeBranch, softDelete bool, deletedAt time.Time) error { - if c.useControlChannel(brokerID) { + switch c.route(ctx, brokerID, brokerEndpoint) { + case routeLocal: return c.controlChannel.DeleteAgent(ctx, brokerID, brokerEndpoint, agentID, projectID, deleteFiles, removeBranch, softDelete, deletedAt) + case routeHTTP: + return c.httpClient.DeleteAgent(ctx, brokerID, brokerEndpoint, agentID, projectID, deleteFiles, removeBranch, softDelete, deletedAt) + default: + return ErrLifecycleDeferred } - return c.httpClient.DeleteAgent(ctx, brokerID, brokerEndpoint, agentID, projectID, deleteFiles, removeBranch, softDelete, deletedAt) } -// MessageAgent sends a message to an agent, preferring control channel. +// MessageAgent sends a message to an agent, using route() to decide the +// delivery path (B3-2). routeLocal uses the control-channel tunnel (unchanged +// fast path), routeHTTP falls back to the broker's HTTP endpoint, and +// routeForward/routeUndeliverable return ErrMessageDeferred so the caller +// can emit a NOTIFY wakeup and return 202 (the message row is durable). func (c *HybridBrokerClient) MessageAgent(ctx context.Context, brokerID, brokerEndpoint, agentID, projectID, message string, interrupt bool, structuredMsg *messages.StructuredMessage) error { - if c.useControlChannel(brokerID) { + switch c.route(ctx, brokerID, brokerEndpoint) { + case routeLocal: return c.controlChannel.MessageAgent(ctx, brokerID, brokerEndpoint, agentID, projectID, message, interrupt, structuredMsg) + case routeHTTP: + return c.httpClient.MessageAgent(ctx, brokerID, brokerEndpoint, agentID, projectID, message, interrupt, structuredMsg) + default: + return ErrMessageDeferred } - return c.httpClient.MessageAgent(ctx, brokerID, brokerEndpoint, agentID, projectID, message, interrupt, structuredMsg) } -// CheckAgentPrompt checks if an agent has a non-empty prompt.md file. +// CheckAgentPrompt checks if an agent has a non-empty prompt.md file, using +// route() to decide the delivery path. routeLocal uses the control-channel +// tunnel, routeHTTP falls back to HTTP, and routeForward/routeUndeliverable +// return ErrLifecycleDeferred so the caller can write durable intent + wait. func (c *HybridBrokerClient) CheckAgentPrompt(ctx context.Context, brokerID, brokerEndpoint, agentID, projectID string) (bool, error) { - if c.useControlChannel(brokerID) { + switch c.route(ctx, brokerID, brokerEndpoint) { + case routeLocal: return c.controlChannel.CheckAgentPrompt(ctx, brokerID, brokerEndpoint, agentID, projectID) + case routeHTTP: + return c.httpClient.CheckAgentPrompt(ctx, brokerID, brokerEndpoint, agentID, projectID) + default: + return false, ErrLifecycleDeferred } - return c.httpClient.CheckAgentPrompt(ctx, brokerID, brokerEndpoint, agentID, projectID) } -// CreateAgentWithGather creates an agent with env-gather support, preferring control channel. +// CreateAgentWithGather creates an agent with env-gather support, using route() +// to decide the delivery path. routeLocal uses the control-channel tunnel, +// routeHTTP falls back to HTTP, and routeForward/routeUndeliverable return +// ErrLifecycleDeferred so the caller can write durable intent + wait. func (c *HybridBrokerClient) CreateAgentWithGather(ctx context.Context, brokerID, brokerEndpoint string, req *RemoteCreateAgentRequest) (*RemoteAgentResponse, *RemoteEnvRequirementsResponse, error) { - if c.useControlChannel(brokerID) { + switch c.route(ctx, brokerID, brokerEndpoint) { + case routeLocal: return c.controlChannel.CreateAgentWithGather(ctx, brokerID, brokerEndpoint, req) + case routeHTTP: + return c.httpClient.CreateAgentWithGather(ctx, brokerID, brokerEndpoint, req) + default: + return nil, nil, ErrLifecycleDeferred } - return c.httpClient.CreateAgentWithGather(ctx, brokerID, brokerEndpoint, req) } // GetAgentLogs retrieves agent.log content, preferring control channel. @@ -559,10 +611,17 @@ func (c *HybridBrokerClient) CleanupProject(ctx context.Context, brokerID, broke return c.httpClient.CleanupProject(ctx, brokerID, brokerEndpoint, projectSlug) } -// FinalizeEnv sends gathered env vars to a broker, preferring control channel. +// FinalizeEnv sends gathered env vars to a broker, using route() to decide the +// delivery path. routeLocal uses the control-channel tunnel, routeHTTP falls +// back to HTTP, and routeForward/routeUndeliverable return ErrLifecycleDeferred +// so the caller can write durable intent + wait. func (c *HybridBrokerClient) FinalizeEnv(ctx context.Context, brokerID, brokerEndpoint, agentID string, env map[string]string) (*RemoteAgentResponse, error) { - if c.useControlChannel(brokerID) { + switch c.route(ctx, brokerID, brokerEndpoint) { + case routeLocal: return c.controlChannel.FinalizeEnv(ctx, brokerID, brokerEndpoint, agentID, env) + case routeHTTP: + return c.httpClient.FinalizeEnv(ctx, brokerID, brokerEndpoint, agentID, env) + default: + return nil, ErrLifecycleDeferred } - return c.httpClient.FinalizeEnv(ctx, brokerID, brokerEndpoint, agentID, env) } diff --git a/pkg/hub/controlchannel_test.go b/pkg/hub/controlchannel_test.go index e817cccfd..8e86380f9 100644 --- a/pkg/hub/controlchannel_test.go +++ b/pkg/hub/controlchannel_test.go @@ -29,21 +29,23 @@ func TestControlChannelManager_OnDisconnectCallback(t *testing.T) { var mu sync.Mutex var receivedBrokerID string + var receivedSessionID string done := make(chan struct{}) - mgr.SetOnDisconnect(func(brokerID string) { + mgr.SetOnDisconnect(func(brokerID, sessionID string) { mu.Lock() defer mu.Unlock() receivedBrokerID = brokerID + receivedSessionID = sessionID close(done) }) // Manually add a connection entry so removeConnection has something to remove mgr.mu.Lock() - mgr.connections[tid("broker-1")] = &BrokerConnection{brokerID: tid("broker-1")} + mgr.connections[tid("broker-1")] = &BrokerConnection{brokerID: tid("broker-1"), sessionID: "sess-1"} mgr.mu.Unlock() - mgr.removeConnection(tid("broker-1")) + mgr.removeConnection(tid("broker-1"), "sess-1") // Wait for async callback select { @@ -55,21 +57,54 @@ func TestControlChannelManager_OnDisconnectCallback(t *testing.T) { mu.Lock() defer mu.Unlock() assert.Equal(t, tid("broker-1"), receivedBrokerID) + assert.Equal(t, "sess-1", receivedSessionID) // Verify connection was removed require.False(t, mgr.IsConnected(tid("broker-1"))) } +// TestControlChannelManager_RemoveStaleSessionNoop verifies that a teardown for +// an OLD session does not remove a NEWER connection that replaced it (flap), and +// does not fire onDisconnect for the stale session. +func TestControlChannelManager_RemoveStaleSessionNoop(t *testing.T) { + mgr := NewControlChannelManager(DefaultControlChannelConfig(), slog.Default()) + + var fired bool + var mu sync.Mutex + mgr.SetOnDisconnect(func(brokerID, sessionID string) { + mu.Lock() + defer mu.Unlock() + fired = true + }) + + // Current live connection is session "new". + mgr.mu.Lock() + mgr.connections[tid("broker-1")] = &BrokerConnection{brokerID: tid("broker-1"), sessionID: "new"} + mgr.mu.Unlock() + + // The old session's teardown must be a no-op. + mgr.removeConnection(tid("broker-1"), "old") + + // Give any (erroneous) async callback a chance to run. + time.Sleep(100 * time.Millisecond) + + mu.Lock() + assert.False(t, fired, "onDisconnect must not fire for a stale session") + mu.Unlock() + // The live (new) connection must still be present. + require.True(t, mgr.IsConnected(tid("broker-1"))) +} + func TestControlChannelManager_OnDisconnectCallback_NilSafe(t *testing.T) { mgr := NewControlChannelManager(DefaultControlChannelConfig(), slog.Default()) // Don't set any callback - verify removeConnection doesn't panic mgr.mu.Lock() - mgr.connections[tid("broker-2")] = &BrokerConnection{brokerID: tid("broker-2")} + mgr.connections[tid("broker-2")] = &BrokerConnection{brokerID: tid("broker-2"), sessionID: "sess-2"} mgr.mu.Unlock() // This should not panic - mgr.removeConnection(tid("broker-2")) + mgr.removeConnection(tid("broker-2"), "sess-2") require.False(t, mgr.IsConnected(tid("broker-2"))) } diff --git a/pkg/hub/dispatch_args.go b/pkg/hub/dispatch_args.go new file mode 100644 index 000000000..f595fd5df --- /dev/null +++ b/pkg/hub/dispatch_args.go @@ -0,0 +1,111 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package hub + +import ( + "encoding/json" + "time" +) + +// StartDispatchArgs carries the parameters for a cross-node agent start. +// Only fields that the owner's DispatchAgentStart cannot re-derive are +// included. Env/secret resolution is performed by the OWNER via +// DispatchAgentStart (all hub instances share the same store + secret +// backend), so resolved env/secrets are NOT serialized here. +type StartDispatchArgs struct { + Task string `json:"task,omitempty"` +} + +// RestartDispatchArgs is intentionally empty — the owner's +// DispatchAgentRestart re-resolves auth tokens and identity vars from the +// shared store on the owning node. +type RestartDispatchArgs struct{} + +// StopDispatchArgs is intentionally empty — a stop needs no additional params +// beyond what the dispatch row already carries (agentID, projectID). +type StopDispatchArgs struct{} + +// DeleteDispatchArgs carries the parameters for a cross-node agent delete. +type DeleteDispatchArgs struct { + DeleteFiles bool `json:"deleteFiles,omitempty"` + RemoveBranch bool `json:"removeBranch,omitempty"` + SoftDelete bool `json:"softDelete,omitempty"` + DeletedAt time.Time `json:"deletedAt,omitempty"` +} + +// CheckPromptDispatchArgs is intentionally empty — the agent slug/ID in the +// dispatch row is sufficient for the owner to run the local check. +type CheckPromptDispatchArgs struct{} + +// FinalizeEnvDispatchArgs carries the gathered env vars for cross-node finalize. +type FinalizeEnvDispatchArgs struct { + Env map[string]string `json:"env,omitempty"` +} + +// CreateWithGatherDispatchArgs is intentionally empty — the owner rebuilds the +// full RemoteCreateAgentRequest from the shared store (same pattern as start). +type CreateWithGatherDispatchArgs struct{} + +// CheckPromptResult is serialized into broker_dispatch.result by the owner. +type CheckPromptResult struct { + HasPrompt bool `json:"hasPrompt"` +} + +// FinalizeEnvResult is serialized into broker_dispatch.result by the owner. +type FinalizeEnvResult struct { + Success bool `json:"success"` +} + +// CreateWithGatherResult is serialized into broker_dispatch.result by the owner. +type CreateWithGatherResult struct { + EnvRequirements *RemoteEnvRequirementsResponse `json:"envRequirements,omitempty"` +} + +// MarshalDispatchArgs serializes a dispatch args struct to JSON for storage in +// broker_dispatch.args. +func MarshalDispatchArgs(v interface{}) (string, error) { + b, err := json.Marshal(v) + if err != nil { + return "", err + } + return string(b), nil +} + +// UnmarshalStartArgs deserializes start dispatch args from the broker_dispatch row. +func UnmarshalStartArgs(raw string) (*StartDispatchArgs, error) { + var a StartDispatchArgs + if err := json.Unmarshal([]byte(raw), &a); err != nil { + return nil, err + } + return &a, nil +} + +// UnmarshalDeleteArgs deserializes delete dispatch args from the broker_dispatch row. +func UnmarshalDeleteArgs(raw string) (*DeleteDispatchArgs, error) { + var a DeleteDispatchArgs + if err := json.Unmarshal([]byte(raw), &a); err != nil { + return nil, err + } + return &a, nil +} + +// UnmarshalFinalizeEnvArgs deserializes finalize_env dispatch args. +func UnmarshalFinalizeEnvArgs(raw string) (*FinalizeEnvDispatchArgs, error) { + var a FinalizeEnvDispatchArgs + if err := json.Unmarshal([]byte(raw), &a); err != nil { + return nil, err + } + return &a, nil +} diff --git a/pkg/hub/dispatch_exec_test.go b/pkg/hub/dispatch_exec_test.go new file mode 100644 index 000000000..0e7b8e877 --- /dev/null +++ b/pkg/hub/dispatch_exec_test.go @@ -0,0 +1,711 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build !no_sqlite + +package hub + +import ( + "context" + "encoding/json" + "log/slog" + "sync/atomic" + "testing" + "time" + + "github.com/GoogleCloudPlatform/scion/pkg/api" + "github.com/GoogleCloudPlatform/scion/pkg/messages" + "github.com/GoogleCloudPlatform/scion/pkg/store" + "github.com/GoogleCloudPlatform/scion/pkg/store/entadapter" + "github.com/GoogleCloudPlatform/scion/pkg/store/enttest" + "github.com/google/uuid" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// lifecycleTestDispatcher captures which lifecycle op was called and with +// what args, so we can verify executeDispatch routes correctly. +type lifecycleTestDispatcher struct { + startCalled atomic.Int32 + stopCalled atomic.Int32 + restartCalled atomic.Int32 + deleteCalled atomic.Int32 + checkPromptCalled atomic.Int32 + finalizeEnvCalled atomic.Int32 + createCalled atomic.Int32 + lastTask string + checkPromptResult bool + lastDeleteFiles bool + lastFinalizeEnv map[string]string +} + +func (d *lifecycleTestDispatcher) DispatchAgentCreate(context.Context, *store.Agent) error { return nil } +func (d *lifecycleTestDispatcher) DispatchAgentProvision(context.Context, *store.Agent) error { + return nil +} +func (d *lifecycleTestDispatcher) DispatchAgentStart(_ context.Context, _ *store.Agent, task string) error { + d.startCalled.Add(1) + d.lastTask = task + return nil +} +func (d *lifecycleTestDispatcher) DispatchAgentStop(_ context.Context, _ *store.Agent) error { + d.stopCalled.Add(1) + return nil +} +func (d *lifecycleTestDispatcher) DispatchAgentRestart(_ context.Context, _ *store.Agent) error { + d.restartCalled.Add(1) + return nil +} +func (d *lifecycleTestDispatcher) DispatchAgentDelete(_ context.Context, _ *store.Agent, deleteFiles, _, _ bool, _ time.Time) error { + d.deleteCalled.Add(1) + d.lastDeleteFiles = deleteFiles + return nil +} +func (d *lifecycleTestDispatcher) DispatchAgentMessage(_ context.Context, _ *store.Agent, _ string, _ bool, _ *messages.StructuredMessage) error { + return nil +} +func (d *lifecycleTestDispatcher) DispatchAgentLogs(context.Context, *store.Agent, int) (string, error) { + return "", nil +} +func (d *lifecycleTestDispatcher) DispatchAgentExec(context.Context, *store.Agent, []string, int) (string, int, error) { + return "", 0, nil +} +func (d *lifecycleTestDispatcher) DispatchCheckAgentPrompt(context.Context, *store.Agent) (bool, error) { + d.checkPromptCalled.Add(1) + return d.checkPromptResult, nil +} +func (d *lifecycleTestDispatcher) DispatchAgentCreateWithGather(context.Context, *store.Agent) (*RemoteEnvRequirementsResponse, error) { + d.createCalled.Add(1) + return nil, nil +} +func (d *lifecycleTestDispatcher) DispatchFinalizeEnv(_ context.Context, _ *store.Agent, env map[string]string) error { + d.finalizeEnvCalled.Add(1) + d.lastFinalizeEnv = env + return nil +} + +func newLifecycleTestServer(t *testing.T) (*Server, *lifecycleTestDispatcher, store.Store) { + t.Helper() + client := enttest.NewClient(t) + cs := entadapter.NewCompositeStore(client) + disp := &lifecycleTestDispatcher{} + events := NewChannelEventPublisher() + t.Cleanup(func() { events.Close() }) + srv := &Server{ + store: cs, + instanceID: "hub-test-" + uuid.NewString()[:8], + agentLifecycleLog: slog.Default(), + events: events, + } + srv.SetDispatcher(disp) + srv.execDispatch = srv.executeDispatch + srv.deliverMsg = srv.deliverMessage + return srv, disp, cs +} + +// seedAgent creates a project + runtime broker + agent and returns the agent. +// The broker has no endpoint (simulates a NAT'd control-channel-only broker). +func seedAgent(t *testing.T, cs store.Store) *store.Agent { + return seedAgentWithBrokerID(t, cs, uuid.NewString()) +} + +func seedAgentWithBrokerID(t *testing.T, cs store.Store, brokerID string) *store.Agent { + t.Helper() + ctx := context.Background() + proj := &store.Project{ + ID: uuid.NewString(), + Name: "test-proj", + Slug: "tp-" + uuid.NewString()[:8], + Visibility: store.VisibilityPrivate, + OwnerID: uuid.NewString(), + } + require.NoError(t, cs.CreateProject(ctx, proj)) + broker := &store.RuntimeBroker{ + ID: brokerID, + Name: "test-broker", + Slug: "tb-" + uuid.NewString()[:8], + Status: "online", + } + require.NoError(t, cs.CreateRuntimeBroker(ctx, broker)) + agent := &store.Agent{ + ID: uuid.NewString(), + Name: "test-agent", + Slug: "ta-" + uuid.NewString()[:8], + ProjectID: proj.ID, + RuntimeBrokerID: brokerID, + } + require.NoError(t, cs.CreateAgent(ctx, agent)) + return agent +} + +func TestExecuteDispatch_Start(t *testing.T) { + ctx := context.Background() + srv, disp, cs := newLifecycleTestServer(t) + agent := seedAgent(t, cs) + + args, err := MarshalDispatchArgs(&StartDispatchArgs{ + Task: "run tests", + }) + require.NoError(t, err) + + d := store.BrokerDispatch{ + ID: uuid.NewString(), + BrokerID: agent.RuntimeBrokerID, + AgentID: agent.ID, + Op: "start", + Args: args, + } + + result, execErr := srv.executeDispatch(ctx, d) + require.NoError(t, execErr) + assert.Empty(t, result) + assert.Equal(t, int32(1), disp.startCalled.Load()) + assert.Equal(t, "run tests", disp.lastTask) +} + +func TestExecuteDispatch_Stop(t *testing.T) { + ctx := context.Background() + srv, disp, cs := newLifecycleTestServer(t) + agent := seedAgent(t, cs) + + d := store.BrokerDispatch{ + ID: uuid.NewString(), + BrokerID: agent.RuntimeBrokerID, + AgentID: agent.ID, + Op: "stop", + } + + _, execErr := srv.executeDispatch(ctx, d) + require.NoError(t, execErr) + assert.Equal(t, int32(1), disp.stopCalled.Load()) +} + +func TestExecuteDispatch_Restart(t *testing.T) { + ctx := context.Background() + srv, disp, cs := newLifecycleTestServer(t) + agent := seedAgent(t, cs) + + d := store.BrokerDispatch{ + ID: uuid.NewString(), + BrokerID: agent.RuntimeBrokerID, + AgentID: agent.ID, + Op: "restart", + } + + _, execErr := srv.executeDispatch(ctx, d) + require.NoError(t, execErr) + assert.Equal(t, int32(1), disp.restartCalled.Load()) +} + +func TestExecuteDispatch_UnknownOp(t *testing.T) { + ctx := context.Background() + srv, _, cs := newLifecycleTestServer(t) + agent := seedAgent(t, cs) + + d := store.BrokerDispatch{ + ID: uuid.NewString(), + BrokerID: agent.RuntimeBrokerID, + AgentID: agent.ID, + Op: "exec_agent", + } + + _, err := srv.executeDispatch(ctx, d) + assert.Error(t, err) + assert.Contains(t, err.Error(), "not yet wired") +} + +func TestExecuteDispatch_MissingAgent(t *testing.T) { + ctx := context.Background() + srv, _, _ := newLifecycleTestServer(t) + + d := store.BrokerDispatch{ + ID: uuid.NewString(), + BrokerID: uuid.NewString(), + AgentID: uuid.NewString(), + Op: "start", + } + + _, err := srv.executeDispatch(ctx, d) + assert.Error(t, err) + assert.Contains(t, err.Error(), "resolve agent") +} + +// ========================================================================= +// Deferred lifecycle integration test (originator side) +// ========================================================================= + +// deferredTestClient is a RuntimeBrokerClient that returns ErrLifecycleDeferred +// for Start/Stop/Restart when the broker is "remote", and succeeds for "local". +type deferredTestClient struct { + fakeHTTPClient + localBroker string + startCalled atomic.Int32 +} + +func (c *deferredTestClient) StartAgent(_ context.Context, brokerID, _, _, _, _, _, _, _ string, _ map[string]string, _ []ResolvedSecret, _ *api.ScionConfig, _ []api.SharedDir, _ bool) (*RemoteAgentResponse, error) { + c.startCalled.Add(1) + if brokerID != c.localBroker { + return nil, ErrLifecycleDeferred + } + return &RemoteAgentResponse{}, nil +} + +func (c *deferredTestClient) StopAgent(_ context.Context, brokerID, _, _, _ string) error { + if brokerID != c.localBroker { + return ErrLifecycleDeferred + } + return nil +} + +func (c *deferredTestClient) RestartAgent(_ context.Context, brokerID, _, _, _ string, _ map[string]string) error { + if brokerID != c.localBroker { + return ErrLifecycleDeferred + } + return nil +} + +func TestDeferredStart_WritesIntentAndWaits(t *testing.T) { + ctx := context.Background() + client := enttest.NewClient(t) + cs := entadapter.NewCompositeStore(client) + + remoteBroker := uuid.NewString() + fakeClient := &deferredTestClient{localBroker: "local-broker"} + + events := NewChannelEventPublisher() + defer events.Close() + + dispatcher := NewHTTPAgentDispatcherWithClient(cs, fakeClient, false, slog.Default()) + dispatcher.SetCrossNodeDeps(events, NoopCommandBus{}) + + agent := seedAgentWithBrokerID(t, cs, remoteBroker) + + // Simulate the owner publishing "running" shortly after intent is written. + go func() { + time.Sleep(50 * time.Millisecond) + updatedAgent := *agent + updatedAgent.Phase = "running" + events.PublishAgentStatus(ctx, &updatedAgent) + }() + + err := dispatcher.DispatchAgentStart(ctx, agent, "my-task") + require.NoError(t, err, "deferred start should succeed when 'running' event arrives") + + // Verify a broker_dispatch row was written (intent is durable). No owner + // claimed it in this test, so it stays pending. + pending, err := cs.ListPendingDispatch(ctx, remoteBroker) + require.NoError(t, err) + assert.Len(t, pending, 1, "durable intent row should exist") + assert.Equal(t, "start", pending[0].Op) + assert.Equal(t, agent.ID, pending[0].AgentID) +} + +func TestDeferredStart_ReturnsErrorOnErrorPhase(t *testing.T) { + ctx := context.Background() + client := enttest.NewClient(t) + cs := entadapter.NewCompositeStore(client) + + remoteBroker := uuid.NewString() + fakeClient := &deferredTestClient{localBroker: "local-broker"} + + events := NewChannelEventPublisher() + defer events.Close() + + dispatcher := NewHTTPAgentDispatcherWithClient(cs, fakeClient, false, slog.Default()) + dispatcher.SetCrossNodeDeps(events, NoopCommandBus{}) + + agent := seedAgentWithBrokerID(t, cs, remoteBroker) + + go func() { + time.Sleep(50 * time.Millisecond) + updatedAgent := *agent + updatedAgent.Phase = "error" + updatedAgent.Message = "container crash" + events.PublishAgentStatus(ctx, &updatedAgent) + }() + + err := dispatcher.DispatchAgentStart(ctx, agent, "") + assert.Error(t, err) + assert.Contains(t, err.Error(), "error phase") +} + +func TestLocalStart_SkipsIntentRow(t *testing.T) { + ctx := context.Background() + client := enttest.NewClient(t) + cs := entadapter.NewCompositeStore(client) + + localBroker := uuid.NewString() + fakeClient := &deferredTestClient{localBroker: localBroker} + + events := NewChannelEventPublisher() + defer events.Close() + + dispatcher := NewHTTPAgentDispatcherWithClient(cs, fakeClient, false, slog.Default()) + dispatcher.SetCrossNodeDeps(events, NoopCommandBus{}) + + agent := seedAgentWithBrokerID(t, cs, localBroker) + + err := dispatcher.DispatchAgentStart(ctx, agent, "local-task") + require.NoError(t, err, "local start should succeed directly") + + // Verify no broker_dispatch row was written (local path skips intent). + pending, err := cs.ListPendingDispatch(ctx, localBroker) + require.NoError(t, err) + assert.Empty(t, pending, "local path should not write intent rows") + + assert.Equal(t, int32(1), fakeClient.startCalled.Load(), "client.StartAgent called once") +} + +// TestReconcileBroker_LifecycleEndToEnd verifies the full reconcile path: +// insert a start dispatch, reconcile, verify the dispatcher was called and +// the dispatch row is marked done. +func TestReconcileBroker_LifecycleEndToEnd(t *testing.T) { + ctx := context.Background() + srv, disp, cs := newLifecycleTestServer(t) + agent := seedAgent(t, cs) + + args, err := MarshalDispatchArgs(&StartDispatchArgs{Task: "deploy"}) + require.NoError(t, err) + + d := &store.BrokerDispatch{ + ID: uuid.NewString(), + BrokerID: agent.RuntimeBrokerID, + AgentID: agent.ID, + Op: "start", + Args: args, + } + require.NoError(t, cs.InsertBrokerDispatch(ctx, d)) + + srv.reconcileBroker(ctx, agent.RuntimeBrokerID) + + assert.Equal(t, int32(1), disp.startCalled.Load()) + assert.Equal(t, "deploy", disp.lastTask) + + pending, err := cs.ListPendingDispatch(ctx, agent.RuntimeBrokerID) + require.NoError(t, err) + assert.Empty(t, pending, "dispatch should be completed") +} + +// ========================================================================= +// B4-3: delete dispatch tests +// ========================================================================= + +func TestExecuteDispatch_Delete(t *testing.T) { + ctx := context.Background() + srv, disp, cs := newLifecycleTestServer(t) + agent := seedAgent(t, cs) + + args, err := MarshalDispatchArgs(&DeleteDispatchArgs{ + DeleteFiles: true, + }) + require.NoError(t, err) + + d := store.BrokerDispatch{ + ID: uuid.NewString(), + BrokerID: agent.RuntimeBrokerID, + AgentID: agent.ID, + Op: "delete", + Args: args, + } + + result, execErr := srv.executeDispatch(ctx, d) + require.NoError(t, execErr) + assert.Empty(t, result) + assert.Equal(t, int32(1), disp.deleteCalled.Load()) + assert.True(t, disp.lastDeleteFiles) +} + +func TestReconcileBroker_DeleteEndToEnd(t *testing.T) { + ctx := context.Background() + srv, disp, cs := newLifecycleTestServer(t) + agent := seedAgent(t, cs) + + args, err := MarshalDispatchArgs(&DeleteDispatchArgs{DeleteFiles: true}) + require.NoError(t, err) + + d := &store.BrokerDispatch{ + ID: uuid.NewString(), + BrokerID: agent.RuntimeBrokerID, + AgentID: agent.ID, + Op: "delete", + Args: args, + } + require.NoError(t, cs.InsertBrokerDispatch(ctx, d)) + + srv.reconcileBroker(ctx, agent.RuntimeBrokerID) + + assert.Equal(t, int32(1), disp.deleteCalled.Load()) + + pending, err := cs.ListPendingDispatch(ctx, agent.RuntimeBrokerID) + require.NoError(t, err) + assert.Empty(t, pending, "dispatch should be completed") + + // Verify result row is readable and in done state. + row, err := cs.GetBrokerDispatch(ctx, d.ID) + require.NoError(t, err) + assert.Equal(t, store.DispatchStateDone, row.State) +} + +// ========================================================================= +// B4-4: data ops dispatch tests (check_prompt, finalize_env, create) +// ========================================================================= + +func TestExecuteDispatch_CheckPrompt(t *testing.T) { + ctx := context.Background() + srv, disp, cs := newLifecycleTestServer(t) + agent := seedAgent(t, cs) + disp.checkPromptResult = true + + d := store.BrokerDispatch{ + ID: uuid.NewString(), + BrokerID: agent.RuntimeBrokerID, + AgentID: agent.ID, + Op: "check_prompt", + } + + result, execErr := srv.executeDispatch(ctx, d) + require.NoError(t, execErr) + assert.Equal(t, int32(1), disp.checkPromptCalled.Load()) + + var cr CheckPromptResult + require.NoError(t, json.Unmarshal([]byte(result), &cr)) + assert.True(t, cr.HasPrompt) +} + +func TestExecuteDispatch_FinalizeEnv(t *testing.T) { + ctx := context.Background() + srv, disp, cs := newLifecycleTestServer(t) + agent := seedAgent(t, cs) + + args, err := MarshalDispatchArgs(&FinalizeEnvDispatchArgs{ + Env: map[string]string{"KEY": "value"}, + }) + require.NoError(t, err) + + d := store.BrokerDispatch{ + ID: uuid.NewString(), + BrokerID: agent.RuntimeBrokerID, + AgentID: agent.ID, + Op: "finalize_env", + Args: args, + } + + result, execErr := srv.executeDispatch(ctx, d) + require.NoError(t, execErr) + assert.Equal(t, int32(1), disp.finalizeEnvCalled.Load()) + assert.Equal(t, map[string]string{"KEY": "value"}, disp.lastFinalizeEnv) + + var fr FinalizeEnvResult + require.NoError(t, json.Unmarshal([]byte(result), &fr)) + assert.True(t, fr.Success) +} + +func TestExecuteDispatch_Create(t *testing.T) { + ctx := context.Background() + srv, disp, cs := newLifecycleTestServer(t) + agent := seedAgent(t, cs) + + d := store.BrokerDispatch{ + ID: uuid.NewString(), + BrokerID: agent.RuntimeBrokerID, + AgentID: agent.ID, + Op: "create", + } + + result, execErr := srv.executeDispatch(ctx, d) + require.NoError(t, execErr) + assert.Equal(t, int32(1), disp.createCalled.Load()) + + var cr CreateWithGatherResult + require.NoError(t, json.Unmarshal([]byte(result), &cr)) + assert.Nil(t, cr.EnvRequirements) +} + +func TestReconcileBroker_CheckPromptEndToEnd(t *testing.T) { + ctx := context.Background() + srv, disp, cs := newLifecycleTestServer(t) + agent := seedAgent(t, cs) + disp.checkPromptResult = true + + d := &store.BrokerDispatch{ + ID: uuid.NewString(), + BrokerID: agent.RuntimeBrokerID, + AgentID: agent.ID, + Op: "check_prompt", + } + require.NoError(t, cs.InsertBrokerDispatch(ctx, d)) + + srv.reconcileBroker(ctx, agent.RuntimeBrokerID) + + assert.Equal(t, int32(1), disp.checkPromptCalled.Load()) + + row, err := cs.GetBrokerDispatch(ctx, d.ID) + require.NoError(t, err) + assert.Equal(t, store.DispatchStateDone, row.State) + + var cr CheckPromptResult + require.NoError(t, json.Unmarshal([]byte(row.Result), &cr)) + assert.True(t, cr.HasPrompt) +} + +// ========================================================================= +// GetBrokerDispatch round-trip +// ========================================================================= + +func TestGetBrokerDispatch_RoundTrip(t *testing.T) { + ctx := context.Background() + client := enttest.NewClient(t) + cs := entadapter.NewCompositeStore(client) + + d := &store.BrokerDispatch{ + ID: uuid.NewString(), + BrokerID: seedAgent(t, cs).RuntimeBrokerID, + Op: "check_prompt", + } + require.NoError(t, cs.InsertBrokerDispatch(ctx, d)) + + got, err := cs.GetBrokerDispatch(ctx, d.ID) + require.NoError(t, err) + assert.Equal(t, d.ID, got.ID) + assert.Equal(t, "check_prompt", got.Op) + assert.Equal(t, store.DispatchStatePending, got.State) + + // Claim (pending→in_progress) before completing, matching the CAS guard. + claimed, err := cs.ClaimBrokerDispatch(ctx, d.ID, "hub-test") + require.NoError(t, err) + require.True(t, claimed) + + require.NoError(t, cs.CompleteBrokerDispatch(ctx, d.ID, `{"hasPrompt":true}`)) + + got, err = cs.GetBrokerDispatch(ctx, d.ID) + require.NoError(t, err) + assert.Equal(t, store.DispatchStateDone, got.State) + assert.Equal(t, `{"hasPrompt":true}`, got.Result) +} + +// ========================================================================= +// B4-3: Deferred delete integration test (originator side) +// ========================================================================= + +func TestDeferredDelete_WritesIntentAndCompletes(t *testing.T) { + ctx := context.Background() + client := enttest.NewClient(t) + cs := entadapter.NewCompositeStore(client) + + remoteBroker := uuid.NewString() + fakeClient := &deferredTestClient{localBroker: "local-broker"} + + events := NewChannelEventPublisher() + defer events.Close() + + dispatcher := NewHTTPAgentDispatcherWithClient(cs, fakeClient, false, slog.Default()) + dispatcher.SetCrossNodeDeps(events, NoopCommandBus{}) + + agent := seedAgentWithBrokerID(t, cs, remoteBroker) + + // Simulate the owner completing the delete dispatch shortly after intent is written. + go func() { + time.Sleep(50 * time.Millisecond) + // Find the pending dispatch row. + pending, err := cs.ListPendingDispatch(ctx, remoteBroker) + if err != nil || len(pending) == 0 { + return + } + d := pending[0] + _, _ = cs.ClaimBrokerDispatch(ctx, d.ID, "owner-hub") + _ = cs.CompleteBrokerDispatch(ctx, d.ID, "") + events.PublishDispatchDone(ctx, d.ID) + }() + + err := dispatcher.DispatchAgentDelete(ctx, agent, true, false, false, time.Time{}) + require.NoError(t, err, "deferred delete should succeed when completion event arrives") + + pending, err := cs.ListPendingDispatch(ctx, remoteBroker) + require.NoError(t, err) + assert.Empty(t, pending, "dispatch should be completed") +} + +// ========================================================================= +// B4-4: Deferred check_prompt integration test (originator side) +// ========================================================================= + +func TestDeferredCheckPrompt_ReturnsResult(t *testing.T) { + ctx := context.Background() + client := enttest.NewClient(t) + cs := entadapter.NewCompositeStore(client) + + remoteBroker := uuid.NewString() + fakeClient := &deferredDataOpTestClient{localBroker: "local-broker"} + + events := NewChannelEventPublisher() + defer events.Close() + + dispatcher := NewHTTPAgentDispatcherWithClient(cs, fakeClient, false, slog.Default()) + dispatcher.SetCrossNodeDeps(events, NoopCommandBus{}) + + agent := seedAgentWithBrokerID(t, cs, remoteBroker) + + // Simulate the owner completing check_prompt with result JSON. + go func() { + time.Sleep(50 * time.Millisecond) + pending, err := cs.ListPendingDispatch(ctx, remoteBroker) + if err != nil || len(pending) == 0 { + return + } + d := pending[0] + _, _ = cs.ClaimBrokerDispatch(ctx, d.ID, "owner-hub") + resultJSON, _ := json.Marshal(CheckPromptResult{HasPrompt: true}) + _ = cs.CompleteBrokerDispatch(ctx, d.ID, string(resultJSON)) + events.PublishDispatchDone(ctx, d.ID) + }() + + hasPrompt, err := dispatcher.DispatchCheckAgentPrompt(ctx, agent) + require.NoError(t, err, "deferred check_prompt should succeed") + assert.True(t, hasPrompt, "should return true from result row") +} + +// deferredDataOpTestClient returns ErrLifecycleDeferred for data ops when the +// broker is not "local", simulating a cross-node dispatch. +type deferredDataOpTestClient struct { + fakeHTTPClient + localBroker string +} + +func (c *deferredDataOpTestClient) DeleteAgent(_ context.Context, brokerID, _, _, _ string, _, _, _ bool, _ time.Time) error { + if brokerID != c.localBroker { + return ErrLifecycleDeferred + } + return nil +} + +func (c *deferredDataOpTestClient) CheckAgentPrompt(_ context.Context, brokerID, _, _, _ string) (bool, error) { + if brokerID != c.localBroker { + return false, ErrLifecycleDeferred + } + return false, nil +} + +func (c *deferredDataOpTestClient) FinalizeEnv(_ context.Context, brokerID, _, _ string, _ map[string]string) (*RemoteAgentResponse, error) { + if brokerID != c.localBroker { + return nil, ErrLifecycleDeferred + } + return nil, nil +} + +func (c *deferredDataOpTestClient) CreateAgentWithGather(_ context.Context, brokerID, _ string, _ *RemoteCreateAgentRequest) (*RemoteAgentResponse, *RemoteEnvRequirementsResponse, error) { + if brokerID != c.localBroker { + return nil, nil, ErrLifecycleDeferred + } + return nil, nil, nil +} diff --git a/pkg/hub/dispatch_lifecycle_test.go b/pkg/hub/dispatch_lifecycle_test.go new file mode 100644 index 000000000..2326cbdf5 --- /dev/null +++ b/pkg/hub/dispatch_lifecycle_test.go @@ -0,0 +1,252 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package hub + +import ( + "context" + "log/slog" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// ========================================================================= +// Route-gating tests for StartAgent / StopAgent / RestartAgent +// ========================================================================= + +func TestHybridBrokerClient_StartAgent_RouteGate(t *testing.T) { + const localBroker = "broker-local" + const remoteBroker = "broker-remote" + + mgr := NewControlChannelManager(DefaultControlChannelConfig(), slog.Default()) + mgr.mu.Lock() + mgr.connections[localBroker] = &BrokerConnection{brokerID: localBroker, sessionID: "s1"} + mgr.mu.Unlock() + + httpClient := &fakeHTTPClient{} + c := NewHybridBrokerClient(mgr, httpClient, nil, false) + + t.Run("routeLocal uses control channel (not deferred)", func(t *testing.T) { + got := c.route(context.Background(), localBroker, "") + assert.Equal(t, routeLocal, got) + }) + + t.Run("routeForward returns ErrLifecycleDeferred", func(t *testing.T) { + c.SetAffinityLookup(func(context.Context, string) (string, bool) { return "hubA", true }) + _, err := c.StartAgent(context.Background(), remoteBroker, "", "a1", "p1", "", "", "", "", nil, nil, nil, nil, false) + assert.ErrorIs(t, err, ErrLifecycleDeferred) + }) + + t.Run("routeUndeliverable returns ErrLifecycleDeferred", func(t *testing.T) { + c.SetAffinityLookup(func(context.Context, string) (string, bool) { return "", false }) + _, err := c.StartAgent(context.Background(), remoteBroker, "", "a1", "p1", "", "", "", "", nil, nil, nil, nil, false) + assert.ErrorIs(t, err, ErrLifecycleDeferred) + }) +} + +func TestHybridBrokerClient_StopAgent_RouteGate(t *testing.T) { + const remoteBroker = "broker-remote" + + mgr := NewControlChannelManager(DefaultControlChannelConfig(), slog.Default()) + c := NewHybridBrokerClient(mgr, &fakeHTTPClient{}, nil, false) + + t.Run("routeForward returns ErrLifecycleDeferred", func(t *testing.T) { + c.SetAffinityLookup(func(context.Context, string) (string, bool) { return "hubA", true }) + err := c.StopAgent(context.Background(), remoteBroker, "", "a1", "p1") + assert.ErrorIs(t, err, ErrLifecycleDeferred) + }) + + t.Run("routeUndeliverable returns ErrLifecycleDeferred", func(t *testing.T) { + c.SetAffinityLookup(func(context.Context, string) (string, bool) { return "", false }) + err := c.StopAgent(context.Background(), remoteBroker, "", "a1", "p1") + assert.ErrorIs(t, err, ErrLifecycleDeferred) + }) +} + +func TestHybridBrokerClient_RestartAgent_RouteGate(t *testing.T) { + const remoteBroker = "broker-remote" + + mgr := NewControlChannelManager(DefaultControlChannelConfig(), slog.Default()) + c := NewHybridBrokerClient(mgr, &fakeHTTPClient{}, nil, false) + + t.Run("routeForward returns ErrLifecycleDeferred", func(t *testing.T) { + c.SetAffinityLookup(func(context.Context, string) (string, bool) { return "hubA", true }) + err := c.RestartAgent(context.Background(), remoteBroker, "", "a1", "p1", nil) + assert.ErrorIs(t, err, ErrLifecycleDeferred) + }) + + t.Run("routeUndeliverable returns ErrLifecycleDeferred", func(t *testing.T) { + c.SetAffinityLookup(func(context.Context, string) (string, bool) { return "", false }) + err := c.RestartAgent(context.Background(), remoteBroker, "", "a1", "p1", nil) + assert.ErrorIs(t, err, ErrLifecycleDeferred) + }) +} + +// ========================================================================= +// Dispatch args round-trip (serialize -> deserialize lossless) +// ========================================================================= + +func TestStartDispatchArgs_RoundTrip(t *testing.T) { + original := &StartDispatchArgs{ + Task: "build the widget", + } + + raw, err := MarshalDispatchArgs(original) + require.NoError(t, err) + require.NotEmpty(t, raw) + + got, err := UnmarshalStartArgs(raw) + require.NoError(t, err) + assert.Equal(t, original.Task, got.Task) +} + +func TestRestartDispatchArgs_RoundTrip(t *testing.T) { + raw, err := MarshalDispatchArgs(&RestartDispatchArgs{}) + require.NoError(t, err) + assert.Equal(t, "{}", raw) +} + +func TestStopDispatchArgs_RoundTrip(t *testing.T) { + raw, err := MarshalDispatchArgs(&StopDispatchArgs{}) + require.NoError(t, err) + assert.Equal(t, "{}", raw) +} + +// ========================================================================= +// B4-3: Route-gating tests for DeleteAgent +// ========================================================================= + +func TestHybridBrokerClient_DeleteAgent_RouteGate(t *testing.T) { + const remoteBroker = "broker-remote" + + mgr := NewControlChannelManager(DefaultControlChannelConfig(), slog.Default()) + c := NewHybridBrokerClient(mgr, &fakeHTTPClient{}, nil, false) + + t.Run("routeForward returns ErrLifecycleDeferred", func(t *testing.T) { + c.SetAffinityLookup(func(context.Context, string) (string, bool) { return "hubA", true }) + err := c.DeleteAgent(context.Background(), remoteBroker, "", "a1", "p1", false, false, false, time.Time{}) + assert.ErrorIs(t, err, ErrLifecycleDeferred) + }) + + t.Run("routeUndeliverable returns ErrLifecycleDeferred", func(t *testing.T) { + c.SetAffinityLookup(func(context.Context, string) (string, bool) { return "", false }) + err := c.DeleteAgent(context.Background(), remoteBroker, "", "a1", "p1", false, false, false, time.Time{}) + assert.ErrorIs(t, err, ErrLifecycleDeferred) + }) +} + +// ========================================================================= +// B4-4: Route-gating tests for CheckAgentPrompt / CreateAgentWithGather / FinalizeEnv +// ========================================================================= + +func TestHybridBrokerClient_CheckAgentPrompt_RouteGate(t *testing.T) { + const remoteBroker = "broker-remote" + + mgr := NewControlChannelManager(DefaultControlChannelConfig(), slog.Default()) + c := NewHybridBrokerClient(mgr, &fakeHTTPClient{}, nil, false) + + t.Run("routeForward returns ErrLifecycleDeferred", func(t *testing.T) { + c.SetAffinityLookup(func(context.Context, string) (string, bool) { return "hubA", true }) + _, err := c.CheckAgentPrompt(context.Background(), remoteBroker, "", "a1", "p1") + assert.ErrorIs(t, err, ErrLifecycleDeferred) + }) + + t.Run("routeUndeliverable returns ErrLifecycleDeferred", func(t *testing.T) { + c.SetAffinityLookup(func(context.Context, string) (string, bool) { return "", false }) + _, err := c.CheckAgentPrompt(context.Background(), remoteBroker, "", "a1", "p1") + assert.ErrorIs(t, err, ErrLifecycleDeferred) + }) +} + +func TestHybridBrokerClient_CreateAgentWithGather_RouteGate(t *testing.T) { + const remoteBroker = "broker-remote" + + mgr := NewControlChannelManager(DefaultControlChannelConfig(), slog.Default()) + c := NewHybridBrokerClient(mgr, &fakeHTTPClient{}, nil, false) + + t.Run("routeForward returns ErrLifecycleDeferred", func(t *testing.T) { + c.SetAffinityLookup(func(context.Context, string) (string, bool) { return "hubA", true }) + _, _, err := c.CreateAgentWithGather(context.Background(), remoteBroker, "", &RemoteCreateAgentRequest{}) + assert.ErrorIs(t, err, ErrLifecycleDeferred) + }) + + t.Run("routeUndeliverable returns ErrLifecycleDeferred", func(t *testing.T) { + c.SetAffinityLookup(func(context.Context, string) (string, bool) { return "", false }) + _, _, err := c.CreateAgentWithGather(context.Background(), remoteBroker, "", &RemoteCreateAgentRequest{}) + assert.ErrorIs(t, err, ErrLifecycleDeferred) + }) +} + +func TestHybridBrokerClient_FinalizeEnv_RouteGate(t *testing.T) { + const remoteBroker = "broker-remote" + + mgr := NewControlChannelManager(DefaultControlChannelConfig(), slog.Default()) + c := NewHybridBrokerClient(mgr, &fakeHTTPClient{}, nil, false) + + t.Run("routeForward returns ErrLifecycleDeferred", func(t *testing.T) { + c.SetAffinityLookup(func(context.Context, string) (string, bool) { return "hubA", true }) + _, err := c.FinalizeEnv(context.Background(), remoteBroker, "", "a1", nil) + assert.ErrorIs(t, err, ErrLifecycleDeferred) + }) + + t.Run("routeUndeliverable returns ErrLifecycleDeferred", func(t *testing.T) { + c.SetAffinityLookup(func(context.Context, string) (string, bool) { return "", false }) + _, err := c.FinalizeEnv(context.Background(), remoteBroker, "", "a1", nil) + assert.ErrorIs(t, err, ErrLifecycleDeferred) + }) +} + +// ========================================================================= +// B4-3/B4-4: Dispatch args round-trip +// ========================================================================= + +func TestDeleteDispatchArgs_RoundTrip(t *testing.T) { + original := &DeleteDispatchArgs{ + DeleteFiles: true, + RemoveBranch: true, + SoftDelete: false, + } + + raw, err := MarshalDispatchArgs(original) + require.NoError(t, err) + require.NotEmpty(t, raw) + + got, err := UnmarshalDeleteArgs(raw) + require.NoError(t, err) + assert.Equal(t, original.DeleteFiles, got.DeleteFiles) + assert.Equal(t, original.RemoveBranch, got.RemoveBranch) + assert.Equal(t, original.SoftDelete, got.SoftDelete) +} + +func TestFinalizeEnvDispatchArgs_RoundTrip(t *testing.T) { + original := &FinalizeEnvDispatchArgs{ + Env: map[string]string{"KEY": "val", "SECRET": "abc"}, + } + + raw, err := MarshalDispatchArgs(original) + require.NoError(t, err) + + got, err := UnmarshalFinalizeEnvArgs(raw) + require.NoError(t, err) + assert.Equal(t, original.Env, got.Env) +} + +func TestCheckPromptDispatchArgs_RoundTrip(t *testing.T) { + raw, err := MarshalDispatchArgs(&CheckPromptDispatchArgs{}) + require.NoError(t, err) + assert.Equal(t, "{}", raw) +} diff --git a/pkg/hub/dispatch_wait.go b/pkg/hub/dispatch_wait.go new file mode 100644 index 000000000..94c95ec4b --- /dev/null +++ b/pkg/hub/dispatch_wait.go @@ -0,0 +1,148 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package hub + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "time" + + "github.com/GoogleCloudPlatform/scion/pkg/store" +) + +// ErrDispatchFailed is returned when a lifecycle dispatch rolling timeout +// expires without receiving any status update within the window — the broker +// went silent and the operation is considered failed (design §6.4). +var ErrDispatchFailed = errors.New("dispatch failed: rolling timeout expired with no status update") + +// dispatchRollingTimeout is the default rolling window for +// waitForAgentTransition. Each status event (phase/activity/detail change) +// resets this timer. If no event arrives within the window, the dispatch is +// considered failed. Single tunable per design §6.4. +const dispatchRollingTimeout = 90 * time.Second + +// waitForAgentTransition waits for an agent's phase to reach a terminal state, +// using a rolling timeout that resets on ANY AgentStatusEvent (phase, activity, +// or detail change). The caller must subscribe to the agent's status events +// BEFORE writing the durable intent, and pass the subscription channel + +// unsubscribe function here. +// +// Parameters: +// - events: the subscription channel from EventPublisher.Subscribe("agent..status") +// - unsub: the unsubscribe function returned by Subscribe (called on return) +// - terminal: returns true when the agent's phase indicates the op is done +// (e.g. "running" or "error" for start; "stopped" or "error" for stop) +// +// Returns the terminal phase on success, or ErrDispatchFailed on rolling +// timeout, or ctx.Err() on context cancellation. +func waitForAgentTransition( + ctx context.Context, + events <-chan Event, + unsub func(), + terminal func(phase string) bool, +) (string, error) { + defer unsub() + + timeout := dispatchRollingTimeout + timer := time.NewTimer(timeout) + defer timer.Stop() + + for { + select { + case ev, ok := <-events: + if !ok { + return "", ErrDispatchFailed + } + var status AgentStatusEvent + if err := json.Unmarshal(ev.Data, &status); err != nil { + continue + } + if terminal(status.Phase) { + return status.Phase, nil + } + if !timer.Stop() { + select { + case <-timer.C: + default: + } + } + timer.Reset(timeout) + + case <-timer.C: + return "", ErrDispatchFailed + + case <-ctx.Done(): + return "", ctx.Err() + } + } +} + +// waitForDispatchDone waits for a broker_dispatch row to reach terminal state. +// The caller subscribes to broker.dispatch..done BEFORE writing intent and +// passes the channel + unsub here. On event arrival (or timeout), the row is +// read from the store — the DB row is authoritative (design §6.3), so a missed +// event is recoverable. +func waitForDispatchDone( + ctx context.Context, + events <-chan Event, + unsub func(), + st store.BrokerDispatchStore, + dispatchID string, +) (*store.BrokerDispatch, error) { + defer unsub() + + timeout := dispatchRollingTimeout + timer := time.NewTimer(timeout) + defer timer.Stop() + + for { + select { + case _, ok := <-events: + if !ok { + return nil, ErrDispatchFailed + } + d, err := st.GetBrokerDispatch(ctx, dispatchID) + if err != nil { + return nil, fmt.Errorf("read dispatch result: %w", err) + } + if d.State == store.DispatchStateDone || d.State == store.DispatchStateFailed { + return d, nil + } + if !timer.Stop() { + select { + case <-timer.C: + default: + } + } + timer.Reset(timeout) + + case <-timer.C: + // Bounded re-read: the event may have been missed (design §6.3). + d, err := st.GetBrokerDispatch(ctx, dispatchID) + if err != nil { + return nil, fmt.Errorf("read dispatch result on timeout: %w", err) + } + if d.State == store.DispatchStateDone || d.State == store.DispatchStateFailed { + return d, nil + } + return nil, ErrDispatchFailed + + case <-ctx.Done(): + return nil, ctx.Err() + } + } +} diff --git a/pkg/hub/dispatch_wait_test.go b/pkg/hub/dispatch_wait_test.go new file mode 100644 index 000000000..de12f8fad --- /dev/null +++ b/pkg/hub/dispatch_wait_test.go @@ -0,0 +1,312 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package hub + +import ( + "context" + "encoding/json" + "testing" + "time" + + "github.com/GoogleCloudPlatform/scion/pkg/store" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// fakeDispatchStore is a minimal in-memory BrokerDispatchStore for unit tests. +type fakeDispatchStore struct { + dispatches map[string]*store.BrokerDispatch +} + +func (f *fakeDispatchStore) GetBrokerDispatch(_ context.Context, id string) (*store.BrokerDispatch, error) { + d, ok := f.dispatches[id] + if !ok { + return nil, store.ErrNotFound + } + return d, nil +} + +func (f *fakeDispatchStore) InsertBrokerDispatch(_ context.Context, d *store.BrokerDispatch) error { + return nil +} +func (f *fakeDispatchStore) ClaimBrokerDispatch(_ context.Context, _, _ string) (bool, error) { + return false, nil +} +func (f *fakeDispatchStore) CompleteBrokerDispatch(_ context.Context, _, _ string) error { + return nil +} +func (f *fakeDispatchStore) FailBrokerDispatch(_ context.Context, _, _ string) error { return nil } +func (f *fakeDispatchStore) ListPendingDispatch(_ context.Context, _ string) ([]store.BrokerDispatch, error) { + return nil, nil +} +func (f *fakeDispatchStore) MarkMessageDispatched(_ context.Context, _ string) (bool, error) { + return false, nil +} +func (f *fakeDispatchStore) ListPendingMessages(_ context.Context, _ string) ([]store.Message, error) { + return nil, nil +} +func (f *fakeDispatchStore) ReapStuckDispatch(_ context.Context, _ time.Time, _ int) (int, int, error) { + return 0, 0, nil +} +func (f *fakeDispatchStore) CountStuckPendingMessages(_ context.Context, _ time.Time) (int, error) { + return 0, nil +} + +// sendStatus pushes a fake AgentStatusEvent onto the channel. +func sendStatus(ch chan<- Event, phase, activity string, detail *AgentDetail) { + evt := AgentStatusEvent{ + AgentID: "agent-1", + Phase: phase, + Activity: activity, + Detail: detail, + } + data, _ := json.Marshal(evt) + ch <- Event{Subject: "agent.agent-1.status", Data: data} +} + +func TestWaitForAgentTransition_TerminalPhase(t *testing.T) { + ch := make(chan Event, 8) + unsub := func() {} + _ = &Server{} // ensure Server type compiles; waitForAgentTransition is standalone + + go func() { + sendStatus(ch, "starting", "pulling image", nil) + sendStatus(ch, "running", "", nil) + }() + + phase, err := waitForAgentTransition( + context.Background(), ch, unsub, + func(p string) bool { return p == "running" || p == "error" }, + ) + require.NoError(t, err) + assert.Equal(t, "running", phase) +} + +func TestWaitForAgentTransition_ErrorPhase(t *testing.T) { + ch := make(chan Event, 8) + unsub := func() {} + _ = &Server{} // ensure Server type compiles; waitForAgentTransition is standalone + + go func() { + sendStatus(ch, "starting", "", nil) + sendStatus(ch, "error", "", nil) + }() + + phase, err := waitForAgentTransition( + context.Background(), ch, unsub, + func(p string) bool { return p == "running" || p == "error" }, + ) + require.NoError(t, err) + assert.Equal(t, "error", phase) +} + +func TestWaitForAgentTransition_RollingReset(t *testing.T) { + // Interim detail updates keep the wait alive past one window. + // We use a very short timeout override for testing speed. + ch := make(chan Event, 64) + unsub := func() {} + _ = &Server{} // ensure Server type compiles; waitForAgentTransition is standalone + + // Override the timeout by wrapping: we cannot easily override the + // const, but we can send events faster than the 90s default and + // confirm the terminal is reached. The real test is that interim + // events don't cause early return. Send 5 interim events, then terminal. + go func() { + for i := 0; i < 5; i++ { + sendStatus(ch, "starting", "step", &AgentDetail{Message: "progress"}) + time.Sleep(5 * time.Millisecond) + } + sendStatus(ch, "running", "", nil) + }() + + phase, err := waitForAgentTransition( + context.Background(), ch, unsub, + func(p string) bool { return p == "running" || p == "error" }, + ) + require.NoError(t, err) + assert.Equal(t, "running", phase) +} + +func TestWaitForAgentTransition_SilenceExpiry(t *testing.T) { + // Override the rolling timeout to something very short so the test + // completes quickly. We can't mutate the const, so instead we close + // the channel which produces a zero Event -> ErrDispatchFailed via + // the ok=false branch. + ch := make(chan Event, 4) + unsub := func() {} + _ = &Server{} // ensure Server type compiles; waitForAgentTransition is standalone + + // Close immediately: simulates silence (no events). + close(ch) + + _, err := waitForAgentTransition( + context.Background(), ch, unsub, + func(p string) bool { return p == "running" }, + ) + assert.ErrorIs(t, err, ErrDispatchFailed) +} + +func TestWaitForAgentTransition_ContextCancel(t *testing.T) { + ch := make(chan Event, 4) + unsub := func() {} + _ = &Server{} // ensure Server type compiles; waitForAgentTransition is standalone + + ctx, cancel := context.WithCancel(context.Background()) + cancel() + + _, err := waitForAgentTransition( + ctx, ch, unsub, + func(p string) bool { return p == "running" }, + ) + assert.ErrorIs(t, err, context.Canceled) +} + +func TestWaitForAgentTransition_UnsubCalled(t *testing.T) { + ch := make(chan Event, 4) + var unsubCalled bool + unsub := func() { unsubCalled = true } + _ = &Server{} // ensure Server type compiles; waitForAgentTransition is standalone + + close(ch) + _, _ = waitForAgentTransition( + context.Background(), ch, unsub, + func(p string) bool { return p == "running" }, + ) + assert.True(t, unsubCalled, "unsub must be called on return") +} + +func TestWaitForAgentTransition_StopTerminal(t *testing.T) { + ch := make(chan Event, 4) + unsub := func() {} + _ = &Server{} // ensure Server type compiles; waitForAgentTransition is standalone + + go func() { + sendStatus(ch, "stopped", "", nil) + }() + + phase, err := waitForAgentTransition( + context.Background(), ch, unsub, + func(p string) bool { return p == "stopped" || p == "error" }, + ) + require.NoError(t, err) + assert.Equal(t, "stopped", phase) +} + +// ========================================================================= +// waitForDispatchDone tests (data-op completion path) +// ========================================================================= + +func TestWaitForDispatchDone_ReturnsOnDone(t *testing.T) { + const dispatchID = "dispatch-1" + ch := make(chan Event, 4) + unsub := func() {} + + fs := &fakeDispatchStore{ + dispatches: map[string]*store.BrokerDispatch{ + dispatchID: { + ID: dispatchID, + State: store.DispatchStateDone, + Result: `{"hasPrompt":true}`, + }, + }, + } + + go func() { + ch <- Event{Subject: "broker.dispatch." + dispatchID + ".done"} + }() + + result, err := waitForDispatchDone(context.Background(), ch, unsub, fs, dispatchID) + require.NoError(t, err) + assert.Equal(t, store.DispatchStateDone, result.State) + assert.Equal(t, `{"hasPrompt":true}`, result.Result) +} + +func TestWaitForDispatchDone_ReturnsOnFailed(t *testing.T) { + const dispatchID = "dispatch-2" + ch := make(chan Event, 4) + unsub := func() {} + + fs := &fakeDispatchStore{ + dispatches: map[string]*store.BrokerDispatch{ + dispatchID: { + ID: dispatchID, + State: store.DispatchStateFailed, + Error: "container crashed", + }, + }, + } + + go func() { + ch <- Event{Subject: "broker.dispatch." + dispatchID + ".done"} + }() + + result, err := waitForDispatchDone(context.Background(), ch, unsub, fs, dispatchID) + require.NoError(t, err) + assert.Equal(t, store.DispatchStateFailed, result.State) + assert.Equal(t, "container crashed", result.Error) +} + +func TestWaitForDispatchDone_ChannelClose(t *testing.T) { + const dispatchID = "dispatch-3" + ch := make(chan Event, 4) + unsub := func() {} + + fs := &fakeDispatchStore{dispatches: map[string]*store.BrokerDispatch{}} + + close(ch) + + _, err := waitForDispatchDone(context.Background(), ch, unsub, fs, dispatchID) + assert.ErrorIs(t, err, ErrDispatchFailed) +} + +func TestWaitForDispatchDone_ContextCancel(t *testing.T) { + const dispatchID = "dispatch-4" + ch := make(chan Event, 4) + unsub := func() {} + + fs := &fakeDispatchStore{dispatches: map[string]*store.BrokerDispatch{}} + + ctx, cancel := context.WithCancel(context.Background()) + cancel() + + _, err := waitForDispatchDone(ctx, ch, unsub, fs, dispatchID) + assert.ErrorIs(t, err, context.Canceled) +} + +func TestWaitForDispatchDone_TimeoutReread(t *testing.T) { + // Verify that on timeout, the row is re-read and if done, returned. + const dispatchID = "dispatch-5" + ch := make(chan Event, 4) + var unsubCalled bool + unsub := func() { unsubCalled = true } + + fs := &fakeDispatchStore{ + dispatches: map[string]*store.BrokerDispatch{ + dispatchID: { + ID: dispatchID, + State: store.DispatchStateDone, + Result: `{"success":true}`, + }, + }, + } + + // Don't send any event — let it time out and re-read. + // We can't easily override the 90s rolling timeout in a unit test, + // so we test the channel-close path instead (above) and verify the + // unsub is called on all paths. + close(ch) + _, _ = waitForDispatchDone(context.Background(), ch, unsub, fs, dispatchID) + assert.True(t, unsubCalled, "unsub must be called on return") +} diff --git a/pkg/hub/events.go b/pkg/hub/events.go index 37ca6b796..2d644ce09 100644 --- a/pkg/hub/events.go +++ b/pkg/hub/events.go @@ -40,6 +40,10 @@ type EventPublisher interface { PublishUserMessage(ctx context.Context, msg *store.Message) PublishAllowListChanged(ctx context.Context, action string, email string) PublishInviteChanged(ctx context.Context, action string, inviteID string, codePrefix string) + // PublishDispatchDone emits a slim completion event on + // broker.dispatch..done so the originator's subscription wakes + // and reads the result from the dispatch row (design §6.3). + PublishDispatchDone(ctx context.Context, dispatchID string) // Subscribe returns a channel that receives events matching the given // subject patterns, along with an unsubscribe function. Patterns use // NATS-style wildcards: '*' matches a single token, '>' matches the @@ -66,6 +70,7 @@ func (noopEventPublisher) PublishNotification(_ context.Context, _ *store.Notifi func (noopEventPublisher) PublishUserMessage(_ context.Context, _ *store.Message) {} func (noopEventPublisher) PublishAllowListChanged(_ context.Context, _, _ string) {} func (noopEventPublisher) PublishInviteChanged(_ context.Context, _, _, _ string) {} +func (noopEventPublisher) PublishDispatchDone(_ context.Context, _ string) {} func (noopEventPublisher) Close() {} // Subscribe on the no-op publisher returns a nil channel (which blocks forever @@ -212,6 +217,14 @@ type InviteChangedEvent struct { CodePrefix string `json:"codePrefix,omitempty"` } +// DispatchDoneEvent is a slim completion event emitted by the owner when a +// broker_dispatch reaches terminal state (done/failed). The originator +// subscribes to broker.dispatch..done BEFORE writing intent and reads the +// result from the dispatch row on wake (design §6.3). +type DispatchDoneEvent struct { + DispatchID string `json:"dispatchId"` +} + // eventBuilder holds the EventPublisher Publish* method implementations shared // by every publisher backend. Each method marshals a typed event struct and // hands the (subject, event) pair to sink, which the embedding publisher wires @@ -553,6 +566,15 @@ func (p *eventBuilder) PublishUserMessage(_ context.Context, msg *store.Message) } } +// PublishDispatchDone emits a slim completion event when a broker_dispatch row +// reaches terminal state. The subject broker.dispatch..done is what the +// originator subscribes to before writing intent (design §6.3). +func (p *eventBuilder) PublishDispatchDone(_ context.Context, dispatchID string) { + p.sink("broker.dispatch."+dispatchID+".done", DispatchDoneEvent{ + DispatchID: dispatchID, + }) +} + // subjectMatchesPattern checks if a subject matches a NATS-style pattern. // '*' matches exactly one token, '>' matches one or more remaining tokens. // Tokens are dot-separated. diff --git a/pkg/hub/handlers.go b/pkg/hub/handlers.go index 4e1b45067..1c5c5db18 100644 --- a/pkg/hub/handlers.go +++ b/pkg/hub/handlers.go @@ -207,16 +207,15 @@ type CreateAgentRequest struct { // GatherEnv enables the env-gather flow where the broker evaluates env // completeness and may return a 202 requiring the CLI to supply missing values. GatherEnv bool `json:"gatherEnv,omitempty"` - // Resume signals that the caller wants to resume an existing stopped agent - // in-place rather than deleting and recreating it. When true and the - // existing agent is in PhaseStopped, the agent record is preserved and - // the broker is asked to restart the container. - Resume bool `json:"resume,omitempty"` // Notify subscribes the creating agent/user to status notifications for the new agent. Notify bool `json:"notify,omitempty"` // CleanupMode controls stale-existing-agent cleanup behavior during create: // "strict" (default) fails create if broker cleanup fails; "force" continues. CleanupMode string `json:"cleanupMode,omitempty"` + // Resume signals that the caller wants to resume an existing stopped agent + // rather than create a brand-new one. When true and a stopped agent with + // the same name exists, the Hub recovers it instead of creating fresh. + Resume bool `json:"resume,omitempty"` // GCPIdentity specifies the GCP identity assignment for the agent. // Controls metadata server behavior and optional service account binding. GCPIdentity *GCPIdentityAssignment `json:"gcp_identity,omitempty"` @@ -783,7 +782,7 @@ func (s *Server) createAgentInProject( } } - // Hub-managed/shared-workspace project remote broker support: if the project has + // Hub-native/shared-workspace project remote broker support: if the project has // a managed workspace and the workspace path is set, upload it to GCS so // a remote broker can download it. if (project.GitRemote == "" || project.IsSharedWorkspace()) && agent.AppliedConfig != nil && agent.AppliedConfig.Workspace != "" { @@ -1926,16 +1925,12 @@ func (s *Server) handleAgentTokenRefresh(w http.ResponseWriter, r *http.Request, // OutboundMessageRequest is the request body for POST /api/v1/agents/{id}/outbound-message. type OutboundMessageRequest struct { - Recipient string `json:"recipient,omitempty"` - RecipientID string `json:"recipient_id,omitempty"` - Msg string `json:"msg"` - Type string `json:"type,omitempty"` - Urgent bool `json:"urgent,omitempty"` - Attachments []string `json:"attachments,omitempty"` - Visibility string `json:"visibility,omitempty"` - Metadata map[string]string `json:"metadata,omitempty"` - Channel string `json:"channel,omitempty"` - ThreadID string `json:"thread_id,omitempty"` + Recipient string `json:"recipient,omitempty"` + RecipientID string `json:"recipient_id,omitempty"` + Msg string `json:"msg"` + Type string `json:"type,omitempty"` + Urgent bool `json:"urgent,omitempty"` + Attachments []string `json:"attachments,omitempty"` } // handleAgentOutboundMessage handles POST /api/v1/agents/{id}/outbound-message. @@ -2047,15 +2042,11 @@ func (s *Server) handleAgentOutboundMessage(w http.ResponseWriter, r *http.Reque Type: req.Type, Urgent: req.Urgent, AgentID: agent.ID, - Channel: req.Channel, - ThreadID: req.ThreadID, CreatedAt: time.Now(), } // Build a structured message for external dispatch paths. structuredMsg := &messages.StructuredMessage{ - Version: messages.Version, - Timestamp: time.Now().UTC().Format(time.RFC3339), Sender: storeMsg.Sender, SenderID: storeMsg.SenderID, Recipient: storeMsg.Recipient, @@ -2064,15 +2055,6 @@ func (s *Server) handleAgentOutboundMessage(w http.ResponseWriter, r *http.Reque Type: storeMsg.Type, Urgent: storeMsg.Urgent, Attachments: req.Attachments, - Visibility: req.Visibility, - Metadata: req.Metadata, - Channel: req.Channel, - ThreadID: req.ThreadID, - } - - if err := structuredMsg.Validate(); err != nil { - ValidationError(w, err.Error(), nil) - return } // Route through broker when available; otherwise persist and publish @@ -2287,9 +2269,6 @@ func (s *Server) handleAgentMessage(w http.ResponseWriter, r *http.Request, id s if structuredMsg.Type == "" { structuredMsg.Type = messages.TypeInstruction } - if structuredMsg.Channel == "" && GetAgentIdentityFromContext(ctx) == nil { - structuredMsg.Channel = "web" - } } else if req.Message != "" { plainMessage = req.Message // Build a structured message from the plain text so that downstream @@ -2306,16 +2285,13 @@ func (s *Server) handleAgentMessage(w http.ResponseWriter, r *http.Request, id s } structuredMsg = messages.NewInstruction(sender, "agent:"+id, plainMessage) structuredMsg.SenderID = senderID - if GetAgentIdentityFromContext(ctx) == nil { - structuredMsg.Channel = "web" - } } else { ValidationError(w, "message or structured_message is required", nil) return } - // Detect group recipient for multi-target fan-out. - if structuredMsg != nil && messages.IsGroupRecipient(structuredMsg.Recipient) { + // Detect set[] recipient for multi-target fan-out. + if structuredMsg != nil && messages.IsSetRecipient(structuredMsg.Recipient) { s.handleGroupMessage(w, r, id, structuredMsg, plainMessage, req.Interrupt) return } @@ -2413,6 +2389,7 @@ func (s *Server) handleAgentMessage(w http.ResponseWriter, r *http.Request, id s s.logMessage("message dispatched", logAttrs...) // Persist to message store (write-through; non-fatal if store fails) + var persistedMsgID string if structuredMsg != nil { storeMsg := &store.Message{ ID: api.NewUUID(), @@ -2428,7 +2405,7 @@ func (s *Server) handleAgentMessage(w http.ResponseWriter, r *http.Request, id s AgentID: agent.ID, CreatedAt: time.Now(), } - // Propagate GroupID from metadata so CLI-originated group messages + // Propagate GroupID from metadata so CLI-originated set[] messages // preserve correlation in the store. if structuredMsg.Metadata != nil { if gid, ok := structuredMsg.Metadata["group_id"]; ok { @@ -2437,6 +2414,8 @@ func (s *Server) handleAgentMessage(w http.ResponseWriter, r *http.Request, id s } if err := s.store.CreateMessage(ctx, storeMsg); err != nil { s.messageLog.Error("Failed to persist message", "error", err) + } else { + persistedMsgID = storeMsg.ID } // Publish SSE event so connected browser clients can update the // per-agent conversation view in real time — mirrors the agent→user @@ -2454,11 +2433,39 @@ func (s *Server) handleAgentMessage(w http.ResponseWriter, r *http.Request, id s ServiceNotReady(w, "Agent has no runtime broker assigned — the server may still be starting up") return } - if err := dispatcher.DispatchAgentMessage(ctx, agent, plainMessage, req.Interrupt, structuredMsg); err != nil { + if err := dispatcher.DispatchAgentMessage(ctx, agent, plainMessage, req.Interrupt, structuredMsg); errors.Is(err, ErrMessageDeferred) { + s.signalDeferredMessage(ctx, agent.RuntimeBrokerID, agent.ID) + // Create notification subscription if requested (before returning 202) + if req.Notify { + var notifySubscriberType, notifySubscriberID, createdBy string + if agentIdent := GetAgentIdentityFromContext(ctx); agentIdent != nil { + createdBy = agentIdent.ID() + if creatorAgent, err := s.store.GetAgent(ctx, agentIdent.ID()); err == nil { + notifySubscriberType = store.SubscriberTypeAgent + notifySubscriberID = creatorAgent.Slug + } + } else if userIdent := GetUserIdentityFromContext(ctx); userIdent != nil { + createdBy = userIdent.ID() + notifySubscriberType = store.SubscriberTypeUser + notifySubscriberID = userIdent.ID() + } + s.createNotifySubscription(ctx, agent.ID, agent.ProjectID, notifySubscriberType, notifySubscriberID, createdBy) + } + w.WriteHeader(http.StatusAccepted) + return + } else if err != nil { RuntimeError(w, "Failed to send message to runtime broker: "+err.Error()) return } + // Mark the message as dispatched so reconcileBroker does not + // re-deliver it on the next broker reconnect. + if persistedMsgID != "" { + if _, err := s.store.MarkMessageDispatched(ctx, persistedMsgID); err != nil { + s.messageLog.Error("Failed to mark message dispatched", "id", persistedMsgID, "error", err) + } + } + // Publish agent-to-agent messages through the broker so plugin observers // (Telegram, broker-log) can see them. ObserverOnly prevents the hub's own // subscription from re-dispatching. @@ -2494,29 +2501,28 @@ func (s *Server) handleAgentMessage(w http.ResponseWriter, r *http.Request, id s w.WriteHeader(http.StatusOK) } -// GroupMessageRecipientResult holds the delivery status for a single recipient -// in a message group fan-out. +// GroupMessageRecipientResult represents the delivery status for one recipient in a set[] delivery. type GroupMessageRecipientResult struct { Recipient string `json:"recipient"` Status string `json:"status"` Error string `json:"error,omitempty"` } -// GroupMessageResponse is the JSON response for a group message delivery. +// GroupMessageResponse is the JSON response for a set[] message delivery. type GroupMessageResponse struct { - GroupID string `json:"group_id"` - Delivered int `json:"delivered"` - Failed int `json:"failed"` + GroupID string `json:"group_id"` + Delivered int `json:"delivered"` + Failed int `json:"failed"` Results []GroupMessageRecipientResult `json:"results"` } -// handleGroupMessage fans out a structured message to multiple recipients in a message group. +// handleGroupMessage fans out a structured message to multiple recipients parsed from set[]. func (s *Server) handleGroupMessage(w http.ResponseWriter, r *http.Request, anchorID string, msg *messages.StructuredMessage, plainMessage string, interrupt bool) { ctx := r.Context() - recipients, err := messages.ParseGroupRecipient(msg.Recipient) + recipients, err := messages.ParseSetRecipient(msg.Recipient) if err != nil { - ValidationError(w, "invalid group recipient: "+err.Error(), nil) + ValidationError(w, "invalid set[] recipient: "+err.Error(), nil) return } @@ -2532,7 +2538,7 @@ func (s *Server) handleGroupMessage(w http.ResponseWriter, r *http.Request, anch for i, r := range recipients { recipientStrs[i] = r.String() } - recipientsSet := messages.FormatGroupRecipients(msg.Sender, recipientStrs) + recipientsSet := messages.FormatSetRecipients(msg.Sender, recipientStrs) groupID := api.NewUUID() results := make([]GroupMessageRecipientResult, len(recipients)) @@ -2571,12 +2577,17 @@ func (s *Server) handleGroupMessage(w http.ResponseWriter, r *http.Request, anch CreatedAt: time.Now(), } if err := s.store.CreateMessage(ctx, storeMsg); err != nil { - s.messageLog.Error("Failed to persist group message", "recipient", recipStr, "error", err) + s.messageLog.Error("Failed to persist set message", "recipient", recipStr, "error", err) } s.events.PublishUserMessage(ctx, storeMsg) if dispatcher != nil && agent.RuntimeBrokerID != "" { - if err := dispatcher.DispatchAgentMessage(ctx, agent, plainMessage, interrupt, &agentMsg); err != nil { + if err := dispatcher.DispatchAgentMessage(ctx, agent, plainMessage, interrupt, &agentMsg); errors.Is(err, ErrMessageDeferred) { + s.signalDeferredMessage(ctx, agent.RuntimeBrokerID, agent.ID) + results[i] = GroupMessageRecipientResult{Recipient: recipStr, Status: "deferred"} + delivered++ + continue + } else if err != nil { results[i] = GroupMessageRecipientResult{Recipient: recipStr, Status: "failed", Error: err.Error()} continue } @@ -2588,13 +2599,19 @@ func (s *Server) handleGroupMessage(w http.ResponseWriter, r *http.Request, anch continue } + // Mark the message as dispatched so reconcileBroker does not + // re-deliver it on the next broker reconnect. + if _, err := s.store.MarkMessageDispatched(ctx, storeMsg.ID); err != nil { + s.messageLog.Error("Failed to mark set message dispatched", "id", storeMsg.ID, "error", err) + } + // Publish agent-to-agent messages through the broker for plugin observers. if strings.HasPrefix(agentMsg.Sender, "agent:") { if bp := s.GetMessageBrokerProxy(); bp != nil { observerMsg := agentMsg observerMsg.ObserverOnly = true if err := bp.PublishMessage(ctx, projectID, &observerMsg); err != nil { - s.messageLog.Error("Failed to publish group observer message", + s.messageLog.Error("Failed to publish set[] observer message", "recipient", recipStr, "error", err) } } @@ -2657,7 +2674,7 @@ func (s *Server) handleGroupMessage(w http.ResponseWriter, r *http.Request, anch CreatedAt: time.Now(), } if err := s.store.CreateMessage(ctx, storeMsg); err != nil { - s.messageLog.Error("Failed to persist group message", "recipient", recipStr, "error", err) + s.messageLog.Error("Failed to persist set message", "recipient", recipStr, "error", err) } s.events.PublishUserMessage(ctx, storeMsg) @@ -2666,7 +2683,7 @@ func (s *Server) handleGroupMessage(w http.ResponseWriter, r *http.Request, anch } } - s.logMessage("group message dispatched", + s.logMessage("set message dispatched", "project_id", projectID, "group_id", groupID, "total", len(recipients), @@ -2794,10 +2811,15 @@ func (s *Server) broadcastDirect(w http.ResponseWriter, r *http.Request, project agentMsg := *msg agentMsg.Recipient = "agent:" + agent.Slug agentMsg.RecipientID = agent.ID - if err := dispatcher.DispatchAgentMessage(ctx, &agent, agentMsg.Msg, interrupt, &agentMsg); err != nil { + dispatched := false + if err := dispatcher.DispatchAgentMessage(ctx, &agent, agentMsg.Msg, interrupt, &agentMsg); errors.Is(err, ErrMessageDeferred) { + s.signalDeferredMessage(ctx, agent.RuntimeBrokerID, agent.ID) + } else if err != nil { s.messageLog.Error("Failed to deliver broadcast message to agent", "agent_id", agent.ID, "agentSlug", agent.Slug, "error", err) + } else { + dispatched = true } // Persist broadcast message per recipient (non-fatal) storeMsg := &store.Message{ @@ -2816,6 +2838,11 @@ func (s *Server) broadcastDirect(w http.ResponseWriter, r *http.Request, project } if err := s.store.CreateMessage(ctx, storeMsg); err != nil { s.messageLog.Error("Failed to persist broadcast message", "agent_id", agent.ID, "error", err) + } else if dispatched { + // Mark dispatched so reconcileBroker does not re-deliver. + if _, err := s.store.MarkMessageDispatched(ctx, storeMsg.ID); err != nil { + s.messageLog.Error("Failed to mark broadcast message dispatched", "id", storeMsg.ID, "error", err) + } } } @@ -3545,7 +3572,7 @@ func (s *Server) createProject(w http.ResponseWriter, r *http.Request) { return } } else if project.GitRemote == "" { - // Hub-managed project (no git remote): create workspace directory. + // Hub-native project (no git remote): create workspace directory. if err := s.initHubManagedProject(project); err != nil { slog.Warn("failed to initialize project workspace", "project_id", project.ID, "slug", project.Slug, "error", err) @@ -3817,7 +3844,7 @@ func (s *Server) initHubManagedProject(project *store.Project) error { return fmt.Errorf("failed to create .scion directory: %w", err) } - // Seed default settings.yaml directly in scionDir. Hub-managed projects + // Seed default settings.yaml directly in scionDir. Hub-native projects // bypass InitProject (which uses split storage for git repos) and keep // all configuration in-place. settingsPath := filepath.Join(scionDir, "settings.yaml") @@ -3849,7 +3876,7 @@ func (s *Server) initHubManagedProject(project *store.Project) error { } // cloneSharedWorkspaceProject performs the host-side git clone for a shared-workspace -// git project. It clones the repository into the hub-managed workspace path and +// git project. It clones the repository into the hub-native workspace path and // seeds the .scion project structure on top. If the clone fails, the workspace // directory is cleaned up and an error is returned. func (s *Server) cloneSharedWorkspaceProject(ctx context.Context, project *store.Project) error { @@ -3995,7 +4022,7 @@ func (s *Server) syncWorkspaceOnStop(ctx context.Context, agent *store.Agent) { project, err := s.store.GetProject(ctx, agent.ProjectID) if err != nil || (project.GitRemote != "" && !project.IsSharedWorkspace()) { - return // Not hub-managed/shared-workspace or project not found + return // Not hub-native/shared-workspace or project not found } // Check if broker is co-located (embedded or has local path) @@ -4202,7 +4229,7 @@ func (s *Server) handleProjectRegister(w http.ResponseWriter, r *http.Request) { // Add as project provider. When the project already existed and the // broker is already a provider, preserve the existing localPath to - // avoid converting a hub-managed git project into a linked project. + // avoid converting a hub-native git project into a linked project. localPath := req.Path if !created { if existingProvider, err := s.store.GetProjectProvider(ctx, project.ID, broker.ID); err == nil { @@ -4298,7 +4325,7 @@ func (s *Server) handleProjectRegister(w http.ResponseWriter, r *http.Request) { // Add as project provider. When the project already existed and the // broker is already a provider, preserve the existing localPath to - // avoid converting a hub-managed git project into a linked project. + // avoid converting a hub-native git project into a linked project. localPath := req.Path if !created { if existingProvider, err := s.store.GetProjectProvider(ctx, project.ID, broker.ID); err == nil { @@ -4529,12 +4556,6 @@ func (s *Server) handleProjectRoutes(w http.ResponseWriter, r *http.Request) { return } - // Check for nested /import-harness-configs path - if subPath == "import-harness-configs" { - s.handleProjectImportHarnessConfigs(w, r, projectID) - return - } - // Check for nested /dav/ path (WebDAV endpoint for project workspace sync) if strings.HasPrefix(subPath, "dav") { davPath := strings.TrimPrefix(subPath, "dav") @@ -5245,7 +5266,7 @@ func (s *Server) deleteProject(w http.ResponseWriter, r *http.Request, id string // Clean up project-scoped harness configs (best-effort), including storage files. s.deleteProjectHarnessConfigs(ctx, id) - // For hub-managed and shared-workspace projects, notify provider brokers to clean up + // For hub-native and shared-workspace projects, notify provider brokers to clean up // their local project directories. This must run before DeleteProject because // the cascade deletes the project_providers we need to enumerate. if project.GitRemote == "" || project.IsSharedWorkspace() { @@ -5257,7 +5278,7 @@ func (s *Server) deleteProject(w http.ResponseWriter, r *http.Request, id string return } - // For hub-managed and shared-workspace projects, remove the filesystem directory. + // For hub-native and shared-workspace projects, remove the filesystem directory. if (project.GitRemote == "" || project.IsSharedWorkspace()) && project.Slug != "" { if projectPath, err := hubManagedProjectPath(project.Slug); err == nil { if err := util.RemoveAllSafe(projectPath); err != nil { @@ -8592,24 +8613,6 @@ func (s *Server) getHarnessConfigFromTemplate(template *store.Template, fallback return fallback } -// lookupHarnessConfigRecord resolves a harness-config reference (name or slug) -// to its Hub record, checking project scope first then global — the same -// precedence the broker uses for on-disk lookup. Returns nil if not found. -func (s *Server) lookupHarnessConfigRecord(ctx context.Context, projectID, ref string) *store.HarnessConfig { - if ref == "" { - return nil - } - if projectID != "" { - if hc, err := s.store.GetHarnessConfigBySlug(ctx, ref, store.HarnessConfigScopeProject, projectID); err == nil && hc != nil { - return hc - } - } - if hc, err := s.store.GetHarnessConfigBySlug(ctx, ref, store.HarnessConfigScopeGlobal, ""); err == nil && hc != nil { - return hc - } - return nil -} - // buildAppliedConfig constructs an AgentAppliedConfig from a CreateAgentRequest. // When req.Config is a ScionConfig, its fields are extracted into the applied config // and the full ScionConfig is preserved as InlineConfig for threading to the broker. @@ -8735,25 +8738,6 @@ func (s *Server) populateAgentConfig(ctx context.Context, agent *store.Agent, pr } } - // Resolve the harness-config name to a Hub record so the broker can hydrate - // it from the configured storage backend, mirroring template hydration. - // Without this a remote broker can only use harness-configs that happen to - // exist on its local filesystem (see resource-storage-refactor §4/§7.3 step 4). - hcRef := agent.AppliedConfig.HarnessConfig - if hcRef == "" && resolvedTemplate != nil { - hcRef = s.getHarnessConfigFromTemplate(resolvedTemplate, "") - } - if hcRef != "" { - projectID := "" - if project != nil { - projectID = project.ID - } - if hc := s.lookupHarnessConfigRecord(ctx, projectID, hcRef); hc != nil { - agent.AppliedConfig.HarnessConfigID = hc.ID - agent.AppliedConfig.HarnessConfigHash = hc.ContentHash - } - } - // Merge hub-level telemetry config as lowest-priority default. // Only applies when no per-agent or template telemetry config is set. s.mu.RLock() @@ -8833,12 +8817,10 @@ func (s *Server) createNotifySubscription(ctx context.Context, agentID, projectI // already exists when a create/start request arrives. // // Phases: -// 0. Resume from suspended (suspended): recover broker, dispatch start, update in-place → started -// 1. Resume from stopped (stopped + Resume flag): recover broker, dispatch start, update in-place → started -// 2. Stale cleanup (running/stopped/error + not resume + not provision-only): dispatch delete, remove from DB → deleted -// 3. Env-gather re-provisioning (provisioning + GatherEnv): dispatch delete, remove from DB → deleted -// 4. Restart (created/provisioning/pending + not provision-only): recover broker ID, update config, dispatch start → started -// 5. Otherwise: none (caller decides what to do) +// 1. Stale cleanup (running/stopped/error + not provision-only): dispatch delete, remove from DB → deleted +// 2. Env-gather re-provisioning (provisioning + GatherEnv): dispatch delete, remove from DB → deleted +// 3. Restart (created/provisioning/pending + not provision-only): recover broker ID, update config, dispatch start → started +// 4. Otherwise: none (caller decides what to do) func (s *Server) handleExistingAgent( ctx context.Context, w http.ResponseWriter, @@ -8904,50 +8886,6 @@ func (s *Server) handleExistingAgent( return existingAgentStarted } - // Stopped agents resumed in-place when the caller explicitly requests resume. - // This preserves the agent ID, metadata, and template association. The broker - // will recreate the container but the hub-level record stays the same. - if !req.ProvisionOnly && req.Resume && existingAgent.Phase == string(state.PhaseStopped) { - if existingAgent.RuntimeBrokerID == "" && runtimeBrokerID != "" { - existingAgent.RuntimeBrokerID = runtimeBrokerID - } - - dispatcher := s.GetDispatcher() - if dispatcher == nil || existingAgent.RuntimeBrokerID == "" { - writeError(w, http.StatusBadRequest, ErrCodeValidationError, - "cannot resume agent: no runtime broker available", nil) - return existingAgentErrored - } - - if existingAgent.AppliedConfig == nil { - existingAgent.AppliedConfig = &store.AgentAppliedConfig{} - } - if req.Task != "" { - existingAgent.AppliedConfig.Task = req.Task - existingAgent.AppliedConfig.Attach = req.Attach - } - - if err := dispatcher.DispatchAgentStart(ctx, existingAgent, req.Task); err != nil { - RuntimeError(w, "Failed to resume stopped agent: "+err.Error()) - return existingAgentErrored - } - - existingAgent.Phase = string(state.PhaseRunning) - if err := s.store.UpdateAgent(ctx, existingAgent); err != nil { - s.agentLifecycleLog.Warn("Failed to update agent status after resume from stopped", "agent_id", existingAgent.ID, "error", err) - } - - if req.Notify { - s.createNotifySubscription(ctx, existingAgent.ID, existingAgent.ProjectID, notifySubscriberType, notifySubscriberID, createdBy) - } - - s.enrichAgent(ctx, existingAgent, project, nil) - writeJSON(w, http.StatusOK, CreateAgentResponse{ - Agent: existingAgent, - }) - return existingAgentStarted - } - // Phase 1: Stale cleanup — agent is running/stopped/error and caller wants a real start. // The old agent is deleted so a fresh one can be created with a new ID. if !req.ProvisionOnly && @@ -9078,7 +9016,7 @@ func (s *Server) resolveRuntimeBroker(ctx context.Context, w http.ResponseWriter "totalProviders", len(allProviders), "onlineProviders", len(availableBrokers), "defaultBroker", project.DefaultRuntimeBrokerID, - "isHubManaged", project.GitRemote == "") + "isHubNative", project.GitRemote == "") // Convert to summary for error responses, marking and prioritizing the default broker brokerSummaries := make([]RuntimeBrokerSummary, 0, len(availableBrokers)) @@ -9372,25 +9310,13 @@ func (s *Server) handleProjectImportTemplates(w http.ResponseWriter, r *http.Req return } - kind := s.templateImportKind() - var run func(progress importProgressFunc) ([]string, error) + var imported []string if req.WorkspacePath != "" { - run = func(progress importProgressFunc) ([]string, error) { - return s.importFromWorkspace(ctx, project, req.WorkspacePath, store.TemplateScopeProject, kind, progress) - } + imported, err = s.importTemplatesFromWorkspace(ctx, project, req.WorkspacePath) } else { - sourceURL := config.NormalizeTemplateSourceURL(req.SourceURL) - run = func(progress importProgressFunc) ([]string, error) { - return s.importFromRemote(ctx, projectID, sourceURL, store.TemplateScopeProject, kind, progress) - } + req.SourceURL = config.NormalizeTemplateSourceURL(req.SourceURL) + imported, err = s.importTemplatesFromRemote(ctx, projectID, req.SourceURL) } - - if importAcceptsNDJSON(r) { - s.streamImport(w, run) - return - } - - imported, err := run(nil) if err != nil { writeError(w, http.StatusBadRequest, "import_failed", err.Error(), nil) return @@ -9401,128 +9327,6 @@ func (s *Server) handleProjectImportTemplates(w http.ResponseWriter, r *http.Req Count: len(imported), }) } - -// ============================================================================ -// Project Harness-Config Import -// ============================================================================ - -// ImportHarnessConfigsRequest is the request body for direct harness-config -// import. Exactly one of SourceURL or WorkspacePath should be provided. -type ImportHarnessConfigsRequest struct { - SourceURL string `json:"sourceUrl"` - WorkspacePath string `json:"workspacePath"` -} - -// ImportHarnessConfigsResponse is returned after a direct harness-config import -// completes. -type ImportHarnessConfigsResponse struct { - HarnessConfigs []string `json:"harnessConfigs"` - Count int `json:"count"` -} - -// handleProjectImportHarnessConfigs imports harness-configs directly from a -// remote URL or the project workspace into the project's harness-config store, -// mirroring handleProjectImportTemplates. -func (s *Server) handleProjectImportHarnessConfigs(w http.ResponseWriter, r *http.Request, projectID string) { - if r.Method != http.MethodPost { - MethodNotAllowed(w) - return - } - - ctx := r.Context() - - // Authorize the caller - if agentIdent := GetAgentIdentityFromContext(ctx); agentIdent != nil { - if !agentIdent.HasScope(ScopeAgentCreate) { - writeError(w, http.StatusForbidden, ErrCodeForbidden, "Missing required scope: project:agent:create", nil) - return - } - if projectID != agentIdent.ProjectID() { - writeError(w, http.StatusForbidden, ErrCodeForbidden, "Agents can only import harness-configs within their own project", nil) - return - } - } else if userIdent := GetUserIdentityFromContext(ctx); userIdent != nil { - decision := s.authzService.CheckAccess(ctx, userIdent, Resource{ - Type: "agent", - ParentType: "project", - ParentID: projectID, - }, ActionCreate) - if !decision.Allowed { - writeError(w, http.StatusForbidden, ErrCodeForbidden, - "You don't have permission to import harness-configs in this project", nil) - return - } - } else { - writeError(w, http.StatusUnauthorized, "unauthorized", "Authentication required", nil) - return - } - - var req ImportHarnessConfigsRequest - if err := readJSON(r, &req); err != nil { - writeError(w, http.StatusBadRequest, "invalid_request", "Invalid request body", nil) - return - } - - if req.SourceURL != "" && req.WorkspacePath != "" { - writeError(w, http.StatusBadRequest, "invalid_request", "Exactly one of sourceUrl or workspacePath must be provided", nil) - return - } - - if req.SourceURL == "" && req.WorkspacePath == "" { - // Default workspace path when neither is provided - req.WorkspacePath = "/.scion/harness-configs" - } - - // Verify project exists - project, err := s.store.GetProject(ctx, projectID) - if err != nil { - if err == store.ErrNotFound { - NotFound(w, "Project") - return - } - writeErrorFromErr(w, err, "") - return - } - - if s.GetStorage() == nil { - writeError(w, http.StatusServiceUnavailable, "storage_unavailable", "Harness-config storage is not configured", nil) - return - } - - kind := s.harnessConfigImportKind() - var run func(progress importProgressFunc) ([]string, error) - if req.WorkspacePath != "" { - run = func(progress importProgressFunc) ([]string, error) { - return s.importFromWorkspace(ctx, project, req.WorkspacePath, store.HarnessConfigScopeProject, kind, progress) - } - } else { - sourceURL := config.NormalizeTemplateSourceURL(req.SourceURL) - run = func(progress importProgressFunc) ([]string, error) { - return s.importFromRemote(ctx, projectID, sourceURL, store.HarnessConfigScopeProject, kind, progress) - } - } - - if importAcceptsNDJSON(r) { - s.streamImport(w, run) - return - } - - imported, err := run(nil) - if err != nil { - writeError(w, http.StatusBadRequest, "import_failed", err.Error(), nil) - return - } - - writeJSON(w, http.StatusOK, ImportHarnessConfigsResponse{ - HarnessConfigs: imported, - Count: len(imported), - }) -} - -// ============================================================================ -// Unified Resource Import (kind/scope-generic) -// ============================================================================ - // ImportResourcesRequest is the body for the unified import endpoint // (POST /api/v1/resources/import). It imports a single kind of resource from a // remote source URL into the given scope. diff --git a/pkg/hub/handlers_broker_inbound.go b/pkg/hub/handlers_broker_inbound.go index c8da35b10..ab9e9fa72 100644 --- a/pkg/hub/handlers_broker_inbound.go +++ b/pkg/hub/handlers_broker_inbound.go @@ -15,6 +15,7 @@ package hub import ( + "errors" "fmt" "net/http" "strings" @@ -103,7 +104,11 @@ func (s *Server) handleBrokerInbound(w http.ResponseWriter, r *http.Request) { return } - if err := dispatcher.DispatchAgentMessage(r.Context(), agent, req.Message.Msg, req.Message.Urgent, req.Message); err != nil { + if err := dispatcher.DispatchAgentMessage(r.Context(), agent, req.Message.Msg, req.Message.Urgent, req.Message); errors.Is(err, ErrMessageDeferred) { + s.signalDeferredMessage(r.Context(), agent.RuntimeBrokerID, agent.ID) + w.WriteHeader(http.StatusAccepted) + return + } else if err != nil { log.Error("Failed to dispatch inbound message", "agent_id", agent.ID, "agent_slug", agentSlug, "error", err) writeError(w, http.StatusBadGateway, ErrCodeRuntimeError, diff --git a/pkg/hub/httpdispatcher.go b/pkg/hub/httpdispatcher.go index bf94ed812..83662f1d0 100644 --- a/pkg/hub/httpdispatcher.go +++ b/pkg/hub/httpdispatcher.go @@ -17,6 +17,8 @@ package hub import ( "context" + "encoding/json" + "errors" "fmt" "log/slog" "strings" @@ -24,9 +26,12 @@ import ( "github.com/GoogleCloudPlatform/scion/pkg/api" "github.com/GoogleCloudPlatform/scion/pkg/messages" + "github.com/GoogleCloudPlatform/scion/pkg/observability/dispatchmetrics" "github.com/GoogleCloudPlatform/scion/pkg/secret" "github.com/GoogleCloudPlatform/scion/pkg/store" + "go.opentelemetry.io/otel/attribute" "github.com/go-jose/go-jose/v4/jwt" + "github.com/google/uuid" ) // HTTPRuntimeBrokerClient is an HTTP-based implementation of RuntimeBrokerClient. @@ -132,6 +137,15 @@ type HTTPAgentDispatcher struct { devAuthToken string // Dev auth token to inject into agent env (dev-auth mode only) debug bool log *slog.Logger + + // Cross-node dispatch deps (B4-2). When events + commandBus are non-nil + // and client.StartAgent/StopAgent/RestartAgent returns ErrLifecycleDeferred, + // the dispatcher writes durable intent + signals the owning node + waits + // for the terminal phase transition. Nil = cross-node dispatch disabled + // (single-node / SQLite mode: all brokers are local). + events EventPublisher + commandBus CommandBus + dispatchMetrics dispatchmetrics.Recorder } // NewHTTPAgentDispatcher creates a new HTTP-based agent dispatcher. @@ -191,6 +205,20 @@ func (d *HTTPAgentDispatcher) SetGitHubAppMinter(m GitHubAppTokenMinter) { d.githubAppMinter = m } +// SetCrossNodeDeps wires the event publisher and command bus needed for +// cross-node lifecycle dispatch (B4-2). When both are set and a lifecycle +// op returns ErrLifecycleDeferred, the dispatcher writes durable intent, +// signals the owning node, and waits for the terminal phase. +func (d *HTTPAgentDispatcher) SetCrossNodeDeps(events EventPublisher, bus CommandBus) { + d.events = events + d.commandBus = bus +} + +// SetDispatchMetrics wires the dispatch metrics recorder (B5-2). +func (d *HTTPAgentDispatcher) SetDispatchMetrics(rec dispatchmetrics.Recorder) { + d.dispatchMetrics = rec +} + // getBrokerEndpoint retrieves the endpoint URL for a runtime broker. // Returns an empty string without error when no endpoint is configured, // which is normal for brokers that connect via WebSocket control channel. @@ -282,7 +310,7 @@ func (d *HTTPAgentDispatcher) buildCreateRequest(ctx context.Context, agent *sto workspace := agent.AppliedConfig.Workspace gitClone := agent.AppliedConfig.GitClone // When the broker has a local provider path for this project, clear - // the hub-managed workspace path — the broker will derive its own + // the hub-native workspace path — the broker will derive its own // workspace location from the project path. However, keep GitClone // config: all hub-linked projects with a git remote use clone-based // provisioning (HTTPS + GitHub token) rather than worktree-based, @@ -300,21 +328,19 @@ func (d *HTTPAgentDispatcher) buildCreateRequest(ctx context.Context, agent *sto } } req.Config = &RemoteAgentConfig{ - Template: agent.Template, - Image: agent.AppliedConfig.Image, - HarnessConfig: agent.AppliedConfig.HarnessConfig, - HarnessAuth: agent.AppliedConfig.HarnessAuth, - Task: agent.AppliedConfig.Task, - Workspace: workspace, - Profile: agent.AppliedConfig.Profile, - Branch: agent.AppliedConfig.Branch, - TemplateID: agent.AppliedConfig.TemplateID, - TemplateHash: agent.AppliedConfig.TemplateHash, - HarnessConfigID: agent.AppliedConfig.HarnessConfigID, - HarnessConfigHash: agent.AppliedConfig.HarnessConfigHash, - GitClone: gitClone, - SharedWorkspace: projectInfo.sharedWorkspace, - GCPIdentity: remoteGCPIdentity, + Template: agent.Template, + Image: agent.AppliedConfig.Image, + HarnessConfig: agent.AppliedConfig.HarnessConfig, + HarnessAuth: agent.AppliedConfig.HarnessAuth, + Task: agent.AppliedConfig.Task, + Workspace: workspace, + Profile: agent.AppliedConfig.Profile, + Branch: agent.AppliedConfig.Branch, + TemplateID: agent.AppliedConfig.TemplateID, + TemplateHash: agent.AppliedConfig.TemplateHash, + GitClone: gitClone, + SharedWorkspace: projectInfo.sharedWorkspace, + GCPIdentity: remoteGCPIdentity, } req.ResolvedEnv = agent.AppliedConfig.Env @@ -504,7 +530,7 @@ func (d *HTTPAgentDispatcher) resolveDispatchProjectPath(ctx context.Context, ag func (d *HTTPAgentDispatcher) resolveDispatchProjectInfo(ctx context.Context, agent *store.Agent) projectDispatchInfo { // Look up the local path for this project on the target runtime broker. - // A provider LocalPath (linked project) takes precedence over hub-managed + // A provider LocalPath (linked project) takes precedence over hub-native // slug resolution, even for projects without a git remote. Only when there // is no provider path and no git remote do we fall back to projectSlug so // the broker resolves the conventional ~/.scion/projects/ path. @@ -537,7 +563,7 @@ func (d *HTTPAgentDispatcher) resolveDispatchProjectInfo(ctx context.Context, ag } } // If no provider path was found, let the broker resolve the path via - // slug. This applies to both hub-managed projects (no git remote) and + // slug. This applies to both hub-native projects (no git remote) and // git-anchored projects — the broker needs a project identity to create // agent directories under ~/.scion/projects// rather than falling // back to the global project. @@ -697,6 +723,9 @@ func (d *HTTPAgentDispatcher) DispatchAgentCreateWithGather(ctx context.Context, "agent_id", agent.ID, "agent", agent.Name, "brokerElapsed", time.Since(brokerCallStart).String(), "totalElapsed", time.Since(dispatchStart).String()) + if errors.Is(err, ErrLifecycleDeferred) { + return d.deferredCreateWithGather(ctx, agent) + } if err != nil { return nil, err } @@ -711,6 +740,22 @@ func (d *HTTPAgentDispatcher) DispatchAgentCreateWithGather(ctx context.Context, return nil, nil } +// deferredCreateWithGather handles a cross-node create-with-gather via durable dispatch. +func (d *HTTPAgentDispatcher) deferredCreateWithGather(ctx context.Context, agent *store.Agent) (*RemoteEnvRequirementsResponse, error) { + result, err := d.deferredDataOpResult(ctx, agent, "create", &CreateWithGatherDispatchArgs{}) + if err != nil { + return nil, err + } + if result.Result == "" { + return nil, nil + } + var cr CreateWithGatherResult + if err := json.Unmarshal([]byte(result.Result), &cr); err != nil { + return nil, fmt.Errorf("unmarshal create result: %w", err) + } + return cr.EnvRequirements, nil +} + // DispatchFinalizeEnv sends gathered env vars to the broker to complete agent creation. func (d *HTTPAgentDispatcher) DispatchFinalizeEnv(ctx context.Context, agent *store.Agent, env map[string]string) error { if err := requireRuntimeBrokerAssigned(agent); err != nil { @@ -723,6 +768,9 @@ func (d *HTTPAgentDispatcher) DispatchFinalizeEnv(ctx context.Context, agent *st } resp, err := d.client.FinalizeEnv(ctx, agent.RuntimeBrokerID, endpoint, agent.ID, env) + if errors.Is(err, ErrLifecycleDeferred) { + return d.deferredFinalizeEnv(ctx, agent, env) + } if err != nil { return err } @@ -733,6 +781,11 @@ func (d *HTTPAgentDispatcher) DispatchFinalizeEnv(ctx context.Context, agent *st return nil } +// deferredFinalizeEnv handles a cross-node finalize_env via durable dispatch. +func (d *HTTPAgentDispatcher) deferredFinalizeEnv(ctx context.Context, agent *store.Agent, env map[string]string) error { + return d.deferredDataOp(ctx, agent, "finalize_env", &FinalizeEnvDispatchArgs{Env: env}) +} + // resolveEnvFromStorage queries Hub env var storage for all applicable scopes // and returns a merged map with precedence: user > project > global. func (d *HTTPAgentDispatcher) resolveEnvFromStorage(ctx context.Context, agent *store.Agent) (map[string]string, error) { @@ -1066,6 +1119,11 @@ func (d *HTTPAgentDispatcher) DispatchAgentStart(ctx context.Context, agent *sto } resp, err := d.client.StartAgent(ctx, agent.RuntimeBrokerID, endpoint, agent.Slug, agent.ProjectID, task, projectPath, projectSlug, harnessConfig, resolvedEnv, resolvedSecrets, inlineConfig, projectInfo.sharedDirs, projectInfo.sharedWorkspace) + if errors.Is(err, ErrLifecycleDeferred) { + return d.deferredStart(ctx, agent, &StartDispatchArgs{ + Task: task, + }) + } if err != nil { return err } @@ -1087,7 +1145,11 @@ func (d *HTTPAgentDispatcher) DispatchAgentStop(ctx context.Context, agent *stor return err } - return d.client.StopAgent(ctx, agent.RuntimeBrokerID, endpoint, agent.Slug, agent.ProjectID) + err = d.client.StopAgent(ctx, agent.RuntimeBrokerID, endpoint, agent.Slug, agent.ProjectID) + if errors.Is(err, ErrLifecycleDeferred) { + return d.deferredStop(ctx, agent) + } + return err } // DispatchAgentRestart restarts an agent on the runtime broker. @@ -1141,7 +1203,11 @@ func (d *HTTPAgentDispatcher) DispatchAgentRestart(ctx context.Context, agent *s } } - return d.client.RestartAgent(ctx, agent.RuntimeBrokerID, endpoint, agent.Slug, agent.ProjectID, resolvedEnv) + err = d.client.RestartAgent(ctx, agent.RuntimeBrokerID, endpoint, agent.Slug, agent.ProjectID, resolvedEnv) + if errors.Is(err, ErrLifecycleDeferred) { + return d.deferredRestart(ctx, agent) + } + return err } // DispatchAgentDelete deletes an agent from the runtime broker. @@ -1155,7 +1221,11 @@ func (d *HTTPAgentDispatcher) DispatchAgentDelete(ctx context.Context, agent *st return err } - return d.client.DeleteAgent(ctx, agent.RuntimeBrokerID, endpoint, agent.Slug, agent.ProjectID, deleteFiles, removeBranch, softDelete, deletedAt) + err = d.client.DeleteAgent(ctx, agent.RuntimeBrokerID, endpoint, agent.Slug, agent.ProjectID, deleteFiles, removeBranch, softDelete, deletedAt) + if errors.Is(err, ErrLifecycleDeferred) { + return d.deferredDelete(ctx, agent, deleteFiles, removeBranch, softDelete, deletedAt) + } + return err } // DispatchAgentMessage sends a message to an agent on the runtime broker. @@ -1211,7 +1281,205 @@ func (d *HTTPAgentDispatcher) DispatchCheckAgentPrompt(ctx context.Context, agen return false, err } - return d.client.CheckAgentPrompt(ctx, agent.RuntimeBrokerID, endpoint, agent.Slug, agent.ProjectID) + hasPrompt, err := d.client.CheckAgentPrompt(ctx, agent.RuntimeBrokerID, endpoint, agent.Slug, agent.ProjectID) + if errors.Is(err, ErrLifecycleDeferred) { + return d.deferredCheckPrompt(ctx, agent) + } + return hasPrompt, err +} + +// deferredCheckPrompt handles a cross-node check_prompt via durable dispatch. +func (d *HTTPAgentDispatcher) deferredCheckPrompt(ctx context.Context, agent *store.Agent) (bool, error) { + result, err := d.deferredDataOpResult(ctx, agent, "check_prompt", &CheckPromptDispatchArgs{}) + if err != nil { + return false, err + } + var cr CheckPromptResult + if result.Result != "" { + if err := json.Unmarshal([]byte(result.Result), &cr); err != nil { + return false, fmt.Errorf("unmarshal check_prompt result: %w", err) + } + } + return cr.HasPrompt, nil +} + +// ============================================================================= +// Cross-node lifecycle dispatch (B4-2) +// ============================================================================= + +// isStartTerminal returns true for terminal phases of a start/restart op. +func isStartTerminal(phase string) bool { return phase == "running" || phase == "error" } + +// isStopTerminal returns true for terminal phases of a stop op. +func isStopTerminal(phase string) bool { return phase == "stopped" || phase == "error" } + +// deferredStart handles a cross-node agent start: subscribe → write intent → +// signal → wait for the terminal phase. Called when client.StartAgent returns +// ErrLifecycleDeferred (broker not locally connected). +func (d *HTTPAgentDispatcher) deferredStart(ctx context.Context, agent *store.Agent, args *StartDispatchArgs) error { + return d.deferredLifecycle(ctx, agent, "start", args, isStartTerminal) +} + +// deferredStop handles a cross-node agent stop. +func (d *HTTPAgentDispatcher) deferredStop(ctx context.Context, agent *store.Agent) error { + return d.deferredLifecycle(ctx, agent, "stop", &StopDispatchArgs{}, isStopTerminal) +} + +// deferredRestart handles a cross-node agent restart. +func (d *HTTPAgentDispatcher) deferredRestart(ctx context.Context, agent *store.Agent) error { + return d.deferredLifecycle(ctx, agent, "restart", &RestartDispatchArgs{}, isStartTerminal) +} + +// deferredDelete handles a cross-node agent delete: subscribe → write intent → +// signal → wait for the dispatch row to reach terminal state. Delete is +// idempotent: 404 from the owner is treated as success. +func (d *HTTPAgentDispatcher) deferredDelete(ctx context.Context, agent *store.Agent, deleteFiles, removeBranch, softDelete bool, deletedAt time.Time) error { + args := &DeleteDispatchArgs{ + DeleteFiles: deleteFiles, + RemoveBranch: removeBranch, + SoftDelete: softDelete, + DeletedAt: deletedAt, + } + return d.deferredDataOp(ctx, agent, "delete", args) +} + +// deferredDataOp is the common flow for cross-node ops that return a result +// via the dispatch row (delete, finalize_env, check_prompt, create): +// 1. Subscribe to broker.dispatch..done BEFORE writing intent +// 2. InsertBrokerDispatch with serialized args +// 3. Best-effort SignalBrokerCmd +// 4. waitForDispatchDone (reads result from the DB row — authoritative) +func (d *HTTPAgentDispatcher) deferredDataOp( + ctx context.Context, + agent *store.Agent, + op string, + args interface{}, +) error { + _, err := d.deferredDataOpResult(ctx, agent, op, args) + return err +} + +// deferredDataOpResult is like deferredDataOp but returns the completed +// dispatch row so callers can read the result JSON. +func (d *HTTPAgentDispatcher) deferredDataOpResult( + ctx context.Context, + agent *store.Agent, + op string, + args interface{}, +) (*store.BrokerDispatch, error) { + if d.events == nil || d.commandBus == nil { + return nil, fmt.Errorf("cross-node dispatch not available: events or command bus not configured") + } + + dispatchID := uuid.NewString() + + // 1. Subscribe BEFORE writing intent so we don't miss events. + eventCh, unsub := d.events.Subscribe("broker.dispatch." + dispatchID + ".done") + + // 2. Serialize args and insert the durable intent row. + argsJSON, err := MarshalDispatchArgs(args) + if err != nil { + unsub() + return nil, fmt.Errorf("marshal dispatch args: %w", err) + } + + dispatch := &store.BrokerDispatch{ + ID: dispatchID, + BrokerID: agent.RuntimeBrokerID, + AgentID: agent.ID, + AgentSlug: agent.Slug, + ProjectID: agent.ProjectID, + Op: op, + Args: argsJSON, + } + if err := d.store.InsertBrokerDispatch(ctx, dispatch); err != nil { + unsub() + return nil, fmt.Errorf("insert dispatch intent: %w", err) + } + if rec := d.dispatchMetrics; rec != nil { + rec.IncPublished(ctx, 1, attribute.String("op", op)) + } + + // 3. Best-effort signal. + if err := d.commandBus.SignalBrokerCmd(ctx, agent.RuntimeBrokerID); err != nil { + d.log.Warn("deferredDataOp: signal failed (durable intent is backstop)", + "op", op, "brokerID", agent.RuntimeBrokerID, "error", err) + } + + // 4. Wait for completion — reads result from the DB row (authoritative). + result, err := waitForDispatchDone(ctx, eventCh, unsub, d.store, dispatchID) + if err != nil { + return nil, err + } + if result.State == store.DispatchStateFailed { + return nil, fmt.Errorf("dispatch %s failed: %s", op, result.Error) + } + return result, nil +} + +// deferredLifecycle is the common flow for cross-node start/stop/restart: +// 1. Subscribe to agent..status BEFORE writing intent (no missed events) +// 2. InsertBrokerDispatch with serialized resolved args +// 3. Best-effort SignalBrokerCmd (the row is durable; reconnect-drain backstop) +// 4. waitForAgentTransition with the op's terminal set +// 5. Return nil on success-terminal, ErrDispatchFailed on timeout, wrapped +// error on error-terminal +func (d *HTTPAgentDispatcher) deferredLifecycle( + ctx context.Context, + agent *store.Agent, + op string, + args interface{}, + terminal func(string) bool, +) error { + if d.events == nil || d.commandBus == nil { + return fmt.Errorf("cross-node dispatch not available: events or command bus not configured") + } + + // 1. Subscribe BEFORE writing intent so we don't miss events. + eventCh, unsub := d.events.Subscribe("agent." + agent.ID + ".status") + + // 2. Serialize args and insert the durable intent row. + argsJSON, err := MarshalDispatchArgs(args) + if err != nil { + unsub() + return fmt.Errorf("marshal dispatch args: %w", err) + } + + dispatch := &store.BrokerDispatch{ + ID: uuid.NewString(), + BrokerID: agent.RuntimeBrokerID, + AgentID: agent.ID, + AgentSlug: agent.Slug, + ProjectID: agent.ProjectID, + Op: op, + Args: argsJSON, + } + if err := d.store.InsertBrokerDispatch(ctx, dispatch); err != nil { + unsub() + return fmt.Errorf("insert dispatch intent: %w", err) + } + if rec := d.dispatchMetrics; rec != nil { + rec.IncPublished(ctx, 1, attribute.String("op", op)) + } + + // 3. Best-effort signal — the row is the durable intent; reconnect-drain + // is the backstop if the signal is missed or no node owns the broker. + if err := d.commandBus.SignalBrokerCmd(ctx, agent.RuntimeBrokerID); err != nil { + d.log.Warn("deferredLifecycle: signal failed (durable intent is backstop)", + "op", op, "brokerID", agent.RuntimeBrokerID, "error", err) + } + + // 4. Wait for terminal phase. + phase, err := waitForAgentTransition(ctx, eventCh, unsub, terminal) + if err != nil { + return err + } + + // 5. Map terminal phase. + if phase == "error" { + return fmt.Errorf("agent entered error phase during %s", op) + } + return nil } // resolveSecrets queries secrets from all applicable scopes and merges them diff --git a/pkg/hub/messagebroker.go b/pkg/hub/messagebroker.go index ac863a8e6..ae8940207 100644 --- a/pkg/hub/messagebroker.go +++ b/pkg/hub/messagebroker.go @@ -17,6 +17,7 @@ package hub import ( "context" "encoding/json" + "errors" "fmt" "log/slog" "strings" @@ -43,17 +44,18 @@ const brokerCallbackTimeout = 30 * time.Second // - Manages subscriptions based on agent lifecycle events (created/deleted) // - Handles broadcast fan-out from a single broker publish to individual agent deliveries type MessageBrokerProxy struct { - bus eventbus.EventBus - store store.Store - events EventPublisher - getDispatcher func() AgentDispatcher - log *slog.Logger - messageLog *slog.Logger + bus eventbus.EventBus + store store.Store + events EventPublisher + getDispatcher func() AgentDispatcher + signalDeferred func(ctx context.Context, brokerID, agentID string) // NOTIFY wakeup for deferred messages + log *slog.Logger + messageLog *slog.Logger mu sync.Mutex subscriptions map[string][]eventbus.Subscription // projectID -> active subscriptions pluginSubscriptions map[string]eventbus.Subscription // pattern -> plugin-initiated subscription - subscribedTopics map[string]bool // dedup guard for project-level subscriptions + subscribedTopics map[string]bool // dedup guard for project-level subscriptions stopCh chan struct{} stopOnce sync.Once wg sync.WaitGroup @@ -80,6 +82,12 @@ func NewMessageBrokerProxy( } } +// SetSignalDeferred injects the callback used to emit a NOTIFY wakeup when a +// message dispatch is deferred (broker not locally connected). +func (p *MessageBrokerProxy) SetSignalDeferred(fn func(ctx context.Context, brokerID, agentID string)) { + p.signalDeferred = fn +} + // Start subscribes to agent lifecycle events and sets up broker subscriptions // for existing running agents. func (p *MessageBrokerProxy) Start() { @@ -243,7 +251,7 @@ func (p *MessageBrokerProxy) PublishUserMessage(ctx context.Context, projectID, return p.bus.Publish(ctx, topic, msg) } -// PublishToGroup fans out a message to a parsed group of recipients, delegating +// PublishToGroup fans out a message to a parsed set of recipients, delegating // to PublishMessage for agents and PublishUserMessage for users. func (p *MessageBrokerProxy) PublishToGroup(ctx context.Context, projectID string, recipients []messages.GroupRecipient, msg *messages.StructuredMessage) map[string]error { errs := make(map[string]error, len(recipients)) @@ -442,8 +450,6 @@ func (p *MessageBrokerProxy) deliverToUser(ctx context.Context, projectID, topic Urgent: msg.Urgent, Broadcasted: msg.Broadcasted, AgentID: agentID, - Channel: msg.Channel, - ThreadID: msg.ThreadID, CreatedAt: time.Now(), } if err := p.store.CreateMessage(ctx, storeMsg); err != nil { @@ -507,13 +513,8 @@ func (p *MessageBrokerProxy) deliverToAgent(ctx context.Context, projectID, agen return } - if err := dispatcher.DispatchAgentMessage(ctx, agent, msg.Msg, msg.Urgent, msg); err != nil { - p.log.Error("Failed to dispatch broker message to agent", - "agentSlug", agentSlug, "error", err) - return - } - - // Persist to message store (write-through; non-fatal if store fails). + // Persist to message store first so the row is durable intent for + // cross-node dispatch (dispatch_state defaults to pending). storeMsg := &store.Message{ ID: api.NewUUID(), ProjectID: projectID, @@ -526,12 +527,30 @@ func (p *MessageBrokerProxy) deliverToAgent(ctx context.Context, projectID, agen Urgent: msg.Urgent, Broadcasted: msg.Broadcasted, AgentID: agent.ID, - Channel: msg.Channel, - ThreadID: msg.ThreadID, CreatedAt: time.Now(), } if err := p.store.CreateMessage(ctx, storeMsg); err != nil { p.log.Error("Failed to persist broker message to store", "agentSlug", agentSlug, "error", err) + // Without a durable row, a deferred signal has nothing for the + // owning node to reconcile — abort dispatch entirely. + return + } + + if err := dispatcher.DispatchAgentMessage(ctx, agent, msg.Msg, msg.Urgent, msg); errors.Is(err, ErrMessageDeferred) { + if p.signalDeferred != nil { + p.signalDeferred(ctx, agent.RuntimeBrokerID, agent.ID) + } + return + } else if err != nil { + p.log.Error("Failed to dispatch broker message to agent", + "agentSlug", agentSlug, "error", err) + return + } + + // Mark the message as dispatched so reconcileBroker does not + // re-deliver it on the next broker reconnect. + if _, err := p.store.MarkMessageDispatched(ctx, storeMsg.ID); err != nil { + p.log.Error("Failed to mark broker message dispatched", "id", storeMsg.ID, "error", err) } // Log to dedicated message audit log @@ -596,8 +615,8 @@ func (p *MessageBrokerProxy) fanOutGlobal(ctx context.Context, msg *messages.Str } } -// ListChannels returns the names of registered bus channels. Returns nil if -// the underlying bus does not support channel listing. +// ListChannels returns the named bus channels when using a FanOutEventBus, +// or nil for single-bus configurations. Used by the message-channels API. func (p *MessageBrokerProxy) ListChannels() []eventbus.BusChannel { if fb, ok := p.bus.(*eventbus.FanOutEventBus); ok { return fb.BusChannels() diff --git a/pkg/hub/notifications.go b/pkg/hub/notifications.go index a79fcd3ba..4a94ee35f 100644 --- a/pkg/hub/notifications.go +++ b/pkg/hub/notifications.go @@ -17,6 +17,7 @@ package hub import ( "context" "encoding/json" + "errors" "fmt" "log/slog" "strings" @@ -35,6 +36,7 @@ type NotificationDispatcher struct { store store.Store events EventPublisher getDispatcher func() AgentDispatcher // lazy getter; dispatcher may be set after startup + signalDeferred func(ctx context.Context, brokerID, agentID string) // NOTIFY wakeup for deferred messages log *slog.Logger messageLog *slog.Logger // dedicated message audit logger (nil = disabled) channelRegistry *ChannelRegistry // external notification channels (nil = disabled) @@ -358,7 +360,13 @@ func (nd *NotificationDispatcher) dispatchToAgent(ctx context.Context, sub *stor structuredMsg.RecipientID = subscriber.ID structuredMsg.Status = strings.ToUpper(notif.Status) - if err := dispatcher.DispatchAgentMessage(ctx, subscriber, notif.Message, false, structuredMsg); err != nil { + if err := dispatcher.DispatchAgentMessage(ctx, subscriber, notif.Message, false, structuredMsg); errors.Is(err, ErrMessageDeferred) { + nd.log.Info("Notification deferred for cross-node delivery", + "subscriberID", sub.SubscriberID, "brokerID", subscriber.RuntimeBrokerID) + if nd.signalDeferred != nil { + nd.signalDeferred(ctx, subscriber.RuntimeBrokerID, subscriber.ID) + } + } else if err != nil { nd.log.Error("Failed to dispatch notification to agent", "subscriberID", sub.SubscriberID, "error", err) } else { diff --git a/pkg/hub/reaper.go b/pkg/hub/reaper.go new file mode 100644 index 000000000..e8bafd55a --- /dev/null +++ b/pkg/hub/reaper.go @@ -0,0 +1,64 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package hub + +import ( + "context" + "log/slog" + "time" +) + +// Staleness thresholds for the broker-affinity reaper (B5-1, design §7.1). +// +// affinityStaleAge: 2× the defaultAffinityFreshness (90s) used by the routing +// layer in broker_routing.go — a broker that hasn't heartbeated in 3 minutes is +// certainly dead and its affinity is safe to clear. +// +// dispatchStuckAge: 3× the dispatchRollingTimeout (90s) from dispatch_wait.go — +// gives the rolling-timeout wait ample time to fail organically before the +// reaper force-transitions the row. +const ( + affinityStaleAge = 2 * defaultAffinityFreshness // 180s + dispatchStuckAge = 3 * dispatchRollingTimeout // 270s + dispatchMaxRetries = 3 +) + +// brokerAffinityReapHandler returns a recurring handler that clears stale broker +// affinity and re-drives (or fails) stuck dispatches. Registered as a singleton +// so at most one replica runs it per tick. +func (s *Server) brokerAffinityReapHandler() func(ctx context.Context) { + return func(ctx context.Context) { + now := time.Now() + + cleared, err := s.store.ReapStaleBrokerAffinity(ctx, now.Add(-affinityStaleAge)) + if err != nil { + slog.Error("Scheduler: broker affinity reap failed", "error", err) + return + } + + requeued, failed, err := s.store.ReapStuckDispatch(ctx, now.Add(-dispatchStuckAge), dispatchMaxRetries) + if err != nil { + slog.Error("Scheduler: stuck dispatch reap failed", "error", err) + return + } + + if cleared > 0 || requeued > 0 || failed > 0 { + slog.Info("Scheduler: broker affinity reap complete", + "affinity_cleared", cleared, + "dispatch_requeued", requeued, + "dispatch_failed", failed) + } + } +} diff --git a/pkg/hub/reconcile.go b/pkg/hub/reconcile.go new file mode 100644 index 000000000..0c6466579 --- /dev/null +++ b/pkg/hub/reconcile.go @@ -0,0 +1,351 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package hub + +import ( + "context" + "encoding/json" + "fmt" + "log/slog" + "time" + + "github.com/GoogleCloudPlatform/scion/pkg/store" + "go.opentelemetry.io/otel/attribute" +) + +// ReconcileBroker is the exported entry point used by the command-bus signal +// handler (B2-4) to drain durable dispatch intent for a broker this node owns. +func (s *Server) ReconcileBroker(ctx context.Context, brokerID string) { + s.reconcileBroker(ctx, brokerID) +} + +// reconcileBroker drains durable dispatch intent for a broker this node owns: +// pending broker_dispatch rows and pending messages, each CAS-claimed so exactly +// one node executes a given item (design §5.3, §2.0.1). It is the durability +// backstop behind BOTH the command-bus NOTIFY signal and reconnect +// (markBrokerOnline) — so a missed signal or a down owner only delays, never +// loses, a command. Idempotent and safe to run concurrently: the store CAS +// (ClaimBrokerDispatch / MarkMessageDispatched) gates double-execution. +// +// Callers must already hold the broker's control-channel socket (markBrokerOnline +// runs on the accepting node; the command bus filters by ownsLocally), since the +// op executors deliver over the local tunnel. +func (s *Server) reconcileBroker(ctx context.Context, brokerID string) { + if s == nil || s.store == nil || brokerID == "" { + return + } + drainStart := time.Now() + defer func() { + if rec := s.dispatchMetrics; rec != nil { + rec.RecordReconcileDrainDuration(ctx, float64(time.Since(drainStart).Milliseconds())) + } + }() + + // 1. Lifecycle / create-time dispatch intents. + dispatches, err := s.store.ListPendingDispatch(ctx, brokerID) + if err != nil { + s.agentLifecycleLog.Error("reconcile: list pending dispatch failed", "brokerID", brokerID, "error", err) + } + for i := range dispatches { + d := dispatches[i] + claimed, err := s.store.ClaimBrokerDispatch(ctx, d.ID, s.instanceID) + if err != nil { + s.agentLifecycleLog.Error("reconcile: claim dispatch failed", "id", d.ID, "error", err) + continue + } + if !claimed { + continue // another node/drain owns this intent (exactly-once) + } + opAttr := attribute.String("op", d.Op) + if rec := s.dispatchMetrics; rec != nil { + rec.IncClaimed(ctx, 1, opAttr) + } + result, execErr := s.execDispatch(ctx, d) + if execErr != nil { + s.agentLifecycleLog.Warn("reconcile: dispatch op failed", "id", d.ID, "op", d.Op, "error", execErr) + if err := s.store.FailBrokerDispatch(ctx, d.ID, execErr.Error()); err != nil { + s.agentLifecycleLog.Error("reconcile: fail dispatch failed", "id", d.ID, "error", err) + } + if rec := s.dispatchMetrics; rec != nil { + rec.IncFailed(ctx, 1, opAttr) + } + if s.events != nil { + s.events.PublishDispatchDone(ctx, d.ID) + } + continue + } + if err := s.store.CompleteBrokerDispatch(ctx, d.ID, result); err != nil { + s.agentLifecycleLog.Error("reconcile: complete dispatch failed", "id", d.ID, "error", err) + } + if rec := s.dispatchMetrics; rec != nil { + rec.IncDone(ctx, 1, opAttr) + latencyMs := float64(time.Since(d.CreatedAt).Milliseconds()) + rec.RecordDispatchLatency(ctx, latencyMs, opAttr) + } + // Emit a slim completion event so originators waiting on + // waitForDispatchDone wake up (design §6.3). + if s.events != nil { + s.events.PublishDispatchDone(ctx, d.ID) + } + } + + // 2. Pending messages destined for agents on this broker. + msgs, err := s.store.ListPendingMessages(ctx, brokerID) + if err != nil { + s.agentLifecycleLog.Error("reconcile: list pending messages failed", "brokerID", brokerID, "error", err) + return + } + for i := range msgs { + m := msgs[i] + dispatched, err := s.store.MarkMessageDispatched(ctx, m.ID) + if err != nil { + s.agentLifecycleLog.Error("reconcile: mark message dispatched failed", "id", m.ID, "error", err) + continue + } + if !dispatched { + continue // another drain already took it (dedupe) + } + if rec := s.dispatchMetrics; rec != nil { + rec.IncMessageDispatched(ctx, 1) + } + if err := s.deliverMsg(ctx, &m); err != nil { + // At-least-once: the row is already marked dispatched; a delivery + // failure is surfaced by the pending-message sweep (B5-2). Phase 3 + // (B3-3) supplies the real local tunnel + failure handling. + s.agentLifecycleLog.Warn("reconcile: message delivery failed", "id", m.ID, "error", err) + } + } +} + +// executeDispatch runs a claimed dispatch intent's op via the LOCAL broker +// tunnel and returns its result JSON. The lifecycle cases (start/stop/restart) +// deserialize args from the dispatch row and call the local dispatcher, which +// delivers over the in-memory control-channel socket. Unknown ops fail cleanly +// (and are retryable). +func (s *Server) executeDispatch(ctx context.Context, d store.BrokerDispatch) (string, error) { + switch d.Op { + case "start": + return s.execDispatchStart(ctx, d) + case "stop": + return s.execDispatchStop(ctx, d) + case "restart": + return s.execDispatchRestart(ctx, d) + case "delete": + return s.execDispatchDelete(ctx, d) + case "check_prompt": + return s.execDispatchCheckPrompt(ctx, d) + case "finalize_env": + return s.execDispatchFinalizeEnv(ctx, d) + case "create": + return s.execDispatchCreate(ctx, d) + default: + return "", fmt.Errorf("broker dispatch op %q not yet wired on this node", d.Op) + } +} + +func (s *Server) execDispatchStart(ctx context.Context, d store.BrokerDispatch) (string, error) { + agent, err := s.resolveDispatchAgent(ctx, d) + if err != nil { + return "", err + } + dispatcher := s.GetDispatcher() + if dispatcher == nil { + return "", fmt.Errorf("no dispatcher available") + } + var task string + if d.Args != "" { + args, err := UnmarshalStartArgs(d.Args) + if err != nil { + return "", fmt.Errorf("unmarshal start args: %w", err) + } + task = args.Task + } + if err := dispatcher.DispatchAgentStart(ctx, agent, task); err != nil { + return "", fmt.Errorf("dispatch start: %w", err) + } + return "", nil +} + +func (s *Server) execDispatchStop(ctx context.Context, d store.BrokerDispatch) (string, error) { + agent, err := s.resolveDispatchAgent(ctx, d) + if err != nil { + return "", err + } + dispatcher := s.GetDispatcher() + if dispatcher == nil { + return "", fmt.Errorf("no dispatcher available") + } + if err := dispatcher.DispatchAgentStop(ctx, agent); err != nil { + return "", fmt.Errorf("dispatch stop: %w", err) + } + return "", nil +} + +func (s *Server) execDispatchRestart(ctx context.Context, d store.BrokerDispatch) (string, error) { + agent, err := s.resolveDispatchAgent(ctx, d) + if err != nil { + return "", err + } + dispatcher := s.GetDispatcher() + if dispatcher == nil { + return "", fmt.Errorf("no dispatcher available") + } + if err := dispatcher.DispatchAgentRestart(ctx, agent); err != nil { + return "", fmt.Errorf("dispatch restart: %w", err) + } + return "", nil +} + +func (s *Server) execDispatchDelete(ctx context.Context, d store.BrokerDispatch) (string, error) { + agent, err := s.resolveDispatchAgent(ctx, d) + if err != nil { + return "", err + } + dispatcher := s.GetDispatcher() + if dispatcher == nil { + return "", fmt.Errorf("no dispatcher available") + } + var deleteFiles, removeBranch, softDelete bool + var deletedAt time.Time + if d.Args != "" { + args, err := UnmarshalDeleteArgs(d.Args) + if err != nil { + return "", fmt.Errorf("unmarshal delete args: %w", err) + } + deleteFiles = args.DeleteFiles + removeBranch = args.RemoveBranch + softDelete = args.SoftDelete + deletedAt = args.DeletedAt + } + if err := dispatcher.DispatchAgentDelete(ctx, agent, deleteFiles, removeBranch, softDelete, deletedAt); err != nil { + return "", fmt.Errorf("dispatch delete: %w", err) + } + return "", nil +} + +func (s *Server) execDispatchCheckPrompt(ctx context.Context, d store.BrokerDispatch) (string, error) { + agent, err := s.resolveDispatchAgent(ctx, d) + if err != nil { + return "", err + } + dispatcher := s.GetDispatcher() + if dispatcher == nil { + return "", fmt.Errorf("no dispatcher available") + } + hasPrompt, err := dispatcher.DispatchCheckAgentPrompt(ctx, agent) + if err != nil { + return "", fmt.Errorf("dispatch check_prompt: %w", err) + } + result, err := json.Marshal(CheckPromptResult{HasPrompt: hasPrompt}) + if err != nil { + return "", fmt.Errorf("marshal check_prompt result: %w", err) + } + return string(result), nil +} + +func (s *Server) execDispatchFinalizeEnv(ctx context.Context, d store.BrokerDispatch) (string, error) { + agent, err := s.resolveDispatchAgent(ctx, d) + if err != nil { + return "", err + } + dispatcher := s.GetDispatcher() + if dispatcher == nil { + return "", fmt.Errorf("no dispatcher available") + } + var env map[string]string + if d.Args != "" { + args, err := UnmarshalFinalizeEnvArgs(d.Args) + if err != nil { + return "", fmt.Errorf("unmarshal finalize_env args: %w", err) + } + env = args.Env + } + if err := dispatcher.DispatchFinalizeEnv(ctx, agent, env); err != nil { + return "", fmt.Errorf("dispatch finalize_env: %w", err) + } + result, err := json.Marshal(FinalizeEnvResult{Success: true}) + if err != nil { + return "", fmt.Errorf("marshal finalize_env result: %w", err) + } + return string(result), nil +} + +func (s *Server) execDispatchCreate(ctx context.Context, d store.BrokerDispatch) (string, error) { + agent, err := s.resolveDispatchAgent(ctx, d) + if err != nil { + return "", err + } + dispatcher := s.GetDispatcher() + if dispatcher == nil { + return "", fmt.Errorf("no dispatcher available") + } + envReqs, err := dispatcher.DispatchAgentCreateWithGather(ctx, agent) + if err != nil { + return "", fmt.Errorf("dispatch create: %w", err) + } + cr := CreateWithGatherResult{EnvRequirements: envReqs} + result, err := json.Marshal(cr) + if err != nil { + return "", fmt.Errorf("marshal create result: %w", err) + } + return string(result), nil +} + +// resolveDispatchAgent loads the agent from the store by slug (used as the +// identifier in the dispatch row's AgentSlug field, matching the runtime +// broker's slug-based addressing). +func (s *Server) resolveDispatchAgent(ctx context.Context, d store.BrokerDispatch) (*store.Agent, error) { + if d.AgentID != "" { + agent, err := s.store.GetAgent(ctx, d.AgentID) + if err != nil { + return nil, fmt.Errorf("resolve agent %s: %w", d.AgentID, err) + } + return agent, nil + } + return nil, fmt.Errorf("dispatch row has no agent ID") +} + +// deliverMessage tunnels a reconciled message to its agent over the LOCAL +// control channel — the same path DispatchAgentMessage uses for a locally- +// connected broker. reconcileBroker has already CAS-marked the message +// dispatched before calling this, so just deliver. +func (s *Server) deliverMessage(ctx context.Context, m *store.Message) error { + if m == nil || m.AgentID == "" { + return fmt.Errorf("message has no agent ID") + } + agent, err := s.store.GetAgent(ctx, m.AgentID) + if err != nil { + return fmt.Errorf("resolve agent %s: %w", m.AgentID, err) + } + if agent.RuntimeBrokerID == "" { + return fmt.Errorf("agent %s has no runtime broker", m.AgentID) + } + dispatcher := s.GetDispatcher() + if dispatcher == nil { + return fmt.Errorf("no dispatcher available for message delivery") + } + return dispatcher.DispatchAgentMessage(ctx, agent, m.Msg, m.Urgent, nil) +} + +// signalDeferredMessage emits a best-effort NOTIFY wakeup so the broker's +// owning node drains the pending message. Called when route() returns +// routeForward or routeUndeliverable (the message row is already durable). +func (s *Server) signalDeferredMessage(ctx context.Context, brokerID, agentID string) { + if s.commandBus != nil { + if err := s.commandBus.SignalBrokerCmd(ctx, brokerID); err != nil { + slog.Warn("failed to signal deferred message", "brokerID", brokerID, "agentID", agentID, "error", err) + } + } +} diff --git a/pkg/hub/reconcile_test.go b/pkg/hub/reconcile_test.go new file mode 100644 index 000000000..0df283e54 --- /dev/null +++ b/pkg/hub/reconcile_test.go @@ -0,0 +1,245 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build !no_sqlite + +package hub + +import ( + "context" + "log/slog" + "sync" + "sync/atomic" + "testing" + "time" + + "github.com/GoogleCloudPlatform/scion/pkg/messages" + "github.com/GoogleCloudPlatform/scion/pkg/store" + "github.com/GoogleCloudPlatform/scion/pkg/store/entadapter" + "github.com/GoogleCloudPlatform/scion/pkg/store/enttest" + "github.com/google/uuid" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// newReconcileServer builds a minimal Server wired only with what reconcileBroker +// needs, plus overridable executor seams. +func newReconcileServer(st store.Store, exec func(context.Context, store.BrokerDispatch) (string, error), deliver func(context.Context, *store.Message) error) *Server { + return &Server{ + store: st, + instanceID: "hub-" + uuid.NewString()[:8], + agentLifecycleLog: slog.Default(), + execDispatch: exec, + deliverMsg: deliver, + } +} + +func TestReconcileBroker_DrainsDispatchOnce(t *testing.T) { + ctx := context.Background() + cs := entadapter.NewCompositeStore(enttest.NewClient(t)) + var execN int32 + s := newReconcileServer(cs, + func(context.Context, store.BrokerDispatch) (string, error) { atomic.AddInt32(&execN, 1); return `{"ok":true}`, nil }, + func(context.Context, *store.Message) error { return nil }) + + broker := uuid.NewString() + d := &store.BrokerDispatch{ID: uuid.NewString(), BrokerID: broker, Op: "start"} + require.NoError(t, cs.InsertBrokerDispatch(ctx, d)) + + s.reconcileBroker(ctx, broker) + + assert.Equal(t, int32(1), atomic.LoadInt32(&execN), "executor runs once") + pending, err := cs.ListPendingDispatch(ctx, broker) + require.NoError(t, err) + assert.Empty(t, pending, "drained dispatch is no longer pending") +} + +func TestReconcileBroker_ConcurrentDrainsExecuteOnce(t *testing.T) { + ctx := context.Background() + cs := entadapter.NewCompositeStore(enttest.NewClient(t)) + var execN int32 + s := newReconcileServer(cs, + func(context.Context, store.BrokerDispatch) (string, error) { atomic.AddInt32(&execN, 1); return "", nil }, + func(context.Context, *store.Message) error { return nil }) + + broker := uuid.NewString() + require.NoError(t, cs.InsertBrokerDispatch(ctx, &store.BrokerDispatch{ID: uuid.NewString(), BrokerID: broker, Op: "start"})) + + const racers = 6 + var wg sync.WaitGroup + wg.Add(racers) + for i := 0; i < racers; i++ { + go func() { defer wg.Done(); s.reconcileBroker(ctx, broker) }() + } + wg.Wait() + + assert.Equal(t, int32(1), atomic.LoadInt32(&execN), "concurrent drains execute the intent exactly once") +} + +func TestReconcileBroker_FailedOpMarkedFailed(t *testing.T) { + ctx := context.Background() + cs := entadapter.NewCompositeStore(enttest.NewClient(t)) + s := newReconcileServer(cs, + func(context.Context, store.BrokerDispatch) (string, error) { return "", assertErr{} }, + func(context.Context, *store.Message) error { return nil }) + + broker := uuid.NewString() + d := &store.BrokerDispatch{ID: uuid.NewString(), BrokerID: broker, Op: "start"} + require.NoError(t, cs.InsertBrokerDispatch(ctx, d)) + + s.reconcileBroker(ctx, broker) + + pending, err := cs.ListPendingDispatch(ctx, broker) + require.NoError(t, err) + assert.Empty(t, pending, "a failed op leaves no pending row (it is marked failed, not retried in-loop)") +} + +func TestReconcileBroker_DrainsPendingMessageOnce(t *testing.T) { + ctx := context.Background() + client := enttest.NewClient(t) + cs := entadapter.NewCompositeStore(client) + var deliverN int32 + s := newReconcileServer(cs, + func(context.Context, store.BrokerDispatch) (string, error) { return "", nil }, + func(context.Context, *store.Message) error { atomic.AddInt32(&deliverN, 1); return nil }) + + broker := uuid.NewString() + proj := &store.Project{ID: uuid.NewString(), Name: "p", Slug: "p-" + uuid.NewString()[:8], Visibility: store.VisibilityPrivate, OwnerID: uuid.NewString()} + require.NoError(t, cs.CreateProject(ctx, proj)) + agent, err := client.Agent.Create(). + SetSlug("a-" + uuid.NewString()[:8]).SetName("a"). + SetProjectID(uuid.MustParse(proj.ID)).SetRuntimeBrokerID(broker). + Save(ctx) + require.NoError(t, err) + + msg := &store.Message{ID: uuid.NewString(), ProjectID: proj.ID, Sender: "user:x", Recipient: "agent:a", Msg: "hi", AgentID: agent.ID.String()} + require.NoError(t, cs.CreateMessage(ctx, msg)) + + s.reconcileBroker(ctx, broker) + + assert.Equal(t, int32(1), atomic.LoadInt32(&deliverN), "pending message delivered once") + got, err := cs.GetMessage(ctx, msg.ID) + require.NoError(t, err) + assert.Equal(t, store.MessageDispatchDispatched, got.DispatchState) +} + +// TestDeliverMessage_TunnelsViaDispatcher verifies that deliverMessage resolves +// the agent from the store and dispatches via the local AgentDispatcher. +func TestDeliverMessage_TunnelsViaDispatcher(t *testing.T) { + ctx := context.Background() + client := enttest.NewClient(t) + cs := entadapter.NewCompositeStore(client) + + proj := &store.Project{ID: uuid.NewString(), Name: "p", Slug: "p-" + uuid.NewString()[:8], Visibility: store.VisibilityPrivate, OwnerID: uuid.NewString()} + require.NoError(t, cs.CreateProject(ctx, proj)) + + brokerID := uuid.NewString() + agent, err := client.Agent.Create(). + SetSlug("a-" + uuid.NewString()[:8]).SetName("deliver-test"). + SetProjectID(uuid.MustParse(proj.ID)).SetRuntimeBrokerID(brokerID). + Save(ctx) + require.NoError(t, err) + + var dispatched atomic.Int32 + var lastMsg string + fakeDispatcher := &reconcileTestDispatcher{ + onMessage: func(a *store.Agent, msg string) error { + dispatched.Add(1) + lastMsg = msg + return nil + }, + } + + srv := &Server{ + store: cs, + instanceID: "hub-test", + agentLifecycleLog: slog.Default(), + } + srv.SetDispatcher(fakeDispatcher) + srv.deliverMsg = srv.deliverMessage + + m := &store.Message{ + ID: uuid.NewString(), + AgentID: agent.ID.String(), + Msg: "hello from reconcile", + Urgent: true, + } + + err = srv.deliverMsg(ctx, m) + require.NoError(t, err) + assert.Equal(t, int32(1), dispatched.Load(), "message dispatched once") + assert.Equal(t, "hello from reconcile", lastMsg) +} + +// TestDeliverMessage_MissingAgent returns an error when the agent doesn't exist. +func TestDeliverMessage_MissingAgent(t *testing.T) { + ctx := context.Background() + cs := entadapter.NewCompositeStore(enttest.NewClient(t)) + srv := &Server{ + store: cs, + instanceID: "hub-test", + agentLifecycleLog: slog.Default(), + } + srv.deliverMsg = srv.deliverMessage + + m := &store.Message{ID: uuid.NewString(), AgentID: uuid.NewString(), Msg: "test"} + err := srv.deliverMsg(ctx, m) + assert.Error(t, err) + assert.Contains(t, err.Error(), "resolve agent") +} + +// reconcileTestDispatcher is a minimal AgentDispatcher for deliverMessage tests. +type reconcileTestDispatcher struct { + onMessage func(agent *store.Agent, msg string) error +} + +func (d *reconcileTestDispatcher) DispatchAgentCreate(context.Context, *store.Agent) error { return nil } +func (d *reconcileTestDispatcher) DispatchAgentProvision(context.Context, *store.Agent) error { + return nil +} +func (d *reconcileTestDispatcher) DispatchAgentStart(context.Context, *store.Agent, string) error { + return nil +} +func (d *reconcileTestDispatcher) DispatchAgentStop(context.Context, *store.Agent) error { return nil } +func (d *reconcileTestDispatcher) DispatchAgentRestart(context.Context, *store.Agent) error { + return nil +} +func (d *reconcileTestDispatcher) DispatchAgentDelete(_ context.Context, _ *store.Agent, _, _, _ bool, _ time.Time) error { + return nil +} +func (d *reconcileTestDispatcher) DispatchAgentMessage(_ context.Context, agent *store.Agent, msg string, _ bool, _ *messages.StructuredMessage) error { + if d.onMessage != nil { + return d.onMessage(agent, msg) + } + return nil +} +func (d *reconcileTestDispatcher) DispatchAgentLogs(context.Context, *store.Agent, int) (string, error) { + return "", nil +} +func (d *reconcileTestDispatcher) DispatchAgentExec(context.Context, *store.Agent, []string, int) (string, int, error) { + return "", 0, nil +} +func (d *reconcileTestDispatcher) DispatchCheckAgentPrompt(context.Context, *store.Agent) (bool, error) { + return false, nil +} +func (d *reconcileTestDispatcher) DispatchAgentCreateWithGather(context.Context, *store.Agent) (*RemoteEnvRequirementsResponse, error) { + return nil, nil +} +func (d *reconcileTestDispatcher) DispatchFinalizeEnv(context.Context, *store.Agent, map[string]string) error { + return nil +} + +type assertErr struct{} + +func (assertErr) Error() string { return "boom" } diff --git a/pkg/hub/server.go b/pkg/hub/server.go index e1184323f..aa989a0d3 100644 --- a/pkg/hub/server.go +++ b/pkg/hub/server.go @@ -28,6 +28,7 @@ import ( "log/slog" "net" "net/http" + "os" "strings" "sync" "time" @@ -37,6 +38,7 @@ import ( "github.com/GoogleCloudPlatform/scion/pkg/hub/githubapp" "github.com/GoogleCloudPlatform/scion/pkg/messages" "github.com/GoogleCloudPlatform/scion/pkg/observability/dbmetrics" + "github.com/GoogleCloudPlatform/scion/pkg/observability/dispatchmetrics" "github.com/GoogleCloudPlatform/scion/pkg/secret" "github.com/GoogleCloudPlatform/scion/pkg/storage" "github.com/GoogleCloudPlatform/scion/pkg/store" @@ -77,6 +79,14 @@ type ServerConfig struct { // UserTokenConfig holds configuration for user JWT tokens. // If SigningKey is empty, a random key is generated. UserTokenConfig UserTokenConfig + // SharedSigningSecret is the deployment-wide secret (the same value every + // replica receives via --session-secret / SESSION_SECRET) from which the + // agent and user JWT signing keys are derived deterministically. When set, + // every replica derives identical signing keys regardless of its + // host-derived HubID, so a JWT minted by one replica validates on any + // other replica behind the load balancer. When empty, signing keys fall + // back to per-hub storage in the secret backend / store. + SharedSigningSecret string // TrustedProxies is a list of trusted proxy IPs/CIDRs for forwarded headers. TrustedProxies []string // Debug enables verbose debug logging. @@ -261,7 +271,7 @@ type RuntimeBrokerClient interface { // brokerID is used for HMAC authentication lookup. // task is an optional task string to pass to the agent on start. // projectPath is the local filesystem path to the project on the broker. - // projectSlug is the project slug for hub-managed projects (no local provider path). + // projectSlug is the project slug for hub-native projects (no local provider path). // resolvedEnv contains environment variables resolved from Hub storage (API keys, etc.). // harnessConfig is the harness config name to use for the agent (e.g. "claude", "gemini"). // resolvedSecrets contains type-aware secrets (including file-type) for auth resolution. @@ -317,7 +327,7 @@ type RuntimeBrokerClient interface { // Returns the command output, exit code, and any error. ExecAgent(ctx context.Context, brokerID, brokerEndpoint, agentID, projectID string, command []string, timeout int) (string, int, error) - // CleanupProject asks a broker to remove its local hub-managed project directory. + // CleanupProject asks a broker to remove its local hub-native project directory. // brokerID is used for HMAC authentication lookup. // 404 responses are tolerated for idempotency. CleanupProject(ctx context.Context, brokerID, brokerEndpoint, projectSlug string) error @@ -365,7 +375,7 @@ type RemoteCreateAgentRequest struct { // Only populated when GatherEnv is true. EnvSources map[string]string `json:"envSources,omitempty"` - // ProjectSlug is the project slug for hub-managed projects. + // ProjectSlug is the project slug for hub-native projects. // When set, the broker creates the workspace at ~/.scion/projects// // instead of the default worktree-based path. ProjectSlug string `json:"projectSlug,omitempty"` @@ -414,15 +424,6 @@ type RemoteAgentConfig struct { // If the cached template's hash matches, it can be used without re-downloading. TemplateHash string `json:"templateHash,omitempty"` - // HarnessConfigID is the Hub harness-config ID for hydration on the broker. - // When set, the broker fetches the harness-config from the Hub's storage - // backend rather than requiring it on the broker's local filesystem. - HarnessConfigID string `json:"harnessConfigId,omitempty"` - - // HarnessConfigHash is the content hash of the harness-config for cache - // validation, mirroring TemplateHash. - HarnessConfigHash string `json:"harnessConfigHash,omitempty"` - // GitClone specifies git clone parameters for git-anchored projects. // When set, the runtime broker skips workspace mounting and injects env vars // so sciontool can clone the repo inside the container. @@ -509,9 +510,15 @@ type Server struct { controlChannel *ControlChannelManager // WebSocket control channel for runtime brokers authzService *AuthzService // Authorization service for policy evaluation events EventPublisher // Event publisher for real-time SSE updates + commandBus CommandBus // Inter-node dispatch signal bus (nil-safe; nil = no-op) notificationDispatcher *NotificationDispatcher // Notification dispatcher for agent status events + // reconcile op executors (seams): default to executeDispatch/deliverMessage; + // Phase 3/4 supply the real local-tunnel ops; tests override for exactly-once. + execDispatch func(ctx context.Context, d store.BrokerDispatch) (string, error) + deliverMsg func(ctx context.Context, m *store.Message) error maintenance *MaintenanceState // Runtime maintenance mode state hubID string // Unique hub instance ID for secret namespacing + instanceID string // Unique per-process ID (uuid); affinity key for broker dispatch embeddedBrokerID string // Broker ID when running in hub+broker combo mode scheduler *Scheduler // Unified scheduler for recurring tasks cleanupOnce sync.Once // Ensures CleanupResources runs only once @@ -541,6 +548,10 @@ type Server struct { // connection-pool sampler started in StartBackgroundServices. dbMetrics dbmetrics.Recorder + // Broker dispatch metrics recorder (B5-2). Defaults to a disabled no-op + // recorder; SetDispatchMetrics wires a real exporter. + dispatchMetrics dispatchmetrics.Recorder + // stopPoolSampler stops the DB pool-stats sampling goroutine on shutdown. stopPoolSampler func() @@ -569,6 +580,16 @@ type Server struct { githubAppRateLimit *githubapp.RateLimitInfo } +func newInstanceID() string { + if podName := os.Getenv("POD_NAME"); podName != "" { + return podName + "-" + uuid.NewString() + } + return uuid.NewString() +} + +// InstanceID returns the per-process unique identifier for this hub instance. +func (s *Server) InstanceID() string { return s.instanceID } + // New creates a new Hub API server. func New(cfg ServerConfig, s store.Store) (*Server, error) { // Apply defaults for zero-value fields that have meaningful defaults. @@ -585,6 +606,7 @@ func New(cfg ServerConfig, s store.Store) (*Server, error) { events: noopEventPublisher{}, maintenance: NewMaintenanceState(cfg.AdminMode, cfg.MaintenanceMessage), hubID: cfg.HubID, + instanceID: newInstanceID(), // Subsystem loggers agentLifecycleLog: logging.Subsystem("hub.agent-lifecycle"), @@ -678,6 +700,10 @@ func New(cfg ServerConfig, s store.Store) (*Server, error) { } // Initialize audit logger (used by broker auth and invite system) + // Default reconcile-drain op executors (Phase 3/4 supply the real local ops). + srv.execDispatch = srv.executeDispatch + srv.deliverMsg = srv.deliverMessage + srv.auditLogger = NewLogAuditLogger("[Hub Audit]", cfg.Debug) // Initialize broker auth service if enabled @@ -696,10 +722,27 @@ func New(cfg ServerConfig, s store.Store) (*Server, error) { RequestTimeout: 120 * time.Second, Debug: cfg.Debug, }, logging.Subsystem("hub.control-channel")) - // Set disconnect callback to mark broker offline when WebSocket drops - srv.controlChannel.SetOnDisconnect(func(brokerID string) { + // Set disconnect callback to mark broker offline when WebSocket drops. + // Compare-and-clear affinity first: only stamp offline if this hub instance + + // session still owns the broker. If affinity has moved (broker flapped to + // another replica or re-dialed with a newer session), this is a stale + // disconnect and we must NOT clobber the live owner's online status. + srv.controlChannel.SetOnDisconnect(func(brokerID, sessionID string) { ctx := context.Background() - slog.Info("Broker disconnected, marking offline", "brokerID", brokerID) + + cleared, err := s.ReleaseRuntimeBrokerConnection(ctx, brokerID, srv.instanceID, sessionID) + if err != nil { + slog.Error("Failed to release broker affinity on disconnect", "brokerID", brokerID, "sessionID", sessionID, "error", err) + return + } + if !cleared { + // Another replica (or a newer session on this replica) already owns + // the socket. Skip the offline stamp to avoid clobbering it. + slog.Info("broker reconnected elsewhere; skipping offline stamp", "brokerID", brokerID, "staleSession", sessionID) + return + } + + slog.Info("Broker disconnected, marking offline", "brokerID", brokerID, "sessionID", sessionID) if err := s.UpdateRuntimeBrokerHeartbeat(ctx, brokerID, store.BrokerStatusOffline); err != nil { slog.Error("Failed to mark broker offline", "brokerID", brokerID, "error", err) @@ -779,6 +822,18 @@ func New(cfg ServerConfig, s store.Store) (*Server, error) { return srv, nil } +// deriveSharedSigningKey deterministically derives a 32-byte HS256 signing key +// from the deployment's shared signing secret and the logical key name. The key +// name (e.g. "user_signing_key", "agent_signing_key") provides domain +// separation so the user and agent keys differ even though both originate from +// the same shared secret. Every replica configured with the same shared secret +// derives identical keys, which is what lets a JWT minted by one replica be +// validated by another. +func deriveSharedSigningKey(secret, keyName string) []byte { + sum := sha256.Sum256([]byte("scion-hub-signing-key:" + keyName + ":" + secret)) + return sum[:] +} + // ensureSigningKey ensures a signing key exists, loading it if it does // or generating and saving it if it doesn't. // @@ -798,6 +853,27 @@ func (s *Server) ensureSigningKey(ctx context.Context, keyName string, existingK return existingKey, nil } + // When a deployment-wide shared signing secret is configured (the same + // secret every replica receives via --session-secret / SESSION_SECRET), + // derive the signing key deterministically from it. This makes the key + // identical on every replica regardless of the host-derived hub ID, so a + // JWT minted by one replica validates on any other. It mirrors the web + // session cookie store (commit 0515e2a8), whose keys are derived from the + // same shared secret, and is what lets the hub scale horizontally behind a + // load balancer without operators having to pin a matching HubID on each + // replica. Per-host secret-backend storage (below) is bypassed entirely. + if s.config.SharedSigningSecret != "" { + key := deriveSharedSigningKey(s.config.SharedSigningSecret, keyName) + fp := sha256.Sum256(key) + slog.Info("ensureSigningKey: derived from shared signing secret", + "key", keyName, + "source", "shared_secret", + "key_len", len(key), + "sha256_prefix", hex.EncodeToString(fp[:8]), + ) + return key, nil + } + hubID := s.hubID hasSecretBackend := s.secretBackend != nil _, isGCPBackend := s.secretBackend.(*secret.GCPBackend) @@ -1268,6 +1344,13 @@ func (s *Server) SetDBMetrics(rec dbmetrics.Recorder) { s.dbMetrics = rec } +// SetDispatchMetrics wires the broker-dispatch metrics recorder (B5-2). +func (s *Server) SetDispatchMetrics(rec dispatchmetrics.Recorder) { + s.mu.Lock() + defer s.mu.Unlock() + s.dispatchMetrics = rec +} + // GetMaintenanceState returns the runtime maintenance state. func (s *Server) GetMaintenanceState() *MaintenanceState { return s.maintenance @@ -1301,6 +1384,24 @@ func (s *Server) SetEventPublisher(ep EventPublisher) { s.events = ep } +// SetCommandBus sets the inter-node dispatch signal bus. Nil is safe (treated +// as no-op). Called from the server-foreground init path after backend selection. +func (s *Server) SetCommandBus(cb CommandBus) { + s.mu.Lock() + defer s.mu.Unlock() + s.commandBus = cb + if pgBus, ok := cb.(*PostgresCommandBus); ok { + pgBus.SetOnReconnect(func() { + if rec := s.dispatchMetrics; rec != nil { + rec.IncCmdBusReconnects(context.Background(), 1) + } + }) + } +} + +// CommandBus returns the configured command bus, or nil. +func (s *Server) CommandBus() CommandBus { return s.commandBus } + // StartNotificationDispatcher creates and starts the notification dispatcher // if a subscription-capable EventPublisher is available. It uses a lazy getter for the // AgentDispatcher so it works even if SetDispatcher is called later. @@ -1321,6 +1422,7 @@ func (s *Server) StartNotificationDispatcher() { nd := NewNotificationDispatcher(s.store, s.events, s.GetDispatcher, logging.Subsystem("hub.notifications")) nd.messageLog = s.dedicatedMessageLog nd.channelRegistry = s.channelRegistry + nd.signalDeferred = s.signalDeferredMessage s.notificationDispatcher = nd s.notificationDispatcher.Start() } @@ -1344,6 +1446,7 @@ func (s *Server) StartMessageBroker(b eventbus.EventBus) { proxy := NewMessageBrokerProxy(b, s.store, s.events, s.GetDispatcher, logging.Subsystem("hub.broker")) proxy.messageLog = s.dedicatedMessageLog + proxy.SetSignalDeferred(s.signalDeferredMessage) s.messageBrokerProxy = proxy proxy.Start() @@ -1372,7 +1475,9 @@ func (s *Server) CreateAuthenticatedDispatcher() *HTTPAgentDispatcher { // Wrap with hybrid client that prefers control channel var client RuntimeBrokerClient if s.controlChannel != nil { - client = NewHybridBrokerClient(s.controlChannel, httpClient, &hmacBrokerSigner{store: s.store}, s.config.Debug) + hbc := NewHybridBrokerClient(s.controlChannel, httpClient, &hmacBrokerSigner{store: s.store}, s.config.Debug) + hbc.SetAffinityLookup(StoreAffinityLookup(s.store, 0)) + client = hbc } else { client = httpClient } @@ -1416,6 +1521,16 @@ func (s *Server) CreateAuthenticatedDispatcher() *HTTPAgentDispatcher { dispatcher.SetGitHubAppMinter(s) } + // Wire cross-node lifecycle dispatch deps (B4-2) so the dispatcher + // can handle ErrLifecycleDeferred from route-gated Start/Stop/Restart + // by writing durable intent, signaling the owning node, and waiting + // for the terminal phase. In SQLite mode events/commandBus are no-ops, + // and route() always returns routeLocal, so this never triggers. + dispatcher.SetCrossNodeDeps(s.events, s.commandBus) + if s.dispatchMetrics != nil { + dispatcher.SetDispatchMetrics(s.dispatchMetrics) + } + return dispatcher } @@ -1604,12 +1719,16 @@ func (s *Server) messageEventHandler() EventHandler { structuredMsg.Plain = payload.Plain structuredMsg.Urgent = payload.Interrupt - if err := dispatcher.DispatchAgentMessage(ctx, agent, payload.Message, payload.Interrupt, structuredMsg); err != nil { + if err := dispatcher.DispatchAgentMessage(ctx, agent, payload.Message, payload.Interrupt, structuredMsg); errors.Is(err, ErrMessageDeferred) { + s.signalDeferredMessage(ctx, agent.RuntimeBrokerID, agent.ID) + slog.Info("Scheduler: message deferred for cross-node delivery", + "eventID", evt.ID, "agent_id", agent.ID, "agentName", agent.Name) + } else if err != nil { return fmt.Errorf("failed to dispatch message to agent %s: %w", agent.Name, err) + } else { + slog.Info("Scheduler: message delivered to agent", + "eventID", evt.ID, "agent_id", agent.ID, "agentName", agent.Name) } - - slog.Info("Scheduler: message delivered to agent", - "eventID", evt.ID, "agent_id", agent.ID, "agentName", agent.Name) return nil } } @@ -1872,6 +1991,8 @@ func (s *Server) StartBackgroundServices(ctx context.Context) { s.scheduler.RegisterEventHandler("message", s.messageEventHandler()) s.scheduler.RegisterEventHandler("dispatch_agent", s.dispatchAgentEventHandler()) s.scheduler.RegisterRecurringSingleton("schedule-evaluator", 1, store.LockScheduleEvaluator, s.evaluateSchedulesHandler()) + s.scheduler.RegisterRecurringSingleton("broker-affinity-reap", 1, store.LockBrokerAffinityReap, s.brokerAffinityReapHandler()) + s.scheduler.RegisterRecurringSingleton("broker-message-sweep", 1, store.LockBrokerMessageSweep, s.brokerMessageSweepHandler()) // Register GitHub App health check if the app is configured s.mu.RLock() @@ -1985,6 +2106,9 @@ func (s *Server) Shutdown(ctx context.Context) error { if s.events != nil { s.events.Close() } + if s.commandBus != nil { + s.commandBus.Close() + } ctx, cancel := context.WithTimeout(ctx, 30*time.Second) defer cancel() @@ -2024,6 +2148,9 @@ func (s *Server) CleanupResources(ctx context.Context) error { if s.events != nil { s.events.Close() } + if s.commandBus != nil { + s.commandBus.Close() + } if s.logQueryService != nil { s.logQueryService.Close() } @@ -2086,9 +2213,6 @@ func (s *Server) registerRoutes() { s.mux.HandleFunc("/api/v1/harness-configs", s.handleHarnessConfigs) s.mux.HandleFunc("/api/v1/harness-configs/", s.handleHarnessConfigByID) - // Unified, kind/scope-generic resource import (templates + harness-configs). - s.mux.HandleFunc("/api/v1/resources/import", s.handleResourcesImport) - s.mux.HandleFunc("/api/v1/users", s.handleUsers) s.mux.HandleFunc("/api/v1/users/", s.handleUserByID) @@ -2113,9 +2237,6 @@ func (s *Server) registerRoutes() { s.mux.HandleFunc("/api/v1/brokers/join", s.handleBrokerJoin) s.mux.HandleFunc("/api/v1/brokers/", s.handleBrokerByIDRoutes) - // Message channel listing - s.mux.HandleFunc("/api/v1/message-channels", s.handleMessageChannels) - // Broker plugin inbound message delivery s.mux.HandleFunc("/api/v1/broker/inbound", s.handleBrokerInbound) @@ -2449,31 +2570,35 @@ func (s *Server) handleRuntimeBrokerConnect(w http.ResponseWriter, r *http.Reque } // Use the broker ID from header - if err := s.controlChannel.HandleUpgrade(w, r, brokerID); err != nil { + sessionID, err := s.controlChannel.HandleUpgrade(w, r, brokerID) + if err != nil { slog.Error("Upgrade failed for broker", "brokerID", brokerID, "error", err) // Error already written by upgrader return } - s.markBrokerOnline(brokerID) + s.markBrokerOnline(brokerID, sessionID) return } // Use authenticated broker identity - if err := s.controlChannel.HandleUpgrade(w, r, broker.ID()); err != nil { + sessionID, err := s.controlChannel.HandleUpgrade(w, r, broker.ID()) + if err != nil { slog.Error("Upgrade failed for broker", "brokerID", broker.ID(), "error", err) // Error already written by upgrader return } - s.markBrokerOnline(broker.ID()) + s.markBrokerOnline(broker.ID(), sessionID) } // markBrokerOnline updates broker and provider statuses to online after a successful WebSocket connection. -func (s *Server) markBrokerOnline(brokerID string) { +// It claims broker affinity for this hub instance + the connection's sessionID, +// which also bumps status->online and refreshes the heartbeat in one CAS write. +func (s *Server) markBrokerOnline(brokerID, sessionID string) { ctx := context.Background() - slog.Info("Broker connected, marking online", "brokerID", brokerID) + slog.Info("Broker connected, marking online", "brokerID", brokerID, "sessionID", sessionID, "instanceID", s.instanceID) - if err := s.store.UpdateRuntimeBrokerHeartbeat(ctx, brokerID, store.BrokerStatusOnline); err != nil { - slog.Error("Failed to mark broker online", "brokerID", brokerID, "error", err) + if err := s.store.ClaimRuntimeBrokerConnection(ctx, brokerID, s.instanceID, sessionID); err != nil { + slog.Error("Failed to claim broker connection", "brokerID", brokerID, "error", err) } providers, err := s.store.GetBrokerProjects(ctx, brokerID) @@ -2498,6 +2623,12 @@ func (s *Server) markBrokerOnline(brokerID string) { brokerName = broker.Name } s.events.PublishBrokerConnected(ctx, brokerID, brokerName, projectIDs) + + // Durability backstop (design §5.3): the moment this node owns the socket, + // drain any durable dispatch intent that accumulated while the broker was + // offline or owned elsewhere. Async so it never blocks the connect path; + // idempotent + CAS-gated so concurrent drains execute each item once. + go s.reconcileBroker(context.Background(), brokerID) } // isWebSocketUpgrade checks if the request is a WebSocket upgrade request. diff --git a/pkg/hub/server_instanceid_test.go b/pkg/hub/server_instanceid_test.go new file mode 100644 index 000000000..987b23e7c --- /dev/null +++ b/pkg/hub/server_instanceid_test.go @@ -0,0 +1,41 @@ +package hub + +import ( + "testing" +) + +func TestNewInstanceID_NonEmpty(t *testing.T) { + id := newInstanceID() + if id == "" { + t.Fatal("newInstanceID() returned empty string") + } +} + +func TestNewInstanceID_Unique(t *testing.T) { + ids := make(map[string]struct{}, 100) + for i := 0; i < 100; i++ { + id := newInstanceID() + if _, exists := ids[id]; exists { + t.Fatalf("duplicate instanceID on call %d: %s", i, id) + } + ids[id] = struct{}{} + } +} + +func TestInstanceID_AccessorMatchesField(t *testing.T) { + s := &Server{instanceID: newInstanceID()} + if s.InstanceID() == "" { + t.Fatal("InstanceID() returned empty string") + } + if s.InstanceID() != s.instanceID { + t.Fatal("InstanceID() does not match instanceID field") + } +} + +func TestInstanceID_TwoServersDistinct(t *testing.T) { + s1 := &Server{instanceID: newInstanceID()} + s2 := &Server{instanceID: newInstanceID()} + if s1.InstanceID() == s2.InstanceID() { + t.Fatalf("two Servers share the same InstanceID: %s", s1.InstanceID()) + } +} diff --git a/pkg/hub/signing_key_shared_test.go b/pkg/hub/signing_key_shared_test.go new file mode 100644 index 000000000..b3849f025 --- /dev/null +++ b/pkg/hub/signing_key_shared_test.go @@ -0,0 +1,126 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package hub + +import ( + "bytes" + "context" + "testing" +) + +// TestDeriveSharedSigningKey_Deterministic verifies that the derivation is +// stable for a given (secret, keyName) pair and domain-separated across key +// names and secrets. +func TestDeriveSharedSigningKey_Deterministic(t *testing.T) { + const secret = "shared-deployment-secret" + + userA := deriveSharedSigningKey(secret, SecretKeyUserSigningKey) + userB := deriveSharedSigningKey(secret, SecretKeyUserSigningKey) + if !bytes.Equal(userA, userB) { + t.Fatal("same secret + key name must derive identical keys") + } + if len(userA) != 32 { + t.Fatalf("expected a 32-byte key, got %d bytes", len(userA)) + } + + // Domain separation: user vs agent key must differ. + agent := deriveSharedSigningKey(secret, SecretKeyAgentSigningKey) + if bytes.Equal(userA, agent) { + t.Fatal("user and agent keys derived from the same secret must differ") + } + + // A different secret must produce a different key. + other := deriveSharedSigningKey("a-different-secret", SecretKeyUserSigningKey) + if bytes.Equal(userA, other) { + t.Fatal("different secrets must derive different keys") + } +} + +// TestEnsureSigningKey_SharedSecretReplicaPortable is the regression test for +// the cross-replica "session_expired" login loop: two replicas with DIFFERENT +// host-derived hub IDs but the SAME shared signing secret must resolve +// identical signing keys, so a user JWT minted by one replica validates on the +// other. A replica with a different shared secret must NOT be able to validate +// the token. +func TestEnsureSigningKey_SharedSecretReplicaPortable(t *testing.T) { + const sharedSecret = "the-load-balancer-shared-secret" + ctx := context.Background() + + // Two replicas of one logical hub, distinct hub IDs (sha256(hostname)). + replicaA := &Server{hubID: "ca39430276ee", config: ServerConfig{SharedSigningSecret: sharedSecret}} + replicaB := &Server{hubID: "9662ebe99da4", config: ServerConfig{SharedSigningSecret: sharedSecret}} + + // ensureSigningKey returns before touching the store/secret backend when a + // shared secret is set, so a nil store is fine here. + keyA, err := replicaA.ensureSigningKey(ctx, SecretKeyUserSigningKey, nil) + if err != nil { + t.Fatalf("replicaA ensureSigningKey: %v", err) + } + keyB, err := replicaB.ensureSigningKey(ctx, SecretKeyUserSigningKey, nil) + if err != nil { + t.Fatalf("replicaB ensureSigningKey: %v", err) + } + if !bytes.Equal(keyA, keyB) { + t.Fatal("replicas sharing a signing secret must derive identical keys despite different hub IDs") + } + + // Mint a user token on replica A; it must validate on replica B. + svcA, err := NewUserTokenService(UserTokenConfig{SigningKey: keyA}) + if err != nil { + t.Fatalf("NewUserTokenService A: %v", err) + } + svcB, err := NewUserTokenService(UserTokenConfig{SigningKey: keyB}) + if err != nil { + t.Fatalf("NewUserTokenService B: %v", err) + } + + accessToken, _, _, err := svcA.GenerateTokenPair("uid-1", "user@example.com", "User", "admin", ClientTypeWeb) + if err != nil { + t.Fatalf("GenerateTokenPair: %v", err) + } + if _, err := svcB.ValidateUserToken(accessToken); err != nil { + t.Fatalf("token minted on replica A must validate on replica B, got: %v", err) + } + + // Negative: a replica with a different shared secret cannot validate it. + replicaC := &Server{hubID: "ca39430276ee", config: ServerConfig{SharedSigningSecret: "a-totally-different-secret"}} + keyC, err := replicaC.ensureSigningKey(ctx, SecretKeyUserSigningKey, nil) + if err != nil { + t.Fatalf("replicaC ensureSigningKey: %v", err) + } + svcC, err := NewUserTokenService(UserTokenConfig{SigningKey: keyC}) + if err != nil { + t.Fatalf("NewUserTokenService C: %v", err) + } + if _, err := svcC.ValidateUserToken(accessToken); err == nil { + t.Fatal("token must NOT validate under a different shared secret") + } +} + +// TestEnsureSigningKey_PreConfiguredKeyTakesPrecedence verifies that an +// explicitly supplied key still wins over shared-secret derivation, preserving +// existing behavior for callers that pass a key directly. +func TestEnsureSigningKey_PreConfiguredKeyTakesPrecedence(t *testing.T) { + explicit := bytes.Repeat([]byte{0xAB}, 32) + s := &Server{hubID: "host1", config: ServerConfig{SharedSigningSecret: "ignored-because-explicit-key-given"}} + + got, err := s.ensureSigningKey(context.Background(), SecretKeyUserSigningKey, explicit) + if err != nil { + t.Fatalf("ensureSigningKey: %v", err) + } + if !bytes.Equal(got, explicit) { + t.Fatal("a pre-configured key must take precedence over shared-secret derivation") + } +} diff --git a/pkg/hub/sweep.go b/pkg/hub/sweep.go new file mode 100644 index 000000000..dbf0e12bf --- /dev/null +++ b/pkg/hub/sweep.go @@ -0,0 +1,45 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package hub + +import ( + "context" + "time" +) + +const stuckMessageThreshold = 5 * time.Minute + +// brokerMessageSweepHandler returns a handler that counts messages still in +// dispatch_state='pending' beyond the stuck threshold and logs/emits metrics. +// Registered as a RecurringSingleton guarded by LockBrokerMessageSweep (B5-2). +func (s *Server) brokerMessageSweepHandler() func(ctx context.Context) { + return func(ctx context.Context) { + cutoff := time.Now().Add(-stuckMessageThreshold) + count, err := s.store.CountStuckPendingMessages(ctx, cutoff) + if err != nil { + s.agentLifecycleLog.Error("sweep: count stuck pending messages failed", "error", err) + return + } + + if count > 0 { + s.agentLifecycleLog.Warn("sweep: stuck pending messages detected", + "count", count, "threshold", stuckMessageThreshold.String()) + } + + if rec := s.dispatchMetrics; rec != nil { + rec.ObserveMessageStuck(ctx, int64(count)) + } + } +} diff --git a/pkg/hub/web.go b/pkg/hub/web.go index e867486e3..a7fdc896c 100644 --- a/pkg/hub/web.go +++ b/pkg/hub/web.go @@ -17,6 +17,7 @@ package hub import ( "context" "crypto/rand" + "crypto/sha256" "encoding/hex" "encoding/json" "fmt" @@ -143,7 +144,7 @@ type WebServer struct { assets fs.FS // embedded or nil assetsDisk string // filesystem override path, or "" shellTmpl *template.Template - sessionStore *sessions.FilesystemStore + sessionStore *sessions.CookieStore oauthService *OAuthService store store.Store userTokenSvc *UserTokenService @@ -420,28 +421,44 @@ func NewWebServer(cfg WebServerConfig) *WebServer { slog.Warn("No session secret configured, using random key (sessions will not persist across restarts)") } - // Use a filesystem-backed session store so that only a small session ID - // is sent as a cookie. This avoids the 4 KB cookie size limit that can - // be exceeded when JWT tokens are stored in the session. - sessionDir := filepath.Join(os.TempDir(), "scion-sessions") - if err := os.MkdirAll(sessionDir, 0700); err != nil { - slog.Error("Failed to create session directory", "dir", sessionDir, "error", err) - } - fsStore := sessions.NewFilesystemStore(sessionDir, []byte(sessionKey)) - // Remove the default 4096-byte securecookie encoding limit. The - // FilesystemStore writes session data to disk (not cookies), so the - // browser cookie-size cap is irrelevant. JWT tokens stored in the - // session regularly exceed 4096 bytes after gob+base64 encoding, - // which causes Save() to fail and tokens to be silently dropped. - fsStore.MaxLength(0) - fsStore.Options = &sessions.Options{ + // Use an encrypted, signed cookie session store so that NO session state + // lives on a single replica's local filesystem. This is required for + // horizontal scaling: behind a load balancer the OAuth login and callback + // (and every subsequent API request) can land on different replicas. A + // cookie-backed store keeps the whole session — the OAuth CSRF state token, + // the post-login return path, the user identity, and the Hub access/refresh + // tokens — in the client's signed+encrypted cookie, so any replica sharing + // SESSION_SECRET can read it. + // + // The previous FilesystemStore kept this state on one replica's disk, which + // caused intermittent "state_mismatch" login failures (and silently dropped + // post-login sessions) whenever the LB routed a follow-up request to a + // different replica. The whole session encodes to roughly 2.6 KB today — + // well within the browser's ~4 KB per-cookie cap — so the historical + // "JWT tokens exceed 4096 bytes" concern that motivated the disk store no + // longer applies to the current compact HS256 tokens. + // + // Keys are derived deterministically from the shared SESSION_SECRET so all + // replicas agree: a 32-byte HMAC authentication key and a 32-byte AES-256 + // encryption key, with domain separation so the two keys differ. + cookieStore := sessions.NewCookieStore( + deriveSessionKey(sessionKey, "scion-session-hash"), + deriveSessionKey(sessionKey, "scion-session-block"), + ) + cookieStore.Options = &sessions.Options{ Path: "/", MaxAge: 86400, // 24 hours HttpOnly: true, Secure: strings.HasPrefix(cfg.BaseURL, "https://"), SameSite: http.SameSiteLaxMode, } - ws.sessionStore = fsStore + // Keep securecookie's timestamp window in sync with the cookie MaxAge. We + // intentionally leave the default 4096-byte securecookie length limit in + // force (unlike the disk store, which disabled it): if a session ever grew + // past the browser cookie cap, Save() would return an error we can log + // rather than silently emitting an oversized cookie the browser drops. + cookieStore.MaxAge(cookieStore.Options.MaxAge) + ws.sessionStore = cookieStore // Resolve asset source if cfg.AssetsDir != "" { @@ -471,6 +488,17 @@ func NewWebServer(cfg WebServerConfig) *WebServer { return ws } +// deriveSessionKey deterministically derives a 32-byte key from the shared +// session secret and a label. The label provides domain separation so the +// HMAC authentication key and the AES encryption key differ even though both +// originate from the same SESSION_SECRET. Every replica configured with the +// same secret derives identical keys, which is what lets a session cookie +// minted by one replica be validated and decrypted by another. +func deriveSessionKey(secret, label string) []byte { + sum := sha256.Sum256([]byte(label + ":" + secret)) + return sum[:] +} + // SetMaintenanceState sets the shared runtime maintenance state. func (ws *WebServer) SetMaintenanceState(ms *MaintenanceState) { ws.maintenance = ms diff --git a/pkg/observability/dispatchmetrics/dispatchmetrics.go b/pkg/observability/dispatchmetrics/dispatchmetrics.go new file mode 100644 index 000000000..d2ced25d5 --- /dev/null +++ b/pkg/observability/dispatchmetrics/dispatchmetrics.go @@ -0,0 +1,206 @@ +/* +Copyright 2026 The Scion Authors. +*/ + +// Package dispatchmetrics provides Cloud Monitoring scaffolding for the +// multi-node broker-dispatch observability requirement (B5-2). +// +// It defines OpenTelemetry metric instruments for the dispatch pipeline: +// published/claimed/done/failed counters, intent-to-done latency histogram, +// message dispatched/stuck counters, command-bus reconnects, and reconcile +// drain duration. The package mirrors the dbmetrics pattern: a Recorder +// interface backed by an OTel MeterProvider (or no-op when none is supplied). +package dispatchmetrics + +import ( + "context" + "fmt" + + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/metric" + "go.opentelemetry.io/otel/metric/noop" +) + +const instrumentationName = "github.com/GoogleCloudPlatform/scion/pkg/observability/dispatchmetrics" + +const ( + MetricDispatchPublished = "scion.dispatch.published" + MetricDispatchClaimed = "scion.dispatch.claimed" + MetricDispatchDone = "scion.dispatch.done" + MetricDispatchFailed = "scion.dispatch.failed" + MetricDispatchLatency = "scion.dispatch.intent_to_done.duration" + MetricMessageDispatched = "scion.dispatch.message.dispatched" + MetricMessageStuck = "scion.dispatch.message.stuck" + MetricCmdBusReconnects = "scion.dispatch.cmdbus.reconnects" + MetricReconcileDrainDur = "scion.dispatch.reconcile.drain.duration" +) + +// Recorder is the interface callers use to record broker-dispatch metrics. +// All methods are safe to call concurrently and are cheap no-ops when metrics +// are disabled. +type Recorder interface { + IncPublished(ctx context.Context, n int64, attrs ...attribute.KeyValue) + IncClaimed(ctx context.Context, n int64, attrs ...attribute.KeyValue) + IncDone(ctx context.Context, n int64, attrs ...attribute.KeyValue) + IncFailed(ctx context.Context, n int64, attrs ...attribute.KeyValue) + + RecordDispatchLatency(ctx context.Context, ms float64, attrs ...attribute.KeyValue) + + IncMessageDispatched(ctx context.Context, n int64, attrs ...attribute.KeyValue) + ObserveMessageStuck(ctx context.Context, n int64, attrs ...attribute.KeyValue) + + IncCmdBusReconnects(ctx context.Context, n int64, attrs ...attribute.KeyValue) + + RecordReconcileDrainDuration(ctx context.Context, ms float64, attrs ...attribute.KeyValue) + + Enabled() bool +} + +type recorder struct { + enabled bool + + published metric.Int64Counter + claimed metric.Int64Counter + done metric.Int64Counter + failed metric.Int64Counter + latency metric.Float64Histogram + + msgDispatched metric.Int64Counter + msgStuck metric.Int64Gauge + + cmdBusReconn metric.Int64Counter + drainDur metric.Float64Histogram +} + +var _ Recorder = (*recorder)(nil) + +// New creates a Recorder backed by the supplied MeterProvider. If mp is nil, +// a no-op MeterProvider is used and every method becomes a cheap no-op. +func New(mp metric.MeterProvider) (Recorder, error) { + enabled := mp != nil + if mp == nil { + mp = noop.NewMeterProvider() + } + + meter := mp.Meter(instrumentationName) + r := &recorder{enabled: enabled} + var err error + + if r.published, err = meter.Int64Counter( + MetricDispatchPublished, + metric.WithUnit("{dispatch}"), + metric.WithDescription("Number of broker dispatch intents published"), + ); err != nil { + return nil, fmt.Errorf("registering %s: %w", MetricDispatchPublished, err) + } + + if r.claimed, err = meter.Int64Counter( + MetricDispatchClaimed, + metric.WithUnit("{dispatch}"), + metric.WithDescription("Number of broker dispatch intents claimed"), + ); err != nil { + return nil, fmt.Errorf("registering %s: %w", MetricDispatchClaimed, err) + } + + if r.done, err = meter.Int64Counter( + MetricDispatchDone, + metric.WithUnit("{dispatch}"), + metric.WithDescription("Number of broker dispatch intents completed"), + ); err != nil { + return nil, fmt.Errorf("registering %s: %w", MetricDispatchDone, err) + } + + if r.failed, err = meter.Int64Counter( + MetricDispatchFailed, + metric.WithUnit("{dispatch}"), + metric.WithDescription("Number of broker dispatch intents failed"), + ); err != nil { + return nil, fmt.Errorf("registering %s: %w", MetricDispatchFailed, err) + } + + if r.latency, err = meter.Float64Histogram( + MetricDispatchLatency, + metric.WithUnit("ms"), + metric.WithDescription("Latency from dispatch intent creation to completion"), + ); err != nil { + return nil, fmt.Errorf("registering %s: %w", MetricDispatchLatency, err) + } + + if r.msgDispatched, err = meter.Int64Counter( + MetricMessageDispatched, + metric.WithUnit("{message}"), + metric.WithDescription("Number of messages dispatched to remote broker"), + ); err != nil { + return nil, fmt.Errorf("registering %s: %w", MetricMessageDispatched, err) + } + + if r.msgStuck, err = meter.Int64Gauge( + MetricMessageStuck, + metric.WithUnit("{message}"), + metric.WithDescription("Number of messages stuck in pending state beyond threshold"), + ); err != nil { + return nil, fmt.Errorf("registering %s: %w", MetricMessageStuck, err) + } + + if r.cmdBusReconn, err = meter.Int64Counter( + MetricCmdBusReconnects, + metric.WithUnit("{reconnect}"), + metric.WithDescription("Number of command bus listener reconnects"), + ); err != nil { + return nil, fmt.Errorf("registering %s: %w", MetricCmdBusReconnects, err) + } + + if r.drainDur, err = meter.Float64Histogram( + MetricReconcileDrainDur, + metric.WithUnit("ms"), + metric.WithDescription("Duration of a reconcile broker drain cycle"), + ); err != nil { + return nil, fmt.Errorf("registering %s: %w", MetricReconcileDrainDur, err) + } + + return r, nil +} + +// NewDisabled returns a Recorder whose calls are all no-ops. +func NewDisabled() Recorder { + r, _ := New(nil) + return r +} + +func (r *recorder) Enabled() bool { return r.enabled } + +func (r *recorder) IncPublished(ctx context.Context, n int64, attrs ...attribute.KeyValue) { + r.published.Add(ctx, n, metric.WithAttributes(attrs...)) +} + +func (r *recorder) IncClaimed(ctx context.Context, n int64, attrs ...attribute.KeyValue) { + r.claimed.Add(ctx, n, metric.WithAttributes(attrs...)) +} + +func (r *recorder) IncDone(ctx context.Context, n int64, attrs ...attribute.KeyValue) { + r.done.Add(ctx, n, metric.WithAttributes(attrs...)) +} + +func (r *recorder) IncFailed(ctx context.Context, n int64, attrs ...attribute.KeyValue) { + r.failed.Add(ctx, n, metric.WithAttributes(attrs...)) +} + +func (r *recorder) RecordDispatchLatency(ctx context.Context, ms float64, attrs ...attribute.KeyValue) { + r.latency.Record(ctx, ms, metric.WithAttributes(attrs...)) +} + +func (r *recorder) IncMessageDispatched(ctx context.Context, n int64, attrs ...attribute.KeyValue) { + r.msgDispatched.Add(ctx, n, metric.WithAttributes(attrs...)) +} + +func (r *recorder) ObserveMessageStuck(ctx context.Context, n int64, attrs ...attribute.KeyValue) { + r.msgStuck.Record(ctx, n, metric.WithAttributes(attrs...)) +} + +func (r *recorder) IncCmdBusReconnects(ctx context.Context, n int64, attrs ...attribute.KeyValue) { + r.cmdBusReconn.Add(ctx, n, metric.WithAttributes(attrs...)) +} + +func (r *recorder) RecordReconcileDrainDuration(ctx context.Context, ms float64, attrs ...attribute.KeyValue) { + r.drainDur.Record(ctx, ms, metric.WithAttributes(attrs...)) +} diff --git a/pkg/observability/dispatchmetrics/dispatchmetrics_test.go b/pkg/observability/dispatchmetrics/dispatchmetrics_test.go new file mode 100644 index 000000000..958338103 --- /dev/null +++ b/pkg/observability/dispatchmetrics/dispatchmetrics_test.go @@ -0,0 +1,116 @@ +/* +Copyright 2026 The Scion Authors. +*/ + +package dispatchmetrics + +import ( + "context" + "testing" + + "go.opentelemetry.io/otel/attribute" + sdkmetric "go.opentelemetry.io/otel/sdk/metric" + "go.opentelemetry.io/otel/sdk/metric/metricdata" +) + +func TestNewDisabledRegisters(t *testing.T) { + r, err := New(nil) + if err != nil { + t.Fatalf("New(nil) returned error: %v", err) + } + if r == nil { + t.Fatal("New(nil) returned nil Recorder") + } + if r.Enabled() { + t.Error("expected Recorder backed by no-op provider to report Enabled()==false") + } +} + +func TestNewDisabledRecordsAreNoops(t *testing.T) { + r := NewDisabled() + ctx := context.Background() + attrs := []attribute.KeyValue{attribute.String("op", "start")} + + r.IncPublished(ctx, 1, attrs...) + r.IncClaimed(ctx, 1, attrs...) + r.IncDone(ctx, 1, attrs...) + r.IncFailed(ctx, 1, attrs...) + r.RecordDispatchLatency(ctx, 42.5, attrs...) + r.IncMessageDispatched(ctx, 1, attrs...) + r.ObserveMessageStuck(ctx, 3, attrs...) + r.IncCmdBusReconnects(ctx, 1) + r.RecordReconcileDrainDuration(ctx, 10.0) +} + +func TestNewWithRealProviderRegisters(t *testing.T) { + reader := sdkmetric.NewManualReader() + mp := sdkmetric.NewMeterProvider(sdkmetric.WithReader(reader)) + t.Cleanup(func() { _ = mp.Shutdown(context.Background()) }) + + r, err := New(mp) + if err != nil { + t.Fatalf("New(mp) returned error: %v", err) + } + if !r.Enabled() { + t.Error("expected Recorder backed by real provider to report Enabled()==true") + } +} + +func TestRecordedMetricsAreExported(t *testing.T) { + reader := sdkmetric.NewManualReader() + mp := sdkmetric.NewMeterProvider(sdkmetric.WithReader(reader)) + t.Cleanup(func() { _ = mp.Shutdown(context.Background()) }) + + r, err := New(mp) + if err != nil { + t.Fatalf("New(mp) returned error: %v", err) + } + + ctx := context.Background() + attrs := []attribute.KeyValue{attribute.String("op", "start")} + + r.IncPublished(ctx, 2, attrs...) + r.IncClaimed(ctx, 1, attrs...) + r.IncDone(ctx, 1, attrs...) + r.IncFailed(ctx, 1, attrs...) + r.RecordDispatchLatency(ctx, 42.5, attrs...) + r.IncMessageDispatched(ctx, 1, attrs...) + r.ObserveMessageStuck(ctx, 3, attrs...) + r.IncCmdBusReconnects(ctx, 1) + r.RecordReconcileDrainDuration(ctx, 10.0) + + var rm metricdata.ResourceMetrics + if err := reader.Collect(ctx, &rm); err != nil { + t.Fatalf("collecting metrics: %v", err) + } + + got := collectedNames(&rm) + + want := []string{ + MetricDispatchPublished, + MetricDispatchClaimed, + MetricDispatchDone, + MetricDispatchFailed, + MetricDispatchLatency, + MetricMessageDispatched, + MetricMessageStuck, + MetricCmdBusReconnects, + MetricReconcileDrainDur, + } + + for _, name := range want { + if !got[name] { + t.Errorf("expected metric %q to be exported, but it was not present", name) + } + } +} + +func collectedNames(rm *metricdata.ResourceMetrics) map[string]bool { + names := make(map[string]bool) + for _, sm := range rm.ScopeMetrics { + for _, m := range sm.Metrics { + names[m.Name] = true + } + } + return names +} diff --git a/pkg/store/concurrency.go b/pkg/store/concurrency.go index 454afe632..bb7a3ec7f 100644 --- a/pkg/store/concurrency.go +++ b/pkg/store/concurrency.go @@ -50,6 +50,10 @@ const ( // LockGitHubAppHealthCheck guards the periodic GitHub App installation // health check. LockGitHubAppHealthCheck AdvisoryLockKey = 0x5C100005 + // LockBrokerAffinityReap guards the stale broker-affinity + stuck dispatch reaper. + LockBrokerAffinityReap AdvisoryLockKey = 0x5C100006 + // LockBrokerMessageSweep guards the periodic stuck-pending-message sweep (B5-2). + LockBrokerMessageSweep AdvisoryLockKey = 0x5C100007 ) // AdvisoryLocker is implemented by backends that can take a cluster-wide diff --git a/pkg/store/entadapter/broker_affinity_test.go b/pkg/store/entadapter/broker_affinity_test.go new file mode 100644 index 000000000..6044ee395 --- /dev/null +++ b/pkg/store/entadapter/broker_affinity_test.go @@ -0,0 +1,135 @@ +package entadapter + +import ( + "context" + "testing" + + "github.com/GoogleCloudPlatform/scion/pkg/store" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// newOfflineBroker returns an unclaimed broker (offline, no affinity) so tests +// can observe the claim transition. +func newOfflineBroker(t *testing.T, ps *ProjectStore) *store.RuntimeBroker { + t.Helper() + b := newBroker() + b.Status = store.BrokerStatusOffline + require.NoError(t, ps.CreateRuntimeBroker(context.Background(), b)) + return b +} + +func TestClaimRuntimeBrokerConnection_SetsAffinityAndOnline(t *testing.T) { + ps := newTestProjectStore(t) + ctx := context.Background() + b := newOfflineBroker(t, ps) + + require.NoError(t, ps.ClaimRuntimeBrokerConnection(ctx, b.ID, "hub-1", "sess-1")) + + got, err := ps.GetRuntimeBroker(ctx, b.ID) + require.NoError(t, err) + require.NotNil(t, got.ConnectedHubID) + assert.Equal(t, "hub-1", *got.ConnectedHubID) + require.NotNil(t, got.ConnectedSessionID) + assert.Equal(t, "sess-1", *got.ConnectedSessionID) + require.NotNil(t, got.ConnectedAt) + assert.False(t, got.ConnectedAt.IsZero()) + // Claim bumps status->online + refreshes heartbeat in the same write. + assert.Equal(t, store.BrokerStatusOnline, got.Status) + assert.False(t, got.LastHeartbeat.IsZero()) +} + +func TestClaimRuntimeBrokerConnection_NewestWins(t *testing.T) { + ps := newTestProjectStore(t) + ctx := context.Background() + b := newOfflineBroker(t, ps) + + require.NoError(t, ps.ClaimRuntimeBrokerConnection(ctx, b.ID, "hub-1", "sess-1")) + require.NoError(t, ps.ClaimRuntimeBrokerConnection(ctx, b.ID, "hub-2", "sess-2")) + + got, err := ps.GetRuntimeBroker(ctx, b.ID) + require.NoError(t, err) + require.NotNil(t, got.ConnectedHubID) + assert.Equal(t, "hub-2", *got.ConnectedHubID) + require.NotNil(t, got.ConnectedSessionID) + assert.Equal(t, "sess-2", *got.ConnectedSessionID) +} + +func TestReleaseRuntimeBrokerConnection_ClearsWhenOwner(t *testing.T) { + ps := newTestProjectStore(t) + ctx := context.Background() + b := newOfflineBroker(t, ps) + + require.NoError(t, ps.ClaimRuntimeBrokerConnection(ctx, b.ID, "hub-1", "sess-1")) + + cleared, err := ps.ReleaseRuntimeBrokerConnection(ctx, b.ID, "hub-1", "sess-1") + require.NoError(t, err) + assert.True(t, cleared) + + got, err := ps.GetRuntimeBroker(ctx, b.ID) + require.NoError(t, err) + assert.Nil(t, got.ConnectedHubID) + assert.Nil(t, got.ConnectedSessionID) + assert.Nil(t, got.ConnectedAt) + // Release must NOT change status — the caller decides offline based on cleared. + assert.Equal(t, store.BrokerStatusOnline, got.Status) +} + +func TestReleaseRuntimeBrokerConnection_NoOpWhenAffinityMoved(t *testing.T) { + ps := newTestProjectStore(t) + ctx := context.Background() + b := newOfflineBroker(t, ps) + + // Affinity currently owned by (hub-2, sess-2). + require.NoError(t, ps.ClaimRuntimeBrokerConnection(ctx, b.ID, "hub-2", "sess-2")) + + // A stale owner (hub-1, sess-1) tries to release: must be a no-op. + cleared, err := ps.ReleaseRuntimeBrokerConnection(ctx, b.ID, "hub-1", "sess-1") + require.NoError(t, err) + assert.False(t, cleared) + + got, err := ps.GetRuntimeBroker(ctx, b.ID) + require.NoError(t, err) + require.NotNil(t, got.ConnectedHubID) + assert.Equal(t, "hub-2", *got.ConnectedHubID) + require.NotNil(t, got.ConnectedSessionID) + assert.Equal(t, "sess-2", *got.ConnectedSessionID) +} + +func TestReleaseRuntimeBrokerConnection_NoOpWhenUnclaimed(t *testing.T) { + ps := newTestProjectStore(t) + ctx := context.Background() + b := newOfflineBroker(t, ps) + + cleared, err := ps.ReleaseRuntimeBrokerConnection(ctx, b.ID, "hub-1", "sess-1") + require.NoError(t, err) + assert.False(t, cleared) +} + +// TestBrokerAffinity_FlapAtoB reproduces the design §9.4 disconnect race: a +// broker flaps from hub A to hub B; A's delayed onDisconnect must NOT clobber +// B's live ownership. +func TestBrokerAffinity_FlapAtoB(t *testing.T) { + ps := newTestProjectStore(t) + ctx := context.Background() + b := newOfflineBroker(t, ps) + + // t0: socket on hub A (session s1). + require.NoError(t, ps.ClaimRuntimeBrokerConnection(ctx, b.ID, "hubA", "s1")) + // t2: broker re-dials, lands on hub B (session s2); B claims (newest wins). + require.NoError(t, ps.ClaimRuntimeBrokerConnection(ctx, b.ID, "hubB", "s2")) + + // t3: hub A's old socket finally errors -> delayed release for (hubA, s1). + cleared, err := ps.ReleaseRuntimeBrokerConnection(ctx, b.ID, "hubA", "s1") + require.NoError(t, err) + assert.False(t, cleared, "stale owner release must be a no-op") + + // Affinity still names B, status still online (no false offline). + got, err := ps.GetRuntimeBroker(ctx, b.ID) + require.NoError(t, err) + require.NotNil(t, got.ConnectedHubID) + assert.Equal(t, "hubB", *got.ConnectedHubID) + require.NotNil(t, got.ConnectedSessionID) + assert.Equal(t, "s2", *got.ConnectedSessionID) + assert.Equal(t, store.BrokerStatusOnline, got.Status) +} diff --git a/pkg/store/entadapter/broker_dispatch_store_test.go b/pkg/store/entadapter/broker_dispatch_store_test.go new file mode 100644 index 000000000..dc2ef53c1 --- /dev/null +++ b/pkg/store/entadapter/broker_dispatch_store_test.go @@ -0,0 +1,252 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build !no_sqlite + +package entadapter + +import ( + "context" + "sync" + "testing" + "time" + + "github.com/GoogleCloudPlatform/scion/pkg/ent" + "github.com/GoogleCloudPlatform/scion/pkg/store" + "github.com/GoogleCloudPlatform/scion/pkg/store/enttest" + "github.com/google/uuid" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func newDispatch(brokerID, op string) *store.BrokerDispatch { + return &store.BrokerDispatch{ + ID: uuid.NewString(), + BrokerID: brokerID, + Op: op, + } +} + +func TestBrokerDispatch_InsertListPending_OnlyPending(t *testing.T) { + client := enttest.NewClient(t) + s := NewBrokerDispatchStore(client) + ctx := context.Background() + brokerA := uuid.NewString() + brokerB := uuid.NewString() + + d1 := newDispatch(brokerA, "start") + d2 := newDispatch(brokerA, "stop") + dOther := newDispatch(brokerB, "start") + require.NoError(t, s.InsertBrokerDispatch(ctx, d1)) + require.NoError(t, s.InsertBrokerDispatch(ctx, d2)) + require.NoError(t, s.InsertBrokerDispatch(ctx, dOther)) + assert.Equal(t, store.DispatchStatePending, d1.State) + + // Claim d1 -> in_progress; it should drop out of the pending drain. + claimed, err := s.ClaimBrokerDispatch(ctx, d1.ID, "hub-1") + require.NoError(t, err) + assert.True(t, claimed) + + pending, err := s.ListPendingDispatch(ctx, brokerA) + require.NoError(t, err) + require.Len(t, pending, 1) + assert.Equal(t, d2.ID, pending[0].ID, "drain returns only pending rows for the broker") +} + +func TestBrokerDispatch_ClaimOnceThenFalse(t *testing.T) { + client := enttest.NewClient(t) + s := NewBrokerDispatchStore(client) + ctx := context.Background() + + d := newDispatch(uuid.NewString(), "start") + require.NoError(t, s.InsertBrokerDispatch(ctx, d)) + + claimed, err := s.ClaimBrokerDispatch(ctx, d.ID, "hub-1") + require.NoError(t, err) + assert.True(t, claimed) + + again, err := s.ClaimBrokerDispatch(ctx, d.ID, "hub-2") + require.NoError(t, err) + assert.False(t, again, "a second claim of a non-pending row must lose") +} + +func TestBrokerDispatch_ConcurrentClaimSingleWinner(t *testing.T) { + client := enttest.NewClient(t) + s := NewBrokerDispatchStore(client) + ctx := context.Background() + + d := newDispatch(uuid.NewString(), "start") + require.NoError(t, s.InsertBrokerDispatch(ctx, d)) + + const racers = 8 + var wg sync.WaitGroup + var mu sync.Mutex + wins := 0 + wg.Add(racers) + for i := 0; i < racers; i++ { + go func() { + defer wg.Done() + won, err := s.ClaimBrokerDispatch(ctx, d.ID, "hub") + if err == nil && won { + mu.Lock() + wins++ + mu.Unlock() + } + }() + } + wg.Wait() + assert.Equal(t, 1, wins, "exactly one concurrent claim must win (exactly-once execution)") +} + +func TestBrokerDispatch_CompleteAndFail(t *testing.T) { + client := enttest.NewClient(t) + s := NewBrokerDispatchStore(client) + ctx := context.Background() + + d := newDispatch(uuid.NewString(), "check_prompt") + require.NoError(t, s.InsertBrokerDispatch(ctx, d)) + _, err := s.ClaimBrokerDispatch(ctx, d.ID, "hub-1") + require.NoError(t, err) + + require.NoError(t, s.CompleteBrokerDispatch(ctx, d.ID, `{"ok":true}`)) + got, err := client.BrokerDispatch.Get(ctx, uuid.MustParse(d.ID)) + require.NoError(t, err) + assert.Equal(t, store.DispatchStateDone, got.State) + assert.Equal(t, `{"ok":true}`, got.Result) + + d2 := newDispatch(uuid.NewString(), "start") + require.NoError(t, s.InsertBrokerDispatch(ctx, d2)) + _, err = s.ClaimBrokerDispatch(ctx, d2.ID, "hub-1") + require.NoError(t, err) + require.NoError(t, s.FailBrokerDispatch(ctx, d2.ID, "boom")) + got2, err := client.BrokerDispatch.Get(ctx, uuid.MustParse(d2.ID)) + require.NoError(t, err) + assert.Equal(t, store.DispatchStateFailed, got2.State) + assert.Equal(t, "boom", got2.Error) + assert.Equal(t, 1, got2.Attempts, "failure bumps the attempt counter") +} + +func TestMarkMessageDispatched_Dedupe(t *testing.T) { + client := enttest.NewClient(t) + cs := NewCompositeStore(client) + ctx := context.Background() + + msg := &store.Message{ + ID: uuid.NewString(), + ProjectID: uuid.NewString(), + Sender: "user:alice", + Recipient: "agent:bob", + Msg: "hi", + } + require.NoError(t, cs.CreateMessage(ctx, msg)) + assert.Equal(t, store.MessageDispatchPending, msg.DispatchState) + + ok, err := cs.MarkMessageDispatched(ctx, msg.ID) + require.NoError(t, err) + assert.True(t, ok) + + again, err := cs.MarkMessageDispatched(ctx, msg.ID) + require.NoError(t, err) + assert.False(t, again, "second dispatch CAS must dedupe") + + got, err := cs.GetMessage(ctx, msg.ID) + require.NoError(t, err) + assert.Equal(t, store.MessageDispatchDispatched, got.DispatchState) + require.NotNil(t, got.DispatchedAt) +} + +func TestListPendingMessages_ByBrokerAgent(t *testing.T) { + client := enttest.NewClient(t) + cs := NewCompositeStore(client) + ctx := context.Background() + brokerA := uuid.NewString() + brokerB := uuid.NewString() + + // A project and two agents, one per broker. + proj := &store.Project{ID: uuid.NewString(), Name: "p", Slug: "p-" + uuid.NewString()[:8], Visibility: store.VisibilityPrivate, OwnerID: uuid.NewString()} + require.NoError(t, cs.CreateProject(ctx, proj)) + projUID := uuid.MustParse(proj.ID) + agentA := mustCreateAgent(t, client, projUID, brokerA) + agentB := mustCreateAgent(t, client, projUID, brokerB) + + // Pending message to agentA (on brokerA), and one to agentB (on brokerB). + msgA := &store.Message{ID: uuid.NewString(), ProjectID: proj.ID, Sender: "user:x", Recipient: "agent:a", Msg: "for A", AgentID: agentA} + msgB := &store.Message{ID: uuid.NewString(), ProjectID: proj.ID, Sender: "user:x", Recipient: "agent:b", Msg: "for B", AgentID: agentB} + require.NoError(t, cs.CreateMessage(ctx, msgA)) + require.NoError(t, cs.CreateMessage(ctx, msgB)) + + pending, err := cs.ListPendingMessages(ctx, brokerA) + require.NoError(t, err) + require.Len(t, pending, 1) + assert.Equal(t, msgA.ID, pending[0].ID, "only the message for an agent on brokerA") + + // Once dispatched, it drops out of the pending set. + _, err = cs.MarkMessageDispatched(ctx, msgA.ID) + require.NoError(t, err) + pending, err = cs.ListPendingMessages(ctx, brokerA) + require.NoError(t, err) + assert.Empty(t, pending) +} + +func TestCountStuckPendingMessages(t *testing.T) { + client := enttest.NewClient(t) + cs := NewCompositeStore(client) + ctx := context.Background() + + proj := &store.Project{ + ID: uuid.NewString(), Name: "p", Slug: "p-" + uuid.NewString()[:8], + Visibility: store.VisibilityPrivate, OwnerID: uuid.NewString(), + } + require.NoError(t, cs.CreateProject(ctx, proj)) + + // A message created 10 minutes ago (stuck). + oldMsg := &store.Message{ + ID: uuid.NewString(), ProjectID: proj.ID, + Sender: "user:x", Recipient: "agent:a", Msg: "old", + CreatedAt: time.Now().Add(-10 * time.Minute), + } + require.NoError(t, cs.CreateMessage(ctx, oldMsg)) + assert.Equal(t, store.MessageDispatchPending, oldMsg.DispatchState) + + // A message created just now (not stuck). + newMsg := &store.Message{ + ID: uuid.NewString(), ProjectID: proj.ID, + Sender: "user:x", Recipient: "agent:b", Msg: "new", + } + require.NoError(t, cs.CreateMessage(ctx, newMsg)) + + cutoff := time.Now().Add(-5 * time.Minute) + count, err := cs.CountStuckPendingMessages(ctx, cutoff) + require.NoError(t, err) + assert.Equal(t, 1, count, "only the old message is stuck") + + // Dispatch the old message — it should no longer be stuck. + _, err = cs.MarkMessageDispatched(ctx, oldMsg.ID) + require.NoError(t, err) + count, err = cs.CountStuckPendingMessages(ctx, cutoff) + require.NoError(t, err) + assert.Equal(t, 0, count, "dispatched message is not stuck") +} + +func mustCreateAgent(t *testing.T, client *ent.Client, projectID uuid.UUID, brokerID string) string { + t.Helper() + a, err := client.Agent.Create(). + SetSlug("agent-" + uuid.NewString()[:8]). + SetName("agent"). + SetProjectID(projectID). + SetRuntimeBrokerID(brokerID). + Save(context.Background()) + require.NoError(t, err) + return a.ID.String() +} diff --git a/pkg/store/entadapter/brokerdispatch_store.go b/pkg/store/entadapter/brokerdispatch_store.go new file mode 100644 index 000000000..4747f066e --- /dev/null +++ b/pkg/store/entadapter/brokerdispatch_store.go @@ -0,0 +1,337 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package entadapter + +import ( + "context" + "time" + + "github.com/GoogleCloudPlatform/scion/pkg/ent" + "github.com/GoogleCloudPlatform/scion/pkg/ent/agent" + "github.com/GoogleCloudPlatform/scion/pkg/ent/brokerdispatch" + "github.com/GoogleCloudPlatform/scion/pkg/ent/message" + "github.com/GoogleCloudPlatform/scion/pkg/store" +) + +// BrokerDispatchStore is the Ent-backed store for the broker_dispatch durable +// intent table plus the message dispatch-state CAS helpers. Exactly-once +// execution across nodes is enforced by conditional (compare-and-swap) updates +// on the state column — no SELECT ... FOR UPDATE, correct on SQLite + Postgres. +type BrokerDispatchStore struct { + client *ent.Client +} + +// NewBrokerDispatchStore creates a new Ent-backed BrokerDispatchStore. +func NewBrokerDispatchStore(client *ent.Client) *BrokerDispatchStore { + return &BrokerDispatchStore{client: client} +} + +func entBrokerDispatchToStore(e *ent.BrokerDispatch) store.BrokerDispatch { + d := store.BrokerDispatch{ + ID: e.ID.String(), + BrokerID: e.BrokerID.String(), + AgentSlug: e.AgentSlug, + Op: e.Op, + Args: e.Args, + State: e.State, + Result: e.Result, + ClaimedBy: e.ClaimedBy, + Attempts: e.Attempts, + Error: e.Error, + CreatedAt: e.CreatedAt, + UpdatedAt: e.UpdatedAt, + } + if e.AgentID != nil { + d.AgentID = e.AgentID.String() + } + if e.ProjectID != nil { + d.ProjectID = e.ProjectID.String() + } + if e.DeadlineAt != nil { + d.DeadlineAt = e.DeadlineAt + } + return d +} + +// InsertBrokerDispatch persists a new durable dispatch intent. State defaults to +// pending. The generated id and timestamps are written back into d. +func (s *BrokerDispatchStore) InsertBrokerDispatch(ctx context.Context, d *store.BrokerDispatch) error { + if d.BrokerID == "" || d.Op == "" { + return store.ErrInvalidInput + } + brokerUID, err := parseUUID(d.BrokerID) + if err != nil { + return err + } + + create := s.client.BrokerDispatch.Create(). + SetBrokerID(brokerUID). + SetOp(d.Op) + + if d.ID != "" { + uid, err := parseUUID(d.ID) + if err != nil { + return err + } + create.SetID(uid) + } + if d.AgentID != "" { + agentUID, err := parseUUID(d.AgentID) + if err != nil { + return err + } + create.SetAgentID(agentUID) + } + if d.AgentSlug != "" { + create.SetAgentSlug(d.AgentSlug) + } + if d.ProjectID != "" { + projUID, err := parseUUID(d.ProjectID) + if err != nil { + return err + } + create.SetProjectID(projUID) + } + if d.Args != "" { + create.SetArgs(d.Args) + } + if d.State != "" { + create.SetState(d.State) + } + if d.DeadlineAt != nil { + create.SetDeadlineAt(*d.DeadlineAt) + } + + created, err := create.Save(ctx) + if err != nil { + return mapError(err) + } + d.ID = created.ID.String() + d.State = created.State + d.CreatedAt = created.CreatedAt + d.UpdatedAt = created.UpdatedAt + return nil +} + +// ClaimBrokerDispatch atomically transitions a dispatch from pending to +// in_progress, recording the claiming hub instance. It is a CAS keyed on +// state='pending', so exactly one node wins for a given row (design §7). Returns +// claimed=false if the row was not pending (already claimed/done/failed/absent). +func (s *BrokerDispatchStore) ClaimBrokerDispatch(ctx context.Context, id, hubInstanceID string) (bool, error) { + uid, err := parseUUID(id) + if err != nil { + return false, err + } + affected, err := s.client.BrokerDispatch.Update(). + Where(brokerdispatch.IDEQ(uid), brokerdispatch.StateEQ(store.DispatchStatePending)). + SetState(store.DispatchStateInProgress). + SetClaimedBy(hubInstanceID). + SetUpdatedAt(time.Now()). + Save(ctx) + if err != nil { + return false, mapError(err) + } + return affected == 1, nil +} + +// CompleteBrokerDispatch marks a dispatch done and records its result JSON. +// The update is guarded by state=in_progress (CAS) so a done or failed +// dispatch cannot be flipped by a stale or duplicate completion call. +func (s *BrokerDispatchStore) CompleteBrokerDispatch(ctx context.Context, id, result string) error { + uid, err := parseUUID(id) + if err != nil { + return err + } + upd := s.client.BrokerDispatch.Update(). + Where(brokerdispatch.IDEQ(uid), brokerdispatch.StateEQ(store.DispatchStateInProgress)). + SetState(store.DispatchStateDone). + SetUpdatedAt(time.Now()) + if result != "" { + upd.SetResult(result) + } + affected, err := upd.Save(ctx) + if err != nil { + return mapError(err) + } + if affected == 0 { + return store.ErrNotFound + } + return nil +} + +// FailBrokerDispatch marks a dispatch failed, records the error, and bumps the +// attempt counter (so a reaper/retry can bound re-drives). The update is +// guarded by state=in_progress (CAS) so a completed or already-failed dispatch +// cannot be overwritten by a stale failure call. +func (s *BrokerDispatchStore) FailBrokerDispatch(ctx context.Context, id, errMsg string) error { + uid, err := parseUUID(id) + if err != nil { + return err + } + affected, err := s.client.BrokerDispatch.Update(). + Where(brokerdispatch.IDEQ(uid), brokerdispatch.StateEQ(store.DispatchStateInProgress)). + SetState(store.DispatchStateFailed). + SetError(errMsg). + AddAttempts(1). + SetUpdatedAt(time.Now()). + Save(ctx) + if err != nil { + return mapError(err) + } + if affected == 0 { + return store.ErrNotFound + } + return nil +} + +// GetBrokerDispatch returns a single dispatch row by ID. Used by the originator +// to read the result/state after the owner completes the dispatch. +func (s *BrokerDispatchStore) GetBrokerDispatch(ctx context.Context, id string) (*store.BrokerDispatch, error) { + uid, err := parseUUID(id) + if err != nil { + return nil, err + } + row, err := s.client.BrokerDispatch.Get(ctx, uid) + if err != nil { + return nil, mapError(err) + } + d := entBrokerDispatchToStore(row) + return &d, nil +} + +// ListPendingDispatch returns the pending dispatch intents for a broker, oldest +// first — the reconcile-drain query (design §5.3). +func (s *BrokerDispatchStore) ListPendingDispatch(ctx context.Context, brokerID string) ([]store.BrokerDispatch, error) { + brokerUID, err := parseUUID(brokerID) + if err != nil { + return nil, err + } + rows, err := s.client.BrokerDispatch.Query(). + Where(brokerdispatch.BrokerIDEQ(brokerUID), brokerdispatch.StateEQ(store.DispatchStatePending)). + Order(ent.Asc(brokerdispatch.FieldCreatedAt)). + All(ctx) + if err != nil { + return nil, mapError(err) + } + out := make([]store.BrokerDispatch, 0, len(rows)) + for _, r := range rows { + out = append(out, entBrokerDispatchToStore(r)) + } + return out, nil +} + +// MarkMessageDispatched CAS-flips a message from dispatch_state=pending to +// dispatched and stamps dispatched_at. Returns dispatched=false if the row was +// not pending (already dispatched/failed/absent) — dedupes concurrent drains. +func (s *BrokerDispatchStore) MarkMessageDispatched(ctx context.Context, id string) (bool, error) { + uid, err := parseUUID(id) + if err != nil { + return false, err + } + affected, err := s.client.Message.Update(). + Where(message.IDEQ(uid), message.DispatchStateEQ(store.MessageDispatchPending)). + SetDispatchState(store.MessageDispatchDispatched). + SetDispatchedAt(time.Now()). + Save(ctx) + if err != nil { + return false, mapError(err) + } + return affected == 1, nil +} + +// CountStuckPendingMessages returns the number of messages still in +// dispatch_state='pending' whose created timestamp is before the given cutoff. +func (s *BrokerDispatchStore) CountStuckPendingMessages(ctx context.Context, before time.Time) (int, error) { + n, err := s.client.Message.Query(). + Where(message.DispatchStateEQ(store.MessageDispatchPending), message.CreatedLT(before)). + Count(ctx) + if err != nil { + return 0, mapError(err) + } + return n, nil +} + +// ListPendingMessages returns messages still pending delivery whose target agent +// lives on the given broker (messages have no broker_id; the association is via +// the recipient agent's runtime_broker_id). +func (s *BrokerDispatchStore) ListPendingMessages(ctx context.Context, brokerID string) ([]store.Message, error) { + agents, err := s.client.Agent.Query(). + Where(agent.RuntimeBrokerIDEQ(brokerID)). + All(ctx) + if err != nil { + return nil, mapError(err) + } + if len(agents) == 0 { + return nil, nil + } + agentIDs := make([]string, 0, len(agents)) + for _, a := range agents { + agentIDs = append(agentIDs, a.ID.String()) + } + rows, err := s.client.Message.Query(). + Where(message.AgentIDIn(agentIDs...), message.DispatchStateEQ(store.MessageDispatchPending)). + Order(ent.Asc(message.FieldCreated)). + All(ctx) + if err != nil { + return nil, mapError(err) + } + out := make([]store.Message, 0, len(rows)) + for _, r := range rows { + out = append(out, *entMessageToStore(r)) + } + return out, nil +} + +// ReapStuckDispatch re-drives or fails in_progress dispatches that have gone +// stale. Dispatches with attempts < maxAttempts are reset to pending; those at +// or above the limit are marked failed. +func (s *BrokerDispatchStore) ReapStuckDispatch(ctx context.Context, stuckBefore time.Time, maxAttempts int) (requeued, failed int, err error) { + now := time.Now() + + stuckPred := brokerdispatch.And( + brokerdispatch.StateEQ(store.DispatchStateInProgress), + brokerdispatch.Or( + brokerdispatch.UpdatedAtLT(stuckBefore), + brokerdispatch.And( + brokerdispatch.DeadlineAtNotNil(), + brokerdispatch.DeadlineAtLT(now), + ), + ), + ) + + requeued, err = s.client.BrokerDispatch.Update(). + Where(stuckPred, brokerdispatch.AttemptsLT(maxAttempts)). + SetState(store.DispatchStatePending). + ClearClaimedBy(). + AddAttempts(1). + SetUpdatedAt(now). + Save(ctx) + if err != nil { + return 0, 0, mapError(err) + } + + failed, err = s.client.BrokerDispatch.Update(). + Where(stuckPred, brokerdispatch.AttemptsGTE(maxAttempts)). + SetState(store.DispatchStateFailed). + SetError("reaper: max attempts exceeded"). + AddAttempts(1). + SetUpdatedAt(now). + Save(ctx) + if err != nil { + return requeued, 0, mapError(err) + } + + return requeued, failed, nil +} diff --git a/pkg/store/entadapter/composite.go b/pkg/store/entadapter/composite.go index 195e6825d..4b10cad3c 100644 --- a/pkg/store/entadapter/composite.go +++ b/pkg/store/entadapter/composite.go @@ -51,6 +51,7 @@ type CompositeStore struct { *AllowListStore *GroupStore *PolicyStore + *BrokerDispatchStore client *ent.Client } @@ -77,9 +78,10 @@ func NewCompositeStore(client *ent.Client) *CompositeStore { ExternalStore: NewExternalStore(client), BrokerSecretStore: NewBrokerSecretStore(client), AllowListStore: NewAllowListStore(client), - GroupStore: NewGroupStore(client), - PolicyStore: NewPolicyStore(client), - client: client, + GroupStore: NewGroupStore(client), + PolicyStore: NewPolicyStore(client), + BrokerDispatchStore: NewBrokerDispatchStore(client), + client: client, } } diff --git a/pkg/store/entadapter/message_store.go b/pkg/store/entadapter/message_store.go index 1743e1d74..72111b1b0 100644 --- a/pkg/store/entadapter/message_store.go +++ b/pkg/store/entadapter/message_store.go @@ -74,9 +74,11 @@ func entMessageToStore(e *ent.Message) *store.Message { Urgent: e.Urgent, Broadcasted: e.Broadcasted, Read: e.Read, - AgentID: e.AgentID, - GroupID: e.GroupID, - CreatedAt: e.Created, + AgentID: e.AgentID, + GroupID: e.GroupID, + CreatedAt: e.Created, + DispatchState: e.DispatchState, + DispatchedAt: e.DispatchedAt, } } @@ -112,6 +114,12 @@ func (s *MessageStore) CreateMessage(ctx context.Context, msg *store.Message) er if msg.Type == "" { create.SetType("instruction") } + if msg.DispatchState != "" { + create.SetDispatchState(msg.DispatchState) + } + if msg.DispatchedAt != nil { + create.SetDispatchedAt(*msg.DispatchedAt) + } if !msg.CreatedAt.IsZero() { create.SetCreated(msg.CreatedAt) } @@ -122,6 +130,7 @@ func (s *MessageStore) CreateMessage(ctx context.Context, msg *store.Message) er } msg.CreatedAt = created.Created msg.Type = created.Type + msg.DispatchState = created.DispatchState // Design-in: announce the new message for LISTEN/NOTIFY subscribers. // Best-effort — a publish failure must not fail the write that succeeded. diff --git a/pkg/store/entadapter/project_store.go b/pkg/store/entadapter/project_store.go index 7a7e1a79c..056d64089 100644 --- a/pkg/store/entadapter/project_store.go +++ b/pkg/store/entadapter/project_store.go @@ -539,6 +539,9 @@ func entBrokerToStore(b *ent.RuntimeBroker) *store.RuntimeBroker { if b.LastHeartbeat != nil { sb.LastHeartbeat = *b.LastHeartbeat } + sb.ConnectedHubID = b.ConnectedHubID + sb.ConnectedSessionID = b.ConnectedSessionID + sb.ConnectedAt = b.ConnectedAt unmarshalRawJSON(b.Capabilities, &sb.Capabilities) // Profiles are persisted in the "runtimes" column (legacy naming). unmarshalRawJSON(b.Runtimes, &sb.Profiles) @@ -580,6 +583,15 @@ func (s *ProjectStore) CreateRuntimeBroker(ctx context.Context, b *store.Runtime if b.CreatedBy != "" { create.SetCreatedBy(b.CreatedBy) } + if b.ConnectedHubID != nil { + create.SetConnectedHubID(*b.ConnectedHubID) + } + if b.ConnectedSessionID != nil { + create.SetConnectedSessionID(*b.ConnectedSessionID) + } + if b.ConnectedAt != nil { + create.SetConnectedAt(*b.ConnectedAt) + } created, err := create.Save(ctx) if err != nil { @@ -637,7 +649,7 @@ func (s *ProjectStore) UpdateRuntimeBroker(ctx context.Context, b *store.Runtime return mapError(err) } - affected, err := s.client.RuntimeBroker.Update(). + update := s.client.RuntimeBroker.Update(). Where(runtimebroker.IDEQ(uid), runtimebroker.LockVersionEQ(cur.LockVersion)). SetName(b.Name). SetSlug(b.Slug). @@ -652,8 +664,23 @@ func (s *ProjectStore) UpdateRuntimeBroker(ctx context.Context, b *store.Runtime SetEndpoint(b.Endpoint). SetAutoProvide(b.AutoProvide). SetUpdated(now). - AddLockVersion(1). - Save(ctx) + AddLockVersion(1) + if b.ConnectedHubID != nil { + update.SetConnectedHubID(*b.ConnectedHubID) + } else { + update.ClearConnectedHubID() + } + if b.ConnectedSessionID != nil { + update.SetConnectedSessionID(*b.ConnectedSessionID) + } else { + update.ClearConnectedSessionID() + } + if b.ConnectedAt != nil { + update.SetConnectedAt(*b.ConnectedAt) + } else { + update.ClearConnectedAt() + } + affected, err := update.Save(ctx) if err != nil { return mapError(err) } @@ -781,6 +808,108 @@ func (s *ProjectStore) UpdateRuntimeBrokerHeartbeat(ctx context.Context, id stri return store.ErrVersionConflict } +// ClaimRuntimeBrokerConnection records this hub instance as the owner of the +// broker's live control-channel socket. The newest connection wins +// (unconditional claim — mirrors a fresh socket replacing an old one): it sets +// the affinity columns and, in the same CAS write, bumps status to online and +// refreshes last_heartbeat. Uses the lock_version optimistic-concurrency loop, +// like UpdateRuntimeBrokerHeartbeat. +func (s *ProjectStore) ClaimRuntimeBrokerConnection(ctx context.Context, brokerID, hubInstanceID, sessionID string) error { + uid, err := parseUUID(brokerID) + if err != nil { + return err + } + + now := time.Now() + for attempt := 0; attempt < maxCASRetries; attempt++ { + cur, err := s.client.RuntimeBroker.Get(ctx, uid) + if err != nil { + return mapError(err) + } + affected, err := s.client.RuntimeBroker.Update(). + Where(runtimebroker.IDEQ(uid), runtimebroker.LockVersionEQ(cur.LockVersion)). + SetConnectedHubID(hubInstanceID). + SetConnectedSessionID(sessionID). + SetConnectedAt(now). + SetStatus(store.BrokerStatusOnline). + SetLastHeartbeat(now). + SetUpdated(now). + AddLockVersion(1). + Save(ctx) + if err != nil { + return mapError(err) + } + if affected == 1 { + return nil + } + } + return store.ErrVersionConflict +} + +// ReleaseRuntimeBrokerConnection clears the broker's affinity ONLY IF it still +// names (hubInstanceID, sessionID) — a compare-and-clear that fixes the +// disconnect-race: a delayed disconnect from a stale owner/session must not +// clobber a live owner. Returns cleared=true when this caller owned the +// affinity and it was cleared; cleared=false (no-op) when affinity has already +// moved (or was already clear). Does not change status — the caller decides +// whether to stamp offline based on cleared. +func (s *ProjectStore) ReleaseRuntimeBrokerConnection(ctx context.Context, brokerID, hubInstanceID, sessionID string) (bool, error) { + uid, err := parseUUID(brokerID) + if err != nil { + return false, err + } + + now := time.Now() + for attempt := 0; attempt < maxCASRetries; attempt++ { + cur, err := s.client.RuntimeBroker.Get(ctx, uid) + if err != nil { + return false, mapError(err) + } + // Compare: only clear if affinity still names this exact (hub, session). + if cur.ConnectedHubID == nil || *cur.ConnectedHubID != hubInstanceID || + cur.ConnectedSessionID == nil || *cur.ConnectedSessionID != sessionID { + return false, nil + } + affected, err := s.client.RuntimeBroker.Update(). + Where(runtimebroker.IDEQ(uid), runtimebroker.LockVersionEQ(cur.LockVersion)). + ClearConnectedHubID(). + ClearConnectedSessionID(). + ClearConnectedAt(). + SetUpdated(now). + AddLockVersion(1). + Save(ctx) + if err != nil { + return false, mapError(err) + } + if affected == 1 { + return true, nil + } + // affected==0: lock_version moved under us; re-read and re-evaluate the + // compare on the next iteration (affinity may have moved away). + } + return false, store.ErrVersionConflict +} + +// ReapStaleBrokerAffinity clears affinity (connected_hub_id/connected_session_id/ +// connected_at) for brokers that still claim affinity but whose last_heartbeat +// is older than staleBefore. Does not change broker status. +func (s *ProjectStore) ReapStaleBrokerAffinity(ctx context.Context, staleBefore time.Time) (int, error) { + affected, err := s.client.RuntimeBroker.Update(). + Where( + runtimebroker.ConnectedHubIDNotNil(), + runtimebroker.LastHeartbeatLT(staleBefore), + ). + ClearConnectedHubID(). + ClearConnectedSessionID(). + ClearConnectedAt(). + SetUpdated(time.Now()). + Save(ctx) + if err != nil { + return 0, mapError(err) + } + return affected, nil +} + // ============================================================================= // ProjectProvider (project_contributors) operations // ============================================================================= diff --git a/pkg/store/entadapter/reaper_test.go b/pkg/store/entadapter/reaper_test.go new file mode 100644 index 000000000..b388c18bf --- /dev/null +++ b/pkg/store/entadapter/reaper_test.go @@ -0,0 +1,218 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build !no_sqlite + +package entadapter + +import ( + "context" + "testing" + "time" + + "github.com/GoogleCloudPlatform/scion/pkg/store" + "github.com/GoogleCloudPlatform/scion/pkg/store/enttest" + "github.com/google/uuid" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// --------------------------------------------------------------------------- +// ReapStaleBrokerAffinity +// --------------------------------------------------------------------------- + +func TestReapStaleBrokerAffinity_ClearsStaleOnly(t *testing.T) { + ps := newTestProjectStore(t) + ctx := context.Background() + + // Broker with stale heartbeat + affinity → should be reaped. + stale := newBroker() + stale.Status = store.BrokerStatusOnline + require.NoError(t, ps.CreateRuntimeBroker(ctx, stale)) + require.NoError(t, ps.ClaimRuntimeBrokerConnection(ctx, stale.ID, "hub-old", "sess-old")) + // Backdate heartbeat to make it stale. + _, err := ps.client.RuntimeBroker.UpdateOneID(uuid.MustParse(stale.ID)). + SetLastHeartbeat(time.Now().Add(-10 * time.Minute)).Save(ctx) + require.NoError(t, err) + + // Broker with fresh heartbeat + affinity → should NOT be reaped. + fresh := newBroker() + fresh.Status = store.BrokerStatusOnline + require.NoError(t, ps.CreateRuntimeBroker(ctx, fresh)) + require.NoError(t, ps.ClaimRuntimeBrokerConnection(ctx, fresh.ID, "hub-alive", "sess-alive")) + + // Broker with no affinity (NULL connected_hub_id) → should NOT be reaped. + noAffinity := newBroker() + noAffinity.Status = store.BrokerStatusOffline + require.NoError(t, ps.CreateRuntimeBroker(ctx, noAffinity)) + + cleared, err := ps.ReapStaleBrokerAffinity(ctx, time.Now().Add(-3*time.Minute)) + require.NoError(t, err) + assert.Equal(t, 1, cleared) + + // Verify stale broker's affinity was cleared. + got, err := ps.GetRuntimeBroker(ctx, stale.ID) + require.NoError(t, err) + assert.Nil(t, got.ConnectedHubID) + assert.Nil(t, got.ConnectedSessionID) + assert.Nil(t, got.ConnectedAt) + + // Verify fresh broker's affinity is intact. + got, err = ps.GetRuntimeBroker(ctx, fresh.ID) + require.NoError(t, err) + require.NotNil(t, got.ConnectedHubID) + assert.Equal(t, "hub-alive", *got.ConnectedHubID) + + // Verify no-affinity broker is untouched. + got, err = ps.GetRuntimeBroker(ctx, noAffinity.ID) + require.NoError(t, err) + assert.Nil(t, got.ConnectedHubID) +} + +func TestReapStaleBrokerAffinity_NothingToReap(t *testing.T) { + ps := newTestProjectStore(t) + ctx := context.Background() + + b := newBroker() + require.NoError(t, ps.CreateRuntimeBroker(ctx, b)) + require.NoError(t, ps.ClaimRuntimeBrokerConnection(ctx, b.ID, "hub-1", "sess-1")) + + cleared, err := ps.ReapStaleBrokerAffinity(ctx, time.Now().Add(-10*time.Minute)) + require.NoError(t, err) + assert.Equal(t, 0, cleared) +} + +// --------------------------------------------------------------------------- +// ReapStuckDispatch +// --------------------------------------------------------------------------- + +func TestReapStuckDispatch_RedrivesBelowMax(t *testing.T) { + client := enttest.NewClient(t) + ds := NewBrokerDispatchStore(client) + ctx := context.Background() + brokerID := uuid.NewString() + + d := newDispatch(brokerID, "start") + require.NoError(t, ds.InsertBrokerDispatch(ctx, d)) + claimed, err := ds.ClaimBrokerDispatch(ctx, d.ID, "hub-1") + require.NoError(t, err) + require.True(t, claimed) + + // Backdate updated_at to make it stuck. + _, err = client.BrokerDispatch.UpdateOneID(uuid.MustParse(d.ID)). + SetUpdatedAt(time.Now().Add(-10 * time.Minute)).Save(ctx) + require.NoError(t, err) + + requeued, failed, err := ds.ReapStuckDispatch(ctx, time.Now().Add(-5*time.Minute), 3) + require.NoError(t, err) + assert.Equal(t, 1, requeued) + assert.Equal(t, 0, failed) + + got, err := ds.GetBrokerDispatch(ctx, d.ID) + require.NoError(t, err) + assert.Equal(t, store.DispatchStatePending, got.State) + assert.Equal(t, "", got.ClaimedBy) + assert.Equal(t, 1, got.Attempts) +} + +func TestReapStuckDispatch_FailsAtMaxAttempts(t *testing.T) { + client := enttest.NewClient(t) + ds := NewBrokerDispatchStore(client) + ctx := context.Background() + brokerID := uuid.NewString() + + d := newDispatch(brokerID, "stop") + d.State = store.DispatchStateInProgress + require.NoError(t, ds.InsertBrokerDispatch(ctx, d)) + + // Set attempts to maxAttempts. + _, err := client.BrokerDispatch.UpdateOneID(uuid.MustParse(d.ID)). + SetAttempts(3). + SetUpdatedAt(time.Now().Add(-10 * time.Minute)). + Save(ctx) + require.NoError(t, err) + + requeued, failed, err := ds.ReapStuckDispatch(ctx, time.Now().Add(-5*time.Minute), 3) + require.NoError(t, err) + assert.Equal(t, 0, requeued) + assert.Equal(t, 1, failed) + + got, err := ds.GetBrokerDispatch(ctx, d.ID) + require.NoError(t, err) + assert.Equal(t, store.DispatchStateFailed, got.State) + assert.Contains(t, got.Error, "max attempts exceeded") +} + +func TestReapStuckDispatch_LeavesFreshAndTerminal(t *testing.T) { + client := enttest.NewClient(t) + ds := NewBrokerDispatchStore(client) + ctx := context.Background() + brokerID := uuid.NewString() + + // Fresh in_progress dispatch (updated recently) → should NOT be reaped. + fresh := newDispatch(brokerID, "start") + require.NoError(t, ds.InsertBrokerDispatch(ctx, fresh)) + claimed, err := ds.ClaimBrokerDispatch(ctx, fresh.ID, "hub-1") + require.NoError(t, err) + require.True(t, claimed) + + // Done dispatch → should NOT be reaped. + done := newDispatch(brokerID, "stop") + require.NoError(t, ds.InsertBrokerDispatch(ctx, done)) + _, err = ds.ClaimBrokerDispatch(ctx, done.ID, "hub-1") + require.NoError(t, err) + require.NoError(t, ds.CompleteBrokerDispatch(ctx, done.ID, `{"ok":true}`)) + + // Pending dispatch → should NOT be reaped (only in_progress is targeted). + pending := newDispatch(brokerID, "restart") + require.NoError(t, ds.InsertBrokerDispatch(ctx, pending)) + + requeued, failed, err := ds.ReapStuckDispatch(ctx, time.Now().Add(-5*time.Minute), 3) + require.NoError(t, err) + assert.Equal(t, 0, requeued) + assert.Equal(t, 0, failed) + + // Verify states are unchanged. + got, _ := ds.GetBrokerDispatch(ctx, fresh.ID) + assert.Equal(t, store.DispatchStateInProgress, got.State) + + got, _ = ds.GetBrokerDispatch(ctx, done.ID) + assert.Equal(t, store.DispatchStateDone, got.State) + + got, _ = ds.GetBrokerDispatch(ctx, pending.ID) + assert.Equal(t, store.DispatchStatePending, got.State) +} + +func TestReapStuckDispatch_PastDeadline(t *testing.T) { + client := enttest.NewClient(t) + ds := NewBrokerDispatchStore(client) + ctx := context.Background() + brokerID := uuid.NewString() + + d := newDispatch(brokerID, "start") + pastDeadline := time.Now().Add(-1 * time.Minute) + d.DeadlineAt = &pastDeadline + d.State = store.DispatchStateInProgress + require.NoError(t, ds.InsertBrokerDispatch(ctx, d)) + + // updated_at is recent (within threshold), but deadline_at is past. + requeued, failed, err := ds.ReapStuckDispatch(ctx, time.Now().Add(-10*time.Minute), 3) + require.NoError(t, err) + assert.Equal(t, 1, requeued) + assert.Equal(t, 0, failed) + + got, err := ds.GetBrokerDispatch(ctx, d.ID) + require.NoError(t, err) + assert.Equal(t, store.DispatchStatePending, got.State) +} diff --git a/pkg/store/models.go b/pkg/store/models.go index 7007ef2de..5c0bc2e22 100644 --- a/pkg/store/models.go +++ b/pkg/store/models.go @@ -308,6 +308,11 @@ type RuntimeBroker struct { Labels map[string]string `json:"labels,omitempty"` Annotations map[string]string `json:"annotations,omitempty"` + // Affinity — which hub instance currently holds the control-channel socket + ConnectedHubID *string `json:"connectedHubId,omitempty"` + ConnectedSessionID *string `json:"connectedSessionId,omitempty"` + ConnectedAt *time.Time `json:"connectedAt,omitempty"` + // Network endpoint (for direct HTTP mode) Endpoint string `json:"endpoint,omitempty"` @@ -747,6 +752,42 @@ const ( BrokerStatusDegraded = "degraded" ) +// BrokerDispatch is the durable intent for a lifecycle/create-time command +// targeted at a broker (design §5.2). The socket-holding node reconciles it: +// CAS-claim (pending->in_progress) → run the local tunnel op → mark done/failed. +type BrokerDispatch struct { + ID string `json:"id"` + BrokerID string `json:"brokerId"` + AgentID string `json:"agentId,omitempty"` // empty for project-scoped ops + AgentSlug string `json:"agentSlug,omitempty"` + ProjectID string `json:"projectId,omitempty"` // empty if unknown/none + Op string `json:"op"` // start|stop|restart|delete|finalize_env|check_prompt|create|message + Args string `json:"args,omitempty"` // JSON + State string `json:"state"` // pending|in_progress|done|failed + Result string `json:"result,omitempty"` // JSON + ClaimedBy string `json:"claimedBy,omitempty"` // hub instanceID that reconciled it + Attempts int `json:"attempts"` + Error string `json:"error,omitempty"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` + DeadlineAt *time.Time `json:"deadlineAt,omitempty"` +} + +// BrokerDispatch.State values. +const ( + DispatchStatePending = "pending" + DispatchStateInProgress = "in_progress" + DispatchStateDone = "done" + DispatchStateFailed = "failed" +) + +// Message.DispatchState values (the message row is its own dispatch intent). +const ( + MessageDispatchPending = "pending" + MessageDispatchDispatched = "dispatched" + MessageDispatchFailed = "failed" +) + // ============================================================================= // Notifications (Agent Status Notification System) // ============================================================================= @@ -1350,6 +1391,11 @@ type Message struct { Channel string `json:"channel,omitempty"` ThreadID string `json:"threadId,omitempty"` CreatedAt time.Time `json:"createdAt"` + // DispatchState tracks cross-node delivery of the message to the broker: + // pending|dispatched|failed. The message row is its own durable dispatch + // intent (design §5.2/§6.1). + DispatchState string `json:"dispatchState,omitempty"` + DispatchedAt *time.Time `json:"dispatchedAt,omitempty"` } // MarshalJSON implements custom marshaling to support legacy groveId field. diff --git a/pkg/store/store.go b/pkg/store/store.go index 22e93e471..6f357f624 100644 --- a/pkg/store/store.go +++ b/pkg/store/store.go @@ -49,6 +49,9 @@ type Store interface { // RuntimeBroker operations RuntimeBrokerStore + // BrokerDispatch operations (multi-node command dispatch) + BrokerDispatchStore + // Template operations TemplateStore @@ -302,6 +305,27 @@ type RuntimeBrokerStore interface { // UpdateRuntimeBrokerHeartbeat updates the last heartbeat and status. UpdateRuntimeBrokerHeartbeat(ctx context.Context, id string, status string) error + + // ClaimRuntimeBrokerConnection records this hub instance as the owner of the + // broker's live control-channel socket. The newest connection wins + // (unconditional claim): it sets connected_hub_id/connected_session_id/ + // connected_at and, in the same write, bumps status to online and refreshes + // last_heartbeat. + ClaimRuntimeBrokerConnection(ctx context.Context, brokerID, hubInstanceID, sessionID string) error + + // ReleaseRuntimeBrokerConnection clears the broker's affinity ONLY IF it still + // names (hubInstanceID, sessionID) — a compare-and-clear. It returns + // cleared=true when this caller owned the affinity and it was cleared; it + // returns cleared=false (a no-op) when affinity has already moved to another + // hub/session, in which case the caller MUST NOT stamp the broker offline. + // It does not change status (the caller decides offline based on cleared). + ReleaseRuntimeBrokerConnection(ctx context.Context, brokerID, hubInstanceID, sessionID string) (cleared bool, err error) + + // ReapStaleBrokerAffinity clears connected_hub_id/connected_session_id/ + // connected_at for brokers whose last_heartbeat is older than staleBefore + // and whose connected_hub_id is not NULL (i.e. they still claim affinity). + // Returns the number of rows cleared. Does not change broker status. + ReapStaleBrokerAffinity(ctx context.Context, staleBefore time.Time) (cleared int, err error) } // RuntimeBrokerFilter defines criteria for filtering runtime brokers. @@ -312,6 +336,49 @@ type RuntimeBrokerFilter struct { AutoProvide *bool // Filter by auto-provide flag (nil = no filter) } +// BrokerDispatchStore defines persistence for the durable broker-dispatch intent +// table and the message dispatch-state CAS helpers (multi-node command dispatch). +type BrokerDispatchStore interface { + // InsertBrokerDispatch persists a new dispatch intent (state defaults pending). + InsertBrokerDispatch(ctx context.Context, d *BrokerDispatch) error + + // ClaimBrokerDispatch CAS-transitions a dispatch pending->in_progress for the + // given hub instance. Returns claimed=false if it was not pending (so exactly + // one node executes a given intent). + ClaimBrokerDispatch(ctx context.Context, id, hubInstanceID string) (claimed bool, err error) + + // CompleteBrokerDispatch marks a dispatch done with an optional result JSON. + CompleteBrokerDispatch(ctx context.Context, id, result string) error + + // FailBrokerDispatch marks a dispatch failed, records the error, bumps attempts. + FailBrokerDispatch(ctx context.Context, id, errMsg string) error + + // GetBrokerDispatch returns a single dispatch row by ID (used by the + // originator to read the result after the owner completes it). + GetBrokerDispatch(ctx context.Context, id string) (*BrokerDispatch, error) + + // ListPendingDispatch returns pending intents for a broker (drain query). + ListPendingDispatch(ctx context.Context, brokerID string) ([]BrokerDispatch, error) + + // MarkMessageDispatched CAS-flips a message pending->dispatched (dedupes drains). + MarkMessageDispatched(ctx context.Context, id string) (dispatched bool, err error) + + // ListPendingMessages returns pending messages whose target agent is on the broker. + ListPendingMessages(ctx context.Context, brokerID string) ([]Message, error) + + // ReapStuckDispatch re-drives or fails in_progress dispatches that have gone + // stale (updated_at < stuckBefore). Dispatches with attempts < maxAttempts + // are reset to pending (re-driven); those at or above the limit are failed. + // Returns counts of re-driven and failed rows. + ReapStuckDispatch(ctx context.Context, stuckBefore time.Time, maxAttempts int) (requeued, failed int, err error) + + // CountStuckPendingMessages returns the number of messages still in + // dispatch_state='pending' whose created timestamp is before the given + // cutoff. Used by the stuck-message sweep (B5-2) to surface messages that + // have not been dispatched within the expected window. + CountStuckPendingMessages(ctx context.Context, before time.Time) (int, error) +} + // TemplateStore defines template persistence operations. type TemplateStore interface { // CreateTemplate creates a new template record. diff --git a/scratch/apitest/main.go b/scratch/apitest/main.go new file mode 100644 index 000000000..336e61306 --- /dev/null +++ b/scratch/apitest/main.go @@ -0,0 +1,237 @@ +// Command apitest drives API-level multi-hub integration/stress traffic against +// two running Scion hubs that share one CloudSQL Postgres instance. It validates +// the connection-pool / keepalive fixes and multi-replica behavior through the +// real HTTP API. Run it ON a hub VM so it reaches both hubs over the fast +// internal network. Not part of the product. +// +// Env: +// A_BASE, B_BASE base URLs (e.g. http://localhost:8080, http://10.128.15.241:8080) +// A_TOK, B_TOK admin bearer tokens (per-hub signing keys) +package main + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" + "os" + "sort" + "sync" + "sync/atomic" + "time" + + "github.com/google/uuid" +) + +type hub struct { + name string + base string + tok string +} + +var client = &http.Client{Timeout: 35 * time.Second} + +func req(h hub, method, path string, body any) (int, []byte, time.Duration) { + var rdr io.Reader + if body != nil { + b, _ := json.Marshal(body) + rdr = bytes.NewReader(b) + } + r, _ := http.NewRequest(method, h.base+path, rdr) + r.Header.Set("Authorization", "Bearer "+h.tok) + if body != nil { + r.Header.Set("Content-Type", "application/json") + } + start := time.Now() + resp, err := client.Do(r) + d := time.Since(start) + if err != nil { + return 0, []byte(err.Error()), d + } + defer resp.Body.Close() + rb, _ := io.ReadAll(resp.Body) + return resp.StatusCode, rb, d +} + +func pct(ds []time.Duration, p float64) time.Duration { + if len(ds) == 0 { + return 0 + } + sort.Slice(ds, func(i, j int) bool { return ds[i] < ds[j] }) + i := int(float64(len(ds)) * p) + if i >= len(ds) { + i = len(ds) - 1 + } + return ds[i] +} + +func main() { + A := hub{"A", os.Getenv("A_BASE"), os.Getenv("A_TOK")} + B := hub{"B", os.Getenv("B_BASE"), os.Getenv("B_TOK")} + hubs := []hub{A, B} + + // ---- Phase 1: concurrent CRUD storm across both hubs ---- + fmt.Println("== Phase 1: concurrent project CRUD storm (both hubs) ==") + const workers, iters = 24, 30 + var ok, fail, stalls int64 + latMu := sync.Mutex{} + lat := map[string][]time.Duration{"A": {}, "B": {}} + var wg sync.WaitGroup + t0 := time.Now() + for w := 0; w < workers; w++ { + wg.Add(1) + go func(w int) { + defer wg.Done() + h := hubs[w%2] + for i := 0; i < iters; i++ { + name := fmt.Sprintf("stress-%d-%d-%s", w, i, uuid.NewString()[:8]) + st, body, d := req(h, "POST", "/api/v1/projects", map[string]string{"name": name}) + if d > 2*time.Second { + atomic.AddInt64(&stalls, 1) + } + if st != 201 && st != 200 { + atomic.AddInt64(&fail, 1) + if i == 0 { + fmt.Printf(" [%s] create failed st=%d body=%.120s\n", h.name, st, body) + } + continue + } + var pr struct { + ID string `json:"id"` + } + json.Unmarshal(body, &pr) + req(h, "GET", "/api/v1/projects/"+pr.ID, nil) + req(h, "GET", "/api/v1/projects?limit=5", nil) + dst, _, dd := req(h, "DELETE", "/api/v1/projects/"+pr.ID, nil) + if dd > 2*time.Second { + atomic.AddInt64(&stalls, 1) + } + if dst >= 200 && dst < 300 { + atomic.AddInt64(&ok, 1) + } else { + atomic.AddInt64(&fail, 1) + } + latMu.Lock() + lat[h.name] = append(lat[h.name], d) + latMu.Unlock() + } + }(w) + } + wg.Wait() + dur := time.Since(t0) + total := int64(workers * iters) + fmt.Printf(" full CRUD cycles ok=%d fail=%d of %d in %s (%.0f cycles/s), stalls(>2s)=%d\n", + ok, fail, total, dur.Truncate(time.Millisecond), float64(total)/dur.Seconds(), stalls) + for _, n := range []string{"A", "B"} { + fmt.Printf(" hub %s create-latency p50=%s p95=%s max=%s (n=%d)\n", + n, pct(lat[n], 0.5), pct(lat[n], 0.95), pct(lat[n], 1.0), len(lat[n])) + } + + // ---- Phase 2: cross-replica read-after-write (create A, read B) ---- + fmt.Println("== Phase 2: cross-replica read-after-write (create on A, GET on B) ==") + const rw = 40 + var immediate, delayed, miss int + for i := 0; i < rw; i++ { + name := "raw-" + uuid.NewString()[:10] + st, body, _ := req(A, "POST", "/api/v1/projects", map[string]string{"name": name}) + if st != 201 && st != 200 { + miss++ + continue + } + var pr struct { + ID string `json:"id"` + } + json.Unmarshal(body, &pr) + got := false + for attempt := 0; attempt < 10; attempt++ { + s2, _, _ := req(B, "GET", "/api/v1/projects/"+pr.ID, nil) + if s2 == 200 { + if attempt == 0 { + immediate++ + } else { + delayed++ + } + got = true + break + } + time.Sleep(50 * time.Millisecond) + } + if !got { + miss++ + } + req(A, "DELETE", "/api/v1/projects/"+pr.ID, nil) + } + fmt.Printf(" read-after-write: immediate=%d delayed=%d miss=%d of %d\n", immediate, delayed, miss, rw) + + // ---- Phase 3: conflict -> HTTP 409 (concurrent duplicate-ID creates) ---- + fmt.Println("== Phase 3: concurrent duplicate-ID create -> expect exactly one 201, rest 409 ==") + const rounds = 25 + var created, conflict, other int + for i := 0; i < rounds; i++ { + id := uuid.NewString() + name := "dup-" + id[:8] + var c201, c409, cother int64 + var w2 sync.WaitGroup + // 4 concurrent creators (2 per hub) racing on the same explicit ID. + for k := 0; k < 4; k++ { + w2.Add(1) + go func(k int) { + defer w2.Done() + h := hubs[k%2] + st, _, _ := req(h, "POST", "/api/v1/projects", map[string]any{"id": id, "name": name}) + switch { + case st == 201 || st == 200: + atomic.AddInt64(&c201, 1) + case st == 409: + atomic.AddInt64(&c409, 1) + default: + atomic.AddInt64(&cother, 1) + } + }(k) + } + w2.Wait() + created += int(c201) + conflict += int(c409) + other += int(cother) + req(A, "DELETE", "/api/v1/projects/"+id, nil) + } + fmt.Printf(" over %d rounds (4 racers each): 201=%d 409=%d other=%d (ideal: 201==%d, 409==%d)\n", + rounds, created, conflict, other, rounds, rounds*3) + + // ---- Phase 4: idle-then-burst (the stale-connection scenario) ---- + idleStr := os.Getenv("IDLE_SECONDS") + idle := 75 + fmt.Sscanf(idleStr, "%d", &idle) + fmt.Printf("== Phase 4: idle %ds then burst (validates keepalive/idle-recycle fix) ==\n", idle) + for _, h := range hubs { // warm the pools + for i := 0; i < 5; i++ { + req(h, "GET", "/api/v1/projects?limit=1", nil) + } + } + fmt.Printf(" pools warm; sleeping %ds to force idle...\n", idle) + time.Sleep(time.Duration(idle) * time.Second) + for _, h := range hubs { + var first time.Duration + var maxd time.Duration + for i := 0; i < 10; i++ { + st, _, d := req(h, "GET", "/api/v1/projects?limit=1", nil) + if i == 0 { + first = d + } + if d > maxd { + maxd = d + } + if st != 200 { + fmt.Printf(" [%s] burst req %d unexpected st=%d\n", h.name, i, st) + } + } + verdict := "OK" + if first > 2*time.Second { + verdict = "STALL (likely dead idle conn)" + } + fmt.Printf(" hub %s post-idle first-request=%s max=%s -> %s\n", + h.name, first.Truncate(time.Millisecond), maxd.Truncate(time.Millisecond), verdict) + } + fmt.Println("== done ==") +} diff --git a/scratch/dbdiag/main.go b/scratch/dbdiag/main.go new file mode 100644 index 000000000..8cc7d92fc --- /dev/null +++ b/scratch/dbdiag/main.go @@ -0,0 +1,42 @@ +// Command dbdiag prints CloudSQL connection usage for diagnosing pool +// saturation. Not part of the product. +package main + +import ( + "context" + "fmt" + "os" + + "github.com/jackc/pgx/v5" +) + +func main() { + ctx := context.Background() + conn, err := pgx.Connect(ctx, os.Getenv("PG_DSN")) + if err != nil { + fmt.Fprintln(os.Stderr, "connect:", err) + os.Exit(1) + } + defer conn.Close(ctx) + + var maxc, used int + conn.QueryRow(ctx, "SHOW max_connections").Scan(&maxc) + conn.QueryRow(ctx, "SELECT count(*) FROM pg_stat_activity WHERE datname='scion_test'").Scan(&used) + fmt.Printf("max_connections=%d total_on_scion_test=%d\n", maxc, used) + + rows, _ := conn.Query(ctx, `SELECT COALESCE(application_name,'(none)'), state, count(*) + FROM pg_stat_activity WHERE datname='scion_test' + GROUP BY 1,2 ORDER BY 3 DESC`) + defer rows.Close() + fmt.Printf("%-32s %-20s %s\n", "application_name", "state", "count") + for rows.Next() { + var app, state string + var n int + rows.Scan(&app, &state, &n) + fmt.Printf("%-32s %-20s %d\n", app, state, n) + } + // Advisory locks currently held. + var locks int + conn.QueryRow(ctx, "SELECT count(*) FROM pg_locks WHERE locktype='advisory'").Scan(&locks) + fmt.Printf("advisory_locks_held=%d\n", locks) +} diff --git a/scratch/dbdiag2/main.go b/scratch/dbdiag2/main.go new file mode 100644 index 000000000..274fb1173 --- /dev/null +++ b/scratch/dbdiag2/main.go @@ -0,0 +1,17 @@ +package main +import ("context";"fmt";"os";"time";"github.com/jackc/pgx/v5") +func main(){ + ctx:=context.Background() + c,_:=pgx.Connect(ctx,os.Getenv("PG_DSN")); defer c.Close(ctx) + for i:=0;i<14;i++{ + rows,_:=c.Query(ctx,`SELECT client_addr::text, state, count(*) FROM pg_stat_activity WHERE datname='scion_test' AND client_addr IS NOT NULL GROUP BY 1,2 ORDER BY 1,2`) + m:=map[string]int{} + for rows.Next(){var a,s string;var n int;rows.Scan(&a,&s,&n);m[a+"/"+s]=n} + rows.Close() + var locks,waiting int + c.QueryRow(ctx,"SELECT count(*) FROM pg_locks WHERE locktype='advisory'").Scan(&locks) + c.QueryRow(ctx,"SELECT count(*) FROM pg_stat_activity WHERE wait_event_type='Client' AND datname='scion_test'").Scan(&waiting) + fmt.Printf("t+%2ds locks=%d %v\n",i*5,locks,m) + time.Sleep(5*time.Second) + } +} diff --git a/scratch/minttoken/main.go b/scratch/minttoken/main.go new file mode 100644 index 000000000..705957868 --- /dev/null +++ b/scratch/minttoken/main.go @@ -0,0 +1,61 @@ +// Command minttoken mints a user access-token JWT for API-level integration +// testing against the running hubs. It looks up an existing (preferably admin) +// user in the shared Postgres DB and signs a token with the per-hub signing key +// read from Secret Manager. Not part of the product; used only for test driving. +package main + +import ( + "context" + "encoding/base64" + "fmt" + "os" + + "github.com/jackc/pgx/v5" + + "github.com/GoogleCloudPlatform/scion/pkg/hub" +) + +func main() { + dsn := os.Getenv("PG_DSN") + keyB64 := os.Getenv("SIGNING_KEY_B64") + if dsn == "" || keyB64 == "" { + fmt.Fprintln(os.Stderr, "PG_DSN and SIGNING_KEY_B64 required") + os.Exit(1) + } + key, err := base64.StdEncoding.DecodeString(keyB64) + if err != nil { + fmt.Fprintln(os.Stderr, "decode key:", err) + os.Exit(1) + } + + ctx := context.Background() + conn, err := pgx.Connect(ctx, dsn) + if err != nil { + fmt.Fprintln(os.Stderr, "db connect:", err) + os.Exit(1) + } + defer conn.Close(ctx) + + var id, email, displayName, role string + // Prefer an admin; fall back to any user. + err = conn.QueryRow(ctx, `SELECT id::text, email, display_name, role FROM users + ORDER BY (role = 'admin') DESC, created ASC LIMIT 1`).Scan(&id, &email, &displayName, &role) + if err != nil { + fmt.Fprintln(os.Stderr, "user lookup:", err) + os.Exit(1) + } + + svc, err := hub.NewUserTokenService(hub.UserTokenConfig{SigningKey: key}) + if err != nil { + fmt.Fprintln(os.Stderr, "token service:", err) + os.Exit(1) + } + // CLI client type → long (30-day) validity so the token outlives the test run. + token, _, err := svc.GenerateAccessToken(id, email, displayName, role, hub.ClientTypeCLI) + if err != nil { + fmt.Fprintln(os.Stderr, "mint:", err) + os.Exit(1) + } + fmt.Fprintf(os.Stderr, "user=%s email=%s role=%s\n", id, email, role) + fmt.Println(token) +} diff --git a/scratch/nm2-test-pod-a.yaml b/scratch/nm2-test-pod-a.yaml new file mode 100644 index 000000000..cab1f7892 --- /dev/null +++ b/scratch/nm2-test-pod-a.yaml @@ -0,0 +1,76 @@ +apiVersion: v1 +kind: Pod +metadata: + name: nm2-test-agent-a + namespace: scion-agents + labels: + test: nm2-scenario-a + scion.dev/project-id: test-project-alpha +spec: + securityContext: + runAsUser: 1000 + runAsGroup: 1000 + fsGroup: 1000 + initContainers: + - name: workspace-provision + image: alpine/git:latest + command: + - sh + - -c + - | + set -e + SENTINEL="/workspace/.scion-provisioned" + if [ -f "$SENTINEL" ]; then + echo "PROVISION: sentinel found, skipping clone" + exit 0 + fi + echo "PROVISION: cloning workspace..." + git clone --depth 1 https://github.com/ptone/scion.git /workspace + echo "$(date -u +%Y-%m-%dT%H:%M:%SZ)" > "$SENTINEL" + echo "PROVISION: clone complete, sentinel written" + volumeMounts: + - name: workspace + mountPath: /workspace + subPath: projects/test-project-alpha/workspace + resources: + requests: + cpu: 250m + memory: 512Mi + containers: + - name: agent + image: busybox:1.36 + command: + - sh + - -c + - | + echo "=== WORKSPACE CONTENTS ===" + ls -la /workspace/ + echo "=== SENTINEL CHECK ===" + cat /workspace/.scion-provisioned 2>/dev/null && echo "SENTINEL: present" || echo "SENTINEL: missing" + echo "=== ISOLATION CHECK ===" + echo "Attempting to access parent dir..." + ls /workspace/../ 2>&1 || echo "ISOLATION: cannot traverse up" + echo "=== WORKSPACE MOUNT INFO ===" + mount | grep workspace || echo "mount info unavailable in busybox" + echo "=== UID/GID CHECK ===" + id + echo "=== TEST COMPLETE ===" + sleep 30 + volumeMounts: + - name: workspace + mountPath: /workspace + subPath: projects/test-project-alpha/workspace + resources: + requests: + cpu: 250m + memory: 256Mi + volumes: + - name: workspace + persistentVolumeClaim: + claimName: scion-workspaces + restartPolicy: Never + tolerations: + - key: "kubernetes.io/arch" + operator: "Equal" + value: "amd64" + effect: "NoSchedule" diff --git a/scratch/nm2-test-pod-b1.yaml b/scratch/nm2-test-pod-b1.yaml new file mode 100644 index 000000000..fc29aa3a4 --- /dev/null +++ b/scratch/nm2-test-pod-b1.yaml @@ -0,0 +1,71 @@ +apiVersion: v1 +kind: Pod +metadata: + name: nm2-test-agent-b1 + namespace: scion-agents + labels: + test: nm2-scenario-b + scion.dev/project-id: test-project-beta +spec: + securityContext: + runAsUser: 1000 + runAsGroup: 1000 + fsGroup: 1000 + initContainers: + - name: workspace-provision + image: alpine/git:latest + command: + - sh + - -c + - | + set -e + SENTINEL="/workspace/.scion-provisioned" + if [ -f "$SENTINEL" ]; then + echo "PROVISION: sentinel found at $(cat $SENTINEL), skipping clone" + exit 0 + fi + echo "PROVISION: cloning workspace for project-beta (agent b1)..." + git clone --depth 1 https://github.com/ptone/scion.git /workspace + echo "b1:$(date -u +%Y-%m-%dT%H:%M:%SZ)" > "$SENTINEL" + echo "PROVISION: clone complete, sentinel written by b1" + volumeMounts: + - name: workspace + mountPath: /workspace + subPath: projects/test-project-beta/workspace + resources: + requests: + cpu: 250m + memory: 512Mi + containers: + - name: agent + image: busybox:1.36 + command: + - sh + - -c + - | + echo "=== AGENT B1 WORKSPACE ===" + ls -la /workspace/ + echo "=== SENTINEL ===" + cat /workspace/.scion-provisioned + echo "=== go.mod (identity check) ===" + head -3 /workspace/go.mod 2>/dev/null || echo "go.mod not found" + echo "=== TEST COMPLETE (b1) ===" + sleep 60 + volumeMounts: + - name: workspace + mountPath: /workspace + subPath: projects/test-project-beta/workspace + resources: + requests: + cpu: 250m + memory: 256Mi + volumes: + - name: workspace + persistentVolumeClaim: + claimName: scion-workspaces + restartPolicy: Never + tolerations: + - key: "kubernetes.io/arch" + operator: "Equal" + value: "amd64" + effect: "NoSchedule" diff --git a/scratch/nm2-test-pod-b2.yaml b/scratch/nm2-test-pod-b2.yaml new file mode 100644 index 000000000..e2b3647ed --- /dev/null +++ b/scratch/nm2-test-pod-b2.yaml @@ -0,0 +1,71 @@ +apiVersion: v1 +kind: Pod +metadata: + name: nm2-test-agent-b2 + namespace: scion-agents + labels: + test: nm2-scenario-b + scion.dev/project-id: test-project-beta +spec: + securityContext: + runAsUser: 1000 + runAsGroup: 1000 + fsGroup: 1000 + initContainers: + - name: workspace-provision + image: alpine/git:latest + command: + - sh + - -c + - | + set -e + SENTINEL="/workspace/.scion-provisioned" + if [ -f "$SENTINEL" ]; then + echo "PROVISION: sentinel found at $(cat $SENTINEL), skipping clone" + exit 0 + fi + echo "PROVISION: cloning workspace for project-beta (agent b2)..." + git clone --depth 1 https://github.com/ptone/scion.git /workspace + echo "b2:$(date -u +%Y-%m-%dT%H:%M:%SZ)" > "$SENTINEL" + echo "PROVISION: clone complete, sentinel written by b2" + volumeMounts: + - name: workspace + mountPath: /workspace + subPath: projects/test-project-beta/workspace + resources: + requests: + cpu: 250m + memory: 512Mi + containers: + - name: agent + image: busybox:1.36 + command: + - sh + - -c + - | + echo "=== AGENT B2 WORKSPACE ===" + ls -la /workspace/ + echo "=== SENTINEL ===" + cat /workspace/.scion-provisioned + echo "=== go.mod (identity check) ===" + head -3 /workspace/go.mod 2>/dev/null || echo "go.mod not found" + echo "=== TEST COMPLETE (b2) ===" + sleep 60 + volumeMounts: + - name: workspace + mountPath: /workspace + subPath: projects/test-project-beta/workspace + resources: + requests: + cpu: 250m + memory: 256Mi + volumes: + - name: workspace + persistentVolumeClaim: + claimName: scion-workspaces + restartPolicy: Never + tolerations: + - key: "kubernetes.io/arch" + operator: "Equal" + value: "amd64" + effect: "NoSchedule" diff --git a/scratch/nm2-test-pod-e.yaml b/scratch/nm2-test-pod-e.yaml new file mode 100644 index 000000000..856f923fc --- /dev/null +++ b/scratch/nm2-test-pod-e.yaml @@ -0,0 +1,97 @@ +apiVersion: v1 +kind: Pod +metadata: + name: nm2-test-agent-e + namespace: scion-agents + labels: + test: nm2-scenario-e + scion.dev/project-id: test-project-epsilon +spec: + securityContext: + runAsUser: 1000 + runAsGroup: 1000 + fsGroup: 1000 + initContainers: + - name: workspace-provision + image: alpine/git:latest + command: + - sh + - -c + - | + set -e + SENTINEL="/workspace/.scion-provisioned" + if [ -f "$SENTINEL" ]; then + echo "PROVISION: sentinel found, skipping clone" + exit 0 + fi + echo "PROVISION: cloning workspace..." + git clone --depth 1 https://github.com/ptone/scion.git /workspace + echo "$(date -u +%Y-%m-%dT%H:%M:%SZ)" > "$SENTINEL" + echo "PROVISION: clone complete" + volumeMounts: + - name: workspace + mountPath: /workspace + subPath: projects/test-project-epsilon/workspace + resources: + requests: + cpu: 250m + memory: 512Mi + - name: shared-dir-provision + image: busybox:1.36 + command: + - sh + - -c + - | + echo "SHARED-DIR: ensuring directory exists..." + mkdir -p /shared/test-data + echo "shared-dir-test-content" > /shared/test-data/readme.txt + ls -la /shared/ + echo "SHARED-DIR: provisioned" + volumeMounts: + - name: shared-dir-0 + mountPath: /shared + subPath: projects/test-project-epsilon/shared-dirs/test-data + resources: + requests: + cpu: 250m + memory: 128Mi + containers: + - name: agent + image: busybox:1.36 + command: + - sh + - -c + - | + echo "=== WORKSPACE ===" + ls -la /workspace/ | head -10 + echo "=== SHARED DIR (/scion-volumes/test-data) ===" + ls -la /scion-volumes/test-data/ + cat /scion-volumes/test-data/readme.txt + echo "=== MOUNT VERIFICATION ===" + echo "Workspace and shared dir are on same PVC with different subPaths" + echo "=== TEST COMPLETE (e) ===" + sleep 30 + volumeMounts: + - name: workspace + mountPath: /workspace + subPath: projects/test-project-epsilon/workspace + - name: shared-dir-0 + mountPath: /scion-volumes/test-data + subPath: projects/test-project-epsilon/shared-dirs/test-data + resources: + requests: + cpu: 250m + memory: 256Mi + volumes: + - name: workspace + persistentVolumeClaim: + claimName: scion-workspaces + - name: shared-dir-0 + persistentVolumeClaim: + claimName: scion-workspaces + restartPolicy: Never + tolerations: + - key: "kubernetes.io/arch" + operator: "Equal" + value: "amd64" + effect: "NoSchedule" diff --git a/scratch/scion-nfs-pv.yaml b/scratch/scion-nfs-pv.yaml new file mode 100644 index 000000000..0fed9b6a9 --- /dev/null +++ b/scratch/scion-nfs-pv.yaml @@ -0,0 +1,31 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: scion-agents +--- +apiVersion: v1 +kind: PersistentVolume +metadata: + name: scion-workspaces +spec: + capacity: + storage: 1Ti + accessModes: [ReadWriteMany] + nfs: + server: 10.45.255.170 + path: /scion_share + mountOptions: [vers=3, hard, nconnect=4] + persistentVolumeReclaimPolicy: Retain +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: scion-workspaces + namespace: scion-agents +spec: + accessModes: [ReadWriteMany] + storageClassName: "" + volumeName: scion-workspaces + resources: + requests: + storage: 1Ti diff --git a/shared-dirs/scratchpad/coordinator.md b/shared-dirs/scratchpad/coordinator.md new file mode 100644 index 000000000..de67981d2 --- /dev/null +++ b/shared-dirs/scratchpad/coordinator.md @@ -0,0 +1,75 @@ +# Coordinator Agent Workflow Instructions + +## Role +You are a coordinator agent. Your primary role is to manage agents using the Scion CLI and communicate with the user via `scion message`. You do not implement code yourself. You are acting as the product manager and are here to ensure that the project is completed completely and at high quality. + + +## Communication +- Always communicate with the user via `scion message --non-interactive ""` — direct text output is not visible to them. +- Report agent progress, and summaries proactively. + +## Agent Lifecycle +- Always use `--notify` when starting agents so you receive async completion notifications. +- After starting an agent, signal blocked status with `sciontool status blocked ""` and wait for the notification — do not poll or sleep. +- Stop agents after their work is complete to free resources. + + +## The `.scratch/` Directory +- `.scratch/` is gitignored — use it for agent briefs, investigation notes, and throwaway docs. +- Keep briefs concise: problem statement, not full analysis. + +## Project Tracking +- Maintain `/scion-volumes/scratchpad/projects.md` as a running index of all project work. +- When an agent completes work (bug fix, design doc, feature), add or update the entry in projects.md with: title, 1-3 line description, branch link, PR link (if any), and status. + +## Context Management +- Keep your coordinator context lean — delegate both investigation and implementation to engineerig managers to assign to developers. +- Don't run Explore agents or do detailed code analysis as the coordinator when you're going to assign an agent anyway. + +## Design Docs +- Design agents should write docs for smaller features should be written to `/scion-volumes/scratchpad/` and larger features to `/workspace/.design/` in the repo. + +## Agent Start Command +- The CLI syntax is `scion start [task...] [flags]` — there is no `--name` or `--instructions` flag. The agent name is a positional arg, and the task/instructions are passed as trailing positional args. +- When the default broker is unavailable, specify `--broker scion-gteam` explicitly (check existing agents with `scion list` to find the broker name). + +## Notification Behavior +- State-change notifications (COMPLETED, STALLED, etc.) fire for agent **subtask** completions too, not just the full job. Always check `scion look` before assuming the agent is done — verify the agent's task list and final output. +- Don't report completion to the user until you've confirmed the agent actually finished all its work. + +## Agent Cleanup +- Always stop then delete agents after their work is confirmed complete: `scion stop --non-interactive && scion delete --non-interactive` +- Clean up stalled agents too — a STALLED notification on a completed agent just means it went idle after finishing. + +## Autonomy & Progress +- **Never block on user availability.** You are the project driver — make decisions, keep moving. +- **Status updates should not pause work.** Report milestones via `scion message`, but immediately continue with the next task. Don't wait for acknowledgement. +- **Own the project direction.** You decide what to build next based on the design doc, security findings, integration results, etc. Only escalate genuine blockers (e.g., access, credentials, architectural ambiguity the design doc doesn't resolve). + +## Delegation Model +- **Never implement code directly.** All coding goes to eng-manager agents with clear, specific task descriptions. +- Use eng-manager agents for: feature implementation, bug fixes, security hardening, test writing, Dockerfile changes. +- Use specialized agents (e.g., sec-review-*) for: code audits, security reviews, focused analysis. +- The coordinator's job: plan phases, write agent briefs, review results, verify commits compile/pass tests, coordinate sequencing, report to Preston. + +## Waiting for Agents (Notification-Based, Not Polling) +- After starting an agent with `--notify`, call `sciontool status blocked ""` and **stop**. Do not create polling crons, sleep loops, or `scion look` checks. +- The scion system will deliver a notification message when the agent's state changes (completed, stalled, etc.). +- Only after receiving the notification, use `scion look` to verify the agent fully finished (subtask completions can also trigger notifications). + + +## Accumulated Tips +- When the user refers to "scratchpad", they mean `/scion-volumes/scratchpad/` — the directory where this instructions document lives. +- Messages typed directly into the coordinator's terminal (not via Scion) don't need a `scion message` reply — just respond inline. Only use `scion message` to reply to named users who sent a Scion message. +- Primary user is `ptone@google.com` (Preston). Use this identifier for `scion message`. +- The user appreciates concise status updates and proactive reporting of agent results (key findings, branch names, GitHub URLs). +- Subtask completion notifications can fire before the agent is truly done — always `scion look` to confirm all tasks are finished before acting on the result. +- When delegating security fixes, provide specific file paths, line numbers, and the exact vulnerability description from the review report — vague instructions lead to incomplete fixes. +- Clean up completed security review agents and old eng-managers once their work is confirmed merged or committed. +- **Multi-user independence:** Other users (e.g. ghchinoy@google.com) may message the coordinator. Reply to them directly. Do NOT notify Preston when you reply to other users — handle each independently. +- **eng-manager slug collision:** Only one eng-manager can run at a time — they share the same slug. Starting a second while one is running silently disrupts both and neither produces work. Always run eng-manager agents sequentially. +- **Agent task size limit:** Passing large briefs inline via `$(cat file.md)` causes the agent to abort silently if the content is too large (~5KB+). Fix: commit the brief to the repo (e.g. `.tasks/phase-N-name.md`) and pass a short pointer task like "Read and implement .tasks/phase-N-name.md". This reliably works. +- **`scion look` fails on stopped containers:** After an agent stops, `scion look` returns a docker exec error. Use `git log --oneline` and `git diff HEAD~1..HEAD --stat` to verify what was committed instead. +- **Plan approval timing:** eng-manager agents enter WAITING_FOR_INPUT for plan approval shortly after starting. If you go `sciontool status blocked` immediately, you may miss that notification and the agent will time out. Either wait ~30–45s and check the agent is still running before going blocked, or check the list quickly after blocking to confirm it hasn't already stopped. +- **Verify agent is actually running before going blocked:** After starting an agent and before calling `sciontool status blocked`, do a quick `sleep 30 && scion list` check to confirm the agent is still in `running` phase. If it stopped immediately, investigate before blocking. +- **`scion look` during active run:** `scion look` works fine while the agent is running but fails after it stops. Use it proactively to check plan approval prompts, not retrospectively. \ No newline at end of file diff --git a/shared-dirs/scratchpad/instance-interaction.md b/shared-dirs/scratchpad/instance-interaction.md new file mode 100644 index 000000000..129b461d4 --- /dev/null +++ b/shared-dirs/scratchpad/instance-interaction.md @@ -0,0 +1,88 @@ +You are an AI agent whose primary role is to manage and interact with a GCP VM via `gcloud compute ssh --zone "us-central1-a" "scion-aiopm" --project "deploy-demo-test"` + +If you do not have the ssh command already installed in your environment, you will need to install it with apt. You have sudo in this environment, and on the scion-aiopm GCE VM. + +Note: this note was adapted and is re-used from an earlier project about building an A2A bridge - some leftover notes may still be in here and can be deleted for flagged for cleanup. + +## VM Details + +- **Instance**: `scion-aiopm` +- **Zone**: `us-central1-a` +- **Project**: `deploy-demo-test` +- **SSH user**: Logs in as a service account (`sa_*`), not as `scion`. Use `sudo -u scion bash -c '...'` to run commands as the scion user, or `sudo` for root-level operations. + +## Repository Configuration + +The scion repo is checked out at `/home/scion/scion` on the VM. + +- **Remote**: `https://github.com/ptone/scion.git` (origin) +- **Branch**: `scion/a2a-bridge` +- **Purpose**: This VM is configured for integration testing of the `scion/a2a-bridge` branch. Changes are pushed from the development workspace to the remote, then pulled down onto the VM. + +## Hub Service + +- **Service**: `scion-hub` (systemd) +- **Config directory**: `/home/scion/.scion/` +- **Environment file**: `/home/scion/.scion/hub.env` +- **Settings**: `/home/scion/.scion/settings.yaml` +- **Database**: `/home/scion/.scion/hub.db` +- **Service file**: `/etc/systemd/system/scion-hub.service` +- **Binary**: `/usr/local/bin/scion` +- **Web UI / API port**: 8080 (behind Caddy reverse proxy) +- **Public URL**: `https://aiopm.projects.scion-ai.dev` +- **Caddy config**: `/etc/caddy/Caddyfile` (serves `aiopm.projects.scion-ai.dev`) + +### Key hub.env settings +- `SCION_MAINTENANCE_REPO_PATH="/home/scion/scion"` — points rebuild operations at the local checkout +- `SCION_MAINTENANCE_REPO_BRANCH=scion/chat-tee` — pins rebuilds to this branch + +## Common Operations + +### Check service status +```bash +gcloud compute ssh --zone "us-central1-a" "scion-aiopm" --project "deploy-demo-test" --command "sudo systemctl status scion-hub" +``` + +### Pull latest code on VM +```bash +gcloud compute ssh --zone "us-central1-a" "scion-aiopm" --project "deploy-demo-test" --command "sudo -u scion bash -c 'cd /home/scion/scion && git pull origin scion/chat-tee'" +``` + +### Rebuild and restart hub +```bash +gcloud compute ssh --zone "us-central1-a" "scion-aiopm" --project "deploy-demo-test" --command " +sudo -u scion bash -c 'cd /home/scion/scion && git pull origin scion/a2a-bridge && make web && /usr/local/go/bin/go build -o scion ./cmd/scion' +sudo systemctl stop scion-hub +sudo mv /home/scion/scion/scion /usr/local/bin/scion +sudo chmod +x /usr/local/bin/scion +sudo systemctl start scion-hub +" +``` + +### View recent logs +```bash +gcloud compute ssh --zone "us-central1-a" "scion-aiopm" --project "deploy-demo-test" --command "sudo journalctl -u scion-hub -n 50 --no-pager" +``` + +### Health check +```bash +gcloud compute ssh --zone "us-central1-a" "scion-aiopm" --project "deploy-demo-test" --command "curl -s http://localhost:8080/healthz" +``` + +## Integration Testing Workflow + +1. Make changes in the development workspace on branch `scion/a2a-bridge` +2. Push to remote: `git push origin scion/a2a-bridge` +3. Pull on VM and rebuild (see commands above), or trigger a rebuild via the hub's admin maintenance UI +4. Test against `https://integration.projects.scion-ai.dev` + + +## SSH Notes + +- **Do NOT use `--tunnel-through-iap`** — the VM has an external IP (35.232.118.211) and OS Login. Direct SSH works fine. +- The previous instance `scion-integration` is not in use — always use `scion-aiopm` +- `integration.projects.scion-ai.dev` (136.111.240.153) is the OLD VM — do not use +- `aiopm.projects.scion-ai.dev` (35.232.118.211) is THIS VM +- The hub can also self-rebuild via its admin maintenance page (rebuild-server / rebuild-web tasks), which respect the `SCION_MAINTENANCE_REPO_BRANCH` setting + + From 02efd44335f0a9477daa8f100d49c103d9da5420 Mon Sep 17 00:00:00 2001 From: Preston Holmes Date: Thu, 4 Jun 2026 21:39:31 -0700 Subject: [PATCH 016/155] feat(runtime): NFS-coordinated workspace sharing across nodes (Model A Docker + Model B GKE) (#306) --- .claude/scheduled_tasks.lock | 1 - .design/nfs-workspace-phase3-cloudrun.md | 77 + ...6-03-n1-1-workspace-backend-abstraction.md | 40 + .../2026-06-03-n1-2-3-5-nfs-runtime-wiring.md | 44 + .../2026-06-03-n1-4-6-nfs-provisioning.md | 56 + .../project-log/2026-06-03-n1-7-nfs-wiring.md | 51 + .../project-log/2026-06-03-n2-2b-initlock.md | 60 + .../2026-06-03-n2-gke-nfs-workspace.md | 68 + .../2026-06-03-nfs-config-foundation-n0.md | 35 + .../2026-06-03-nm1b-qa-wired-auto-mount.md | 107 ++ .../2026-06-03-nm2-gke-nfs-workspace.md | 30 + .../2026-06-05-pr306-review-fixes.md | 26 + .gitignore | 4 + cmd/server_foreground.go | 3 + cmd/server_migrate.go | 4 + internal/fixturegen/main.go | 3 +- pkg/api/types.go | 12 +- pkg/api/types_test.go | 30 +- pkg/config/settings_v1.go | 88 +- pkg/config/settings_v1_test.go | 176 +++ pkg/config/templates_test.go | 33 +- pkg/hub/broker_http_transport.go | 5 +- pkg/hub/broker_routing_test.go | 2 +- pkg/hub/brokerclient.go | 4 +- pkg/hub/controlchannel_client.go | 11 +- pkg/hub/handlers.go | 2 +- pkg/hub/httpdispatcher.go | 4 +- pkg/hub/httpdispatcher_test.go | 2 +- pkg/hub/scheduler_test.go | 12 + pkg/hub/server.go | 3 +- pkg/runtime/common.go | 27 +- pkg/runtime/interface.go | 47 + pkg/runtime/k8s_nfs_test.go | 1289 +++++++++++++++++ pkg/runtime/k8s_runtime.go | 488 ++++++- pkg/runtime/nfs_path_guard.go | 96 ++ pkg/runtime/nfs_path_guard_test.go | 368 +++++ pkg/runtime/nfs_uid_test.go | 232 +++ pkg/runtime/podman.go | 10 + pkg/runtime/workspace_backend.go | 210 +++ pkg/runtime/workspace_backend_local.go | 85 ++ pkg/runtime/workspace_backend_nfs.go | 489 +++++++ pkg/runtime/workspace_backend_test.go | 695 +++++++++ pkg/runtime/workspace_cleanup.go | 93 ++ pkg/runtime/workspace_cleanup_test.go | 220 +++ pkg/runtime/workspace_provision_test.go | 660 +++++++++ pkg/runtimebroker/NFS_DEPLOY_NOTES.md | 80 + pkg/runtimebroker/handlers.go | 37 + pkg/runtimebroker/nfs_mount.go | 296 ++++ pkg/runtimebroker/nfs_mount_exec.go | 125 ++ pkg/runtimebroker/nfs_mount_exec_test.go | 141 ++ pkg/runtimebroker/nfs_mount_test.go | 401 +++++ pkg/runtimebroker/nfs_wiring_test.go | 173 +++ pkg/runtimebroker/server.go | 34 + pkg/store/concurrency.go | 51 +- pkg/store/concurrency_test.go | 93 ++ pkg/store/entadapter/locking.go | 55 + pkg/store/entadapter/locking_test.go | 35 + pkg/store/models.go | 41 + pkg/store/models_test.go | 73 + scratch/apitest/main.go | 5 +- scratch/dbdiag2/main.go | 44 +- 61 files changed, 7568 insertions(+), 118 deletions(-) delete mode 100644 .claude/scheduled_tasks.lock create mode 100644 .design/nfs-workspace-phase3-cloudrun.md create mode 100644 .design/project-log/2026-06-03-n1-1-workspace-backend-abstraction.md create mode 100644 .design/project-log/2026-06-03-n1-2-3-5-nfs-runtime-wiring.md create mode 100644 .design/project-log/2026-06-03-n1-4-6-nfs-provisioning.md create mode 100644 .design/project-log/2026-06-03-n1-7-nfs-wiring.md create mode 100644 .design/project-log/2026-06-03-n2-2b-initlock.md create mode 100644 .design/project-log/2026-06-03-n2-gke-nfs-workspace.md create mode 100644 .design/project-log/2026-06-03-nfs-config-foundation-n0.md create mode 100644 .design/project-log/2026-06-03-nm1b-qa-wired-auto-mount.md create mode 100644 .design/project-log/2026-06-03-nm2-gke-nfs-workspace.md create mode 100644 .design/project-log/2026-06-05-pr306-review-fixes.md create mode 100644 pkg/runtime/k8s_nfs_test.go create mode 100644 pkg/runtime/nfs_path_guard.go create mode 100644 pkg/runtime/nfs_path_guard_test.go create mode 100644 pkg/runtime/nfs_uid_test.go create mode 100644 pkg/runtime/workspace_backend.go create mode 100644 pkg/runtime/workspace_backend_local.go create mode 100644 pkg/runtime/workspace_backend_nfs.go create mode 100644 pkg/runtime/workspace_backend_test.go create mode 100644 pkg/runtime/workspace_cleanup.go create mode 100644 pkg/runtime/workspace_cleanup_test.go create mode 100644 pkg/runtime/workspace_provision_test.go create mode 100644 pkg/runtimebroker/NFS_DEPLOY_NOTES.md create mode 100644 pkg/runtimebroker/nfs_mount.go create mode 100644 pkg/runtimebroker/nfs_mount_exec.go create mode 100644 pkg/runtimebroker/nfs_mount_exec_test.go create mode 100644 pkg/runtimebroker/nfs_mount_test.go create mode 100644 pkg/runtimebroker/nfs_wiring_test.go create mode 100644 pkg/store/concurrency_test.go create mode 100644 pkg/store/models_test.go diff --git a/.claude/scheduled_tasks.lock b/.claude/scheduled_tasks.lock deleted file mode 100644 index 297107d87..000000000 --- a/.claude/scheduled_tasks.lock +++ /dev/null @@ -1 +0,0 @@ -{"sessionId":"f5564605-9019-4019-968f-3ae798b30ae8","pid":44,"procStart":"238465155","acquiredAt":1780324261213} \ No newline at end of file diff --git a/.design/nfs-workspace-phase3-cloudrun.md b/.design/nfs-workspace-phase3-cloudrun.md new file mode 100644 index 000000000..b54de4bb3 --- /dev/null +++ b/.design/nfs-workspace-phase3-cloudrun.md @@ -0,0 +1,77 @@ +# NFS Workspace — Phase 3 (Cloud Run + Filestore-CSI) Design Note + +**Status:** Documentation deliverable (N3-1 + N3-2). **No code in this phase.** +**Why doc-only:** Verified against `postgres/wave-b-integration` — Scion has **no Cloud Run +runtime** (`pkg/runtime/factory.go` supports `container`/`docker`/`podman`/`kubernetes` only; +no `run.googleapis.com`/knative anywhere). There is no Cloud Run Service/Job spec to attach an +NFS volume to, so N3-1 "emit an NFS volume in the Cloud Run spec" cannot land as code until a +Cloud Run runtime exists. Building that runtime is a separate, larger effort outside the NFS +plan's scope. This note records the realization design so it's a config/wiring change, not a +redesign, when a Cloud Run runtime is added. (Companion: `nfs-workspace.md` §5.4/§9.4.) + +--- + +## N3-1 — Cloud Run NFS workspace realization (design, for when a Cloud Run runtime exists) + +Cloud Run (gen2 execution environment) supports NFS volume mounts (incl. Filestore). When a +Cloud Run runtime is added to `pkg/runtime`, NFS realization should mirror the Docker/K8s +backends already shipped (Wave 1/2): + +- **Selection:** reuse `SelectWorkspaceBackend(cfg, mode)` (Wave 1) — NFS applies for + Shared-plain + Worktree-per-agent; Clone-per-agent stays node-local. No new toggle. +- **Volume spec:** emit an NFS volume in the Cloud Run Service/Job spec: + ```yaml + volumes: + - name: workspace + nfs: + server: # e.g. 10.45.255.170 + path: //projects//workspace # server-side path = isolation + readOnly: false + containers: + - volumeMounts: [{ name: workspace, mountPath: /workspace }] + ``` +- **Isolation (critical, §9.4):** Cloud Run has **no `subPath`**. Isolation therefore comes + from the **server-side `path`** being the project subdir — the instance can only reach what + the export path exposes. The Hub MUST put `projects//workspace` in the NFS `path`, + never the export root. Reuse the spirit of `ValidateNotExportRoot` (Wave 1 + `pkg/runtime/nfs_path_guard.go`): assert the emitted server path is strictly below the + export root before realizing. +- **UID/GID:** same convergence as Docker/K8s — stable 1000:1000 (`V1NFSConfig.UID/GID`); the + container runs as 1000. (Cloud Run runs the container user; align with the provisioned + ownership.) +- **Mount options / tier:** Filestore **basic = NFSv3** (default `vers=3`, set in Wave 1 + N1-7). NFSv4.1 needs Enterprise/zonal. +- **Provisioning:** Cloud Run instances have no host access for the Hub to clone into; the + workspace must be **pre-provisioned** on NFS (same as the K8s init-container model, Wave 2 + N2-2/N2-2b) — guarded by the per-project Postgres advisory lock + (`TryAdvisoryLockObject(LockWorkspaceProvision, StableProjectHash(pid))`). A Cloud Run + runtime would need an equivalent first-access provisioning step (e.g. a pre-create + provisioning Job, or Hub-side provisioner with NFS access) before starting the instance. +- **Acceptance (future):** a Cloud Run instance mounts only `projects//workspace` and + cannot reach the export root (server-path isolation). + +## N3-2 — Filestore-CSI dynamic PVC (Enterprise-only, deferred — recorded upgrade path) + +Not implemented (Q4: target is Filestore **basic**, which has no multishare and a 1 TiB +minimum, so one-PVC-per-project is economically impossible; CSI dynamic is Enterprise/zonal +"multishare" only). Recorded upgrade path: + +- Reuse the **generalized project-RWX-claim helper** from Wave 2 N2-5 (the generalized + `sharedDirPVCName`/`createSharedDirPVCs`/`cleanupSharedDirPVCs`) plus `V1NFSConfig.StorageClass` + (`filestore.csi.storage.gke.io`) → **one PVC per project** instead of the static + RWX-PV-+-subPath default (Wave 2 N2-1). +- Keep `V1NFSShare.ID` **per-Hub** (already in config) so moving to **instance-per-Hub + isolation** (the true Hub↔Hub isolation option, §9.4) is a config change, not a redesign. +- When adopted: swap the workspace volume source from static-PV+subPath to a per-project + dynamic PVC via the generalized helper; lifecycle (create-on-first-agent, cleanup on + project delete) already mirrors `cleanup*PVCs`. + +--- + +## Summary +Wave 3 is documentation only on this branch: the Cloud Run NFS realization (N3-1) is fully +specified and reuses Wave 1/2 primitives (backend selector, export-root isolation guard, +stable UID, advisory-lock provisioning, vers=3) — it just needs a Cloud Run runtime to attach +to. The Filestore-CSI dynamic per-project strategy (N3-2) is the recorded Enterprise-tier +upgrade, reusing the Wave 2 N2-5 generalized PVC helper, with per-Hub share IDs keeping the +instance-per-Hub isolation path open. diff --git a/.design/project-log/2026-06-03-n1-1-workspace-backend-abstraction.md b/.design/project-log/2026-06-03-n1-1-workspace-backend-abstraction.md new file mode 100644 index 000000000..d88bcb5ef --- /dev/null +++ b/.design/project-log/2026-06-03-n1-1-workspace-backend-abstraction.md @@ -0,0 +1,40 @@ +# N1-1: Workspace Storage Backend Abstraction + +**Date:** 2026-06-03 +**Agent:** runtime-agent-1 +**Branch:** `nfs/n1-1-backend-abstraction` +**Commit:** `eca1b882` +**Status:** Complete + +## What was done + +Introduced the `WorkspaceBackend` interface in `pkg/runtime/` with three methods mapping to the NFS design's three questions: + +1. **Resolve** — deterministic path computation from project/agent IDs + sharing mode. No DB, no I/O. +2. **Provision** — stub for N1-4 (NFS clone+advisory-lock); no-op for local. +3. **Realize** — stub for N1-3 (mount wiring); local returns today's bind-mount descriptor. + +### Files added (4) + +- `pkg/runtime/workspace_backend.go` — interface + input/output structs + `SelectWorkspaceBackend` helper +- `pkg/runtime/workspace_backend_local.go` — `localBackend` wrapping today's behavior +- `pkg/runtime/workspace_backend_nfs.go` — `nfsBackend` with complete Resolve, stub Provision/Realize +- `pkg/runtime/workspace_backend_test.go` — 22 table-driven tests + +### Key design decisions + +- **Package placement:** `pkg/runtime/` chosen because it can import both `config` and `store` without cycles (neither imports `runtime`), and `runtimebroker` already imports `runtime`. +- **SelectWorkspaceBackend** routes `nfsBackend` only when `Backend=nfs` AND mode ∈ {SharedPlain, WorktreePerAgent}. ClonePerAgent always gets `localBackend` — the deliberate node-local escape hatch per design §3.1. +- **localBackend.Resolve** returns `ProjectDir` as-is — faithful to today's broker path resolution, zero behavior change. +- **nfsBackend.Resolve** uses first configured share, lays out `//workspace` and `//shared-dirs/`. + +### Test results + +- `go build ./...` — clean +- `go vet ./pkg/runtime/` — clean +- All 22 new tests pass; existing runtime tests unaffected + +## Process notes + +- Shared workspace was concurrently switched to another branch by a parallel agent. Resolved by creating a git worktree at `/tmp/nfs-n1-1` on the correct branch, avoiding conflicts. +- Phase-0 config structs (`V1WorkspaceStorageConfig`, `V1NFSConfig`, `V1NFSShare`, `WorkspaceSharingMode`) were already present on the integration branch — used directly. diff --git a/.design/project-log/2026-06-03-n1-2-3-5-nfs-runtime-wiring.md b/.design/project-log/2026-06-03-n1-2-3-5-nfs-runtime-wiring.md new file mode 100644 index 000000000..9beb0e122 --- /dev/null +++ b/.design/project-log/2026-06-03-n1-2-3-5-nfs-runtime-wiring.md @@ -0,0 +1,44 @@ +# N1-2, N1-3, N1-5 — Docker-runtime NFS wiring + +**Date:** 2026-06-03 +**Agent:** runtime-agent-2 +**Branch:** `nfs/n1-2-3-5-runtime` +**Base:** `postgres/wave-b-integration` @ dfed3bc7 + +## Tasks completed + +### N1-2: Broker NFS mount reconciliation (37a51b97) +- Created `pkg/runtimebroker/nfs_mount.go` with `NFSMountReconciler` +- `MountChecker` interface isolates mount syscalls for testability +- Reconciliation logic: not mounted→mkdir+mount; correct→no-op; wrong source→remount +- Per-share health tracking with `IsHealthy()`/`HealthCheckString()` for integration with broker health endpoint +- `EnsureShareMounted(shareID)` for pre-dispatch verification +- Multi-share support: shares are a set keyed by share-id +- 15 unit tests covering all reconciliation scenarios (idempotency, failures, multi-share) + +### N1-3: Redirect path resolution to NFS (c919fab8) +- Created `pkg/runtime/nfs_path_guard.go` with: + - `ValidateNotExportRoot` — isolation guard rejecting export-root binds (design §9.4) + - `NFSSharedDirsToVolumeMounts` — NFS-aware shared dir volume mount builder +- Updated `nfsBackend.Realize` from stub to full Docker bind-mount descriptor +- Isolation guard enforced in both `Realize` and `NFSSharedDirsToVolumeMounts` +- Local backend behavior byte-identical — verified via tests +- 14 unit tests covering isolation guard, NFS shared dirs, end-to-end resolve→realize + +### N1-5: Stable UID/GID branch for NFS (745f967c) +- Added `WorkspaceBackendName`, `NFSUID`, `NFSGID` to `RunConfig` (minimal threading) +- `buildCommonRunArgs`: local→os.Getuid(), nfs→NFS.UID/GID (default 1000:1000) +- Exports `SCION_WORKSPACE_BACKEND` env var for sciontool init chown skip +- Podman rootless + NFS rejected with clear error message +- 8 unit tests covering UID branching, default values, backend env, rootless rejection + +## Design decisions +- **No behavior change for backend=local** — all three tasks verified this via explicit tests +- **Isolation guard as a shared function** — used by both Realize and shared-dirs helpers +- **MountChecker interface** — avoids importing exec/syscall in test code +- **WorkspaceBackendName as string** — keeps RunConfig changes minimal; avoids importing config types into runtime + +## Verification +- `go build ./...` — clean +- `go vet ./pkg/runtime/... ./pkg/runtimebroker/...` — clean +- All new + existing unit tests green (37 new tests total) diff --git a/.design/project-log/2026-06-03-n1-4-6-nfs-provisioning.md b/.design/project-log/2026-06-03-n1-4-6-nfs-provisioning.md new file mode 100644 index 000000000..513dcc1e9 --- /dev/null +++ b/.design/project-log/2026-06-03-n1-4-6-nfs-provisioning.md @@ -0,0 +1,56 @@ +# Project Log: N1-4 + N1-6 — NFS Workspace Provisioning & Cleanup + +**Date:** 2026-06-03 +**Agent:** provisioning-agent +**Branch:** `nfs/n1-4-6-provisioning` (from `postgres/wave-b-integration`) +**Tasks:** N1-4 (workspace provisioning + advisory lock), N1-6 (project-deletion cleanup) + +## What was done + +### N1-4A: Per-project advisory lock API extension +- Extended `AdvisoryLocker` interface with `TryAdvisoryLockObject(ctx, classID, objID)` — uses Postgres's two-integer form `pg_try_advisory_lock(int4, int4)` for per-project locks +- `LockWorkspaceProvision` (0x5C101001) as classID, `StableProjectHash()` (FNV-32a) for deterministic objID from project UUID +- SQLite implementation: no-op (always acquired) — single-writer already serializes +- Implemented in `pkg/store/concurrency.go` (interface + hash helper) and `pkg/store/entadapter/locking.go` (Postgres/SQLite impl) + +### N1-4B: nfsBackend.Provision implementation +- Full first-access provisioning flow in `pkg/runtime/workspace_backend_nfs.go`: + 1. Acquire per-project advisory lock (retry loop, 30 attempts × 1s) + 2. Check sentinel `.scion-provisioned` → short-circuit if present + 3. mkdir -p workspace + shared-dirs + 4. Git clone if project is git-backed + 5. chown to stable NFS UID/GID (one-time, non-fatal if unprivileged) + 6. Write sentinel atomically (temp + rename) + 7. For WorktreePerAgent: create per-agent git worktree + 8. Release lock +- ClonePerAgent mode asserted out (defense in depth) +- `ProvisionInput` extended with `Locker`, `NFSUID/NFSGID`, `AgentName` + +### N1-6: Project-deletion cleanup +- `CleanupNFSProject` helper in `pkg/runtime/workspace_cleanup.go` +- Removes `//projects//` subtree +- Safety: `ValidateNotExportRoot` + path traversal protection + idempotent +- Wired into broker's `deleteProject` handler via `NFSConfig` on `ServerConfig` +- Hub passes `project_id` query param; broker reads it for NFS path computation +- Extended `RuntimeBrokerClient.CleanupProject` signature to accept `projectID` + +## Test coverage +- **42 new test cases** across 4 test files +- `pkg/store/concurrency_test.go`: StableProjectHash determinism, range, key uniqueness +- `pkg/store/entadapter/locking_test.go`: TryAdvisoryLockObject SQLite no-op, independence +- `pkg/runtime/workspace_provision_test.go`: Full provisioning lifecycle — git clone, sentinel short-circuit, worktree creation, two-agent worktree independence, lock retry/mutual exclusion, ClonePerAgent rejection, degraded mode (no locker), missing-field validation, branch name sanitization, sentinel atomicity +- `pkg/runtime/workspace_cleanup_test.go`: Subtree removal, idempotency, share-root refusal, path traversal refusal, project isolation, nil config, no shares, default SubPathRoot + +## Findings & observations +1. The two-int advisory lock form fit naturally — no awkwardness with the existing API. The Postgres `pg_try_advisory_lock(int4, int4)` namespace is separate from the single-int form, so no collision risk. +2. Git clone in tests required `--initial-branch=main` to work across git versions. +3. `chown` in provisioning is non-fatal — tests run unprivileged and the operator may have pre-set ownership. This is by design (§9.1). +4. The `RuntimeBrokerClient.CleanupProject` interface change touched 5 implementations (HTTP transport, control channel, hybrid, authenticated, HTTP dispatcher) — all mechanical pass-throughs. The project_id query param is backward-compatible (empty = no NFS cleanup). + +## Commits +``` +59f6ee78 feat(store): per-project advisory lock (two-int form) for NFS provisioning guard (N1-4A) +1b1ecb0b feat(runtime): NFS workspace provisioning with advisory-lock race guard (N1-4B) +b68116ad feat(runtime): NFS project-deletion cleanup mirroring K8s cleanupSharedDirPVCs (N1-6) +7a59f4e2 fix(hub): add TryAdvisoryLockObject to lockerStore mock (test fix for N1-4A) +``` diff --git a/.design/project-log/2026-06-03-n1-7-nfs-wiring.md b/.design/project-log/2026-06-03-n1-7-nfs-wiring.md new file mode 100644 index 000000000..89c07ba0b --- /dev/null +++ b/.design/project-log/2026-06-03-n1-7-nfs-wiring.md @@ -0,0 +1,51 @@ +# N1-7: Wire NFS Mount Reconciler + Fix vers=3 Default + Deploy Notes + +**Date:** 2026-06-03 +**Agent:** runtime-wire-agent +**Branch:** `nfs/n1-7-wiring` (from `postgres/wave-b-integration` @ `42bffd67`) +**Commit:** `08cab2ac` + +## What was done + +### 1. Wired NFSMountReconciler into broker startup (the main gap) +- Created `ExecMountChecker` (`nfs_mount_exec.go`) — production `MountChecker` + implementation using `mount(8)`, `umount(8)`, `mountpoint(1)`, and `/proc/mounts`. + The `runCommand` field is injectable for testing. +- Added `nfsMountReconciler` field to `Server` struct. +- In `New()`: construct reconciler when `NFSConfig` has shares configured. +- In `Start()`: call `Reconcile()` alongside other startup tasks, logging health status. +- In `GetHealthInfo()`: surface `nfs_mounts` key in health checks; degrades status if unhealthy. +- In `createAgent()`: added `ensureNFSMountsReady()` guard before `buildStartContext` — returns + 503 if any NFS share cannot be mounted (no silent fallback). +- In `server_foreground.go`: wire `NFSConfig` from `versionedSettings.Server.WorkspaceStorage.NFS` + into `ServerConfig` when backend=nfs. + +### 2. Fixed default MountOptions: vers=4.1 → vers=3 +- Changed default in `ApplyNFSDefaults()` and the reconciler's inline fallback. +- Updated doc comment on `V1NFSConfig.MountOptions` explaining the NFSv3 rationale. +- Updated test expectations in both `settings_v1_test.go` and `nfs_mount_test.go`. + +### 3. Deploy notes +- Created `pkg/runtimebroker/NFS_DEPLOY_NOTES.md` covering UID/GID alignment, + mount privilege requirements, and the NFSv3 default. + +## Tests added +- `nfs_mount_exec_test.go`: ExecMountChecker (mock exec, interface compliance) +- `nfs_wiring_test.go`: Server construction (reconciler wired/nil), health surface, + `ensureNFSMountsReady()` dispatch guard + +## Verification +- `go build ./...` — clean +- `go vet ./...` — clean +- `go test ./pkg/runtimebroker/...` — all pass (0 failures) +- `go test ./pkg/config/ -run TestWorkspaceStorage` — all pass +- `go test ./pkg/runtime/...` — all pass +- Pre-existing failures in `pkg/config` (`TestIsInsideProject` etc.) confirmed on base branch + +## Process notes +- The `NFSConfig` field was already on `ServerConfig` (added by N1-6 for cleanup), + but was never populated from settings in `server_foreground.go`. Fixed that gap. +- `SelectWorkspaceBackend` exists in `pkg/runtime` but is not yet called in production + dispatch flow — NFS fields on `StartConfig` are also not populated by the broker. + The reconciler wiring is the broker-level mount guard; K8s-level NFS integration + flows through the runtime's `buildPod` path. diff --git a/.design/project-log/2026-06-03-n2-2b-initlock.md b/.design/project-log/2026-06-03-n2-2b-initlock.md new file mode 100644 index 000000000..a16d9f227 --- /dev/null +++ b/.design/project-log/2026-06-03-n2-2b-initlock.md @@ -0,0 +1,60 @@ +# N2-2b: Advisory-lock guard for K8s init-container provisioning + +**Date**: 2026-06-03 +**Agent**: k8s-lock-agent +**Branch**: `nfs/n2-2b-initlock` +**Commit**: `ed023f2e` + +## Summary + +Added the per-project Postgres advisory lock to the K8s init-container +provisioning path, closing risk RN1 (concurrent first-clone corruption +when two pods for the same project are scheduled on different nodes +sharing an NFS volume). + +## Problem + +N2-2 shipped sentinel-only guarding for the init container. The sentinel +file check is check-then-act and cannot prevent two pods on different +nodes from both seeing "no sentinel" and `git clone`-ing concurrently +into the same NFS subPath, causing corruption. + +## Solution + +The advisory lock cannot live in the init-container shell (no DB access), +so it is acquired in the Go-side `Run()` method before `buildPod()`: + +1. **Lock winner** (`TryAdvisoryLockObject` returns `acquired=true`): + - Injects the existing cloning init container (checks sentinel, clones if absent) + - Lock held through `waitForPodReady()` (init containers complete), then released via defer + +2. **Lock loser** (`acquired=false`): + - Injects a wait-for-sentinel init container that polls for `.scion-provisioned` with a 300s bounded timeout + - No clone attempt + +3. **Lock error**: dispatch fails immediately (no unguarded clone) + +4. **No locker** (nil / SQLite): sentinel-only fallback (single-node safe) + +All gated on `backend=nfs` — zero behavior change for local backend. + +## Files Changed + +- `pkg/runtime/interface.go` — Added `Locker` and `nfsProvisionLockLost` fields to `RunConfig` +- `pkg/runtime/k8s_runtime.go` — Advisory lock acquisition in `Run()`, updated `buildPod()` init container logic, added `nfsWaitForSentinelScript()` +- `pkg/runtime/k8s_nfs_test.go` — 12 new tests covering winner/loser/error/no-locker/local/multi-project scenarios + +## Verification + +- `go build ./...` — clean +- `go vet ./pkg/runtime/...` — clean +- `go test ./pkg/runtime/...` — all pass (24s, includes 3s timeouts for Run-level tests) +- `go test ./pkg/store/...` — all pass +- `go test ./pkg/agent/...` — all pass + +## Design Notes + +- Reuses N1-4's `TryAdvisoryLockObject` + `LockWorkspaceProvision` + `StableProjectHash` from `pkg/store/concurrency.go` +- Lock lifetime mirrors N1-4's "hold during clone" pattern +- The `nfsProvisionLockLost` field is unexported (set internally by `Run()`, used by `buildPod()` in the same package) +- Wait-for-sentinel script uses 300s timeout (5 min) with 2s poll interval diff --git a/.design/project-log/2026-06-03-n2-gke-nfs-workspace.md b/.design/project-log/2026-06-03-n2-gke-nfs-workspace.md new file mode 100644 index 000000000..24865f576 --- /dev/null +++ b/.design/project-log/2026-06-03-n2-gke-nfs-workspace.md @@ -0,0 +1,68 @@ +# N2-1..N2-5: GKE NFS Workspace Realization (Wave 2, Model B) + +**Date:** 2026-06-03 +**Agent:** k8s-agent +**Branch:** `nfs/n2-gke` (from `postgres/wave-b-integration`) + +## Summary + +Implemented all five N2 tasks for GKE NFS workspace support in +`pkg/runtime/k8s_runtime.go`. These changes realize the NFS workspace +backend on Kubernetes, converting the design's §5 (Model B) into +working pod spec transformations. + +## Commits + +| SHA | Task | Description | +|-----|------|-------------| +| `75032d8a` | N2-1 | NFS-backed workspace volume — replace EmptyDir with PVC+subPath | +| `2da9afbc` | N2-2 | Init-container workspace provisioning with sentinel idempotency | +| `caf874d5` | N2-3 | Skip workspace kubectl cp when backend=nfs | +| `36737080` | N2-4 | Stable FSGroup/UID (NFS GID default 1000) | +| `45b95293` | N2-5 | Generalize shared-dir PVC helpers for NFS subPath | + +## Design Decisions + +1. **PVC+subPath isolation (N2-1):** Each NFS pod mounts the shared PVC with + `subPath: projects//workspace`, ensuring pods only see their project's + subtree — never the export root. Falls back to EmptyDir if NFSPVClaimName + is empty. + +2. **Init-container provisioning (N2-2):** Uses a `workspace-provision` init + container that checks `.scion-provisioned` sentinel before cloning. The + advisory lock is NOT used in-pod — init containers serialize per-pod + naturally, and the sentinel provides cross-pod idempotency. Full advisory + lock integration deferred to NM2 live cluster gate. + +3. **Workspace sync skip (N2-3):** NFS workspace bytes are pre-populated by + the init container, so kubectl cp is skipped. Home-dir/secret sync and the + startup gate are RETAINED for both backends. + +4. **FSGroup branching (N2-4):** NFS pods use stable GID (config or default + 1000) instead of host GID. This avoids permission issues across nodes. + +5. **Shared-dir subPath (N2-5):** NFS shared dirs mount from the same PVC + with `subPath: projects//shared-dirs/`, eliminating per-dir PVCs. + Refactored into generalized `ensureProjectRWXClaim`/`cleanupProjectRWXClaims` + helpers. + +## New RunConfig Fields + +- `NFSPVClaimName` — PVC name for the NFS workspace volume +- `NFSSubPath` — project-scoped subPath within the PVC +- `NFSStorageClass` — StorageClass for NFS PVCs +- `GitCloneForInit` — git clone config for init-container provisioning + +## Tests + +All changes include unit tests in `pkg/runtime/k8s_nfs_test.go`: +- `go build ./...` clean +- `go vet` clean +- All existing k8s_runtime tests pass (no regressions) +- 20+ new test cases covering NFS and local backend paths + +## Zero Behavior Change Guarantee + +Every NFS branch is gated on `config.WorkspaceBackendName == "nfs"`. +When backend is local (default/empty), all five tasks produce exactly +the same pod spec as before. diff --git a/.design/project-log/2026-06-03-nfs-config-foundation-n0.md b/.design/project-log/2026-06-03-nfs-config-foundation-n0.md new file mode 100644 index 000000000..d09055269 --- /dev/null +++ b/.design/project-log/2026-06-03-nfs-config-foundation-n0.md @@ -0,0 +1,35 @@ +# NFS Workspace Config Foundation (N0-1, N0-2, N0-3) + +**Agent:** config-agent +**Date:** 2026-06-03 +**Branch:** postgres/wave-b-integration +**Commit:** d8f8c987 + +## Summary + +Landed the Phase 0 config-only foundation for NFS-backed workspace storage. +Three tasks, all no-op at runtime (backend defaults to "local"). + +### N0-1: Workspace-storage config block +- Added `V1WorkspaceStorageConfig`, `V1NFSConfig`, `V1NFSShare` types to `pkg/config/settings_v1.go` +- Wired `WorkspaceStorage` into `V1ServerConfig` +- `ApplyNFSDefaults()` fills mount_options, UID/GID=1000, subpath_root="projects" when backend=nfs +- No NFS block materialized for local/empty backend +- Tests: YAML round-trip via LoadVersionedSettings, JSON round-trip, defaults, nil-safety + +### N0-2: VolumeMount nfs type + validation +- Added `Server` field to `VolumeMount` (`pkg/api/types.go`) +- Extended `Validate()` with `case "nfs":` requiring Server+Source+Target +- Updated default error message to list all three valid types +- Flipped existing nfs fixtures from rejected→valid in both `types_test.go` and `templates_test.go` +- Added negative cases: nfs-missing-server, nfs-missing-source, genuinely-invalid type ("bogus") + +### N0-3: Workspace-sharing-mode enum alignment +- Added typed `WorkspaceSharingMode` (string) with 3 canonical values (`pkg/store/models.go`) +- `ResolveWorkspaceSharingMode(label)` maps legacy label values, new values, empty/unknown→default +- Existing `LabelWorkspaceMode`, `WorkspaceModeShared`, `WorkspaceModePerAgent` constants unchanged (lossless) +- Unit tests in new `pkg/store/models_test.go` cover all cases + +## Process Notes +- Pre-existing test failures in pkg/config (IsInsideProject, FindProjectRoot, etc.) are environment-dependent and unrelated to these changes +- All N0-specific tests green; `go build ./...` clean diff --git a/.design/project-log/2026-06-03-nm1b-qa-wired-auto-mount.md b/.design/project-log/2026-06-03-nm1b-qa-wired-auto-mount.md new file mode 100644 index 000000000..0a7e2bdfe --- /dev/null +++ b/.design/project-log/2026-06-03-nm1b-qa-wired-auto-mount.md @@ -0,0 +1,107 @@ +# NM1b QA — WIRED Model-A NFS Path Re-validation + +**Date:** 2026-06-03 +**Agent:** qa-agent-2 +**Binary:** commit 1eaecd95 (N1-7 wired reconciler + vers=3 default) +**VMs:** scion-integration, scion-integration2 (us-central1-a, deploy-demo-test) + +## Summary + +**Overall: PASS (with one deploy-notes finding)** + +NM1b re-validates the WIRED Model-A path: the broker auto-mounts NFS at startup +via the NFSMountReconciler (no manual mount), serves healthy NFS status on /healthz, +and the Postgres advisory lock mechanism prevents provisioning races. + +## Per-Step Results + +### Step 1: Build WIRED binary — PASS +- Built from isolated temp clone at `/tmp/nm1b-build/scion-build` +- Confirmed `git rev-parse HEAD` = 1eaecd95 +- Binary version output: `Commit: 1eaecd95, Build Time: 2026-06-03T02:07:34Z` + +### Step 2: Deploy to both VMs — PASS +- Baseline captured: both VMs running commit 9a998934 +- Backup created: `/usr/local/bin/scion.bak-nm1b` +- New binary installed, version confirmed on both VMs + +### Step 3: Configure backend=nfs — PASS (with lesson learned) +- **Lesson:** Settings file MUST include `schema_version: "1"` or the startup migration + process strips unrecognized fields. First attempt lost workspace_storage config. +- Correct format: versioned settings with `schema_version: "1"` + `server.workspace_storage` + block alongside `server.broker.broker_id`. +- mount_options omitted to test vers=3 default. + +### Step 4: Restart + Verify AUTO-MOUNT — PASS ★ (KEY NM1b GATE) + +**Wiring confirmed on both VMs.** Journal evidence: + +``` +INFO "NFS mount reconciler initialized" shares=1 mountRoot="/mnt/nfs" +INFO "Reconciling NFS share" shareID="demo" target="/mnt/nfs/demo" server="10.45.255.170" export="/scion_share" +INFO "Mounting NFS share" source="10.45.255.170:/scion_share" target="/mnt/nfs/demo" options="vers=3,hard,nconnect=4,_netdev" +INFO "NFS share mounted" shareID="demo" target="/mnt/nfs/demo" +INFO "NFS mounts reconciled at startup" status="healthy" +``` + +- Mount verified: `10.45.255.170:/scion_share on /mnt/nfs/demo type nfs (rw,relatime,vers=3,...,nconnect=4,...)` +- **vers=3 default confirmed** — mount_options was empty in config, code defaulted to `vers=3,hard,nconnect=4,_netdev` +- /healthz: `{"checks":{"docker":"available","nfs_mounts":"healthy"}}` on both VMs + +**Finding: CAP_SYS_ADMIN insufficient for mount.nfs** +- The broker service runs as user `scion` (uid=1002). `AmbientCapabilities=CAP_SYS_ADMIN` + was applied (confirmed in `/proc//status` CapEff=0x200000), BUT `mount.nfs` is a + setuid binary that checks `uid==0`, not capabilities. +- **Workaround for NM1b:** Ran service as `User=root` via systemd override. +- **Recommendation for production:** Either run broker as root, or modify + `ExecMountChecker.Mount()` to use `sudo mount` (requires sudoers entry), or use + `mount(2)` syscall directly in Go (which respects CAP_SYS_ADMIN). Update + `NFS_DEPLOY_NOTES.md` accordingly — the CAP_SYS_ADMIN approach documented there + does not work with `mount.nfs`. + +### Step 5: Live Tests + +#### (a) Cross-node visibility — PASS +- VM1 wrote file → VM2 read identical content +- VM2 wrote file → VM1 read identical content +- Files visible within seconds across nodes + +#### (b) Provisioning race (advisory lock) — PASS +- **Postgres advisory lock test:** VM1 acquired `pg_try_advisory_lock(999999)` → VM2 attempt + returned `f` (false). After VM1 released, VM2 acquired successfully. Lock contention works. +- **Sentinel reuse test:** VM1 provisioned fresh project dir with sentinel → VM2 found + sentinel, correctly reused workspace instead of re-provisioning. No corruption. +- CloudSQL reachable from both VMs: `SELECT 1 as connected` succeeded. + +#### (d) Cross-node UID 1000 writability — PASS +- Files written by VM1 as uid 1000 writable by VM2 as uid 1000 +- Files written by VM2 writable by VM1 +- No permission denials across nodes + +#### (restart) Restart idempotency — PASS +- Pre-restart: 1 NFS mount +- Journal after restart: `"NFS share already mounted correctly"` (reconciler detected existing mount) +- Post-restart: still exactly 1 mount (no double-mount) +- Data survived restart, healthz healthy + +### Step 6: Restore baseline — PASS +- NFS config removed from settings.yaml on both VMs +- NFS unmounted on both VMs +- Systemd overrides removed (User=scion restored) +- Services restarted and healthy +- Binary left at 1eaecd95 (integration tip, backward-compatible) +- Backup at `/usr/local/bin/scion.bak-nm1b` (commit 9a998934) + +## Final VM State +- **Binary:** 1eaecd95 (WIRED, left in place — backward compatible) +- **Config:** NFS block removed, broker_id preserved +- **NFS:** Not mounted (config removed) +- **Services:** Running, healthy +- **Overrides:** Removed + +## Observations +1. Settings migration strips unknown fields from legacy-format files — always include + `schema_version: "1"` when writing settings.yaml. +2. The `mountpoint -q` command returns exit code 32 (not 1) when the path exists but is + not a mountpoint. The code handles this correctly (treats as "not mounted"). +3. nconnect=4 works fine on kernel 6.8.0-1054-gcp with NFSv3 on Filestore BASIC_HDD. diff --git a/.design/project-log/2026-06-03-nm2-gke-nfs-workspace.md b/.design/project-log/2026-06-03-nm2-gke-nfs-workspace.md new file mode 100644 index 000000000..cb40beb8a --- /dev/null +++ b/.design/project-log/2026-06-03-nm2-gke-nfs-workspace.md @@ -0,0 +1,30 @@ +# NM2 — GKE NFS Workspace Live Test — PASSED + +**Date:** 2026-06-03 +**Agent:** qa-agent +**Branch:** `postgres/wave-b-integration` @ `4a6ccf50` +**Gate:** NM2 (Model B — GKE Autopilot + Filestore) +**Full report:** `/scion-volumes/scratchpad/NM2-REPORT.md` + +## Result: PASS (4 full + 1 partial-expected) + +| Scenario | Result | +|----------|--------| +| (a) NFS-backed workspace PVC+subPath, not EmptyDir; init-container pre-populates | **PASS** | +| (b) Init-container provisioning race-safety via sentinel | **PARTIAL** — sequential sentinel works; concurrent race needs advisory lock (N2-2b), which requires hub-mediated provisioning with CloudSQL. Expected limitation in direct-pod test. | +| (c) kubectl cp skip for NFS backend (verified at `k8s_runtime.go:394`) | **PASS** | +| (d) Stable FSGroup/UID 1000 on all NFS-backed pods | **PASS** | +| (e) Shared dirs on NFS via same PVC + distinct subPaths | **PASS** | + +## Infrastructure Provisioned in `scion-demo-cluster` + +- Namespace `scion-agents` created +- Static PV `scion-workspaces` + PVC `scion-workspaces` bound to Filestore `10.45.255.170:/scion_share` (RWX, NFSv3, 1Ti) — left in place for future tests +- Binary `4a6ccf50` confirmed working on GKE Autopilot + +## Key Observations + +- GKE Autopilot auto-injects `seccompProfile: RuntimeDefault` — no node-level tuning possible, all pod-level +- NFSv3 + nconnect=4 works on Autopilot nodes (confirmed in mount output) +- Filestore BASIC_HDD ownership issue: share root owned by 1002:1003 from NM1 setup; new project dirs need UID 1000 for pod writes. In production, the hub's provisioner handles this under the advisory lock. +- Advisory lock end-to-end (N2-2b) requires GKE pods to reach CloudSQL (35.202.106.255:5432) — network path not fully verified; this is the one remaining unconfirmed integration point for GKE Model B. diff --git a/.design/project-log/2026-06-05-pr306-review-fixes.md b/.design/project-log/2026-06-05-pr306-review-fixes.md new file mode 100644 index 000000000..1b9c00887 --- /dev/null +++ b/.design/project-log/2026-06-05-pr306-review-fixes.md @@ -0,0 +1,26 @@ +# PR #306 Review Feedback — First Fix Round + +**Date:** 2026-06-05 +**Branch:** pr/nfs-workspace +**PR:** https://github.com/GoogleCloudPlatform/scion/pull/306 + +## Review Comments Addressed + +All 6 review comments from gemini-code-assist were addressed: + +1. **HIGH — `cmd/server_foreground.go`**: Added `os.Stat` file existence check in `maybeMigrateLegacySQLite` before calling `IsLegacyRawSQLSchema`. Prevents errors on fresh installs where the database file doesn't exist yet. + +2. **MEDIUM — `.claude/scheduled_tasks.lock`**: Removed accidentally committed lock file from the repository. + +3. **MEDIUM — `.gitignore`**: Added `.claude/` to `.gitignore` to prevent future accidental commits of Claude temporary files. Also added `fixturegen` binary. + +4. **MEDIUM — `internal/fixturegen/main.go`**: Changed `copyFile` to use `defer out.Close()` so the file descriptor is always closed, even if a panic occurs during `io.Copy`. + +5. **MEDIUM — `cmd/server_migrate.go`**: Added non-negative validation for `--batch-size` flag before proceeding with migration. + +6. **MEDIUM — `pkg/config/settings_v1.go`**: Added `ValidateNFS()` method on `V1WorkspaceStorageConfig` that returns an error when backend is `"nfs"` but no shares are defined. Wired into server startup in `cmd/server_foreground.go`. Added 4 test cases covering: empty shares error, valid shares pass, local backend skip, nil receiver safety. + +## Additional + +- Ran `make fmt` to fix pre-existing gofmt issues across the codebase (committed separately as `style: run gofmt on pre-existing formatting issues`). +- Pre-existing test failures in `pkg/config` (5 tests unrelated to this PR) confirmed as pre-existing. diff --git a/.gitignore b/.gitignore index 8602ae133..199ec5b67 100644 --- a/.gitignore +++ b/.gitignore @@ -55,3 +55,7 @@ Thumbs.db testdata/hub-v46-fixture.db testdata/hub-v46-fixture.db-wal testdata/hub-v46-fixture.db-shm + +# Claude temporary files +.claude/ +fixturegen diff --git a/cmd/server_foreground.go b/cmd/server_foreground.go index 822d99a98..bd667e528 100644 --- a/cmd/server_foreground.go +++ b/cmd/server_foreground.go @@ -731,6 +731,9 @@ func initStore(ctx context.Context, cfg *config.GlobalConfig) (store.Store, erro // the file is already the Ent schema, empty, or absent. The provided context // allows the migration to be cancelled (e.g. Ctrl+C during first boot). func maybeMigrateLegacySQLite(ctx context.Context, path string) error { + if _, err := os.Stat(path); os.IsNotExist(err) { + return nil + } legacy, err := entc.IsLegacyRawSQLSchema(path) if err != nil { return fmt.Errorf("detecting database schema: %w", err) diff --git a/cmd/server_migrate.go b/cmd/server_migrate.go index 02c89c594..75c58f9f8 100644 --- a/cmd/server_migrate.go +++ b/cmd/server_migrate.go @@ -86,6 +86,10 @@ func runServerMigrate(cmd *cobra.Command, _ []string) error { ctx := cmd.Context() out := cmd.OutOrStdout() + if migrateBatchSize < 0 { + return fmt.Errorf("batch size must be non-negative, got %d", migrateBatchSize) + } + srcDSN, srcPath, err := parseSQLiteSourceDSN(migrateFrom) if err != nil { return err diff --git a/internal/fixturegen/main.go b/internal/fixturegen/main.go index e8a304c3d..924500cf8 100644 --- a/internal/fixturegen/main.go +++ b/internal/fixturegen/main.go @@ -115,8 +115,9 @@ func copyFile(src, dst string) error { if err != nil { return err } + defer out.Close() + if _, err := io.Copy(out, in); err != nil { - out.Close() return err } return out.Close() diff --git a/pkg/api/types.go b/pkg/api/types.go index 074a527fc..1e678938f 100644 --- a/pkg/api/types.go +++ b/pkg/api/types.go @@ -249,10 +249,11 @@ type VolumeMount struct { Source string `json:"source" yaml:"source"` Target string `json:"target" yaml:"target"` ReadOnly bool `json:"read_only,omitempty" yaml:"read_only,omitempty"` - Type string `json:"type,omitempty" yaml:"type,omitempty"` // "local" (default) or "gcs" + Type string `json:"type,omitempty" yaml:"type,omitempty"` // "local" (default), "gcs", or "nfs" Bucket string `json:"bucket,omitempty" yaml:"bucket,omitempty"` // For GCS Prefix string `json:"prefix,omitempty" yaml:"prefix,omitempty"` // For GCS Mode string `json:"mode,omitempty" yaml:"mode,omitempty"` // Mount options + Server string `json:"server,omitempty" yaml:"server,omitempty"` // NFS: server host/IP } // Validate checks that a VolumeMount has the required fields and valid values. @@ -271,8 +272,15 @@ func (v VolumeMount) Validate() error { if v.Bucket == "" { return fmt.Errorf("GCS volume mount for target %q missing required field: bucket", v.Target) } + case "nfs": + if v.Server == "" { + return fmt.Errorf("NFS volume mount for target %q missing required field: server", v.Target) + } + if v.Source == "" { + return fmt.Errorf("NFS volume mount for target %q missing required field: source (server export path)", v.Target) + } default: - return fmt.Errorf("volume mount for target %q has invalid type %q (must be \"local\" or \"gcs\")", v.Target, v.Type) + return fmt.Errorf("volume mount for target %q has invalid type %q (must be \"local\", \"gcs\", or \"nfs\")", v.Target, v.Type) } return nil diff --git a/pkg/api/types_test.go b/pkg/api/types_test.go index 3cf06bbe9..4afd26323 100644 --- a/pkg/api/types_test.go +++ b/pkg/api/types_test.go @@ -173,12 +173,40 @@ func TestVolumeMountValidate(t *testing.T) { }, wantErr: "missing required field: source", }, + { + name: "valid nfs", + vol: VolumeMount{ + Source: "/scion-workspaces", + Target: "/workspace", + Type: "nfs", + Server: "10.0.0.2", + }, + wantErr: "", + }, + { + name: "nfs missing server", + vol: VolumeMount{ + Source: "/scion-workspaces", + Target: "/workspace", + Type: "nfs", + }, + wantErr: "missing required field: server", + }, + { + name: "nfs missing source", + vol: VolumeMount{ + Target: "/workspace", + Type: "nfs", + Server: "10.0.0.2", + }, + wantErr: "missing required field: source", + }, { name: "invalid type", vol: VolumeMount{ Source: "/host/path", Target: "/container/path", - Type: "nfs", + Type: "bogus", }, wantErr: "invalid type", }, diff --git a/pkg/config/settings_v1.go b/pkg/config/settings_v1.go index decb821e0..f7f6905f7 100644 --- a/pkg/config/settings_v1.go +++ b/pkg/config/settings_v1.go @@ -259,10 +259,11 @@ type V1ServerConfig struct { Database *V1DatabaseConfig `json:"database,omitempty" yaml:"database,omitempty" koanf:"database"` Auth *V1AuthConfig `json:"auth,omitempty" yaml:"auth,omitempty" koanf:"auth"` OAuth *V1OAuthConfig `json:"oauth,omitempty" yaml:"oauth,omitempty" koanf:"oauth"` - Storage *V1StorageConfig `json:"storage,omitempty" yaml:"storage,omitempty" koanf:"storage"` - Secrets *V1SecretsConfig `json:"secrets,omitempty" yaml:"secrets,omitempty" koanf:"secrets"` - LogLevel string `json:"log_level,omitempty" yaml:"log_level,omitempty" koanf:"log_level"` - LogFormat string `json:"log_format,omitempty" yaml:"log_format,omitempty" koanf:"log_format"` + Storage *V1StorageConfig `json:"storage,omitempty" yaml:"storage,omitempty" koanf:"storage"` + WorkspaceStorage *V1WorkspaceStorageConfig `json:"workspace_storage,omitempty" yaml:"workspace_storage,omitempty" koanf:"workspace_storage"` + Secrets *V1SecretsConfig `json:"secrets,omitempty" yaml:"secrets,omitempty" koanf:"secrets"` + LogLevel string `json:"log_level,omitempty" yaml:"log_level,omitempty" koanf:"log_level"` + LogFormat string `json:"log_format,omitempty" yaml:"log_format,omitempty" koanf:"log_format"` // NotificationChannels configures external notification delivery channels. // Secrets (webhook URLs, API tokens) are held in memory only — never persisted to a database. @@ -418,6 +419,80 @@ type V1StorageConfig struct { LocalPath string `json:"local_path,omitempty" yaml:"local_path,omitempty" koanf:"local_path"` } +// V1WorkspaceStorageConfig selects the workspace storage backend. +// Backend defaults to "local" (today's node-local behavior). When set to "nfs", +// the NFS sub-block configures shared network-attached workspace storage. +type V1WorkspaceStorageConfig struct { + Backend string `json:"backend,omitempty" yaml:"backend,omitempty" koanf:"backend"` // "local" (default) | "nfs" + NFS *V1NFSConfig `json:"nfs,omitempty" yaml:"nfs,omitempty" koanf:"nfs"` +} + +// V1NFSConfig holds NFS workspace storage settings. +type V1NFSConfig struct { + // MountRoot is the local base under which each share is mounted at /. + MountRoot string `json:"mount_root,omitempty" yaml:"mount_root,omitempty" koanf:"mount_root"` + // MountOptions are passed to mount.nfs. Default "vers=3,hard,nconnect=4,_netdev". + // NFSv4.1 requires Filestore Enterprise/zonal or self-hosted NFS; basic/HDD + // (BASIC_HDD) supports NFSv3 only. We use Postgres advisory locks, not NFS + // flock, so v3 is fine for correctness. + MountOptions string `json:"mount_options,omitempty" yaml:"mount_options,omitempty" koanf:"mount_options"` + Shares []V1NFSShare `json:"shares,omitempty" yaml:"shares,omitempty" koanf:"shares"` + + // Stable, node-independent ownership for NFS-backed trees. + // Default 1000:1000 to converge with the K8s pod UID/GID. + UID int `json:"uid,omitempty" yaml:"uid,omitempty" koanf:"uid"` // default 1000 + GID int `json:"gid,omitempty" yaml:"gid,omitempty" koanf:"gid"` // default 1000 + + // Kubernetes realization + StorageClass string `json:"storage_class,omitempty" yaml:"storage_class,omitempty" koanf:"storage_class"` + SubPathRoot string `json:"subpath_root,omitempty" yaml:"subpath_root,omitempty" koanf:"subpath_root"` // default "projects" +} + +// V1NFSShare identifies a single NFS export that may be mounted by a Runtime Broker. +type V1NFSShare struct { + ID string `json:"id,omitempty" yaml:"id,omitempty" koanf:"id"` // stable share id → mount dir + (K8s) PV name + Server string `json:"server,omitempty" yaml:"server,omitempty" koanf:"server"` // e.g. 10.0.0.2 or Filestore IP + Export string `json:"export,omitempty" yaml:"export,omitempty" koanf:"export"` // server export path, e.g. /scion-workspaces + PVName string `json:"pv_name,omitempty" yaml:"pv_name,omitempty" koanf:"pv_name"` // K8s static PV+subPath strategy +} + +// ApplyNFSDefaults fills default values for NFS sub-fields when Backend is "nfs". +// When Backend is empty or "local", the NFS block is left as-is (no materialization). +// This is idempotent and safe to call multiple times. +func (ws *V1WorkspaceStorageConfig) ApplyNFSDefaults() { + if ws == nil || strings.ToLower(ws.Backend) != "nfs" { + return + } + if ws.NFS == nil { + ws.NFS = &V1NFSConfig{} + } + if ws.NFS.MountOptions == "" { + ws.NFS.MountOptions = "vers=3,hard,nconnect=4,_netdev" + } + if ws.NFS.UID == 0 { + ws.NFS.UID = 1000 + } + if ws.NFS.GID == 0 { + ws.NFS.GID = 1000 + } + if ws.NFS.SubPathRoot == "" { + ws.NFS.SubPathRoot = "projects" + } +} + +// ValidateNFS returns an error if Backend is "nfs" but the NFS block is +// misconfigured (e.g. no shares defined). Call after ApplyNFSDefaults. +func (ws *V1WorkspaceStorageConfig) ValidateNFS() error { + if ws == nil || strings.ToLower(ws.Backend) != "nfs" { + return nil + } + if ws.NFS == nil || len(ws.NFS.Shares) == 0 { + return fmt.Errorf("workspace_storage.backend is \"nfs\" but no NFS shares are defined; " + + "add at least one entry under workspace_storage.nfs.shares") + } + return nil +} + // V1SecretsConfig holds secrets backend settings. type V1SecretsConfig struct { Backend string `json:"backend,omitempty" yaml:"backend,omitempty" koanf:"backend"` @@ -1231,6 +1306,11 @@ func ConvertV1ServerToGlobalConfig(v1 *V1ServerConfig) *GlobalConfig { } } + // Workspace storage NFS defaults (conditional on backend=nfs) + if v1.WorkspaceStorage != nil { + v1.WorkspaceStorage.ApplyNFSDefaults() + } + // GitHub App if v1.GitHubApp != nil { gc.GitHubApp.AppID = v1.GitHubApp.AppID diff --git a/pkg/config/settings_v1_test.go b/pkg/config/settings_v1_test.go index 776f74d50..213e54900 100644 --- a/pkg/config/settings_v1_test.go +++ b/pkg/config/settings_v1_test.go @@ -3627,6 +3627,182 @@ func TestRequireImageRegistry_Configured(t *testing.T) { assert.NoError(t, err) } +// --- N0-1: Workspace storage config tests --- + +func TestWorkspaceStorageConfig_YAMLRoundTrip(t *testing.T) { + yamlInput := ` +schema_version: "1" +server: + workspace_storage: + backend: nfs + nfs: + mount_root: /mnt/nfs + mount_options: "vers=4.1,hard,nconnect=8" + uid: 2000 + gid: 2000 + subpath_root: workspaces + storage_class: filestore-sc + shares: + - id: share-1 + server: "10.0.0.2" + export: /scion-workspaces + pv_name: scion-workspaces-pv +` + tmpDir := t.TempDir() + originalHome := os.Getenv("HOME") + defer os.Setenv("HOME", originalHome) + os.Setenv("HOME", tmpDir) + + globalDir := filepath.Join(tmpDir, ".scion") + require.NoError(t, os.MkdirAll(globalDir, 0755)) + require.NoError(t, os.WriteFile(filepath.Join(globalDir, "settings.yaml"), []byte(yamlInput), 0644)) + + projectDir := filepath.Join(tmpDir, "project", ".scion") + require.NoError(t, os.MkdirAll(projectDir, 0755)) + + vs, err := LoadVersionedSettings(projectDir) + require.NoError(t, err) + require.NotNil(t, vs.Server) + require.NotNil(t, vs.Server.WorkspaceStorage) + + ws := vs.Server.WorkspaceStorage + assert.Equal(t, "nfs", ws.Backend) + require.NotNil(t, ws.NFS) + assert.Equal(t, "/mnt/nfs", ws.NFS.MountRoot) + assert.Equal(t, "vers=4.1,hard,nconnect=8", ws.NFS.MountOptions) + assert.Equal(t, 2000, ws.NFS.UID) + assert.Equal(t, 2000, ws.NFS.GID) + assert.Equal(t, "workspaces", ws.NFS.SubPathRoot) + assert.Equal(t, "filestore-sc", ws.NFS.StorageClass) + require.Len(t, ws.NFS.Shares, 1) + assert.Equal(t, "share-1", ws.NFS.Shares[0].ID) + assert.Equal(t, "10.0.0.2", ws.NFS.Shares[0].Server) + assert.Equal(t, "/scion-workspaces", ws.NFS.Shares[0].Export) + assert.Equal(t, "scion-workspaces-pv", ws.NFS.Shares[0].PVName) +} + +func TestWorkspaceStorageConfig_JSONRoundTrip(t *testing.T) { + ws := &V1WorkspaceStorageConfig{ + Backend: "nfs", + NFS: &V1NFSConfig{ + MountRoot: "/mnt/nfs", + MountOptions: "vers=4.1,hard,nconnect=4,_netdev", + UID: 1000, + GID: 1000, + SubPathRoot: "projects", + Shares: []V1NFSShare{ + {ID: "main", Server: "10.0.0.2", Export: "/scion-workspaces", PVName: "scion-ws-pv"}, + }, + }, + } + + data, err := json.Marshal(ws) + require.NoError(t, err) + + var roundTripped V1WorkspaceStorageConfig + require.NoError(t, json.Unmarshal(data, &roundTripped)) + + assert.Equal(t, ws.Backend, roundTripped.Backend) + require.NotNil(t, roundTripped.NFS) + assert.Equal(t, ws.NFS.MountRoot, roundTripped.NFS.MountRoot) + assert.Equal(t, ws.NFS.MountOptions, roundTripped.NFS.MountOptions) + assert.Equal(t, ws.NFS.UID, roundTripped.NFS.UID) + assert.Equal(t, ws.NFS.GID, roundTripped.NFS.GID) + assert.Equal(t, ws.NFS.SubPathRoot, roundTripped.NFS.SubPathRoot) + require.Len(t, roundTripped.NFS.Shares, 1) + assert.Equal(t, ws.NFS.Shares[0].ID, roundTripped.NFS.Shares[0].ID) + assert.Equal(t, ws.NFS.Shares[0].Server, roundTripped.NFS.Shares[0].Server) +} + +func TestWorkspaceStorageConfig_NFSDefaults(t *testing.T) { + t.Run("nfs backend applies defaults to empty sub-fields", func(t *testing.T) { + ws := &V1WorkspaceStorageConfig{Backend: "nfs"} + ws.ApplyNFSDefaults() + + require.NotNil(t, ws.NFS) + assert.Equal(t, "vers=3,hard,nconnect=4,_netdev", ws.NFS.MountOptions) + assert.Equal(t, 1000, ws.NFS.UID) + assert.Equal(t, 1000, ws.NFS.GID) + assert.Equal(t, "projects", ws.NFS.SubPathRoot) + }) + + t.Run("nfs backend preserves explicit values", func(t *testing.T) { + ws := &V1WorkspaceStorageConfig{ + Backend: "nfs", + NFS: &V1NFSConfig{ + MountOptions: "custom-opts", + UID: 5000, + GID: 5000, + SubPathRoot: "custom-root", + }, + } + ws.ApplyNFSDefaults() + + assert.Equal(t, "custom-opts", ws.NFS.MountOptions) + assert.Equal(t, 5000, ws.NFS.UID) + assert.Equal(t, 5000, ws.NFS.GID) + assert.Equal(t, "custom-root", ws.NFS.SubPathRoot) + }) + + t.Run("local backend does not materialize NFS block", func(t *testing.T) { + ws := &V1WorkspaceStorageConfig{Backend: "local"} + ws.ApplyNFSDefaults() + assert.Nil(t, ws.NFS) + }) + + t.Run("empty backend does not materialize NFS block", func(t *testing.T) { + ws := &V1WorkspaceStorageConfig{} + ws.ApplyNFSDefaults() + assert.Nil(t, ws.NFS) + }) + + t.Run("nil receiver is safe", func(t *testing.T) { + var ws *V1WorkspaceStorageConfig + ws.ApplyNFSDefaults() // should not panic + }) +} + +func TestWorkspaceStorageConfig_ValidateNFS(t *testing.T) { + t.Run("nfs backend with no shares returns error", func(t *testing.T) { + ws := &V1WorkspaceStorageConfig{Backend: "nfs"} + ws.ApplyNFSDefaults() + err := ws.ValidateNFS() + require.Error(t, err) + assert.Contains(t, err.Error(), "no NFS shares are defined") + }) + + t.Run("nfs backend with shares passes", func(t *testing.T) { + ws := &V1WorkspaceStorageConfig{ + Backend: "nfs", + NFS: &V1NFSConfig{ + Shares: []V1NFSShare{{ID: "share1", Server: "10.0.0.2", Export: "/data"}}, + }, + } + ws.ApplyNFSDefaults() + err := ws.ValidateNFS() + require.NoError(t, err) + }) + + t.Run("local backend skips validation", func(t *testing.T) { + ws := &V1WorkspaceStorageConfig{Backend: "local"} + err := ws.ValidateNFS() + require.NoError(t, err) + }) + + t.Run("nil receiver is safe", func(t *testing.T) { + var ws *V1WorkspaceStorageConfig + err := ws.ValidateNFS() + require.NoError(t, err) + }) +} + +func TestWorkspaceStorageConfig_BackendUnset_IsLocal(t *testing.T) { + // Backend unset => treated as "local", no NFS struct required. + ws := &V1WorkspaceStorageConfig{} + assert.Equal(t, "", ws.Backend, "empty backend is treated as local") + assert.Nil(t, ws.NFS, "no NFS block when backend is local/empty") +} + // --- Helper --- func boolPtr(b bool) *bool { diff --git a/pkg/config/templates_test.go b/pkg/config/templates_test.go index 319cce87e..42bf62f55 100644 --- a/pkg/config/templates_test.go +++ b/pkg/config/templates_test.go @@ -431,6 +431,37 @@ func TestLoadConfigInvalidVolumes(t *testing.T) { } }) + t.Run("valid nfs volume", func(t *testing.T) { + tmpDir, err := os.MkdirTemp("", "scion-test-nfs-volumes-*") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpDir) + + configContent := `{ + "harness": "gemini", + "volumes": [{"source": "/scion-workspaces", "target": "/workspace", "type": "nfs", "server": "10.0.0.2"}] + }` + if err := os.WriteFile(filepath.Join(tmpDir, "scion-agent.json"), []byte(configContent), 0644); err != nil { + t.Fatal(err) + } + + tpl := &Template{Path: tmpDir} + cfg, err := tpl.LoadConfig() + if err != nil { + t.Fatalf("LoadConfig() unexpected error for valid nfs volume: %v", err) + } + if len(cfg.Volumes) != 1 { + t.Fatalf("LoadConfig() expected 1 volume, got %d", len(cfg.Volumes)) + } + if cfg.Volumes[0].Type != "nfs" { + t.Errorf("Volume type = %q, want %q", cfg.Volumes[0].Type, "nfs") + } + if cfg.Volumes[0].Server != "10.0.0.2" { + t.Errorf("Volume server = %q, want %q", cfg.Volumes[0].Server, "10.0.0.2") + } + }) + t.Run("volume with invalid type", func(t *testing.T) { tmpDir, err := os.MkdirTemp("", "scion-test-invalid-volumes-*") if err != nil { @@ -440,7 +471,7 @@ func TestLoadConfigInvalidVolumes(t *testing.T) { configContent := `{ "harness": "gemini", - "volumes": [{"source": "/foo", "target": "/bar", "type": "nfs"}] + "volumes": [{"source": "/foo", "target": "/bar", "type": "bogus"}] }` if err := os.WriteFile(filepath.Join(tmpDir, "scion-agent.json"), []byte(configContent), 0644); err != nil { t.Fatal(err) diff --git a/pkg/hub/broker_http_transport.go b/pkg/hub/broker_http_transport.go index 9dab4d1b0..0b552ebfa 100644 --- a/pkg/hub/broker_http_transport.go +++ b/pkg/hub/broker_http_transport.go @@ -447,8 +447,11 @@ func (t *brokerHTTPTransport) ExecAgent(ctx context.Context, brokerID, brokerEnd return result.Output, result.ExitCode, nil } -func (t *brokerHTTPTransport) CleanupProject(ctx context.Context, brokerID, brokerEndpoint, projectSlug string) error { +func (t *brokerHTTPTransport) CleanupProject(ctx context.Context, brokerID, brokerEndpoint, projectSlug, projectID string) error { endpoint := fmt.Sprintf("%s/api/v1/projects/%s", strings.TrimSuffix(brokerEndpoint, "/"), url.PathEscape(projectSlug)) + if projectID != "" { + endpoint += "?project_id=" + url.QueryEscape(projectID) + } resp, err := t.doRequest(ctx, brokerID, http.MethodDelete, endpoint, nil) if err != nil { return fmt.Errorf("failed to send request: %w", err) diff --git a/pkg/hub/broker_routing_test.go b/pkg/hub/broker_routing_test.go index a3cbe773b..0fb3761fa 100644 --- a/pkg/hub/broker_routing_test.go +++ b/pkg/hub/broker_routing_test.go @@ -67,7 +67,7 @@ func (f *fakeHTTPClient) GetAgentLogs(context.Context, string, string, string, s func (f *fakeHTTPClient) ExecAgent(context.Context, string, string, string, string, []string, int) (string, int, error) { return "", 0, nil } -func (f *fakeHTTPClient) CleanupProject(context.Context, string, string, string) error { +func (f *fakeHTTPClient) CleanupProject(context.Context, string, string, string, string) error { return nil } diff --git a/pkg/hub/brokerclient.go b/pkg/hub/brokerclient.go index b3dffcf54..745f4cb74 100644 --- a/pkg/hub/brokerclient.go +++ b/pkg/hub/brokerclient.go @@ -89,8 +89,8 @@ func (c *AuthenticatedBrokerClient) ExecAgent(ctx context.Context, brokerID, bro } // CleanupProject asks a broker to remove its local hub-managed project directory with HMAC authentication. -func (c *AuthenticatedBrokerClient) CleanupProject(ctx context.Context, brokerID, brokerEndpoint, projectSlug string) error { - return c.transport.CleanupProject(ctx, brokerID, brokerEndpoint, projectSlug) +func (c *AuthenticatedBrokerClient) CleanupProject(ctx context.Context, brokerID, brokerEndpoint, projectSlug, projectID string) error { + return c.transport.CleanupProject(ctx, brokerID, brokerEndpoint, projectSlug, projectID) } // FinalizeEnv sends gathered env vars to a broker to complete agent creation. diff --git a/pkg/hub/controlchannel_client.go b/pkg/hub/controlchannel_client.go index bba8e4e8a..c0aeaa0f4 100644 --- a/pkg/hub/controlchannel_client.go +++ b/pkg/hub/controlchannel_client.go @@ -326,9 +326,12 @@ func (c *ControlChannelBrokerClient) ExecAgent(ctx context.Context, brokerID, br return result.Output, result.ExitCode, nil } -func (c *ControlChannelBrokerClient) CleanupProject(ctx context.Context, brokerID, brokerEndpoint, projectSlug string) error { +func (c *ControlChannelBrokerClient) CleanupProject(ctx context.Context, brokerID, brokerEndpoint, projectSlug, projectID string) error { _ = brokerEndpoint path := fmt.Sprintf("/api/v1/projects/%s", url.PathEscape(projectSlug)) + if projectID != "" { + path += "?project_id=" + url.QueryEscape(projectID) + } resp, err := c.doRequest(ctx, brokerID, "DELETE", path, "", nil) if err != nil { return err @@ -604,11 +607,11 @@ func (c *HybridBrokerClient) ExecAgent(ctx context.Context, brokerID, brokerEndp return c.httpClient.ExecAgent(ctx, brokerID, brokerEndpoint, agentID, projectID, command, timeout) } -func (c *HybridBrokerClient) CleanupProject(ctx context.Context, brokerID, brokerEndpoint, projectSlug string) error { +func (c *HybridBrokerClient) CleanupProject(ctx context.Context, brokerID, brokerEndpoint, projectSlug, projectID string) error { if c.useControlChannel(brokerID) { - return c.controlChannel.CleanupProject(ctx, brokerID, brokerEndpoint, projectSlug) + return c.controlChannel.CleanupProject(ctx, brokerID, brokerEndpoint, projectSlug, projectID) } - return c.httpClient.CleanupProject(ctx, brokerID, brokerEndpoint, projectSlug) + return c.httpClient.CleanupProject(ctx, brokerID, brokerEndpoint, projectSlug, projectID) } // FinalizeEnv sends gathered env vars to a broker, using route() to decide the diff --git a/pkg/hub/handlers.go b/pkg/hub/handlers.go index 1c5c5db18..50f1656ae 100644 --- a/pkg/hub/handlers.go +++ b/pkg/hub/handlers.go @@ -5461,7 +5461,7 @@ func (s *Server) cleanupBrokerProjectDirectories(ctx context.Context, project *s continue } - if err := client.CleanupProject(ctx, provider.BrokerID, broker.Endpoint, project.Slug); err != nil { + if err := client.CleanupProject(ctx, provider.BrokerID, broker.Endpoint, project.Slug, project.ID); err != nil { slog.Warn("failed to cleanup project on broker", "project_id", project.ID, "slug", project.Slug, "broker", provider.BrokerID, "endpoint", broker.Endpoint, "error", err) diff --git a/pkg/hub/httpdispatcher.go b/pkg/hub/httpdispatcher.go index 83662f1d0..167fa04da 100644 --- a/pkg/hub/httpdispatcher.go +++ b/pkg/hub/httpdispatcher.go @@ -100,8 +100,8 @@ func (c *HTTPRuntimeBrokerClient) ExecAgent(ctx context.Context, brokerID, broke return c.transport.ExecAgent(ctx, brokerID, brokerEndpoint, agentID, projectID, command, timeout) } -func (c *HTTPRuntimeBrokerClient) CleanupProject(ctx context.Context, brokerID, brokerEndpoint, projectSlug string) error { - return c.transport.CleanupProject(ctx, brokerID, brokerEndpoint, projectSlug) +func (c *HTTPRuntimeBrokerClient) CleanupProject(ctx context.Context, brokerID, brokerEndpoint, projectSlug, projectID string) error { + return c.transport.CleanupProject(ctx, brokerID, brokerEndpoint, projectSlug, projectID) } // GetClient returns the underlying RuntimeBrokerClient. diff --git a/pkg/hub/httpdispatcher_test.go b/pkg/hub/httpdispatcher_test.go index 9e774d4c6..8b0eb451c 100644 --- a/pkg/hub/httpdispatcher_test.go +++ b/pkg/hub/httpdispatcher_test.go @@ -180,7 +180,7 @@ func (m *mockRuntimeBrokerClient) GetAgentLogs(ctx context.Context, brokerID, br return "", nil } -func (m *mockRuntimeBrokerClient) CleanupProject(ctx context.Context, brokerID, brokerEndpoint, projectSlug string) error { +func (m *mockRuntimeBrokerClient) CleanupProject(ctx context.Context, brokerID, brokerEndpoint, projectSlug, projectID string) error { m.cleanupCalled = true m.cleanupCalls++ m.lastBrokerID = brokerID diff --git a/pkg/hub/scheduler_test.go b/pkg/hub/scheduler_test.go index 29fb504d1..796d7488d 100644 --- a/pkg/hub/scheduler_test.go +++ b/pkg/hub/scheduler_test.go @@ -1292,6 +1292,18 @@ func (l *lockerStore) TryAdvisoryLock(_ context.Context, _ store.AdvisoryLockKey }, nil } +func (l *lockerStore) TryAdvisoryLockObject(_ context.Context, _ store.AdvisoryLockKey, _ int32) (bool, func() error, error) { + if l.err != nil { + return false, func() error { return nil }, l.err + } + return l.acquired, func() error { + if l.released != nil { + l.released.Add(1) + } + return nil + }, nil +} + // TestSingletonGuard_SkipsTickOnLockError verifies that a lock-acquisition error // (e.g. a connection timeout) causes the tick to be SKIPPED rather than running // the handler unguarded — running unguarded would let multiple replicas execute diff --git a/pkg/hub/server.go b/pkg/hub/server.go index aa989a0d3..e3890f6ca 100644 --- a/pkg/hub/server.go +++ b/pkg/hub/server.go @@ -329,8 +329,9 @@ type RuntimeBrokerClient interface { // CleanupProject asks a broker to remove its local hub-native project directory. // brokerID is used for HMAC authentication lookup. + // projectID is passed to enable NFS subtree cleanup (keyed by project ID). // 404 responses are tolerated for idempotency. - CleanupProject(ctx context.Context, brokerID, brokerEndpoint, projectSlug string) error + CleanupProject(ctx context.Context, brokerID, brokerEndpoint, projectSlug, projectID string) error } // RemoteCreateAgentRequest is the request body for creating an agent on a remote runtime broker. diff --git a/pkg/runtime/common.go b/pkg/runtime/common.go index 4a3f79802..a9e1801e9 100644 --- a/pkg/runtime/common.go +++ b/pkg/runtime/common.go @@ -277,9 +277,30 @@ func buildCommonRunArgs(config RunConfig) ([]string, error) { } } - // Pass host user UID/GID for container user synchronization - addEnv("SCION_HOST_UID", fmt.Sprintf("%d", os.Getuid())) - addEnv("SCION_HOST_GID", fmt.Sprintf("%d", os.Getgid())) + // Pass host user UID/GID for container user synchronization. + // N1-5: branch on workspace backend — NFS needs a stable, node-independent + // UID/GID (default 1000:1000) so files written by agents on different nodes + // have consistent ownership on the shared filesystem. The local backend + // continues to use the broker's host UID/GID (today's behavior, unchanged). + uid, gid := os.Getuid(), os.Getgid() + if config.WorkspaceBackendName == "nfs" { + uid, gid = config.NFSUID, config.NFSGID + if uid == 0 { + uid = 1000 // default stable NFS UID + } + if gid == 0 { + gid = 1000 // default stable NFS GID + } + } + addEnv("SCION_HOST_UID", fmt.Sprintf("%d", uid)) + addEnv("SCION_HOST_GID", fmt.Sprintf("%d", gid)) + + // Expose the workspace backend to the container so sciontool init can + // skip the per-start recursive chown when backend=nfs (slow/racy over + // the network; ownership is set once by operator + provisioner). + if config.WorkspaceBackendName != "" { + addEnv("SCION_WORKSPACE_BACKEND", config.WorkspaceBackendName) + } // Phase 3 & 5: Project identity injection addEnv("SCION_PROJECT", config.Project) diff --git a/pkg/runtime/interface.go b/pkg/runtime/interface.go index 2cc02e769..98acfdadd 100644 --- a/pkg/runtime/interface.go +++ b/pkg/runtime/interface.go @@ -18,6 +18,7 @@ import ( "context" "github.com/GoogleCloudPlatform/scion/pkg/api" + "github.com/GoogleCloudPlatform/scion/pkg/store" ) type RunConfig struct { @@ -51,6 +52,52 @@ type RunConfig struct { NetworkMode string // Container network mode (e.g. "host" for --network=host) Project string // Project name (e.g., "global" or "my-project") ProjectID string // Project ID (e.g., "550e8400-e29b-41d4-a716-446655440000") + + // WorkspaceBackendName is "local" or "nfs", set by the workspace backend + // selector. Used to branch UID/GID injection and skip per-start chown + // when NFS (N1-5). + WorkspaceBackendName string + // NFSUID and NFSGID are the stable, node-independent UID/GID for NFS-backed + // workspaces. Advertised as SCION_HOST_UID/GID when WorkspaceBackendName is "nfs" + // instead of os.Getuid()/os.Getgid(). Default 1000:1000 (design §9.1). + NFSUID int + NFSGID int + + // NFSPVClaimName is the K8s PVC name for the NFS-backed workspace volume. + // Set when WorkspaceBackendName is "nfs". The PVC references a static RWX PV + // bound to the Filestore/NFS export. Empty for local backend. + NFSPVClaimName string + // NFSSubPath is the subPath within the NFS PVC that isolates this project's + // workspace (e.g. "projects//workspace"). Used by K8s buildPod to scope + // the volume mount — pod sees only its project subtree (design §9.4). + NFSSubPath string + // NFSStorageClass is the K8s StorageClass for NFS-backed PVCs. + // Used when creating shared-dir PVCs on NFS. Empty uses cluster default. + NFSStorageClass string + + // GitCloneForInit holds git clone configuration for NFS init-container + // workspace provisioning (N2-2). When set, buildPod adds an init container + // that clones/provisions the workspace before the main container starts. + GitCloneForInit *api.GitCloneConfig + + // Locker provides the per-project advisory lock for NFS workspace + // provisioning (N2-2b, design §7, risk RN1). When set and backend=nfs, + // the K8s runtime acquires the lock before building the pod to determine + // whether this pod should clone (lock winner) or wait for the sentinel + // (lock loser). This prevents concurrent first-clone corruption when + // two pods for the same project are scheduled on different nodes. + // + // May be nil — when absent, all pods get the cloning init container + // (sentinel-only guard, correct for single-node but unsafe for + // multi-node). On Postgres-backed deployments this is wired from + // the store's AdvisoryLocker capability. + Locker store.AdvisoryLocker + + // nfsProvisionLockLost is set internally by Run() after a failed + // advisory lock acquisition attempt. When true, buildPod injects a + // wait-for-sentinel init container instead of the cloning one. + // Callers should not set this field. + nfsProvisionLockLost bool } type Runtime interface { diff --git a/pkg/runtime/k8s_nfs_test.go b/pkg/runtime/k8s_nfs_test.go new file mode 100644 index 000000000..6fdfde4c9 --- /dev/null +++ b/pkg/runtime/k8s_nfs_test.go @@ -0,0 +1,1289 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package runtime + +import ( + "context" + "errors" + "os" + "strings" + "testing" + "time" + + "github.com/GoogleCloudPlatform/scion/pkg/api" + "github.com/GoogleCloudPlatform/scion/pkg/k8s" + "github.com/GoogleCloudPlatform/scion/pkg/store" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + k8sruntime "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/dynamic/fake" + k8sfake "k8s.io/client-go/kubernetes/fake" +) + +// newNFSTestK8sRuntime creates a KubernetesRuntime backed by a fake clientset +// for unit testing buildPod and related methods. +func newNFSTestK8sRuntime() *KubernetesRuntime { + clientset := k8sfake.NewClientset() + scheme := k8sruntime.NewScheme() + fc := fake.NewSimpleDynamicClient(scheme) + client := k8s.NewTestClient(fc, clientset) + return NewKubernetesRuntime(client) +} + +// --- N2-1: NFS-backed workspace volume tests --- + +func TestBuildPod_WorkspaceVolume_LocalBackend_EmptyDir(t *testing.T) { + r := newNFSTestK8sRuntime() + config := RunConfig{ + Name: "test-local", + Image: "test-image", + UnixUsername: "scion", + // WorkspaceBackendName defaults to "" (local) + } + + pod, err := r.buildPod("default", config) + if err != nil { + t.Fatalf("buildPod failed: %v", err) + } + + // Volume must be EmptyDir + var found bool + for _, v := range pod.Spec.Volumes { + if v.Name == "workspace" { + found = true + if v.VolumeSource.EmptyDir == nil { + t.Errorf("local backend: workspace volume should be EmptyDir, got %+v", v.VolumeSource) + } + if v.VolumeSource.PersistentVolumeClaim != nil { + t.Errorf("local backend: workspace volume should NOT be PVC") + } + } + } + if !found { + t.Fatal("workspace volume not found in pod spec") + } + + // VolumeMount must not have subPath + for _, vm := range pod.Spec.Containers[0].VolumeMounts { + if vm.Name == "workspace" { + if vm.SubPath != "" { + t.Errorf("local backend: workspace mount should not have subPath, got %q", vm.SubPath) + } + if vm.MountPath != "/workspace" { + t.Errorf("local backend: workspace mount path = %q, want /workspace", vm.MountPath) + } + } + } +} + +func TestBuildPod_WorkspaceVolume_NFSBackend_PVCWithSubPath(t *testing.T) { + r := newNFSTestK8sRuntime() + config := RunConfig{ + Name: "test-nfs", + Image: "test-image", + UnixUsername: "scion", + WorkspaceBackendName: "nfs", + NFSPVClaimName: "scion-workspaces", + NFSSubPath: "projects/proj-123/workspace", + } + + pod, err := r.buildPod("default", config) + if err != nil { + t.Fatalf("buildPod failed: %v", err) + } + + // Volume must be PVC + var found bool + for _, v := range pod.Spec.Volumes { + if v.Name == "workspace" { + found = true + if v.VolumeSource.PersistentVolumeClaim == nil { + t.Fatalf("NFS backend: workspace volume should be PVC, got %+v", v.VolumeSource) + } + if v.VolumeSource.PersistentVolumeClaim.ClaimName != "scion-workspaces" { + t.Errorf("PVC claimName = %q, want %q", v.VolumeSource.PersistentVolumeClaim.ClaimName, "scion-workspaces") + } + if v.VolumeSource.EmptyDir != nil { + t.Errorf("NFS backend: workspace volume should NOT be EmptyDir") + } + } + } + if !found { + t.Fatal("workspace volume not found in pod spec") + } + + // VolumeMount must have subPath for isolation + for _, vm := range pod.Spec.Containers[0].VolumeMounts { + if vm.Name == "workspace" { + if vm.SubPath != "projects/proj-123/workspace" { + t.Errorf("NFS backend: workspace mount subPath = %q, want %q", vm.SubPath, "projects/proj-123/workspace") + } + if vm.MountPath != "/workspace" { + t.Errorf("NFS backend: workspace mount path = %q, want /workspace", vm.MountPath) + } + } + } +} + +func TestBuildPod_WorkspaceVolume_NFSWithoutPVCName_FallsBackToEmptyDir(t *testing.T) { + r := newNFSTestK8sRuntime() + // NFS backend but missing PVC name — defensive fallback to EmptyDir + config := RunConfig{ + Name: "test-nfs-no-pvc", + Image: "test-image", + UnixUsername: "scion", + WorkspaceBackendName: "nfs", + // NFSPVClaimName is empty + } + + pod, err := r.buildPod("default", config) + if err != nil { + t.Fatalf("buildPod failed: %v", err) + } + + for _, v := range pod.Spec.Volumes { + if v.Name == "workspace" { + if v.VolumeSource.EmptyDir == nil { + t.Errorf("NFS without PVC name: should fall back to EmptyDir, got %+v", v.VolumeSource) + } + } + } +} + +func TestBuildPod_NoInitContainers_LocalBackend(t *testing.T) { + r := newNFSTestK8sRuntime() + config := RunConfig{ + Name: "test-local", + Image: "test-image", + UnixUsername: "scion", + } + + pod, err := r.buildPod("default", config) + if err != nil { + t.Fatalf("buildPod failed: %v", err) + } + + if len(pod.Spec.InitContainers) != 0 { + t.Errorf("local backend: expected no init containers, got %d", len(pod.Spec.InitContainers)) + } +} + +// --- N2-2: Init-container workspace provisioning tests --- + +func TestBuildPod_NFSBackend_InitContainer_Present(t *testing.T) { + r := newNFSTestK8sRuntime() + config := RunConfig{ + Name: "test-nfs-init", + Image: "test-image", + UnixUsername: "scion", + WorkspaceBackendName: "nfs", + NFSPVClaimName: "scion-workspaces", + NFSSubPath: "projects/proj-123/workspace", + GitCloneForInit: &api.GitCloneConfig{ + URL: "https://github.com/example/repo.git", + Branch: "main", + Depth: 1, + }, + } + + pod, err := r.buildPod("default", config) + if err != nil { + t.Fatalf("buildPod failed: %v", err) + } + + // Must have exactly one init container + if len(pod.Spec.InitContainers) != 1 { + t.Fatalf("expected 1 init container, got %d", len(pod.Spec.InitContainers)) + } + + ic := pod.Spec.InitContainers[0] + + // Init container name + if ic.Name != "workspace-provision" { + t.Errorf("init container name = %q, want %q", ic.Name, "workspace-provision") + } + + // Uses the same image + if ic.Image != "test-image" { + t.Errorf("init container image = %q, want %q", ic.Image, "test-image") + } + + // Must mount workspace volume with subPath + var wsMount *corev1.VolumeMount + for i := range ic.VolumeMounts { + if ic.VolumeMounts[i].Name == "workspace" { + wsMount = &ic.VolumeMounts[i] + break + } + } + if wsMount == nil { + t.Fatal("init container: workspace volume mount not found") + } + if wsMount.MountPath != "/workspace" { + t.Errorf("init container workspace mountPath = %q, want /workspace", wsMount.MountPath) + } + if wsMount.SubPath != "projects/proj-123/workspace" { + t.Errorf("init container workspace subPath = %q, want %q", wsMount.SubPath, "projects/proj-123/workspace") + } + + // Command should reference env vars (not inline URL/branch) and contain sentinel check + if len(ic.Command) < 3 { + t.Fatalf("init container command too short: %v", ic.Command) + } + script := ic.Command[2] // sh -c