From ce1ffee49934a3b00c92b865fd41f1ee3e4c294b Mon Sep 17 00:00:00 2001 From: sdairs Date: Fri, 2 Oct 2026 19:11:22 +0100 Subject: [PATCH 1/2] Add Go gqlgen and Ent playlist API example --- .github/workflows/playlist-api.yml | 25 + applications/playlist-api/.env.example | 10 + applications/playlist-api/.gitignore | 8 + applications/playlist-api/README.md | 130 + applications/playlist-api/cmd/server/main.go | 138 + applications/playlist-api/ent/account.go | 118 + .../playlist-api/ent/account/account.go | 70 + .../playlist-api/ent/account/where.go | 93 + .../playlist-api/ent/account_create.go | 213 + .../playlist-api/ent/account_delete.go | 88 + .../playlist-api/ent/account_query.go | 622 +++ .../playlist-api/ent/account_update.go | 339 ++ applications/playlist-api/ent/client.go | 867 ++++ applications/playlist-api/ent/ent.go | 614 +++ .../playlist-api/ent/enttest/enttest.go | 84 + applications/playlist-api/ent/hook/hook.go | 235 ++ applications/playlist-api/ent/item.go | 174 + applications/playlist-api/ent/item/item.go | 118 + applications/playlist-api/ent/item/where.go | 211 + applications/playlist-api/ent/item_create.go | 288 ++ applications/playlist-api/ent/item_delete.go | 88 + applications/playlist-api/ent/item_query.go | 719 ++++ applications/playlist-api/ent/item_update.go | 267 ++ .../playlist-api/ent/migrate/migrate.go | 64 + .../playlist-api/ent/migrate/schema.go | 114 + applications/playlist-api/ent/mutation.go | 2157 ++++++++++ applications/playlist-api/ent/playlist.go | 173 + .../playlist-api/ent/playlist/playlist.go | 129 + .../playlist-api/ent/playlist/where.go | 256 ++ .../playlist-api/ent/playlist_create.go | 315 ++ .../playlist-api/ent/playlist_delete.go | 88 + .../playlist-api/ent/playlist_query.go | 719 ++++ .../playlist-api/ent/playlist_update.go | 469 +++ .../playlist-api/ent/predicate/predicate.go | 19 + applications/playlist-api/ent/runtime.go | 127 + .../playlist-api/ent/runtime/runtime.go | 10 + .../playlist-api/ent/schema/account.go | 13 + applications/playlist-api/ent/schema/item.go | 21 + .../playlist-api/ent/schema/playlist.go | 19 + applications/playlist-api/ent/schema/track.go | 15 + applications/playlist-api/ent/track.go | 154 + applications/playlist-api/ent/track/track.go | 103 + applications/playlist-api/ent/track/where.go | 278 ++ applications/playlist-api/ent/track_create.go | 268 ++ applications/playlist-api/ent/track_delete.go | 88 + applications/playlist-api/ent/track_query.go | 644 +++ applications/playlist-api/ent/track_update.go | 507 +++ applications/playlist-api/ent/tx.go | 219 + .../playlist-api/examples/create.json | 12 + applications/playlist-api/examples/list.json | 6 + .../playlist-api/examples/reorder.json | 13 + applications/playlist-api/generate.go | 4 + applications/playlist-api/go.mod | 60 + applications/playlist-api/go.sum | 142 + applications/playlist-api/gqlgen.yml | 13 + applications/playlist-api/graph/generated.go | 3637 +++++++++++++++++ .../playlist-api/graph/model/models_gen.go | 44 + applications/playlist-api/graph/resolver.go | 3 + .../playlist-api/graph/schema.graphqls | 8 + .../playlist-api/graph/schema.resolvers.go | 49 + .../playlist-api/internal/app/cloud_test.go | 180 + .../playlist-api/internal/app/database.go | 133 + .../playlist-api/internal/app/domain.go | 102 + .../playlist-api/internal/app/domain_test.go | 78 + .../playlist-api/internal/app/middleware.go | 84 + .../playlist-api/internal/app/policy.go | 80 + .../playlist-api/internal/app/store.go | 202 + .../playlist-api/internal/gen/main.go | 15 + .../playlist-api/migrations/001-down.sql | 10 + .../playlist-api/migrations/001-up.sql | 40 + .../playlist-api/scripts/bootstrap.sql | 11 + .../playlist-api/scripts/check-generation.py | 18 + applications/playlist-api/scripts/grants.sql | 4 + applications/playlist-api/scripts/migrate.sh | 11 + applications/playlist-api/scripts/seed.sql | 3 + applications/playlist-api/tests/acceptance.py | 384 ++ .../playlist-api/tests/requirements.in | 3 + .../playlist-api/tests/requirements.txt | 10 + .../playlist-api/tests/unicode_regression.py | 69 + applications/playlist-api/tools.go | 9 + 80 files changed, 17925 insertions(+) create mode 100644 .github/workflows/playlist-api.yml create mode 100644 applications/playlist-api/.env.example create mode 100644 applications/playlist-api/.gitignore create mode 100644 applications/playlist-api/README.md create mode 100644 applications/playlist-api/cmd/server/main.go create mode 100644 applications/playlist-api/ent/account.go create mode 100644 applications/playlist-api/ent/account/account.go create mode 100644 applications/playlist-api/ent/account/where.go create mode 100644 applications/playlist-api/ent/account_create.go create mode 100644 applications/playlist-api/ent/account_delete.go create mode 100644 applications/playlist-api/ent/account_query.go create mode 100644 applications/playlist-api/ent/account_update.go create mode 100644 applications/playlist-api/ent/client.go create mode 100644 applications/playlist-api/ent/ent.go create mode 100644 applications/playlist-api/ent/enttest/enttest.go create mode 100644 applications/playlist-api/ent/hook/hook.go create mode 100644 applications/playlist-api/ent/item.go create mode 100644 applications/playlist-api/ent/item/item.go create mode 100644 applications/playlist-api/ent/item/where.go create mode 100644 applications/playlist-api/ent/item_create.go create mode 100644 applications/playlist-api/ent/item_delete.go create mode 100644 applications/playlist-api/ent/item_query.go create mode 100644 applications/playlist-api/ent/item_update.go create mode 100644 applications/playlist-api/ent/migrate/migrate.go create mode 100644 applications/playlist-api/ent/migrate/schema.go create mode 100644 applications/playlist-api/ent/mutation.go create mode 100644 applications/playlist-api/ent/playlist.go create mode 100644 applications/playlist-api/ent/playlist/playlist.go create mode 100644 applications/playlist-api/ent/playlist/where.go create mode 100644 applications/playlist-api/ent/playlist_create.go create mode 100644 applications/playlist-api/ent/playlist_delete.go create mode 100644 applications/playlist-api/ent/playlist_query.go create mode 100644 applications/playlist-api/ent/playlist_update.go create mode 100644 applications/playlist-api/ent/predicate/predicate.go create mode 100644 applications/playlist-api/ent/runtime.go create mode 100644 applications/playlist-api/ent/runtime/runtime.go create mode 100644 applications/playlist-api/ent/schema/account.go create mode 100644 applications/playlist-api/ent/schema/item.go create mode 100644 applications/playlist-api/ent/schema/playlist.go create mode 100644 applications/playlist-api/ent/schema/track.go create mode 100644 applications/playlist-api/ent/track.go create mode 100644 applications/playlist-api/ent/track/track.go create mode 100644 applications/playlist-api/ent/track/where.go create mode 100644 applications/playlist-api/ent/track_create.go create mode 100644 applications/playlist-api/ent/track_delete.go create mode 100644 applications/playlist-api/ent/track_query.go create mode 100644 applications/playlist-api/ent/track_update.go create mode 100644 applications/playlist-api/ent/tx.go create mode 100644 applications/playlist-api/examples/create.json create mode 100644 applications/playlist-api/examples/list.json create mode 100644 applications/playlist-api/examples/reorder.json create mode 100644 applications/playlist-api/generate.go create mode 100644 applications/playlist-api/go.mod create mode 100644 applications/playlist-api/go.sum create mode 100644 applications/playlist-api/gqlgen.yml create mode 100644 applications/playlist-api/graph/generated.go create mode 100644 applications/playlist-api/graph/model/models_gen.go create mode 100644 applications/playlist-api/graph/resolver.go create mode 100644 applications/playlist-api/graph/schema.graphqls create mode 100644 applications/playlist-api/graph/schema.resolvers.go create mode 100644 applications/playlist-api/internal/app/cloud_test.go create mode 100644 applications/playlist-api/internal/app/database.go create mode 100644 applications/playlist-api/internal/app/domain.go create mode 100644 applications/playlist-api/internal/app/domain_test.go create mode 100644 applications/playlist-api/internal/app/middleware.go create mode 100644 applications/playlist-api/internal/app/policy.go create mode 100644 applications/playlist-api/internal/app/store.go create mode 100644 applications/playlist-api/internal/gen/main.go create mode 100644 applications/playlist-api/migrations/001-down.sql create mode 100644 applications/playlist-api/migrations/001-up.sql create mode 100644 applications/playlist-api/scripts/bootstrap.sql create mode 100644 applications/playlist-api/scripts/check-generation.py create mode 100644 applications/playlist-api/scripts/grants.sql create mode 100755 applications/playlist-api/scripts/migrate.sh create mode 100644 applications/playlist-api/scripts/seed.sql create mode 100644 applications/playlist-api/tests/acceptance.py create mode 100644 applications/playlist-api/tests/requirements.in create mode 100644 applications/playlist-api/tests/requirements.txt create mode 100644 applications/playlist-api/tests/unicode_regression.py create mode 100644 applications/playlist-api/tools.go diff --git a/.github/workflows/playlist-api.yml b/.github/workflows/playlist-api.yml new file mode 100644 index 00000000..8749bc94 --- /dev/null +++ b/.github/workflows/playlist-api.yml @@ -0,0 +1,25 @@ +name: Playlist API +on: + push: + paths: ['applications/playlist-api/**', '.github/workflows/playlist-api.yml'] + pull_request: + paths: ['applications/playlist-api/**', '.github/workflows/playlist-api.yml'] +permissions: + contents: read +jobs: + checks: + runs-on: ubuntu-24.04 + defaults: + run: + working-directory: applications/playlist-api + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version: '1.27.1' + cache-dependency-path: applications/playlist-api/go.sum + - run: go mod download && go mod verify + - run: test -z "$(gofmt -l cmd internal graph ent *.go)" + - run: go test -v -count=1 ./... + - run: python3 scripts/check-generation.py + - run: go build ./cmd/server diff --git a/applications/playlist-api/.env.example b/applications/playlist-api/.env.example new file mode 100644 index 00000000..fcbed6ec --- /dev/null +++ b/applications/playlist-api/.env.example @@ -0,0 +1,10 @@ +PGHOST=your-cloud-hostname +PGPORT=5432 +PGDATABASE=postgres +PGUSER=playlist_app +PGPASSWORD=replace-with-runtime-password +PGSSLROOTCERT=/absolute/path/to/cloud-ca.pem +ACCOUNT_A_TOKEN=replace-with-distinct-random-URL-safe-token-of-at-least-32-characters +ACCOUNT_B_TOKEN=replace-with-another-distinct-random-URL-safe-token-of-at-least-32-characters +# Optional private diagnostics for live acceptance, disabled by default. +QUERY_COUNT_EVIDENCE=0 diff --git a/applications/playlist-api/.gitignore b/applications/playlist-api/.gitignore new file mode 100644 index 00000000..bcd78476 --- /dev/null +++ b/applications/playlist-api/.gitignore @@ -0,0 +1,8 @@ +.local/ +.venv/ +__pycache__/ +.ruff_cache/ +.private/ +*.env +.env +*.pem diff --git a/applications/playlist-api/README.md b/applications/playlist-api/README.md new file mode 100644 index 00000000..88ca63a5 --- /dev/null +++ b/applications/playlist-api/README.md @@ -0,0 +1,130 @@ +# Playlist API with gqlgen and Ent + +A compact GraphQL API composes playlists over synthetic track metadata in ClickHouse Managed Postgres (public beta). There is no audio, upload, streaming, commercial catalogue or music-service integration. Two environment bearer tokens identify fixed server-side accounts. Create a named playlist, read its ordered metadata and reorder its existing tracks with the revision you observed. + +## Verified native stack + +Use native Linux with Go 1.27.1, Python 3/venv for checks, curl, and `psql`. This example was generated and compiled on Ubuntu 24.04 ARM64 **before** database allocation. Exact released pins: Go 1.27.1, gqlgen 0.17.95, Ent 0.14.6, ent/contrib 0.7.0 and pgx 5.11.0. `go.mod` and `go.sum` commit the dependency graph; generated Ent and gqlgen code is checked in. Some transitive generator modules use fixed upstream pseudo-versions; no floating `latest` appears in generation commands. See [official Go downloads](https://go.dev/dl/) for the correct native architecture archive and checksum. + +```bash +go mod download +go generate ./... +go test ./... +python3 scripts/check-generation.py +go build -o .local/playlist-api ./cmd/server +``` + +Regeneration is deterministic and checks Go/module source bytes before and after. Runtime never calls Ent `Schema.Create`. Owner-applied SQL is authoritative: the Ent position index expresses uniqueness, while the explicit SQL migration makes that uniqueness **DEFERRABLE INITIALLY DEFERRED**. Replacing this setup with automatic Ent migration would lose that ordering contract. Keep the Ent schemas and versioned SQL together. + +## Create a dedicated Cloud fixture + +Install/configure [clickhousectl](https://github.com/ClickHouse/clickhousectl) with your Cloud API credentials. Confirm current [supported regions/sizes](https://clickhouse.com/docs/products/managed-postgres/) and [pricing](https://clickhouse.com/pricing). The tested service used AWS us-east-1, c6gd.large, PostgreSQL 18 and no HA. Compute/storage are billable; stopping the API does not remove the database. + +```bash +mkdir -p "$HOME/playlist-private" +chmod 700 "$HOME/playlist-private" +clickhousectl cloud postgres create --name playlist-api-demo \ + --provider aws --region us-east-1 --size c6gd.large --pg-version 18 \ + --ha-type none --tag purpose=playlist-api --org-id YOUR_ORG_ID \ + --json > "$HOME/playlist-private/create.json" +chmod 600 "$HOME/playlist-private/create.json" +clickhousectl cloud postgres get YOUR_SERVICE_ID --org-id YOUR_ORG_ID --json +``` + +Read the ID from the private one-time receipt. Repeat `get` until running with a hostname, then retrieve the official CA: + +```bash +clickhousectl cloud postgres certs get YOUR_SERVICE_ID --org-id YOUR_ORG_ID \ + --output "$HOME/playlist-private/ca.pem" +chmod 600 "$HOME/playlist-private/ca.pem" +``` + +Create four private shell env files (mode 600), with receipt hostname, port, database and absolute `PGSSLROOTCERT` path, plus `PGSSLMODE=verify-full` for setup: + +* `admin.env`: receipt PGUSER/PGPASSWORD, plus new random PG_MIGRATION_PASSWORD and PG_APP_PASSWORD. +* `migration.env`: PGUSER=playlist_owner and its password. +* `runtime.env`: PGUSER=playlist_app and its password, ACCOUNT_A_TOKEN and ACCOUNT_B_TOKEN. +* `test.env`: TEST_OWNER_USER=playlist_owner and TEST_OWNER_PASSWORD, used only for opt-in live checks. + +Generate passwords/tokens privately with `python3 -c 'import secrets; print(secrets.token_urlsafe(32))'`. Tokens must be distinct URL-safe strings of 32–128 characters. They map to the two seeded account UUIDs; clients cannot supply an owner. They are long-lived bearer credentials loaded at startup, without expiry, per-user identity or stored revocation. Use explicit Authorization headers, never query-string tokens. This tutorial binds loopback; a real deployment requires HTTPS and appropriate token distribution. There are no browser cookies, CORS allowances, subscriptions, multipart uploads or global node/CRUD endpoints. + +## Owner setup and runtime + +Never run the bootstrap on a shared database: it changes PUBLIC database/schema creation privileges. In separate exported subshells: + +```bash +(set -a; source "$HOME/playlist-private/admin.env"; set +a + psql -X -v ON_ERROR_STOP=1 -f scripts/bootstrap.sql) +(set -a; source "$HOME/playlist-private/migration.env"; set +a + scripts/migrate.sh up + psql -X -v ON_ERROR_STOP=1 -f scripts/grants.sql + psql -X -v ON_ERROR_STOP=1 -f scripts/seed.sql) +``` + +`up` takes an advisory lock, verifies the stored checksum on repeat and refuses changed applied SQL. `down` destroys this app's tables/data; use only on your dedicated fixture. A fresh down/up cycle requires reapplying grants and seed. Seeding the two accounts and 60 synthetic tracks is repeat-safe. Runtime has SELECT, necessary playlist/item INSERT, playlist revision UPDATE and item position UPDATE. Tracks are SELECT-only; no app role DDL, DELETE or temporary tables. + +The shared database role is **trusted** across accounts. It can query either account directly and can insert/update item positions outside API revision checks. The parent complete-items trigger checks on playlist insertion/update, not every direct item edit. There is no RLS or universal immutable-membership guarantee against arbitrary direct SQL. The API exposes no membership editing after creation and scopes every parent read/write by authenticated owner. + +Open a new terminal that has not sourced setup credentials, then export only runtime env: + +```bash +set -a; source "$HOME/playlist-private/runtime.env"; set +a +.local/playlist-api +``` + +The pool has at most four connections. pgx's database/sql adapter uses the downloaded CA and full hostname verification, with no insecure or plaintext fallback. Connect/ping, SQL statement and lock deadlines are 10/10/8 seconds. HTTP headers/read/write have 5/10/30-second bounds; each response has 25 seconds, followed by up to 30 seconds for joined graceful shutdown before the Ent client closes. Requests are at most 16 KiB. SQL parameters, tokens and payloads are never logged by diagnostics. + +## Reproducible GraphQL workflow + +```bash +curl --fail-with-body http://127.0.0.1:8080/query \ + -H "Authorization: Bearer $ACCOUNT_A_TOKEN" -H 'Content-Type: application/json' \ + --data-binary @examples/create.json +curl --fail-with-body http://127.0.0.1:8080/query \ + -H "Authorization: Bearer $ACCOUNT_A_TOKEN" -H 'Content-Type: application/json' \ + --data-binary @examples/list.json +``` + +Put the returned playlist UUID and observed revision into examples/reorder.json, then submit it the same way. Positions are 1-based, each playlist has 1–50 distinct existing tracks, names have 1–80 characters, and each account has at most 100 playlists. Names reject controls and U+FFFD: Go JSON decoding replaces escaped lone surrogates with that rune, so rejecting it conservatively also rejects an intentionally supplied replacement character. Valid paired non-BMP Unicode remains supported. Create commits the parent and complete ordered membership together. Reorder takes an owner-scoped parent FOR UPDATE lock, compares the supplied revision, requires an exact membership permutation, rewrites positions and advances revision once. The deferrable position constraint permits swaps without an intermediate uniqueness failure. A stale revision returns a GraphQL STALE error; invalid membership returns INVALID. Creation is not request-idempotent: retrying a successful create can add another playlist. + +The pinned `entgql.Transactioner` opens a mutation transaction around response execution, serializes resolver middleware and commits after selections finish. Every resolver obtains `ent.FromContext`; there is no global-client fallback. Only **one expanded top-level mutation field occurrence** is allowed per operation, conservatively counting aliases, inline/named fragments and skipped branches. Multiple fields are rejected before opening a transaction, rather than promising batch atomicity. A deferred commit failure returns errors with no success data and rolls back all writes. Ordinary resolver domain failures use HTTP 200 with GraphQL errors; inspect the JSON body, not only curl's HTTP success. + +Queries use one read-only repeatable-read transaction for a coherent revision and order across eager SELECTs. Ent loads items ordered by position and their tracks together; DTO response fields never fall back to per-item database queries. Page limits are 1–10, ordered by UUID ascending with a validated opaque account-bound cursor. This is a live keyset listing, not a frozen snapshot; inserts below a previous cursor can be missed when continuing. Track metadata queries have a 50-row limit. A page plus its sentinel reads at most 11 playlists and 550 items. + +Operation policy permits depth at most 5, at most 100 expanded selections and at most 5 top-level query fields. Introspection is disabled. Custom saturated complexity multiplies validated playlist limits and the 50-item bound; excessive aliases/fan-out are rejected before expensive SQL. The total cost limit is 6000. Bad limits are rejected before querying. The schema has no recursive backreferences. + +## Real validation + +Basic CI runs generation/format/build and six database-free policy/boundary tests. Cloud checks are separate and never silently skipped. They require a running runtime server and exported runtime/test env. Enable `QUERY_COUNT_EVIDENCE=1` only for private diagnostic evidence and redirect server output to a portable log: + +```bash +export SERVER_LOG=/tmp/playlist-api-server.log +QUERY_COUNT_EVIDENCE=1 .local/playlist-api > "$SERVER_LOG" 2>&1 +``` + +Run helpers in a separate shell: + +```bash +python3 -m venv .venv +.venv/bin/pip install -r tests/requirements.txt +.venv/bin/pip check +(set -a; source "$HOME/playlist-private/runtime.env"; source "$HOME/playlist-private/test.env"; set +a + export SERVER_LOG=/tmp/playlist-api-server.log + .venv/bin/python tests/acceptance.py + .venv/bin/python tests/unicode_regression.py) +(set -a; source "$HOME/playlist-private/runtime.env"; set +a + go test -tags cloud -v -count=1 ./internal/app) +``` + +Live fixtures prove independent real lock contention, child-write and deferred commit rollback, account/nested scope, exact permutation/position constraints, bounded queries and trusted-role limits. The Cloud-tagged tests exercise actual pgx certificate/hostname failures and pause the first eager header SELECT while another HTTP reorder commits. Read-only repeatable-read keeps the old complete snapshot, and a fresh query sees the new order. Diagnostic counts measure Ent-issued statements, excluding BEGIN/COMMIT and trigger-internal SQL; they are not throughput benchmarks. + +## Cleanup + +Stop the Go process and wait for its exit. Delete only your exact service ID, with explicit organization: + +```bash +clickhousectl cloud postgres delete YOUR_SERVICE_ID --org-id YOUR_ORG_ID --json +clickhousectl cloud postgres list --org-id YOUR_ORG_ID --json +``` + +Deletion is asynchronous; verify that exact ID becomes absent. If retaining the database, restore administrator receipt PGUSER/PGPASSWORD before any schema/role removal, rather than reusing runtime credentials. Stop/preserve the VM after saving source and private evidence; do not delete it. diff --git a/applications/playlist-api/cmd/server/main.go b/applications/playlist-api/cmd/server/main.go new file mode 100644 index 00000000..6d2a2827 --- /dev/null +++ b/applications/playlist-api/cmd/server/main.go @@ -0,0 +1,138 @@ +package main + +import ( + "bytes" + "context" + "crypto/subtle" + "database/sql" + "database/sql/driver" + "entgo.io/contrib/entgql" + "errors" + "example.com/playlist-api/ent" + "example.com/playlist-api/graph" + "example.com/playlist-api/internal/app" + "github.com/99designs/gqlgen/graphql" + "github.com/99designs/gqlgen/graphql/handler" + "github.com/99designs/gqlgen/graphql/handler/extension" + "github.com/99designs/gqlgen/graphql/handler/transport" + "github.com/google/uuid" + "io" + "log" + "net/http" + "os" + "os/signal" + "regexp" + "strings" + "syscall" + "time" + "unicode/utf8" +) + +func main() { + if err := run(); err != nil { + log.Fatal("Playlist API startup failed.") + } +} +func run() error { + tokens := []string{os.Getenv("ACCOUNT_A_TOKEN"), os.Getenv("ACCOUNT_B_TOKEN")} + pattern := regexp.MustCompile(`^[A-Za-z0-9_-]{32,128}$`) + if !pattern.MatchString(tokens[0]) || !pattern.MatchString(tokens[1]) || tokens[0] == tokens[1] { + return errors.New("distinct tokens required") + } + owners := []uuid.UUID{uuid.MustParse("00000000-0000-4000-8000-000000000001"), uuid.MustParse("00000000-0000-4000-8000-000000000002")} + client, _, err := app.Open() + if err != nil { + return err + } + defer client.Close() + cfg := graph.Config{Resolvers: &graph.Resolver{}} + cfg.Complexity.Query.Playlists = func(child, limit int, cursor *string) int { + if limit < 1 || limit > 10 { + return app.CostLimit + 1 + } + return app.Cost(child, limit) + } + cfg.Complexity.Query.Tracks = func(child, limit int) int { return app.Cost(child, limit) } + cfg.Complexity.Playlist.Items = func(child int) int { return app.Cost(child, 50) } + server := handler.New(graph.NewExecutableSchema(cfg)) + server.AddTransport(transport.POST{}) + server.SetErrorPresenter(app.PresentError) + server.SetRecoverFunc(func(context.Context, any) error { return errors.New("request failed") }) + server.Use(app.Policy{}) + server.Use(extension.FixedComplexityLimit(app.CostLimit)) + server.AroundResponses(func(ctx context.Context, next graphql.ResponseHandler) *graphql.Response { + response := app.Sanitize(ctx, next) + if os.Getenv("QUERY_COUNT_EVIDENCE") == "1" && response != nil { + response.Extensions = map[string]any{"sqlStatements": app.SQLCount(ctx)} + } + return response + }) + server.Use(app.QuerySnapshot{Client: client}) + server.Use(entgql.Transactioner{TxOpener: entgql.TxOpenerFunc(func(ctx context.Context) (context.Context, driver.Tx, error) { + tx, err := client.BeginTx(ctx, &sql.TxOptions{Isolation: sql.LevelReadCommitted}) + if err != nil { + return nil, nil, errors.New("Database unavailable.") + } + ctx = ent.NewTxContext(ctx, tx) + ctx = ent.NewContext(ctx, tx.Client()) + return ctx, tx, nil + })}) + api := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.URL.Path != "/query" { + http.NotFound(w, r) + return + } + if r.Method != "POST" { + w.WriteHeader(405) + return + } + headers := r.Header.Values("Authorization") + owner := uuid.Nil + if len(headers) == 1 && strings.HasPrefix(headers[0], "Bearer ") { + value := strings.TrimPrefix(headers[0], "Bearer ") + if len(value) <= 128 { + for i, token := range tokens { + if subtle.ConstantTimeCompare([]byte(value), []byte(token)) == 1 { + owner = owners[i] + } + } + } + } + if owner == uuid.Nil { + w.WriteHeader(401) + return + } + r.Body = http.MaxBytesReader(w, r.Body, 16384) + data, err := io.ReadAll(r.Body) + if err != nil { + w.WriteHeader(413) + return + } + if !utf8.Valid(data) { + w.WriteHeader(400) + return + } + r.Body = io.NopCloser(bytes.NewReader(data)) + ctx := app.WithOwner(r.Context(), owner) + ctx, count := app.WithCounter(ctx) + if os.Getenv("QUERY_COUNT_EVIDENCE") == "1" { + defer func() { log.Printf("Observed Ent SQL statements=%d", count.Load()) }() + } + server.ServeHTTP(w, r.WithContext(ctx)) + }) + httpServer := &http.Server{Addr: "127.0.0.1:8080", Handler: http.TimeoutHandler(api, 25*time.Second, "Request timed out."), ReadHeaderTimeout: 5 * time.Second, ReadTimeout: 10 * time.Second, WriteTimeout: 30 * time.Second, IdleTimeout: 30 * time.Second, MaxHeaderBytes: 8192} + stop := make(chan os.Signal, 1) + shutdownDone := make(chan error, 1) + signal.Notify(stop, syscall.SIGINT, syscall.SIGTERM) + go func() { + <-stop + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + shutdownDone <- httpServer.Shutdown(ctx) + }() + err = httpServer.ListenAndServe() + if errors.Is(err, http.ErrServerClosed) { + return <-shutdownDone + } + return err +} diff --git a/applications/playlist-api/ent/account.go b/applications/playlist-api/ent/account.go new file mode 100644 index 00000000..9f602d48 --- /dev/null +++ b/applications/playlist-api/ent/account.go @@ -0,0 +1,118 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "fmt" + "strings" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "example.com/playlist-api/ent/account" + "github.com/google/uuid" +) + +// Account is the model entity for the Account schema. +type Account struct { + config + // ID of the ent. + ID uuid.UUID `json:"id,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the AccountQuery when eager-loading is set. + Edges AccountEdges `json:"edges"` + selectValues sql.SelectValues +} + +// AccountEdges holds the relations/edges for other nodes in the graph. +type AccountEdges struct { + // Playlists holds the value of the playlists edge. + Playlists []*Playlist `json:"playlists,omitempty"` + // loadedTypes holds the information for reporting if a + // type was loaded (or requested) in eager-loading or not. + loadedTypes [1]bool +} + +// PlaylistsOrErr returns the Playlists value or an error if the edge +// was not loaded in eager-loading. +func (e AccountEdges) PlaylistsOrErr() ([]*Playlist, error) { + if e.loadedTypes[0] { + return e.Playlists, nil + } + return nil, &NotLoadedError{edge: "playlists"} +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*Account) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case account.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 Account fields. +func (_m *Account) 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 account.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 + } + default: + _m.selectValues.Set(columns[i], values[i]) + } + } + return nil +} + +// Value returns the ent.Value that was dynamically selected and assigned to the Account. +// This includes values selected through modifiers, order, etc. +func (_m *Account) Value(name string) (ent.Value, error) { + return _m.selectValues.Get(name) +} + +// QueryPlaylists queries the "playlists" edge of the Account entity. +func (_m *Account) QueryPlaylists() *PlaylistQuery { + return NewAccountClient(_m.config).QueryPlaylists(_m) +} + +// Update returns a builder for updating this Account. +// Note that you need to call Account.Unwrap() before calling this method if this Account +// was returned from a transaction, and the transaction was committed or rolled back. +func (_m *Account) Update() *AccountUpdateOne { + return NewAccountClient(_m.config).UpdateOne(_m) +} + +// Unwrap unwraps the Account 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 *Account) Unwrap() *Account { + _tx, ok := _m.config.driver.(*txDriver) + if !ok { + panic("ent: Account is not a transactional entity") + } + _m.config.driver = _tx.drv + return _m +} + +// String implements the fmt.Stringer. +func (_m *Account) String() string { + var builder strings.Builder + builder.WriteString("Account(") + builder.WriteString(fmt.Sprintf("id=%v", _m.ID)) + builder.WriteByte(')') + return builder.String() +} + +// Accounts is a parsable slice of Account. +type Accounts []*Account diff --git a/applications/playlist-api/ent/account/account.go b/applications/playlist-api/ent/account/account.go new file mode 100644 index 00000000..5d4a5a1f --- /dev/null +++ b/applications/playlist-api/ent/account/account.go @@ -0,0 +1,70 @@ +// Code generated by ent, DO NOT EDIT. + +package account + +import ( + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" +) + +const ( + // Label holds the string label denoting the account type in the database. + Label = "account" + // FieldID holds the string denoting the id field in the database. + FieldID = "id" + // EdgePlaylists holds the string denoting the playlists edge name in mutations. + EdgePlaylists = "playlists" + // Table holds the table name of the account in the database. + Table = "accounts" + // PlaylistsTable is the table that holds the playlists relation/edge. + PlaylistsTable = "playlists" + // PlaylistsInverseTable is the table name for the Playlist entity. + // It exists in this package in order to avoid circular dependency with the "playlist" package. + PlaylistsInverseTable = "playlists" + // PlaylistsColumn is the table column denoting the playlists relation/edge. + PlaylistsColumn = "owner_id" +) + +// Columns holds all SQL columns for account fields. +var Columns = []string{ + FieldID, +} + +// 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 +} + +// OrderOption defines the ordering options for the Account 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() +} + +// ByPlaylistsCount orders the results by playlists count. +func ByPlaylistsCount(opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborsCount(s, newPlaylistsStep(), opts...) + } +} + +// ByPlaylists orders the results by playlists terms. +func ByPlaylists(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newPlaylistsStep(), append([]sql.OrderTerm{term}, terms...)...) + } +} +func newPlaylistsStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(PlaylistsInverseTable, FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, PlaylistsTable, PlaylistsColumn), + ) +} diff --git a/applications/playlist-api/ent/account/where.go b/applications/playlist-api/ent/account/where.go new file mode 100644 index 00000000..9a8aeef8 --- /dev/null +++ b/applications/playlist-api/ent/account/where.go @@ -0,0 +1,93 @@ +// Code generated by ent, DO NOT EDIT. + +package account + +import ( + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "example.com/playlist-api/ent/predicate" + "github.com/google/uuid" +) + +// ID filters vertices based on their ID field. +func ID(id uuid.UUID) predicate.Account { + return predicate.Account(sql.FieldEQ(FieldID, id)) +} + +// IDEQ applies the EQ predicate on the ID field. +func IDEQ(id uuid.UUID) predicate.Account { + return predicate.Account(sql.FieldEQ(FieldID, id)) +} + +// IDNEQ applies the NEQ predicate on the ID field. +func IDNEQ(id uuid.UUID) predicate.Account { + return predicate.Account(sql.FieldNEQ(FieldID, id)) +} + +// IDIn applies the In predicate on the ID field. +func IDIn(ids ...uuid.UUID) predicate.Account { + return predicate.Account(sql.FieldIn(FieldID, ids...)) +} + +// IDNotIn applies the NotIn predicate on the ID field. +func IDNotIn(ids ...uuid.UUID) predicate.Account { + return predicate.Account(sql.FieldNotIn(FieldID, ids...)) +} + +// IDGT applies the GT predicate on the ID field. +func IDGT(id uuid.UUID) predicate.Account { + return predicate.Account(sql.FieldGT(FieldID, id)) +} + +// IDGTE applies the GTE predicate on the ID field. +func IDGTE(id uuid.UUID) predicate.Account { + return predicate.Account(sql.FieldGTE(FieldID, id)) +} + +// IDLT applies the LT predicate on the ID field. +func IDLT(id uuid.UUID) predicate.Account { + return predicate.Account(sql.FieldLT(FieldID, id)) +} + +// IDLTE applies the LTE predicate on the ID field. +func IDLTE(id uuid.UUID) predicate.Account { + return predicate.Account(sql.FieldLTE(FieldID, id)) +} + +// HasPlaylists applies the HasEdge predicate on the "playlists" edge. +func HasPlaylists() predicate.Account { + return predicate.Account(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, PlaylistsTable, PlaylistsColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasPlaylistsWith applies the HasEdge predicate on the "playlists" edge with a given conditions (other predicates). +func HasPlaylistsWith(preds ...predicate.Playlist) predicate.Account { + return predicate.Account(func(s *sql.Selector) { + step := newPlaylistsStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + +// And groups predicates with the AND operator between them. +func And(predicates ...predicate.Account) predicate.Account { + return predicate.Account(sql.AndPredicates(predicates...)) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.Account) predicate.Account { + return predicate.Account(sql.OrPredicates(predicates...)) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.Account) predicate.Account { + return predicate.Account(sql.NotPredicates(p)) +} diff --git a/applications/playlist-api/ent/account_create.go b/applications/playlist-api/ent/account_create.go new file mode 100644 index 00000000..e09a65bf --- /dev/null +++ b/applications/playlist-api/ent/account_create.go @@ -0,0 +1,213 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "fmt" + + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "example.com/playlist-api/ent/account" + "example.com/playlist-api/ent/playlist" + "github.com/google/uuid" +) + +// AccountCreate is the builder for creating a Account entity. +type AccountCreate struct { + config + mutation *AccountMutation + hooks []Hook +} + +// SetID sets the "id" field. +func (_c *AccountCreate) SetID(v uuid.UUID) *AccountCreate { + _c.mutation.SetID(v) + return _c +} + +// AddPlaylistIDs adds the "playlists" edge to the Playlist entity by IDs. +func (_c *AccountCreate) AddPlaylistIDs(ids ...uuid.UUID) *AccountCreate { + _c.mutation.AddPlaylistIDs(ids...) + return _c +} + +// AddPlaylists adds the "playlists" edges to the Playlist entity. +func (_c *AccountCreate) AddPlaylists(v ...*Playlist) *AccountCreate { + ids := make([]uuid.UUID, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _c.AddPlaylistIDs(ids...) +} + +// Mutation returns the AccountMutation object of the builder. +func (_c *AccountCreate) Mutation() *AccountMutation { + return _c.mutation +} + +// Save creates the Account in the database. +func (_c *AccountCreate) Save(ctx context.Context) (*Account, error) { + return withHooks(ctx, _c.sqlSave, _c.mutation, _c.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (_c *AccountCreate) SaveX(ctx context.Context) *Account { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *AccountCreate) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *AccountCreate) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_c *AccountCreate) check() error { + return nil +} + +func (_c *AccountCreate) sqlSave(ctx context.Context) (*Account, 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 *AccountCreate) createSpec() (*Account, *sqlgraph.CreateSpec) { + var ( + _node = &Account{config: _c.config} + _spec = sqlgraph.NewCreateSpec(account.Table, sqlgraph.NewFieldSpec(account.FieldID, field.TypeUUID)) + ) + if id, ok := _c.mutation.ID(); ok { + _node.ID = id + _spec.ID.Value = &id + } + if nodes := _c.mutation.PlaylistsIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: account.PlaylistsTable, + Columns: []string{account.PlaylistsColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(playlist.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges = append(_spec.Edges, edge) + } + return _node, _spec +} + +// AccountCreateBulk is the builder for creating many Account entities in bulk. +type AccountCreateBulk struct { + config + err error + builders []*AccountCreate +} + +// Save creates the Account entities in the database. +func (_c *AccountCreateBulk) Save(ctx context.Context) ([]*Account, error) { + if _c.err != nil { + return nil, _c.err + } + specs := make([]*sqlgraph.CreateSpec, len(_c.builders)) + nodes := make([]*Account, len(_c.builders)) + mutators := make([]Mutator, len(_c.builders)) + for i := range _c.builders { + func(i int, root context.Context) { + builder := _c.builders[i] + var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { + mutation, ok := m.(*AccountMutation) + 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} + } + } + } + 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 *AccountCreateBulk) SaveX(ctx context.Context) []*Account { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *AccountCreateBulk) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *AccountCreateBulk) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/applications/playlist-api/ent/account_delete.go b/applications/playlist-api/ent/account_delete.go new file mode 100644 index 00000000..708d1ab9 --- /dev/null +++ b/applications/playlist-api/ent/account_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" + "example.com/playlist-api/ent/account" + "example.com/playlist-api/ent/predicate" +) + +// AccountDelete is the builder for deleting a Account entity. +type AccountDelete struct { + config + hooks []Hook + mutation *AccountMutation +} + +// Where appends a list predicates to the AccountDelete builder. +func (_d *AccountDelete) Where(ps ...predicate.Account) *AccountDelete { + _d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (_d *AccountDelete) 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 *AccountDelete) ExecX(ctx context.Context) int { + n, err := _d.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (_d *AccountDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(account.Table, sqlgraph.NewFieldSpec(account.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 +} + +// AccountDeleteOne is the builder for deleting a single Account entity. +type AccountDeleteOne struct { + _d *AccountDelete +} + +// Where appends a list predicates to the AccountDelete builder. +func (_d *AccountDeleteOne) Where(ps ...predicate.Account) *AccountDeleteOne { + _d._d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query. +func (_d *AccountDeleteOne) Exec(ctx context.Context) error { + n, err := _d._d.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{account.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *AccountDeleteOne) ExecX(ctx context.Context) { + if err := _d.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/applications/playlist-api/ent/account_query.go b/applications/playlist-api/ent/account_query.go new file mode 100644 index 00000000..4f335673 --- /dev/null +++ b/applications/playlist-api/ent/account_query.go @@ -0,0 +1,622 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "database/sql/driver" + "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" + "example.com/playlist-api/ent/account" + "example.com/playlist-api/ent/playlist" + "example.com/playlist-api/ent/predicate" + "github.com/google/uuid" +) + +// AccountQuery is the builder for querying Account entities. +type AccountQuery struct { + config + ctx *QueryContext + order []account.OrderOption + inters []Interceptor + predicates []predicate.Account + withPlaylists *PlaylistQuery + 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 AccountQuery builder. +func (_q *AccountQuery) Where(ps ...predicate.Account) *AccountQuery { + _q.predicates = append(_q.predicates, ps...) + return _q +} + +// Limit the number of records to be returned by this query. +func (_q *AccountQuery) Limit(limit int) *AccountQuery { + _q.ctx.Limit = &limit + return _q +} + +// Offset to start from. +func (_q *AccountQuery) Offset(offset int) *AccountQuery { + _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 *AccountQuery) Unique(unique bool) *AccountQuery { + _q.ctx.Unique = &unique + return _q +} + +// Order specifies how the records should be ordered. +func (_q *AccountQuery) Order(o ...account.OrderOption) *AccountQuery { + _q.order = append(_q.order, o...) + return _q +} + +// QueryPlaylists chains the current query on the "playlists" edge. +func (_q *AccountQuery) QueryPlaylists() *PlaylistQuery { + query := (&PlaylistClient{config: _q.config}).Query() + query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + selector := _q.sqlQuery(ctx) + if err := selector.Err(); err != nil { + return nil, err + } + step := sqlgraph.NewStep( + sqlgraph.From(account.Table, account.FieldID, selector), + sqlgraph.To(playlist.Table, playlist.FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, account.PlaylistsTable, account.PlaylistsColumn), + ) + fromU = sqlgraph.SetNeighbors(_q.driver.Dialect(), step) + return fromU, nil + } + return query +} + +// First returns the first Account entity from the query. +// Returns a *NotFoundError when no Account was found. +func (_q *AccountQuery) First(ctx context.Context) (*Account, 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{account.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (_q *AccountQuery) FirstX(ctx context.Context) *Account { + node, err := _q.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// FirstID returns the first Account ID from the query. +// Returns a *NotFoundError when no Account ID was found. +func (_q *AccountQuery) 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{account.Label} + return + } + return ids[0], nil +} + +// FirstIDX is like FirstID, but panics if an error occurs. +func (_q *AccountQuery) FirstIDX(ctx context.Context) uuid.UUID { + id, err := _q.FirstID(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return id +} + +// Only returns a single Account entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one Account entity is found. +// Returns a *NotFoundError when no Account entities are found. +func (_q *AccountQuery) Only(ctx context.Context) (*Account, 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{account.Label} + default: + return nil, &NotSingularError{account.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (_q *AccountQuery) OnlyX(ctx context.Context) *Account { + node, err := _q.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// OnlyID is like Only, but returns the only Account ID in the query. +// Returns a *NotSingularError when more than one Account ID is found. +// Returns a *NotFoundError when no entities are found. +func (_q *AccountQuery) 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{account.Label} + default: + err = &NotSingularError{account.Label} + } + return +} + +// OnlyIDX is like OnlyID, but panics if an error occurs. +func (_q *AccountQuery) 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 Accounts. +func (_q *AccountQuery) All(ctx context.Context) ([]*Account, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryAll) + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*Account, *AccountQuery]() + return withInterceptors[[]*Account](ctx, _q, qr, _q.inters) +} + +// AllX is like All, but panics if an error occurs. +func (_q *AccountQuery) AllX(ctx context.Context) []*Account { + nodes, err := _q.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// IDs executes the query and returns a list of Account IDs. +func (_q *AccountQuery) 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(account.FieldID).Scan(ctx, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// IDsX is like IDs, but panics if an error occurs. +func (_q *AccountQuery) 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 *AccountQuery) 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[*AccountQuery](), _q.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (_q *AccountQuery) 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 *AccountQuery) 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 *AccountQuery) ExistX(ctx context.Context) bool { + exist, err := _q.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the AccountQuery 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 *AccountQuery) Clone() *AccountQuery { + if _q == nil { + return nil + } + return &AccountQuery{ + config: _q.config, + ctx: _q.ctx.Clone(), + order: append([]account.OrderOption{}, _q.order...), + inters: append([]Interceptor{}, _q.inters...), + predicates: append([]predicate.Account{}, _q.predicates...), + withPlaylists: _q.withPlaylists.Clone(), + // clone intermediate query. + sql: _q.sql.Clone(), + path: _q.path, + } +} + +// WithPlaylists tells the query-builder to eager-load the nodes that are connected to +// the "playlists" edge. The optional arguments are used to configure the query builder of the edge. +func (_q *AccountQuery) WithPlaylists(opts ...func(*PlaylistQuery)) *AccountQuery { + query := (&PlaylistClient{config: _q.config}).Query() + for _, opt := range opts { + opt(query) + } + _q.withPlaylists = query + return _q +} + +// 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. +func (_q *AccountQuery) GroupBy(field string, fields ...string) *AccountGroupBy { + _q.ctx.Fields = append([]string{field}, fields...) + grbuild := &AccountGroupBy{build: _q} + grbuild.flds = &_q.ctx.Fields + grbuild.label = account.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. +func (_q *AccountQuery) Select(fields ...string) *AccountSelect { + _q.ctx.Fields = append(_q.ctx.Fields, fields...) + sbuild := &AccountSelect{AccountQuery: _q} + sbuild.label = account.Label + sbuild.flds, sbuild.scan = &_q.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a AccountSelect configured with the given aggregations. +func (_q *AccountQuery) Aggregate(fns ...AggregateFunc) *AccountSelect { + return _q.Select().Aggregate(fns...) +} + +func (_q *AccountQuery) 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 !account.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 *AccountQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*Account, error) { + var ( + nodes = []*Account{} + _spec = _q.querySpec() + loadedTypes = [1]bool{ + _q.withPlaylists != nil, + } + ) + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*Account).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &Account{config: _q.config} + nodes = append(nodes, node) + 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) + } + if err := sqlgraph.QueryNodes(ctx, _q.driver, _spec); err != nil { + return nil, err + } + if len(nodes) == 0 { + return nodes, nil + } + if query := _q.withPlaylists; query != nil { + if err := _q.loadPlaylists(ctx, query, nodes, + func(n *Account) { n.Edges.Playlists = []*Playlist{} }, + func(n *Account, e *Playlist) { n.Edges.Playlists = append(n.Edges.Playlists, e) }); err != nil { + return nil, err + } + } + return nodes, nil +} + +func (_q *AccountQuery) loadPlaylists(ctx context.Context, query *PlaylistQuery, nodes []*Account, init func(*Account), assign func(*Account, *Playlist)) error { + fks := make([]driver.Value, 0, len(nodes)) + nodeids := make(map[uuid.UUID]*Account) + for i := range nodes { + fks = append(fks, nodes[i].ID) + nodeids[nodes[i].ID] = nodes[i] + if init != nil { + init(nodes[i]) + } + } + if len(query.ctx.Fields) > 0 { + query.ctx.AppendFieldOnce(playlist.FieldOwnerID) + } + query.Where(predicate.Playlist(func(s *sql.Selector) { + s.Where(sql.InValues(s.C(account.PlaylistsColumn), fks...)) + })) + neighbors, err := query.All(ctx) + if err != nil { + return err + } + for _, n := range neighbors { + fk := n.OwnerID + node, ok := nodeids[fk] + if !ok { + return fmt.Errorf(`unexpected referenced foreign-key "owner_id" returned %v for node %v`, fk, n.ID) + } + assign(node, n) + } + return nil +} + +func (_q *AccountQuery) 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 *AccountQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(account.Table, account.Columns, sqlgraph.NewFieldSpec(account.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, account.FieldID) + for i := range fields { + if fields[i] != account.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 *AccountQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(_q.driver.Dialect()) + t1 := builder.Table(account.Table) + columns := _q.ctx.Fields + if len(columns) == 0 { + columns = account.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 *AccountQuery) ForUpdate(opts ...sql.LockOption) *AccountQuery { + 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 *AccountQuery) ForShare(opts ...sql.LockOption) *AccountQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForShare(opts...) + }) + return _q +} + +// AccountGroupBy is the group-by builder for Account entities. +type AccountGroupBy struct { + selector + build *AccountQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (_g *AccountGroupBy) Aggregate(fns ...AggregateFunc) *AccountGroupBy { + _g.fns = append(_g.fns, fns...) + return _g +} + +// Scan applies the selector query and scans the result into the given value. +func (_g *AccountGroupBy) 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[*AccountQuery, *AccountGroupBy](ctx, _g.build, _g, _g.build.inters, v) +} + +func (_g *AccountGroupBy) sqlScan(ctx context.Context, root *AccountQuery, 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) +} + +// AccountSelect is the builder for selecting fields of Account entities. +type AccountSelect struct { + *AccountQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (_s *AccountSelect) Aggregate(fns ...AggregateFunc) *AccountSelect { + _s.fns = append(_s.fns, fns...) + return _s +} + +// Scan applies the selector query and scans the result into the given value. +func (_s *AccountSelect) 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[*AccountQuery, *AccountSelect](ctx, _s.AccountQuery, _s, _s.inters, v) +} + +func (_s *AccountSelect) sqlScan(ctx context.Context, root *AccountQuery, 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/applications/playlist-api/ent/account_update.go b/applications/playlist-api/ent/account_update.go new file mode 100644 index 00000000..b647548a --- /dev/null +++ b/applications/playlist-api/ent/account_update.go @@ -0,0 +1,339 @@ +// 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" + "example.com/playlist-api/ent/account" + "example.com/playlist-api/ent/playlist" + "example.com/playlist-api/ent/predicate" + "github.com/google/uuid" +) + +// AccountUpdate is the builder for updating Account entities. +type AccountUpdate struct { + config + hooks []Hook + mutation *AccountMutation +} + +// Where appends a list predicates to the AccountUpdate builder. +func (_u *AccountUpdate) Where(ps ...predicate.Account) *AccountUpdate { + _u.mutation.Where(ps...) + return _u +} + +// AddPlaylistIDs adds the "playlists" edge to the Playlist entity by IDs. +func (_u *AccountUpdate) AddPlaylistIDs(ids ...uuid.UUID) *AccountUpdate { + _u.mutation.AddPlaylistIDs(ids...) + return _u +} + +// AddPlaylists adds the "playlists" edges to the Playlist entity. +func (_u *AccountUpdate) AddPlaylists(v ...*Playlist) *AccountUpdate { + ids := make([]uuid.UUID, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.AddPlaylistIDs(ids...) +} + +// Mutation returns the AccountMutation object of the builder. +func (_u *AccountUpdate) Mutation() *AccountMutation { + return _u.mutation +} + +// ClearPlaylists clears all "playlists" edges to the Playlist entity. +func (_u *AccountUpdate) ClearPlaylists() *AccountUpdate { + _u.mutation.ClearPlaylists() + return _u +} + +// RemovePlaylistIDs removes the "playlists" edge to Playlist entities by IDs. +func (_u *AccountUpdate) RemovePlaylistIDs(ids ...uuid.UUID) *AccountUpdate { + _u.mutation.RemovePlaylistIDs(ids...) + return _u +} + +// RemovePlaylists removes "playlists" edges to Playlist entities. +func (_u *AccountUpdate) RemovePlaylists(v ...*Playlist) *AccountUpdate { + ids := make([]uuid.UUID, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.RemovePlaylistIDs(ids...) +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (_u *AccountUpdate) 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 *AccountUpdate) SaveX(ctx context.Context) int { + affected, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (_u *AccountUpdate) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *AccountUpdate) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +func (_u *AccountUpdate) sqlSave(ctx context.Context) (_node int, err error) { + _spec := sqlgraph.NewUpdateSpec(account.Table, account.Columns, sqlgraph.NewFieldSpec(account.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 _u.mutation.PlaylistsCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: account.PlaylistsTable, + Columns: []string{account.PlaylistsColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(playlist.FieldID, field.TypeUUID), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := _u.mutation.RemovedPlaylistsIDs(); len(nodes) > 0 && !_u.mutation.PlaylistsCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: account.PlaylistsTable, + Columns: []string{account.PlaylistsColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(playlist.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := _u.mutation.PlaylistsIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: account.PlaylistsTable, + Columns: []string{account.PlaylistsColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(playlist.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{account.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + _u.mutation.done = true + return _node, nil +} + +// AccountUpdateOne is the builder for updating a single Account entity. +type AccountUpdateOne struct { + config + fields []string + hooks []Hook + mutation *AccountMutation +} + +// AddPlaylistIDs adds the "playlists" edge to the Playlist entity by IDs. +func (_u *AccountUpdateOne) AddPlaylistIDs(ids ...uuid.UUID) *AccountUpdateOne { + _u.mutation.AddPlaylistIDs(ids...) + return _u +} + +// AddPlaylists adds the "playlists" edges to the Playlist entity. +func (_u *AccountUpdateOne) AddPlaylists(v ...*Playlist) *AccountUpdateOne { + ids := make([]uuid.UUID, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.AddPlaylistIDs(ids...) +} + +// Mutation returns the AccountMutation object of the builder. +func (_u *AccountUpdateOne) Mutation() *AccountMutation { + return _u.mutation +} + +// ClearPlaylists clears all "playlists" edges to the Playlist entity. +func (_u *AccountUpdateOne) ClearPlaylists() *AccountUpdateOne { + _u.mutation.ClearPlaylists() + return _u +} + +// RemovePlaylistIDs removes the "playlists" edge to Playlist entities by IDs. +func (_u *AccountUpdateOne) RemovePlaylistIDs(ids ...uuid.UUID) *AccountUpdateOne { + _u.mutation.RemovePlaylistIDs(ids...) + return _u +} + +// RemovePlaylists removes "playlists" edges to Playlist entities. +func (_u *AccountUpdateOne) RemovePlaylists(v ...*Playlist) *AccountUpdateOne { + ids := make([]uuid.UUID, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.RemovePlaylistIDs(ids...) +} + +// Where appends a list predicates to the AccountUpdate builder. +func (_u *AccountUpdateOne) Where(ps ...predicate.Account) *AccountUpdateOne { + _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 *AccountUpdateOne) Select(field string, fields ...string) *AccountUpdateOne { + _u.fields = append([]string{field}, fields...) + return _u +} + +// Save executes the query and returns the updated Account entity. +func (_u *AccountUpdateOne) Save(ctx context.Context) (*Account, error) { + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *AccountUpdateOne) SaveX(ctx context.Context) *Account { + node, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (_u *AccountUpdateOne) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *AccountUpdateOne) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +func (_u *AccountUpdateOne) sqlSave(ctx context.Context) (_node *Account, err error) { + _spec := sqlgraph.NewUpdateSpec(account.Table, account.Columns, sqlgraph.NewFieldSpec(account.FieldID, field.TypeUUID)) + id, ok := _u.mutation.ID() + if !ok { + return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "Account.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, account.FieldID) + for _, f := range fields { + if !account.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + if f != account.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 _u.mutation.PlaylistsCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: account.PlaylistsTable, + Columns: []string{account.PlaylistsColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(playlist.FieldID, field.TypeUUID), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := _u.mutation.RemovedPlaylistsIDs(); len(nodes) > 0 && !_u.mutation.PlaylistsCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: account.PlaylistsTable, + Columns: []string{account.PlaylistsColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(playlist.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := _u.mutation.PlaylistsIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: account.PlaylistsTable, + Columns: []string{account.PlaylistsColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(playlist.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + _node = &Account{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{account.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/applications/playlist-api/ent/client.go b/applications/playlist-api/ent/client.go new file mode 100644 index 00000000..b4adb99c --- /dev/null +++ b/applications/playlist-api/ent/client.go @@ -0,0 +1,867 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "log" + "reflect" + + "example.com/playlist-api/ent/migrate" + "github.com/google/uuid" + + "entgo.io/ent" + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "example.com/playlist-api/ent/account" + "example.com/playlist-api/ent/item" + "example.com/playlist-api/ent/playlist" + "example.com/playlist-api/ent/track" +) + +// Client is the client that holds all ent builders. +type Client struct { + config + // Schema is the client for creating, migrating and dropping schema. + Schema *migrate.Schema + // Account is the client for interacting with the Account builders. + Account *AccountClient + // Item is the client for interacting with the Item builders. + Item *ItemClient + // Playlist is the client for interacting with the Playlist builders. + Playlist *PlaylistClient + // Track is the client for interacting with the Track builders. + Track *TrackClient +} + +// NewClient creates a new client configured with the given options. +func NewClient(opts ...Option) *Client { + client := &Client{config: newConfig(opts...)} + client.init() + return client +} + +func (c *Client) init() { + c.Schema = migrate.NewSchema(c.driver) + c.Account = NewAccountClient(c.config) + c.Item = NewItemClient(c.config) + c.Playlist = NewPlaylistClient(c.config) + c.Track = NewTrackClient(c.config) +} + +type ( + // config is the configuration for the client and its builder. + config struct { + // driver used for executing database requests. + driver dialect.Driver + // debug enable a debug logging. + debug bool + // log used for logging on debug mode. + log func(...any) + // hooks to execute on mutations. + hooks *hooks + // interceptors to execute on queries. + inters *inters + } + // Option function to configure the client. + Option func(*config) +) + +// newConfig creates a new config for the client. +func newConfig(opts ...Option) config { + cfg := config{log: log.Println, hooks: &hooks{}, inters: &inters{}} + cfg.options(opts...) + return cfg +} + +// options applies the options on the config object. +func (c *config) options(opts ...Option) { + for _, opt := range opts { + opt(c) + } + if c.debug { + c.driver = dialect.Debug(c.driver, c.log) + } +} + +// Debug enables debug logging on the ent.Driver. +func Debug() Option { + return func(c *config) { + c.debug = true + } +} + +// Log sets the logging function for debug mode. +func Log(fn func(...any)) Option { + return func(c *config) { + c.log = fn + } +} + +// Driver configures the client driver. +func Driver(driver dialect.Driver) Option { + return func(c *config) { + c.driver = driver + } +} + +// Open opens a database/sql.DB specified by the driver name and +// the data source name, and returns a new client attached to it. +// Optional parameters can be added for configuring the client. +func Open(driverName, dataSourceName string, options ...Option) (*Client, error) { + switch driverName { + case dialect.MySQL, dialect.Postgres, dialect.SQLite: + drv, err := sql.Open(driverName, dataSourceName) + if err != nil { + return nil, err + } + return NewClient(append(options, Driver(drv))...), nil + default: + return nil, fmt.Errorf("unsupported driver: %q", driverName) + } +} + +// ErrTxStarted is returned when trying to start a new transaction from a transactional client. +var ErrTxStarted = errors.New("ent: cannot start a transaction within a transaction") + +// Tx returns a new transactional client. The provided context +// is used until the transaction is committed or rolled back. +func (c *Client) Tx(ctx context.Context) (*Tx, error) { + if _, ok := c.driver.(*txDriver); ok { + return nil, ErrTxStarted + } + tx, err := newTx(ctx, c.driver) + if err != nil { + return nil, fmt.Errorf("ent: starting a transaction: %w", err) + } + cfg := c.config + cfg.driver = tx + return &Tx{ + ctx: ctx, + config: cfg, + Account: NewAccountClient(cfg), + Item: NewItemClient(cfg), + Playlist: NewPlaylistClient(cfg), + Track: NewTrackClient(cfg), + }, nil +} + +// BeginTx returns a transactional client with specified options. +func (c *Client) BeginTx(ctx context.Context, opts *sql.TxOptions) (*Tx, error) { + if _, ok := c.driver.(*txDriver); ok { + return nil, errors.New("ent: cannot start a transaction within a transaction") + } + tx, err := c.driver.(interface { + BeginTx(context.Context, *sql.TxOptions) (dialect.Tx, error) + }).BeginTx(ctx, opts) + if err != nil { + return nil, fmt.Errorf("ent: starting a transaction: %w", err) + } + cfg := c.config + cfg.driver = &txDriver{tx: tx, drv: c.driver} + return &Tx{ + ctx: ctx, + config: cfg, + Account: NewAccountClient(cfg), + Item: NewItemClient(cfg), + Playlist: NewPlaylistClient(cfg), + Track: NewTrackClient(cfg), + }, nil +} + +// Debug returns a new debug-client. It's used to get verbose logging on specific operations. +// +// client.Debug(). +// Account. +// Query(). +// Count(ctx) +func (c *Client) Debug() *Client { + if c.debug { + return c + } + cfg := c.config + cfg.driver = dialect.Debug(c.driver, c.log) + client := &Client{config: cfg} + client.init() + return client +} + +// Close closes the database connection and prevents new queries from starting. +func (c *Client) Close() error { + return c.driver.Close() +} + +// Use adds the mutation hooks to all the entity clients. +// In order to add hooks to a specific client, call: `client.Node.Use(...)`. +func (c *Client) Use(hooks ...Hook) { + c.Account.Use(hooks...) + c.Item.Use(hooks...) + c.Playlist.Use(hooks...) + c.Track.Use(hooks...) +} + +// Intercept adds the query interceptors to all the entity clients. +// In order to add interceptors to a specific client, call: `client.Node.Intercept(...)`. +func (c *Client) Intercept(interceptors ...Interceptor) { + c.Account.Intercept(interceptors...) + c.Item.Intercept(interceptors...) + c.Playlist.Intercept(interceptors...) + c.Track.Intercept(interceptors...) +} + +// Mutate implements the ent.Mutator interface. +func (c *Client) Mutate(ctx context.Context, m Mutation) (Value, error) { + switch m := m.(type) { + case *AccountMutation: + return c.Account.mutate(ctx, m) + case *ItemMutation: + return c.Item.mutate(ctx, m) + case *PlaylistMutation: + return c.Playlist.mutate(ctx, m) + case *TrackMutation: + return c.Track.mutate(ctx, m) + default: + return nil, fmt.Errorf("ent: unknown mutation type %T", m) + } +} + +// AccountClient is a client for the Account schema. +type AccountClient struct { + config +} + +// NewAccountClient returns a client for the Account from the given config. +func NewAccountClient(c config) *AccountClient { + return &AccountClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `account.Hooks(f(g(h())))`. +func (c *AccountClient) Use(hooks ...Hook) { + c.hooks.Account = append(c.hooks.Account, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `account.Intercept(f(g(h())))`. +func (c *AccountClient) Intercept(interceptors ...Interceptor) { + c.inters.Account = append(c.inters.Account, interceptors...) +} + +// Create returns a builder for creating a Account entity. +func (c *AccountClient) Create() *AccountCreate { + mutation := newAccountMutation(c.config, OpCreate) + return &AccountCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of Account entities. +func (c *AccountClient) CreateBulk(builders ...*AccountCreate) *AccountCreateBulk { + return &AccountCreateBulk{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 *AccountClient) MapCreateBulk(slice any, setFunc func(*AccountCreate, int)) *AccountCreateBulk { + rv := reflect.ValueOf(slice) + if rv.Kind() != reflect.Slice { + return &AccountCreateBulk{err: fmt.Errorf("calling to AccountClient.MapCreateBulk with wrong type %T, need slice", slice)} + } + builders := make([]*AccountCreate, rv.Len()) + for i := 0; i < rv.Len(); i++ { + builders[i] = c.Create() + setFunc(builders[i], i) + } + return &AccountCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for Account. +func (c *AccountClient) Update() *AccountUpdate { + mutation := newAccountMutation(c.config, OpUpdate) + return &AccountUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *AccountClient) UpdateOne(_m *Account) *AccountUpdateOne { + mutation := newAccountMutation(c.config, OpUpdateOne, withAccount(_m)) + return &AccountUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOneID returns an update builder for the given id. +func (c *AccountClient) UpdateOneID(id uuid.UUID) *AccountUpdateOne { + mutation := newAccountMutation(c.config, OpUpdateOne, withAccountID(id)) + return &AccountUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for Account. +func (c *AccountClient) Delete() *AccountDelete { + mutation := newAccountMutation(c.config, OpDelete) + return &AccountDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// DeleteOne returns a builder for deleting the given entity. +func (c *AccountClient) DeleteOne(_m *Account) *AccountDeleteOne { + return c.DeleteOneID(_m.ID) +} + +// DeleteOneID returns a builder for deleting the given entity by its id. +func (c *AccountClient) DeleteOneID(id uuid.UUID) *AccountDeleteOne { + builder := c.Delete().Where(account.ID(id)) + builder.mutation.id = &id + builder.mutation.op = OpDeleteOne + return &AccountDeleteOne{builder} +} + +// Query returns a query builder for Account. +func (c *AccountClient) Query() *AccountQuery { + return &AccountQuery{ + config: c.config, + ctx: &QueryContext{Type: TypeAccount}, + inters: c.Interceptors(), + } +} + +// Get returns a Account entity by its id. +func (c *AccountClient) Get(ctx context.Context, id uuid.UUID) (*Account, error) { + return c.Query().Where(account.ID(id)).Only(ctx) +} + +// GetX is like Get, but panics if an error occurs. +func (c *AccountClient) GetX(ctx context.Context, id uuid.UUID) *Account { + obj, err := c.Get(ctx, id) + if err != nil { + panic(err) + } + return obj +} + +// QueryPlaylists queries the playlists edge of a Account. +func (c *AccountClient) QueryPlaylists(_m *Account) *PlaylistQuery { + query := (&PlaylistClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := _m.ID + step := sqlgraph.NewStep( + sqlgraph.From(account.Table, account.FieldID, id), + sqlgraph.To(playlist.Table, playlist.FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, account.PlaylistsTable, account.PlaylistsColumn), + ) + fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// Hooks returns the client hooks. +func (c *AccountClient) Hooks() []Hook { + return c.hooks.Account +} + +// Interceptors returns the client interceptors. +func (c *AccountClient) Interceptors() []Interceptor { + return c.inters.Account +} + +func (c *AccountClient) mutate(ctx context.Context, m *AccountMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&AccountCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&AccountUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&AccountUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&AccountDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("ent: unknown Account mutation op: %q", m.Op()) + } +} + +// ItemClient is a client for the Item schema. +type ItemClient struct { + config +} + +// NewItemClient returns a client for the Item from the given config. +func NewItemClient(c config) *ItemClient { + return &ItemClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `item.Hooks(f(g(h())))`. +func (c *ItemClient) Use(hooks ...Hook) { + c.hooks.Item = append(c.hooks.Item, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `item.Intercept(f(g(h())))`. +func (c *ItemClient) Intercept(interceptors ...Interceptor) { + c.inters.Item = append(c.inters.Item, interceptors...) +} + +// Create returns a builder for creating a Item entity. +func (c *ItemClient) Create() *ItemCreate { + mutation := newItemMutation(c.config, OpCreate) + return &ItemCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of Item entities. +func (c *ItemClient) CreateBulk(builders ...*ItemCreate) *ItemCreateBulk { + return &ItemCreateBulk{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 *ItemClient) MapCreateBulk(slice any, setFunc func(*ItemCreate, int)) *ItemCreateBulk { + rv := reflect.ValueOf(slice) + if rv.Kind() != reflect.Slice { + return &ItemCreateBulk{err: fmt.Errorf("calling to ItemClient.MapCreateBulk with wrong type %T, need slice", slice)} + } + builders := make([]*ItemCreate, rv.Len()) + for i := 0; i < rv.Len(); i++ { + builders[i] = c.Create() + setFunc(builders[i], i) + } + return &ItemCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for Item. +func (c *ItemClient) Update() *ItemUpdate { + mutation := newItemMutation(c.config, OpUpdate) + return &ItemUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *ItemClient) UpdateOne(_m *Item) *ItemUpdateOne { + mutation := newItemMutation(c.config, OpUpdateOne, withItem(_m)) + return &ItemUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOneID returns an update builder for the given id. +func (c *ItemClient) UpdateOneID(id uuid.UUID) *ItemUpdateOne { + mutation := newItemMutation(c.config, OpUpdateOne, withItemID(id)) + return &ItemUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for Item. +func (c *ItemClient) Delete() *ItemDelete { + mutation := newItemMutation(c.config, OpDelete) + return &ItemDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// DeleteOne returns a builder for deleting the given entity. +func (c *ItemClient) DeleteOne(_m *Item) *ItemDeleteOne { + return c.DeleteOneID(_m.ID) +} + +// DeleteOneID returns a builder for deleting the given entity by its id. +func (c *ItemClient) DeleteOneID(id uuid.UUID) *ItemDeleteOne { + builder := c.Delete().Where(item.ID(id)) + builder.mutation.id = &id + builder.mutation.op = OpDeleteOne + return &ItemDeleteOne{builder} +} + +// Query returns a query builder for Item. +func (c *ItemClient) Query() *ItemQuery { + return &ItemQuery{ + config: c.config, + ctx: &QueryContext{Type: TypeItem}, + inters: c.Interceptors(), + } +} + +// Get returns a Item entity by its id. +func (c *ItemClient) Get(ctx context.Context, id uuid.UUID) (*Item, error) { + return c.Query().Where(item.ID(id)).Only(ctx) +} + +// GetX is like Get, but panics if an error occurs. +func (c *ItemClient) GetX(ctx context.Context, id uuid.UUID) *Item { + obj, err := c.Get(ctx, id) + if err != nil { + panic(err) + } + return obj +} + +// QueryPlaylist queries the playlist edge of a Item. +func (c *ItemClient) QueryPlaylist(_m *Item) *PlaylistQuery { + query := (&PlaylistClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := _m.ID + step := sqlgraph.NewStep( + sqlgraph.From(item.Table, item.FieldID, id), + sqlgraph.To(playlist.Table, playlist.FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, item.PlaylistTable, item.PlaylistColumn), + ) + fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// QueryTrack queries the track edge of a Item. +func (c *ItemClient) QueryTrack(_m *Item) *TrackQuery { + query := (&TrackClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := _m.ID + step := sqlgraph.NewStep( + sqlgraph.From(item.Table, item.FieldID, id), + sqlgraph.To(track.Table, track.FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, item.TrackTable, item.TrackColumn), + ) + fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// Hooks returns the client hooks. +func (c *ItemClient) Hooks() []Hook { + return c.hooks.Item +} + +// Interceptors returns the client interceptors. +func (c *ItemClient) Interceptors() []Interceptor { + return c.inters.Item +} + +func (c *ItemClient) mutate(ctx context.Context, m *ItemMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&ItemCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&ItemUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&ItemUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&ItemDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("ent: unknown Item mutation op: %q", m.Op()) + } +} + +// PlaylistClient is a client for the Playlist schema. +type PlaylistClient struct { + config +} + +// NewPlaylistClient returns a client for the Playlist from the given config. +func NewPlaylistClient(c config) *PlaylistClient { + return &PlaylistClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `playlist.Hooks(f(g(h())))`. +func (c *PlaylistClient) Use(hooks ...Hook) { + c.hooks.Playlist = append(c.hooks.Playlist, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `playlist.Intercept(f(g(h())))`. +func (c *PlaylistClient) Intercept(interceptors ...Interceptor) { + c.inters.Playlist = append(c.inters.Playlist, interceptors...) +} + +// Create returns a builder for creating a Playlist entity. +func (c *PlaylistClient) Create() *PlaylistCreate { + mutation := newPlaylistMutation(c.config, OpCreate) + return &PlaylistCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of Playlist entities. +func (c *PlaylistClient) CreateBulk(builders ...*PlaylistCreate) *PlaylistCreateBulk { + return &PlaylistCreateBulk{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 *PlaylistClient) MapCreateBulk(slice any, setFunc func(*PlaylistCreate, int)) *PlaylistCreateBulk { + rv := reflect.ValueOf(slice) + if rv.Kind() != reflect.Slice { + return &PlaylistCreateBulk{err: fmt.Errorf("calling to PlaylistClient.MapCreateBulk with wrong type %T, need slice", slice)} + } + builders := make([]*PlaylistCreate, rv.Len()) + for i := 0; i < rv.Len(); i++ { + builders[i] = c.Create() + setFunc(builders[i], i) + } + return &PlaylistCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for Playlist. +func (c *PlaylistClient) Update() *PlaylistUpdate { + mutation := newPlaylistMutation(c.config, OpUpdate) + return &PlaylistUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *PlaylistClient) UpdateOne(_m *Playlist) *PlaylistUpdateOne { + mutation := newPlaylistMutation(c.config, OpUpdateOne, withPlaylist(_m)) + return &PlaylistUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOneID returns an update builder for the given id. +func (c *PlaylistClient) UpdateOneID(id uuid.UUID) *PlaylistUpdateOne { + mutation := newPlaylistMutation(c.config, OpUpdateOne, withPlaylistID(id)) + return &PlaylistUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for Playlist. +func (c *PlaylistClient) Delete() *PlaylistDelete { + mutation := newPlaylistMutation(c.config, OpDelete) + return &PlaylistDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// DeleteOne returns a builder for deleting the given entity. +func (c *PlaylistClient) DeleteOne(_m *Playlist) *PlaylistDeleteOne { + return c.DeleteOneID(_m.ID) +} + +// DeleteOneID returns a builder for deleting the given entity by its id. +func (c *PlaylistClient) DeleteOneID(id uuid.UUID) *PlaylistDeleteOne { + builder := c.Delete().Where(playlist.ID(id)) + builder.mutation.id = &id + builder.mutation.op = OpDeleteOne + return &PlaylistDeleteOne{builder} +} + +// Query returns a query builder for Playlist. +func (c *PlaylistClient) Query() *PlaylistQuery { + return &PlaylistQuery{ + config: c.config, + ctx: &QueryContext{Type: TypePlaylist}, + inters: c.Interceptors(), + } +} + +// Get returns a Playlist entity by its id. +func (c *PlaylistClient) Get(ctx context.Context, id uuid.UUID) (*Playlist, error) { + return c.Query().Where(playlist.ID(id)).Only(ctx) +} + +// GetX is like Get, but panics if an error occurs. +func (c *PlaylistClient) GetX(ctx context.Context, id uuid.UUID) *Playlist { + obj, err := c.Get(ctx, id) + if err != nil { + panic(err) + } + return obj +} + +// QueryItems queries the items edge of a Playlist. +func (c *PlaylistClient) QueryItems(_m *Playlist) *ItemQuery { + query := (&ItemClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := _m.ID + step := sqlgraph.NewStep( + sqlgraph.From(playlist.Table, playlist.FieldID, id), + sqlgraph.To(item.Table, item.FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, playlist.ItemsTable, playlist.ItemsColumn), + ) + fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// QueryOwner queries the owner edge of a Playlist. +func (c *PlaylistClient) QueryOwner(_m *Playlist) *AccountQuery { + query := (&AccountClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := _m.ID + step := sqlgraph.NewStep( + sqlgraph.From(playlist.Table, playlist.FieldID, id), + sqlgraph.To(account.Table, account.FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, playlist.OwnerTable, playlist.OwnerColumn), + ) + fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// Hooks returns the client hooks. +func (c *PlaylistClient) Hooks() []Hook { + return c.hooks.Playlist +} + +// Interceptors returns the client interceptors. +func (c *PlaylistClient) Interceptors() []Interceptor { + return c.inters.Playlist +} + +func (c *PlaylistClient) mutate(ctx context.Context, m *PlaylistMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&PlaylistCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&PlaylistUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&PlaylistUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&PlaylistDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("ent: unknown Playlist mutation op: %q", m.Op()) + } +} + +// TrackClient is a client for the Track schema. +type TrackClient struct { + config +} + +// NewTrackClient returns a client for the Track from the given config. +func NewTrackClient(c config) *TrackClient { + return &TrackClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `track.Hooks(f(g(h())))`. +func (c *TrackClient) Use(hooks ...Hook) { + c.hooks.Track = append(c.hooks.Track, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `track.Intercept(f(g(h())))`. +func (c *TrackClient) Intercept(interceptors ...Interceptor) { + c.inters.Track = append(c.inters.Track, interceptors...) +} + +// Create returns a builder for creating a Track entity. +func (c *TrackClient) Create() *TrackCreate { + mutation := newTrackMutation(c.config, OpCreate) + return &TrackCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of Track entities. +func (c *TrackClient) CreateBulk(builders ...*TrackCreate) *TrackCreateBulk { + return &TrackCreateBulk{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 *TrackClient) MapCreateBulk(slice any, setFunc func(*TrackCreate, int)) *TrackCreateBulk { + rv := reflect.ValueOf(slice) + if rv.Kind() != reflect.Slice { + return &TrackCreateBulk{err: fmt.Errorf("calling to TrackClient.MapCreateBulk with wrong type %T, need slice", slice)} + } + builders := make([]*TrackCreate, rv.Len()) + for i := 0; i < rv.Len(); i++ { + builders[i] = c.Create() + setFunc(builders[i], i) + } + return &TrackCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for Track. +func (c *TrackClient) Update() *TrackUpdate { + mutation := newTrackMutation(c.config, OpUpdate) + return &TrackUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *TrackClient) UpdateOne(_m *Track) *TrackUpdateOne { + mutation := newTrackMutation(c.config, OpUpdateOne, withTrack(_m)) + return &TrackUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOneID returns an update builder for the given id. +func (c *TrackClient) UpdateOneID(id uuid.UUID) *TrackUpdateOne { + mutation := newTrackMutation(c.config, OpUpdateOne, withTrackID(id)) + return &TrackUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for Track. +func (c *TrackClient) Delete() *TrackDelete { + mutation := newTrackMutation(c.config, OpDelete) + return &TrackDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// DeleteOne returns a builder for deleting the given entity. +func (c *TrackClient) DeleteOne(_m *Track) *TrackDeleteOne { + return c.DeleteOneID(_m.ID) +} + +// DeleteOneID returns a builder for deleting the given entity by its id. +func (c *TrackClient) DeleteOneID(id uuid.UUID) *TrackDeleteOne { + builder := c.Delete().Where(track.ID(id)) + builder.mutation.id = &id + builder.mutation.op = OpDeleteOne + return &TrackDeleteOne{builder} +} + +// Query returns a query builder for Track. +func (c *TrackClient) Query() *TrackQuery { + return &TrackQuery{ + config: c.config, + ctx: &QueryContext{Type: TypeTrack}, + inters: c.Interceptors(), + } +} + +// Get returns a Track entity by its id. +func (c *TrackClient) Get(ctx context.Context, id uuid.UUID) (*Track, error) { + return c.Query().Where(track.ID(id)).Only(ctx) +} + +// GetX is like Get, but panics if an error occurs. +func (c *TrackClient) GetX(ctx context.Context, id uuid.UUID) *Track { + obj, err := c.Get(ctx, id) + if err != nil { + panic(err) + } + return obj +} + +// QueryItems queries the items edge of a Track. +func (c *TrackClient) QueryItems(_m *Track) *ItemQuery { + query := (&ItemClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := _m.ID + step := sqlgraph.NewStep( + sqlgraph.From(track.Table, track.FieldID, id), + sqlgraph.To(item.Table, item.FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, track.ItemsTable, track.ItemsColumn), + ) + fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// Hooks returns the client hooks. +func (c *TrackClient) Hooks() []Hook { + return c.hooks.Track +} + +// Interceptors returns the client interceptors. +func (c *TrackClient) Interceptors() []Interceptor { + return c.inters.Track +} + +func (c *TrackClient) mutate(ctx context.Context, m *TrackMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&TrackCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&TrackUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&TrackUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&TrackDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("ent: unknown Track mutation op: %q", m.Op()) + } +} + +// hooks and interceptors per client, for fast access. +type ( + hooks struct { + Account, Item, Playlist, Track []ent.Hook + } + inters struct { + Account, Item, Playlist, Track []ent.Interceptor + } +) diff --git a/applications/playlist-api/ent/ent.go b/applications/playlist-api/ent/ent.go new file mode 100644 index 00000000..b07a2a58 --- /dev/null +++ b/applications/playlist-api/ent/ent.go @@ -0,0 +1,614 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "reflect" + "sync" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "example.com/playlist-api/ent/account" + "example.com/playlist-api/ent/item" + "example.com/playlist-api/ent/playlist" + "example.com/playlist-api/ent/track" +) + +// ent aliases to avoid import conflicts in user's code. +type ( + Op = ent.Op + Hook = ent.Hook + Value = ent.Value + Query = ent.Query + QueryContext = ent.QueryContext + Querier = ent.Querier + QuerierFunc = ent.QuerierFunc + Interceptor = ent.Interceptor + InterceptFunc = ent.InterceptFunc + Traverser = ent.Traverser + TraverseFunc = ent.TraverseFunc + Policy = ent.Policy + Mutator = ent.Mutator + Mutation = ent.Mutation + MutateFunc = ent.MutateFunc +) + +type clientCtxKey struct{} + +// FromContext returns a Client stored inside a context, or nil if there isn't one. +func FromContext(ctx context.Context) *Client { + c, _ := ctx.Value(clientCtxKey{}).(*Client) + return c +} + +// NewContext returns a new context with the given Client attached. +func NewContext(parent context.Context, c *Client) context.Context { + return context.WithValue(parent, clientCtxKey{}, c) +} + +type txCtxKey struct{} + +// TxFromContext returns a Tx stored inside a context, or nil if there isn't one. +func TxFromContext(ctx context.Context) *Tx { + tx, _ := ctx.Value(txCtxKey{}).(*Tx) + return tx +} + +// NewTxContext returns a new context with the given Tx attached. +func NewTxContext(parent context.Context, tx *Tx) context.Context { + return context.WithValue(parent, txCtxKey{}, tx) +} + +// OrderFunc applies an ordering on the sql selector. +// Deprecated: Use Asc/Desc functions or the package builders instead. +type OrderFunc func(*sql.Selector) + +var ( + initCheck sync.Once + columnCheck sql.ColumnCheck +) + +// checkColumn checks if the column exists in the given table. +func checkColumn(t, c string) error { + initCheck.Do(func() { + columnCheck = sql.NewColumnCheck(map[string]func(string) bool{ + account.Table: account.ValidColumn, + item.Table: item.ValidColumn, + playlist.Table: playlist.ValidColumn, + track.Table: track.ValidColumn, + }) + }) + return columnCheck(t, c) +} + +// Asc applies the given fields in ASC order. +func Asc(fields ...string) func(*sql.Selector) { + return func(s *sql.Selector) { + for _, f := range fields { + if err := checkColumn(s.TableName(), f); err != nil { + s.AddError(&ValidationError{Name: f, err: fmt.Errorf("ent: %w", err)}) + } + s.OrderBy(sql.Asc(s.C(f))) + } + } +} + +// Desc applies the given fields in DESC order. +func Desc(fields ...string) func(*sql.Selector) { + return func(s *sql.Selector) { + for _, f := range fields { + if err := checkColumn(s.TableName(), f); err != nil { + s.AddError(&ValidationError{Name: f, err: fmt.Errorf("ent: %w", err)}) + } + s.OrderBy(sql.Desc(s.C(f))) + } + } +} + +// AggregateFunc applies an aggregation step on the group-by traversal/selector. +type AggregateFunc func(*sql.Selector) string + +// As is a pseudo aggregation function for renaming another other functions with custom names. For example: +// +// GroupBy(field1, field2). +// Aggregate(ent.As(ent.Sum(field1), "sum_field1"), (ent.As(ent.Sum(field2), "sum_field2")). +// Scan(ctx, &v) +func As(fn AggregateFunc, end string) AggregateFunc { + return func(s *sql.Selector) string { + return sql.As(fn(s), end) + } +} + +// Count applies the "count" aggregation function on each group. +func Count() AggregateFunc { + return func(s *sql.Selector) string { + return sql.Count("*") + } +} + +// Max applies the "max" aggregation function on the given field of each group. +func Max(field string) AggregateFunc { + return func(s *sql.Selector) string { + if err := checkColumn(s.TableName(), field); err != nil { + s.AddError(&ValidationError{Name: field, err: fmt.Errorf("ent: %w", err)}) + return "" + } + return sql.Max(s.C(field)) + } +} + +// Mean applies the "mean" aggregation function on the given field of each group. +func Mean(field string) AggregateFunc { + return func(s *sql.Selector) string { + if err := checkColumn(s.TableName(), field); err != nil { + s.AddError(&ValidationError{Name: field, err: fmt.Errorf("ent: %w", err)}) + return "" + } + return sql.Avg(s.C(field)) + } +} + +// Min applies the "min" aggregation function on the given field of each group. +func Min(field string) AggregateFunc { + return func(s *sql.Selector) string { + if err := checkColumn(s.TableName(), field); err != nil { + s.AddError(&ValidationError{Name: field, err: fmt.Errorf("ent: %w", err)}) + return "" + } + return sql.Min(s.C(field)) + } +} + +// Sum applies the "sum" aggregation function on the given field of each group. +func Sum(field string) AggregateFunc { + return func(s *sql.Selector) string { + if err := checkColumn(s.TableName(), field); err != nil { + s.AddError(&ValidationError{Name: field, err: fmt.Errorf("ent: %w", err)}) + return "" + } + return sql.Sum(s.C(field)) + } +} + +// ValidationError returns when validating a field or edge fails. +type ValidationError struct { + Name string // Field or edge name. + err error +} + +// Error implements the error interface. +func (e *ValidationError) Error() string { + return e.err.Error() +} + +// Unwrap implements the errors.Wrapper interface. +func (e *ValidationError) Unwrap() error { + return e.err +} + +// IsValidationError returns a boolean indicating whether the error is a validation error. +func IsValidationError(err error) bool { + if err == nil { + return false + } + var e *ValidationError + return errors.As(err, &e) +} + +// NotFoundError returns when trying to fetch a specific entity and it was not found in the database. +type NotFoundError struct { + label string +} + +// Error implements the error interface. +func (e *NotFoundError) Error() string { + return "ent: " + e.label + " not found" +} + +// IsNotFound returns a boolean indicating whether the error is a not found error. +func IsNotFound(err error) bool { + if err == nil { + return false + } + var e *NotFoundError + return errors.As(err, &e) +} + +// MaskNotFound masks not found error. +func MaskNotFound(err error) error { + if IsNotFound(err) { + return nil + } + return err +} + +// NotSingularError returns when trying to fetch a singular entity and more then one was found in the database. +type NotSingularError struct { + label string +} + +// Error implements the error interface. +func (e *NotSingularError) Error() string { + return "ent: " + e.label + " not singular" +} + +// IsNotSingular returns a boolean indicating whether the error is a not singular error. +func IsNotSingular(err error) bool { + if err == nil { + return false + } + var e *NotSingularError + return errors.As(err, &e) +} + +// NotLoadedError returns when trying to get a node that was not loaded by the query. +type NotLoadedError struct { + edge string +} + +// Error implements the error interface. +func (e *NotLoadedError) Error() string { + return "ent: " + e.edge + " edge was not loaded" +} + +// IsNotLoaded returns a boolean indicating whether the error is a not loaded error. +func IsNotLoaded(err error) bool { + if err == nil { + return false + } + var e *NotLoadedError + return errors.As(err, &e) +} + +// ConstraintError returns when trying to create/update one or more entities and +// one or more of their constraints failed. For example, violation of edge or +// field uniqueness. +type ConstraintError struct { + msg string + wrap error +} + +// Error implements the error interface. +func (e ConstraintError) Error() string { + return "ent: constraint failed: " + e.msg +} + +// Unwrap implements the errors.Wrapper interface. +func (e *ConstraintError) Unwrap() error { + return e.wrap +} + +// IsConstraintError returns a boolean indicating whether the error is a constraint failure. +func IsConstraintError(err error) bool { + if err == nil { + return false + } + var e *ConstraintError + return errors.As(err, &e) +} + +// selector embedded by the different Select/GroupBy builders. +type selector struct { + label string + flds *[]string + fns []AggregateFunc + scan func(context.Context, any) error +} + +// ScanX is like Scan, but panics if an error occurs. +func (s *selector) ScanX(ctx context.Context, v any) { + if err := s.scan(ctx, v); err != nil { + panic(err) + } +} + +// Strings returns list of strings from a selector. It is only allowed when selecting one field. +func (s *selector) Strings(ctx context.Context) ([]string, error) { + if len(*s.flds) > 1 { + return nil, errors.New("ent: Strings is not achievable when selecting more than 1 field") + } + var v []string + if err := s.scan(ctx, &v); err != nil { + return nil, err + } + return v, nil +} + +// StringsX is like Strings, but panics if an error occurs. +func (s *selector) StringsX(ctx context.Context) []string { + v, err := s.Strings(ctx) + if err != nil { + panic(err) + } + return v +} + +// String returns a single string from a selector. It is only allowed when selecting one field. +func (s *selector) String(ctx context.Context) (_ string, err error) { + var v []string + if v, err = s.Strings(ctx); err != nil { + return + } + switch len(v) { + case 1: + return v[0], nil + case 0: + err = &NotFoundError{s.label} + default: + err = fmt.Errorf("ent: Strings returned %d results when one was expected", len(v)) + } + return +} + +// StringX is like String, but panics if an error occurs. +func (s *selector) StringX(ctx context.Context) string { + v, err := s.String(ctx) + if err != nil { + panic(err) + } + return v +} + +// Ints returns list of ints from a selector. It is only allowed when selecting one field. +func (s *selector) Ints(ctx context.Context) ([]int, error) { + if len(*s.flds) > 1 { + return nil, errors.New("ent: Ints is not achievable when selecting more than 1 field") + } + var v []int + if err := s.scan(ctx, &v); err != nil { + return nil, err + } + return v, nil +} + +// IntsX is like Ints, but panics if an error occurs. +func (s *selector) IntsX(ctx context.Context) []int { + v, err := s.Ints(ctx) + if err != nil { + panic(err) + } + return v +} + +// Int returns a single int from a selector. It is only allowed when selecting one field. +func (s *selector) Int(ctx context.Context) (_ int, err error) { + var v []int + if v, err = s.Ints(ctx); err != nil { + return + } + switch len(v) { + case 1: + return v[0], nil + case 0: + err = &NotFoundError{s.label} + default: + err = fmt.Errorf("ent: Ints returned %d results when one was expected", len(v)) + } + return +} + +// IntX is like Int, but panics if an error occurs. +func (s *selector) IntX(ctx context.Context) int { + v, err := s.Int(ctx) + if err != nil { + panic(err) + } + return v +} + +// Float64s returns list of float64s from a selector. It is only allowed when selecting one field. +func (s *selector) Float64s(ctx context.Context) ([]float64, error) { + if len(*s.flds) > 1 { + return nil, errors.New("ent: Float64s is not achievable when selecting more than 1 field") + } + var v []float64 + if err := s.scan(ctx, &v); err != nil { + return nil, err + } + return v, nil +} + +// Float64sX is like Float64s, but panics if an error occurs. +func (s *selector) Float64sX(ctx context.Context) []float64 { + v, err := s.Float64s(ctx) + if err != nil { + panic(err) + } + return v +} + +// Float64 returns a single float64 from a selector. It is only allowed when selecting one field. +func (s *selector) Float64(ctx context.Context) (_ float64, err error) { + var v []float64 + if v, err = s.Float64s(ctx); err != nil { + return + } + switch len(v) { + case 1: + return v[0], nil + case 0: + err = &NotFoundError{s.label} + default: + err = fmt.Errorf("ent: Float64s returned %d results when one was expected", len(v)) + } + return +} + +// Float64X is like Float64, but panics if an error occurs. +func (s *selector) Float64X(ctx context.Context) float64 { + v, err := s.Float64(ctx) + if err != nil { + panic(err) + } + return v +} + +// Bools returns list of bools from a selector. It is only allowed when selecting one field. +func (s *selector) Bools(ctx context.Context) ([]bool, error) { + if len(*s.flds) > 1 { + return nil, errors.New("ent: Bools is not achievable when selecting more than 1 field") + } + var v []bool + if err := s.scan(ctx, &v); err != nil { + return nil, err + } + return v, nil +} + +// BoolsX is like Bools, but panics if an error occurs. +func (s *selector) BoolsX(ctx context.Context) []bool { + v, err := s.Bools(ctx) + if err != nil { + panic(err) + } + return v +} + +// Bool returns a single bool from a selector. It is only allowed when selecting one field. +func (s *selector) Bool(ctx context.Context) (_ bool, err error) { + var v []bool + if v, err = s.Bools(ctx); err != nil { + return + } + switch len(v) { + case 1: + return v[0], nil + case 0: + err = &NotFoundError{s.label} + default: + err = fmt.Errorf("ent: Bools returned %d results when one was expected", len(v)) + } + return +} + +// BoolX is like Bool, but panics if an error occurs. +func (s *selector) BoolX(ctx context.Context) bool { + v, err := s.Bool(ctx) + if err != nil { + panic(err) + } + return v +} + +// withHooks invokes the builder operation with the given hooks, if any. +func withHooks[V Value, M any, PM interface { + *M + Mutation +}](ctx context.Context, exec func(context.Context) (V, error), mutation PM, hooks []Hook) (value V, err error) { + if len(hooks) == 0 { + return exec(ctx) + } + var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { + mutationT, ok := any(m).(PM) + if !ok { + return nil, fmt.Errorf("unexpected mutation type %T", m) + } + // Set the mutation to the builder. + *mutation = *mutationT + return exec(ctx) + }) + for i := len(hooks) - 1; i >= 0; i-- { + if hooks[i] == nil { + return value, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)") + } + mut = hooks[i](mut) + } + v, err := mut.Mutate(ctx, mutation) + if err != nil { + return value, err + } + nv, ok := v.(V) + if !ok { + return value, fmt.Errorf("unexpected node type %T returned from %T", v, mutation) + } + return nv, nil +} + +// setContextOp returns a new context with the given QueryContext attached (including its op) in case it does not exist. +func setContextOp(ctx context.Context, qc *QueryContext, op string) context.Context { + if ent.QueryFromContext(ctx) == nil { + qc.Op = op + ctx = ent.NewQueryContext(ctx, qc) + } + return ctx +} + +func querierAll[V Value, Q interface { + sqlAll(context.Context, ...queryHook) (V, error) +}]() Querier { + return QuerierFunc(func(ctx context.Context, q Query) (Value, error) { + query, ok := q.(Q) + if !ok { + return nil, fmt.Errorf("unexpected query type %T", q) + } + return query.sqlAll(ctx) + }) +} + +func querierCount[Q interface { + sqlCount(context.Context) (int, error) +}]() Querier { + return QuerierFunc(func(ctx context.Context, q Query) (Value, error) { + query, ok := q.(Q) + if !ok { + return nil, fmt.Errorf("unexpected query type %T", q) + } + return query.sqlCount(ctx) + }) +} + +func withInterceptors[V Value](ctx context.Context, q Query, qr Querier, inters []Interceptor) (v V, err error) { + for i := len(inters) - 1; i >= 0; i-- { + qr = inters[i].Intercept(qr) + } + rv, err := qr.Query(ctx, q) + if err != nil { + return v, err + } + vt, ok := rv.(V) + if !ok { + return v, fmt.Errorf("unexpected type %T returned from %T. expected type: %T", vt, q, v) + } + return vt, nil +} + +func scanWithInterceptors[Q1 ent.Query, Q2 interface { + sqlScan(context.Context, Q1, any) error +}](ctx context.Context, rootQuery Q1, selectOrGroup Q2, inters []Interceptor, v any) error { + rv := reflect.ValueOf(v) + var qr Querier = QuerierFunc(func(ctx context.Context, q Query) (Value, error) { + query, ok := q.(Q1) + if !ok { + return nil, fmt.Errorf("unexpected query type %T", q) + } + if err := selectOrGroup.sqlScan(ctx, query, v); err != nil { + return nil, err + } + if k := rv.Kind(); k == reflect.Pointer && rv.Elem().CanInterface() { + return rv.Elem().Interface(), nil + } + return v, nil + }) + for i := len(inters) - 1; i >= 0; i-- { + qr = inters[i].Intercept(qr) + } + vv, err := qr.Query(ctx, rootQuery) + if err != nil { + return err + } + switch rv2 := reflect.ValueOf(vv); { + case rv.IsNil(), rv2.IsNil(), rv.Kind() != reflect.Pointer: + case rv.Type() == rv2.Type(): + rv.Elem().Set(rv2.Elem()) + case rv.Elem().Type() == rv2.Type(): + rv.Elem().Set(rv2) + } + return nil +} + +// queryHook describes an internal hook for the different sqlAll methods. +type queryHook func(context.Context, *sqlgraph.QuerySpec) diff --git a/applications/playlist-api/ent/enttest/enttest.go b/applications/playlist-api/ent/enttest/enttest.go new file mode 100644 index 00000000..75b84ebe --- /dev/null +++ b/applications/playlist-api/ent/enttest/enttest.go @@ -0,0 +1,84 @@ +// Code generated by ent, DO NOT EDIT. + +package enttest + +import ( + "context" + + "example.com/playlist-api/ent" + // required by schema hooks. + _ "example.com/playlist-api/ent/runtime" + + "entgo.io/ent/dialect/sql/schema" + "example.com/playlist-api/ent/migrate" +) + +type ( + // TestingT is the interface that is shared between + // testing.T and testing.B and used by enttest. + TestingT interface { + FailNow() + Error(...any) + } + + // Option configures client creation. + Option func(*options) + + options struct { + opts []ent.Option + migrateOpts []schema.MigrateOption + } +) + +// WithOptions forwards options to client creation. +func WithOptions(opts ...ent.Option) Option { + return func(o *options) { + o.opts = append(o.opts, opts...) + } +} + +// WithMigrateOptions forwards options to auto migration. +func WithMigrateOptions(opts ...schema.MigrateOption) Option { + return func(o *options) { + o.migrateOpts = append(o.migrateOpts, opts...) + } +} + +func newOptions(opts []Option) *options { + o := &options{} + for _, opt := range opts { + opt(o) + } + return o +} + +// Open calls ent.Open and auto-run migration. +func Open(t TestingT, driverName, dataSourceName string, opts ...Option) *ent.Client { + o := newOptions(opts) + c, err := ent.Open(driverName, dataSourceName, o.opts...) + if err != nil { + t.Error(err) + t.FailNow() + } + migrateSchema(t, c, o) + return c +} + +// NewClient calls ent.NewClient and auto-run migration. +func NewClient(t TestingT, opts ...Option) *ent.Client { + o := newOptions(opts) + c := ent.NewClient(o.opts...) + migrateSchema(t, c, o) + return c +} +func migrateSchema(t TestingT, c *ent.Client, o *options) { + tables, err := schema.CopyTables(migrate.Tables) + if err != nil { + t.Error(err) + t.FailNow() + } + if err := migrate.Create(context.Background(), c.Schema, tables, o.migrateOpts...); err != nil { + t.Error(err) + t.FailNow() + } +} diff --git a/applications/playlist-api/ent/hook/hook.go b/applications/playlist-api/ent/hook/hook.go new file mode 100644 index 00000000..3d6b60d6 --- /dev/null +++ b/applications/playlist-api/ent/hook/hook.go @@ -0,0 +1,235 @@ +// Code generated by ent, DO NOT EDIT. + +package hook + +import ( + "context" + "fmt" + + "example.com/playlist-api/ent" +) + +// The AccountFunc type is an adapter to allow the use of ordinary +// function as Account mutator. +type AccountFunc func(context.Context, *ent.AccountMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f AccountFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if mv, ok := m.(*ent.AccountMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.AccountMutation", m) +} + +// The ItemFunc type is an adapter to allow the use of ordinary +// function as Item mutator. +type ItemFunc func(context.Context, *ent.ItemMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f ItemFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if mv, ok := m.(*ent.ItemMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.ItemMutation", m) +} + +// The PlaylistFunc type is an adapter to allow the use of ordinary +// function as Playlist mutator. +type PlaylistFunc func(context.Context, *ent.PlaylistMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f PlaylistFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if mv, ok := m.(*ent.PlaylistMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.PlaylistMutation", m) +} + +// The TrackFunc type is an adapter to allow the use of ordinary +// function as Track mutator. +type TrackFunc func(context.Context, *ent.TrackMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f TrackFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if mv, ok := m.(*ent.TrackMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.TrackMutation", m) +} + +// Condition is a hook condition function. +type Condition func(context.Context, ent.Mutation) bool + +// And groups conditions with the AND operator. +func And(first, second Condition, rest ...Condition) Condition { + return func(ctx context.Context, m ent.Mutation) bool { + if !first(ctx, m) || !second(ctx, m) { + return false + } + for _, cond := range rest { + if !cond(ctx, m) { + return false + } + } + return true + } +} + +// Or groups conditions with the OR operator. +func Or(first, second Condition, rest ...Condition) Condition { + return func(ctx context.Context, m ent.Mutation) bool { + if first(ctx, m) || second(ctx, m) { + return true + } + for _, cond := range rest { + if cond(ctx, m) { + return true + } + } + return false + } +} + +// Not negates a given condition. +func Not(cond Condition) Condition { + return func(ctx context.Context, m ent.Mutation) bool { + return !cond(ctx, m) + } +} + +// HasOp is a condition testing mutation operation. +func HasOp(op ent.Op) Condition { + return func(_ context.Context, m ent.Mutation) bool { + return m.Op().Is(op) + } +} + +// HasAddedFields is a condition validating `.AddedField` on fields. +func HasAddedFields(field string, fields ...string) Condition { + return func(_ context.Context, m ent.Mutation) bool { + if _, exists := m.AddedField(field); !exists { + return false + } + for _, field := range fields { + if _, exists := m.AddedField(field); !exists { + return false + } + } + return true + } +} + +// HasClearedFields is a condition validating `.FieldCleared` on fields. +func HasClearedFields(field string, fields ...string) Condition { + return func(_ context.Context, m ent.Mutation) bool { + if exists := m.FieldCleared(field); !exists { + return false + } + for _, field := range fields { + if exists := m.FieldCleared(field); !exists { + return false + } + } + return true + } +} + +// HasFields is a condition validating `.Field` on fields. +func HasFields(field string, fields ...string) Condition { + return func(_ context.Context, m ent.Mutation) bool { + if _, exists := m.Field(field); !exists { + return false + } + for _, field := range fields { + if _, exists := m.Field(field); !exists { + return false + } + } + return true + } +} + +// If executes the given hook under condition. +// +// hook.If(ComputeAverage, And(HasFields(...), HasAddedFields(...))) +func If(hk ent.Hook, cond Condition) ent.Hook { + return func(next ent.Mutator) ent.Mutator { + return ent.MutateFunc(func(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if cond(ctx, m) { + return hk(next).Mutate(ctx, m) + } + return next.Mutate(ctx, m) + }) + } +} + +// On executes the given hook only for the given operation. +// +// hook.On(Log, ent.Delete|ent.Create) +func On(hk ent.Hook, op ent.Op) ent.Hook { + return If(hk, HasOp(op)) +} + +// Unless skips the given hook only for the given operation. +// +// hook.Unless(Log, ent.Update|ent.UpdateOne) +func Unless(hk ent.Hook, op ent.Op) ent.Hook { + return If(hk, Not(HasOp(op))) +} + +// FixedError is a hook returning a fixed error. +func FixedError(err error) ent.Hook { + return func(ent.Mutator) ent.Mutator { + return ent.MutateFunc(func(context.Context, ent.Mutation) (ent.Value, error) { + return nil, err + }) + } +} + +// Reject returns a hook that rejects all operations that match op. +// +// func (T) Hooks() []ent.Hook { +// return []ent.Hook{ +// Reject(ent.Delete|ent.Update), +// } +// } +func Reject(op ent.Op) ent.Hook { + hk := FixedError(fmt.Errorf("%s operation is not allowed", op)) + return On(hk, op) +} + +// Chain acts as a list of hooks and is effectively immutable. +// Once created, it will always hold the same set of hooks in the same order. +type Chain struct { + hooks []ent.Hook +} + +// NewChain creates a new chain of hooks. +func NewChain(hooks ...ent.Hook) Chain { + return Chain{append([]ent.Hook(nil), hooks...)} +} + +// Hook chains the list of hooks and returns the final hook. +func (c Chain) Hook() ent.Hook { + return func(mutator ent.Mutator) ent.Mutator { + for i := len(c.hooks) - 1; i >= 0; i-- { + mutator = c.hooks[i](mutator) + } + return mutator + } +} + +// Append extends a chain, adding the specified hook +// as the last ones in the mutation flow. +func (c Chain) Append(hooks ...ent.Hook) Chain { + newHooks := make([]ent.Hook, 0, len(c.hooks)+len(hooks)) + newHooks = append(newHooks, c.hooks...) + newHooks = append(newHooks, hooks...) + return Chain{newHooks} +} + +// Extend extends a chain, adding the specified chain +// as the last ones in the mutation flow. +func (c Chain) Extend(chain Chain) Chain { + return c.Append(chain.hooks...) +} diff --git a/applications/playlist-api/ent/item.go b/applications/playlist-api/ent/item.go new file mode 100644 index 00000000..7935ff2b --- /dev/null +++ b/applications/playlist-api/ent/item.go @@ -0,0 +1,174 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "fmt" + "strings" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "example.com/playlist-api/ent/item" + "example.com/playlist-api/ent/playlist" + "example.com/playlist-api/ent/track" + "github.com/google/uuid" +) + +// Item is the model entity for the Item schema. +type Item struct { + config `json:"-"` + // ID of the ent. + ID uuid.UUID `json:"id,omitempty"` + // PlaylistID holds the value of the "playlist_id" field. + PlaylistID uuid.UUID `json:"playlist_id,omitempty"` + // TrackID holds the value of the "track_id" field. + TrackID uuid.UUID `json:"track_id,omitempty"` + // Position holds the value of the "position" field. + Position int `json:"position,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the ItemQuery when eager-loading is set. + Edges ItemEdges `json:"edges"` + selectValues sql.SelectValues +} + +// ItemEdges holds the relations/edges for other nodes in the graph. +type ItemEdges struct { + // Playlist holds the value of the playlist edge. + Playlist *Playlist `json:"playlist,omitempty"` + // Track holds the value of the track edge. + Track *Track `json:"track,omitempty"` + // loadedTypes holds the information for reporting if a + // type was loaded (or requested) in eager-loading or not. + loadedTypes [2]bool +} + +// PlaylistOrErr returns the Playlist value or an error if the edge +// was not loaded in eager-loading, or loaded but was not found. +func (e ItemEdges) PlaylistOrErr() (*Playlist, error) { + if e.Playlist != nil { + return e.Playlist, nil + } else if e.loadedTypes[0] { + return nil, &NotFoundError{label: playlist.Label} + } + return nil, &NotLoadedError{edge: "playlist"} +} + +// TrackOrErr returns the Track value or an error if the edge +// was not loaded in eager-loading, or loaded but was not found. +func (e ItemEdges) TrackOrErr() (*Track, error) { + if e.Track != nil { + return e.Track, nil + } else if e.loadedTypes[1] { + return nil, &NotFoundError{label: track.Label} + } + return nil, &NotLoadedError{edge: "track"} +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*Item) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case item.FieldPosition: + values[i] = new(sql.NullInt64) + case item.FieldID, item.FieldPlaylistID, item.FieldTrackID: + 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 Item fields. +func (_m *Item) 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 item.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 item.FieldPlaylistID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field playlist_id", values[i]) + } else if value != nil { + _m.PlaylistID = *value + } + case item.FieldTrackID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field track_id", values[i]) + } else if value != nil { + _m.TrackID = *value + } + case item.FieldPosition: + if value, ok := values[i].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for field position", values[i]) + } else if value.Valid { + _m.Position = int(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 Item. +// This includes values selected through modifiers, order, etc. +func (_m *Item) Value(name string) (ent.Value, error) { + return _m.selectValues.Get(name) +} + +// QueryPlaylist queries the "playlist" edge of the Item entity. +func (_m *Item) QueryPlaylist() *PlaylistQuery { + return NewItemClient(_m.config).QueryPlaylist(_m) +} + +// QueryTrack queries the "track" edge of the Item entity. +func (_m *Item) QueryTrack() *TrackQuery { + return NewItemClient(_m.config).QueryTrack(_m) +} + +// Update returns a builder for updating this Item. +// Note that you need to call Item.Unwrap() before calling this method if this Item +// was returned from a transaction, and the transaction was committed or rolled back. +func (_m *Item) Update() *ItemUpdateOne { + return NewItemClient(_m.config).UpdateOne(_m) +} + +// Unwrap unwraps the Item 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 *Item) Unwrap() *Item { + _tx, ok := _m.config.driver.(*txDriver) + if !ok { + panic("ent: Item is not a transactional entity") + } + _m.config.driver = _tx.drv + return _m +} + +// String implements the fmt.Stringer. +func (_m *Item) String() string { + var builder strings.Builder + builder.WriteString("Item(") + builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID)) + builder.WriteString("playlist_id=") + builder.WriteString(fmt.Sprintf("%v", _m.PlaylistID)) + builder.WriteString(", ") + builder.WriteString("track_id=") + builder.WriteString(fmt.Sprintf("%v", _m.TrackID)) + builder.WriteString(", ") + builder.WriteString("position=") + builder.WriteString(fmt.Sprintf("%v", _m.Position)) + builder.WriteByte(')') + return builder.String() +} + +// Items is a parsable slice of Item. +type Items []*Item diff --git a/applications/playlist-api/ent/item/item.go b/applications/playlist-api/ent/item/item.go new file mode 100644 index 00000000..39976c2a --- /dev/null +++ b/applications/playlist-api/ent/item/item.go @@ -0,0 +1,118 @@ +// Code generated by ent, DO NOT EDIT. + +package item + +import ( + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "github.com/google/uuid" +) + +const ( + // Label holds the string label denoting the item type in the database. + Label = "item" + // FieldID holds the string denoting the id field in the database. + FieldID = "id" + // FieldPlaylistID holds the string denoting the playlist_id field in the database. + FieldPlaylistID = "playlist_id" + // FieldTrackID holds the string denoting the track_id field in the database. + FieldTrackID = "track_id" + // FieldPosition holds the string denoting the position field in the database. + FieldPosition = "position" + // EdgePlaylist holds the string denoting the playlist edge name in mutations. + EdgePlaylist = "playlist" + // EdgeTrack holds the string denoting the track edge name in mutations. + EdgeTrack = "track" + // Table holds the table name of the item in the database. + Table = "items" + // PlaylistTable is the table that holds the playlist relation/edge. + PlaylistTable = "items" + // PlaylistInverseTable is the table name for the Playlist entity. + // It exists in this package in order to avoid circular dependency with the "playlist" package. + PlaylistInverseTable = "playlists" + // PlaylistColumn is the table column denoting the playlist relation/edge. + PlaylistColumn = "playlist_id" + // TrackTable is the table that holds the track relation/edge. + TrackTable = "items" + // TrackInverseTable is the table name for the Track entity. + // It exists in this package in order to avoid circular dependency with the "track" package. + TrackInverseTable = "tracks" + // TrackColumn is the table column denoting the track relation/edge. + TrackColumn = "track_id" +) + +// Columns holds all SQL columns for item fields. +var Columns = []string{ + FieldID, + FieldPlaylistID, + FieldTrackID, + FieldPosition, +} + +// 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 ( + // PositionValidator is a validator for the "position" field. It is called by the builders before save. + PositionValidator func(int) error + // DefaultID holds the default value on creation for the "id" field. + DefaultID func() uuid.UUID +) + +// OrderOption defines the ordering options for the Item 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() +} + +// ByPlaylistID orders the results by the playlist_id field. +func ByPlaylistID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldPlaylistID, opts...).ToFunc() +} + +// ByTrackID orders the results by the track_id field. +func ByTrackID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldTrackID, opts...).ToFunc() +} + +// ByPosition orders the results by the position field. +func ByPosition(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldPosition, opts...).ToFunc() +} + +// ByPlaylistField orders the results by playlist field. +func ByPlaylistField(field string, opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newPlaylistStep(), sql.OrderByField(field, opts...)) + } +} + +// ByTrackField orders the results by track field. +func ByTrackField(field string, opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newTrackStep(), sql.OrderByField(field, opts...)) + } +} +func newPlaylistStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(PlaylistInverseTable, FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, PlaylistTable, PlaylistColumn), + ) +} +func newTrackStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(TrackInverseTable, FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, TrackTable, TrackColumn), + ) +} diff --git a/applications/playlist-api/ent/item/where.go b/applications/playlist-api/ent/item/where.go new file mode 100644 index 00000000..1bcf602e --- /dev/null +++ b/applications/playlist-api/ent/item/where.go @@ -0,0 +1,211 @@ +// Code generated by ent, DO NOT EDIT. + +package item + +import ( + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "example.com/playlist-api/ent/predicate" + "github.com/google/uuid" +) + +// ID filters vertices based on their ID field. +func ID(id uuid.UUID) predicate.Item { + return predicate.Item(sql.FieldEQ(FieldID, id)) +} + +// IDEQ applies the EQ predicate on the ID field. +func IDEQ(id uuid.UUID) predicate.Item { + return predicate.Item(sql.FieldEQ(FieldID, id)) +} + +// IDNEQ applies the NEQ predicate on the ID field. +func IDNEQ(id uuid.UUID) predicate.Item { + return predicate.Item(sql.FieldNEQ(FieldID, id)) +} + +// IDIn applies the In predicate on the ID field. +func IDIn(ids ...uuid.UUID) predicate.Item { + return predicate.Item(sql.FieldIn(FieldID, ids...)) +} + +// IDNotIn applies the NotIn predicate on the ID field. +func IDNotIn(ids ...uuid.UUID) predicate.Item { + return predicate.Item(sql.FieldNotIn(FieldID, ids...)) +} + +// IDGT applies the GT predicate on the ID field. +func IDGT(id uuid.UUID) predicate.Item { + return predicate.Item(sql.FieldGT(FieldID, id)) +} + +// IDGTE applies the GTE predicate on the ID field. +func IDGTE(id uuid.UUID) predicate.Item { + return predicate.Item(sql.FieldGTE(FieldID, id)) +} + +// IDLT applies the LT predicate on the ID field. +func IDLT(id uuid.UUID) predicate.Item { + return predicate.Item(sql.FieldLT(FieldID, id)) +} + +// IDLTE applies the LTE predicate on the ID field. +func IDLTE(id uuid.UUID) predicate.Item { + return predicate.Item(sql.FieldLTE(FieldID, id)) +} + +// PlaylistID applies equality check predicate on the "playlist_id" field. It's identical to PlaylistIDEQ. +func PlaylistID(v uuid.UUID) predicate.Item { + return predicate.Item(sql.FieldEQ(FieldPlaylistID, v)) +} + +// TrackID applies equality check predicate on the "track_id" field. It's identical to TrackIDEQ. +func TrackID(v uuid.UUID) predicate.Item { + return predicate.Item(sql.FieldEQ(FieldTrackID, v)) +} + +// Position applies equality check predicate on the "position" field. It's identical to PositionEQ. +func Position(v int) predicate.Item { + return predicate.Item(sql.FieldEQ(FieldPosition, v)) +} + +// PlaylistIDEQ applies the EQ predicate on the "playlist_id" field. +func PlaylistIDEQ(v uuid.UUID) predicate.Item { + return predicate.Item(sql.FieldEQ(FieldPlaylistID, v)) +} + +// PlaylistIDNEQ applies the NEQ predicate on the "playlist_id" field. +func PlaylistIDNEQ(v uuid.UUID) predicate.Item { + return predicate.Item(sql.FieldNEQ(FieldPlaylistID, v)) +} + +// PlaylistIDIn applies the In predicate on the "playlist_id" field. +func PlaylistIDIn(vs ...uuid.UUID) predicate.Item { + return predicate.Item(sql.FieldIn(FieldPlaylistID, vs...)) +} + +// PlaylistIDNotIn applies the NotIn predicate on the "playlist_id" field. +func PlaylistIDNotIn(vs ...uuid.UUID) predicate.Item { + return predicate.Item(sql.FieldNotIn(FieldPlaylistID, vs...)) +} + +// TrackIDEQ applies the EQ predicate on the "track_id" field. +func TrackIDEQ(v uuid.UUID) predicate.Item { + return predicate.Item(sql.FieldEQ(FieldTrackID, v)) +} + +// TrackIDNEQ applies the NEQ predicate on the "track_id" field. +func TrackIDNEQ(v uuid.UUID) predicate.Item { + return predicate.Item(sql.FieldNEQ(FieldTrackID, v)) +} + +// TrackIDIn applies the In predicate on the "track_id" field. +func TrackIDIn(vs ...uuid.UUID) predicate.Item { + return predicate.Item(sql.FieldIn(FieldTrackID, vs...)) +} + +// TrackIDNotIn applies the NotIn predicate on the "track_id" field. +func TrackIDNotIn(vs ...uuid.UUID) predicate.Item { + return predicate.Item(sql.FieldNotIn(FieldTrackID, vs...)) +} + +// PositionEQ applies the EQ predicate on the "position" field. +func PositionEQ(v int) predicate.Item { + return predicate.Item(sql.FieldEQ(FieldPosition, v)) +} + +// PositionNEQ applies the NEQ predicate on the "position" field. +func PositionNEQ(v int) predicate.Item { + return predicate.Item(sql.FieldNEQ(FieldPosition, v)) +} + +// PositionIn applies the In predicate on the "position" field. +func PositionIn(vs ...int) predicate.Item { + return predicate.Item(sql.FieldIn(FieldPosition, vs...)) +} + +// PositionNotIn applies the NotIn predicate on the "position" field. +func PositionNotIn(vs ...int) predicate.Item { + return predicate.Item(sql.FieldNotIn(FieldPosition, vs...)) +} + +// PositionGT applies the GT predicate on the "position" field. +func PositionGT(v int) predicate.Item { + return predicate.Item(sql.FieldGT(FieldPosition, v)) +} + +// PositionGTE applies the GTE predicate on the "position" field. +func PositionGTE(v int) predicate.Item { + return predicate.Item(sql.FieldGTE(FieldPosition, v)) +} + +// PositionLT applies the LT predicate on the "position" field. +func PositionLT(v int) predicate.Item { + return predicate.Item(sql.FieldLT(FieldPosition, v)) +} + +// PositionLTE applies the LTE predicate on the "position" field. +func PositionLTE(v int) predicate.Item { + return predicate.Item(sql.FieldLTE(FieldPosition, v)) +} + +// HasPlaylist applies the HasEdge predicate on the "playlist" edge. +func HasPlaylist() predicate.Item { + return predicate.Item(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, PlaylistTable, PlaylistColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasPlaylistWith applies the HasEdge predicate on the "playlist" edge with a given conditions (other predicates). +func HasPlaylistWith(preds ...predicate.Playlist) predicate.Item { + return predicate.Item(func(s *sql.Selector) { + step := newPlaylistStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + +// HasTrack applies the HasEdge predicate on the "track" edge. +func HasTrack() predicate.Item { + return predicate.Item(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, TrackTable, TrackColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasTrackWith applies the HasEdge predicate on the "track" edge with a given conditions (other predicates). +func HasTrackWith(preds ...predicate.Track) predicate.Item { + return predicate.Item(func(s *sql.Selector) { + step := newTrackStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + +// And groups predicates with the AND operator between them. +func And(predicates ...predicate.Item) predicate.Item { + return predicate.Item(sql.AndPredicates(predicates...)) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.Item) predicate.Item { + return predicate.Item(sql.OrPredicates(predicates...)) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.Item) predicate.Item { + return predicate.Item(sql.NotPredicates(p)) +} diff --git a/applications/playlist-api/ent/item_create.go b/applications/playlist-api/ent/item_create.go new file mode 100644 index 00000000..32d19258 --- /dev/null +++ b/applications/playlist-api/ent/item_create.go @@ -0,0 +1,288 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "example.com/playlist-api/ent/item" + "example.com/playlist-api/ent/playlist" + "example.com/playlist-api/ent/track" + "github.com/google/uuid" +) + +// ItemCreate is the builder for creating a Item entity. +type ItemCreate struct { + config + mutation *ItemMutation + hooks []Hook +} + +// SetPlaylistID sets the "playlist_id" field. +func (_c *ItemCreate) SetPlaylistID(v uuid.UUID) *ItemCreate { + _c.mutation.SetPlaylistID(v) + return _c +} + +// SetTrackID sets the "track_id" field. +func (_c *ItemCreate) SetTrackID(v uuid.UUID) *ItemCreate { + _c.mutation.SetTrackID(v) + return _c +} + +// SetPosition sets the "position" field. +func (_c *ItemCreate) SetPosition(v int) *ItemCreate { + _c.mutation.SetPosition(v) + return _c +} + +// SetID sets the "id" field. +func (_c *ItemCreate) SetID(v uuid.UUID) *ItemCreate { + _c.mutation.SetID(v) + return _c +} + +// SetNillableID sets the "id" field if the given value is not nil. +func (_c *ItemCreate) SetNillableID(v *uuid.UUID) *ItemCreate { + if v != nil { + _c.SetID(*v) + } + return _c +} + +// SetPlaylist sets the "playlist" edge to the Playlist entity. +func (_c *ItemCreate) SetPlaylist(v *Playlist) *ItemCreate { + return _c.SetPlaylistID(v.ID) +} + +// SetTrack sets the "track" edge to the Track entity. +func (_c *ItemCreate) SetTrack(v *Track) *ItemCreate { + return _c.SetTrackID(v.ID) +} + +// Mutation returns the ItemMutation object of the builder. +func (_c *ItemCreate) Mutation() *ItemMutation { + return _c.mutation +} + +// Save creates the Item in the database. +func (_c *ItemCreate) Save(ctx context.Context) (*Item, error) { + _c.defaults() + return withHooks(ctx, _c.sqlSave, _c.mutation, _c.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (_c *ItemCreate) SaveX(ctx context.Context) *Item { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *ItemCreate) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *ItemCreate) 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 *ItemCreate) defaults() { + if _, ok := _c.mutation.ID(); !ok { + v := item.DefaultID() + _c.mutation.SetID(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_c *ItemCreate) check() error { + if _, ok := _c.mutation.PlaylistID(); !ok { + return &ValidationError{Name: "playlist_id", err: errors.New(`ent: missing required field "Item.playlist_id"`)} + } + if _, ok := _c.mutation.TrackID(); !ok { + return &ValidationError{Name: "track_id", err: errors.New(`ent: missing required field "Item.track_id"`)} + } + if _, ok := _c.mutation.Position(); !ok { + return &ValidationError{Name: "position", err: errors.New(`ent: missing required field "Item.position"`)} + } + if v, ok := _c.mutation.Position(); ok { + if err := item.PositionValidator(v); err != nil { + return &ValidationError{Name: "position", err: fmt.Errorf(`ent: validator failed for field "Item.position": %w`, err)} + } + } + if len(_c.mutation.PlaylistIDs()) == 0 { + return &ValidationError{Name: "playlist", err: errors.New(`ent: missing required edge "Item.playlist"`)} + } + if len(_c.mutation.TrackIDs()) == 0 { + return &ValidationError{Name: "track", err: errors.New(`ent: missing required edge "Item.track"`)} + } + return nil +} + +func (_c *ItemCreate) sqlSave(ctx context.Context) (*Item, 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 *ItemCreate) createSpec() (*Item, *sqlgraph.CreateSpec) { + var ( + _node = &Item{config: _c.config} + _spec = sqlgraph.NewCreateSpec(item.Table, sqlgraph.NewFieldSpec(item.FieldID, field.TypeUUID)) + ) + if id, ok := _c.mutation.ID(); ok { + _node.ID = id + _spec.ID.Value = &id + } + if value, ok := _c.mutation.Position(); ok { + _spec.SetField(item.FieldPosition, field.TypeInt, value) + _node.Position = value + } + if nodes := _c.mutation.PlaylistIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: item.PlaylistTable, + Columns: []string{item.PlaylistColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(playlist.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _node.PlaylistID = nodes[0] + _spec.Edges = append(_spec.Edges, edge) + } + if nodes := _c.mutation.TrackIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: item.TrackTable, + Columns: []string{item.TrackColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(track.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _node.TrackID = nodes[0] + _spec.Edges = append(_spec.Edges, edge) + } + return _node, _spec +} + +// ItemCreateBulk is the builder for creating many Item entities in bulk. +type ItemCreateBulk struct { + config + err error + builders []*ItemCreate +} + +// Save creates the Item entities in the database. +func (_c *ItemCreateBulk) Save(ctx context.Context) ([]*Item, error) { + if _c.err != nil { + return nil, _c.err + } + specs := make([]*sqlgraph.CreateSpec, len(_c.builders)) + nodes := make([]*Item, 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.(*ItemMutation) + 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} + } + } + } + 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 *ItemCreateBulk) SaveX(ctx context.Context) []*Item { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *ItemCreateBulk) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *ItemCreateBulk) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/applications/playlist-api/ent/item_delete.go b/applications/playlist-api/ent/item_delete.go new file mode 100644 index 00000000..6a78701b --- /dev/null +++ b/applications/playlist-api/ent/item_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" + "example.com/playlist-api/ent/item" + "example.com/playlist-api/ent/predicate" +) + +// ItemDelete is the builder for deleting a Item entity. +type ItemDelete struct { + config + hooks []Hook + mutation *ItemMutation +} + +// Where appends a list predicates to the ItemDelete builder. +func (_d *ItemDelete) Where(ps ...predicate.Item) *ItemDelete { + _d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (_d *ItemDelete) 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 *ItemDelete) ExecX(ctx context.Context) int { + n, err := _d.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (_d *ItemDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(item.Table, sqlgraph.NewFieldSpec(item.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 +} + +// ItemDeleteOne is the builder for deleting a single Item entity. +type ItemDeleteOne struct { + _d *ItemDelete +} + +// Where appends a list predicates to the ItemDelete builder. +func (_d *ItemDeleteOne) Where(ps ...predicate.Item) *ItemDeleteOne { + _d._d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query. +func (_d *ItemDeleteOne) Exec(ctx context.Context) error { + n, err := _d._d.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{item.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *ItemDeleteOne) ExecX(ctx context.Context) { + if err := _d.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/applications/playlist-api/ent/item_query.go b/applications/playlist-api/ent/item_query.go new file mode 100644 index 00000000..712f937b --- /dev/null +++ b/applications/playlist-api/ent/item_query.go @@ -0,0 +1,719 @@ +// 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" + "example.com/playlist-api/ent/item" + "example.com/playlist-api/ent/playlist" + "example.com/playlist-api/ent/predicate" + "example.com/playlist-api/ent/track" + "github.com/google/uuid" +) + +// ItemQuery is the builder for querying Item entities. +type ItemQuery struct { + config + ctx *QueryContext + order []item.OrderOption + inters []Interceptor + predicates []predicate.Item + withPlaylist *PlaylistQuery + withTrack *TrackQuery + 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 ItemQuery builder. +func (_q *ItemQuery) Where(ps ...predicate.Item) *ItemQuery { + _q.predicates = append(_q.predicates, ps...) + return _q +} + +// Limit the number of records to be returned by this query. +func (_q *ItemQuery) Limit(limit int) *ItemQuery { + _q.ctx.Limit = &limit + return _q +} + +// Offset to start from. +func (_q *ItemQuery) Offset(offset int) *ItemQuery { + _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 *ItemQuery) Unique(unique bool) *ItemQuery { + _q.ctx.Unique = &unique + return _q +} + +// Order specifies how the records should be ordered. +func (_q *ItemQuery) Order(o ...item.OrderOption) *ItemQuery { + _q.order = append(_q.order, o...) + return _q +} + +// QueryPlaylist chains the current query on the "playlist" edge. +func (_q *ItemQuery) QueryPlaylist() *PlaylistQuery { + query := (&PlaylistClient{config: _q.config}).Query() + query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + selector := _q.sqlQuery(ctx) + if err := selector.Err(); err != nil { + return nil, err + } + step := sqlgraph.NewStep( + sqlgraph.From(item.Table, item.FieldID, selector), + sqlgraph.To(playlist.Table, playlist.FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, item.PlaylistTable, item.PlaylistColumn), + ) + fromU = sqlgraph.SetNeighbors(_q.driver.Dialect(), step) + return fromU, nil + } + return query +} + +// QueryTrack chains the current query on the "track" edge. +func (_q *ItemQuery) QueryTrack() *TrackQuery { + query := (&TrackClient{config: _q.config}).Query() + query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + selector := _q.sqlQuery(ctx) + if err := selector.Err(); err != nil { + return nil, err + } + step := sqlgraph.NewStep( + sqlgraph.From(item.Table, item.FieldID, selector), + sqlgraph.To(track.Table, track.FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, item.TrackTable, item.TrackColumn), + ) + fromU = sqlgraph.SetNeighbors(_q.driver.Dialect(), step) + return fromU, nil + } + return query +} + +// First returns the first Item entity from the query. +// Returns a *NotFoundError when no Item was found. +func (_q *ItemQuery) First(ctx context.Context) (*Item, 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{item.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (_q *ItemQuery) FirstX(ctx context.Context) *Item { + node, err := _q.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// FirstID returns the first Item ID from the query. +// Returns a *NotFoundError when no Item ID was found. +func (_q *ItemQuery) 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{item.Label} + return + } + return ids[0], nil +} + +// FirstIDX is like FirstID, but panics if an error occurs. +func (_q *ItemQuery) FirstIDX(ctx context.Context) uuid.UUID { + id, err := _q.FirstID(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return id +} + +// Only returns a single Item entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one Item entity is found. +// Returns a *NotFoundError when no Item entities are found. +func (_q *ItemQuery) Only(ctx context.Context) (*Item, 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{item.Label} + default: + return nil, &NotSingularError{item.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (_q *ItemQuery) OnlyX(ctx context.Context) *Item { + node, err := _q.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// OnlyID is like Only, but returns the only Item ID in the query. +// Returns a *NotSingularError when more than one Item ID is found. +// Returns a *NotFoundError when no entities are found. +func (_q *ItemQuery) 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{item.Label} + default: + err = &NotSingularError{item.Label} + } + return +} + +// OnlyIDX is like OnlyID, but panics if an error occurs. +func (_q *ItemQuery) 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 Items. +func (_q *ItemQuery) All(ctx context.Context) ([]*Item, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryAll) + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*Item, *ItemQuery]() + return withInterceptors[[]*Item](ctx, _q, qr, _q.inters) +} + +// AllX is like All, but panics if an error occurs. +func (_q *ItemQuery) AllX(ctx context.Context) []*Item { + nodes, err := _q.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// IDs executes the query and returns a list of Item IDs. +func (_q *ItemQuery) 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(item.FieldID).Scan(ctx, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// IDsX is like IDs, but panics if an error occurs. +func (_q *ItemQuery) 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 *ItemQuery) 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[*ItemQuery](), _q.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (_q *ItemQuery) 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 *ItemQuery) 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 *ItemQuery) ExistX(ctx context.Context) bool { + exist, err := _q.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the ItemQuery 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 *ItemQuery) Clone() *ItemQuery { + if _q == nil { + return nil + } + return &ItemQuery{ + config: _q.config, + ctx: _q.ctx.Clone(), + order: append([]item.OrderOption{}, _q.order...), + inters: append([]Interceptor{}, _q.inters...), + predicates: append([]predicate.Item{}, _q.predicates...), + withPlaylist: _q.withPlaylist.Clone(), + withTrack: _q.withTrack.Clone(), + // clone intermediate query. + sql: _q.sql.Clone(), + path: _q.path, + } +} + +// WithPlaylist tells the query-builder to eager-load the nodes that are connected to +// the "playlist" edge. The optional arguments are used to configure the query builder of the edge. +func (_q *ItemQuery) WithPlaylist(opts ...func(*PlaylistQuery)) *ItemQuery { + query := (&PlaylistClient{config: _q.config}).Query() + for _, opt := range opts { + opt(query) + } + _q.withPlaylist = query + return _q +} + +// WithTrack tells the query-builder to eager-load the nodes that are connected to +// the "track" edge. The optional arguments are used to configure the query builder of the edge. +func (_q *ItemQuery) WithTrack(opts ...func(*TrackQuery)) *ItemQuery { + query := (&TrackClient{config: _q.config}).Query() + for _, opt := range opts { + opt(query) + } + _q.withTrack = query + return _q +} + +// 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 { +// PlaylistID uuid.UUID `json:"playlist_id,omitempty"` +// Count int `json:"count,omitempty"` +// } +// +// client.Item.Query(). +// GroupBy(item.FieldPlaylistID). +// Aggregate(ent.Count()). +// Scan(ctx, &v) +func (_q *ItemQuery) GroupBy(field string, fields ...string) *ItemGroupBy { + _q.ctx.Fields = append([]string{field}, fields...) + grbuild := &ItemGroupBy{build: _q} + grbuild.flds = &_q.ctx.Fields + grbuild.label = item.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 { +// PlaylistID uuid.UUID `json:"playlist_id,omitempty"` +// } +// +// client.Item.Query(). +// Select(item.FieldPlaylistID). +// Scan(ctx, &v) +func (_q *ItemQuery) Select(fields ...string) *ItemSelect { + _q.ctx.Fields = append(_q.ctx.Fields, fields...) + sbuild := &ItemSelect{ItemQuery: _q} + sbuild.label = item.Label + sbuild.flds, sbuild.scan = &_q.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a ItemSelect configured with the given aggregations. +func (_q *ItemQuery) Aggregate(fns ...AggregateFunc) *ItemSelect { + return _q.Select().Aggregate(fns...) +} + +func (_q *ItemQuery) 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 !item.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 *ItemQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*Item, error) { + var ( + nodes = []*Item{} + _spec = _q.querySpec() + loadedTypes = [2]bool{ + _q.withPlaylist != nil, + _q.withTrack != nil, + } + ) + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*Item).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &Item{config: _q.config} + nodes = append(nodes, node) + 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) + } + if err := sqlgraph.QueryNodes(ctx, _q.driver, _spec); err != nil { + return nil, err + } + if len(nodes) == 0 { + return nodes, nil + } + if query := _q.withPlaylist; query != nil { + if err := _q.loadPlaylist(ctx, query, nodes, nil, + func(n *Item, e *Playlist) { n.Edges.Playlist = e }); err != nil { + return nil, err + } + } + if query := _q.withTrack; query != nil { + if err := _q.loadTrack(ctx, query, nodes, nil, + func(n *Item, e *Track) { n.Edges.Track = e }); err != nil { + return nil, err + } + } + return nodes, nil +} + +func (_q *ItemQuery) loadPlaylist(ctx context.Context, query *PlaylistQuery, nodes []*Item, init func(*Item), assign func(*Item, *Playlist)) error { + ids := make([]uuid.UUID, 0, len(nodes)) + nodeids := make(map[uuid.UUID][]*Item) + for i := range nodes { + fk := nodes[i].PlaylistID + if _, ok := nodeids[fk]; !ok { + ids = append(ids, fk) + } + nodeids[fk] = append(nodeids[fk], nodes[i]) + } + if len(ids) == 0 { + return nil + } + query.Where(playlist.IDIn(ids...)) + neighbors, err := query.All(ctx) + if err != nil { + return err + } + for _, n := range neighbors { + nodes, ok := nodeids[n.ID] + if !ok { + return fmt.Errorf(`unexpected foreign-key "playlist_id" returned %v`, n.ID) + } + for i := range nodes { + assign(nodes[i], n) + } + } + return nil +} +func (_q *ItemQuery) loadTrack(ctx context.Context, query *TrackQuery, nodes []*Item, init func(*Item), assign func(*Item, *Track)) error { + ids := make([]uuid.UUID, 0, len(nodes)) + nodeids := make(map[uuid.UUID][]*Item) + for i := range nodes { + fk := nodes[i].TrackID + if _, ok := nodeids[fk]; !ok { + ids = append(ids, fk) + } + nodeids[fk] = append(nodeids[fk], nodes[i]) + } + if len(ids) == 0 { + return nil + } + query.Where(track.IDIn(ids...)) + neighbors, err := query.All(ctx) + if err != nil { + return err + } + for _, n := range neighbors { + nodes, ok := nodeids[n.ID] + if !ok { + return fmt.Errorf(`unexpected foreign-key "track_id" returned %v`, n.ID) + } + for i := range nodes { + assign(nodes[i], n) + } + } + return nil +} + +func (_q *ItemQuery) 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 *ItemQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(item.Table, item.Columns, sqlgraph.NewFieldSpec(item.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, item.FieldID) + for i := range fields { + if fields[i] != item.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, fields[i]) + } + } + if _q.withPlaylist != nil { + _spec.Node.AddColumnOnce(item.FieldPlaylistID) + } + if _q.withTrack != nil { + _spec.Node.AddColumnOnce(item.FieldTrackID) + } + } + 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 *ItemQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(_q.driver.Dialect()) + t1 := builder.Table(item.Table) + columns := _q.ctx.Fields + if len(columns) == 0 { + columns = item.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 *ItemQuery) ForUpdate(opts ...sql.LockOption) *ItemQuery { + 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 *ItemQuery) ForShare(opts ...sql.LockOption) *ItemQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForShare(opts...) + }) + return _q +} + +// ItemGroupBy is the group-by builder for Item entities. +type ItemGroupBy struct { + selector + build *ItemQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (_g *ItemGroupBy) Aggregate(fns ...AggregateFunc) *ItemGroupBy { + _g.fns = append(_g.fns, fns...) + return _g +} + +// Scan applies the selector query and scans the result into the given value. +func (_g *ItemGroupBy) 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[*ItemQuery, *ItemGroupBy](ctx, _g.build, _g, _g.build.inters, v) +} + +func (_g *ItemGroupBy) sqlScan(ctx context.Context, root *ItemQuery, 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) +} + +// ItemSelect is the builder for selecting fields of Item entities. +type ItemSelect struct { + *ItemQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (_s *ItemSelect) Aggregate(fns ...AggregateFunc) *ItemSelect { + _s.fns = append(_s.fns, fns...) + return _s +} + +// Scan applies the selector query and scans the result into the given value. +func (_s *ItemSelect) 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[*ItemQuery, *ItemSelect](ctx, _s.ItemQuery, _s, _s.inters, v) +} + +func (_s *ItemSelect) sqlScan(ctx context.Context, root *ItemQuery, 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/applications/playlist-api/ent/item_update.go b/applications/playlist-api/ent/item_update.go new file mode 100644 index 00000000..9d01bd01 --- /dev/null +++ b/applications/playlist-api/ent/item_update.go @@ -0,0 +1,267 @@ +// 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" + "example.com/playlist-api/ent/item" + "example.com/playlist-api/ent/predicate" +) + +// ItemUpdate is the builder for updating Item entities. +type ItemUpdate struct { + config + hooks []Hook + mutation *ItemMutation +} + +// Where appends a list predicates to the ItemUpdate builder. +func (_u *ItemUpdate) Where(ps ...predicate.Item) *ItemUpdate { + _u.mutation.Where(ps...) + return _u +} + +// SetPosition sets the "position" field. +func (_u *ItemUpdate) SetPosition(v int) *ItemUpdate { + _u.mutation.ResetPosition() + _u.mutation.SetPosition(v) + return _u +} + +// SetNillablePosition sets the "position" field if the given value is not nil. +func (_u *ItemUpdate) SetNillablePosition(v *int) *ItemUpdate { + if v != nil { + _u.SetPosition(*v) + } + return _u +} + +// AddPosition adds value to the "position" field. +func (_u *ItemUpdate) AddPosition(v int) *ItemUpdate { + _u.mutation.AddPosition(v) + return _u +} + +// Mutation returns the ItemMutation object of the builder. +func (_u *ItemUpdate) Mutation() *ItemMutation { + return _u.mutation +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (_u *ItemUpdate) 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 *ItemUpdate) SaveX(ctx context.Context) int { + affected, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (_u *ItemUpdate) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *ItemUpdate) 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 *ItemUpdate) check() error { + if v, ok := _u.mutation.Position(); ok { + if err := item.PositionValidator(v); err != nil { + return &ValidationError{Name: "position", err: fmt.Errorf(`ent: validator failed for field "Item.position": %w`, err)} + } + } + if _u.mutation.PlaylistCleared() && len(_u.mutation.PlaylistIDs()) > 0 { + return errors.New(`ent: clearing a required unique edge "Item.playlist"`) + } + if _u.mutation.TrackCleared() && len(_u.mutation.TrackIDs()) > 0 { + return errors.New(`ent: clearing a required unique edge "Item.track"`) + } + return nil +} + +func (_u *ItemUpdate) sqlSave(ctx context.Context) (_node int, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(item.Table, item.Columns, sqlgraph.NewFieldSpec(item.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.Position(); ok { + _spec.SetField(item.FieldPosition, field.TypeInt, value) + } + if value, ok := _u.mutation.AddedPosition(); ok { + _spec.AddField(item.FieldPosition, field.TypeInt, value) + } + if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{item.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + _u.mutation.done = true + return _node, nil +} + +// ItemUpdateOne is the builder for updating a single Item entity. +type ItemUpdateOne struct { + config + fields []string + hooks []Hook + mutation *ItemMutation +} + +// SetPosition sets the "position" field. +func (_u *ItemUpdateOne) SetPosition(v int) *ItemUpdateOne { + _u.mutation.ResetPosition() + _u.mutation.SetPosition(v) + return _u +} + +// SetNillablePosition sets the "position" field if the given value is not nil. +func (_u *ItemUpdateOne) SetNillablePosition(v *int) *ItemUpdateOne { + if v != nil { + _u.SetPosition(*v) + } + return _u +} + +// AddPosition adds value to the "position" field. +func (_u *ItemUpdateOne) AddPosition(v int) *ItemUpdateOne { + _u.mutation.AddPosition(v) + return _u +} + +// Mutation returns the ItemMutation object of the builder. +func (_u *ItemUpdateOne) Mutation() *ItemMutation { + return _u.mutation +} + +// Where appends a list predicates to the ItemUpdate builder. +func (_u *ItemUpdateOne) Where(ps ...predicate.Item) *ItemUpdateOne { + _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 *ItemUpdateOne) Select(field string, fields ...string) *ItemUpdateOne { + _u.fields = append([]string{field}, fields...) + return _u +} + +// Save executes the query and returns the updated Item entity. +func (_u *ItemUpdateOne) Save(ctx context.Context) (*Item, error) { + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *ItemUpdateOne) SaveX(ctx context.Context) *Item { + node, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (_u *ItemUpdateOne) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *ItemUpdateOne) 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 *ItemUpdateOne) check() error { + if v, ok := _u.mutation.Position(); ok { + if err := item.PositionValidator(v); err != nil { + return &ValidationError{Name: "position", err: fmt.Errorf(`ent: validator failed for field "Item.position": %w`, err)} + } + } + if _u.mutation.PlaylistCleared() && len(_u.mutation.PlaylistIDs()) > 0 { + return errors.New(`ent: clearing a required unique edge "Item.playlist"`) + } + if _u.mutation.TrackCleared() && len(_u.mutation.TrackIDs()) > 0 { + return errors.New(`ent: clearing a required unique edge "Item.track"`) + } + return nil +} + +func (_u *ItemUpdateOne) sqlSave(ctx context.Context) (_node *Item, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(item.Table, item.Columns, sqlgraph.NewFieldSpec(item.FieldID, field.TypeUUID)) + id, ok := _u.mutation.ID() + if !ok { + return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "Item.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, item.FieldID) + for _, f := range fields { + if !item.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + if f != item.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.Position(); ok { + _spec.SetField(item.FieldPosition, field.TypeInt, value) + } + if value, ok := _u.mutation.AddedPosition(); ok { + _spec.AddField(item.FieldPosition, field.TypeInt, value) + } + _node = &Item{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{item.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/applications/playlist-api/ent/migrate/migrate.go b/applications/playlist-api/ent/migrate/migrate.go new file mode 100644 index 00000000..1956a6bf --- /dev/null +++ b/applications/playlist-api/ent/migrate/migrate.go @@ -0,0 +1,64 @@ +// Code generated by ent, DO NOT EDIT. + +package migrate + +import ( + "context" + "fmt" + "io" + + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql/schema" +) + +var ( + // WithGlobalUniqueID sets the universal ids options to the migration. + // If this option is enabled, ent migration will allocate a 1<<32 range + // for the ids of each entity (table). + // Note that this option cannot be applied on tables that already exist. + WithGlobalUniqueID = schema.WithGlobalUniqueID + // WithDropColumn sets the drop column option to the migration. + // If this option is enabled, ent migration will drop old columns + // that were used for both fields and edges. This defaults to false. + WithDropColumn = schema.WithDropColumn + // WithDropIndex sets the drop index option to the migration. + // If this option is enabled, ent migration will drop old indexes + // that were defined in the schema. This defaults to false. + // Note that unique constraints are defined using `UNIQUE INDEX`, + // and therefore, it's recommended to enable this option to get more + // flexibility in the schema changes. + WithDropIndex = schema.WithDropIndex + // WithForeignKeys enables creating foreign-key in schema DDL. This defaults to true. + WithForeignKeys = schema.WithForeignKeys +) + +// Schema is the API for creating, migrating and dropping a schema. +type Schema struct { + drv dialect.Driver +} + +// NewSchema creates a new schema client. +func NewSchema(drv dialect.Driver) *Schema { return &Schema{drv: drv} } + +// Create creates all schema resources. +func (s *Schema) Create(ctx context.Context, opts ...schema.MigrateOption) error { + return Create(ctx, s, Tables, opts...) +} + +// Create creates all table resources using the given schema driver. +func Create(ctx context.Context, s *Schema, tables []*schema.Table, opts ...schema.MigrateOption) error { + migrate, err := schema.NewMigrate(s.drv, opts...) + if err != nil { + return fmt.Errorf("ent/migrate: %w", err) + } + return migrate.Create(ctx, tables...) +} + +// WriteTo writes the schema changes to w instead of running them against the database. +// +// if err := client.Schema.WriteTo(context.Background(), os.Stdout); err != nil { +// log.Fatal(err) +// } +func (s *Schema) WriteTo(ctx context.Context, w io.Writer, opts ...schema.MigrateOption) error { + return Create(ctx, &Schema{drv: &schema.WriteDriver{Writer: w, Driver: s.drv}}, Tables, opts...) +} diff --git a/applications/playlist-api/ent/migrate/schema.go b/applications/playlist-api/ent/migrate/schema.go new file mode 100644 index 00000000..730eb86b --- /dev/null +++ b/applications/playlist-api/ent/migrate/schema.go @@ -0,0 +1,114 @@ +// Code generated by ent, DO NOT EDIT. + +package migrate + +import ( + "entgo.io/ent/dialect/sql/schema" + "entgo.io/ent/schema/field" +) + +var ( + // AccountsColumns holds the columns for the "accounts" table. + AccountsColumns = []*schema.Column{ + {Name: "id", Type: field.TypeUUID}, + } + // AccountsTable holds the schema information for the "accounts" table. + AccountsTable = &schema.Table{ + Name: "accounts", + Columns: AccountsColumns, + PrimaryKey: []*schema.Column{AccountsColumns[0]}, + } + // ItemsColumns holds the columns for the "items" table. + ItemsColumns = []*schema.Column{ + {Name: "id", Type: field.TypeUUID}, + {Name: "position", Type: field.TypeInt}, + {Name: "playlist_id", Type: field.TypeUUID}, + {Name: "track_id", Type: field.TypeUUID}, + } + // ItemsTable holds the schema information for the "items" table. + ItemsTable = &schema.Table{ + Name: "items", + Columns: ItemsColumns, + PrimaryKey: []*schema.Column{ItemsColumns[0]}, + ForeignKeys: []*schema.ForeignKey{ + { + Symbol: "items_playlists_items", + Columns: []*schema.Column{ItemsColumns[2]}, + RefColumns: []*schema.Column{PlaylistsColumns[0]}, + OnDelete: schema.NoAction, + }, + { + Symbol: "items_tracks_items", + Columns: []*schema.Column{ItemsColumns[3]}, + RefColumns: []*schema.Column{TracksColumns[0]}, + OnDelete: schema.NoAction, + }, + }, + Indexes: []*schema.Index{ + { + Name: "item_playlist_id_track_id", + Unique: true, + Columns: []*schema.Column{ItemsColumns[2], ItemsColumns[3]}, + }, + { + Name: "items_playlist_position", + Unique: true, + Columns: []*schema.Column{ItemsColumns[2], ItemsColumns[1]}, + }, + }, + } + // PlaylistsColumns holds the columns for the "playlists" table. + PlaylistsColumns = []*schema.Column{ + {Name: "id", Type: field.TypeUUID}, + {Name: "name", Type: field.TypeString, Size: 80}, + {Name: "revision", Type: field.TypeInt, Default: 1}, + {Name: "owner_id", Type: field.TypeUUID}, + } + // PlaylistsTable holds the schema information for the "playlists" table. + PlaylistsTable = &schema.Table{ + Name: "playlists", + Columns: PlaylistsColumns, + PrimaryKey: []*schema.Column{PlaylistsColumns[0]}, + ForeignKeys: []*schema.ForeignKey{ + { + Symbol: "playlists_accounts_playlists", + Columns: []*schema.Column{PlaylistsColumns[3]}, + RefColumns: []*schema.Column{AccountsColumns[0]}, + OnDelete: schema.NoAction, + }, + }, + Indexes: []*schema.Index{ + { + Name: "playlist_owner_id_id", + Unique: false, + Columns: []*schema.Column{PlaylistsColumns[3], PlaylistsColumns[0]}, + }, + }, + } + // TracksColumns holds the columns for the "tracks" table. + TracksColumns = []*schema.Column{ + {Name: "id", Type: field.TypeUUID}, + {Name: "title", Type: field.TypeString, Size: 80}, + {Name: "artist", Type: field.TypeString, Size: 80}, + {Name: "duration_seconds", Type: field.TypeInt}, + } + // TracksTable holds the schema information for the "tracks" table. + TracksTable = &schema.Table{ + Name: "tracks", + Columns: TracksColumns, + PrimaryKey: []*schema.Column{TracksColumns[0]}, + } + // Tables holds all the tables in the schema. + Tables = []*schema.Table{ + AccountsTable, + ItemsTable, + PlaylistsTable, + TracksTable, + } +) + +func init() { + ItemsTable.ForeignKeys[0].RefTable = PlaylistsTable + ItemsTable.ForeignKeys[1].RefTable = TracksTable + PlaylistsTable.ForeignKeys[0].RefTable = AccountsTable +} diff --git a/applications/playlist-api/ent/mutation.go b/applications/playlist-api/ent/mutation.go new file mode 100644 index 00000000..947da66a --- /dev/null +++ b/applications/playlist-api/ent/mutation.go @@ -0,0 +1,2157 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "sync" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "example.com/playlist-api/ent/account" + "example.com/playlist-api/ent/item" + "example.com/playlist-api/ent/playlist" + "example.com/playlist-api/ent/predicate" + "example.com/playlist-api/ent/track" + "github.com/google/uuid" +) + +const ( + // Operation types. + OpCreate = ent.OpCreate + OpDelete = ent.OpDelete + OpDeleteOne = ent.OpDeleteOne + OpUpdate = ent.OpUpdate + OpUpdateOne = ent.OpUpdateOne + + // Node types. + TypeAccount = "Account" + TypeItem = "Item" + TypePlaylist = "Playlist" + TypeTrack = "Track" +) + +// AccountMutation represents an operation that mutates the Account nodes in the graph. +type AccountMutation struct { + config + op Op + typ string + id *uuid.UUID + clearedFields map[string]struct{} + playlists map[uuid.UUID]struct{} + removedplaylists map[uuid.UUID]struct{} + clearedplaylists bool + done bool + oldValue func(context.Context) (*Account, error) + predicates []predicate.Account +} + +var _ ent.Mutation = (*AccountMutation)(nil) + +// accountOption allows management of the mutation configuration using functional options. +type accountOption func(*AccountMutation) + +// newAccountMutation creates new mutation for the Account entity. +func newAccountMutation(c config, op Op, opts ...accountOption) *AccountMutation { + m := &AccountMutation{ + config: c, + op: op, + typ: TypeAccount, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// withAccountID sets the ID field of the mutation. +func withAccountID(id uuid.UUID) accountOption { + return func(m *AccountMutation) { + var ( + err error + once sync.Once + value *Account + ) + m.oldValue = func(ctx context.Context) (*Account, error) { + once.Do(func() { + if m.done { + err = errors.New("querying old values post mutation is not allowed") + } else { + value, err = m.Client().Account.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } +} + +// withAccount sets the old Account of the mutation. +func withAccount(node *Account) accountOption { + return func(m *AccountMutation) { + m.oldValue = func(context.Context) (*Account, 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 AccountMutation) 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 AccountMutation) 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 Account entities. +func (m *AccountMutation) 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 *AccountMutation) 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 *AccountMutation) 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().Account.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } +} + +// AddPlaylistIDs adds the "playlists" edge to the Playlist entity by ids. +func (m *AccountMutation) AddPlaylistIDs(ids ...uuid.UUID) { + if m.playlists == nil { + m.playlists = make(map[uuid.UUID]struct{}) + } + for i := range ids { + m.playlists[ids[i]] = struct{}{} + } +} + +// ClearPlaylists clears the "playlists" edge to the Playlist entity. +func (m *AccountMutation) ClearPlaylists() { + m.clearedplaylists = true +} + +// PlaylistsCleared reports if the "playlists" edge to the Playlist entity was cleared. +func (m *AccountMutation) PlaylistsCleared() bool { + return m.clearedplaylists +} + +// RemovePlaylistIDs removes the "playlists" edge to the Playlist entity by IDs. +func (m *AccountMutation) RemovePlaylistIDs(ids ...uuid.UUID) { + if m.removedplaylists == nil { + m.removedplaylists = make(map[uuid.UUID]struct{}) + } + for i := range ids { + delete(m.playlists, ids[i]) + m.removedplaylists[ids[i]] = struct{}{} + } +} + +// RemovedPlaylists returns the removed IDs of the "playlists" edge to the Playlist entity. +func (m *AccountMutation) RemovedPlaylistsIDs() (ids []uuid.UUID) { + for id := range m.removedplaylists { + ids = append(ids, id) + } + return +} + +// PlaylistsIDs returns the "playlists" edge IDs in the mutation. +func (m *AccountMutation) PlaylistsIDs() (ids []uuid.UUID) { + for id := range m.playlists { + ids = append(ids, id) + } + return +} + +// ResetPlaylists resets all changes to the "playlists" edge. +func (m *AccountMutation) ResetPlaylists() { + m.playlists = nil + m.clearedplaylists = false + m.removedplaylists = nil +} + +// Where appends a list predicates to the AccountMutation builder. +func (m *AccountMutation) Where(ps ...predicate.Account) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the AccountMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *AccountMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.Account, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *AccountMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *AccountMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (Account). +func (m *AccountMutation) 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 *AccountMutation) Fields() []string { + fields := make([]string, 0, 0) + 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 *AccountMutation) Field(name string) (ent.Value, bool) { + 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 *AccountMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + return nil, fmt.Errorf("unknown Account 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 *AccountMutation) SetField(name string, value ent.Value) error { + switch name { + } + return fmt.Errorf("unknown Account field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *AccountMutation) 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 *AccountMutation) 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 *AccountMutation) AddField(name string, value ent.Value) error { + return fmt.Errorf("unknown Account numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *AccountMutation) ClearedFields() []string { + return nil +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *AccountMutation) 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 *AccountMutation) ClearField(name string) error { + return fmt.Errorf("unknown Account 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 *AccountMutation) ResetField(name string) error { + return fmt.Errorf("unknown Account field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *AccountMutation) AddedEdges() []string { + edges := make([]string, 0, 1) + if m.playlists != nil { + edges = append(edges, account.EdgePlaylists) + } + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *AccountMutation) AddedIDs(name string) []ent.Value { + switch name { + case account.EdgePlaylists: + ids := make([]ent.Value, 0, len(m.playlists)) + for id := range m.playlists { + ids = append(ids, id) + } + return ids + } + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *AccountMutation) RemovedEdges() []string { + edges := make([]string, 0, 1) + if m.removedplaylists != nil { + edges = append(edges, account.EdgePlaylists) + } + return edges +} + +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *AccountMutation) RemovedIDs(name string) []ent.Value { + switch name { + case account.EdgePlaylists: + ids := make([]ent.Value, 0, len(m.removedplaylists)) + for id := range m.removedplaylists { + ids = append(ids, id) + } + return ids + } + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *AccountMutation) ClearedEdges() []string { + edges := make([]string, 0, 1) + if m.clearedplaylists { + edges = append(edges, account.EdgePlaylists) + } + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *AccountMutation) EdgeCleared(name string) bool { + switch name { + case account.EdgePlaylists: + return m.clearedplaylists + } + 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 *AccountMutation) ClearEdge(name string) error { + switch name { + } + return fmt.Errorf("unknown Account 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 *AccountMutation) ResetEdge(name string) error { + switch name { + case account.EdgePlaylists: + m.ResetPlaylists() + return nil + } + return fmt.Errorf("unknown Account edge %s", name) +} + +// ItemMutation represents an operation that mutates the Item nodes in the graph. +type ItemMutation struct { + config + op Op + typ string + id *uuid.UUID + position *int + addposition *int + clearedFields map[string]struct{} + playlist *uuid.UUID + clearedplaylist bool + track *uuid.UUID + clearedtrack bool + done bool + oldValue func(context.Context) (*Item, error) + predicates []predicate.Item +} + +var _ ent.Mutation = (*ItemMutation)(nil) + +// itemOption allows management of the mutation configuration using functional options. +type itemOption func(*ItemMutation) + +// newItemMutation creates new mutation for the Item entity. +func newItemMutation(c config, op Op, opts ...itemOption) *ItemMutation { + m := &ItemMutation{ + config: c, + op: op, + typ: TypeItem, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// withItemID sets the ID field of the mutation. +func withItemID(id uuid.UUID) itemOption { + return func(m *ItemMutation) { + var ( + err error + once sync.Once + value *Item + ) + m.oldValue = func(ctx context.Context) (*Item, error) { + once.Do(func() { + if m.done { + err = errors.New("querying old values post mutation is not allowed") + } else { + value, err = m.Client().Item.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } +} + +// withItem sets the old Item of the mutation. +func withItem(node *Item) itemOption { + return func(m *ItemMutation) { + m.oldValue = func(context.Context) (*Item, 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 ItemMutation) 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 ItemMutation) 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 Item entities. +func (m *ItemMutation) 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 *ItemMutation) 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 *ItemMutation) 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().Item.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } +} + +// SetPlaylistID sets the "playlist_id" field. +func (m *ItemMutation) SetPlaylistID(u uuid.UUID) { + m.playlist = &u +} + +// PlaylistID returns the value of the "playlist_id" field in the mutation. +func (m *ItemMutation) PlaylistID() (r uuid.UUID, exists bool) { + v := m.playlist + if v == nil { + return + } + return *v, true +} + +// OldPlaylistID returns the old "playlist_id" field's value of the Item entity. +// If the Item object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ItemMutation) OldPlaylistID(ctx context.Context) (v uuid.UUID, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldPlaylistID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldPlaylistID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldPlaylistID: %w", err) + } + return oldValue.PlaylistID, nil +} + +// ResetPlaylistID resets all changes to the "playlist_id" field. +func (m *ItemMutation) ResetPlaylistID() { + m.playlist = nil +} + +// SetTrackID sets the "track_id" field. +func (m *ItemMutation) SetTrackID(u uuid.UUID) { + m.track = &u +} + +// TrackID returns the value of the "track_id" field in the mutation. +func (m *ItemMutation) TrackID() (r uuid.UUID, exists bool) { + v := m.track + if v == nil { + return + } + return *v, true +} + +// OldTrackID returns the old "track_id" field's value of the Item entity. +// If the Item object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ItemMutation) OldTrackID(ctx context.Context) (v uuid.UUID, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldTrackID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldTrackID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldTrackID: %w", err) + } + return oldValue.TrackID, nil +} + +// ResetTrackID resets all changes to the "track_id" field. +func (m *ItemMutation) ResetTrackID() { + m.track = nil +} + +// SetPosition sets the "position" field. +func (m *ItemMutation) SetPosition(i int) { + m.position = &i + m.addposition = nil +} + +// Position returns the value of the "position" field in the mutation. +func (m *ItemMutation) Position() (r int, exists bool) { + v := m.position + if v == nil { + return + } + return *v, true +} + +// OldPosition returns the old "position" field's value of the Item entity. +// If the Item object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ItemMutation) OldPosition(ctx context.Context) (v int, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldPosition is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldPosition requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldPosition: %w", err) + } + return oldValue.Position, nil +} + +// AddPosition adds i to the "position" field. +func (m *ItemMutation) AddPosition(i int) { + if m.addposition != nil { + *m.addposition += i + } else { + m.addposition = &i + } +} + +// AddedPosition returns the value that was added to the "position" field in this mutation. +func (m *ItemMutation) AddedPosition() (r int, exists bool) { + v := m.addposition + if v == nil { + return + } + return *v, true +} + +// ResetPosition resets all changes to the "position" field. +func (m *ItemMutation) ResetPosition() { + m.position = nil + m.addposition = nil +} + +// ClearPlaylist clears the "playlist" edge to the Playlist entity. +func (m *ItemMutation) ClearPlaylist() { + m.clearedplaylist = true + m.clearedFields[item.FieldPlaylistID] = struct{}{} +} + +// PlaylistCleared reports if the "playlist" edge to the Playlist entity was cleared. +func (m *ItemMutation) PlaylistCleared() bool { + return m.clearedplaylist +} + +// PlaylistIDs returns the "playlist" edge IDs in the mutation. +// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use +// PlaylistID instead. It exists only for internal usage by the builders. +func (m *ItemMutation) PlaylistIDs() (ids []uuid.UUID) { + if id := m.playlist; id != nil { + ids = append(ids, *id) + } + return +} + +// ResetPlaylist resets all changes to the "playlist" edge. +func (m *ItemMutation) ResetPlaylist() { + m.playlist = nil + m.clearedplaylist = false +} + +// ClearTrack clears the "track" edge to the Track entity. +func (m *ItemMutation) ClearTrack() { + m.clearedtrack = true + m.clearedFields[item.FieldTrackID] = struct{}{} +} + +// TrackCleared reports if the "track" edge to the Track entity was cleared. +func (m *ItemMutation) TrackCleared() bool { + return m.clearedtrack +} + +// TrackIDs returns the "track" edge IDs in the mutation. +// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use +// TrackID instead. It exists only for internal usage by the builders. +func (m *ItemMutation) TrackIDs() (ids []uuid.UUID) { + if id := m.track; id != nil { + ids = append(ids, *id) + } + return +} + +// ResetTrack resets all changes to the "track" edge. +func (m *ItemMutation) ResetTrack() { + m.track = nil + m.clearedtrack = false +} + +// Where appends a list predicates to the ItemMutation builder. +func (m *ItemMutation) Where(ps ...predicate.Item) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the ItemMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *ItemMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.Item, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *ItemMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *ItemMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (Item). +func (m *ItemMutation) 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 *ItemMutation) Fields() []string { + fields := make([]string, 0, 3) + if m.playlist != nil { + fields = append(fields, item.FieldPlaylistID) + } + if m.track != nil { + fields = append(fields, item.FieldTrackID) + } + if m.position != nil { + fields = append(fields, item.FieldPosition) + } + 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 *ItemMutation) Field(name string) (ent.Value, bool) { + switch name { + case item.FieldPlaylistID: + return m.PlaylistID() + case item.FieldTrackID: + return m.TrackID() + case item.FieldPosition: + return m.Position() + } + 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 *ItemMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + switch name { + case item.FieldPlaylistID: + return m.OldPlaylistID(ctx) + case item.FieldTrackID: + return m.OldTrackID(ctx) + case item.FieldPosition: + return m.OldPosition(ctx) + } + return nil, fmt.Errorf("unknown Item 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 *ItemMutation) SetField(name string, value ent.Value) error { + switch name { + case item.FieldPlaylistID: + v, ok := value.(uuid.UUID) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetPlaylistID(v) + return nil + case item.FieldTrackID: + v, ok := value.(uuid.UUID) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetTrackID(v) + return nil + case item.FieldPosition: + v, ok := value.(int) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetPosition(v) + return nil + } + return fmt.Errorf("unknown Item field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *ItemMutation) AddedFields() []string { + var fields []string + if m.addposition != nil { + fields = append(fields, item.FieldPosition) + } + 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 *ItemMutation) AddedField(name string) (ent.Value, bool) { + switch name { + case item.FieldPosition: + return m.AddedPosition() + } + 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 *ItemMutation) AddField(name string, value ent.Value) error { + switch name { + case item.FieldPosition: + v, ok := value.(int) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.AddPosition(v) + return nil + } + return fmt.Errorf("unknown Item numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *ItemMutation) ClearedFields() []string { + return nil +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *ItemMutation) 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 *ItemMutation) ClearField(name string) error { + return fmt.Errorf("unknown Item 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 *ItemMutation) ResetField(name string) error { + switch name { + case item.FieldPlaylistID: + m.ResetPlaylistID() + return nil + case item.FieldTrackID: + m.ResetTrackID() + return nil + case item.FieldPosition: + m.ResetPosition() + return nil + } + return fmt.Errorf("unknown Item field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *ItemMutation) AddedEdges() []string { + edges := make([]string, 0, 2) + if m.playlist != nil { + edges = append(edges, item.EdgePlaylist) + } + if m.track != nil { + edges = append(edges, item.EdgeTrack) + } + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *ItemMutation) AddedIDs(name string) []ent.Value { + switch name { + case item.EdgePlaylist: + if id := m.playlist; id != nil { + return []ent.Value{*id} + } + case item.EdgeTrack: + if id := m.track; id != nil { + return []ent.Value{*id} + } + } + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *ItemMutation) RemovedEdges() []string { + edges := make([]string, 0, 2) + return edges +} + +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *ItemMutation) RemovedIDs(name string) []ent.Value { + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *ItemMutation) ClearedEdges() []string { + edges := make([]string, 0, 2) + if m.clearedplaylist { + edges = append(edges, item.EdgePlaylist) + } + if m.clearedtrack { + edges = append(edges, item.EdgeTrack) + } + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *ItemMutation) EdgeCleared(name string) bool { + switch name { + case item.EdgePlaylist: + return m.clearedplaylist + case item.EdgeTrack: + return m.clearedtrack + } + 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 *ItemMutation) ClearEdge(name string) error { + switch name { + case item.EdgePlaylist: + m.ClearPlaylist() + return nil + case item.EdgeTrack: + m.ClearTrack() + return nil + } + return fmt.Errorf("unknown Item 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 *ItemMutation) ResetEdge(name string) error { + switch name { + case item.EdgePlaylist: + m.ResetPlaylist() + return nil + case item.EdgeTrack: + m.ResetTrack() + return nil + } + return fmt.Errorf("unknown Item edge %s", name) +} + +// PlaylistMutation represents an operation that mutates the Playlist nodes in the graph. +type PlaylistMutation struct { + config + op Op + typ string + id *uuid.UUID + name *string + revision *int + addrevision *int + clearedFields map[string]struct{} + items map[uuid.UUID]struct{} + removeditems map[uuid.UUID]struct{} + cleareditems bool + owner *uuid.UUID + clearedowner bool + done bool + oldValue func(context.Context) (*Playlist, error) + predicates []predicate.Playlist +} + +var _ ent.Mutation = (*PlaylistMutation)(nil) + +// playlistOption allows management of the mutation configuration using functional options. +type playlistOption func(*PlaylistMutation) + +// newPlaylistMutation creates new mutation for the Playlist entity. +func newPlaylistMutation(c config, op Op, opts ...playlistOption) *PlaylistMutation { + m := &PlaylistMutation{ + config: c, + op: op, + typ: TypePlaylist, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// withPlaylistID sets the ID field of the mutation. +func withPlaylistID(id uuid.UUID) playlistOption { + return func(m *PlaylistMutation) { + var ( + err error + once sync.Once + value *Playlist + ) + m.oldValue = func(ctx context.Context) (*Playlist, error) { + once.Do(func() { + if m.done { + err = errors.New("querying old values post mutation is not allowed") + } else { + value, err = m.Client().Playlist.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } +} + +// withPlaylist sets the old Playlist of the mutation. +func withPlaylist(node *Playlist) playlistOption { + return func(m *PlaylistMutation) { + m.oldValue = func(context.Context) (*Playlist, 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 PlaylistMutation) 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 PlaylistMutation) 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 Playlist entities. +func (m *PlaylistMutation) 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 *PlaylistMutation) 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 *PlaylistMutation) 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().Playlist.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } +} + +// SetOwnerID sets the "owner_id" field. +func (m *PlaylistMutation) SetOwnerID(u uuid.UUID) { + m.owner = &u +} + +// OwnerID returns the value of the "owner_id" field in the mutation. +func (m *PlaylistMutation) 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 Playlist entity. +// If the Playlist object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *PlaylistMutation) 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 +} + +// ResetOwnerID resets all changes to the "owner_id" field. +func (m *PlaylistMutation) ResetOwnerID() { + m.owner = nil +} + +// SetName sets the "name" field. +func (m *PlaylistMutation) SetName(s string) { + m.name = &s +} + +// Name returns the value of the "name" field in the mutation. +func (m *PlaylistMutation) 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 Playlist entity. +// If the Playlist object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *PlaylistMutation) 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 *PlaylistMutation) ResetName() { + m.name = nil +} + +// SetRevision sets the "revision" field. +func (m *PlaylistMutation) SetRevision(i int) { + m.revision = &i + m.addrevision = nil +} + +// Revision returns the value of the "revision" field in the mutation. +func (m *PlaylistMutation) Revision() (r int, exists bool) { + v := m.revision + if v == nil { + return + } + return *v, true +} + +// OldRevision returns the old "revision" field's value of the Playlist entity. +// If the Playlist object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *PlaylistMutation) OldRevision(ctx context.Context) (v int, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldRevision is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldRevision requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldRevision: %w", err) + } + return oldValue.Revision, nil +} + +// AddRevision adds i to the "revision" field. +func (m *PlaylistMutation) AddRevision(i int) { + if m.addrevision != nil { + *m.addrevision += i + } else { + m.addrevision = &i + } +} + +// AddedRevision returns the value that was added to the "revision" field in this mutation. +func (m *PlaylistMutation) AddedRevision() (r int, exists bool) { + v := m.addrevision + if v == nil { + return + } + return *v, true +} + +// ResetRevision resets all changes to the "revision" field. +func (m *PlaylistMutation) ResetRevision() { + m.revision = nil + m.addrevision = nil +} + +// AddItemIDs adds the "items" edge to the Item entity by ids. +func (m *PlaylistMutation) AddItemIDs(ids ...uuid.UUID) { + if m.items == nil { + m.items = make(map[uuid.UUID]struct{}) + } + for i := range ids { + m.items[ids[i]] = struct{}{} + } +} + +// ClearItems clears the "items" edge to the Item entity. +func (m *PlaylistMutation) ClearItems() { + m.cleareditems = true +} + +// ItemsCleared reports if the "items" edge to the Item entity was cleared. +func (m *PlaylistMutation) ItemsCleared() bool { + return m.cleareditems +} + +// RemoveItemIDs removes the "items" edge to the Item entity by IDs. +func (m *PlaylistMutation) RemoveItemIDs(ids ...uuid.UUID) { + if m.removeditems == nil { + m.removeditems = make(map[uuid.UUID]struct{}) + } + for i := range ids { + delete(m.items, ids[i]) + m.removeditems[ids[i]] = struct{}{} + } +} + +// RemovedItems returns the removed IDs of the "items" edge to the Item entity. +func (m *PlaylistMutation) RemovedItemsIDs() (ids []uuid.UUID) { + for id := range m.removeditems { + ids = append(ids, id) + } + return +} + +// ItemsIDs returns the "items" edge IDs in the mutation. +func (m *PlaylistMutation) ItemsIDs() (ids []uuid.UUID) { + for id := range m.items { + ids = append(ids, id) + } + return +} + +// ResetItems resets all changes to the "items" edge. +func (m *PlaylistMutation) ResetItems() { + m.items = nil + m.cleareditems = false + m.removeditems = nil +} + +// ClearOwner clears the "owner" edge to the Account entity. +func (m *PlaylistMutation) ClearOwner() { + m.clearedowner = true + m.clearedFields[playlist.FieldOwnerID] = struct{}{} +} + +// OwnerCleared reports if the "owner" edge to the Account entity was cleared. +func (m *PlaylistMutation) OwnerCleared() bool { + return 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 *PlaylistMutation) 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 *PlaylistMutation) ResetOwner() { + m.owner = nil + m.clearedowner = false +} + +// Where appends a list predicates to the PlaylistMutation builder. +func (m *PlaylistMutation) Where(ps ...predicate.Playlist) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the PlaylistMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *PlaylistMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.Playlist, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *PlaylistMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *PlaylistMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (Playlist). +func (m *PlaylistMutation) 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 *PlaylistMutation) Fields() []string { + fields := make([]string, 0, 3) + if m.owner != nil { + fields = append(fields, playlist.FieldOwnerID) + } + if m.name != nil { + fields = append(fields, playlist.FieldName) + } + if m.revision != nil { + fields = append(fields, playlist.FieldRevision) + } + 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 *PlaylistMutation) Field(name string) (ent.Value, bool) { + switch name { + case playlist.FieldOwnerID: + return m.OwnerID() + case playlist.FieldName: + return m.Name() + case playlist.FieldRevision: + return m.Revision() + } + 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 *PlaylistMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + switch name { + case playlist.FieldOwnerID: + return m.OldOwnerID(ctx) + case playlist.FieldName: + return m.OldName(ctx) + case playlist.FieldRevision: + return m.OldRevision(ctx) + } + return nil, fmt.Errorf("unknown Playlist 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 *PlaylistMutation) SetField(name string, value ent.Value) error { + switch name { + case playlist.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 playlist.FieldName: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetName(v) + return nil + case playlist.FieldRevision: + v, ok := value.(int) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetRevision(v) + return nil + } + return fmt.Errorf("unknown Playlist field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *PlaylistMutation) AddedFields() []string { + var fields []string + if m.addrevision != nil { + fields = append(fields, playlist.FieldRevision) + } + 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 *PlaylistMutation) AddedField(name string) (ent.Value, bool) { + switch name { + case playlist.FieldRevision: + return m.AddedRevision() + } + 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 *PlaylistMutation) AddField(name string, value ent.Value) error { + switch name { + case playlist.FieldRevision: + v, ok := value.(int) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.AddRevision(v) + return nil + } + return fmt.Errorf("unknown Playlist numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *PlaylistMutation) ClearedFields() []string { + return nil +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *PlaylistMutation) 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 *PlaylistMutation) ClearField(name string) error { + return fmt.Errorf("unknown Playlist 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 *PlaylistMutation) ResetField(name string) error { + switch name { + case playlist.FieldOwnerID: + m.ResetOwnerID() + return nil + case playlist.FieldName: + m.ResetName() + return nil + case playlist.FieldRevision: + m.ResetRevision() + return nil + } + return fmt.Errorf("unknown Playlist field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *PlaylistMutation) AddedEdges() []string { + edges := make([]string, 0, 2) + if m.items != nil { + edges = append(edges, playlist.EdgeItems) + } + if m.owner != nil { + edges = append(edges, playlist.EdgeOwner) + } + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *PlaylistMutation) AddedIDs(name string) []ent.Value { + switch name { + case playlist.EdgeItems: + ids := make([]ent.Value, 0, len(m.items)) + for id := range m.items { + ids = append(ids, id) + } + return ids + case playlist.EdgeOwner: + if id := m.owner; id != nil { + return []ent.Value{*id} + } + } + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *PlaylistMutation) RemovedEdges() []string { + edges := make([]string, 0, 2) + if m.removeditems != nil { + edges = append(edges, playlist.EdgeItems) + } + return edges +} + +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *PlaylistMutation) RemovedIDs(name string) []ent.Value { + switch name { + case playlist.EdgeItems: + ids := make([]ent.Value, 0, len(m.removeditems)) + for id := range m.removeditems { + ids = append(ids, id) + } + return ids + } + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *PlaylistMutation) ClearedEdges() []string { + edges := make([]string, 0, 2) + if m.cleareditems { + edges = append(edges, playlist.EdgeItems) + } + if m.clearedowner { + edges = append(edges, playlist.EdgeOwner) + } + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *PlaylistMutation) EdgeCleared(name string) bool { + switch name { + case playlist.EdgeItems: + return m.cleareditems + case playlist.EdgeOwner: + return m.clearedowner + } + 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 *PlaylistMutation) ClearEdge(name string) error { + switch name { + case playlist.EdgeOwner: + m.ClearOwner() + return nil + } + return fmt.Errorf("unknown Playlist 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 *PlaylistMutation) ResetEdge(name string) error { + switch name { + case playlist.EdgeItems: + m.ResetItems() + return nil + case playlist.EdgeOwner: + m.ResetOwner() + return nil + } + return fmt.Errorf("unknown Playlist edge %s", name) +} + +// TrackMutation represents an operation that mutates the Track nodes in the graph. +type TrackMutation struct { + config + op Op + typ string + id *uuid.UUID + title *string + artist *string + duration_seconds *int + addduration_seconds *int + clearedFields map[string]struct{} + items map[uuid.UUID]struct{} + removeditems map[uuid.UUID]struct{} + cleareditems bool + done bool + oldValue func(context.Context) (*Track, error) + predicates []predicate.Track +} + +var _ ent.Mutation = (*TrackMutation)(nil) + +// trackOption allows management of the mutation configuration using functional options. +type trackOption func(*TrackMutation) + +// newTrackMutation creates new mutation for the Track entity. +func newTrackMutation(c config, op Op, opts ...trackOption) *TrackMutation { + m := &TrackMutation{ + config: c, + op: op, + typ: TypeTrack, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// withTrackID sets the ID field of the mutation. +func withTrackID(id uuid.UUID) trackOption { + return func(m *TrackMutation) { + var ( + err error + once sync.Once + value *Track + ) + m.oldValue = func(ctx context.Context) (*Track, error) { + once.Do(func() { + if m.done { + err = errors.New("querying old values post mutation is not allowed") + } else { + value, err = m.Client().Track.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } +} + +// withTrack sets the old Track of the mutation. +func withTrack(node *Track) trackOption { + return func(m *TrackMutation) { + m.oldValue = func(context.Context) (*Track, 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 TrackMutation) 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 TrackMutation) 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 Track entities. +func (m *TrackMutation) 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 *TrackMutation) 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 *TrackMutation) 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().Track.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } +} + +// SetTitle sets the "title" field. +func (m *TrackMutation) SetTitle(s string) { + m.title = &s +} + +// Title returns the value of the "title" field in the mutation. +func (m *TrackMutation) 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 Track entity. +// If the Track object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *TrackMutation) 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 *TrackMutation) ResetTitle() { + m.title = nil +} + +// SetArtist sets the "artist" field. +func (m *TrackMutation) SetArtist(s string) { + m.artist = &s +} + +// Artist returns the value of the "artist" field in the mutation. +func (m *TrackMutation) Artist() (r string, exists bool) { + v := m.artist + if v == nil { + return + } + return *v, true +} + +// OldArtist returns the old "artist" field's value of the Track entity. +// If the Track object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *TrackMutation) OldArtist(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldArtist is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldArtist requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldArtist: %w", err) + } + return oldValue.Artist, nil +} + +// ResetArtist resets all changes to the "artist" field. +func (m *TrackMutation) ResetArtist() { + m.artist = nil +} + +// SetDurationSeconds sets the "duration_seconds" field. +func (m *TrackMutation) SetDurationSeconds(i int) { + m.duration_seconds = &i + m.addduration_seconds = nil +} + +// DurationSeconds returns the value of the "duration_seconds" field in the mutation. +func (m *TrackMutation) DurationSeconds() (r int, exists bool) { + v := m.duration_seconds + if v == nil { + return + } + return *v, true +} + +// OldDurationSeconds returns the old "duration_seconds" field's value of the Track entity. +// If the Track object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *TrackMutation) OldDurationSeconds(ctx context.Context) (v int, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldDurationSeconds is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldDurationSeconds requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldDurationSeconds: %w", err) + } + return oldValue.DurationSeconds, nil +} + +// AddDurationSeconds adds i to the "duration_seconds" field. +func (m *TrackMutation) AddDurationSeconds(i int) { + if m.addduration_seconds != nil { + *m.addduration_seconds += i + } else { + m.addduration_seconds = &i + } +} + +// AddedDurationSeconds returns the value that was added to the "duration_seconds" field in this mutation. +func (m *TrackMutation) AddedDurationSeconds() (r int, exists bool) { + v := m.addduration_seconds + if v == nil { + return + } + return *v, true +} + +// ResetDurationSeconds resets all changes to the "duration_seconds" field. +func (m *TrackMutation) ResetDurationSeconds() { + m.duration_seconds = nil + m.addduration_seconds = nil +} + +// AddItemIDs adds the "items" edge to the Item entity by ids. +func (m *TrackMutation) AddItemIDs(ids ...uuid.UUID) { + if m.items == nil { + m.items = make(map[uuid.UUID]struct{}) + } + for i := range ids { + m.items[ids[i]] = struct{}{} + } +} + +// ClearItems clears the "items" edge to the Item entity. +func (m *TrackMutation) ClearItems() { + m.cleareditems = true +} + +// ItemsCleared reports if the "items" edge to the Item entity was cleared. +func (m *TrackMutation) ItemsCleared() bool { + return m.cleareditems +} + +// RemoveItemIDs removes the "items" edge to the Item entity by IDs. +func (m *TrackMutation) RemoveItemIDs(ids ...uuid.UUID) { + if m.removeditems == nil { + m.removeditems = make(map[uuid.UUID]struct{}) + } + for i := range ids { + delete(m.items, ids[i]) + m.removeditems[ids[i]] = struct{}{} + } +} + +// RemovedItems returns the removed IDs of the "items" edge to the Item entity. +func (m *TrackMutation) RemovedItemsIDs() (ids []uuid.UUID) { + for id := range m.removeditems { + ids = append(ids, id) + } + return +} + +// ItemsIDs returns the "items" edge IDs in the mutation. +func (m *TrackMutation) ItemsIDs() (ids []uuid.UUID) { + for id := range m.items { + ids = append(ids, id) + } + return +} + +// ResetItems resets all changes to the "items" edge. +func (m *TrackMutation) ResetItems() { + m.items = nil + m.cleareditems = false + m.removeditems = nil +} + +// Where appends a list predicates to the TrackMutation builder. +func (m *TrackMutation) Where(ps ...predicate.Track) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the TrackMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *TrackMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.Track, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *TrackMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *TrackMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (Track). +func (m *TrackMutation) 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 *TrackMutation) Fields() []string { + fields := make([]string, 0, 3) + if m.title != nil { + fields = append(fields, track.FieldTitle) + } + if m.artist != nil { + fields = append(fields, track.FieldArtist) + } + if m.duration_seconds != nil { + fields = append(fields, track.FieldDurationSeconds) + } + 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 *TrackMutation) Field(name string) (ent.Value, bool) { + switch name { + case track.FieldTitle: + return m.Title() + case track.FieldArtist: + return m.Artist() + case track.FieldDurationSeconds: + return m.DurationSeconds() + } + 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 *TrackMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + switch name { + case track.FieldTitle: + return m.OldTitle(ctx) + case track.FieldArtist: + return m.OldArtist(ctx) + case track.FieldDurationSeconds: + return m.OldDurationSeconds(ctx) + } + return nil, fmt.Errorf("unknown Track 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 *TrackMutation) SetField(name string, value ent.Value) error { + switch name { + case track.FieldTitle: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetTitle(v) + return nil + case track.FieldArtist: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetArtist(v) + return nil + case track.FieldDurationSeconds: + v, ok := value.(int) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetDurationSeconds(v) + return nil + } + return fmt.Errorf("unknown Track field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *TrackMutation) AddedFields() []string { + var fields []string + if m.addduration_seconds != nil { + fields = append(fields, track.FieldDurationSeconds) + } + 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 *TrackMutation) AddedField(name string) (ent.Value, bool) { + switch name { + case track.FieldDurationSeconds: + return m.AddedDurationSeconds() + } + 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 *TrackMutation) AddField(name string, value ent.Value) error { + switch name { + case track.FieldDurationSeconds: + v, ok := value.(int) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.AddDurationSeconds(v) + return nil + } + return fmt.Errorf("unknown Track numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *TrackMutation) ClearedFields() []string { + return nil +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *TrackMutation) 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 *TrackMutation) ClearField(name string) error { + return fmt.Errorf("unknown Track 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 *TrackMutation) ResetField(name string) error { + switch name { + case track.FieldTitle: + m.ResetTitle() + return nil + case track.FieldArtist: + m.ResetArtist() + return nil + case track.FieldDurationSeconds: + m.ResetDurationSeconds() + return nil + } + return fmt.Errorf("unknown Track field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *TrackMutation) AddedEdges() []string { + edges := make([]string, 0, 1) + if m.items != nil { + edges = append(edges, track.EdgeItems) + } + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *TrackMutation) AddedIDs(name string) []ent.Value { + switch name { + case track.EdgeItems: + ids := make([]ent.Value, 0, len(m.items)) + for id := range m.items { + ids = append(ids, id) + } + return ids + } + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *TrackMutation) RemovedEdges() []string { + edges := make([]string, 0, 1) + if m.removeditems != nil { + edges = append(edges, track.EdgeItems) + } + return edges +} + +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *TrackMutation) RemovedIDs(name string) []ent.Value { + switch name { + case track.EdgeItems: + ids := make([]ent.Value, 0, len(m.removeditems)) + for id := range m.removeditems { + ids = append(ids, id) + } + return ids + } + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *TrackMutation) ClearedEdges() []string { + edges := make([]string, 0, 1) + if m.cleareditems { + edges = append(edges, track.EdgeItems) + } + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *TrackMutation) EdgeCleared(name string) bool { + switch name { + case track.EdgeItems: + return m.cleareditems + } + 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 *TrackMutation) ClearEdge(name string) error { + switch name { + } + return fmt.Errorf("unknown Track 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 *TrackMutation) ResetEdge(name string) error { + switch name { + case track.EdgeItems: + m.ResetItems() + return nil + } + return fmt.Errorf("unknown Track edge %s", name) +} diff --git a/applications/playlist-api/ent/playlist.go b/applications/playlist-api/ent/playlist.go new file mode 100644 index 00000000..12c21929 --- /dev/null +++ b/applications/playlist-api/ent/playlist.go @@ -0,0 +1,173 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "fmt" + "strings" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "example.com/playlist-api/ent/account" + "example.com/playlist-api/ent/playlist" + "github.com/google/uuid" +) + +// Playlist is the model entity for the Playlist schema. +type Playlist struct { + config `json:"-"` + // ID of the ent. + ID uuid.UUID `json:"id,omitempty"` + // OwnerID holds the value of the "owner_id" field. + OwnerID uuid.UUID `json:"owner_id,omitempty"` + // Name holds the value of the "name" field. + Name string `json:"name,omitempty"` + // Revision holds the value of the "revision" field. + Revision int `json:"revision,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the PlaylistQuery when eager-loading is set. + Edges PlaylistEdges `json:"edges"` + selectValues sql.SelectValues +} + +// PlaylistEdges holds the relations/edges for other nodes in the graph. +type PlaylistEdges struct { + // Items holds the value of the items edge. + Items []*Item `json:"items,omitempty"` + // Owner holds the value of the owner edge. + Owner *Account `json:"owner,omitempty"` + // loadedTypes holds the information for reporting if a + // type was loaded (or requested) in eager-loading or not. + loadedTypes [2]bool +} + +// ItemsOrErr returns the Items value or an error if the edge +// was not loaded in eager-loading. +func (e PlaylistEdges) ItemsOrErr() ([]*Item, error) { + if e.loadedTypes[0] { + return e.Items, nil + } + return nil, &NotLoadedError{edge: "items"} +} + +// OwnerOrErr returns the Owner value or an error if the edge +// was not loaded in eager-loading, or loaded but was not found. +func (e PlaylistEdges) OwnerOrErr() (*Account, error) { + if e.Owner != nil { + return e.Owner, nil + } else if e.loadedTypes[1] { + return nil, &NotFoundError{label: account.Label} + } + return nil, &NotLoadedError{edge: "owner"} +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*Playlist) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case playlist.FieldRevision: + values[i] = new(sql.NullInt64) + case playlist.FieldName: + values[i] = new(sql.NullString) + case playlist.FieldID, playlist.FieldOwnerID: + 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 Playlist fields. +func (_m *Playlist) 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 playlist.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 playlist.FieldOwnerID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field owner_id", values[i]) + } else if value != nil { + _m.OwnerID = *value + } + case playlist.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 playlist.FieldRevision: + if value, ok := values[i].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for field revision", values[i]) + } else if value.Valid { + _m.Revision = int(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 Playlist. +// This includes values selected through modifiers, order, etc. +func (_m *Playlist) Value(name string) (ent.Value, error) { + return _m.selectValues.Get(name) +} + +// QueryItems queries the "items" edge of the Playlist entity. +func (_m *Playlist) QueryItems() *ItemQuery { + return NewPlaylistClient(_m.config).QueryItems(_m) +} + +// QueryOwner queries the "owner" edge of the Playlist entity. +func (_m *Playlist) QueryOwner() *AccountQuery { + return NewPlaylistClient(_m.config).QueryOwner(_m) +} + +// Update returns a builder for updating this Playlist. +// Note that you need to call Playlist.Unwrap() before calling this method if this Playlist +// was returned from a transaction, and the transaction was committed or rolled back. +func (_m *Playlist) Update() *PlaylistUpdateOne { + return NewPlaylistClient(_m.config).UpdateOne(_m) +} + +// Unwrap unwraps the Playlist 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 *Playlist) Unwrap() *Playlist { + _tx, ok := _m.config.driver.(*txDriver) + if !ok { + panic("ent: Playlist is not a transactional entity") + } + _m.config.driver = _tx.drv + return _m +} + +// String implements the fmt.Stringer. +func (_m *Playlist) String() string { + var builder strings.Builder + builder.WriteString("Playlist(") + builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID)) + builder.WriteString("owner_id=") + builder.WriteString(fmt.Sprintf("%v", _m.OwnerID)) + builder.WriteString(", ") + builder.WriteString("name=") + builder.WriteString(_m.Name) + builder.WriteString(", ") + builder.WriteString("revision=") + builder.WriteString(fmt.Sprintf("%v", _m.Revision)) + builder.WriteByte(')') + return builder.String() +} + +// Playlists is a parsable slice of Playlist. +type Playlists []*Playlist diff --git a/applications/playlist-api/ent/playlist/playlist.go b/applications/playlist-api/ent/playlist/playlist.go new file mode 100644 index 00000000..094cfbf3 --- /dev/null +++ b/applications/playlist-api/ent/playlist/playlist.go @@ -0,0 +1,129 @@ +// Code generated by ent, DO NOT EDIT. + +package playlist + +import ( + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "github.com/google/uuid" +) + +const ( + // Label holds the string label denoting the playlist type in the database. + Label = "playlist" + // FieldID holds the string denoting the id field in the database. + FieldID = "id" + // FieldOwnerID holds the string denoting the owner_id field in the database. + FieldOwnerID = "owner_id" + // FieldName holds the string denoting the name field in the database. + FieldName = "name" + // FieldRevision holds the string denoting the revision field in the database. + FieldRevision = "revision" + // EdgeItems holds the string denoting the items edge name in mutations. + EdgeItems = "items" + // EdgeOwner holds the string denoting the owner edge name in mutations. + EdgeOwner = "owner" + // Table holds the table name of the playlist in the database. + Table = "playlists" + // ItemsTable is the table that holds the items relation/edge. + ItemsTable = "items" + // ItemsInverseTable is the table name for the Item entity. + // It exists in this package in order to avoid circular dependency with the "item" package. + ItemsInverseTable = "items" + // ItemsColumn is the table column denoting the items relation/edge. + ItemsColumn = "playlist_id" + // OwnerTable is the table that holds the owner relation/edge. + OwnerTable = "playlists" + // OwnerInverseTable is the table name for the Account entity. + // It exists in this package in order to avoid circular dependency with the "account" package. + OwnerInverseTable = "accounts" + // OwnerColumn is the table column denoting the owner relation/edge. + OwnerColumn = "owner_id" +) + +// Columns holds all SQL columns for playlist fields. +var Columns = []string{ + FieldID, + FieldOwnerID, + FieldName, + FieldRevision, +} + +// 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 + // DefaultRevision holds the default value on creation for the "revision" field. + DefaultRevision int + // RevisionValidator is a validator for the "revision" field. It is called by the builders before save. + RevisionValidator func(int) error + // DefaultID holds the default value on creation for the "id" field. + DefaultID func() uuid.UUID +) + +// OrderOption defines the ordering options for the Playlist 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() +} + +// ByOwnerID orders the results by the owner_id field. +func ByOwnerID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldOwnerID, opts...).ToFunc() +} + +// ByName orders the results by the name field. +func ByName(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldName, opts...).ToFunc() +} + +// ByRevision orders the results by the revision field. +func ByRevision(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldRevision, opts...).ToFunc() +} + +// ByItemsCount orders the results by items count. +func ByItemsCount(opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborsCount(s, newItemsStep(), opts...) + } +} + +// ByItems orders the results by items terms. +func ByItems(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newItemsStep(), append([]sql.OrderTerm{term}, terms...)...) + } +} + +// ByOwnerField orders the results by owner field. +func ByOwnerField(field string, opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newOwnerStep(), sql.OrderByField(field, opts...)) + } +} +func newItemsStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(ItemsInverseTable, FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, ItemsTable, ItemsColumn), + ) +} +func newOwnerStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(OwnerInverseTable, FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, OwnerTable, OwnerColumn), + ) +} diff --git a/applications/playlist-api/ent/playlist/where.go b/applications/playlist-api/ent/playlist/where.go new file mode 100644 index 00000000..362b4f56 --- /dev/null +++ b/applications/playlist-api/ent/playlist/where.go @@ -0,0 +1,256 @@ +// Code generated by ent, DO NOT EDIT. + +package playlist + +import ( + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "example.com/playlist-api/ent/predicate" + "github.com/google/uuid" +) + +// ID filters vertices based on their ID field. +func ID(id uuid.UUID) predicate.Playlist { + return predicate.Playlist(sql.FieldEQ(FieldID, id)) +} + +// IDEQ applies the EQ predicate on the ID field. +func IDEQ(id uuid.UUID) predicate.Playlist { + return predicate.Playlist(sql.FieldEQ(FieldID, id)) +} + +// IDNEQ applies the NEQ predicate on the ID field. +func IDNEQ(id uuid.UUID) predicate.Playlist { + return predicate.Playlist(sql.FieldNEQ(FieldID, id)) +} + +// IDIn applies the In predicate on the ID field. +func IDIn(ids ...uuid.UUID) predicate.Playlist { + return predicate.Playlist(sql.FieldIn(FieldID, ids...)) +} + +// IDNotIn applies the NotIn predicate on the ID field. +func IDNotIn(ids ...uuid.UUID) predicate.Playlist { + return predicate.Playlist(sql.FieldNotIn(FieldID, ids...)) +} + +// IDGT applies the GT predicate on the ID field. +func IDGT(id uuid.UUID) predicate.Playlist { + return predicate.Playlist(sql.FieldGT(FieldID, id)) +} + +// IDGTE applies the GTE predicate on the ID field. +func IDGTE(id uuid.UUID) predicate.Playlist { + return predicate.Playlist(sql.FieldGTE(FieldID, id)) +} + +// IDLT applies the LT predicate on the ID field. +func IDLT(id uuid.UUID) predicate.Playlist { + return predicate.Playlist(sql.FieldLT(FieldID, id)) +} + +// IDLTE applies the LTE predicate on the ID field. +func IDLTE(id uuid.UUID) predicate.Playlist { + return predicate.Playlist(sql.FieldLTE(FieldID, id)) +} + +// OwnerID applies equality check predicate on the "owner_id" field. It's identical to OwnerIDEQ. +func OwnerID(v uuid.UUID) predicate.Playlist { + return predicate.Playlist(sql.FieldEQ(FieldOwnerID, v)) +} + +// Name applies equality check predicate on the "name" field. It's identical to NameEQ. +func Name(v string) predicate.Playlist { + return predicate.Playlist(sql.FieldEQ(FieldName, v)) +} + +// Revision applies equality check predicate on the "revision" field. It's identical to RevisionEQ. +func Revision(v int) predicate.Playlist { + return predicate.Playlist(sql.FieldEQ(FieldRevision, v)) +} + +// OwnerIDEQ applies the EQ predicate on the "owner_id" field. +func OwnerIDEQ(v uuid.UUID) predicate.Playlist { + return predicate.Playlist(sql.FieldEQ(FieldOwnerID, v)) +} + +// OwnerIDNEQ applies the NEQ predicate on the "owner_id" field. +func OwnerIDNEQ(v uuid.UUID) predicate.Playlist { + return predicate.Playlist(sql.FieldNEQ(FieldOwnerID, v)) +} + +// OwnerIDIn applies the In predicate on the "owner_id" field. +func OwnerIDIn(vs ...uuid.UUID) predicate.Playlist { + return predicate.Playlist(sql.FieldIn(FieldOwnerID, vs...)) +} + +// OwnerIDNotIn applies the NotIn predicate on the "owner_id" field. +func OwnerIDNotIn(vs ...uuid.UUID) predicate.Playlist { + return predicate.Playlist(sql.FieldNotIn(FieldOwnerID, vs...)) +} + +// NameEQ applies the EQ predicate on the "name" field. +func NameEQ(v string) predicate.Playlist { + return predicate.Playlist(sql.FieldEQ(FieldName, v)) +} + +// NameNEQ applies the NEQ predicate on the "name" field. +func NameNEQ(v string) predicate.Playlist { + return predicate.Playlist(sql.FieldNEQ(FieldName, v)) +} + +// NameIn applies the In predicate on the "name" field. +func NameIn(vs ...string) predicate.Playlist { + return predicate.Playlist(sql.FieldIn(FieldName, vs...)) +} + +// NameNotIn applies the NotIn predicate on the "name" field. +func NameNotIn(vs ...string) predicate.Playlist { + return predicate.Playlist(sql.FieldNotIn(FieldName, vs...)) +} + +// NameGT applies the GT predicate on the "name" field. +func NameGT(v string) predicate.Playlist { + return predicate.Playlist(sql.FieldGT(FieldName, v)) +} + +// NameGTE applies the GTE predicate on the "name" field. +func NameGTE(v string) predicate.Playlist { + return predicate.Playlist(sql.FieldGTE(FieldName, v)) +} + +// NameLT applies the LT predicate on the "name" field. +func NameLT(v string) predicate.Playlist { + return predicate.Playlist(sql.FieldLT(FieldName, v)) +} + +// NameLTE applies the LTE predicate on the "name" field. +func NameLTE(v string) predicate.Playlist { + return predicate.Playlist(sql.FieldLTE(FieldName, v)) +} + +// NameContains applies the Contains predicate on the "name" field. +func NameContains(v string) predicate.Playlist { + return predicate.Playlist(sql.FieldContains(FieldName, v)) +} + +// NameHasPrefix applies the HasPrefix predicate on the "name" field. +func NameHasPrefix(v string) predicate.Playlist { + return predicate.Playlist(sql.FieldHasPrefix(FieldName, v)) +} + +// NameHasSuffix applies the HasSuffix predicate on the "name" field. +func NameHasSuffix(v string) predicate.Playlist { + return predicate.Playlist(sql.FieldHasSuffix(FieldName, v)) +} + +// NameEqualFold applies the EqualFold predicate on the "name" field. +func NameEqualFold(v string) predicate.Playlist { + return predicate.Playlist(sql.FieldEqualFold(FieldName, v)) +} + +// NameContainsFold applies the ContainsFold predicate on the "name" field. +func NameContainsFold(v string) predicate.Playlist { + return predicate.Playlist(sql.FieldContainsFold(FieldName, v)) +} + +// RevisionEQ applies the EQ predicate on the "revision" field. +func RevisionEQ(v int) predicate.Playlist { + return predicate.Playlist(sql.FieldEQ(FieldRevision, v)) +} + +// RevisionNEQ applies the NEQ predicate on the "revision" field. +func RevisionNEQ(v int) predicate.Playlist { + return predicate.Playlist(sql.FieldNEQ(FieldRevision, v)) +} + +// RevisionIn applies the In predicate on the "revision" field. +func RevisionIn(vs ...int) predicate.Playlist { + return predicate.Playlist(sql.FieldIn(FieldRevision, vs...)) +} + +// RevisionNotIn applies the NotIn predicate on the "revision" field. +func RevisionNotIn(vs ...int) predicate.Playlist { + return predicate.Playlist(sql.FieldNotIn(FieldRevision, vs...)) +} + +// RevisionGT applies the GT predicate on the "revision" field. +func RevisionGT(v int) predicate.Playlist { + return predicate.Playlist(sql.FieldGT(FieldRevision, v)) +} + +// RevisionGTE applies the GTE predicate on the "revision" field. +func RevisionGTE(v int) predicate.Playlist { + return predicate.Playlist(sql.FieldGTE(FieldRevision, v)) +} + +// RevisionLT applies the LT predicate on the "revision" field. +func RevisionLT(v int) predicate.Playlist { + return predicate.Playlist(sql.FieldLT(FieldRevision, v)) +} + +// RevisionLTE applies the LTE predicate on the "revision" field. +func RevisionLTE(v int) predicate.Playlist { + return predicate.Playlist(sql.FieldLTE(FieldRevision, v)) +} + +// HasItems applies the HasEdge predicate on the "items" edge. +func HasItems() predicate.Playlist { + return predicate.Playlist(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, ItemsTable, ItemsColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasItemsWith applies the HasEdge predicate on the "items" edge with a given conditions (other predicates). +func HasItemsWith(preds ...predicate.Item) predicate.Playlist { + return predicate.Playlist(func(s *sql.Selector) { + step := newItemsStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + +// HasOwner applies the HasEdge predicate on the "owner" edge. +func HasOwner() predicate.Playlist { + return predicate.Playlist(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, OwnerTable, OwnerColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasOwnerWith applies the HasEdge predicate on the "owner" edge with a given conditions (other predicates). +func HasOwnerWith(preds ...predicate.Account) predicate.Playlist { + return predicate.Playlist(func(s *sql.Selector) { + step := newOwnerStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + +// And groups predicates with the AND operator between them. +func And(predicates ...predicate.Playlist) predicate.Playlist { + return predicate.Playlist(sql.AndPredicates(predicates...)) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.Playlist) predicate.Playlist { + return predicate.Playlist(sql.OrPredicates(predicates...)) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.Playlist) predicate.Playlist { + return predicate.Playlist(sql.NotPredicates(p)) +} diff --git a/applications/playlist-api/ent/playlist_create.go b/applications/playlist-api/ent/playlist_create.go new file mode 100644 index 00000000..a966dfb9 --- /dev/null +++ b/applications/playlist-api/ent/playlist_create.go @@ -0,0 +1,315 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "example.com/playlist-api/ent/account" + "example.com/playlist-api/ent/item" + "example.com/playlist-api/ent/playlist" + "github.com/google/uuid" +) + +// PlaylistCreate is the builder for creating a Playlist entity. +type PlaylistCreate struct { + config + mutation *PlaylistMutation + hooks []Hook +} + +// SetOwnerID sets the "owner_id" field. +func (_c *PlaylistCreate) SetOwnerID(v uuid.UUID) *PlaylistCreate { + _c.mutation.SetOwnerID(v) + return _c +} + +// SetName sets the "name" field. +func (_c *PlaylistCreate) SetName(v string) *PlaylistCreate { + _c.mutation.SetName(v) + return _c +} + +// SetRevision sets the "revision" field. +func (_c *PlaylistCreate) SetRevision(v int) *PlaylistCreate { + _c.mutation.SetRevision(v) + return _c +} + +// SetNillableRevision sets the "revision" field if the given value is not nil. +func (_c *PlaylistCreate) SetNillableRevision(v *int) *PlaylistCreate { + if v != nil { + _c.SetRevision(*v) + } + return _c +} + +// SetID sets the "id" field. +func (_c *PlaylistCreate) SetID(v uuid.UUID) *PlaylistCreate { + _c.mutation.SetID(v) + return _c +} + +// SetNillableID sets the "id" field if the given value is not nil. +func (_c *PlaylistCreate) SetNillableID(v *uuid.UUID) *PlaylistCreate { + if v != nil { + _c.SetID(*v) + } + return _c +} + +// AddItemIDs adds the "items" edge to the Item entity by IDs. +func (_c *PlaylistCreate) AddItemIDs(ids ...uuid.UUID) *PlaylistCreate { + _c.mutation.AddItemIDs(ids...) + return _c +} + +// AddItems adds the "items" edges to the Item entity. +func (_c *PlaylistCreate) AddItems(v ...*Item) *PlaylistCreate { + ids := make([]uuid.UUID, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _c.AddItemIDs(ids...) +} + +// SetOwner sets the "owner" edge to the Account entity. +func (_c *PlaylistCreate) SetOwner(v *Account) *PlaylistCreate { + return _c.SetOwnerID(v.ID) +} + +// Mutation returns the PlaylistMutation object of the builder. +func (_c *PlaylistCreate) Mutation() *PlaylistMutation { + return _c.mutation +} + +// Save creates the Playlist in the database. +func (_c *PlaylistCreate) Save(ctx context.Context) (*Playlist, error) { + _c.defaults() + return withHooks(ctx, _c.sqlSave, _c.mutation, _c.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (_c *PlaylistCreate) SaveX(ctx context.Context) *Playlist { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *PlaylistCreate) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *PlaylistCreate) 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 *PlaylistCreate) defaults() { + if _, ok := _c.mutation.Revision(); !ok { + v := playlist.DefaultRevision + _c.mutation.SetRevision(v) + } + if _, ok := _c.mutation.ID(); !ok { + v := playlist.DefaultID() + _c.mutation.SetID(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_c *PlaylistCreate) check() error { + if _, ok := _c.mutation.OwnerID(); !ok { + return &ValidationError{Name: "owner_id", err: errors.New(`ent: missing required field "Playlist.owner_id"`)} + } + if _, ok := _c.mutation.Name(); !ok { + return &ValidationError{Name: "name", err: errors.New(`ent: missing required field "Playlist.name"`)} + } + if v, ok := _c.mutation.Name(); ok { + if err := playlist.NameValidator(v); err != nil { + return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "Playlist.name": %w`, err)} + } + } + if _, ok := _c.mutation.Revision(); !ok { + return &ValidationError{Name: "revision", err: errors.New(`ent: missing required field "Playlist.revision"`)} + } + if v, ok := _c.mutation.Revision(); ok { + if err := playlist.RevisionValidator(v); err != nil { + return &ValidationError{Name: "revision", err: fmt.Errorf(`ent: validator failed for field "Playlist.revision": %w`, err)} + } + } + if len(_c.mutation.OwnerIDs()) == 0 { + return &ValidationError{Name: "owner", err: errors.New(`ent: missing required edge "Playlist.owner"`)} + } + return nil +} + +func (_c *PlaylistCreate) sqlSave(ctx context.Context) (*Playlist, 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 *PlaylistCreate) createSpec() (*Playlist, *sqlgraph.CreateSpec) { + var ( + _node = &Playlist{config: _c.config} + _spec = sqlgraph.NewCreateSpec(playlist.Table, sqlgraph.NewFieldSpec(playlist.FieldID, field.TypeUUID)) + ) + if id, ok := _c.mutation.ID(); ok { + _node.ID = id + _spec.ID.Value = &id + } + if value, ok := _c.mutation.Name(); ok { + _spec.SetField(playlist.FieldName, field.TypeString, value) + _node.Name = value + } + if value, ok := _c.mutation.Revision(); ok { + _spec.SetField(playlist.FieldRevision, field.TypeInt, value) + _node.Revision = value + } + if nodes := _c.mutation.ItemsIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: playlist.ItemsTable, + Columns: []string{playlist.ItemsColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(item.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges = append(_spec.Edges, edge) + } + if nodes := _c.mutation.OwnerIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: playlist.OwnerTable, + Columns: []string{playlist.OwnerColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(account.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _node.OwnerID = nodes[0] + _spec.Edges = append(_spec.Edges, edge) + } + return _node, _spec +} + +// PlaylistCreateBulk is the builder for creating many Playlist entities in bulk. +type PlaylistCreateBulk struct { + config + err error + builders []*PlaylistCreate +} + +// Save creates the Playlist entities in the database. +func (_c *PlaylistCreateBulk) Save(ctx context.Context) ([]*Playlist, error) { + if _c.err != nil { + return nil, _c.err + } + specs := make([]*sqlgraph.CreateSpec, len(_c.builders)) + nodes := make([]*Playlist, 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.(*PlaylistMutation) + 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} + } + } + } + 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 *PlaylistCreateBulk) SaveX(ctx context.Context) []*Playlist { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *PlaylistCreateBulk) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *PlaylistCreateBulk) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/applications/playlist-api/ent/playlist_delete.go b/applications/playlist-api/ent/playlist_delete.go new file mode 100644 index 00000000..12f1f1dc --- /dev/null +++ b/applications/playlist-api/ent/playlist_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" + "example.com/playlist-api/ent/playlist" + "example.com/playlist-api/ent/predicate" +) + +// PlaylistDelete is the builder for deleting a Playlist entity. +type PlaylistDelete struct { + config + hooks []Hook + mutation *PlaylistMutation +} + +// Where appends a list predicates to the PlaylistDelete builder. +func (_d *PlaylistDelete) Where(ps ...predicate.Playlist) *PlaylistDelete { + _d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (_d *PlaylistDelete) 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 *PlaylistDelete) ExecX(ctx context.Context) int { + n, err := _d.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (_d *PlaylistDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(playlist.Table, sqlgraph.NewFieldSpec(playlist.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 +} + +// PlaylistDeleteOne is the builder for deleting a single Playlist entity. +type PlaylistDeleteOne struct { + _d *PlaylistDelete +} + +// Where appends a list predicates to the PlaylistDelete builder. +func (_d *PlaylistDeleteOne) Where(ps ...predicate.Playlist) *PlaylistDeleteOne { + _d._d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query. +func (_d *PlaylistDeleteOne) Exec(ctx context.Context) error { + n, err := _d._d.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{playlist.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *PlaylistDeleteOne) ExecX(ctx context.Context) { + if err := _d.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/applications/playlist-api/ent/playlist_query.go b/applications/playlist-api/ent/playlist_query.go new file mode 100644 index 00000000..c0b2258f --- /dev/null +++ b/applications/playlist-api/ent/playlist_query.go @@ -0,0 +1,719 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "database/sql/driver" + "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" + "example.com/playlist-api/ent/account" + "example.com/playlist-api/ent/item" + "example.com/playlist-api/ent/playlist" + "example.com/playlist-api/ent/predicate" + "github.com/google/uuid" +) + +// PlaylistQuery is the builder for querying Playlist entities. +type PlaylistQuery struct { + config + ctx *QueryContext + order []playlist.OrderOption + inters []Interceptor + predicates []predicate.Playlist + withItems *ItemQuery + withOwner *AccountQuery + 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 PlaylistQuery builder. +func (_q *PlaylistQuery) Where(ps ...predicate.Playlist) *PlaylistQuery { + _q.predicates = append(_q.predicates, ps...) + return _q +} + +// Limit the number of records to be returned by this query. +func (_q *PlaylistQuery) Limit(limit int) *PlaylistQuery { + _q.ctx.Limit = &limit + return _q +} + +// Offset to start from. +func (_q *PlaylistQuery) Offset(offset int) *PlaylistQuery { + _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 *PlaylistQuery) Unique(unique bool) *PlaylistQuery { + _q.ctx.Unique = &unique + return _q +} + +// Order specifies how the records should be ordered. +func (_q *PlaylistQuery) Order(o ...playlist.OrderOption) *PlaylistQuery { + _q.order = append(_q.order, o...) + return _q +} + +// QueryItems chains the current query on the "items" edge. +func (_q *PlaylistQuery) QueryItems() *ItemQuery { + query := (&ItemClient{config: _q.config}).Query() + query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + selector := _q.sqlQuery(ctx) + if err := selector.Err(); err != nil { + return nil, err + } + step := sqlgraph.NewStep( + sqlgraph.From(playlist.Table, playlist.FieldID, selector), + sqlgraph.To(item.Table, item.FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, playlist.ItemsTable, playlist.ItemsColumn), + ) + fromU = sqlgraph.SetNeighbors(_q.driver.Dialect(), step) + return fromU, nil + } + return query +} + +// QueryOwner chains the current query on the "owner" edge. +func (_q *PlaylistQuery) QueryOwner() *AccountQuery { + query := (&AccountClient{config: _q.config}).Query() + query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + selector := _q.sqlQuery(ctx) + if err := selector.Err(); err != nil { + return nil, err + } + step := sqlgraph.NewStep( + sqlgraph.From(playlist.Table, playlist.FieldID, selector), + sqlgraph.To(account.Table, account.FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, playlist.OwnerTable, playlist.OwnerColumn), + ) + fromU = sqlgraph.SetNeighbors(_q.driver.Dialect(), step) + return fromU, nil + } + return query +} + +// First returns the first Playlist entity from the query. +// Returns a *NotFoundError when no Playlist was found. +func (_q *PlaylistQuery) First(ctx context.Context) (*Playlist, 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{playlist.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (_q *PlaylistQuery) FirstX(ctx context.Context) *Playlist { + node, err := _q.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// FirstID returns the first Playlist ID from the query. +// Returns a *NotFoundError when no Playlist ID was found. +func (_q *PlaylistQuery) 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{playlist.Label} + return + } + return ids[0], nil +} + +// FirstIDX is like FirstID, but panics if an error occurs. +func (_q *PlaylistQuery) FirstIDX(ctx context.Context) uuid.UUID { + id, err := _q.FirstID(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return id +} + +// Only returns a single Playlist entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one Playlist entity is found. +// Returns a *NotFoundError when no Playlist entities are found. +func (_q *PlaylistQuery) Only(ctx context.Context) (*Playlist, 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{playlist.Label} + default: + return nil, &NotSingularError{playlist.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (_q *PlaylistQuery) OnlyX(ctx context.Context) *Playlist { + node, err := _q.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// OnlyID is like Only, but returns the only Playlist ID in the query. +// Returns a *NotSingularError when more than one Playlist ID is found. +// Returns a *NotFoundError when no entities are found. +func (_q *PlaylistQuery) 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{playlist.Label} + default: + err = &NotSingularError{playlist.Label} + } + return +} + +// OnlyIDX is like OnlyID, but panics if an error occurs. +func (_q *PlaylistQuery) 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 Playlists. +func (_q *PlaylistQuery) All(ctx context.Context) ([]*Playlist, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryAll) + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*Playlist, *PlaylistQuery]() + return withInterceptors[[]*Playlist](ctx, _q, qr, _q.inters) +} + +// AllX is like All, but panics if an error occurs. +func (_q *PlaylistQuery) AllX(ctx context.Context) []*Playlist { + nodes, err := _q.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// IDs executes the query and returns a list of Playlist IDs. +func (_q *PlaylistQuery) 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(playlist.FieldID).Scan(ctx, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// IDsX is like IDs, but panics if an error occurs. +func (_q *PlaylistQuery) 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 *PlaylistQuery) 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[*PlaylistQuery](), _q.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (_q *PlaylistQuery) 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 *PlaylistQuery) 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 *PlaylistQuery) ExistX(ctx context.Context) bool { + exist, err := _q.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the PlaylistQuery 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 *PlaylistQuery) Clone() *PlaylistQuery { + if _q == nil { + return nil + } + return &PlaylistQuery{ + config: _q.config, + ctx: _q.ctx.Clone(), + order: append([]playlist.OrderOption{}, _q.order...), + inters: append([]Interceptor{}, _q.inters...), + predicates: append([]predicate.Playlist{}, _q.predicates...), + withItems: _q.withItems.Clone(), + withOwner: _q.withOwner.Clone(), + // clone intermediate query. + sql: _q.sql.Clone(), + path: _q.path, + } +} + +// WithItems tells the query-builder to eager-load the nodes that are connected to +// the "items" edge. The optional arguments are used to configure the query builder of the edge. +func (_q *PlaylistQuery) WithItems(opts ...func(*ItemQuery)) *PlaylistQuery { + query := (&ItemClient{config: _q.config}).Query() + for _, opt := range opts { + opt(query) + } + _q.withItems = query + return _q +} + +// WithOwner tells the query-builder to eager-load the nodes that are connected to +// the "owner" edge. The optional arguments are used to configure the query builder of the edge. +func (_q *PlaylistQuery) WithOwner(opts ...func(*AccountQuery)) *PlaylistQuery { + query := (&AccountClient{config: _q.config}).Query() + for _, opt := range opts { + opt(query) + } + _q.withOwner = query + return _q +} + +// 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 { +// OwnerID uuid.UUID `json:"owner_id,omitempty"` +// Count int `json:"count,omitempty"` +// } +// +// client.Playlist.Query(). +// GroupBy(playlist.FieldOwnerID). +// Aggregate(ent.Count()). +// Scan(ctx, &v) +func (_q *PlaylistQuery) GroupBy(field string, fields ...string) *PlaylistGroupBy { + _q.ctx.Fields = append([]string{field}, fields...) + grbuild := &PlaylistGroupBy{build: _q} + grbuild.flds = &_q.ctx.Fields + grbuild.label = playlist.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 { +// OwnerID uuid.UUID `json:"owner_id,omitempty"` +// } +// +// client.Playlist.Query(). +// Select(playlist.FieldOwnerID). +// Scan(ctx, &v) +func (_q *PlaylistQuery) Select(fields ...string) *PlaylistSelect { + _q.ctx.Fields = append(_q.ctx.Fields, fields...) + sbuild := &PlaylistSelect{PlaylistQuery: _q} + sbuild.label = playlist.Label + sbuild.flds, sbuild.scan = &_q.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a PlaylistSelect configured with the given aggregations. +func (_q *PlaylistQuery) Aggregate(fns ...AggregateFunc) *PlaylistSelect { + return _q.Select().Aggregate(fns...) +} + +func (_q *PlaylistQuery) 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 !playlist.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 *PlaylistQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*Playlist, error) { + var ( + nodes = []*Playlist{} + _spec = _q.querySpec() + loadedTypes = [2]bool{ + _q.withItems != nil, + _q.withOwner != nil, + } + ) + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*Playlist).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &Playlist{config: _q.config} + nodes = append(nodes, node) + 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) + } + if err := sqlgraph.QueryNodes(ctx, _q.driver, _spec); err != nil { + return nil, err + } + if len(nodes) == 0 { + return nodes, nil + } + if query := _q.withItems; query != nil { + if err := _q.loadItems(ctx, query, nodes, + func(n *Playlist) { n.Edges.Items = []*Item{} }, + func(n *Playlist, e *Item) { n.Edges.Items = append(n.Edges.Items, e) }); err != nil { + return nil, err + } + } + if query := _q.withOwner; query != nil { + if err := _q.loadOwner(ctx, query, nodes, nil, + func(n *Playlist, e *Account) { n.Edges.Owner = e }); err != nil { + return nil, err + } + } + return nodes, nil +} + +func (_q *PlaylistQuery) loadItems(ctx context.Context, query *ItemQuery, nodes []*Playlist, init func(*Playlist), assign func(*Playlist, *Item)) error { + fks := make([]driver.Value, 0, len(nodes)) + nodeids := make(map[uuid.UUID]*Playlist) + for i := range nodes { + fks = append(fks, nodes[i].ID) + nodeids[nodes[i].ID] = nodes[i] + if init != nil { + init(nodes[i]) + } + } + if len(query.ctx.Fields) > 0 { + query.ctx.AppendFieldOnce(item.FieldPlaylistID) + } + query.Where(predicate.Item(func(s *sql.Selector) { + s.Where(sql.InValues(s.C(playlist.ItemsColumn), fks...)) + })) + neighbors, err := query.All(ctx) + if err != nil { + return err + } + for _, n := range neighbors { + fk := n.PlaylistID + node, ok := nodeids[fk] + if !ok { + return fmt.Errorf(`unexpected referenced foreign-key "playlist_id" returned %v for node %v`, fk, n.ID) + } + assign(node, n) + } + return nil +} +func (_q *PlaylistQuery) loadOwner(ctx context.Context, query *AccountQuery, nodes []*Playlist, init func(*Playlist), assign func(*Playlist, *Account)) error { + ids := make([]uuid.UUID, 0, len(nodes)) + nodeids := make(map[uuid.UUID][]*Playlist) + for i := range nodes { + fk := nodes[i].OwnerID + if _, ok := nodeids[fk]; !ok { + ids = append(ids, fk) + } + nodeids[fk] = append(nodeids[fk], nodes[i]) + } + if len(ids) == 0 { + return nil + } + query.Where(account.IDIn(ids...)) + neighbors, err := query.All(ctx) + if err != nil { + return err + } + for _, n := range neighbors { + nodes, ok := nodeids[n.ID] + if !ok { + return fmt.Errorf(`unexpected foreign-key "owner_id" returned %v`, n.ID) + } + for i := range nodes { + assign(nodes[i], n) + } + } + return nil +} + +func (_q *PlaylistQuery) 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 *PlaylistQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(playlist.Table, playlist.Columns, sqlgraph.NewFieldSpec(playlist.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, playlist.FieldID) + for i := range fields { + if fields[i] != playlist.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, fields[i]) + } + } + if _q.withOwner != nil { + _spec.Node.AddColumnOnce(playlist.FieldOwnerID) + } + } + 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 *PlaylistQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(_q.driver.Dialect()) + t1 := builder.Table(playlist.Table) + columns := _q.ctx.Fields + if len(columns) == 0 { + columns = playlist.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 *PlaylistQuery) ForUpdate(opts ...sql.LockOption) *PlaylistQuery { + 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 *PlaylistQuery) ForShare(opts ...sql.LockOption) *PlaylistQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForShare(opts...) + }) + return _q +} + +// PlaylistGroupBy is the group-by builder for Playlist entities. +type PlaylistGroupBy struct { + selector + build *PlaylistQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (_g *PlaylistGroupBy) Aggregate(fns ...AggregateFunc) *PlaylistGroupBy { + _g.fns = append(_g.fns, fns...) + return _g +} + +// Scan applies the selector query and scans the result into the given value. +func (_g *PlaylistGroupBy) 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[*PlaylistQuery, *PlaylistGroupBy](ctx, _g.build, _g, _g.build.inters, v) +} + +func (_g *PlaylistGroupBy) sqlScan(ctx context.Context, root *PlaylistQuery, 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) +} + +// PlaylistSelect is the builder for selecting fields of Playlist entities. +type PlaylistSelect struct { + *PlaylistQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (_s *PlaylistSelect) Aggregate(fns ...AggregateFunc) *PlaylistSelect { + _s.fns = append(_s.fns, fns...) + return _s +} + +// Scan applies the selector query and scans the result into the given value. +func (_s *PlaylistSelect) 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[*PlaylistQuery, *PlaylistSelect](ctx, _s.PlaylistQuery, _s, _s.inters, v) +} + +func (_s *PlaylistSelect) sqlScan(ctx context.Context, root *PlaylistQuery, 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/applications/playlist-api/ent/playlist_update.go b/applications/playlist-api/ent/playlist_update.go new file mode 100644 index 00000000..6a9fe7de --- /dev/null +++ b/applications/playlist-api/ent/playlist_update.go @@ -0,0 +1,469 @@ +// 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" + "example.com/playlist-api/ent/item" + "example.com/playlist-api/ent/playlist" + "example.com/playlist-api/ent/predicate" + "github.com/google/uuid" +) + +// PlaylistUpdate is the builder for updating Playlist entities. +type PlaylistUpdate struct { + config + hooks []Hook + mutation *PlaylistMutation +} + +// Where appends a list predicates to the PlaylistUpdate builder. +func (_u *PlaylistUpdate) Where(ps ...predicate.Playlist) *PlaylistUpdate { + _u.mutation.Where(ps...) + return _u +} + +// SetName sets the "name" field. +func (_u *PlaylistUpdate) SetName(v string) *PlaylistUpdate { + _u.mutation.SetName(v) + return _u +} + +// SetNillableName sets the "name" field if the given value is not nil. +func (_u *PlaylistUpdate) SetNillableName(v *string) *PlaylistUpdate { + if v != nil { + _u.SetName(*v) + } + return _u +} + +// SetRevision sets the "revision" field. +func (_u *PlaylistUpdate) SetRevision(v int) *PlaylistUpdate { + _u.mutation.ResetRevision() + _u.mutation.SetRevision(v) + return _u +} + +// SetNillableRevision sets the "revision" field if the given value is not nil. +func (_u *PlaylistUpdate) SetNillableRevision(v *int) *PlaylistUpdate { + if v != nil { + _u.SetRevision(*v) + } + return _u +} + +// AddRevision adds value to the "revision" field. +func (_u *PlaylistUpdate) AddRevision(v int) *PlaylistUpdate { + _u.mutation.AddRevision(v) + return _u +} + +// AddItemIDs adds the "items" edge to the Item entity by IDs. +func (_u *PlaylistUpdate) AddItemIDs(ids ...uuid.UUID) *PlaylistUpdate { + _u.mutation.AddItemIDs(ids...) + return _u +} + +// AddItems adds the "items" edges to the Item entity. +func (_u *PlaylistUpdate) AddItems(v ...*Item) *PlaylistUpdate { + ids := make([]uuid.UUID, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.AddItemIDs(ids...) +} + +// Mutation returns the PlaylistMutation object of the builder. +func (_u *PlaylistUpdate) Mutation() *PlaylistMutation { + return _u.mutation +} + +// ClearItems clears all "items" edges to the Item entity. +func (_u *PlaylistUpdate) ClearItems() *PlaylistUpdate { + _u.mutation.ClearItems() + return _u +} + +// RemoveItemIDs removes the "items" edge to Item entities by IDs. +func (_u *PlaylistUpdate) RemoveItemIDs(ids ...uuid.UUID) *PlaylistUpdate { + _u.mutation.RemoveItemIDs(ids...) + return _u +} + +// RemoveItems removes "items" edges to Item entities. +func (_u *PlaylistUpdate) RemoveItems(v ...*Item) *PlaylistUpdate { + ids := make([]uuid.UUID, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.RemoveItemIDs(ids...) +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (_u *PlaylistUpdate) 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 *PlaylistUpdate) SaveX(ctx context.Context) int { + affected, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (_u *PlaylistUpdate) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *PlaylistUpdate) 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 *PlaylistUpdate) check() error { + if v, ok := _u.mutation.Name(); ok { + if err := playlist.NameValidator(v); err != nil { + return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "Playlist.name": %w`, err)} + } + } + if v, ok := _u.mutation.Revision(); ok { + if err := playlist.RevisionValidator(v); err != nil { + return &ValidationError{Name: "revision", err: fmt.Errorf(`ent: validator failed for field "Playlist.revision": %w`, err)} + } + } + if _u.mutation.OwnerCleared() && len(_u.mutation.OwnerIDs()) > 0 { + return errors.New(`ent: clearing a required unique edge "Playlist.owner"`) + } + return nil +} + +func (_u *PlaylistUpdate) sqlSave(ctx context.Context) (_node int, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(playlist.Table, playlist.Columns, sqlgraph.NewFieldSpec(playlist.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(playlist.FieldName, field.TypeString, value) + } + if value, ok := _u.mutation.Revision(); ok { + _spec.SetField(playlist.FieldRevision, field.TypeInt, value) + } + if value, ok := _u.mutation.AddedRevision(); ok { + _spec.AddField(playlist.FieldRevision, field.TypeInt, value) + } + if _u.mutation.ItemsCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: playlist.ItemsTable, + Columns: []string{playlist.ItemsColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(item.FieldID, field.TypeUUID), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := _u.mutation.RemovedItemsIDs(); len(nodes) > 0 && !_u.mutation.ItemsCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: playlist.ItemsTable, + Columns: []string{playlist.ItemsColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(item.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := _u.mutation.ItemsIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: playlist.ItemsTable, + Columns: []string{playlist.ItemsColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(item.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{playlist.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + _u.mutation.done = true + return _node, nil +} + +// PlaylistUpdateOne is the builder for updating a single Playlist entity. +type PlaylistUpdateOne struct { + config + fields []string + hooks []Hook + mutation *PlaylistMutation +} + +// SetName sets the "name" field. +func (_u *PlaylistUpdateOne) SetName(v string) *PlaylistUpdateOne { + _u.mutation.SetName(v) + return _u +} + +// SetNillableName sets the "name" field if the given value is not nil. +func (_u *PlaylistUpdateOne) SetNillableName(v *string) *PlaylistUpdateOne { + if v != nil { + _u.SetName(*v) + } + return _u +} + +// SetRevision sets the "revision" field. +func (_u *PlaylistUpdateOne) SetRevision(v int) *PlaylistUpdateOne { + _u.mutation.ResetRevision() + _u.mutation.SetRevision(v) + return _u +} + +// SetNillableRevision sets the "revision" field if the given value is not nil. +func (_u *PlaylistUpdateOne) SetNillableRevision(v *int) *PlaylistUpdateOne { + if v != nil { + _u.SetRevision(*v) + } + return _u +} + +// AddRevision adds value to the "revision" field. +func (_u *PlaylistUpdateOne) AddRevision(v int) *PlaylistUpdateOne { + _u.mutation.AddRevision(v) + return _u +} + +// AddItemIDs adds the "items" edge to the Item entity by IDs. +func (_u *PlaylistUpdateOne) AddItemIDs(ids ...uuid.UUID) *PlaylistUpdateOne { + _u.mutation.AddItemIDs(ids...) + return _u +} + +// AddItems adds the "items" edges to the Item entity. +func (_u *PlaylistUpdateOne) AddItems(v ...*Item) *PlaylistUpdateOne { + ids := make([]uuid.UUID, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.AddItemIDs(ids...) +} + +// Mutation returns the PlaylistMutation object of the builder. +func (_u *PlaylistUpdateOne) Mutation() *PlaylistMutation { + return _u.mutation +} + +// ClearItems clears all "items" edges to the Item entity. +func (_u *PlaylistUpdateOne) ClearItems() *PlaylistUpdateOne { + _u.mutation.ClearItems() + return _u +} + +// RemoveItemIDs removes the "items" edge to Item entities by IDs. +func (_u *PlaylistUpdateOne) RemoveItemIDs(ids ...uuid.UUID) *PlaylistUpdateOne { + _u.mutation.RemoveItemIDs(ids...) + return _u +} + +// RemoveItems removes "items" edges to Item entities. +func (_u *PlaylistUpdateOne) RemoveItems(v ...*Item) *PlaylistUpdateOne { + ids := make([]uuid.UUID, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.RemoveItemIDs(ids...) +} + +// Where appends a list predicates to the PlaylistUpdate builder. +func (_u *PlaylistUpdateOne) Where(ps ...predicate.Playlist) *PlaylistUpdateOne { + _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 *PlaylistUpdateOne) Select(field string, fields ...string) *PlaylistUpdateOne { + _u.fields = append([]string{field}, fields...) + return _u +} + +// Save executes the query and returns the updated Playlist entity. +func (_u *PlaylistUpdateOne) Save(ctx context.Context) (*Playlist, error) { + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *PlaylistUpdateOne) SaveX(ctx context.Context) *Playlist { + node, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (_u *PlaylistUpdateOne) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *PlaylistUpdateOne) 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 *PlaylistUpdateOne) check() error { + if v, ok := _u.mutation.Name(); ok { + if err := playlist.NameValidator(v); err != nil { + return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "Playlist.name": %w`, err)} + } + } + if v, ok := _u.mutation.Revision(); ok { + if err := playlist.RevisionValidator(v); err != nil { + return &ValidationError{Name: "revision", err: fmt.Errorf(`ent: validator failed for field "Playlist.revision": %w`, err)} + } + } + if _u.mutation.OwnerCleared() && len(_u.mutation.OwnerIDs()) > 0 { + return errors.New(`ent: clearing a required unique edge "Playlist.owner"`) + } + return nil +} + +func (_u *PlaylistUpdateOne) sqlSave(ctx context.Context) (_node *Playlist, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(playlist.Table, playlist.Columns, sqlgraph.NewFieldSpec(playlist.FieldID, field.TypeUUID)) + id, ok := _u.mutation.ID() + if !ok { + return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "Playlist.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, playlist.FieldID) + for _, f := range fields { + if !playlist.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + if f != playlist.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(playlist.FieldName, field.TypeString, value) + } + if value, ok := _u.mutation.Revision(); ok { + _spec.SetField(playlist.FieldRevision, field.TypeInt, value) + } + if value, ok := _u.mutation.AddedRevision(); ok { + _spec.AddField(playlist.FieldRevision, field.TypeInt, value) + } + if _u.mutation.ItemsCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: playlist.ItemsTable, + Columns: []string{playlist.ItemsColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(item.FieldID, field.TypeUUID), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := _u.mutation.RemovedItemsIDs(); len(nodes) > 0 && !_u.mutation.ItemsCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: playlist.ItemsTable, + Columns: []string{playlist.ItemsColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(item.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := _u.mutation.ItemsIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: playlist.ItemsTable, + Columns: []string{playlist.ItemsColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(item.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + _node = &Playlist{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{playlist.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/applications/playlist-api/ent/predicate/predicate.go b/applications/playlist-api/ent/predicate/predicate.go new file mode 100644 index 00000000..2d99ecd0 --- /dev/null +++ b/applications/playlist-api/ent/predicate/predicate.go @@ -0,0 +1,19 @@ +// Code generated by ent, DO NOT EDIT. + +package predicate + +import ( + "entgo.io/ent/dialect/sql" +) + +// Account is the predicate function for account builders. +type Account func(*sql.Selector) + +// Item is the predicate function for item builders. +type Item func(*sql.Selector) + +// Playlist is the predicate function for playlist builders. +type Playlist func(*sql.Selector) + +// Track is the predicate function for track builders. +type Track func(*sql.Selector) diff --git a/applications/playlist-api/ent/runtime.go b/applications/playlist-api/ent/runtime.go new file mode 100644 index 00000000..06e11264 --- /dev/null +++ b/applications/playlist-api/ent/runtime.go @@ -0,0 +1,127 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "example.com/playlist-api/ent/item" + "example.com/playlist-api/ent/playlist" + "example.com/playlist-api/ent/schema" + "example.com/playlist-api/ent/track" + "github.com/google/uuid" +) + +// The init function reads all schema descriptors with runtime code +// (default values, validators, hooks and policies) and stitches it +// to their package variables. +func init() { + itemFields := schema.Item{}.Fields() + _ = itemFields + // itemDescPosition is the schema descriptor for position field. + itemDescPosition := itemFields[3].Descriptor() + // item.PositionValidator is a validator for the "position" field. It is called by the builders before save. + item.PositionValidator = func() func(int) error { + validators := itemDescPosition.Validators + fns := [...]func(int) error{ + validators[0].(func(int) error), + validators[1].(func(int) error), + } + return func(position int) error { + for _, fn := range fns { + if err := fn(position); err != nil { + return err + } + } + return nil + } + }() + // itemDescID is the schema descriptor for id field. + itemDescID := itemFields[0].Descriptor() + // item.DefaultID holds the default value on creation for the id field. + item.DefaultID = itemDescID.Default.(func() uuid.UUID) + playlistFields := schema.Playlist{}.Fields() + _ = playlistFields + // playlistDescName is the schema descriptor for name field. + playlistDescName := playlistFields[2].Descriptor() + // playlist.NameValidator is a validator for the "name" field. It is called by the builders before save. + playlist.NameValidator = func() func(string) error { + validators := playlistDescName.Validators + fns := [...]func(string) error{ + validators[0].(func(string) error), + validators[1].(func(string) error), + } + return func(name string) error { + for _, fn := range fns { + if err := fn(name); err != nil { + return err + } + } + return nil + } + }() + // playlistDescRevision is the schema descriptor for revision field. + playlistDescRevision := playlistFields[3].Descriptor() + // playlist.DefaultRevision holds the default value on creation for the revision field. + playlist.DefaultRevision = playlistDescRevision.Default.(int) + // playlist.RevisionValidator is a validator for the "revision" field. It is called by the builders before save. + playlist.RevisionValidator = playlistDescRevision.Validators[0].(func(int) error) + // playlistDescID is the schema descriptor for id field. + playlistDescID := playlistFields[0].Descriptor() + // playlist.DefaultID holds the default value on creation for the id field. + playlist.DefaultID = playlistDescID.Default.(func() uuid.UUID) + trackFields := schema.Track{}.Fields() + _ = trackFields + // trackDescTitle is the schema descriptor for title field. + trackDescTitle := trackFields[1].Descriptor() + // track.TitleValidator is a validator for the "title" field. It is called by the builders before save. + track.TitleValidator = func() func(string) error { + validators := trackDescTitle.Validators + fns := [...]func(string) error{ + validators[0].(func(string) error), + validators[1].(func(string) error), + } + return func(title string) error { + for _, fn := range fns { + if err := fn(title); err != nil { + return err + } + } + return nil + } + }() + // trackDescArtist is the schema descriptor for artist field. + trackDescArtist := trackFields[2].Descriptor() + // track.ArtistValidator is a validator for the "artist" field. It is called by the builders before save. + track.ArtistValidator = func() func(string) error { + validators := trackDescArtist.Validators + fns := [...]func(string) error{ + validators[0].(func(string) error), + validators[1].(func(string) error), + } + return func(artist string) error { + for _, fn := range fns { + if err := fn(artist); err != nil { + return err + } + } + return nil + } + }() + // trackDescDurationSeconds is the schema descriptor for duration_seconds field. + trackDescDurationSeconds := trackFields[3].Descriptor() + // track.DurationSecondsValidator is a validator for the "duration_seconds" field. It is called by the builders before save. + track.DurationSecondsValidator = func() func(int) error { + validators := trackDescDurationSeconds.Validators + fns := [...]func(int) error{ + validators[0].(func(int) error), + validators[1].(func(int) error), + } + return func(duration_seconds int) error { + for _, fn := range fns { + if err := fn(duration_seconds); err != nil { + return err + } + } + return nil + } + }() +} diff --git a/applications/playlist-api/ent/runtime/runtime.go b/applications/playlist-api/ent/runtime/runtime.go new file mode 100644 index 00000000..7753f708 --- /dev/null +++ b/applications/playlist-api/ent/runtime/runtime.go @@ -0,0 +1,10 @@ +// Code generated by ent, DO NOT EDIT. + +package runtime + +// The schema-stitching logic is generated in example.com/playlist-api/ent/runtime.go + +const ( + Version = "v0.14.6" // Version of ent codegen. + Sum = "h1:/f2696BpwuWAEEG6PVGWflg6+Inrpq4pRWuNlWz/Skk=" // Sum of ent codegen. +) diff --git a/applications/playlist-api/ent/schema/account.go b/applications/playlist-api/ent/schema/account.go new file mode 100644 index 00000000..ecea5c61 --- /dev/null +++ b/applications/playlist-api/ent/schema/account.go @@ -0,0 +1,13 @@ +package schema + +import ( + "entgo.io/ent" + "entgo.io/ent/schema/edge" + "entgo.io/ent/schema/field" + "github.com/google/uuid" +) + +type Account struct{ ent.Schema } + +func (Account) Fields() []ent.Field { return []ent.Field{field.UUID("id", uuid.UUID{})} } +func (Account) Edges() []ent.Edge { return []ent.Edge{edge.To("playlists", Playlist.Type)} } diff --git a/applications/playlist-api/ent/schema/item.go b/applications/playlist-api/ent/schema/item.go new file mode 100644 index 00000000..b729a439 --- /dev/null +++ b/applications/playlist-api/ent/schema/item.go @@ -0,0 +1,21 @@ +package schema + +import ( + "entgo.io/ent" + "entgo.io/ent/schema/edge" + "entgo.io/ent/schema/field" + "entgo.io/ent/schema/index" + "github.com/google/uuid" +) + +type Item struct{ ent.Schema } + +func (Item) Fields() []ent.Field { + return []ent.Field{field.UUID("id", uuid.UUID{}).Default(uuid.New), field.UUID("playlist_id", uuid.UUID{}).Immutable(), field.UUID("track_id", uuid.UUID{}).Immutable(), field.Int("position").Min(1).Max(50)} +} +func (Item) Edges() []ent.Edge { + return []ent.Edge{edge.From("playlist", Playlist.Type).Ref("items").Field("playlist_id").Unique().Required().Immutable(), edge.From("track", Track.Type).Ref("items").Field("track_id").Unique().Required().Immutable()} +} +func (Item) Indexes() []ent.Index { + return []ent.Index{index.Fields("playlist_id", "track_id").Unique(), index.Fields("playlist_id", "position").Unique().StorageKey("items_playlist_position")} +} diff --git a/applications/playlist-api/ent/schema/playlist.go b/applications/playlist-api/ent/schema/playlist.go new file mode 100644 index 00000000..cbfaabd4 --- /dev/null +++ b/applications/playlist-api/ent/schema/playlist.go @@ -0,0 +1,19 @@ +package schema + +import ( + "entgo.io/ent" + "entgo.io/ent/schema/edge" + "entgo.io/ent/schema/field" + "entgo.io/ent/schema/index" + "github.com/google/uuid" +) + +type Playlist struct{ ent.Schema } + +func (Playlist) Fields() []ent.Field { + return []ent.Field{field.UUID("id", uuid.UUID{}).Default(uuid.New), field.UUID("owner_id", uuid.UUID{}).Immutable(), field.String("name").MaxLen(80).NotEmpty(), field.Int("revision").Default(1).Min(1)} +} +func (Playlist) Edges() []ent.Edge { + return []ent.Edge{edge.To("items", Item.Type), edge.From("owner", Account.Type).Ref("playlists").Field("owner_id").Unique().Required().Immutable()} +} +func (Playlist) Indexes() []ent.Index { return []ent.Index{index.Fields("owner_id", "id")} } diff --git a/applications/playlist-api/ent/schema/track.go b/applications/playlist-api/ent/schema/track.go new file mode 100644 index 00000000..ce2bc44c --- /dev/null +++ b/applications/playlist-api/ent/schema/track.go @@ -0,0 +1,15 @@ +package schema + +import ( + "entgo.io/ent" + "entgo.io/ent/schema/edge" + "entgo.io/ent/schema/field" + "github.com/google/uuid" +) + +type Track struct{ ent.Schema } + +func (Track) Fields() []ent.Field { + return []ent.Field{field.UUID("id", uuid.UUID{}), field.String("title").MaxLen(80).NotEmpty(), field.String("artist").MaxLen(80).NotEmpty(), field.Int("duration_seconds").Min(1).Max(3600)} +} +func (Track) Edges() []ent.Edge { return []ent.Edge{edge.To("items", Item.Type)} } diff --git a/applications/playlist-api/ent/track.go b/applications/playlist-api/ent/track.go new file mode 100644 index 00000000..76478897 --- /dev/null +++ b/applications/playlist-api/ent/track.go @@ -0,0 +1,154 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "fmt" + "strings" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "example.com/playlist-api/ent/track" + "github.com/google/uuid" +) + +// Track is the model entity for the Track schema. +type Track struct { + config `json:"-"` + // ID of the ent. + ID uuid.UUID `json:"id,omitempty"` + // Title holds the value of the "title" field. + Title string `json:"title,omitempty"` + // Artist holds the value of the "artist" field. + Artist string `json:"artist,omitempty"` + // DurationSeconds holds the value of the "duration_seconds" field. + DurationSeconds int `json:"duration_seconds,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the TrackQuery when eager-loading is set. + Edges TrackEdges `json:"edges"` + selectValues sql.SelectValues +} + +// TrackEdges holds the relations/edges for other nodes in the graph. +type TrackEdges struct { + // Items holds the value of the items edge. + Items []*Item `json:"items,omitempty"` + // loadedTypes holds the information for reporting if a + // type was loaded (or requested) in eager-loading or not. + loadedTypes [1]bool +} + +// ItemsOrErr returns the Items value or an error if the edge +// was not loaded in eager-loading. +func (e TrackEdges) ItemsOrErr() ([]*Item, error) { + if e.loadedTypes[0] { + return e.Items, nil + } + return nil, &NotLoadedError{edge: "items"} +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*Track) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case track.FieldDurationSeconds: + values[i] = new(sql.NullInt64) + case track.FieldTitle, track.FieldArtist: + values[i] = new(sql.NullString) + case track.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 Track fields. +func (_m *Track) 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 track.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 track.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 track.FieldArtist: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field artist", values[i]) + } else if value.Valid { + _m.Artist = value.String + } + case track.FieldDurationSeconds: + if value, ok := values[i].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for field duration_seconds", values[i]) + } else if value.Valid { + _m.DurationSeconds = int(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 Track. +// This includes values selected through modifiers, order, etc. +func (_m *Track) Value(name string) (ent.Value, error) { + return _m.selectValues.Get(name) +} + +// QueryItems queries the "items" edge of the Track entity. +func (_m *Track) QueryItems() *ItemQuery { + return NewTrackClient(_m.config).QueryItems(_m) +} + +// Update returns a builder for updating this Track. +// Note that you need to call Track.Unwrap() before calling this method if this Track +// was returned from a transaction, and the transaction was committed or rolled back. +func (_m *Track) Update() *TrackUpdateOne { + return NewTrackClient(_m.config).UpdateOne(_m) +} + +// Unwrap unwraps the Track 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 *Track) Unwrap() *Track { + _tx, ok := _m.config.driver.(*txDriver) + if !ok { + panic("ent: Track is not a transactional entity") + } + _m.config.driver = _tx.drv + return _m +} + +// String implements the fmt.Stringer. +func (_m *Track) String() string { + var builder strings.Builder + builder.WriteString("Track(") + builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID)) + builder.WriteString("title=") + builder.WriteString(_m.Title) + builder.WriteString(", ") + builder.WriteString("artist=") + builder.WriteString(_m.Artist) + builder.WriteString(", ") + builder.WriteString("duration_seconds=") + builder.WriteString(fmt.Sprintf("%v", _m.DurationSeconds)) + builder.WriteByte(')') + return builder.String() +} + +// Tracks is a parsable slice of Track. +type Tracks []*Track diff --git a/applications/playlist-api/ent/track/track.go b/applications/playlist-api/ent/track/track.go new file mode 100644 index 00000000..b2668057 --- /dev/null +++ b/applications/playlist-api/ent/track/track.go @@ -0,0 +1,103 @@ +// Code generated by ent, DO NOT EDIT. + +package track + +import ( + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" +) + +const ( + // Label holds the string label denoting the track type in the database. + Label = "track" + // FieldID holds the string denoting the id field in the database. + FieldID = "id" + // FieldTitle holds the string denoting the title field in the database. + FieldTitle = "title" + // FieldArtist holds the string denoting the artist field in the database. + FieldArtist = "artist" + // FieldDurationSeconds holds the string denoting the duration_seconds field in the database. + FieldDurationSeconds = "duration_seconds" + // EdgeItems holds the string denoting the items edge name in mutations. + EdgeItems = "items" + // Table holds the table name of the track in the database. + Table = "tracks" + // ItemsTable is the table that holds the items relation/edge. + ItemsTable = "items" + // ItemsInverseTable is the table name for the Item entity. + // It exists in this package in order to avoid circular dependency with the "item" package. + ItemsInverseTable = "items" + // ItemsColumn is the table column denoting the items relation/edge. + ItemsColumn = "track_id" +) + +// Columns holds all SQL columns for track fields. +var Columns = []string{ + FieldID, + FieldTitle, + FieldArtist, + FieldDurationSeconds, +} + +// 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 ( + // TitleValidator is a validator for the "title" field. It is called by the builders before save. + TitleValidator func(string) error + // ArtistValidator is a validator for the "artist" field. It is called by the builders before save. + ArtistValidator func(string) error + // DurationSecondsValidator is a validator for the "duration_seconds" field. It is called by the builders before save. + DurationSecondsValidator func(int) error +) + +// OrderOption defines the ordering options for the Track 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() +} + +// ByTitle orders the results by the title field. +func ByTitle(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldTitle, opts...).ToFunc() +} + +// ByArtist orders the results by the artist field. +func ByArtist(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldArtist, opts...).ToFunc() +} + +// ByDurationSeconds orders the results by the duration_seconds field. +func ByDurationSeconds(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldDurationSeconds, opts...).ToFunc() +} + +// ByItemsCount orders the results by items count. +func ByItemsCount(opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborsCount(s, newItemsStep(), opts...) + } +} + +// ByItems orders the results by items terms. +func ByItems(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newItemsStep(), append([]sql.OrderTerm{term}, terms...)...) + } +} +func newItemsStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(ItemsInverseTable, FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, ItemsTable, ItemsColumn), + ) +} diff --git a/applications/playlist-api/ent/track/where.go b/applications/playlist-api/ent/track/where.go new file mode 100644 index 00000000..e247a4bb --- /dev/null +++ b/applications/playlist-api/ent/track/where.go @@ -0,0 +1,278 @@ +// Code generated by ent, DO NOT EDIT. + +package track + +import ( + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "example.com/playlist-api/ent/predicate" + "github.com/google/uuid" +) + +// ID filters vertices based on their ID field. +func ID(id uuid.UUID) predicate.Track { + return predicate.Track(sql.FieldEQ(FieldID, id)) +} + +// IDEQ applies the EQ predicate on the ID field. +func IDEQ(id uuid.UUID) predicate.Track { + return predicate.Track(sql.FieldEQ(FieldID, id)) +} + +// IDNEQ applies the NEQ predicate on the ID field. +func IDNEQ(id uuid.UUID) predicate.Track { + return predicate.Track(sql.FieldNEQ(FieldID, id)) +} + +// IDIn applies the In predicate on the ID field. +func IDIn(ids ...uuid.UUID) predicate.Track { + return predicate.Track(sql.FieldIn(FieldID, ids...)) +} + +// IDNotIn applies the NotIn predicate on the ID field. +func IDNotIn(ids ...uuid.UUID) predicate.Track { + return predicate.Track(sql.FieldNotIn(FieldID, ids...)) +} + +// IDGT applies the GT predicate on the ID field. +func IDGT(id uuid.UUID) predicate.Track { + return predicate.Track(sql.FieldGT(FieldID, id)) +} + +// IDGTE applies the GTE predicate on the ID field. +func IDGTE(id uuid.UUID) predicate.Track { + return predicate.Track(sql.FieldGTE(FieldID, id)) +} + +// IDLT applies the LT predicate on the ID field. +func IDLT(id uuid.UUID) predicate.Track { + return predicate.Track(sql.FieldLT(FieldID, id)) +} + +// IDLTE applies the LTE predicate on the ID field. +func IDLTE(id uuid.UUID) predicate.Track { + return predicate.Track(sql.FieldLTE(FieldID, id)) +} + +// Title applies equality check predicate on the "title" field. It's identical to TitleEQ. +func Title(v string) predicate.Track { + return predicate.Track(sql.FieldEQ(FieldTitle, v)) +} + +// Artist applies equality check predicate on the "artist" field. It's identical to ArtistEQ. +func Artist(v string) predicate.Track { + return predicate.Track(sql.FieldEQ(FieldArtist, v)) +} + +// DurationSeconds applies equality check predicate on the "duration_seconds" field. It's identical to DurationSecondsEQ. +func DurationSeconds(v int) predicate.Track { + return predicate.Track(sql.FieldEQ(FieldDurationSeconds, v)) +} + +// TitleEQ applies the EQ predicate on the "title" field. +func TitleEQ(v string) predicate.Track { + return predicate.Track(sql.FieldEQ(FieldTitle, v)) +} + +// TitleNEQ applies the NEQ predicate on the "title" field. +func TitleNEQ(v string) predicate.Track { + return predicate.Track(sql.FieldNEQ(FieldTitle, v)) +} + +// TitleIn applies the In predicate on the "title" field. +func TitleIn(vs ...string) predicate.Track { + return predicate.Track(sql.FieldIn(FieldTitle, vs...)) +} + +// TitleNotIn applies the NotIn predicate on the "title" field. +func TitleNotIn(vs ...string) predicate.Track { + return predicate.Track(sql.FieldNotIn(FieldTitle, vs...)) +} + +// TitleGT applies the GT predicate on the "title" field. +func TitleGT(v string) predicate.Track { + return predicate.Track(sql.FieldGT(FieldTitle, v)) +} + +// TitleGTE applies the GTE predicate on the "title" field. +func TitleGTE(v string) predicate.Track { + return predicate.Track(sql.FieldGTE(FieldTitle, v)) +} + +// TitleLT applies the LT predicate on the "title" field. +func TitleLT(v string) predicate.Track { + return predicate.Track(sql.FieldLT(FieldTitle, v)) +} + +// TitleLTE applies the LTE predicate on the "title" field. +func TitleLTE(v string) predicate.Track { + return predicate.Track(sql.FieldLTE(FieldTitle, v)) +} + +// TitleContains applies the Contains predicate on the "title" field. +func TitleContains(v string) predicate.Track { + return predicate.Track(sql.FieldContains(FieldTitle, v)) +} + +// TitleHasPrefix applies the HasPrefix predicate on the "title" field. +func TitleHasPrefix(v string) predicate.Track { + return predicate.Track(sql.FieldHasPrefix(FieldTitle, v)) +} + +// TitleHasSuffix applies the HasSuffix predicate on the "title" field. +func TitleHasSuffix(v string) predicate.Track { + return predicate.Track(sql.FieldHasSuffix(FieldTitle, v)) +} + +// TitleEqualFold applies the EqualFold predicate on the "title" field. +func TitleEqualFold(v string) predicate.Track { + return predicate.Track(sql.FieldEqualFold(FieldTitle, v)) +} + +// TitleContainsFold applies the ContainsFold predicate on the "title" field. +func TitleContainsFold(v string) predicate.Track { + return predicate.Track(sql.FieldContainsFold(FieldTitle, v)) +} + +// ArtistEQ applies the EQ predicate on the "artist" field. +func ArtistEQ(v string) predicate.Track { + return predicate.Track(sql.FieldEQ(FieldArtist, v)) +} + +// ArtistNEQ applies the NEQ predicate on the "artist" field. +func ArtistNEQ(v string) predicate.Track { + return predicate.Track(sql.FieldNEQ(FieldArtist, v)) +} + +// ArtistIn applies the In predicate on the "artist" field. +func ArtistIn(vs ...string) predicate.Track { + return predicate.Track(sql.FieldIn(FieldArtist, vs...)) +} + +// ArtistNotIn applies the NotIn predicate on the "artist" field. +func ArtistNotIn(vs ...string) predicate.Track { + return predicate.Track(sql.FieldNotIn(FieldArtist, vs...)) +} + +// ArtistGT applies the GT predicate on the "artist" field. +func ArtistGT(v string) predicate.Track { + return predicate.Track(sql.FieldGT(FieldArtist, v)) +} + +// ArtistGTE applies the GTE predicate on the "artist" field. +func ArtistGTE(v string) predicate.Track { + return predicate.Track(sql.FieldGTE(FieldArtist, v)) +} + +// ArtistLT applies the LT predicate on the "artist" field. +func ArtistLT(v string) predicate.Track { + return predicate.Track(sql.FieldLT(FieldArtist, v)) +} + +// ArtistLTE applies the LTE predicate on the "artist" field. +func ArtistLTE(v string) predicate.Track { + return predicate.Track(sql.FieldLTE(FieldArtist, v)) +} + +// ArtistContains applies the Contains predicate on the "artist" field. +func ArtistContains(v string) predicate.Track { + return predicate.Track(sql.FieldContains(FieldArtist, v)) +} + +// ArtistHasPrefix applies the HasPrefix predicate on the "artist" field. +func ArtistHasPrefix(v string) predicate.Track { + return predicate.Track(sql.FieldHasPrefix(FieldArtist, v)) +} + +// ArtistHasSuffix applies the HasSuffix predicate on the "artist" field. +func ArtistHasSuffix(v string) predicate.Track { + return predicate.Track(sql.FieldHasSuffix(FieldArtist, v)) +} + +// ArtistEqualFold applies the EqualFold predicate on the "artist" field. +func ArtistEqualFold(v string) predicate.Track { + return predicate.Track(sql.FieldEqualFold(FieldArtist, v)) +} + +// ArtistContainsFold applies the ContainsFold predicate on the "artist" field. +func ArtistContainsFold(v string) predicate.Track { + return predicate.Track(sql.FieldContainsFold(FieldArtist, v)) +} + +// DurationSecondsEQ applies the EQ predicate on the "duration_seconds" field. +func DurationSecondsEQ(v int) predicate.Track { + return predicate.Track(sql.FieldEQ(FieldDurationSeconds, v)) +} + +// DurationSecondsNEQ applies the NEQ predicate on the "duration_seconds" field. +func DurationSecondsNEQ(v int) predicate.Track { + return predicate.Track(sql.FieldNEQ(FieldDurationSeconds, v)) +} + +// DurationSecondsIn applies the In predicate on the "duration_seconds" field. +func DurationSecondsIn(vs ...int) predicate.Track { + return predicate.Track(sql.FieldIn(FieldDurationSeconds, vs...)) +} + +// DurationSecondsNotIn applies the NotIn predicate on the "duration_seconds" field. +func DurationSecondsNotIn(vs ...int) predicate.Track { + return predicate.Track(sql.FieldNotIn(FieldDurationSeconds, vs...)) +} + +// DurationSecondsGT applies the GT predicate on the "duration_seconds" field. +func DurationSecondsGT(v int) predicate.Track { + return predicate.Track(sql.FieldGT(FieldDurationSeconds, v)) +} + +// DurationSecondsGTE applies the GTE predicate on the "duration_seconds" field. +func DurationSecondsGTE(v int) predicate.Track { + return predicate.Track(sql.FieldGTE(FieldDurationSeconds, v)) +} + +// DurationSecondsLT applies the LT predicate on the "duration_seconds" field. +func DurationSecondsLT(v int) predicate.Track { + return predicate.Track(sql.FieldLT(FieldDurationSeconds, v)) +} + +// DurationSecondsLTE applies the LTE predicate on the "duration_seconds" field. +func DurationSecondsLTE(v int) predicate.Track { + return predicate.Track(sql.FieldLTE(FieldDurationSeconds, v)) +} + +// HasItems applies the HasEdge predicate on the "items" edge. +func HasItems() predicate.Track { + return predicate.Track(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, ItemsTable, ItemsColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasItemsWith applies the HasEdge predicate on the "items" edge with a given conditions (other predicates). +func HasItemsWith(preds ...predicate.Item) predicate.Track { + return predicate.Track(func(s *sql.Selector) { + step := newItemsStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + +// And groups predicates with the AND operator between them. +func And(predicates ...predicate.Track) predicate.Track { + return predicate.Track(sql.AndPredicates(predicates...)) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.Track) predicate.Track { + return predicate.Track(sql.OrPredicates(predicates...)) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.Track) predicate.Track { + return predicate.Track(sql.NotPredicates(p)) +} diff --git a/applications/playlist-api/ent/track_create.go b/applications/playlist-api/ent/track_create.go new file mode 100644 index 00000000..4bc1b51d --- /dev/null +++ b/applications/playlist-api/ent/track_create.go @@ -0,0 +1,268 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "example.com/playlist-api/ent/item" + "example.com/playlist-api/ent/track" + "github.com/google/uuid" +) + +// TrackCreate is the builder for creating a Track entity. +type TrackCreate struct { + config + mutation *TrackMutation + hooks []Hook +} + +// SetTitle sets the "title" field. +func (_c *TrackCreate) SetTitle(v string) *TrackCreate { + _c.mutation.SetTitle(v) + return _c +} + +// SetArtist sets the "artist" field. +func (_c *TrackCreate) SetArtist(v string) *TrackCreate { + _c.mutation.SetArtist(v) + return _c +} + +// SetDurationSeconds sets the "duration_seconds" field. +func (_c *TrackCreate) SetDurationSeconds(v int) *TrackCreate { + _c.mutation.SetDurationSeconds(v) + return _c +} + +// SetID sets the "id" field. +func (_c *TrackCreate) SetID(v uuid.UUID) *TrackCreate { + _c.mutation.SetID(v) + return _c +} + +// AddItemIDs adds the "items" edge to the Item entity by IDs. +func (_c *TrackCreate) AddItemIDs(ids ...uuid.UUID) *TrackCreate { + _c.mutation.AddItemIDs(ids...) + return _c +} + +// AddItems adds the "items" edges to the Item entity. +func (_c *TrackCreate) AddItems(v ...*Item) *TrackCreate { + ids := make([]uuid.UUID, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _c.AddItemIDs(ids...) +} + +// Mutation returns the TrackMutation object of the builder. +func (_c *TrackCreate) Mutation() *TrackMutation { + return _c.mutation +} + +// Save creates the Track in the database. +func (_c *TrackCreate) Save(ctx context.Context) (*Track, error) { + return withHooks(ctx, _c.sqlSave, _c.mutation, _c.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (_c *TrackCreate) SaveX(ctx context.Context) *Track { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *TrackCreate) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *TrackCreate) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_c *TrackCreate) check() error { + if _, ok := _c.mutation.Title(); !ok { + return &ValidationError{Name: "title", err: errors.New(`ent: missing required field "Track.title"`)} + } + if v, ok := _c.mutation.Title(); ok { + if err := track.TitleValidator(v); err != nil { + return &ValidationError{Name: "title", err: fmt.Errorf(`ent: validator failed for field "Track.title": %w`, err)} + } + } + if _, ok := _c.mutation.Artist(); !ok { + return &ValidationError{Name: "artist", err: errors.New(`ent: missing required field "Track.artist"`)} + } + if v, ok := _c.mutation.Artist(); ok { + if err := track.ArtistValidator(v); err != nil { + return &ValidationError{Name: "artist", err: fmt.Errorf(`ent: validator failed for field "Track.artist": %w`, err)} + } + } + if _, ok := _c.mutation.DurationSeconds(); !ok { + return &ValidationError{Name: "duration_seconds", err: errors.New(`ent: missing required field "Track.duration_seconds"`)} + } + if v, ok := _c.mutation.DurationSeconds(); ok { + if err := track.DurationSecondsValidator(v); err != nil { + return &ValidationError{Name: "duration_seconds", err: fmt.Errorf(`ent: validator failed for field "Track.duration_seconds": %w`, err)} + } + } + return nil +} + +func (_c *TrackCreate) sqlSave(ctx context.Context) (*Track, 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 *TrackCreate) createSpec() (*Track, *sqlgraph.CreateSpec) { + var ( + _node = &Track{config: _c.config} + _spec = sqlgraph.NewCreateSpec(track.Table, sqlgraph.NewFieldSpec(track.FieldID, field.TypeUUID)) + ) + if id, ok := _c.mutation.ID(); ok { + _node.ID = id + _spec.ID.Value = &id + } + if value, ok := _c.mutation.Title(); ok { + _spec.SetField(track.FieldTitle, field.TypeString, value) + _node.Title = value + } + if value, ok := _c.mutation.Artist(); ok { + _spec.SetField(track.FieldArtist, field.TypeString, value) + _node.Artist = value + } + if value, ok := _c.mutation.DurationSeconds(); ok { + _spec.SetField(track.FieldDurationSeconds, field.TypeInt, value) + _node.DurationSeconds = value + } + if nodes := _c.mutation.ItemsIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: track.ItemsTable, + Columns: []string{track.ItemsColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(item.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges = append(_spec.Edges, edge) + } + return _node, _spec +} + +// TrackCreateBulk is the builder for creating many Track entities in bulk. +type TrackCreateBulk struct { + config + err error + builders []*TrackCreate +} + +// Save creates the Track entities in the database. +func (_c *TrackCreateBulk) Save(ctx context.Context) ([]*Track, error) { + if _c.err != nil { + return nil, _c.err + } + specs := make([]*sqlgraph.CreateSpec, len(_c.builders)) + nodes := make([]*Track, len(_c.builders)) + mutators := make([]Mutator, len(_c.builders)) + for i := range _c.builders { + func(i int, root context.Context) { + builder := _c.builders[i] + var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { + mutation, ok := m.(*TrackMutation) + 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} + } + } + } + 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 *TrackCreateBulk) SaveX(ctx context.Context) []*Track { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *TrackCreateBulk) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *TrackCreateBulk) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/applications/playlist-api/ent/track_delete.go b/applications/playlist-api/ent/track_delete.go new file mode 100644 index 00000000..1ddab9d4 --- /dev/null +++ b/applications/playlist-api/ent/track_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" + "example.com/playlist-api/ent/predicate" + "example.com/playlist-api/ent/track" +) + +// TrackDelete is the builder for deleting a Track entity. +type TrackDelete struct { + config + hooks []Hook + mutation *TrackMutation +} + +// Where appends a list predicates to the TrackDelete builder. +func (_d *TrackDelete) Where(ps ...predicate.Track) *TrackDelete { + _d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (_d *TrackDelete) 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 *TrackDelete) ExecX(ctx context.Context) int { + n, err := _d.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (_d *TrackDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(track.Table, sqlgraph.NewFieldSpec(track.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 +} + +// TrackDeleteOne is the builder for deleting a single Track entity. +type TrackDeleteOne struct { + _d *TrackDelete +} + +// Where appends a list predicates to the TrackDelete builder. +func (_d *TrackDeleteOne) Where(ps ...predicate.Track) *TrackDeleteOne { + _d._d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query. +func (_d *TrackDeleteOne) Exec(ctx context.Context) error { + n, err := _d._d.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{track.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *TrackDeleteOne) ExecX(ctx context.Context) { + if err := _d.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/applications/playlist-api/ent/track_query.go b/applications/playlist-api/ent/track_query.go new file mode 100644 index 00000000..51323b67 --- /dev/null +++ b/applications/playlist-api/ent/track_query.go @@ -0,0 +1,644 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "database/sql/driver" + "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" + "example.com/playlist-api/ent/item" + "example.com/playlist-api/ent/predicate" + "example.com/playlist-api/ent/track" + "github.com/google/uuid" +) + +// TrackQuery is the builder for querying Track entities. +type TrackQuery struct { + config + ctx *QueryContext + order []track.OrderOption + inters []Interceptor + predicates []predicate.Track + withItems *ItemQuery + 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 TrackQuery builder. +func (_q *TrackQuery) Where(ps ...predicate.Track) *TrackQuery { + _q.predicates = append(_q.predicates, ps...) + return _q +} + +// Limit the number of records to be returned by this query. +func (_q *TrackQuery) Limit(limit int) *TrackQuery { + _q.ctx.Limit = &limit + return _q +} + +// Offset to start from. +func (_q *TrackQuery) Offset(offset int) *TrackQuery { + _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 *TrackQuery) Unique(unique bool) *TrackQuery { + _q.ctx.Unique = &unique + return _q +} + +// Order specifies how the records should be ordered. +func (_q *TrackQuery) Order(o ...track.OrderOption) *TrackQuery { + _q.order = append(_q.order, o...) + return _q +} + +// QueryItems chains the current query on the "items" edge. +func (_q *TrackQuery) QueryItems() *ItemQuery { + query := (&ItemClient{config: _q.config}).Query() + query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + selector := _q.sqlQuery(ctx) + if err := selector.Err(); err != nil { + return nil, err + } + step := sqlgraph.NewStep( + sqlgraph.From(track.Table, track.FieldID, selector), + sqlgraph.To(item.Table, item.FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, track.ItemsTable, track.ItemsColumn), + ) + fromU = sqlgraph.SetNeighbors(_q.driver.Dialect(), step) + return fromU, nil + } + return query +} + +// First returns the first Track entity from the query. +// Returns a *NotFoundError when no Track was found. +func (_q *TrackQuery) First(ctx context.Context) (*Track, 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{track.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (_q *TrackQuery) FirstX(ctx context.Context) *Track { + node, err := _q.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// FirstID returns the first Track ID from the query. +// Returns a *NotFoundError when no Track ID was found. +func (_q *TrackQuery) 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{track.Label} + return + } + return ids[0], nil +} + +// FirstIDX is like FirstID, but panics if an error occurs. +func (_q *TrackQuery) FirstIDX(ctx context.Context) uuid.UUID { + id, err := _q.FirstID(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return id +} + +// Only returns a single Track entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one Track entity is found. +// Returns a *NotFoundError when no Track entities are found. +func (_q *TrackQuery) Only(ctx context.Context) (*Track, 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{track.Label} + default: + return nil, &NotSingularError{track.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (_q *TrackQuery) OnlyX(ctx context.Context) *Track { + node, err := _q.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// OnlyID is like Only, but returns the only Track ID in the query. +// Returns a *NotSingularError when more than one Track ID is found. +// Returns a *NotFoundError when no entities are found. +func (_q *TrackQuery) 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{track.Label} + default: + err = &NotSingularError{track.Label} + } + return +} + +// OnlyIDX is like OnlyID, but panics if an error occurs. +func (_q *TrackQuery) 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 Tracks. +func (_q *TrackQuery) All(ctx context.Context) ([]*Track, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryAll) + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*Track, *TrackQuery]() + return withInterceptors[[]*Track](ctx, _q, qr, _q.inters) +} + +// AllX is like All, but panics if an error occurs. +func (_q *TrackQuery) AllX(ctx context.Context) []*Track { + nodes, err := _q.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// IDs executes the query and returns a list of Track IDs. +func (_q *TrackQuery) 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(track.FieldID).Scan(ctx, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// IDsX is like IDs, but panics if an error occurs. +func (_q *TrackQuery) 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 *TrackQuery) 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[*TrackQuery](), _q.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (_q *TrackQuery) 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 *TrackQuery) 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 *TrackQuery) ExistX(ctx context.Context) bool { + exist, err := _q.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the TrackQuery 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 *TrackQuery) Clone() *TrackQuery { + if _q == nil { + return nil + } + return &TrackQuery{ + config: _q.config, + ctx: _q.ctx.Clone(), + order: append([]track.OrderOption{}, _q.order...), + inters: append([]Interceptor{}, _q.inters...), + predicates: append([]predicate.Track{}, _q.predicates...), + withItems: _q.withItems.Clone(), + // clone intermediate query. + sql: _q.sql.Clone(), + path: _q.path, + } +} + +// WithItems tells the query-builder to eager-load the nodes that are connected to +// the "items" edge. The optional arguments are used to configure the query builder of the edge. +func (_q *TrackQuery) WithItems(opts ...func(*ItemQuery)) *TrackQuery { + query := (&ItemClient{config: _q.config}).Query() + for _, opt := range opts { + opt(query) + } + _q.withItems = query + return _q +} + +// 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 { +// Title string `json:"title,omitempty"` +// Count int `json:"count,omitempty"` +// } +// +// client.Track.Query(). +// GroupBy(track.FieldTitle). +// Aggregate(ent.Count()). +// Scan(ctx, &v) +func (_q *TrackQuery) GroupBy(field string, fields ...string) *TrackGroupBy { + _q.ctx.Fields = append([]string{field}, fields...) + grbuild := &TrackGroupBy{build: _q} + grbuild.flds = &_q.ctx.Fields + grbuild.label = track.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 { +// Title string `json:"title,omitempty"` +// } +// +// client.Track.Query(). +// Select(track.FieldTitle). +// Scan(ctx, &v) +func (_q *TrackQuery) Select(fields ...string) *TrackSelect { + _q.ctx.Fields = append(_q.ctx.Fields, fields...) + sbuild := &TrackSelect{TrackQuery: _q} + sbuild.label = track.Label + sbuild.flds, sbuild.scan = &_q.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a TrackSelect configured with the given aggregations. +func (_q *TrackQuery) Aggregate(fns ...AggregateFunc) *TrackSelect { + return _q.Select().Aggregate(fns...) +} + +func (_q *TrackQuery) 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 !track.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 *TrackQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*Track, error) { + var ( + nodes = []*Track{} + _spec = _q.querySpec() + loadedTypes = [1]bool{ + _q.withItems != nil, + } + ) + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*Track).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &Track{config: _q.config} + nodes = append(nodes, node) + 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) + } + if err := sqlgraph.QueryNodes(ctx, _q.driver, _spec); err != nil { + return nil, err + } + if len(nodes) == 0 { + return nodes, nil + } + if query := _q.withItems; query != nil { + if err := _q.loadItems(ctx, query, nodes, + func(n *Track) { n.Edges.Items = []*Item{} }, + func(n *Track, e *Item) { n.Edges.Items = append(n.Edges.Items, e) }); err != nil { + return nil, err + } + } + return nodes, nil +} + +func (_q *TrackQuery) loadItems(ctx context.Context, query *ItemQuery, nodes []*Track, init func(*Track), assign func(*Track, *Item)) error { + fks := make([]driver.Value, 0, len(nodes)) + nodeids := make(map[uuid.UUID]*Track) + for i := range nodes { + fks = append(fks, nodes[i].ID) + nodeids[nodes[i].ID] = nodes[i] + if init != nil { + init(nodes[i]) + } + } + if len(query.ctx.Fields) > 0 { + query.ctx.AppendFieldOnce(item.FieldTrackID) + } + query.Where(predicate.Item(func(s *sql.Selector) { + s.Where(sql.InValues(s.C(track.ItemsColumn), fks...)) + })) + neighbors, err := query.All(ctx) + if err != nil { + return err + } + for _, n := range neighbors { + fk := n.TrackID + node, ok := nodeids[fk] + if !ok { + return fmt.Errorf(`unexpected referenced foreign-key "track_id" returned %v for node %v`, fk, n.ID) + } + assign(node, n) + } + return nil +} + +func (_q *TrackQuery) 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 *TrackQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(track.Table, track.Columns, sqlgraph.NewFieldSpec(track.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, track.FieldID) + for i := range fields { + if fields[i] != track.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 *TrackQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(_q.driver.Dialect()) + t1 := builder.Table(track.Table) + columns := _q.ctx.Fields + if len(columns) == 0 { + columns = track.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 *TrackQuery) ForUpdate(opts ...sql.LockOption) *TrackQuery { + 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 *TrackQuery) ForShare(opts ...sql.LockOption) *TrackQuery { + if _q.driver.Dialect() == dialect.Postgres { + _q.Unique(false) + } + _q.modifiers = append(_q.modifiers, func(s *sql.Selector) { + s.ForShare(opts...) + }) + return _q +} + +// TrackGroupBy is the group-by builder for Track entities. +type TrackGroupBy struct { + selector + build *TrackQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (_g *TrackGroupBy) Aggregate(fns ...AggregateFunc) *TrackGroupBy { + _g.fns = append(_g.fns, fns...) + return _g +} + +// Scan applies the selector query and scans the result into the given value. +func (_g *TrackGroupBy) 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[*TrackQuery, *TrackGroupBy](ctx, _g.build, _g, _g.build.inters, v) +} + +func (_g *TrackGroupBy) sqlScan(ctx context.Context, root *TrackQuery, 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) +} + +// TrackSelect is the builder for selecting fields of Track entities. +type TrackSelect struct { + *TrackQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (_s *TrackSelect) Aggregate(fns ...AggregateFunc) *TrackSelect { + _s.fns = append(_s.fns, fns...) + return _s +} + +// Scan applies the selector query and scans the result into the given value. +func (_s *TrackSelect) 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[*TrackQuery, *TrackSelect](ctx, _s.TrackQuery, _s, _s.inters, v) +} + +func (_s *TrackSelect) sqlScan(ctx context.Context, root *TrackQuery, 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/applications/playlist-api/ent/track_update.go b/applications/playlist-api/ent/track_update.go new file mode 100644 index 00000000..47558625 --- /dev/null +++ b/applications/playlist-api/ent/track_update.go @@ -0,0 +1,507 @@ +// 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" + "example.com/playlist-api/ent/item" + "example.com/playlist-api/ent/predicate" + "example.com/playlist-api/ent/track" + "github.com/google/uuid" +) + +// TrackUpdate is the builder for updating Track entities. +type TrackUpdate struct { + config + hooks []Hook + mutation *TrackMutation +} + +// Where appends a list predicates to the TrackUpdate builder. +func (_u *TrackUpdate) Where(ps ...predicate.Track) *TrackUpdate { + _u.mutation.Where(ps...) + return _u +} + +// SetTitle sets the "title" field. +func (_u *TrackUpdate) SetTitle(v string) *TrackUpdate { + _u.mutation.SetTitle(v) + return _u +} + +// SetNillableTitle sets the "title" field if the given value is not nil. +func (_u *TrackUpdate) SetNillableTitle(v *string) *TrackUpdate { + if v != nil { + _u.SetTitle(*v) + } + return _u +} + +// SetArtist sets the "artist" field. +func (_u *TrackUpdate) SetArtist(v string) *TrackUpdate { + _u.mutation.SetArtist(v) + return _u +} + +// SetNillableArtist sets the "artist" field if the given value is not nil. +func (_u *TrackUpdate) SetNillableArtist(v *string) *TrackUpdate { + if v != nil { + _u.SetArtist(*v) + } + return _u +} + +// SetDurationSeconds sets the "duration_seconds" field. +func (_u *TrackUpdate) SetDurationSeconds(v int) *TrackUpdate { + _u.mutation.ResetDurationSeconds() + _u.mutation.SetDurationSeconds(v) + return _u +} + +// SetNillableDurationSeconds sets the "duration_seconds" field if the given value is not nil. +func (_u *TrackUpdate) SetNillableDurationSeconds(v *int) *TrackUpdate { + if v != nil { + _u.SetDurationSeconds(*v) + } + return _u +} + +// AddDurationSeconds adds value to the "duration_seconds" field. +func (_u *TrackUpdate) AddDurationSeconds(v int) *TrackUpdate { + _u.mutation.AddDurationSeconds(v) + return _u +} + +// AddItemIDs adds the "items" edge to the Item entity by IDs. +func (_u *TrackUpdate) AddItemIDs(ids ...uuid.UUID) *TrackUpdate { + _u.mutation.AddItemIDs(ids...) + return _u +} + +// AddItems adds the "items" edges to the Item entity. +func (_u *TrackUpdate) AddItems(v ...*Item) *TrackUpdate { + ids := make([]uuid.UUID, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.AddItemIDs(ids...) +} + +// Mutation returns the TrackMutation object of the builder. +func (_u *TrackUpdate) Mutation() *TrackMutation { + return _u.mutation +} + +// ClearItems clears all "items" edges to the Item entity. +func (_u *TrackUpdate) ClearItems() *TrackUpdate { + _u.mutation.ClearItems() + return _u +} + +// RemoveItemIDs removes the "items" edge to Item entities by IDs. +func (_u *TrackUpdate) RemoveItemIDs(ids ...uuid.UUID) *TrackUpdate { + _u.mutation.RemoveItemIDs(ids...) + return _u +} + +// RemoveItems removes "items" edges to Item entities. +func (_u *TrackUpdate) RemoveItems(v ...*Item) *TrackUpdate { + ids := make([]uuid.UUID, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.RemoveItemIDs(ids...) +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (_u *TrackUpdate) 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 *TrackUpdate) SaveX(ctx context.Context) int { + affected, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (_u *TrackUpdate) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *TrackUpdate) 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 *TrackUpdate) check() error { + if v, ok := _u.mutation.Title(); ok { + if err := track.TitleValidator(v); err != nil { + return &ValidationError{Name: "title", err: fmt.Errorf(`ent: validator failed for field "Track.title": %w`, err)} + } + } + if v, ok := _u.mutation.Artist(); ok { + if err := track.ArtistValidator(v); err != nil { + return &ValidationError{Name: "artist", err: fmt.Errorf(`ent: validator failed for field "Track.artist": %w`, err)} + } + } + if v, ok := _u.mutation.DurationSeconds(); ok { + if err := track.DurationSecondsValidator(v); err != nil { + return &ValidationError{Name: "duration_seconds", err: fmt.Errorf(`ent: validator failed for field "Track.duration_seconds": %w`, err)} + } + } + return nil +} + +func (_u *TrackUpdate) sqlSave(ctx context.Context) (_node int, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(track.Table, track.Columns, sqlgraph.NewFieldSpec(track.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.Title(); ok { + _spec.SetField(track.FieldTitle, field.TypeString, value) + } + if value, ok := _u.mutation.Artist(); ok { + _spec.SetField(track.FieldArtist, field.TypeString, value) + } + if value, ok := _u.mutation.DurationSeconds(); ok { + _spec.SetField(track.FieldDurationSeconds, field.TypeInt, value) + } + if value, ok := _u.mutation.AddedDurationSeconds(); ok { + _spec.AddField(track.FieldDurationSeconds, field.TypeInt, value) + } + if _u.mutation.ItemsCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: track.ItemsTable, + Columns: []string{track.ItemsColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(item.FieldID, field.TypeUUID), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := _u.mutation.RemovedItemsIDs(); len(nodes) > 0 && !_u.mutation.ItemsCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: track.ItemsTable, + Columns: []string{track.ItemsColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(item.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := _u.mutation.ItemsIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: track.ItemsTable, + Columns: []string{track.ItemsColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(item.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{track.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + _u.mutation.done = true + return _node, nil +} + +// TrackUpdateOne is the builder for updating a single Track entity. +type TrackUpdateOne struct { + config + fields []string + hooks []Hook + mutation *TrackMutation +} + +// SetTitle sets the "title" field. +func (_u *TrackUpdateOne) SetTitle(v string) *TrackUpdateOne { + _u.mutation.SetTitle(v) + return _u +} + +// SetNillableTitle sets the "title" field if the given value is not nil. +func (_u *TrackUpdateOne) SetNillableTitle(v *string) *TrackUpdateOne { + if v != nil { + _u.SetTitle(*v) + } + return _u +} + +// SetArtist sets the "artist" field. +func (_u *TrackUpdateOne) SetArtist(v string) *TrackUpdateOne { + _u.mutation.SetArtist(v) + return _u +} + +// SetNillableArtist sets the "artist" field if the given value is not nil. +func (_u *TrackUpdateOne) SetNillableArtist(v *string) *TrackUpdateOne { + if v != nil { + _u.SetArtist(*v) + } + return _u +} + +// SetDurationSeconds sets the "duration_seconds" field. +func (_u *TrackUpdateOne) SetDurationSeconds(v int) *TrackUpdateOne { + _u.mutation.ResetDurationSeconds() + _u.mutation.SetDurationSeconds(v) + return _u +} + +// SetNillableDurationSeconds sets the "duration_seconds" field if the given value is not nil. +func (_u *TrackUpdateOne) SetNillableDurationSeconds(v *int) *TrackUpdateOne { + if v != nil { + _u.SetDurationSeconds(*v) + } + return _u +} + +// AddDurationSeconds adds value to the "duration_seconds" field. +func (_u *TrackUpdateOne) AddDurationSeconds(v int) *TrackUpdateOne { + _u.mutation.AddDurationSeconds(v) + return _u +} + +// AddItemIDs adds the "items" edge to the Item entity by IDs. +func (_u *TrackUpdateOne) AddItemIDs(ids ...uuid.UUID) *TrackUpdateOne { + _u.mutation.AddItemIDs(ids...) + return _u +} + +// AddItems adds the "items" edges to the Item entity. +func (_u *TrackUpdateOne) AddItems(v ...*Item) *TrackUpdateOne { + ids := make([]uuid.UUID, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.AddItemIDs(ids...) +} + +// Mutation returns the TrackMutation object of the builder. +func (_u *TrackUpdateOne) Mutation() *TrackMutation { + return _u.mutation +} + +// ClearItems clears all "items" edges to the Item entity. +func (_u *TrackUpdateOne) ClearItems() *TrackUpdateOne { + _u.mutation.ClearItems() + return _u +} + +// RemoveItemIDs removes the "items" edge to Item entities by IDs. +func (_u *TrackUpdateOne) RemoveItemIDs(ids ...uuid.UUID) *TrackUpdateOne { + _u.mutation.RemoveItemIDs(ids...) + return _u +} + +// RemoveItems removes "items" edges to Item entities. +func (_u *TrackUpdateOne) RemoveItems(v ...*Item) *TrackUpdateOne { + ids := make([]uuid.UUID, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.RemoveItemIDs(ids...) +} + +// Where appends a list predicates to the TrackUpdate builder. +func (_u *TrackUpdateOne) Where(ps ...predicate.Track) *TrackUpdateOne { + _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 *TrackUpdateOne) Select(field string, fields ...string) *TrackUpdateOne { + _u.fields = append([]string{field}, fields...) + return _u +} + +// Save executes the query and returns the updated Track entity. +func (_u *TrackUpdateOne) Save(ctx context.Context) (*Track, error) { + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *TrackUpdateOne) SaveX(ctx context.Context) *Track { + node, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (_u *TrackUpdateOne) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *TrackUpdateOne) 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 *TrackUpdateOne) check() error { + if v, ok := _u.mutation.Title(); ok { + if err := track.TitleValidator(v); err != nil { + return &ValidationError{Name: "title", err: fmt.Errorf(`ent: validator failed for field "Track.title": %w`, err)} + } + } + if v, ok := _u.mutation.Artist(); ok { + if err := track.ArtistValidator(v); err != nil { + return &ValidationError{Name: "artist", err: fmt.Errorf(`ent: validator failed for field "Track.artist": %w`, err)} + } + } + if v, ok := _u.mutation.DurationSeconds(); ok { + if err := track.DurationSecondsValidator(v); err != nil { + return &ValidationError{Name: "duration_seconds", err: fmt.Errorf(`ent: validator failed for field "Track.duration_seconds": %w`, err)} + } + } + return nil +} + +func (_u *TrackUpdateOne) sqlSave(ctx context.Context) (_node *Track, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(track.Table, track.Columns, sqlgraph.NewFieldSpec(track.FieldID, field.TypeUUID)) + id, ok := _u.mutation.ID() + if !ok { + return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "Track.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, track.FieldID) + for _, f := range fields { + if !track.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + if f != track.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.Title(); ok { + _spec.SetField(track.FieldTitle, field.TypeString, value) + } + if value, ok := _u.mutation.Artist(); ok { + _spec.SetField(track.FieldArtist, field.TypeString, value) + } + if value, ok := _u.mutation.DurationSeconds(); ok { + _spec.SetField(track.FieldDurationSeconds, field.TypeInt, value) + } + if value, ok := _u.mutation.AddedDurationSeconds(); ok { + _spec.AddField(track.FieldDurationSeconds, field.TypeInt, value) + } + if _u.mutation.ItemsCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: track.ItemsTable, + Columns: []string{track.ItemsColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(item.FieldID, field.TypeUUID), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := _u.mutation.RemovedItemsIDs(); len(nodes) > 0 && !_u.mutation.ItemsCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: track.ItemsTable, + Columns: []string{track.ItemsColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(item.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := _u.mutation.ItemsIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: track.ItemsTable, + Columns: []string{track.ItemsColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(item.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + _node = &Track{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{track.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/applications/playlist-api/ent/tx.go b/applications/playlist-api/ent/tx.go new file mode 100644 index 00000000..fff952a8 --- /dev/null +++ b/applications/playlist-api/ent/tx.go @@ -0,0 +1,219 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "sync" + + "entgo.io/ent/dialect" +) + +// Tx is a transactional client that is created by calling Client.Tx(). +type Tx struct { + config + // Account is the client for interacting with the Account builders. + Account *AccountClient + // Item is the client for interacting with the Item builders. + Item *ItemClient + // Playlist is the client for interacting with the Playlist builders. + Playlist *PlaylistClient + // Track is the client for interacting with the Track builders. + Track *TrackClient + + // lazily loaded. + client *Client + clientOnce sync.Once + // ctx lives for the life of the transaction. It is + // the same context used by the underlying connection. + ctx context.Context +} + +type ( + // Committer is the interface that wraps the Commit method. + Committer interface { + Commit(context.Context, *Tx) error + } + + // The CommitFunc type is an adapter to allow the use of ordinary + // function as a Committer. If f is a function with the appropriate + // signature, CommitFunc(f) is a Committer that calls f. + CommitFunc func(context.Context, *Tx) error + + // CommitHook defines the "commit middleware". A function that gets a Committer + // and returns a Committer. For example: + // + // hook := func(next ent.Committer) ent.Committer { + // return ent.CommitFunc(func(ctx context.Context, tx *ent.Tx) error { + // // Do some stuff before. + // if err := next.Commit(ctx, tx); err != nil { + // return err + // } + // // Do some stuff after. + // return nil + // }) + // } + // + CommitHook func(Committer) Committer +) + +// Commit calls f(ctx, m). +func (f CommitFunc) Commit(ctx context.Context, tx *Tx) error { + return f(ctx, tx) +} + +// Commit commits the transaction. +func (tx *Tx) Commit() error { + txDriver := tx.config.driver.(*txDriver) + var fn Committer = CommitFunc(func(context.Context, *Tx) error { + return txDriver.tx.Commit() + }) + txDriver.mu.Lock() + hooks := append([]CommitHook(nil), txDriver.onCommit...) + txDriver.mu.Unlock() + for i := len(hooks) - 1; i >= 0; i-- { + fn = hooks[i](fn) + } + return fn.Commit(tx.ctx, tx) +} + +// OnCommit adds a hook to call on commit. +func (tx *Tx) OnCommit(f CommitHook) { + txDriver := tx.config.driver.(*txDriver) + txDriver.mu.Lock() + txDriver.onCommit = append(txDriver.onCommit, f) + txDriver.mu.Unlock() +} + +type ( + // Rollbacker is the interface that wraps the Rollback method. + Rollbacker interface { + Rollback(context.Context, *Tx) error + } + + // The RollbackFunc type is an adapter to allow the use of ordinary + // function as a Rollbacker. If f is a function with the appropriate + // signature, RollbackFunc(f) is a Rollbacker that calls f. + RollbackFunc func(context.Context, *Tx) error + + // RollbackHook defines the "rollback middleware". A function that gets a Rollbacker + // and returns a Rollbacker. For example: + // + // hook := func(next ent.Rollbacker) ent.Rollbacker { + // return ent.RollbackFunc(func(ctx context.Context, tx *ent.Tx) error { + // // Do some stuff before. + // if err := next.Rollback(ctx, tx); err != nil { + // return err + // } + // // Do some stuff after. + // return nil + // }) + // } + // + RollbackHook func(Rollbacker) Rollbacker +) + +// Rollback calls f(ctx, m). +func (f RollbackFunc) Rollback(ctx context.Context, tx *Tx) error { + return f(ctx, tx) +} + +// Rollback rollbacks the transaction. +func (tx *Tx) Rollback() error { + txDriver := tx.config.driver.(*txDriver) + var fn Rollbacker = RollbackFunc(func(context.Context, *Tx) error { + return txDriver.tx.Rollback() + }) + txDriver.mu.Lock() + hooks := append([]RollbackHook(nil), txDriver.onRollback...) + txDriver.mu.Unlock() + for i := len(hooks) - 1; i >= 0; i-- { + fn = hooks[i](fn) + } + return fn.Rollback(tx.ctx, tx) +} + +// OnRollback adds a hook to call on rollback. +func (tx *Tx) OnRollback(f RollbackHook) { + txDriver := tx.config.driver.(*txDriver) + txDriver.mu.Lock() + txDriver.onRollback = append(txDriver.onRollback, f) + txDriver.mu.Unlock() +} + +// Client returns a Client that binds to current transaction. +func (tx *Tx) Client() *Client { + tx.clientOnce.Do(func() { + tx.client = &Client{config: tx.config} + tx.client.init() + }) + return tx.client +} + +func (tx *Tx) init() { + tx.Account = NewAccountClient(tx.config) + tx.Item = NewItemClient(tx.config) + tx.Playlist = NewPlaylistClient(tx.config) + tx.Track = NewTrackClient(tx.config) +} + +// txDriver wraps the given dialect.Tx with a nop dialect.Driver implementation. +// The idea is to support transactions without adding any extra code to the builders. +// When a builder calls to driver.Tx(), it gets the same dialect.Tx instance. +// Commit and Rollback are nop for the internal builders and the user must call one +// of them in order to commit or rollback the transaction. +// +// If a closed transaction is embedded in one of the generated entities, and the entity +// applies a query, for example: Account.QueryXXX(), the query will be executed +// through the driver which created this transaction. +// +// Note that txDriver is not goroutine safe. +type txDriver struct { + // the driver we started the transaction from. + drv dialect.Driver + // tx is the underlying transaction. + tx dialect.Tx + // completion hooks. + mu sync.Mutex + onCommit []CommitHook + onRollback []RollbackHook +} + +// newTx creates a new transactional driver. +func newTx(ctx context.Context, drv dialect.Driver) (*txDriver, error) { + tx, err := drv.Tx(ctx) + if err != nil { + return nil, err + } + return &txDriver{tx: tx, drv: drv}, nil +} + +// Tx returns the transaction wrapper (txDriver) to avoid Commit or Rollback calls +// from the internal builders. Should be called only by the internal builders. +func (tx *txDriver) Tx(context.Context) (dialect.Tx, error) { return tx, nil } + +// Dialect returns the dialect of the driver we started the transaction from. +func (tx *txDriver) Dialect() string { return tx.drv.Dialect() } + +// Close is a nop close. +func (*txDriver) Close() error { return nil } + +// Commit is a nop commit for the internal builders. +// User must call `Tx.Commit` in order to commit the transaction. +func (*txDriver) Commit() error { return nil } + +// Rollback is a nop rollback for the internal builders. +// User must call `Tx.Rollback` in order to rollback the transaction. +func (*txDriver) Rollback() error { return nil } + +// Exec calls tx.Exec. +func (tx *txDriver) Exec(ctx context.Context, query string, args, v any) error { + return tx.tx.Exec(ctx, query, args, v) +} + +// Query calls tx.Query. +func (tx *txDriver) Query(ctx context.Context, query string, args, v any) error { + return tx.tx.Query(ctx, query, args, v) +} + +var _ dialect.Driver = (*txDriver)(nil) diff --git a/applications/playlist-api/examples/create.json b/applications/playlist-api/examples/create.json new file mode 100644 index 00000000..a5713246 --- /dev/null +++ b/applications/playlist-api/examples/create.json @@ -0,0 +1,12 @@ +{ + "query": "mutation Create($input: CreatePlaylistInput!) { createPlaylist(input: $input) { id name revision items { position track { id title artist durationSeconds } } } }", + "variables": { + "input": { + "name": "Synthetic afternoon", + "trackIds": [ + "10000000-0000-4000-8000-000000000001", + "10000000-0000-4000-8000-000000000002" + ] + } + } +} diff --git a/applications/playlist-api/examples/list.json b/applications/playlist-api/examples/list.json new file mode 100644 index 00000000..f49ab000 --- /dev/null +++ b/applications/playlist-api/examples/list.json @@ -0,0 +1,6 @@ +{ + "query": "query List($limit:Int!,$cursor:String){playlists(limit:$limit,cursor:$cursor){playlists{id name revision items{position track{id title}}} nextCursor}}", + "variables": { + "limit": 2 + } +} diff --git a/applications/playlist-api/examples/reorder.json b/applications/playlist-api/examples/reorder.json new file mode 100644 index 00000000..cab63556 --- /dev/null +++ b/applications/playlist-api/examples/reorder.json @@ -0,0 +1,13 @@ +{ + "query": "mutation Reorder($input:ReorderInput!){reorderPlaylist(input:$input){id revision items{position track{id title}}}}", + "variables": { + "input": { + "id": "REPLACE_WITH_CREATED_PLAYLIST_UUID", + "expectedRevision": 1, + "trackIds": [ + "10000000-0000-4000-8000-000000000002", + "10000000-0000-4000-8000-000000000001" + ] + } + } +} diff --git a/applications/playlist-api/generate.go b/applications/playlist-api/generate.go new file mode 100644 index 00000000..0143c24e --- /dev/null +++ b/applications/playlist-api/generate.go @@ -0,0 +1,4 @@ +package playlistapi + +//go:generate go run ./internal/gen/main.go +//go:generate go run github.com/99designs/gqlgen generate diff --git a/applications/playlist-api/go.mod b/applications/playlist-api/go.mod new file mode 100644 index 00000000..5c99293d --- /dev/null +++ b/applications/playlist-api/go.mod @@ -0,0 +1,60 @@ +module example.com/playlist-api + +go 1.27.1 + +require ( + entgo.io/contrib v0.7.0 + entgo.io/ent v0.14.6 + github.com/99designs/gqlgen v0.17.95 + github.com/google/uuid v1.6.0 + github.com/jackc/pgx/v5 v5.11.0 + github.com/vektah/gqlparser/v2 v2.5.37 +) + +require ( + ariga.io/atlas v0.36.2-0.20250730182955-2c6300d0a3e1 // indirect + github.com/agext/levenshtein v1.2.3 // indirect + github.com/agnivade/levenshtein v1.2.1 // indirect + github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect + github.com/bmatcuk/doublestar v1.3.4 // indirect + github.com/clipperhouse/displaywidth v0.6.2 // indirect + github.com/clipperhouse/stringish v0.1.1 // indirect + github.com/clipperhouse/uax29/v2 v2.3.0 // indirect + github.com/coder/websocket v1.8.15 // indirect + github.com/fatih/color v1.18.0 // indirect + github.com/go-openapi/inflect v0.19.0 // indirect + github.com/go-viper/mapstructure/v2 v2.5.0 // indirect + github.com/goccy/go-yaml v1.19.2 // indirect + github.com/google/go-cmp v0.6.0 // indirect + github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect + github.com/hashicorp/hcl/v2 v2.18.1 // 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/mattn/go-colorable v0.1.15 // indirect + github.com/mattn/go-isatty v0.0.24 // indirect + github.com/mattn/go-runewidth v0.0.19 // indirect + github.com/mitchellh/go-wordwrap v1.0.1 // indirect + github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 // indirect + github.com/olekukonko/errors v1.1.0 // indirect + github.com/olekukonko/ll v0.1.4-0.20260115111900-9e59c2286df0 // indirect + github.com/olekukonko/tablewriter v1.1.3 // indirect + github.com/rogpeppe/go-internal v1.16.0 // indirect + github.com/sosodev/duration v1.4.0 // indirect + github.com/spf13/cobra v1.7.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/urfave/cli/v3 v3.11.0 // indirect + github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect + github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect + github.com/zclconf/go-cty v1.14.4 // indirect + github.com/zclconf/go-cty-yaml v1.1.0 // indirect + golang.org/x/exp v0.0.0-20221230185412-738e83a70c30 // indirect + golang.org/x/mod v0.40.0 // indirect + golang.org/x/sync v0.22.0 // indirect + golang.org/x/sys v0.47.0 // indirect + golang.org/x/text v0.41.0 // indirect + golang.org/x/tools v0.49.0 // indirect + golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/applications/playlist-api/go.sum b/applications/playlist-api/go.sum new file mode 100644 index 00000000..1ac8f97c --- /dev/null +++ b/applications/playlist-api/go.sum @@ -0,0 +1,142 @@ +ariga.io/atlas v0.36.2-0.20250730182955-2c6300d0a3e1 h1:NPPfBaVZgz4LKBCIc0FbMogCjvXN+yGf7CZwotOwJo8= +ariga.io/atlas v0.36.2-0.20250730182955-2c6300d0a3e1/go.mod h1:Ex5l1xHsnWQUc3wYnrJ9gD7RUEzG76P7ZRQp8wNr0wc= +entgo.io/contrib v0.7.0 h1:4Ghx8O0rqSMmca3FIJ6QyZbQAoLvdzWqLMl1MbHFEEw= +entgo.io/contrib v0.7.0/go.mod h1:zbPSUrbn+6dfyv8S9HWEvn1MyGpO95ik2lUNgaqWTt4= +entgo.io/ent v0.14.6 h1:/f2696BpwuWAEEG6PVGWflg6+Inrpq4pRWuNlWz/Skk= +entgo.io/ent v0.14.6/go.mod h1:z46QBUdGC+BATwsedbDuREfSS0oSCV+csdEYlL4p73s= +github.com/99designs/gqlgen v0.17.95 h1:882h7F5iJImgtyUVttc4MOK2NbzbMYc2oyNeHqkjpP4= +github.com/99designs/gqlgen v0.17.95/go.mod h1:kHYPrpwOXDU1OQyxIg3Z7nVXSnlUoHVWBY7CMJCAM4M= +github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= +github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= +github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo= +github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= +github.com/agnivade/levenshtein v1.2.1 h1:EHBY3UOn1gwdy/VbFwgo4cxecRznFk7fKWN1KOX7eoM= +github.com/agnivade/levenshtein v1.2.1/go.mod h1:QVVI16kDrtSuwcpd0p1+xMC6Z/VfhtCyDIjcwga4/DU= +github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY= +github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4= +github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q= +github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= +github.com/bmatcuk/doublestar v1.3.4 h1:gPypJ5xD31uhX6Tf54sDPUOBXTqKH4c9aPY66CyQrS0= +github.com/bmatcuk/doublestar v1.3.4/go.mod h1:wiQtGV+rzVYxB7WIlirSN++5HPtPlXEo9MEoZQC/PmE= +github.com/clipperhouse/displaywidth v0.6.2 h1:ZDpTkFfpHOKte4RG5O/BOyf3ysnvFswpyYrV7z2uAKo= +github.com/clipperhouse/displaywidth v0.6.2/go.mod h1:R+kHuzaYWFkTm7xoMmK1lFydbci4X2CicfbGstSGg0o= +github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs= +github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA= +github.com/clipperhouse/uax29/v2 v2.3.0 h1:SNdx9DVUqMoBuBoW3iLOj4FQv3dN5mDtuqwuhIGpJy4= +github.com/clipperhouse/uax29/v2 v2.3.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g= +github.com/coder/websocket v1.8.15 h1:6B2JPeOGlpff2Uz6vOEH1Vzpi0iUz20A+lPVhPHtNUA= +github.com/coder/websocket v1.8.15/go.mod h1:NX3SzP+inril6yawo5CQXx8+fk145lPDC6pumgx0mVg= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54 h1:SG7nF6SRlWhcT7cNTs5R6Hk4V2lcmLz2NsG2VnInyNo= +github.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA= +github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= +github.com/go-openapi/inflect v0.19.0 h1:9jCH9scKIbHeV9m12SmPilScz6krDxKRasNNSNPXu/4= +github.com/go-openapi/inflect v0.19.0/go.mod h1:lHpZVlpIQqLyKwJ4N+YSc9hchQy/i12fJykb83CRBH4= +github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68= +github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= +github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro= +github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM= +github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= +github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= +github.com/hashicorp/hcl/v2 v2.18.1 h1:6nxnOJFku1EuSawSD81fuviYUV8DxFr3fp2dUi3ZYSo= +github.com/hashicorp/hcl/v2 v2.18.1/go.mod h1:ThLC89FV4p9MPW804KVbe/cEXoQ8NZEh+JtMeeGErHE= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +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.11.0 h1:IzBBtyK9AHqf98cctWFifYSci2hgQR/cd56wB4p+ogg= +github.com/jackc/pgx/v5 v5.11.0/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/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/mattn/go-colorable v0.1.15 h1:+u9SLTRGnXv73cEsnsmoZBom+dMU88B2M0aDcWy0/jY= +github.com/mattn/go-colorable v0.1.15/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= +github.com/mattn/go-isatty v0.0.24 h1:tGZZoVgT/KiqK1c8ocVLeDS8BSWMRd47J3Lbz7vsReI= +github.com/mattn/go-isatty v0.0.24/go.mod h1:nMCL3Zebbrt45jsMDgnfIwz6ydEQApk5oEI3HqDio6A= +github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw= +github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= +github.com/mattn/go-sqlite3 v1.14.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEum7A= +github.com/mattn/go-sqlite3 v1.14.28/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= +github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= +github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 h1:zrbMGy9YXpIeTnGj4EljqMiZsIcE09mmF8XsD5AYOJc= +github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6/go.mod h1:rEKTHC9roVVicUIfZK7DYrdIoM0EOr8mK1Hj5s3JjH0= +github.com/olekukonko/errors v1.1.0 h1:RNuGIh15QdDenh+hNvKrJkmxxjV4hcS50Db478Ou5sM= +github.com/olekukonko/errors v1.1.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y= +github.com/olekukonko/ll v0.1.4-0.20260115111900-9e59c2286df0 h1:jrYnow5+hy3WRDCBypUFvVKNSPPCdqgSXIE9eJDD8LM= +github.com/olekukonko/ll v0.1.4-0.20260115111900-9e59c2286df0/go.mod h1:b52bVQRRPObe+yyBl0TxNfhesL0nedD4Cht0/zx55Ew= +github.com/olekukonko/tablewriter v1.1.3 h1:VSHhghXxrP0JHl+0NnKid7WoEmd9/urKRJLysb70nnA= +github.com/olekukonko/tablewriter v1.1.3/go.mod h1:9VU0knjhmMkXjnMKrZ3+L2JhhtsQ/L38BbL3CRNE8tM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.16.0 h1:O9DK+vNMDVGLr2BeZqmpLeMjiMNkuXfcqntWbZV6S5g= +github.com/rogpeppe/go-internal v1.16.0/go.mod h1:DrUVZyrJU+txYW5/1kwtXQSMFio52ZOxX7yM1VHvnxs= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= +github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= +github.com/sosodev/duration v1.4.0 h1:35ed0KiVFriGHHzZZJaZLgmTEEICIyt8Sx0RQfj9IjE= +github.com/sosodev/duration v1.4.0/go.mod h1:RQIBBX0+fMLc/D9+Jb/fwvVmo0eZvDDEERAikUR6SDg= +github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= +github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +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.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= +github.com/urfave/cli/v3 v3.11.0 h1:P/euJp99kb9p0tlVY+iYTLYYTAQlfl0hR2gUO1Img1Q= +github.com/urfave/cli/v3 v3.11.0/go.mod h1:ysVLtOEmg2tOy6PknnYVhDoouyC/6N42TMeoMzskhso= +github.com/vektah/gqlparser/v2 v2.5.37 h1:jbb1Ilv+xBklV6653tKb4oVUupPNTLb5LmrnBKVI12Y= +github.com/vektah/gqlparser/v2 v2.5.37/go.mod h1:9O4Ox6Ngd3Y12bMD3w6i3CRQXh8W1oC1q0m6olCymDM= +github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU= +github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc= +github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= +github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= +github.com/zclconf/go-cty v1.14.4 h1:uXXczd9QDGsgu0i/QFR/hzI5NYCHLf6NQw/atrbnhq8= +github.com/zclconf/go-cty v1.14.4/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= +github.com/zclconf/go-cty-yaml v1.1.0 h1:nP+jp0qPHv2IhUVqmQSzjvqAWcObN0KBkUl2rWBdig0= +github.com/zclconf/go-cty-yaml v1.1.0/go.mod h1:9YLUH4g7lOhVWqUbctnVlZ5KLpg7JAprQNgxSZ1Gyxs= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= +golang.org/x/exp v0.0.0-20221230185412-738e83a70c30 h1:m9O6OTJ627iFnN2JIWfdqlZCzneRO6EEBsHXI25P8ws= +golang.org/x/exp v0.0.0-20221230185412-738e83a70c30/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/mod v0.40.0 h1:hUv+3cXcdRHz08UmSiOob7sadHig73uo5bkXxQ/tvUs= +golang.org/x/mod v0.40.0/go.mod h1:0/weTWkPWGBikyTWAX3dkjVztMmBA5hM0DH6BElSupE= +golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= +golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= +golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/text v0.41.0 h1:vz/seA0lnX87Othu2f/0L24RcgrXD9/YFTSuGjj3rH8= +golang.org/x/text v0.41.0/go.mod h1:jvf1O8ajNzZqhSrQBPbutR/EB83Cc0CFrezNQIwbb5M= +golang.org/x/tools v0.49.0 h1:3NI7VXzL9+1WZD52Dx2ttoPwD5DWrFGpl9mFZDlmisI= +golang.org/x/tools v0.49.0/go.mod h1:SJNXV9DBKT0UbdttsQjbfJlAE/q+y36++zo3uL3N0Oo= +golang.org/x/tools/go/expect v0.1.0-deprecated h1:jY2C5HGYR5lqex3gEniOQL0r7Dq5+VGVgY1nudX5lXY= +golang.org/x/tools/go/expect v0.1.0-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY= +golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated h1:1h2MnaIAIXISqTFKdENegdpAgUXz6NrPEsbIeWaBRvM= +golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated/go.mod h1:RVAQXBGNv1ib0J382/DPCRS/BPnsGebyM1Gj5VSDpG8= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/applications/playlist-api/gqlgen.yml b/applications/playlist-api/gqlgen.yml new file mode 100644 index 00000000..96043d6e --- /dev/null +++ b/applications/playlist-api/gqlgen.yml @@ -0,0 +1,13 @@ +schema: + - graph/schema.graphqls +exec: + filename: graph/generated.go + package: graph +model: + filename: graph/model/models_gen.go + package: model +resolver: + layout: follow-schema + dir: graph + package: graph + filename_template: "{name}.resolvers.go" diff --git a/applications/playlist-api/graph/generated.go b/applications/playlist-api/graph/generated.go new file mode 100644 index 00000000..c7e2e84a --- /dev/null +++ b/applications/playlist-api/graph/generated.go @@ -0,0 +1,3637 @@ +// Code generated by github.com/99designs/gqlgen, DO NOT EDIT. + +package graph + +import ( + "bytes" + "context" + "embed" + "errors" + "fmt" + "math" + "strconv" + "sync/atomic" + + "example.com/playlist-api/graph/model" + "github.com/99designs/gqlgen/graphql" + "github.com/99designs/gqlgen/graphql/introspection" + gqlparser "github.com/vektah/gqlparser/v2" + "github.com/vektah/gqlparser/v2/ast" +) + +// region ***************************** api!.gotpl ***************************** + +// NewExecutableSchema creates an ExecutableSchema from the ResolverRoot interface. +func NewExecutableSchema(cfg Config) graphql.ExecutableSchema { + return &executableSchema{SchemaData: cfg.Schema, Resolvers: cfg.Resolvers, Directives: cfg.Directives, ComplexityRoot: cfg.Complexity} +} + +type Config = graphql.Config[ResolverRoot, DirectiveRoot, ComplexityRoot] + +type ResolverRoot interface { + Mutation() MutationResolver + Query() QueryResolver +} + +type DirectiveRoot struct { +} + +type ComplexityRoot struct { + Mutation struct { + CreatePlaylist func(childComplexity int, input model.CreatePlaylistInput) int + ReorderPlaylist func(childComplexity int, input model.ReorderInput) int + } + + Playlist struct { + ID func(childComplexity int) int + Items func(childComplexity int) int + Name func(childComplexity int) int + Revision func(childComplexity int) int + } + + PlaylistItem struct { + Position func(childComplexity int) int + Track func(childComplexity int) int + } + + PlaylistPage struct { + NextCursor func(childComplexity int) int + Playlists func(childComplexity int) int + } + + Query struct { + Playlist func(childComplexity int, id string) int + Playlists func(childComplexity int, limit int, cursor *string) int + Tracks func(childComplexity int, limit int) int + } + + Track struct { + Artist func(childComplexity int) int + DurationSeconds func(childComplexity int) int + ID func(childComplexity int) int + Title func(childComplexity int) int + } +} + +// endregion ***************************** api!.gotpl ***************************** + +// region ************************** generated!.gotpl ************************** + +type MutationResolver interface { + CreatePlaylist(ctx context.Context, input model.CreatePlaylistInput) (*model.Playlist, error) + ReorderPlaylist(ctx context.Context, input model.ReorderInput) (*model.Playlist, error) +} +type QueryResolver interface { + Tracks(ctx context.Context, limit int) ([]*model.Track, error) + Playlists(ctx context.Context, limit int, cursor *string) (*model.PlaylistPage, error) + Playlist(ctx context.Context, id string) (*model.Playlist, error) +} + +// endregion ************************** generated!.gotpl ************************** + +// region ************************** internal!.gotpl *************************** + +type executableSchema graphql.ExecutableSchemaState[ResolverRoot, DirectiveRoot, ComplexityRoot] + +func (e *executableSchema) Schema() *ast.Schema { + if e.SchemaData != nil { + return e.SchemaData + } + return parsedSchema +} + +func (e *executableSchema) Complexity(ctx context.Context, typeName, field string, childComplexity int, rawArgs map[string]any) (int, bool) { + ec := newExecutionContext(nil, e, nil) + _ = ec + switch typeName + "." + field { + + case "Mutation.createPlaylist": + if e.ComplexityRoot.Mutation.CreatePlaylist == nil { + break + } + + args, err := ec.field_Mutation_createPlaylist_args(ctx, rawArgs) + if err != nil { + return 0, false + } + + return e.ComplexityRoot.Mutation.CreatePlaylist(childComplexity, args["input"].(model.CreatePlaylistInput)), true + case "Mutation.reorderPlaylist": + if e.ComplexityRoot.Mutation.ReorderPlaylist == nil { + break + } + + args, err := ec.field_Mutation_reorderPlaylist_args(ctx, rawArgs) + if err != nil { + return 0, false + } + + return e.ComplexityRoot.Mutation.ReorderPlaylist(childComplexity, args["input"].(model.ReorderInput)), true + + case "Playlist.id": + if e.ComplexityRoot.Playlist.ID == nil { + break + } + + return e.ComplexityRoot.Playlist.ID(childComplexity), true + case "Playlist.items": + if e.ComplexityRoot.Playlist.Items == nil { + break + } + + return e.ComplexityRoot.Playlist.Items(childComplexity), true + case "Playlist.name": + if e.ComplexityRoot.Playlist.Name == nil { + break + } + + return e.ComplexityRoot.Playlist.Name(childComplexity), true + case "Playlist.revision": + if e.ComplexityRoot.Playlist.Revision == nil { + break + } + + return e.ComplexityRoot.Playlist.Revision(childComplexity), true + + case "PlaylistItem.position": + if e.ComplexityRoot.PlaylistItem.Position == nil { + break + } + + return e.ComplexityRoot.PlaylistItem.Position(childComplexity), true + case "PlaylistItem.track": + if e.ComplexityRoot.PlaylistItem.Track == nil { + break + } + + return e.ComplexityRoot.PlaylistItem.Track(childComplexity), true + + case "PlaylistPage.nextCursor": + if e.ComplexityRoot.PlaylistPage.NextCursor == nil { + break + } + + return e.ComplexityRoot.PlaylistPage.NextCursor(childComplexity), true + case "PlaylistPage.playlists": + if e.ComplexityRoot.PlaylistPage.Playlists == nil { + break + } + + return e.ComplexityRoot.PlaylistPage.Playlists(childComplexity), true + + case "Query.playlist": + if e.ComplexityRoot.Query.Playlist == nil { + break + } + + args, err := ec.field_Query_playlist_args(ctx, rawArgs) + if err != nil { + return 0, false + } + + return e.ComplexityRoot.Query.Playlist(childComplexity, args["id"].(string)), true + case "Query.playlists": + if e.ComplexityRoot.Query.Playlists == nil { + break + } + + args, err := ec.field_Query_playlists_args(ctx, rawArgs) + if err != nil { + return 0, false + } + + return e.ComplexityRoot.Query.Playlists(childComplexity, args["limit"].(int), args["cursor"].(*string)), true + case "Query.tracks": + if e.ComplexityRoot.Query.Tracks == nil { + break + } + + args, err := ec.field_Query_tracks_args(ctx, rawArgs) + if err != nil { + return 0, false + } + + return e.ComplexityRoot.Query.Tracks(childComplexity, args["limit"].(int)), true + + case "Track.artist": + if e.ComplexityRoot.Track.Artist == nil { + break + } + + return e.ComplexityRoot.Track.Artist(childComplexity), true + case "Track.durationSeconds": + if e.ComplexityRoot.Track.DurationSeconds == nil { + break + } + + return e.ComplexityRoot.Track.DurationSeconds(childComplexity), true + case "Track.id": + if e.ComplexityRoot.Track.ID == nil { + break + } + + return e.ComplexityRoot.Track.ID(childComplexity), true + case "Track.title": + if e.ComplexityRoot.Track.Title == nil { + break + } + + return e.ComplexityRoot.Track.Title(childComplexity), true + + } + return 0, false +} + +func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { + opCtx := graphql.GetOperationContext(ctx) + ec := newExecutionContext(opCtx, e, make(chan graphql.DeferredResult)) + inputUnmarshalMap := graphql.BuildUnmarshalerMap( + ec.unmarshalInputCreatePlaylistInput, + ec.unmarshalInputReorderInput, + ) + first := true + + switch opCtx.Operation.Operation { + case ast.Query: + return func(ctx context.Context) *graphql.Response { + var response graphql.Response + var data graphql.Marshaler + if first { + first = false + ctx = graphql.WithUnmarshalerMap(ctx, inputUnmarshalMap) + data = ec._Query(ctx, opCtx.Operation.SelectionSet) + } else { + if atomic.LoadInt32(&ec.PendingDeferred) > 0 { + result := <-ec.DeferredResults + atomic.AddInt32(&ec.PendingDeferred, -1) + data = result.Result + response.Path = result.Path + response.Label = result.Label + response.Errors = result.Errors + } else { + return nil + } + } + var buf bytes.Buffer + data.MarshalGQL(&buf) + response.Data = buf.Bytes() + if atomic.LoadInt32(&ec.Deferred) > 0 { + hasNext := atomic.LoadInt32(&ec.PendingDeferred) > 0 + response.HasNext = &hasNext + } + + return &response + } + case ast.Mutation: + return func(ctx context.Context) *graphql.Response { + if !first { + return nil + } + first = false + ctx = graphql.WithUnmarshalerMap(ctx, inputUnmarshalMap) + data := ec._Mutation(ctx, opCtx.Operation.SelectionSet) + var buf bytes.Buffer + data.MarshalGQL(&buf) + + return &graphql.Response{ + Data: buf.Bytes(), + } + } + + default: + return graphql.OneShot(graphql.ErrorResponse(ctx, "unsupported GraphQL operation")) + } +} + +type executionContext struct { + *graphql.ExecutionContextState[ResolverRoot, DirectiveRoot, ComplexityRoot] +} + +func newExecutionContext( + opCtx *graphql.OperationContext, + execSchema *executableSchema, + deferredResults chan graphql.DeferredResult, +) *executionContext { + return &executionContext{ + ExecutionContextState: graphql.NewExecutionContextState[ResolverRoot, DirectiveRoot, ComplexityRoot]( + opCtx, + (*graphql.ExecutableSchemaState[ResolverRoot, DirectiveRoot, ComplexityRoot])(execSchema), + parsedSchema, + deferredResults, + ), + } +} + +//go:embed "schema.graphqls" +var sourcesFS embed.FS + +func sourceData(filename string) string { + data, err := sourcesFS.ReadFile(filename) + if err != nil { + panic(fmt.Sprintf("codegen problem: %s not available", filename)) + } + return string(data) +} + +var sources = []*ast.Source{ + {Name: "schema.graphqls", Input: sourceData("schema.graphqls"), BuiltIn: false}, +} +var parsedSchema = gqlparser.MustLoadSchema(sources...) + +// childFields_* functions provide shared child field context lookups. +// Each function is generated once per unique object type, deduplicating the +// switch statements that were previously inlined in every fieldContext_* function. + +func (ec *executionContext) childFields_Playlist(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_Playlist_id(ctx, field) + case "name": + return ec.fieldContext_Playlist_name(ctx, field) + case "revision": + return ec.fieldContext_Playlist_revision(ctx, field) + case "items": + return ec.fieldContext_Playlist_items(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Playlist", field.Name) +} + +func (ec *executionContext) childFields_PlaylistItem(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "position": + return ec.fieldContext_PlaylistItem_position(ctx, field) + case "track": + return ec.fieldContext_PlaylistItem_track(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type PlaylistItem", field.Name) +} + +func (ec *executionContext) childFields_PlaylistPage(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "playlists": + return ec.fieldContext_PlaylistPage_playlists(ctx, field) + case "nextCursor": + return ec.fieldContext_PlaylistPage_nextCursor(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type PlaylistPage", field.Name) +} + +func (ec *executionContext) childFields_Track(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_Track_id(ctx, field) + case "title": + return ec.fieldContext_Track_title(ctx, field) + case "artist": + return ec.fieldContext_Track_artist(ctx, field) + case "durationSeconds": + return ec.fieldContext_Track_durationSeconds(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Track", field.Name) +} + +func (ec *executionContext) childFields___Directive(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "name": + return ec.fieldContext___Directive_name(ctx, field) + case "description": + return ec.fieldContext___Directive_description(ctx, field) + case "isRepeatable": + return ec.fieldContext___Directive_isRepeatable(ctx, field) + case "locations": + return ec.fieldContext___Directive_locations(ctx, field) + case "args": + return ec.fieldContext___Directive_args(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type __Directive", field.Name) +} + +func (ec *executionContext) childFields___EnumValue(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "name": + return ec.fieldContext___EnumValue_name(ctx, field) + case "description": + return ec.fieldContext___EnumValue_description(ctx, field) + case "isDeprecated": + return ec.fieldContext___EnumValue_isDeprecated(ctx, field) + case "deprecationReason": + return ec.fieldContext___EnumValue_deprecationReason(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type __EnumValue", field.Name) +} + +func (ec *executionContext) childFields___Field(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "name": + return ec.fieldContext___Field_name(ctx, field) + case "description": + return ec.fieldContext___Field_description(ctx, field) + case "args": + return ec.fieldContext___Field_args(ctx, field) + case "type": + return ec.fieldContext___Field_type(ctx, field) + case "isDeprecated": + return ec.fieldContext___Field_isDeprecated(ctx, field) + case "deprecationReason": + return ec.fieldContext___Field_deprecationReason(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type __Field", field.Name) +} + +func (ec *executionContext) childFields___InputValue(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "name": + return ec.fieldContext___InputValue_name(ctx, field) + case "description": + return ec.fieldContext___InputValue_description(ctx, field) + case "type": + return ec.fieldContext___InputValue_type(ctx, field) + case "defaultValue": + return ec.fieldContext___InputValue_defaultValue(ctx, field) + case "isDeprecated": + return ec.fieldContext___InputValue_isDeprecated(ctx, field) + case "deprecationReason": + return ec.fieldContext___InputValue_deprecationReason(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type __InputValue", field.Name) +} + +func (ec *executionContext) childFields___Schema(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "description": + return ec.fieldContext___Schema_description(ctx, field) + case "types": + return ec.fieldContext___Schema_types(ctx, field) + case "queryType": + return ec.fieldContext___Schema_queryType(ctx, field) + case "mutationType": + return ec.fieldContext___Schema_mutationType(ctx, field) + case "subscriptionType": + return ec.fieldContext___Schema_subscriptionType(ctx, field) + case "directives": + return ec.fieldContext___Schema_directives(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type __Schema", field.Name) +} + +func (ec *executionContext) childFields___Type(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "kind": + return ec.fieldContext___Type_kind(ctx, field) + case "name": + return ec.fieldContext___Type_name(ctx, field) + case "description": + return ec.fieldContext___Type_description(ctx, field) + case "specifiedByURL": + return ec.fieldContext___Type_specifiedByURL(ctx, field) + case "fields": + return ec.fieldContext___Type_fields(ctx, field) + case "interfaces": + return ec.fieldContext___Type_interfaces(ctx, field) + case "possibleTypes": + return ec.fieldContext___Type_possibleTypes(ctx, field) + case "enumValues": + return ec.fieldContext___Type_enumValues(ctx, field) + case "inputFields": + return ec.fieldContext___Type_inputFields(ctx, field) + case "ofType": + return ec.fieldContext___Type_ofType(ctx, field) + case "isOneOf": + return ec.fieldContext___Type_isOneOf(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type __Type", field.Name) +} + +// endregion ************************** internal!.gotpl *************************** + +// region ***************************** args.gotpl ***************************** + +func (ec *executionContext) field_Mutation_createPlaylist_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { + var err error + args := map[string]any{} + arg0, err := graphql.ProcessArgField(ctx, rawArgs, "input", + func(ctx context.Context, v any) (model.CreatePlaylistInput, error) { + return ec.unmarshalNCreatePlaylistInput2exampleᚗcomᚋplaylistᚑapiᚋgraphᚋmodelᚐCreatePlaylistInput(ctx, v) + }) + if err != nil { + return nil, err + } + args["input"] = arg0 + return args, nil +} + +func (ec *executionContext) field_Mutation_reorderPlaylist_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { + var err error + args := map[string]any{} + arg0, err := graphql.ProcessArgField(ctx, rawArgs, "input", + func(ctx context.Context, v any) (model.ReorderInput, error) { + return ec.unmarshalNReorderInput2exampleᚗcomᚋplaylistᚑapiᚋgraphᚋmodelᚐReorderInput(ctx, v) + }) + if err != nil { + return nil, err + } + args["input"] = arg0 + return args, nil +} + +func (ec *executionContext) field_Query___type_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { + var err error + args := map[string]any{} + arg0, err := graphql.ProcessArgField(ctx, rawArgs, "name", + func(ctx context.Context, v any) (string, error) { + return ec.unmarshalNString2string(ctx, v) + }) + if err != nil { + return nil, err + } + args["name"] = arg0 + return args, nil +} + +func (ec *executionContext) field_Query_playlist_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { + var err error + args := map[string]any{} + arg0, err := graphql.ProcessArgField(ctx, rawArgs, "id", + func(ctx context.Context, v any) (string, error) { + return ec.unmarshalNID2string(ctx, v) + }) + if err != nil { + return nil, err + } + args["id"] = arg0 + return args, nil +} + +func (ec *executionContext) field_Query_playlists_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { + var err error + args := map[string]any{} + arg0, err := graphql.ProcessArgField(ctx, rawArgs, "limit", + func(ctx context.Context, v any) (int, error) { + return ec.unmarshalNInt2int(ctx, v) + }) + if err != nil { + return nil, err + } + args["limit"] = arg0 + arg1, err := graphql.ProcessArgField(ctx, rawArgs, "cursor", + func(ctx context.Context, v any) (*string, error) { + return ec.unmarshalOString2ᚖstring(ctx, v) + }) + if err != nil { + return nil, err + } + args["cursor"] = arg1 + return args, nil +} + +func (ec *executionContext) field_Query_tracks_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { + var err error + args := map[string]any{} + arg0, err := graphql.ProcessArgField(ctx, rawArgs, "limit", + func(ctx context.Context, v any) (int, error) { + return ec.unmarshalNInt2int(ctx, v) + }) + if err != nil { + return nil, err + } + args["limit"] = arg0 + return args, nil +} + +func (ec *executionContext) field___Directive_args_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { + var err error + args := map[string]any{} + arg0, err := graphql.ProcessArgField(ctx, rawArgs, "includeDeprecated", + func(ctx context.Context, v any) (*bool, error) { + return ec.unmarshalOBoolean2ᚖbool(ctx, v) + }) + if err != nil { + return nil, err + } + args["includeDeprecated"] = arg0 + return args, nil +} + +func (ec *executionContext) field___Field_args_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { + var err error + args := map[string]any{} + arg0, err := graphql.ProcessArgField(ctx, rawArgs, "includeDeprecated", + func(ctx context.Context, v any) (*bool, error) { + return ec.unmarshalOBoolean2ᚖbool(ctx, v) + }) + if err != nil { + return nil, err + } + args["includeDeprecated"] = arg0 + return args, nil +} + +func (ec *executionContext) field___Type_enumValues_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { + var err error + args := map[string]any{} + arg0, err := graphql.ProcessArgField(ctx, rawArgs, "includeDeprecated", + func(ctx context.Context, v any) (bool, error) { + return ec.unmarshalOBoolean2bool(ctx, v) + }) + if err != nil { + return nil, err + } + args["includeDeprecated"] = arg0 + return args, nil +} + +func (ec *executionContext) field___Type_fields_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { + var err error + args := map[string]any{} + arg0, err := graphql.ProcessArgField(ctx, rawArgs, "includeDeprecated", + func(ctx context.Context, v any) (bool, error) { + return ec.unmarshalOBoolean2bool(ctx, v) + }) + if err != nil { + return nil, err + } + args["includeDeprecated"] = arg0 + return args, nil +} + +// endregion ***************************** args.gotpl ***************************** + +// region **************************** field.gotpl ***************************** + +func (ec *executionContext) _Mutation_createPlaylist(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_Mutation_createPlaylist(ctx, field) + }, + func(ctx context.Context) (any, error) { + fc := graphql.GetFieldContext(ctx) + return ec.Resolvers.Mutation().CreatePlaylist(ctx, fc.Args["input"].(model.CreatePlaylistInput)) + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *model.Playlist) graphql.Marshaler { + return ec.marshalNPlaylist2ᚖexampleᚗcomᚋplaylistᚑapiᚋgraphᚋmodelᚐPlaylist(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_Mutation_createPlaylist(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Mutation", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields_Playlist(ctx, field) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Mutation_createPlaylist_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _Mutation_reorderPlaylist(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_Mutation_reorderPlaylist(ctx, field) + }, + func(ctx context.Context) (any, error) { + fc := graphql.GetFieldContext(ctx) + return ec.Resolvers.Mutation().ReorderPlaylist(ctx, fc.Args["input"].(model.ReorderInput)) + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *model.Playlist) graphql.Marshaler { + return ec.marshalNPlaylist2ᚖexampleᚗcomᚋplaylistᚑapiᚋgraphᚋmodelᚐPlaylist(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_Mutation_reorderPlaylist(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Mutation", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields_Playlist(ctx, field) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Mutation_reorderPlaylist_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _Playlist_id(ctx context.Context, field graphql.CollectedField, obj *model.Playlist) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_Playlist_id(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.ID, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v string) graphql.Marshaler { + return ec.marshalNID2string(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_Playlist_id(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("Playlist", field, false, false, errors.New("field of type ID does not have child fields")) +} + +func (ec *executionContext) _Playlist_name(ctx context.Context, field graphql.CollectedField, obj *model.Playlist) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_Playlist_name(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Name, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v string) graphql.Marshaler { + return ec.marshalNString2string(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_Playlist_name(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("Playlist", field, false, false, errors.New("field of type String does not have child fields")) +} + +func (ec *executionContext) _Playlist_revision(ctx context.Context, field graphql.CollectedField, obj *model.Playlist) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_Playlist_revision(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Revision, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v int) graphql.Marshaler { + return ec.marshalNInt2int(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_Playlist_revision(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("Playlist", field, false, false, errors.New("field of type Int does not have child fields")) +} + +func (ec *executionContext) _Playlist_items(ctx context.Context, field graphql.CollectedField, obj *model.Playlist) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_Playlist_items(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Items, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v []*model.PlaylistItem) graphql.Marshaler { + return ec.marshalNPlaylistItem2ᚕᚖexampleᚗcomᚋplaylistᚑapiᚋgraphᚋmodelᚐPlaylistItemᚄ(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_Playlist_items(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Playlist", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields_PlaylistItem(ctx, field) + }, + } + return fc, nil +} + +func (ec *executionContext) _PlaylistItem_position(ctx context.Context, field graphql.CollectedField, obj *model.PlaylistItem) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_PlaylistItem_position(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Position, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v int) graphql.Marshaler { + return ec.marshalNInt2int(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_PlaylistItem_position(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("PlaylistItem", field, false, false, errors.New("field of type Int does not have child fields")) +} + +func (ec *executionContext) _PlaylistItem_track(ctx context.Context, field graphql.CollectedField, obj *model.PlaylistItem) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_PlaylistItem_track(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Track, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *model.Track) graphql.Marshaler { + return ec.marshalNTrack2ᚖexampleᚗcomᚋplaylistᚑapiᚋgraphᚋmodelᚐTrack(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_PlaylistItem_track(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "PlaylistItem", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields_Track(ctx, field) + }, + } + return fc, nil +} + +func (ec *executionContext) _PlaylistPage_playlists(ctx context.Context, field graphql.CollectedField, obj *model.PlaylistPage) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_PlaylistPage_playlists(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Playlists, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v []*model.Playlist) graphql.Marshaler { + return ec.marshalNPlaylist2ᚕᚖexampleᚗcomᚋplaylistᚑapiᚋgraphᚋmodelᚐPlaylistᚄ(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_PlaylistPage_playlists(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "PlaylistPage", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields_Playlist(ctx, field) + }, + } + return fc, nil +} + +func (ec *executionContext) _PlaylistPage_nextCursor(ctx context.Context, field graphql.CollectedField, obj *model.PlaylistPage) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_PlaylistPage_nextCursor(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.NextCursor, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *string) graphql.Marshaler { + return ec.marshalOString2ᚖstring(ctx, selections, v) + }, + true, + false, + ) +} +func (ec *executionContext) fieldContext_PlaylistPage_nextCursor(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("PlaylistPage", field, false, false, errors.New("field of type String does not have child fields")) +} + +func (ec *executionContext) _Query_tracks(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_Query_tracks(ctx, field) + }, + func(ctx context.Context) (any, error) { + fc := graphql.GetFieldContext(ctx) + return ec.Resolvers.Query().Tracks(ctx, fc.Args["limit"].(int)) + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v []*model.Track) graphql.Marshaler { + return ec.marshalNTrack2ᚕᚖexampleᚗcomᚋplaylistᚑapiᚋgraphᚋmodelᚐTrackᚄ(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_Query_tracks(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields_Track(ctx, field) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Query_tracks_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _Query_playlists(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_Query_playlists(ctx, field) + }, + func(ctx context.Context) (any, error) { + fc := graphql.GetFieldContext(ctx) + return ec.Resolvers.Query().Playlists(ctx, fc.Args["limit"].(int), fc.Args["cursor"].(*string)) + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *model.PlaylistPage) graphql.Marshaler { + return ec.marshalNPlaylistPage2ᚖexampleᚗcomᚋplaylistᚑapiᚋgraphᚋmodelᚐPlaylistPage(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_Query_playlists(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields_PlaylistPage(ctx, field) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Query_playlists_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _Query_playlist(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_Query_playlist(ctx, field) + }, + func(ctx context.Context) (any, error) { + fc := graphql.GetFieldContext(ctx) + return ec.Resolvers.Query().Playlist(ctx, fc.Args["id"].(string)) + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *model.Playlist) graphql.Marshaler { + return ec.marshalNPlaylist2ᚖexampleᚗcomᚋplaylistᚑapiᚋgraphᚋmodelᚐPlaylist(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_Query_playlist(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields_Playlist(ctx, field) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Query_playlist_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _Query___type(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_Query___type(ctx, field) + }, + func(ctx context.Context) (any, error) { + fc := graphql.GetFieldContext(ctx) + return ec.IntrospectType(fc.Args["name"].(string)) + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *introspection.Type) graphql.Marshaler { + return ec.marshalO__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, selections, v) + }, + true, + false, + ) +} +func (ec *executionContext) fieldContext_Query___type(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields___Type(ctx, field) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Query___type_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _Query___schema(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_Query___schema(ctx, field) + }, + func(ctx context.Context) (any, error) { + return ec.IntrospectSchema() + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *introspection.Schema) graphql.Marshaler { + return ec.marshalO__Schema2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐSchema(ctx, selections, v) + }, + true, + false, + ) +} +func (ec *executionContext) fieldContext_Query___schema(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields___Schema(ctx, field) + }, + } + return fc, nil +} + +func (ec *executionContext) _Track_id(ctx context.Context, field graphql.CollectedField, obj *model.Track) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_Track_id(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.ID, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v string) graphql.Marshaler { + return ec.marshalNID2string(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_Track_id(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("Track", field, false, false, errors.New("field of type ID does not have child fields")) +} + +func (ec *executionContext) _Track_title(ctx context.Context, field graphql.CollectedField, obj *model.Track) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_Track_title(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Title, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v string) graphql.Marshaler { + return ec.marshalNString2string(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_Track_title(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("Track", field, false, false, errors.New("field of type String does not have child fields")) +} + +func (ec *executionContext) _Track_artist(ctx context.Context, field graphql.CollectedField, obj *model.Track) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_Track_artist(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Artist, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v string) graphql.Marshaler { + return ec.marshalNString2string(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_Track_artist(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("Track", field, false, false, errors.New("field of type String does not have child fields")) +} + +func (ec *executionContext) _Track_durationSeconds(ctx context.Context, field graphql.CollectedField, obj *model.Track) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_Track_durationSeconds(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.DurationSeconds, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v int) graphql.Marshaler { + return ec.marshalNInt2int(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_Track_durationSeconds(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("Track", field, false, false, errors.New("field of type Int does not have child fields")) +} + +func (ec *executionContext) ___Directive_name(ctx context.Context, field graphql.CollectedField, obj *introspection.Directive) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext___Directive_name(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Name, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v string) graphql.Marshaler { + return ec.marshalNString2string(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext___Directive_name(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("__Directive", field, false, false, errors.New("field of type String does not have child fields")) +} + +func (ec *executionContext) ___Directive_description(ctx context.Context, field graphql.CollectedField, obj *introspection.Directive) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext___Directive_description(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Description(), nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *string) graphql.Marshaler { + return ec.marshalOString2ᚖstring(ctx, selections, v) + }, + true, + false, + ) +} +func (ec *executionContext) fieldContext___Directive_description(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("__Directive", field, true, false, errors.New("field of type String does not have child fields")) +} + +func (ec *executionContext) ___Directive_isRepeatable(ctx context.Context, field graphql.CollectedField, obj *introspection.Directive) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext___Directive_isRepeatable(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.IsRepeatable, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v bool) graphql.Marshaler { + return ec.marshalNBoolean2bool(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext___Directive_isRepeatable(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("__Directive", field, false, false, errors.New("field of type Boolean does not have child fields")) +} + +func (ec *executionContext) ___Directive_locations(ctx context.Context, field graphql.CollectedField, obj *introspection.Directive) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext___Directive_locations(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Locations, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v []string) graphql.Marshaler { + return ec.marshalN__DirectiveLocation2ᚕstringᚄ(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext___Directive_locations(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("__Directive", field, false, false, errors.New("field of type __DirectiveLocation does not have child fields")) +} + +func (ec *executionContext) ___Directive_args(ctx context.Context, field graphql.CollectedField, obj *introspection.Directive) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext___Directive_args(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Args, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v []introspection.InputValue) graphql.Marshaler { + return ec.marshalN__InputValue2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐInputValueᚄ(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext___Directive_args(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Directive", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields___InputValue(ctx, field) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field___Directive_args_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) ___EnumValue_name(ctx context.Context, field graphql.CollectedField, obj *introspection.EnumValue) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext___EnumValue_name(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Name, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v string) graphql.Marshaler { + return ec.marshalNString2string(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext___EnumValue_name(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("__EnumValue", field, false, false, errors.New("field of type String does not have child fields")) +} + +func (ec *executionContext) ___EnumValue_description(ctx context.Context, field graphql.CollectedField, obj *introspection.EnumValue) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext___EnumValue_description(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Description(), nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *string) graphql.Marshaler { + return ec.marshalOString2ᚖstring(ctx, selections, v) + }, + true, + false, + ) +} +func (ec *executionContext) fieldContext___EnumValue_description(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("__EnumValue", field, true, false, errors.New("field of type String does not have child fields")) +} + +func (ec *executionContext) ___EnumValue_isDeprecated(ctx context.Context, field graphql.CollectedField, obj *introspection.EnumValue) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext___EnumValue_isDeprecated(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.IsDeprecated(), nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v bool) graphql.Marshaler { + return ec.marshalNBoolean2bool(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext___EnumValue_isDeprecated(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("__EnumValue", field, true, false, errors.New("field of type Boolean does not have child fields")) +} + +func (ec *executionContext) ___EnumValue_deprecationReason(ctx context.Context, field graphql.CollectedField, obj *introspection.EnumValue) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext___EnumValue_deprecationReason(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.DeprecationReason(), nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *string) graphql.Marshaler { + return ec.marshalOString2ᚖstring(ctx, selections, v) + }, + true, + false, + ) +} +func (ec *executionContext) fieldContext___EnumValue_deprecationReason(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("__EnumValue", field, true, false, errors.New("field of type String does not have child fields")) +} + +func (ec *executionContext) ___Field_name(ctx context.Context, field graphql.CollectedField, obj *introspection.Field) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext___Field_name(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Name, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v string) graphql.Marshaler { + return ec.marshalNString2string(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext___Field_name(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("__Field", field, false, false, errors.New("field of type String does not have child fields")) +} + +func (ec *executionContext) ___Field_description(ctx context.Context, field graphql.CollectedField, obj *introspection.Field) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext___Field_description(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Description(), nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *string) graphql.Marshaler { + return ec.marshalOString2ᚖstring(ctx, selections, v) + }, + true, + false, + ) +} +func (ec *executionContext) fieldContext___Field_description(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("__Field", field, true, false, errors.New("field of type String does not have child fields")) +} + +func (ec *executionContext) ___Field_args(ctx context.Context, field graphql.CollectedField, obj *introspection.Field) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext___Field_args(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Args, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v []introspection.InputValue) graphql.Marshaler { + return ec.marshalN__InputValue2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐInputValueᚄ(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext___Field_args(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Field", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields___InputValue(ctx, field) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field___Field_args_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) ___Field_type(ctx context.Context, field graphql.CollectedField, obj *introspection.Field) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext___Field_type(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Type, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *introspection.Type) graphql.Marshaler { + return ec.marshalN__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext___Field_type(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Field", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields___Type(ctx, field) + }, + } + return fc, nil +} + +func (ec *executionContext) ___Field_isDeprecated(ctx context.Context, field graphql.CollectedField, obj *introspection.Field) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext___Field_isDeprecated(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.IsDeprecated(), nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v bool) graphql.Marshaler { + return ec.marshalNBoolean2bool(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext___Field_isDeprecated(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("__Field", field, true, false, errors.New("field of type Boolean does not have child fields")) +} + +func (ec *executionContext) ___Field_deprecationReason(ctx context.Context, field graphql.CollectedField, obj *introspection.Field) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext___Field_deprecationReason(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.DeprecationReason(), nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *string) graphql.Marshaler { + return ec.marshalOString2ᚖstring(ctx, selections, v) + }, + true, + false, + ) +} +func (ec *executionContext) fieldContext___Field_deprecationReason(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("__Field", field, true, false, errors.New("field of type String does not have child fields")) +} + +func (ec *executionContext) ___InputValue_name(ctx context.Context, field graphql.CollectedField, obj *introspection.InputValue) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext___InputValue_name(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Name, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v string) graphql.Marshaler { + return ec.marshalNString2string(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext___InputValue_name(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("__InputValue", field, false, false, errors.New("field of type String does not have child fields")) +} + +func (ec *executionContext) ___InputValue_description(ctx context.Context, field graphql.CollectedField, obj *introspection.InputValue) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext___InputValue_description(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Description(), nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *string) graphql.Marshaler { + return ec.marshalOString2ᚖstring(ctx, selections, v) + }, + true, + false, + ) +} +func (ec *executionContext) fieldContext___InputValue_description(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("__InputValue", field, true, false, errors.New("field of type String does not have child fields")) +} + +func (ec *executionContext) ___InputValue_type(ctx context.Context, field graphql.CollectedField, obj *introspection.InputValue) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext___InputValue_type(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Type, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *introspection.Type) graphql.Marshaler { + return ec.marshalN__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext___InputValue_type(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__InputValue", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields___Type(ctx, field) + }, + } + return fc, nil +} + +func (ec *executionContext) ___InputValue_defaultValue(ctx context.Context, field graphql.CollectedField, obj *introspection.InputValue) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext___InputValue_defaultValue(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.DefaultValue, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *string) graphql.Marshaler { + return ec.marshalOString2ᚖstring(ctx, selections, v) + }, + true, + false, + ) +} +func (ec *executionContext) fieldContext___InputValue_defaultValue(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("__InputValue", field, false, false, errors.New("field of type String does not have child fields")) +} + +func (ec *executionContext) ___InputValue_isDeprecated(ctx context.Context, field graphql.CollectedField, obj *introspection.InputValue) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext___InputValue_isDeprecated(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.IsDeprecated(), nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v bool) graphql.Marshaler { + return ec.marshalNBoolean2bool(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext___InputValue_isDeprecated(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("__InputValue", field, true, false, errors.New("field of type Boolean does not have child fields")) +} + +func (ec *executionContext) ___InputValue_deprecationReason(ctx context.Context, field graphql.CollectedField, obj *introspection.InputValue) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext___InputValue_deprecationReason(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.DeprecationReason(), nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *string) graphql.Marshaler { + return ec.marshalOString2ᚖstring(ctx, selections, v) + }, + true, + false, + ) +} +func (ec *executionContext) fieldContext___InputValue_deprecationReason(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("__InputValue", field, true, false, errors.New("field of type String does not have child fields")) +} + +func (ec *executionContext) ___Schema_description(ctx context.Context, field graphql.CollectedField, obj *introspection.Schema) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext___Schema_description(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Description(), nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *string) graphql.Marshaler { + return ec.marshalOString2ᚖstring(ctx, selections, v) + }, + true, + false, + ) +} +func (ec *executionContext) fieldContext___Schema_description(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("__Schema", field, true, false, errors.New("field of type String does not have child fields")) +} + +func (ec *executionContext) ___Schema_types(ctx context.Context, field graphql.CollectedField, obj *introspection.Schema) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext___Schema_types(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Types(), nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v []introspection.Type) graphql.Marshaler { + return ec.marshalN__Type2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐTypeᚄ(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext___Schema_types(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Schema", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields___Type(ctx, field) + }, + } + return fc, nil +} + +func (ec *executionContext) ___Schema_queryType(ctx context.Context, field graphql.CollectedField, obj *introspection.Schema) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext___Schema_queryType(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.QueryType(), nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *introspection.Type) graphql.Marshaler { + return ec.marshalN__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext___Schema_queryType(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Schema", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields___Type(ctx, field) + }, + } + return fc, nil +} + +func (ec *executionContext) ___Schema_mutationType(ctx context.Context, field graphql.CollectedField, obj *introspection.Schema) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext___Schema_mutationType(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.MutationType(), nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *introspection.Type) graphql.Marshaler { + return ec.marshalO__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, selections, v) + }, + true, + false, + ) +} +func (ec *executionContext) fieldContext___Schema_mutationType(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Schema", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields___Type(ctx, field) + }, + } + return fc, nil +} + +func (ec *executionContext) ___Schema_subscriptionType(ctx context.Context, field graphql.CollectedField, obj *introspection.Schema) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext___Schema_subscriptionType(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.SubscriptionType(), nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *introspection.Type) graphql.Marshaler { + return ec.marshalO__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, selections, v) + }, + true, + false, + ) +} +func (ec *executionContext) fieldContext___Schema_subscriptionType(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Schema", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields___Type(ctx, field) + }, + } + return fc, nil +} + +func (ec *executionContext) ___Schema_directives(ctx context.Context, field graphql.CollectedField, obj *introspection.Schema) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext___Schema_directives(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Directives(), nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v []introspection.Directive) graphql.Marshaler { + return ec.marshalN__Directive2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐDirectiveᚄ(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext___Schema_directives(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Schema", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields___Directive(ctx, field) + }, + } + return fc, nil +} + +func (ec *executionContext) ___Type_kind(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext___Type_kind(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Kind(), nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v string) graphql.Marshaler { + return ec.marshalN__TypeKind2string(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext___Type_kind(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("__Type", field, true, false, errors.New("field of type __TypeKind does not have child fields")) +} + +func (ec *executionContext) ___Type_name(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext___Type_name(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Name(), nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *string) graphql.Marshaler { + return ec.marshalOString2ᚖstring(ctx, selections, v) + }, + true, + false, + ) +} +func (ec *executionContext) fieldContext___Type_name(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("__Type", field, true, false, errors.New("field of type String does not have child fields")) +} + +func (ec *executionContext) ___Type_description(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext___Type_description(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Description(), nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *string) graphql.Marshaler { + return ec.marshalOString2ᚖstring(ctx, selections, v) + }, + true, + false, + ) +} +func (ec *executionContext) fieldContext___Type_description(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("__Type", field, true, false, errors.New("field of type String does not have child fields")) +} + +func (ec *executionContext) ___Type_specifiedByURL(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext___Type_specifiedByURL(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.SpecifiedByURL(), nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *string) graphql.Marshaler { + return ec.marshalOString2ᚖstring(ctx, selections, v) + }, + true, + false, + ) +} +func (ec *executionContext) fieldContext___Type_specifiedByURL(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("__Type", field, true, false, errors.New("field of type String does not have child fields")) +} + +func (ec *executionContext) ___Type_fields(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext___Type_fields(ctx, field) + }, + func(ctx context.Context) (any, error) { + fc := graphql.GetFieldContext(ctx) + return obj.Fields(fc.Args["includeDeprecated"].(bool)), nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v []introspection.Field) graphql.Marshaler { + return ec.marshalO__Field2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐFieldᚄ(ctx, selections, v) + }, + true, + false, + ) +} +func (ec *executionContext) fieldContext___Type_fields(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Type", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields___Field(ctx, field) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field___Type_fields_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) ___Type_interfaces(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext___Type_interfaces(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Interfaces(), nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v []introspection.Type) graphql.Marshaler { + return ec.marshalO__Type2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐTypeᚄ(ctx, selections, v) + }, + true, + false, + ) +} +func (ec *executionContext) fieldContext___Type_interfaces(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Type", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields___Type(ctx, field) + }, + } + return fc, nil +} + +func (ec *executionContext) ___Type_possibleTypes(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext___Type_possibleTypes(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.PossibleTypes(), nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v []introspection.Type) graphql.Marshaler { + return ec.marshalO__Type2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐTypeᚄ(ctx, selections, v) + }, + true, + false, + ) +} +func (ec *executionContext) fieldContext___Type_possibleTypes(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Type", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields___Type(ctx, field) + }, + } + return fc, nil +} + +func (ec *executionContext) ___Type_enumValues(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext___Type_enumValues(ctx, field) + }, + func(ctx context.Context) (any, error) { + fc := graphql.GetFieldContext(ctx) + return obj.EnumValues(fc.Args["includeDeprecated"].(bool)), nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v []introspection.EnumValue) graphql.Marshaler { + return ec.marshalO__EnumValue2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐEnumValueᚄ(ctx, selections, v) + }, + true, + false, + ) +} +func (ec *executionContext) fieldContext___Type_enumValues(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Type", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields___EnumValue(ctx, field) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field___Type_enumValues_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) ___Type_inputFields(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext___Type_inputFields(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.InputFields(), nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v []introspection.InputValue) graphql.Marshaler { + return ec.marshalO__InputValue2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐInputValueᚄ(ctx, selections, v) + }, + true, + false, + ) +} +func (ec *executionContext) fieldContext___Type_inputFields(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Type", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields___InputValue(ctx, field) + }, + } + return fc, nil +} + +func (ec *executionContext) ___Type_ofType(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext___Type_ofType(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.OfType(), nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *introspection.Type) graphql.Marshaler { + return ec.marshalO__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, selections, v) + }, + true, + false, + ) +} +func (ec *executionContext) fieldContext___Type_ofType(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "__Type", + Field: field, + IsMethod: true, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields___Type(ctx, field) + }, + } + return fc, nil +} + +func (ec *executionContext) ___Type_isOneOf(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext___Type_isOneOf(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.IsOneOf(), nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v bool) graphql.Marshaler { + return ec.marshalOBoolean2bool(ctx, selections, v) + }, + true, + false, + ) +} +func (ec *executionContext) fieldContext___Type_isOneOf(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("__Type", field, true, false, errors.New("field of type Boolean does not have child fields")) +} + +// endregion **************************** field.gotpl ***************************** + +// region **************************** input.gotpl ***************************** + +func (ec *executionContext) unmarshalInputCreatePlaylistInput(ctx context.Context, obj any) (model.CreatePlaylistInput, error) { + var it model.CreatePlaylistInput + if obj == nil { + return it, nil + } + + asMap := map[string]any{} + for k, v := range obj.(map[string]any) { + asMap[k] = v + } + + fieldsInOrder := [...]string{"name", "trackIds"} + for _, k := range fieldsInOrder { + v, ok := asMap[k] + if !ok { + continue + } + switch k { + case "name": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("name")) + data, err := ec.unmarshalNString2string(ctx, v) + if err != nil { + return it, err + } + it.Name = data + case "trackIds": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("trackIds")) + data, err := ec.unmarshalNID2ᚕstringᚄ(ctx, v) + if err != nil { + return it, err + } + it.TrackIds = data + } + } + return it, nil +} + +func (ec *executionContext) unmarshalInputReorderInput(ctx context.Context, obj any) (model.ReorderInput, error) { + var it model.ReorderInput + if obj == nil { + return it, nil + } + + asMap := map[string]any{} + for k, v := range obj.(map[string]any) { + asMap[k] = v + } + + fieldsInOrder := [...]string{"id", "expectedRevision", "trackIds"} + for _, k := range fieldsInOrder { + v, ok := asMap[k] + if !ok { + continue + } + switch k { + case "id": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("id")) + data, err := ec.unmarshalNID2string(ctx, v) + if err != nil { + return it, err + } + it.ID = data + case "expectedRevision": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("expectedRevision")) + data, err := ec.unmarshalNInt2int(ctx, v) + if err != nil { + return it, err + } + it.ExpectedRevision = data + case "trackIds": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("trackIds")) + data, err := ec.unmarshalNID2ᚕstringᚄ(ctx, v) + if err != nil { + return it, err + } + it.TrackIds = data + } + } + return it, nil +} + +// endregion **************************** input.gotpl ***************************** + +// region ************************** interface.gotpl *************************** + +// endregion ************************** interface.gotpl *************************** + +// region **************************** object.gotpl **************************** + +var mutationImplementors = []string{"Mutation"} + +func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, mutationImplementors) + ctx = graphql.WithFieldContext(ctx, &graphql.FieldContext{ + Object: "Mutation", + }) + + out := graphql.NewFieldSet(fields) + deferredFieldSet := graphql.NewFieldSet(nil) + deferLabelToView := make(map[string]*graphql.FieldSetView) + for i, field := range fields { + innerCtx := graphql.WithRootFieldContext(ctx, &graphql.RootFieldContext{ + Object: field.Name, + Field: field, + }) + + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("Mutation") + case "createPlaylist": + out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { + return ec._Mutation_createPlaylist(ctx, field) + }) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "reorderPlaylist": + out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { + return ec._Mutation_reorderPlaylist(ctx, field) + }) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.Deferred, int32(min(len(deferLabelToView), math.MaxInt32))) + + ec.ProcessDeferredGroup(graphql.DeferredGroup{ + Defers: deferLabelToView, + Path: graphql.GetPath(ctx), + FieldSet: deferredFieldSet, + Context: ctx, + }) + + return out +} + +var playlistImplementors = []string{"Playlist"} + +func (ec *executionContext) _Playlist(ctx context.Context, sel ast.SelectionSet, obj *model.Playlist) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, playlistImplementors) + + out := graphql.NewFieldSet(fields) + deferredFieldSet := graphql.NewFieldSet(nil) + deferLabelToView := make(map[string]*graphql.FieldSetView) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("Playlist") + case "id": + out.Values[i] = ec._Playlist_id(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "name": + out.Values[i] = ec._Playlist_name(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "revision": + out.Values[i] = ec._Playlist_revision(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "items": + out.Values[i] = ec._Playlist_items(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.Deferred, int32(min(len(deferLabelToView), math.MaxInt32))) + + ec.ProcessDeferredGroup(graphql.DeferredGroup{ + Defers: deferLabelToView, + Path: graphql.GetPath(ctx), + FieldSet: deferredFieldSet, + Context: ctx, + }) + + return out +} + +var playlistItemImplementors = []string{"PlaylistItem"} + +func (ec *executionContext) _PlaylistItem(ctx context.Context, sel ast.SelectionSet, obj *model.PlaylistItem) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, playlistItemImplementors) + + out := graphql.NewFieldSet(fields) + deferredFieldSet := graphql.NewFieldSet(nil) + deferLabelToView := make(map[string]*graphql.FieldSetView) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("PlaylistItem") + case "position": + out.Values[i] = ec._PlaylistItem_position(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "track": + out.Values[i] = ec._PlaylistItem_track(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.Deferred, int32(min(len(deferLabelToView), math.MaxInt32))) + + ec.ProcessDeferredGroup(graphql.DeferredGroup{ + Defers: deferLabelToView, + Path: graphql.GetPath(ctx), + FieldSet: deferredFieldSet, + Context: ctx, + }) + + return out +} + +var playlistPageImplementors = []string{"PlaylistPage"} + +func (ec *executionContext) _PlaylistPage(ctx context.Context, sel ast.SelectionSet, obj *model.PlaylistPage) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, playlistPageImplementors) + + out := graphql.NewFieldSet(fields) + deferredFieldSet := graphql.NewFieldSet(nil) + deferLabelToView := make(map[string]*graphql.FieldSetView) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("PlaylistPage") + case "playlists": + out.Values[i] = ec._PlaylistPage_playlists(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "nextCursor": + out.Values[i] = ec._PlaylistPage_nextCursor(ctx, field, obj) + if out.Values[i] == graphql.RequiredNull { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.Deferred, int32(min(len(deferLabelToView), math.MaxInt32))) + + ec.ProcessDeferredGroup(graphql.DeferredGroup{ + Defers: deferLabelToView, + Path: graphql.GetPath(ctx), + FieldSet: deferredFieldSet, + Context: ctx, + }) + + return out +} + +var queryImplementors = []string{"Query"} + +func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, queryImplementors) + ctx = graphql.WithFieldContext(ctx, &graphql.FieldContext{ + Object: "Query", + }) + + out := graphql.NewFieldSet(fields) + deferredFieldSet := graphql.NewFieldSet(nil) + deferLabelToView := make(map[string]*graphql.FieldSetView) + for i, field := range fields { + innerCtx := graphql.WithRootFieldContext(ctx, &graphql.RootFieldContext{ + Object: field.Name, + Field: field, + }) + + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("Query") + case "tracks": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_tracks(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) + case "playlists": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_playlists(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) + case "playlist": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_playlist(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) + case "__type": + out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { + return ec._Query___type(ctx, field) + }) + if out.Values[i] == graphql.RequiredNull { + atomic.AddUint32(&out.Invalids, 1) + } + case "__schema": + out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { + return ec._Query___schema(ctx, field) + }) + if out.Values[i] == graphql.RequiredNull { + atomic.AddUint32(&out.Invalids, 1) + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.Deferred, int32(min(len(deferLabelToView), math.MaxInt32))) + + ec.ProcessDeferredGroup(graphql.DeferredGroup{ + Defers: deferLabelToView, + Path: graphql.GetPath(ctx), + FieldSet: deferredFieldSet, + Context: ctx, + }) + + return out +} + +var trackImplementors = []string{"Track"} + +func (ec *executionContext) _Track(ctx context.Context, sel ast.SelectionSet, obj *model.Track) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, trackImplementors) + + out := graphql.NewFieldSet(fields) + deferredFieldSet := graphql.NewFieldSet(nil) + deferLabelToView := make(map[string]*graphql.FieldSetView) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("Track") + case "id": + out.Values[i] = ec._Track_id(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "title": + out.Values[i] = ec._Track_title(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "artist": + out.Values[i] = ec._Track_artist(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "durationSeconds": + out.Values[i] = ec._Track_durationSeconds(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.Deferred, int32(min(len(deferLabelToView), math.MaxInt32))) + + ec.ProcessDeferredGroup(graphql.DeferredGroup{ + Defers: deferLabelToView, + Path: graphql.GetPath(ctx), + FieldSet: deferredFieldSet, + Context: ctx, + }) + + return out +} + +var __DirectiveImplementors = []string{"__Directive"} + +func (ec *executionContext) ___Directive(ctx context.Context, sel ast.SelectionSet, obj *introspection.Directive) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, __DirectiveImplementors) + + out := graphql.NewFieldSet(fields) + deferredFieldSet := graphql.NewFieldSet(nil) + deferLabelToView := make(map[string]*graphql.FieldSetView) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("__Directive") + case "name": + out.Values[i] = ec.___Directive_name(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "description": + out.Values[i] = ec.___Directive_description(ctx, field, obj) + if out.Values[i] == graphql.RequiredNull { + out.Invalids++ + } + case "isRepeatable": + out.Values[i] = ec.___Directive_isRepeatable(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "locations": + out.Values[i] = ec.___Directive_locations(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "args": + out.Values[i] = ec.___Directive_args(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.Deferred, int32(min(len(deferLabelToView), math.MaxInt32))) + + ec.ProcessDeferredGroup(graphql.DeferredGroup{ + Defers: deferLabelToView, + Path: graphql.GetPath(ctx), + FieldSet: deferredFieldSet, + Context: ctx, + }) + + return out +} + +var __EnumValueImplementors = []string{"__EnumValue"} + +func (ec *executionContext) ___EnumValue(ctx context.Context, sel ast.SelectionSet, obj *introspection.EnumValue) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, __EnumValueImplementors) + + out := graphql.NewFieldSet(fields) + deferredFieldSet := graphql.NewFieldSet(nil) + deferLabelToView := make(map[string]*graphql.FieldSetView) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("__EnumValue") + case "name": + out.Values[i] = ec.___EnumValue_name(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "description": + out.Values[i] = ec.___EnumValue_description(ctx, field, obj) + if out.Values[i] == graphql.RequiredNull { + out.Invalids++ + } + case "isDeprecated": + out.Values[i] = ec.___EnumValue_isDeprecated(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "deprecationReason": + out.Values[i] = ec.___EnumValue_deprecationReason(ctx, field, obj) + if out.Values[i] == graphql.RequiredNull { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.Deferred, int32(min(len(deferLabelToView), math.MaxInt32))) + + ec.ProcessDeferredGroup(graphql.DeferredGroup{ + Defers: deferLabelToView, + Path: graphql.GetPath(ctx), + FieldSet: deferredFieldSet, + Context: ctx, + }) + + return out +} + +var __FieldImplementors = []string{"__Field"} + +func (ec *executionContext) ___Field(ctx context.Context, sel ast.SelectionSet, obj *introspection.Field) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, __FieldImplementors) + + out := graphql.NewFieldSet(fields) + deferredFieldSet := graphql.NewFieldSet(nil) + deferLabelToView := make(map[string]*graphql.FieldSetView) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("__Field") + case "name": + out.Values[i] = ec.___Field_name(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "description": + out.Values[i] = ec.___Field_description(ctx, field, obj) + if out.Values[i] == graphql.RequiredNull { + out.Invalids++ + } + case "args": + out.Values[i] = ec.___Field_args(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "type": + out.Values[i] = ec.___Field_type(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "isDeprecated": + out.Values[i] = ec.___Field_isDeprecated(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "deprecationReason": + out.Values[i] = ec.___Field_deprecationReason(ctx, field, obj) + if out.Values[i] == graphql.RequiredNull { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.Deferred, int32(min(len(deferLabelToView), math.MaxInt32))) + + ec.ProcessDeferredGroup(graphql.DeferredGroup{ + Defers: deferLabelToView, + Path: graphql.GetPath(ctx), + FieldSet: deferredFieldSet, + Context: ctx, + }) + + return out +} + +var __InputValueImplementors = []string{"__InputValue"} + +func (ec *executionContext) ___InputValue(ctx context.Context, sel ast.SelectionSet, obj *introspection.InputValue) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, __InputValueImplementors) + + out := graphql.NewFieldSet(fields) + deferredFieldSet := graphql.NewFieldSet(nil) + deferLabelToView := make(map[string]*graphql.FieldSetView) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("__InputValue") + case "name": + out.Values[i] = ec.___InputValue_name(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "description": + out.Values[i] = ec.___InputValue_description(ctx, field, obj) + if out.Values[i] == graphql.RequiredNull { + out.Invalids++ + } + case "type": + out.Values[i] = ec.___InputValue_type(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "defaultValue": + out.Values[i] = ec.___InputValue_defaultValue(ctx, field, obj) + if out.Values[i] == graphql.RequiredNull { + out.Invalids++ + } + case "isDeprecated": + out.Values[i] = ec.___InputValue_isDeprecated(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "deprecationReason": + out.Values[i] = ec.___InputValue_deprecationReason(ctx, field, obj) + if out.Values[i] == graphql.RequiredNull { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.Deferred, int32(min(len(deferLabelToView), math.MaxInt32))) + + ec.ProcessDeferredGroup(graphql.DeferredGroup{ + Defers: deferLabelToView, + Path: graphql.GetPath(ctx), + FieldSet: deferredFieldSet, + Context: ctx, + }) + + return out +} + +var __SchemaImplementors = []string{"__Schema"} + +func (ec *executionContext) ___Schema(ctx context.Context, sel ast.SelectionSet, obj *introspection.Schema) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, __SchemaImplementors) + + out := graphql.NewFieldSet(fields) + deferredFieldSet := graphql.NewFieldSet(nil) + deferLabelToView := make(map[string]*graphql.FieldSetView) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("__Schema") + case "description": + out.Values[i] = ec.___Schema_description(ctx, field, obj) + if out.Values[i] == graphql.RequiredNull { + out.Invalids++ + } + case "types": + out.Values[i] = ec.___Schema_types(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "queryType": + out.Values[i] = ec.___Schema_queryType(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "mutationType": + out.Values[i] = ec.___Schema_mutationType(ctx, field, obj) + if out.Values[i] == graphql.RequiredNull { + out.Invalids++ + } + case "subscriptionType": + out.Values[i] = ec.___Schema_subscriptionType(ctx, field, obj) + if out.Values[i] == graphql.RequiredNull { + out.Invalids++ + } + case "directives": + out.Values[i] = ec.___Schema_directives(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.Deferred, int32(min(len(deferLabelToView), math.MaxInt32))) + + ec.ProcessDeferredGroup(graphql.DeferredGroup{ + Defers: deferLabelToView, + Path: graphql.GetPath(ctx), + FieldSet: deferredFieldSet, + Context: ctx, + }) + + return out +} + +var __TypeImplementors = []string{"__Type"} + +func (ec *executionContext) ___Type(ctx context.Context, sel ast.SelectionSet, obj *introspection.Type) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, __TypeImplementors) + + out := graphql.NewFieldSet(fields) + deferredFieldSet := graphql.NewFieldSet(nil) + deferLabelToView := make(map[string]*graphql.FieldSetView) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("__Type") + case "kind": + out.Values[i] = ec.___Type_kind(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "name": + out.Values[i] = ec.___Type_name(ctx, field, obj) + if out.Values[i] == graphql.RequiredNull { + out.Invalids++ + } + case "description": + out.Values[i] = ec.___Type_description(ctx, field, obj) + if out.Values[i] == graphql.RequiredNull { + out.Invalids++ + } + case "specifiedByURL": + out.Values[i] = ec.___Type_specifiedByURL(ctx, field, obj) + if out.Values[i] == graphql.RequiredNull { + out.Invalids++ + } + case "fields": + out.Values[i] = ec.___Type_fields(ctx, field, obj) + if out.Values[i] == graphql.RequiredNull { + out.Invalids++ + } + case "interfaces": + out.Values[i] = ec.___Type_interfaces(ctx, field, obj) + if out.Values[i] == graphql.RequiredNull { + out.Invalids++ + } + case "possibleTypes": + out.Values[i] = ec.___Type_possibleTypes(ctx, field, obj) + if out.Values[i] == graphql.RequiredNull { + out.Invalids++ + } + case "enumValues": + out.Values[i] = ec.___Type_enumValues(ctx, field, obj) + if out.Values[i] == graphql.RequiredNull { + out.Invalids++ + } + case "inputFields": + out.Values[i] = ec.___Type_inputFields(ctx, field, obj) + if out.Values[i] == graphql.RequiredNull { + out.Invalids++ + } + case "ofType": + out.Values[i] = ec.___Type_ofType(ctx, field, obj) + if out.Values[i] == graphql.RequiredNull { + out.Invalids++ + } + case "isOneOf": + out.Values[i] = ec.___Type_isOneOf(ctx, field, obj) + if out.Values[i] == graphql.RequiredNull { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.Deferred, int32(min(len(deferLabelToView), math.MaxInt32))) + + ec.ProcessDeferredGroup(graphql.DeferredGroup{ + Defers: deferLabelToView, + Path: graphql.GetPath(ctx), + FieldSet: deferredFieldSet, + Context: ctx, + }) + + return out +} + +// endregion **************************** object.gotpl **************************** + +// region ***************************** type.gotpl ***************************** + +func (ec *executionContext) unmarshalNBoolean2bool(ctx context.Context, v any) (bool, error) { + res, err := graphql.UnmarshalBoolean(v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalNBoolean2bool(ctx context.Context, sel ast.SelectionSet, v bool) graphql.Marshaler { + _ = sel + res := graphql.MarshalBoolean(v) + if res == graphql.Null { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + graphql.AddErrorf(ctx, "the requested element is null which the schema does not allow") + } + } + return res +} + +func (ec *executionContext) unmarshalNCreatePlaylistInput2exampleᚗcomᚋplaylistᚑapiᚋgraphᚋmodelᚐCreatePlaylistInput(ctx context.Context, v any) (model.CreatePlaylistInput, error) { + res, err := ec.unmarshalInputCreatePlaylistInput(ctx, v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) unmarshalNID2string(ctx context.Context, v any) (string, error) { + res, err := graphql.UnmarshalID(v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalNID2string(ctx context.Context, sel ast.SelectionSet, v string) graphql.Marshaler { + _ = sel + res := graphql.MarshalID(v) + if res == graphql.Null { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + graphql.AddErrorf(ctx, "the requested element is null which the schema does not allow") + } + } + return res +} + +func (ec *executionContext) unmarshalNID2ᚕstringᚄ(ctx context.Context, v any) ([]string, error) { + vSlice := graphql.CoerceList(v) + var err error + res := make([]string, len(vSlice)) + for i := range vSlice { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithIndex(i)) + res[i], err = ec.unmarshalNID2string(ctx, vSlice[i]) + if err != nil { + return nil, err + } + } + return res, nil +} + +func (ec *executionContext) marshalNID2ᚕstringᚄ(ctx context.Context, sel ast.SelectionSet, v []string) graphql.Marshaler { + ret := make(graphql.Array, len(v)) + for i := range v { + ret[i] = ec.marshalNID2string(ctx, sel, v[i]) + } + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + +func (ec *executionContext) unmarshalNInt2int(ctx context.Context, v any) (int, error) { + res, err := graphql.UnmarshalInt(v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalNInt2int(ctx context.Context, sel ast.SelectionSet, v int) graphql.Marshaler { + _ = sel + res := graphql.MarshalInt(v) + if res == graphql.Null { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + graphql.AddErrorf(ctx, "the requested element is null which the schema does not allow") + } + } + return res +} + +func (ec *executionContext) marshalNPlaylist2ᚕᚖexampleᚗcomᚋplaylistᚑapiᚋgraphᚋmodelᚐPlaylistᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.Playlist) graphql.Marshaler { + ret := graphql.MarshalSliceConcurrently(ctx, len(v), 0, false, func(ctx context.Context, i int) graphql.Marshaler { + fc := graphql.GetFieldContext(ctx) + fc.Result = &v[i] + return ec.marshalNPlaylist2ᚖexampleᚗcomᚋplaylistᚑapiᚋgraphᚋmodelᚐPlaylist(ctx, sel, v[i]) + }) + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + +func (ec *executionContext) marshalNPlaylist2ᚖexampleᚗcomᚋplaylistᚑapiᚋgraphᚋmodelᚐPlaylist(ctx context.Context, sel ast.SelectionSet, v *model.Playlist) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + graphql.AddErrorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._Playlist(ctx, sel, v) +} + +func (ec *executionContext) marshalNPlaylistItem2ᚕᚖexampleᚗcomᚋplaylistᚑapiᚋgraphᚋmodelᚐPlaylistItemᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.PlaylistItem) graphql.Marshaler { + ret := graphql.MarshalSliceConcurrently(ctx, len(v), 0, false, func(ctx context.Context, i int) graphql.Marshaler { + fc := graphql.GetFieldContext(ctx) + fc.Result = &v[i] + return ec.marshalNPlaylistItem2ᚖexampleᚗcomᚋplaylistᚑapiᚋgraphᚋmodelᚐPlaylistItem(ctx, sel, v[i]) + }) + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + +func (ec *executionContext) marshalNPlaylistItem2ᚖexampleᚗcomᚋplaylistᚑapiᚋgraphᚋmodelᚐPlaylistItem(ctx context.Context, sel ast.SelectionSet, v *model.PlaylistItem) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + graphql.AddErrorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._PlaylistItem(ctx, sel, v) +} + +func (ec *executionContext) marshalNPlaylistPage2ᚖexampleᚗcomᚋplaylistᚑapiᚋgraphᚋmodelᚐPlaylistPage(ctx context.Context, sel ast.SelectionSet, v *model.PlaylistPage) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + graphql.AddErrorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._PlaylistPage(ctx, sel, v) +} + +func (ec *executionContext) unmarshalNReorderInput2exampleᚗcomᚋplaylistᚑapiᚋgraphᚋmodelᚐReorderInput(ctx context.Context, v any) (model.ReorderInput, error) { + res, err := ec.unmarshalInputReorderInput(ctx, v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) unmarshalNString2string(ctx context.Context, v any) (string, error) { + res, err := graphql.UnmarshalString(v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalNString2string(ctx context.Context, sel ast.SelectionSet, v string) graphql.Marshaler { + _ = sel + res := graphql.MarshalString(v) + if res == graphql.Null { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + graphql.AddErrorf(ctx, "the requested element is null which the schema does not allow") + } + } + return res +} + +func (ec *executionContext) marshalNTrack2ᚕᚖexampleᚗcomᚋplaylistᚑapiᚋgraphᚋmodelᚐTrackᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.Track) graphql.Marshaler { + ret := graphql.MarshalSliceConcurrently(ctx, len(v), 0, false, func(ctx context.Context, i int) graphql.Marshaler { + fc := graphql.GetFieldContext(ctx) + fc.Result = &v[i] + return ec.marshalNTrack2ᚖexampleᚗcomᚋplaylistᚑapiᚋgraphᚋmodelᚐTrack(ctx, sel, v[i]) + }) + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + +func (ec *executionContext) marshalNTrack2ᚖexampleᚗcomᚋplaylistᚑapiᚋgraphᚋmodelᚐTrack(ctx context.Context, sel ast.SelectionSet, v *model.Track) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + graphql.AddErrorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._Track(ctx, sel, v) +} + +func (ec *executionContext) marshalN__Directive2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐDirective(ctx context.Context, sel ast.SelectionSet, v introspection.Directive) graphql.Marshaler { + return ec.___Directive(ctx, sel, &v) +} + +func (ec *executionContext) marshalN__Directive2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐDirectiveᚄ(ctx context.Context, sel ast.SelectionSet, v []introspection.Directive) graphql.Marshaler { + ret := graphql.MarshalSliceConcurrently(ctx, len(v), 0, false, func(ctx context.Context, i int) graphql.Marshaler { + fc := graphql.GetFieldContext(ctx) + fc.Result = &v[i] + return ec.marshalN__Directive2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐDirective(ctx, sel, v[i]) + }) + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + +func (ec *executionContext) unmarshalN__DirectiveLocation2string(ctx context.Context, v any) (string, error) { + res, err := graphql.UnmarshalString(v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalN__DirectiveLocation2string(ctx context.Context, sel ast.SelectionSet, v string) graphql.Marshaler { + _ = sel + res := graphql.MarshalString(v) + if res == graphql.Null { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + graphql.AddErrorf(ctx, "the requested element is null which the schema does not allow") + } + } + return res +} + +func (ec *executionContext) unmarshalN__DirectiveLocation2ᚕstringᚄ(ctx context.Context, v any) ([]string, error) { + vSlice := graphql.CoerceList(v) + var err error + res := make([]string, len(vSlice)) + for i := range vSlice { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithIndex(i)) + res[i], err = ec.unmarshalN__DirectiveLocation2string(ctx, vSlice[i]) + if err != nil { + return nil, err + } + } + return res, nil +} + +func (ec *executionContext) marshalN__DirectiveLocation2ᚕstringᚄ(ctx context.Context, sel ast.SelectionSet, v []string) graphql.Marshaler { + ret := graphql.MarshalSliceConcurrently(ctx, len(v), 0, false, func(ctx context.Context, i int) graphql.Marshaler { + fc := graphql.GetFieldContext(ctx) + fc.Result = &v[i] + return ec.marshalN__DirectiveLocation2string(ctx, sel, v[i]) + }) + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + +func (ec *executionContext) marshalN__EnumValue2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐEnumValue(ctx context.Context, sel ast.SelectionSet, v introspection.EnumValue) graphql.Marshaler { + return ec.___EnumValue(ctx, sel, &v) +} + +func (ec *executionContext) marshalN__Field2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐField(ctx context.Context, sel ast.SelectionSet, v introspection.Field) graphql.Marshaler { + return ec.___Field(ctx, sel, &v) +} + +func (ec *executionContext) marshalN__InputValue2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐInputValue(ctx context.Context, sel ast.SelectionSet, v introspection.InputValue) graphql.Marshaler { + return ec.___InputValue(ctx, sel, &v) +} + +func (ec *executionContext) marshalN__InputValue2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐInputValueᚄ(ctx context.Context, sel ast.SelectionSet, v []introspection.InputValue) graphql.Marshaler { + ret := graphql.MarshalSliceConcurrently(ctx, len(v), 0, false, func(ctx context.Context, i int) graphql.Marshaler { + fc := graphql.GetFieldContext(ctx) + fc.Result = &v[i] + return ec.marshalN__InputValue2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐInputValue(ctx, sel, v[i]) + }) + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + +func (ec *executionContext) marshalN__Type2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx context.Context, sel ast.SelectionSet, v introspection.Type) graphql.Marshaler { + return ec.___Type(ctx, sel, &v) +} + +func (ec *executionContext) marshalN__Type2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐTypeᚄ(ctx context.Context, sel ast.SelectionSet, v []introspection.Type) graphql.Marshaler { + ret := graphql.MarshalSliceConcurrently(ctx, len(v), 0, false, func(ctx context.Context, i int) graphql.Marshaler { + fc := graphql.GetFieldContext(ctx) + fc.Result = &v[i] + return ec.marshalN__Type2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, sel, v[i]) + }) + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + +func (ec *executionContext) marshalN__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx context.Context, sel ast.SelectionSet, v *introspection.Type) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + graphql.AddErrorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec.___Type(ctx, sel, v) +} + +func (ec *executionContext) unmarshalN__TypeKind2string(ctx context.Context, v any) (string, error) { + res, err := graphql.UnmarshalString(v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalN__TypeKind2string(ctx context.Context, sel ast.SelectionSet, v string) graphql.Marshaler { + _ = sel + res := graphql.MarshalString(v) + if res == graphql.Null { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + graphql.AddErrorf(ctx, "the requested element is null which the schema does not allow") + } + } + return res +} + +func (ec *executionContext) unmarshalOBoolean2bool(ctx context.Context, v any) (bool, error) { + res, err := graphql.UnmarshalBoolean(v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalOBoolean2bool(ctx context.Context, sel ast.SelectionSet, v bool) graphql.Marshaler { + _ = sel + _ = ctx + res := graphql.MarshalBoolean(v) + return res +} + +func (ec *executionContext) unmarshalOBoolean2ᚖbool(ctx context.Context, v any) (*bool, error) { + if v == nil { + return nil, nil + } + res, err := graphql.UnmarshalBoolean(v) + return &res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalOBoolean2ᚖbool(ctx context.Context, sel ast.SelectionSet, v *bool) graphql.Marshaler { + if v == nil { + return graphql.Null + } + _ = sel + _ = ctx + res := graphql.MarshalBoolean(*v) + return res +} + +func (ec *executionContext) unmarshalOString2ᚖstring(ctx context.Context, v any) (*string, error) { + if v == nil { + return nil, nil + } + res, err := graphql.UnmarshalString(v) + return &res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalOString2ᚖstring(ctx context.Context, sel ast.SelectionSet, v *string) graphql.Marshaler { + if v == nil { + return graphql.Null + } + _ = sel + _ = ctx + res := graphql.MarshalString(*v) + return res +} + +func (ec *executionContext) marshalO__EnumValue2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐEnumValueᚄ(ctx context.Context, sel ast.SelectionSet, v []introspection.EnumValue) graphql.Marshaler { + if v == nil { + return graphql.Null + } + ret := graphql.MarshalSliceConcurrently(ctx, len(v), 0, false, func(ctx context.Context, i int) graphql.Marshaler { + fc := graphql.GetFieldContext(ctx) + fc.Result = &v[i] + return ec.marshalN__EnumValue2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐEnumValue(ctx, sel, v[i]) + }) + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + +func (ec *executionContext) marshalO__Field2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐFieldᚄ(ctx context.Context, sel ast.SelectionSet, v []introspection.Field) graphql.Marshaler { + if v == nil { + return graphql.Null + } + ret := graphql.MarshalSliceConcurrently(ctx, len(v), 0, false, func(ctx context.Context, i int) graphql.Marshaler { + fc := graphql.GetFieldContext(ctx) + fc.Result = &v[i] + return ec.marshalN__Field2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐField(ctx, sel, v[i]) + }) + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + +func (ec *executionContext) marshalO__InputValue2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐInputValueᚄ(ctx context.Context, sel ast.SelectionSet, v []introspection.InputValue) graphql.Marshaler { + if v == nil { + return graphql.Null + } + ret := graphql.MarshalSliceConcurrently(ctx, len(v), 0, false, func(ctx context.Context, i int) graphql.Marshaler { + fc := graphql.GetFieldContext(ctx) + fc.Result = &v[i] + return ec.marshalN__InputValue2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐInputValue(ctx, sel, v[i]) + }) + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + +func (ec *executionContext) marshalO__Schema2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐSchema(ctx context.Context, sel ast.SelectionSet, v *introspection.Schema) graphql.Marshaler { + if v == nil { + return graphql.Null + } + return ec.___Schema(ctx, sel, v) +} + +func (ec *executionContext) marshalO__Type2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐTypeᚄ(ctx context.Context, sel ast.SelectionSet, v []introspection.Type) graphql.Marshaler { + if v == nil { + return graphql.Null + } + ret := graphql.MarshalSliceConcurrently(ctx, len(v), 0, false, func(ctx context.Context, i int) graphql.Marshaler { + fc := graphql.GetFieldContext(ctx) + fc.Result = &v[i] + return ec.marshalN__Type2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, sel, v[i]) + }) + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + +func (ec *executionContext) marshalO__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx context.Context, sel ast.SelectionSet, v *introspection.Type) graphql.Marshaler { + if v == nil { + return graphql.Null + } + return ec.___Type(ctx, sel, v) +} + +// endregion ***************************** type.gotpl ***************************** diff --git a/applications/playlist-api/graph/model/models_gen.go b/applications/playlist-api/graph/model/models_gen.go new file mode 100644 index 00000000..0db7b4a3 --- /dev/null +++ b/applications/playlist-api/graph/model/models_gen.go @@ -0,0 +1,44 @@ +// Code generated by github.com/99designs/gqlgen, DO NOT EDIT. + +package model + +type CreatePlaylistInput struct { + Name string `json:"name"` + TrackIds []string `json:"trackIds"` +} + +type Mutation struct { +} + +type Playlist struct { + ID string `json:"id"` + Name string `json:"name"` + Revision int `json:"revision"` + Items []*PlaylistItem `json:"items"` +} + +type PlaylistItem struct { + Position int `json:"position"` + Track *Track `json:"track"` +} + +type PlaylistPage struct { + Playlists []*Playlist `json:"playlists"` + NextCursor *string `json:"nextCursor,omitempty"` +} + +type Query struct { +} + +type ReorderInput struct { + ID string `json:"id"` + ExpectedRevision int `json:"expectedRevision"` + TrackIds []string `json:"trackIds"` +} + +type Track struct { + ID string `json:"id"` + Title string `json:"title"` + Artist string `json:"artist"` + DurationSeconds int `json:"durationSeconds"` +} diff --git a/applications/playlist-api/graph/resolver.go b/applications/playlist-api/graph/resolver.go new file mode 100644 index 00000000..7b272f36 --- /dev/null +++ b/applications/playlist-api/graph/resolver.go @@ -0,0 +1,3 @@ +package graph + +type Resolver struct{} diff --git a/applications/playlist-api/graph/schema.graphqls b/applications/playlist-api/graph/schema.graphqls new file mode 100644 index 00000000..565859a9 --- /dev/null +++ b/applications/playlist-api/graph/schema.graphqls @@ -0,0 +1,8 @@ +type Track { id: ID!, title: String!, artist: String!, durationSeconds: Int! } +type PlaylistItem { position: Int!, track: Track! } +type Playlist { id: ID!, name: String!, revision: Int!, items: [PlaylistItem!]! } +type PlaylistPage { playlists: [Playlist!]!, nextCursor: String } +input CreatePlaylistInput { name: String!, trackIds: [ID!]! } +input ReorderInput { id: ID!, expectedRevision: Int!, trackIds: [ID!]! } +type Query { tracks(limit: Int!): [Track!]!, playlists(limit: Int!, cursor: String): PlaylistPage!, playlist(id: ID!): Playlist! } +type Mutation { createPlaylist(input: CreatePlaylistInput!): Playlist!, reorderPlaylist(input: ReorderInput!): Playlist! } diff --git a/applications/playlist-api/graph/schema.resolvers.go b/applications/playlist-api/graph/schema.resolvers.go new file mode 100644 index 00000000..78efc5e9 --- /dev/null +++ b/applications/playlist-api/graph/schema.resolvers.go @@ -0,0 +1,49 @@ +package graph + +// This file will be automatically regenerated based on the schema, any resolver +// implementations +// will be copied through when generating and any unknown code will be moved to the end. +// Code generated by github.com/99designs/gqlgen version v0.17.95 + +import ( + "context" + + "example.com/playlist-api/graph/model" + "example.com/playlist-api/internal/app" +) + +// CreatePlaylist is the resolver for the createPlaylist field. +func (r *mutationResolver) CreatePlaylist(ctx context.Context, input model.CreatePlaylistInput) (*model.Playlist, error) { + return app.Create(ctx, input) +} + +// ReorderPlaylist is the resolver for the reorderPlaylist field. +func (r *mutationResolver) ReorderPlaylist(ctx context.Context, input model.ReorderInput) (*model.Playlist, error) { + return app.Reorder(ctx, input) +} + +// Tracks is the resolver for the tracks field. +func (r *queryResolver) Tracks(ctx context.Context, limit int) ([]*model.Track, error) { + return app.Tracks(ctx, limit) +} + +// Playlists is the resolver for the playlists field. +func (r *queryResolver) Playlists(ctx context.Context, limit int, cursor *string) (*model.PlaylistPage, error) { + return app.List(ctx, limit, cursor) +} + +// Playlist is the resolver for the playlist field. +func (r *queryResolver) Playlist(ctx context.Context, id string) (*model.Playlist, error) { + return app.Read(ctx, id) +} + +// Mutation returns MutationResolver implementation. +func (r *Resolver) Mutation() MutationResolver { return &mutationResolver{r} } + +// Query returns QueryResolver implementation. +func (r *Resolver) Query() QueryResolver { return &queryResolver{r} } + +type ( + mutationResolver struct{ *Resolver } + queryResolver struct{ *Resolver } +) diff --git a/applications/playlist-api/internal/app/cloud_test.go b/applications/playlist-api/internal/app/cloud_test.go new file mode 100644 index 00000000..a184f8ea --- /dev/null +++ b/applications/playlist-api/internal/app/cloud_test.go @@ -0,0 +1,180 @@ +//go:build cloud + +package app + +import ( + "bytes" + "context" + "crypto/x509" + "database/sql" + "encoding/json" + "entgo.io/ent/dialect" + entsql "entgo.io/ent/dialect/sql" + "errors" + "example.com/playlist-api/ent" + "example.com/playlist-api/graph/model" + "github.com/google/uuid" + "github.com/jackc/pgx/v5" + "io" + "net/http" + "os" + "strings" + "sync" + "testing" + "time" +) + +func TestCloudTLS(t *testing.T) { + base, err := Config() + if err != nil { + t.Fatal(err) + } + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + positive, err := pgx.ConnectConfig(ctx, base) + if err != nil { + t.Fatal("verify-full positive failed") + } + positive.Close(ctx) + wrongCA := base.Copy() + wrongCA.TLSConfig = base.TLSConfig.Clone() + wrongCA.TLSConfig.RootCAs, _ = x509.SystemCertPool() + _, err = pgx.ConnectConfig(ctx, wrongCA) + var authority x509.UnknownAuthorityError + if !errors.As(err, &authority) { + t.Fatalf("Expected x509.UnknownAuthorityError, got %T", err) + } + wrongName := base.Copy() + wrongName.TLSConfig = base.TLSConfig.Clone() + wrongName.TLSConfig.ServerName = "wrong-hostname.invalid" + _, err = pgx.ConnectConfig(ctx, wrongName) + var name x509.HostnameError + if !errors.As(err, &name) { + t.Fatalf("Expected x509.HostnameError, got %T", err) + } + t.Log("Actual pgx positive; CA UnknownAuthorityError; explicit wrong ServerName HostnameError. Default production verifier unchanged.") + +} + +type barrier struct { + reached chan struct{} + release chan struct{} + once sync.Once +} +type gatedDriver struct { + countedDriver + gate *barrier +} + +func (d gatedDriver) BeginTx(ctx context.Context, options *sql.TxOptions) (dialect.Tx, error) { + tx, err := d.countedDriver.BeginTx(ctx, options) + if err != nil { + return nil, err + } + return gatedTx{tx, d.gate}, nil +} + +type gatedTx struct { + dialect.Tx + gate *barrier +} + +func (tx gatedTx) Query(ctx context.Context, q string, args, v any) error { + err := tx.Tx.Query(ctx, q, args, v) + if err == nil && strings.Contains(q, `"playlists"`) { + tx.gate.once.Do(func() { + close(tx.gate.reached) + select { + case <-tx.gate.release: + case <-ctx.Done(): + } + }) + } + return err +} +func postCloud(t *testing.T, query string, variables any) *model.Playlist { + t.Helper() + body, _ := json.Marshal(map[string]any{"query": query, "variables": variables}) + request, _ := http.NewRequest("POST", "http://127.0.0.1:8080/query", bytes.NewReader(body)) + request.Header.Set("Content-Type", "application/json") + request.Header.Set("Authorization", "Bearer "+os.Getenv("ACCOUNT_A_TOKEN")) + response, err := (&http.Client{Timeout: 35 * time.Second}).Do(request) + if err != nil { + t.Fatal(err) + } + defer response.Body.Close() + raw, err := io.ReadAll(io.LimitReader(response.Body, 1048576)) + if err != nil { + t.Fatal(err) + } + var decoded struct { + Data map[string]*model.Playlist + Errors []any + } + if json.Unmarshal(raw, &decoded) != nil || len(decoded.Errors) > 0 || len(decoded.Data) != 1 { + t.Fatal("GraphQL fixture failed", response.StatusCode) + } + for _, playlist := range decoded.Data { + return playlist + } + t.Fatal("No result") + return nil +} +func TestCloudEagerReadCoherence(t *testing.T) { + fields := "id name revision items{position track{id title artist durationSeconds}}" + original := postCloud(t, "mutation($input:CreatePlaylistInput!){createPlaylist(input:$input){"+fields+"}}", map[string]any{"input": map[string]any{"name": "Coherence " + uuid.NewString(), "trackIds": []string{"10000000-0000-4000-8000-000000000001", "10000000-0000-4000-8000-000000000002"}}}) + client, db, err := Open() + if err != nil { + t.Fatal(err) + } + defer client.Close() + gate := &barrier{reached: make(chan struct{}), release: make(chan struct{})} + var releaseOnce sync.Once + release := func() { releaseOnce.Do(func() { close(gate.release) }) } + defer release() + gated := ent.NewClient(ent.Driver(gatedDriver{countedDriver{entsql.OpenDB(dialect.Postgres, db)}, gate})) + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + ctx = WithOwner(ctx, uuid.MustParse("00000000-0000-4000-8000-000000000001")) + ctx, count := WithCounter(ctx) + tx, err := gated.BeginTx(ctx, &sql.TxOptions{Isolation: sql.LevelRepeatableRead, ReadOnly: true}) + if err != nil { + t.Fatal(err) + } + defer tx.Rollback() + ctx = ent.NewContext(ctx, tx.Client()) + type answer struct { + row *model.Playlist + err error + } + done := make(chan answer, 1) + go func() { row, err := Read(ctx, original.ID); done <- answer{row, err} }() + select { + case <-gate.reached: + case <-ctx.Done(): + t.Fatal("Header SELECT barrier not reached") + } + changed := postCloud(t, "mutation($input:ReorderInput!){reorderPlaylist(input:$input){"+fields+"}}", map[string]any{"input": map[string]any{"id": original.ID, "expectedRevision": 1, "trackIds": []string{original.Items[1].Track.ID, original.Items[0].Track.ID}}}) + if changed.Revision != 2 { + t.Fatal("Writer did not commit revision2") + } + release() + result := <-done + if result.err != nil { + t.Fatal(result.err) + } + if result.row.Revision != 1 || result.row.Items[0].Track.ID != original.Items[0].Track.ID || result.row.Items[1].Track.ID != original.Items[1].Track.ID { + t.Fatal("Mixed header/items snapshot") + } + if count.Load() != 3 { + t.Fatal("Expected three actual eager SELECTs", count.Load()) + } + if err := tx.Commit(); err != nil { + t.Fatal(err) + } + latest := postCloud(t, "query($id:ID!){playlist(id:$id){"+fields+"}}", map[string]any{"id": original.ID}) + if latest.Revision != 2 || latest.Items[0].Track.ID != original.Items[1].Track.ID { + t.Fatal("Latest snapshot missing committed reorder") + } + t.Log("Actual first header SELECT paused; competing HTTP reorder committed; subsequent item/track SELECTs retained revision1 old order, fresh query sees revision2 swapped order. Exactly3 eager statements.") +} diff --git a/applications/playlist-api/internal/app/database.go b/applications/playlist-api/internal/app/database.go new file mode 100644 index 00000000..bc98d77e --- /dev/null +++ b/applications/playlist-api/internal/app/database.go @@ -0,0 +1,133 @@ +package app + +import ( + "context" + "crypto/tls" + "crypto/x509" + "database/sql" + "entgo.io/ent/dialect" + entsql "entgo.io/ent/dialect/sql" + "errors" + "example.com/playlist-api/ent" + "fmt" + "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/stdlib" + "net" + "net/url" + "os" + "sync/atomic" + "time" +) + +type counterKey struct{} + +func WithCounter(ctx context.Context) (context.Context, *atomic.Int64) { + count := &atomic.Int64{} + return context.WithValue(ctx, counterKey{}, count), count +} +func counted(ctx context.Context) { + if count, ok := ctx.Value(counterKey{}).(*atomic.Int64); ok { + count.Add(1) + } +} + +type countedDriver struct{ dialect.Driver } + +func (d countedDriver) Query(ctx context.Context, q string, args, v any) error { + counted(ctx) + return d.Driver.Query(ctx, q, args, v) +} +func (d countedDriver) Exec(ctx context.Context, q string, args, v any) error { + counted(ctx) + return d.Driver.Exec(ctx, q, args, v) +} +func (d countedDriver) Tx(ctx context.Context) (dialect.Tx, error) { + tx, err := d.Driver.Tx(ctx) + if err != nil { + return nil, err + } + return countedTx{tx}, nil +} + +// Ent BeginTx uses its optional dialect.TxBeginner interface; preserve it. +func (d countedDriver) BeginTx(ctx context.Context, options *sql.TxOptions) (dialect.Tx, error) { + tx, err := d.Driver.(interface { + BeginTx(context.Context, *sql.TxOptions) (dialect.Tx, error) + }).BeginTx(ctx, options) + if err != nil { + return nil, err + } + return countedTx{tx}, nil +} + +type countedTx struct{ dialect.Tx } + +func (t countedTx) Query(ctx context.Context, q string, args, v any) error { + counted(ctx) + return t.Tx.Query(ctx, q, args, v) +} +func (t countedTx) Exec(ctx context.Context, q string, args, v any) error { + counted(ctx) + return t.Tx.Exec(ctx, q, args, v) +} +func Config() (*pgx.ConnConfig, error) { + host := os.Getenv("PGHOST") + if host == "" { + return nil, errors.New("PGHOST required") + } + port := os.Getenv("PGPORT") + if port == "" { + port = "5432" + } + database := os.Getenv("PGDATABASE") + if database == "" { + database = "postgres" + } + u := url.URL{Scheme: "postgres", Host: net.JoinHostPort(host, port), Path: database, User: url.UserPassword(os.Getenv("PGUSER"), os.Getenv("PGPASSWORD"))} + params := url.Values{"sslmode": {"verify-full"}, "sslrootcert": {os.Getenv("PGSSLROOTCERT")}, "connect_timeout": {"10"}, "application_name": {"playlist-api"}} + u.RawQuery = params.Encode() + config, err := pgx.ParseConfig(u.String()) + if err != nil { + return nil, errors.New("Invalid database configuration") + } + ca, err := os.ReadFile(os.Getenv("PGSSLROOTCERT")) + if err != nil { + return nil, errors.New("Cloud CA required") + } + roots := x509.NewCertPool() + if !roots.AppendCertsFromPEM(ca) { + return nil, errors.New("Cloud CA invalid") + } + config.TLSConfig = &tls.Config{RootCAs: roots, ServerName: host, MinVersion: tls.VersionTLS12} + config.Fallbacks = nil + config.RuntimeParams["search_path"] = "playlist_api,public" + config.RuntimeParams["statement_timeout"] = "10000" + config.RuntimeParams["lock_timeout"] = "8000" + config.ConnectTimeout = 10 * time.Second + return config, nil +} + +func Open() (*ent.Client, *sql.DB, error) { + config, err := Config() + if err != nil { + return nil, nil, err + } + db := stdlib.OpenDB(*config) + db.SetMaxOpenConns(4) + db.SetMaxIdleConns(2) + db.SetConnMaxLifetime(15 * time.Minute) + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + if err = db.PingContext(ctx); err != nil { + db.Close() + return nil, nil, fmt.Errorf("Database connection failed") + } + return ent.NewClient(ent.Driver(countedDriver{entsql.OpenDB(dialect.Postgres, db)})), db, nil +} + +func SQLCount(ctx context.Context) int64 { + if c, ok := ctx.Value(counterKey{}).(*atomic.Int64); ok { + return c.Load() + } + return 0 +} diff --git a/applications/playlist-api/internal/app/domain.go b/applications/playlist-api/internal/app/domain.go new file mode 100644 index 00000000..07e567fa --- /dev/null +++ b/applications/playlist-api/internal/app/domain.go @@ -0,0 +1,102 @@ +package app + +import ( + "context" + "encoding/base64" + "errors" + "example.com/playlist-api/ent" + "github.com/google/uuid" + "strings" + "unicode" + "unicode/utf8" +) + +type PublicError struct{ Code, Message string } + +func (e *PublicError) Error() string { return e.Message } +func bad(message string) error { return &PublicError{"INVALID", message} } + +type ownerKey struct{} + +func WithOwner(ctx context.Context, id uuid.UUID) context.Context { + return context.WithValue(ctx, ownerKey{}, id) +} +func Owner(ctx context.Context) (uuid.UUID, error) { + id, ok := ctx.Value(ownerKey{}).(uuid.UUID) + if !ok { + return uuid.Nil, errors.New("missing authenticated identity") + } + return id, nil +} +func Client(ctx context.Context) (*ent.Client, error) { + client := ent.FromContext(ctx) + if client == nil { + return nil, errors.New("transaction client missing") + } + return client, nil +} +func ID(raw string) (uuid.UUID, error) { + if len(raw) != 36 { + return uuid.Nil, bad("A UUID is required.") + } + id, err := uuid.Parse(raw) + if err != nil { + return uuid.Nil, bad("A UUID is required.") + } + return id, nil +} +func Name(raw string) (string, error) { + if !utf8.ValidString(raw) || utf8.RuneCountInString(raw) > 80 { + return "", bad("Name must contain 1–80 characters.") + } + for _, r := range raw { + if r == utf8.RuneError || unicode.IsControl(r) || unicode.Is(unicode.Cs, r) { + return "", bad("Name contains invalid characters.") + } + } + raw = strings.TrimSpace(raw) + if raw == "" { + return "", bad("Name is required.") + } + return raw, nil +} +func IDs(raw []string) ([]uuid.UUID, error) { + if len(raw) < 1 || len(raw) > 50 { + return nil, bad("Supply 1–50 distinct tracks.") + } + result := make([]uuid.UUID, 0, len(raw)) + seen := map[uuid.UUID]bool{} + for _, value := range raw { + id, err := ID(value) + if err != nil { + return nil, err + } + if seen[id] { + return nil, bad("Track IDs must be distinct.") + } + seen[id] = true + result = append(result, id) + } + return result, nil +} +func Cursor(owner, last uuid.UUID) string { + return base64.RawURLEncoding.EncodeToString([]byte(owner.String() + "|" + last.String())) +} +func ParseCursor(raw string, owner uuid.UUID) (uuid.UUID, error) { + if len(raw) > 100 { + return uuid.Nil, bad("Invalid cursor.") + } + data, err := base64.RawURLEncoding.DecodeString(raw) + if err != nil { + return uuid.Nil, bad("Invalid cursor.") + } + parts := strings.Split(string(data), "|") + if len(parts) != 2 || parts[0] != owner.String() { + return uuid.Nil, bad("Cursor belongs to another account.") + } + id, err := ID(parts[1]) + if err != nil || Cursor(owner, id) != raw { + return uuid.Nil, bad("Invalid cursor.") + } + return id, nil +} diff --git a/applications/playlist-api/internal/app/domain_test.go b/applications/playlist-api/internal/app/domain_test.go new file mode 100644 index 00000000..f2a1a169 --- /dev/null +++ b/applications/playlist-api/internal/app/domain_test.go @@ -0,0 +1,78 @@ +package app + +import ( + "context" + "github.com/google/uuid" + "github.com/vektah/gqlparser/v2/ast" + "github.com/vektah/gqlparser/v2/parser" + "testing" +) + +func TestMutationExpansion(t *testing.T) { + for _, query := range []string{`mutation{ a:createPlaylist(input:{name:"x",trackIds:[]}){id} b:createPlaylist(input:{name:"y",trackIds:[]}){id}}`, `mutation{...F} fragment F on Mutation{a:createPlaylist(input:{name:"x",trackIds:[]}){id} ...G} fragment G on Mutation{b:createPlaylist(input:{name:"x",trackIds:[]}){id}}`, `mutation{...F ...F} fragment F on Mutation{createPlaylist(input:{name:"x",trackIds:[]}){id}}`} { + doc, err := parser.ParseQuery(&ast.Source{Input: query}) + if err != nil { + t.Fatal(err) + } + if CheckOperation(doc, doc.Operations[0]) == nil { + t.Fatal("multiple expanded writes accepted") + } + } + doc, _ := parser.ParseQuery(&ast.Source{Input: `mutation{...F} fragment F on Mutation{one:createPlaylist(input:{name:"x",trackIds:[]}){id}}`}) + if err := CheckOperation(doc, doc.Operations[0]); err != nil { + t.Fatal(err) + } +} +func TestBoundsAndExactPermutationInputs(t *testing.T) { + id := uuid.NewString() + if _, err := IDs([]string{id, id}); err == nil { + t.Fatal("duplicate accepted") + } + if _, err := IDs(make([]string, 51)); err == nil { + t.Fatal("oversized accepted") + } + if _, err := Name("\u0085name"); err == nil { + t.Fatal("control stripped") + } + if _, err := Name(string([]byte{255})); err == nil { + t.Fatal("invalid Unicode accepted") + } +} +func TestScopedCanonicalCursor(t *testing.T) { + a, b, last := uuid.New(), uuid.New(), uuid.New() + text := Cursor(a, last) + if got, err := ParseCursor(text, a); err != nil || got != last { + t.Fatal("cursor roundtrip") + } + if _, err := ParseCursor(text, b); err == nil { + t.Fatal("account crossing") + } + if _, err := ParseCursor(text+"=", a); err == nil { + t.Fatal("noncanonical accepted") + } +} +func TestNoGlobalClientFallback(t *testing.T) { + if _, err := Client(context.Background()); err == nil { + t.Fatal("missing transaction client accepted") + } +} +func TestComplexitySaturates(t *testing.T) { + if Cost(1<<62, 50) <= CostLimit { + t.Fatal("overflow accepted") + } + if Cost(4, 50) != 201 { + t.Fatal("fanout not multiplied") + } + if Cost(1, 51) <= CostLimit { + t.Fatal("invalid fanout accepted") + } +} + +func TestNameReplacementRuneAndPairedUnicode(t *testing.T) { + if _, err := Name("Lone surrogate decoded as \uFFFD"); err == nil { + t.Fatal("replacement rune accepted") + } + if value, err := Name("Paired \U0001F680"); err != nil || value != "Paired \U0001F680" { + t.Fatal("valid non-BMP name rejected") + } +} diff --git a/applications/playlist-api/internal/app/middleware.go b/applications/playlist-api/internal/app/middleware.go new file mode 100644 index 00000000..23af4302 --- /dev/null +++ b/applications/playlist-api/internal/app/middleware.go @@ -0,0 +1,84 @@ +package app + +import ( + "context" + "database/sql" + "errors" + "example.com/playlist-api/ent" + "github.com/99designs/gqlgen/graphql" + "github.com/vektah/gqlparser/v2/ast" + "github.com/vektah/gqlparser/v2/gqlerror" +) + +type Policy struct{} + +func (Policy) ExtensionName() string { return "BoundedOperationPolicy" } +func (Policy) Validate(graphql.ExecutableSchema) error { return nil } +func (Policy) MutateOperationContext(_ context.Context, op *graphql.OperationContext) *gqlerror.Error { + if err := CheckOperation(op.Doc, op.Operation); err != nil { + return &gqlerror.Error{Message: err.Error(), Extensions: map[string]any{"code": "OPERATION_POLICY"}} + } + return nil +} + +type QuerySnapshot struct{ Client *ent.Client } + +func (QuerySnapshot) ExtensionName() string { return "QuerySnapshot" } +func (QuerySnapshot) Validate(graphql.ExecutableSchema) error { return nil } +func (q QuerySnapshot) InterceptResponse(ctx context.Context, next graphql.ResponseHandler) *graphql.Response { + if graphql.GetOperationContext(ctx).Operation.Operation != ast.Query { + return next(ctx) + } + tx, err := q.Client.BeginTx(ctx, &sql.TxOptions{Isolation: sql.LevelRepeatableRead, ReadOnly: true}) + if err != nil { + return graphql.ErrorResponse(ctx, "Database unavailable.") + } + defer tx.Rollback() + ctx = ent.NewTxContext(ctx, tx) + ctx = ent.NewContext(ctx, tx.Client()) + response := next(ctx) + if len(response.Errors) > 0 { + return &graphql.Response{Errors: response.Errors} + } + if err = tx.Commit(); err != nil { + return graphql.ErrorResponse(ctx, "Database unavailable.") + } + return response +} +func PresentError(ctx context.Context, err error) *gqlerror.Error { + out := graphql.DefaultErrorPresenter(ctx, err) + if out.Extensions["code"] == "OPERATION_POLICY" { + return out + } + if out.Extensions["code"] == "COMPLEXITY_LIMIT_EXCEEDED" { + out.Message = "The requested query is too expensive." + return out + } + var public *PublicError + if errors.As(err, &public) { + out.Message = public.Message + out.Extensions = map[string]any{"code": public.Code} + } else { + out.Message = "The request could not be completed." + out.Extensions = map[string]any{"code": "REQUEST_FAILED"} + } + return out +} + +// Wrap the completed response outside Ent's middleware because its commit/open +// errors are constructed directly, bypassing the resolver error presenter. +func Sanitize(ctx context.Context, next graphql.ResponseHandler) *graphql.Response { + response := next(ctx) + if response == nil { + return nil + } + for _, err := range response.Errors { + code, _ := err.Extensions["code"].(string) + switch code { + case "INVALID", "NOT_FOUND", "STALE", "OPERATION_POLICY", "COMPLEXITY_LIMIT_EXCEEDED": + default: + err.Message = "The request could not be completed." + } + } + return response +} diff --git a/applications/playlist-api/internal/app/policy.go b/applications/playlist-api/internal/app/policy.go new file mode 100644 index 00000000..3f5ecf48 --- /dev/null +++ b/applications/playlist-api/internal/app/policy.go @@ -0,0 +1,80 @@ +package app + +import ( + "fmt" + "github.com/vektah/gqlparser/v2/ast" +) + +const CostLimit = 6000 + +func Cost(child, fanout int) int { + if child < 0 || fanout < 1 || fanout > 50 || child > CostLimit/fanout { + return CostLimit + 1 + } + return 1 + child*fanout +} + +// Count expanded syntactic occurrences, not merged response keys. Repeated +// fragments and aliases cannot turn two mutation fields into one allowed write. +func CheckOperation(doc *ast.QueryDocument, op *ast.OperationDefinition) error { + if op.Operation == ast.Subscription { + return bad("Subscriptions are disabled.") + } + fields, total := 0, 0 + var walk func(ast.SelectionSet, int) error + walk = func(selections ast.SelectionSet, depth int) error { + if len(selections) == 0 { + return nil + } + if depth > 5 { + return bad("Selection depth exceeds five.") + } + for _, selection := range selections { + total++ + if total > 100 { + return bad("Too many expanded selections.") + } + switch node := selection.(type) { + case *ast.Field: + if depth == 1 { + fields++ + } + if node.Name == "__schema" || node.Name == "__type" { + return bad("Introspection is disabled.") + } + if err := walk(node.SelectionSet, depth+1); err != nil { + return err + } + case *ast.InlineFragment: + if err := walk(node.SelectionSet, depth); err != nil { + return err + } + case *ast.FragmentSpread: + fragment := doc.Fragments.ForName(node.Name) + if fragment == nil { + return bad("Unknown fragment.") + } + if err := walk(fragment.SelectionSet, depth); err != nil { + return err + } + } + } + return nil + } + if err := walk(op.SelectionSet, 1); err != nil { + return err + } + if op.Operation == ast.Mutation && fields != 1 { + return &PublicError{"OPERATION_POLICY", "Supply exactly one top-level mutation field."} + } + if fields > 5 { + return bad("At most five top-level query fields are allowed.") + } + return nil +} +func Bound(limit, max int) error { + if limit < 1 || limit > max { + return bad(fmt.Sprintf("Limit must be 1–%d.", max)) + } + return nil +} diff --git a/applications/playlist-api/internal/app/store.go b/applications/playlist-api/internal/app/store.go new file mode 100644 index 00000000..f3c325e3 --- /dev/null +++ b/applications/playlist-api/internal/app/store.go @@ -0,0 +1,202 @@ +package app + +import ( + "context" + "example.com/playlist-api/ent" + "example.com/playlist-api/ent/item" + "example.com/playlist-api/ent/playlist" + "example.com/playlist-api/ent/track" + "example.com/playlist-api/graph/model" + "github.com/google/uuid" +) + +func eager(query *ent.PlaylistQuery) *ent.PlaylistQuery { + return query.WithItems(func(q *ent.ItemQuery) { q.Order(ent.Asc(item.FieldPosition)).WithTrack() }) +} +func dto(row *ent.Playlist) (*model.Playlist, error) { + result := &model.Playlist{ID: row.ID.String(), Name: row.Name, Revision: row.Revision, Items: []*model.PlaylistItem{}} + children, err := row.Edges.ItemsOrErr() + if err != nil { + return nil, err + } + for _, child := range children { + t, err := child.Edges.TrackOrErr() + if err != nil { + return nil, err + } + result.Items = append(result.Items, &model.PlaylistItem{Position: child.Position, Track: trackDTO(t)}) + } + return result, nil +} +func trackDTO(t *ent.Track) *model.Track { + return &model.Track{ID: t.ID.String(), Title: t.Title, Artist: t.Artist, DurationSeconds: t.DurationSeconds} +} +func Read(ctx context.Context, raw string) (*model.Playlist, error) { + id, err := ID(raw) + if err != nil { + return nil, err + } + owner, err := Owner(ctx) + if err != nil { + return nil, err + } + client, err := Client(ctx) + if err != nil { + return nil, err + } + row, err := eager(client.Playlist.Query().Where(playlist.IDEQ(id), playlist.OwnerIDEQ(owner))).Only(ctx) + if ent.IsNotFound(err) { + return nil, &PublicError{"NOT_FOUND", "Playlist not found."} + } + if err != nil { + return nil, err + } + return dto(row) +} +func List(ctx context.Context, limit int, cursor *string) (*model.PlaylistPage, error) { + if limit < 1 || limit > 10 { + return nil, bad("Page limit must be 1–10.") + } + owner, err := Owner(ctx) + if err != nil { + return nil, err + } + client, err := Client(ctx) + if err != nil { + return nil, err + } + query := client.Playlist.Query().Where(playlist.OwnerIDEQ(owner)) + if cursor != nil { + id, err := ParseCursor(*cursor, owner) + if err != nil { + return nil, err + } + query = query.Where(playlist.IDGT(id)) + } + rows, err := eager(query.Order(ent.Asc(playlist.FieldID)).Limit(limit + 1)).All(ctx) + if err != nil { + return nil, err + } + result := &model.PlaylistPage{Playlists: []*model.Playlist{}} + if len(rows) > limit { + next := Cursor(owner, rows[limit-1].ID) + result.NextCursor = &next + rows = rows[:limit] + } + for _, row := range rows { + out, err := dto(row) + if err != nil { + return nil, err + } + result.Playlists = append(result.Playlists, out) + } + return result, nil +} +func Tracks(ctx context.Context, limit int) ([]*model.Track, error) { + if limit < 1 || limit > 50 { + return nil, bad("Track limit must be 1–50.") + } + client, err := Client(ctx) + if err != nil { + return nil, err + } + rows, err := client.Track.Query().Order(ent.Asc(track.FieldID)).Limit(limit).All(ctx) + if err != nil { + return nil, err + } + result := make([]*model.Track, 0, len(rows)) + for _, row := range rows { + result = append(result, trackDTO(row)) + } + return result, nil +} +func Create(ctx context.Context, input model.CreatePlaylistInput) (*model.Playlist, error) { + name, err := Name(input.Name) + if err != nil { + return nil, err + } + ids, err := IDs(input.TrackIds) + if err != nil { + return nil, err + } + owner, err := Owner(ctx) + if err != nil { + return nil, err + } + client, err := Client(ctx) + if err != nil { + return nil, err + } + count, err := client.Track.Query().Where(track.IDIn(ids...)).Count(ctx) + if err != nil { + return nil, err + } + if count != len(ids) { + return nil, bad("Every track must exist.") + } + row, err := client.Playlist.Create().SetOwnerID(owner).SetName(name).Save(ctx) + if err != nil { + return nil, err + } + for position, id := range ids { + if _, err = client.Item.Create().SetPlaylistID(row.ID).SetTrackID(id).SetPosition(position + 1).Save(ctx); err != nil { + return nil, err + } + } + return Read(ctx, row.ID.String()) +} +func Reorder(ctx context.Context, input model.ReorderInput) (*model.Playlist, error) { + id, err := ID(input.ID) + if err != nil { + return nil, err + } + ids, err := IDs(input.TrackIds) + if err != nil { + return nil, err + } + if input.ExpectedRevision < 1 || input.ExpectedRevision >= 2147483647 { + return nil, bad("Revision is out of bounds.") + } + owner, err := Owner(ctx) + if err != nil { + return nil, err + } + client, err := Client(ctx) + if err != nil { + return nil, err + } + row, err := client.Playlist.Query().Where(playlist.IDEQ(id), playlist.OwnerIDEQ(owner)).ForUpdate().Only(ctx) + if ent.IsNotFound(err) { + return nil, &PublicError{"NOT_FOUND", "Playlist not found."} + } + if err != nil { + return nil, err + } + if row.Revision != input.ExpectedRevision { + return nil, &PublicError{"STALE", "Playlist changed. Read its latest revision first."} + } + existing, err := client.Item.Query().Where(item.PlaylistIDEQ(id)).All(ctx) + if err != nil { + return nil, err + } + members := map[uuid.UUID]*ent.Item{} + for _, child := range existing { + members[child.TrackID] = child + } + if len(existing) != len(ids) { + return nil, bad("Reorder must preserve every existing track.") + } + for position, trackID := range ids { + child, ok := members[trackID] + if !ok { + return nil, bad("Reorder must be an exact membership permutation.") + } + if _, err = client.Item.UpdateOne(child).SetPosition(position + 1).Save(ctx); err != nil { + return nil, err + } + } + if _, err = client.Playlist.UpdateOne(row).AddRevision(1).Save(ctx); err != nil { + return nil, err + } + return Read(ctx, id.String()) +} diff --git a/applications/playlist-api/internal/gen/main.go b/applications/playlist-api/internal/gen/main.go new file mode 100644 index 00000000..120253f5 --- /dev/null +++ b/applications/playlist-api/internal/gen/main.go @@ -0,0 +1,15 @@ +//go:build ignore + +package main + +import ( + "entgo.io/ent/entc" + "entgo.io/ent/entc/gen" + "log" +) + +func main() { + if err := entc.Generate("./ent/schema", &gen.Config{Features: []gen.Feature{gen.FeatureLock}}); err != nil { + log.Fatal(err) + } +} diff --git a/applications/playlist-api/migrations/001-down.sql b/applications/playlist-api/migrations/001-down.sql new file mode 100644 index 00000000..df4f17bc --- /dev/null +++ b/applications/playlist-api/migrations/001-down.sql @@ -0,0 +1,10 @@ +BEGIN; +SELECT pg_advisory_xact_lock(hashtextextended('playlist_api:001',0)); +DROP TABLE IF EXISTS playlist_api.items; +DROP TABLE IF EXISTS playlist_api.playlists; +DROP TABLE IF EXISTS playlist_api.tracks; +DROP TABLE IF EXISTS playlist_api.accounts; +DROP FUNCTION IF EXISTS playlist_api.complete_items(); +DROP FUNCTION IF EXISTS playlist_api.playlist_quota(); +DELETE FROM playlist_api.schema_migrations WHERE version=1; +COMMIT; diff --git a/applications/playlist-api/migrations/001-up.sql b/applications/playlist-api/migrations/001-up.sql new file mode 100644 index 00000000..666ae021 --- /dev/null +++ b/applications/playlist-api/migrations/001-up.sql @@ -0,0 +1,40 @@ +\getenv migration_sha MIGRATION_SHA +BEGIN; +SELECT pg_advisory_xact_lock(hashtextextended('playlist_api:001',0)); +CREATE TABLE IF NOT EXISTS playlist_api.schema_migrations(version integer PRIMARY KEY,checksum text NOT NULL,applied_at timestamptz NOT NULL DEFAULT now()); +SELECT EXISTS(SELECT 1 FROM playlist_api.schema_migrations WHERE version=1) AS applied \gset +\if :applied +SELECT checksum=:'migration_sha' AS same FROM playlist_api.schema_migrations WHERE version=1 \gset +\if :same +\echo Migration already applied with matching checksum. +\else +\echo Migration checksum changed; refusing. +\quit 3 +\endif +\else +CREATE TABLE playlist_api.accounts(id uuid PRIMARY KEY); +CREATE TABLE playlist_api.tracks(id uuid PRIMARY KEY,title text NOT NULL CHECK(length(title) BETWEEN 1 AND 80),artist text NOT NULL CHECK(length(artist) BETWEEN 1 AND 80),duration_seconds bigint NOT NULL CHECK(duration_seconds BETWEEN 1 AND 3600)); +CREATE TABLE playlist_api.playlists(id uuid PRIMARY KEY,owner_id uuid NOT NULL REFERENCES playlist_api.accounts(id),name text NOT NULL CHECK(length(btrim(name)) BETWEEN 1 AND 80),revision bigint NOT NULL DEFAULT 1 CHECK(revision BETWEEN 1 AND 2147483647)); +CREATE INDEX playlists_owner_id_id ON playlist_api.playlists(owner_id,id); +CREATE TABLE playlist_api.items(id uuid PRIMARY KEY,playlist_id uuid NOT NULL REFERENCES playlist_api.playlists(id),track_id uuid NOT NULL REFERENCES playlist_api.tracks(id),position bigint NOT NULL CHECK(position BETWEEN 1 AND 50),CONSTRAINT items_playlist_track UNIQUE(playlist_id,track_id),CONSTRAINT items_playlist_position UNIQUE(playlist_id,position) DEFERRABLE INITIALLY DEFERRED); +-- Narrow SECURITY DEFINER trigger locks the existing account row as owner. +-- It never accepts SQL/text identifiers and uses a fixed search_path. +CREATE FUNCTION playlist_api.playlist_quota() RETURNS trigger LANGUAGE plpgsql SECURITY DEFINER SET search_path=pg_catalog,playlist_api AS $$ +BEGIN + PERFORM 1 FROM playlist_api.accounts WHERE id=NEW.owner_id FOR UPDATE; + IF NOT FOUND THEN RAISE EXCEPTION 'Account not found' USING ERRCODE='23503'; END IF; + IF (SELECT count(*) FROM playlist_api.playlists WHERE owner_id=NEW.owner_id)>=100 THEN RAISE EXCEPTION 'Playlist limit reached' USING ERRCODE='23514'; END IF; + RETURN NEW; +END $$; +CREATE TRIGGER playlist_quota BEFORE INSERT ON playlist_api.playlists FOR EACH ROW EXECUTE FUNCTION playlist_api.playlist_quota(); +CREATE FUNCTION playlist_api.complete_items() RETURNS trigger LANGUAGE plpgsql AS $$ +DECLARE total integer; lo bigint; hi bigint; +BEGIN + SELECT count(*),min(position),max(position) INTO total,lo,hi FROM playlist_api.items WHERE playlist_id=NEW.id; + IF total NOT BETWEEN 1 AND 50 OR lo<>1 OR hi<>total THEN RAISE EXCEPTION 'Playlist requires complete contiguous positions' USING ERRCODE='23514'; END IF; + RETURN NEW; +END $$; +CREATE CONSTRAINT TRIGGER complete_items AFTER INSERT OR UPDATE ON playlist_api.playlists DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION playlist_api.complete_items(); +INSERT INTO playlist_api.schema_migrations(version,checksum) VALUES(1,:'migration_sha'); +\endif +COMMIT; diff --git a/applications/playlist-api/scripts/bootstrap.sql b/applications/playlist-api/scripts/bootstrap.sql new file mode 100644 index 00000000..c65595c9 --- /dev/null +++ b/applications/playlist-api/scripts/bootstrap.sql @@ -0,0 +1,11 @@ +\getenv owner_password PG_MIGRATION_PASSWORD +\getenv app_password PG_APP_PASSWORD +CREATE ROLE playlist_owner LOGIN PASSWORD :'owner_password'; +CREATE ROLE playlist_app LOGIN PASSWORD :'app_password'; +CREATE SCHEMA playlist_api AUTHORIZATION playlist_owner; +REVOKE ALL ON SCHEMA playlist_api FROM PUBLIC; +REVOKE CREATE ON SCHEMA public FROM PUBLIC; +REVOKE CREATE, TEMP ON DATABASE postgres FROM PUBLIC; +GRANT CONNECT ON DATABASE postgres TO playlist_owner,playlist_app; +ALTER ROLE playlist_owner SET search_path TO playlist_api,public; +ALTER ROLE playlist_app SET search_path TO playlist_api,public; diff --git a/applications/playlist-api/scripts/check-generation.py b/applications/playlist-api/scripts/check-generation.py new file mode 100644 index 00000000..296ac88f --- /dev/null +++ b/applications/playlist-api/scripts/check-generation.py @@ -0,0 +1,18 @@ +"""Fail if pinned regeneration changes any Go/config/module source file.""" +from pathlib import Path +import hashlib +import subprocess + + +def snapshot(): + return {str(path): hashlib.sha256(path.read_bytes()).hexdigest() + for path in Path(".").rglob("*") if path.is_file() + and (path.suffix == ".go" or path.name in ("go.mod", "go.sum")) + and ".local" not in path.parts} + + +before = snapshot() +subprocess.run(["go", "generate", "./..."], check=True) +after = snapshot() +assert before == after, "Generation drift: review generated changes before commit" +print(f"Deterministic regeneration matched {len(before)} Go/module source files") diff --git a/applications/playlist-api/scripts/grants.sql b/applications/playlist-api/scripts/grants.sql new file mode 100644 index 00000000..e90ab8e9 --- /dev/null +++ b/applications/playlist-api/scripts/grants.sql @@ -0,0 +1,4 @@ +GRANT USAGE ON SCHEMA playlist_api TO playlist_app; +GRANT SELECT ON playlist_api.accounts,playlist_api.playlists,playlist_api.items,playlist_api.tracks TO playlist_app; +GRANT INSERT(id,owner_id,name,revision),UPDATE(revision) ON playlist_api.playlists TO playlist_app; +GRANT INSERT(id,playlist_id,track_id,position),UPDATE(position) ON playlist_api.items TO playlist_app; diff --git a/applications/playlist-api/scripts/migrate.sh b/applications/playlist-api/scripts/migrate.sh new file mode 100755 index 00000000..eb784325 --- /dev/null +++ b/applications/playlist-api/scripts/migrate.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +set -euo pipefail +cd "$(dirname "$0")/.." +: "${PGSSLROOTCERT:?Cloud CA required}" +test "${PGUSER:?Schema owner required}" = playlist_owner +export PGSSLMODE=verify-full +case "${1:-up}" in + up) export MIGRATION_SHA; MIGRATION_SHA=$(sha256sum migrations/001-up.sql | cut -d ' ' -f1); psql -X -v ON_ERROR_STOP=1 -f migrations/001-up.sql ;; + down) psql -X -v ON_ERROR_STOP=1 -f migrations/001-down.sql ;; + *) echo 'Use up or down' >&2; exit 2 ;; +esac diff --git a/applications/playlist-api/scripts/seed.sql b/applications/playlist-api/scripts/seed.sql new file mode 100644 index 00000000..9fb9558e --- /dev/null +++ b/applications/playlist-api/scripts/seed.sql @@ -0,0 +1,3 @@ +INSERT INTO playlist_api.accounts(id) VALUES('00000000-0000-4000-8000-000000000001'),('00000000-0000-4000-8000-000000000002') ON CONFLICT DO NOTHING; +INSERT INTO playlist_api.tracks(id,title,artist,duration_seconds) +SELECT ('10000000-0000-4000-8000-'||lpad(n::text,12,'0'))::uuid,'Synthetic Track '||n,'Example Ensemble',90+n FROM generate_series(1,60) AS n ON CONFLICT DO NOTHING; diff --git a/applications/playlist-api/tests/acceptance.py b/applications/playlist-api/tests/acceptance.py new file mode 100644 index 00000000..dbc41bff --- /dev/null +++ b/applications/playlist-api/tests/acceptance.py @@ -0,0 +1,384 @@ +"""Opt-in actual GraphQL/Cloud controls, not part of basic CI.""" + +import os, uuid, time, json, unittest +from pathlib import Path +from concurrent.futures import ThreadPoolExecutor +import httpx, psycopg + +BASE = os.getenv("BASE_URL", "http://127.0.0.1:8080") +A = "00000000-0000-4000-8000-000000000001" +B = "00000000-0000-4000-8000-000000000002" +TRACKS = [f"10000000-0000-4000-8000-{n:012d}" for n in range(1, 61)] +FIELDS = "id name revision items {position track {id title artist durationSeconds}}" +CREATE = ( + "mutation($input:CreatePlaylistInput!){createPlaylist(input:$input){" + + FIELDS + + "}}" +) +REORDER = ( + "mutation($input:ReorderInput!){reorderPlaylist(input:$input){" + FIELDS + "}}" +) +READ = "query($id:ID!){playlist(id:$id){" + FIELDS + "}}" +LIST = ( + "query($limit:Int!,$cursor:String){playlists(limit:$limit,cursor:$cursor){playlists{" + + FIELDS + + "} nextCursor}}" +) + + +def db(owner=False): + return psycopg.connect( + host=os.environ["PGHOST"], + port=os.getenv("PGPORT", "5432"), + dbname=os.getenv("PGDATABASE", "postgres"), + user=os.environ["TEST_OWNER_USER"] if owner else os.environ["PGUSER"], + password=os.environ["TEST_OWNER_PASSWORD"] + if owner + else os.environ["PGPASSWORD"], + sslmode="verify-full", + sslrootcert=os.environ["PGSSLROOTCERT"], + connect_timeout=10, + options="-c statement_timeout=10000 -c lock_timeout=8000", + ) + + +def gql(query, variables=None, account="A"): + with httpx.Client(timeout=35) as client: + return client.post( + BASE + "/query", + json={"query": query, "variables": variables or {}}, + headers={ + "Authorization": "Bearer " + os.environ[f"ACCOUNT_{account}_TOKEN"] + }, + ) + + +def order(row): + return [x["track"]["id"] for x in row["items"]] + + +class Acceptance(unittest.TestCase): + def success(self, response, key): + self.assertEqual(response.status_code, 200, response.text) + data = response.json() + self.assertNotIn("errors", data, data) + return data["data"][key] + + def error(self, response, code=None): + self.assertEqual(response.status_code, 200, response.text) + data = response.json() + self.assertTrue(data.get("errors"), data) + self.assertFalse(data.get("data")) + if code: + self.assertEqual(data["errors"][0].get("extensions", {}).get("code"), code) + return data + + def create(self, tracks=None, account="A", name=None): + return self.success( + gql( + CREATE, + { + "input": { + "name": name or "Synthetic " + uuid.uuid4().hex, + "trackIds": tracks or TRACKS[:3], + } + }, + account, + ), + "createPlaylist", + ) + + def reorder(self, row, tracks=None, revision=None, account="A"): + return gql( + REORDER, + { + "input": { + "id": row["id"], + "expectedRevision": row["revision"] + if revision is None + else revision, + "trackIds": tracks or list(reversed(order(row))), + } + }, + account, + ) + + def test_01_create_owner_and_nested_scope(self): + row = self.create() + self.assertEqual(row["revision"], 1) + self.assertEqual(order(row), TRACKS[:3]) + self.assertEqual([x["position"] for x in row["items"]], [1, 2, 3]) + self.assertTrue( + all(x["track"]["artist"] == "Example Ensemble" for x in row["items"]) + ) + self.assertEqual(self.success(gql(READ, {"id": row["id"]}), "playlist"), row) + self.error(gql(READ, {"id": row["id"]}, "B"), "NOT_FOUND") + self.error(self.reorder(row, account="B"), "NOT_FOUND") + other = self.create(account="B") + listed = self.success(gql(LIST, {"limit": 10}, "B"), "playlists") + self.assertNotIn(row["id"], [x["id"] for x in listed["playlists"]]) + self.assertIn(other["id"], [x["id"] for x in listed["playlists"]]) + self.assertEqual( + httpx.post( + BASE + "/query", json={"query": "{tracks(limit:1){id}}"} + ).status_code, + 401, + ) + self.assertEqual(httpx.get(BASE + "/query").status_code, 405) + + def test_02_swap_revision_and_permutation(self): + row = self.create(TRACKS[:2]) + changed = self.success(self.reorder(row), "reorderPlaylist") + self.assertEqual(changed["revision"], 2) + self.assertEqual(order(changed), TRACKS[1::-1]) + self.error(self.reorder(row), "STALE") + for tracks in [[TRACKS[0], TRACKS[0]], [TRACKS[0]], [TRACKS[0], TRACKS[2]]]: + self.error(self.reorder(changed, tracks), "INVALID") + self.assertEqual( + self.success(gql(READ, {"id": row["id"]}), "playlist"), changed + ) + for body in [ + {"name": " ", "trackIds": TRACKS[:1]}, + {"name": "x", "trackIds": []}, + {"name": "x", "trackIds": TRACKS[:51]}, + {"name": "x", "trackIds": [str(uuid.uuid4())]}, + ]: + self.error(gql(CREATE, {"input": body}), "INVALID") + + def test_03_independent_contending_reorders(self): + row = self.create() + sequences = [list(reversed(order(row))), order(row)[1:] + order(row)[:1]] + with db(True) as owner: + owner.execute( + "SELECT id FROM playlist_api.playlists WHERE id=%s FOR UPDATE", + (row["id"],), + ) + with ThreadPoolExecutor(2) as pool: + tasks = [pool.submit(self.reorder, row, tracks) for tracks in sequences] + deadline = time.monotonic() + 5 + blocked = 0 + while time.monotonic() < deadline: + owner.execute("SELECT pg_stat_clear_snapshot()") + blocked = owner.execute( + "SELECT count(*) FROM pg_stat_activity WHERE usename='playlist_app' AND cardinality(pg_blocking_pids(pid))>0" + ).fetchone()[0] + if blocked >= 2: + break + time.sleep(0.05) + self.assertGreaterEqual(blocked, 2) + owner.commit() + responses = [task.result() for task in tasks] + winners = [r for r in responses if not r.json().get("errors")] + self.assertEqual(len(winners), 1) + loser = next(r for r in responses if r.json().get("errors")) + self.error(loser, "STALE") + winner = self.success(winners[0], "reorderPlaylist") + self.assertEqual(winner["revision"], 2) + self.assertIn(order(winner), sequences) + + def test_04_alias_fragment_cost_and_transport_policy(self): + field = ( + 'createPlaylist(input:{name:"forbidden",trackIds:["' + + TRACKS[0] + + '"]}){id}' + ) + for query in [ + "mutation{a:" + field + " b:" + field + "}", + "mutation{...F} fragment F on Mutation{a:" + + field + + " ...G} fragment G on Mutation{b:" + + field + + "}", + "mutation{...F ...F} fragment F on Mutation{" + field + "}", + ]: + response = gql(query) + self.assertEqual(response.status_code, 200, response.text) + self.assertTrue(response.json().get("errors")) + self.assertNotIn("cannot commit", response.text) + expensive = ( + "{" + + " ".join( + "a" + str(n) + ":playlists(limit:10){playlists{" + FIELDS + "}}" + for n in range(5) + ) + + "}" + ) + response = gql(expensive) + self.assertEqual(response.status_code, 200, response.text) + self.assertTrue(response.json().get("errors")) + log = Path(os.environ["SERVER_LOG"]).read_text().splitlines() + self.assertIn("Observed Ent SQL statements=0", log[-1]) + for query in [ + "{playlists(limit:11){nextCursor}}", + "{tracks(limit:51){id}}", + "{__schema{types{name}}}", + ]: + response = gql(query) + self.assertEqual(response.status_code, 200, response.text) + response = gql("{tracks(limit:1){id}}") + self.success(response, "tracks") + response = httpx.post( + BASE + "/query", + headers={ + "Authorization": "Bearer " + os.environ["ACCOUNT_A_TOKEN"], + "Content-Type": "application/json", + }, + content=b" " * 16385, + ) + self.assertEqual(response.status_code, 413) + + def controlled_failure(self, deferred): + marker = "failure_" + uuid.uuid4().hex + with db(True) as owner: + if deferred: + owner.execute( + """CREATE FUNCTION playlist_api.acceptance_fault() RETURNS trigger LANGUAGE plpgsql AS $$ BEGIN IF NEW.name LIKE 'failure_%%' THEN RAISE EXCEPTION 'Controlled deferred fault 23514' USING ERRCODE='23514';END IF;RETURN NEW;END $$""" + ) + owner.execute( + "CREATE CONSTRAINT TRIGGER acceptance_fault AFTER INSERT ON playlist_api.playlists DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION playlist_api.acceptance_fault()" + ) + else: + owner.execute( + """CREATE FUNCTION playlist_api.acceptance_fault() RETURNS trigger LANGUAGE plpgsql AS $$ BEGIN IF NEW.position=2 AND EXISTS(SELECT 1 FROM playlist_api.items WHERE playlist_id=NEW.playlist_id AND position=1) THEN RAISE EXCEPTION 'Controlled child fault 23514' USING ERRCODE='23514';END IF;RETURN NEW;END $$""" + ) + owner.execute( + "CREATE TRIGGER acceptance_fault AFTER INSERT ON playlist_api.items FOR EACH ROW EXECUTE FUNCTION playlist_api.acceptance_fault()" + ) + try: + response = gql(CREATE, {"input": {"name": marker, "trackIds": TRACKS[:3]}}) + self.error(response) + self.assertNotIn("23514", response.text) + self.assertNotIn("Controlled", response.text) + self.assertNotIn("cannot commit", response.text) + with db(True) as owner: + self.assertEqual( + owner.execute( + "SELECT count(*) FROM playlist_api.playlists WHERE name=%s", + (marker,), + ).fetchone()[0], + 0, + ) + self.assertEqual( + owner.execute( + "SELECT count(*) FROM playlist_api.items i JOIN playlist_api.playlists p ON i.playlist_id=p.id WHERE p.name=%s", + (marker,), + ).fetchone()[0], + 0, + ) + finally: + with db(True) as owner: + owner.execute( + "DROP TRIGGER acceptance_fault ON playlist_api." + + ("playlists" if deferred else "items") + ) + owner.execute("DROP FUNCTION playlist_api.acceptance_fault()") + self.create(name=marker) + + def test_05_after_parent_and_child_rollback(self): + self.controlled_failure(False) + + def test_06_deferred_commit_has_no_success_data(self): + self.controlled_failure(True) + + def test_07_runtime_role_and_position_constraint(self): + row = self.create() + for sql in [ + "UPDATE playlist_api.tracks SET title='bad'", + "DELETE FROM playlist_api.items", + "CREATE TABLE playlist_api.forbidden(id int)", + "CREATE TEMP TABLE forbidden(id int)", + ]: + with db() as runtime: + with self.assertRaises(psycopg.errors.InsufficientPrivilege): + runtime.execute(sql) + with db(True) as owner: + with self.assertRaises(psycopg.errors.UniqueViolation): + owner.execute( + "UPDATE playlist_api.items SET position=1 WHERE playlist_id=%s AND position=2", + (row["id"],), + ) + owner.commit() + self.assertEqual(self.success(gql(READ, {"id": row["id"]}), "playlist"), row) + + def test_08_scoped_cursor_and_live_keyset(self): + page = self.success(gql(LIST, {"limit": 2}), "playlists") + self.assertEqual(len(page["playlists"]), 2) + self.assertTrue(page["nextCursor"]) + self.error( + gql(LIST, {"limit": 2, "cursor": page["nextCursor"]}, "B"), "INVALID" + ) + nextpage = self.success( + gql(LIST, {"limit": 2, "cursor": page["nextCursor"]}), "playlists" + ) + self.assertTrue( + set(x["id"] for x in page["playlists"]).isdisjoint( + x["id"] for x in nextpage["playlists"] + ) + ) + + def clear_b(self): + with db(True) as owner: + owner.execute( + "DELETE FROM playlist_api.items WHERE playlist_id IN (SELECT id FROM playlist_api.playlists WHERE owner_id=%s)", + (B,), + ) + owner.execute("DELETE FROM playlist_api.playlists WHERE owner_id=%s", (B,)) + + def test_09_observed_small_and_larger_nested_counts(self): + self.clear_b() + small = self.create(TRACKS[:2], "B") + response = gql(READ, {"id": small["id"]}, "B") + self.success(response, "playlist") + self.assertEqual(response.json()["extensions"]["sqlStatements"], 3) + self.clear_b() + for _ in range(10): + self.create(TRACKS[:50], "B") + response = gql(LIST, {"limit": 10}, "B") + page = self.success(response, "playlists") + self.assertEqual(len(page["playlists"]), 10) + self.assertEqual(sum(len(p["items"]) for p in page["playlists"]), 500) + self.assertEqual(response.json()["extensions"]["sqlStatements"], 3) + print( + "Observed Ent statement counts: 1 playlist/2 items=3; 10 playlists/500 items=3 (excluding transaction control and trigger internals)" + ) + + def test_10_database_playlist_quota(self): + marker = "quota_" + uuid.uuid4().hex + with db(True) as owner: + count = owner.execute( + "SELECT count(*) FROM playlist_api.playlists WHERE owner_id=%s", (B,) + ).fetchone()[0] + remaining = 100 - count + owner.execute( + """WITH inserted AS (INSERT INTO playlist_api.playlists(id,owner_id,name) SELECT gen_random_uuid(),%s,%s FROM generate_series(1,%s) RETURNING id) INSERT INTO playlist_api.items(id,playlist_id,track_id,position) SELECT gen_random_uuid(),id,%s,1 FROM inserted""", + (B, marker, remaining, TRACKS[0]), + ) + try: + self.error( + gql( + CREATE, + {"input": {"name": "over quota", "trackIds": TRACKS[:1]}}, + "B", + ) + ) + with db(True) as owner: + self.assertEqual( + owner.execute( + "SELECT count(*) FROM playlist_api.playlists WHERE owner_id=%s", + (B,), + ).fetchone()[0], + 100, + ) + finally: + with db(True) as owner: + owner.execute( + "DELETE FROM playlist_api.items WHERE playlist_id IN (SELECT id FROM playlist_api.playlists WHERE name=%s)", + (marker,), + ) + owner.execute( + "DELETE FROM playlist_api.playlists WHERE name=%s", (marker,) + ) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/applications/playlist-api/tests/requirements.in b/applications/playlist-api/tests/requirements.in new file mode 100644 index 00000000..f6139e6a --- /dev/null +++ b/applications/playlist-api/tests/requirements.in @@ -0,0 +1,3 @@ +httpx==0.28.1 +psycopg[binary]==3.3.6 +ruff==0.16.10 diff --git a/applications/playlist-api/tests/requirements.txt b/applications/playlist-api/tests/requirements.txt new file mode 100644 index 00000000..63ec0f80 --- /dev/null +++ b/applications/playlist-api/tests/requirements.txt @@ -0,0 +1,10 @@ +anyio==4.15.1 +certifi==2026.7.22 +h11==0.16.0 +httpcore==1.0.9 +httpx==0.28.1 +idna==3.20 +psycopg==3.3.6 +psycopg-binary==3.3.6 +ruff==0.16.10 +typing_extensions==4.16.0 diff --git a/applications/playlist-api/tests/unicode_regression.py b/applications/playlist-api/tests/unicode_regression.py new file mode 100644 index 00000000..ee0ca981 --- /dev/null +++ b/applications/playlist-api/tests/unicode_regression.py @@ -0,0 +1,69 @@ +"""Focused actual HTTP regression for JSON surrogate replacement.""" + +import json +import os +import unittest +import uuid +import httpx +from acceptance import CREATE, TRACKS, BASE, db + + +class UnicodeRegression(unittest.TestCase): + def test_reject_surrogate_replacements_allow_valid_pair(self): + marker = "Unicode regression " + uuid.uuid4().hex + for suffix in ["\ud800", "\udc00", "\ufffd"]: + body = { + "query": CREATE, + "variables": { + "input": {"name": marker + suffix, "trackIds": TRACKS[:1]} + }, + } + response = httpx.post( + BASE + "/query", + content=json.dumps(body, ensure_ascii=True).encode("ascii"), + headers={ + "Content-Type": "application/json", + "Authorization": "Bearer " + os.environ["ACCOUNT_A_TOKEN"], + }, + timeout=35, + ) + self.assertEqual(response.status_code, 200, response.text) + result = response.json() + self.assertFalse(result.get("data")) + self.assertEqual(result["errors"][0]["extensions"]["code"], "INVALID") + self.assertEqual(result["extensions"]["sqlStatements"], 0) + with db(True) as owner: + self.assertEqual( + owner.execute( + "SELECT count(*) FROM playlist_api.playlists WHERE name=%s", + (marker + "\ufffd",), + ).fetchone()[0], + 0, + ) + paired = marker + "\ud83d\ude80" + body = { + "query": CREATE, + "variables": {"input": {"name": paired, "trackIds": TRACKS[:1]}}, + } + response = httpx.post( + BASE + "/query", + content=json.dumps(body, ensure_ascii=True).encode("ascii"), + headers={ + "Content-Type": "application/json", + "Authorization": "Bearer " + os.environ["ACCOUNT_A_TOKEN"], + }, + timeout=35, + ) + self.assertEqual(response.status_code, 200, response.text) + result = response.json() + self.assertNotIn("errors", result) + self.assertEqual( + result["data"]["createPlaylist"]["name"], marker + "\U0001f680" + ) + print( + "Escaped lone high/low surrogates and literal U+FFFD rejected with HTTP200 INVALID and zero Ent statements; valid paired non-BMP name persists unchanged" + ) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/applications/playlist-api/tools.go b/applications/playlist-api/tools.go new file mode 100644 index 00000000..ffb6b1cc --- /dev/null +++ b/applications/playlist-api/tools.go @@ -0,0 +1,9 @@ +//go:build tools + +package playlistapi + +import ( + _ "entgo.io/contrib/entgql" + _ "entgo.io/ent/cmd/ent" + _ "github.com/99designs/gqlgen" +) From 6a75553a9b45adb153eaa8fbfcf0f1233fc64c47 Mon Sep 17 00:00:00 2001 From: sdairs Date: Fri, 2 Oct 2026 21:31:17 +0100 Subject: [PATCH 2/2] docs: remove public beta wording --- applications/playlist-api/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/playlist-api/README.md b/applications/playlist-api/README.md index 88ca63a5..24e079a3 100644 --- a/applications/playlist-api/README.md +++ b/applications/playlist-api/README.md @@ -1,6 +1,6 @@ # Playlist API with gqlgen and Ent -A compact GraphQL API composes playlists over synthetic track metadata in ClickHouse Managed Postgres (public beta). There is no audio, upload, streaming, commercial catalogue or music-service integration. Two environment bearer tokens identify fixed server-side accounts. Create a named playlist, read its ordered metadata and reorder its existing tracks with the revision you observed. +A compact GraphQL API composes playlists over synthetic track metadata in ClickHouse Managed Postgres. There is no audio, upload, streaming, commercial catalogue or music-service integration. Two environment bearer tokens identify fixed server-side accounts. Create a named playlist, read its ordered metadata and reorder its existing tracks with the revision you observed. ## Verified native stack